001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j;
018
019import java.io.Serializable;
020import java.util.Collection;
021import java.util.Locale;
022import java.util.concurrent.ConcurrentHashMap;
023import java.util.concurrent.ConcurrentMap;
024
025import org.apache.logging.log4j.spi.StandardLevel;
026
027/**
028 * Levels used for identifying the severity of an event. Levels are organized from most specific to least:
029 * <ul>
030 * <li>{@link #OFF} (most specific, no logging)</li>
031 * <li>{@link #FATAL} (most specific, little data)</li>
032 * <li>{@link #ERROR}</li>
033 * <li>{@link #WARN}</li>
034 * <li>{@link #INFO}</li>
035 * <li>{@link #DEBUG}</li>
036 * <li>{@link #TRACE} (least specific, a lot of data)</li>
037 * <li>{@link #ALL} (least specific, all data)</li>
038 * </ul>
039 *
040 * Typically, configuring a level in a filter or on a logger will cause logging events of that level and those
041 * that are more specific to pass through the filter.
042 * A special level, {@link #ALL}, is guaranteed to capture all levels when used in logging configurations.
043 */
044public final class Level implements Comparable<Level>, Serializable {
045
046    private static final long serialVersionUID = 1581082L;
047    private static final ConcurrentMap<String, Level> levels = new ConcurrentHashMap<String, Level>();
048
049    /**
050     * No events will be logged.
051     */
052    public static final Level OFF;
053
054    /**
055     * A severe error that will prevent the application from continuing.
056     */
057    public static final Level FATAL;
058
059    /**
060     * An error in the application, possibly recoverable.
061     */
062    public static final Level ERROR;
063
064    /**
065     * An event that might possible lead to an error.
066     */
067    public static final Level WARN;
068
069    /**
070     * An event for informational purposes.
071     */
072    public static final Level INFO;
073
074    /**
075     * A general debugging event.
076     */
077    public static final Level DEBUG;
078
079    /**
080     * A fine-grained debug message, typically capturing the flow through the application.
081     */
082    public static final Level TRACE;
083
084    /**
085     * All events should be logged.
086     */
087    public static final Level ALL;
088
089    static {
090        OFF = new Level("OFF", StandardLevel.OFF.intLevel());
091        FATAL = new Level("FATAL", StandardLevel.FATAL.intLevel());
092        ERROR = new Level("ERROR", StandardLevel.ERROR.intLevel());
093        WARN = new Level("WARN", StandardLevel.WARN.intLevel());
094        INFO = new Level("INFO", StandardLevel.INFO.intLevel());
095        DEBUG = new Level("DEBUG", StandardLevel.DEBUG.intLevel());
096        TRACE = new Level("TRACE", StandardLevel.TRACE.intLevel());
097        ALL = new Level("ALL", StandardLevel.ALL.intLevel());
098    }
099
100    private final String name;
101    private final int intLevel;
102    private final StandardLevel standardLevel;
103
104    private Level(final String name, final int intLevel) {
105        if (name == null || name.isEmpty()) {
106            throw new IllegalArgumentException("Illegal null Level constant");
107        }
108        if (intLevel < 0) {
109            throw new IllegalArgumentException("Illegal Level int less than zero.");
110        }
111        this.name = name;
112        this.intLevel = intLevel;
113        this.standardLevel = StandardLevel.getStandardLevel(intLevel);
114        if (levels.putIfAbsent(name, this) != null) {
115            throw new IllegalStateException("Level " + name + " has already been defined.");
116        }
117    }
118
119    /**
120     * Gets the integral value of this Level.
121     *
122     * @return the value of this Level.
123     */
124    public int intLevel() {
125        return this.intLevel;
126    }
127
128    /**
129     * Gets the standard Level values as an enum.
130     *
131     * @return an enum of the standard Levels.
132     */
133    public StandardLevel getStandardLevel() {
134        return standardLevel;
135    }
136
137    /**
138     * Compares this level against the level passed as an argument and returns true if this level is the same or is less
139     * specific.T
140     *
141     * @param level
142     *            The level to test.
143     * @return True if this level Level is less specific or the same as the given Level.
144     */
145    public boolean isLessSpecificThan(final Level level) {
146        return this.intLevel >= level.intLevel;
147    }
148
149    /**
150     * Compares this level against the level passed as an argument and returns true if this level is the same or is more
151     * specific.
152     *
153     * @param level
154     *            The level to test.
155     * @return True if this level Level is more specific or the same as the given Level.
156     */
157    public boolean isMoreSpecificThan(final Level level) {
158        return this.intLevel <= level.intLevel;
159    }
160
161    @Override
162    @SuppressWarnings("CloneDoesntCallSuperClone")
163    public Level clone() throws CloneNotSupportedException {
164        throw new CloneNotSupportedException();
165    }
166
167    @Override
168    public int compareTo(final Level other) {
169        return intLevel < other.intLevel ? -1 : (intLevel > other.intLevel ? 1 : 0);
170    }
171
172    @Override
173    public boolean equals(final Object other) {
174        return other instanceof Level && other == this;
175    }
176
177    public Class<Level> getDeclaringClass() {
178        return Level.class;
179    }
180
181    @Override
182    public int hashCode() {
183        return this.name.hashCode();
184    }
185
186    /**
187     * Gets the symbolic name of this Level. Equivalent to calling {@link #toString()}.
188     *
189     * @return the name of this Level.
190     */
191    public String name() {
192        return this.name;
193    }
194
195    @Override
196    public String toString() {
197        return this.name;
198    }
199
200    /**
201     * Retrieves an existing Level or creates on if it didn't previously exist.
202     * @param name The name of the level.
203     * @param intValue The integer value for the Level. If the level was previously created this value is ignored.
204     * @return The Level.
205     * @throws java.lang.IllegalArgumentException if the name is null or intValue is less than zero.
206     */
207    public static Level forName(final String name, final int intValue) {
208        final Level level = levels.get(name);
209        if (level != null) {
210            return level;
211        }
212        try {
213            return new Level(name, intValue);
214        } catch (final IllegalStateException ex) {
215            // The level was added by something else so just return that one.
216            return levels.get(name);
217        }
218    }
219
220    /**
221     * Return the Level associated with the name or null if the Level cannot be found.
222     * @param name The name of the Level.
223     * @return The Level or null.
224     */
225    public static Level getLevel(final String name) {
226        return levels.get(name);
227    }
228
229    /**
230     * Converts the string passed as argument to a level. If the
231     * conversion fails, then this method returns {@link #DEBUG}.
232     *
233     * @param sArg The name of the desired Level.
234     * @return The Level associated with the String.
235     */
236    public static Level toLevel(final String sArg) {
237        return toLevel(sArg, Level.DEBUG);
238    }
239
240    /**
241     * Converts the string passed as argument to a level. If the
242     * conversion fails, then this method returns the value of
243     * <code>defaultLevel</code>.
244     *
245     * @param name The name of the desired Level.
246     * @param defaultLevel The Level to use if the String is invalid.
247     * @return The Level associated with the String.
248     */
249    public static Level toLevel(final String name, final Level defaultLevel) {
250        if (name == null) {
251            return defaultLevel;
252        }
253        final Level level = levels.get(name.toUpperCase(Locale.ENGLISH));
254        return level == null ? defaultLevel : level;
255    }
256
257    /**
258     * Return an array of all the Levels that have been registered.
259     * @return An array of Levels.
260     */
261    public static Level[] values() {
262        final Collection<Level> values = Level.levels.values();
263        return values.toArray(new Level[values.size()]);
264    }
265
266    /**
267     * Return the Level associated with the name.
268     * @param name The name of the Level to return.
269     * @return The Level.
270     * @throws java.lang.NullPointerException if the Level name is {@code null}.
271     * @throws java.lang.IllegalArgumentException if the Level name is not registered.
272     */
273    public static Level valueOf(final String name) {
274        if (name == null) {
275            throw new NullPointerException("No level name given.");
276        }
277        final String levelName = name.toUpperCase();
278        if (levels.containsKey(levelName)) {
279            return levels.get(levelName);
280        }
281        throw new IllegalArgumentException("Unknown level constant [" + levelName + "].");
282    }
283
284    /**
285     * Returns the enum constant of the specified enum type with the specified name. The name must match exactly an
286     * identifier used to declare an enum constant in this type. (Extraneous whitespace characters are not permitted.)
287     *
288     * @param enumType the {@code Class} object of the enum type from which to return a constant
289     * @param name     the name of the constant to return
290     * @param <T>      The enum type whose constant is to be returned
291     * @return the enum constant of the specified enum type with the specified name
292     * @throws java.lang.IllegalArgumentException if the specified enum type has no constant with the specified name,
293     *                                            or the specified class object does not represent an enum type
294     * @throws java.lang.NullPointerException     if {@code enumType} or {@code name} are {@code null}
295     * @see java.lang.Enum#valueOf(Class, String)
296     */
297    public static <T extends Enum<T>> T valueOf(final Class<T> enumType, final String name) {
298        return Enum.valueOf(enumType, name);
299    }
300
301    // for deserialization
302    protected Object readResolve() {
303        return Level.valueOf(this.name);
304    }
305}
306