1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache license, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the license for the specific language governing permissions and
15 * limitations under the license.
16 */
17 package org.apache.logging.log4j;
18
19 import java.io.Serializable;
20 import java.util.Collection;
21 import java.util.Locale;
22 import java.util.Objects;
23 import java.util.concurrent.ConcurrentHashMap;
24 import java.util.concurrent.ConcurrentMap;
25
26 import org.apache.logging.log4j.spi.StandardLevel;
27 import org.apache.logging.log4j.util.Strings;
28
29 /**
30 * Levels used for identifying the severity of an event. Levels are organized from most specific to least:
31 * <ul>
32 * <li>{@link #OFF} (most specific, no logging)</li>
33 * <li>{@link #FATAL} (most specific, little data)</li>
34 * <li>{@link #ERROR}</li>
35 * <li>{@link #WARN}</li>
36 * <li>{@link #INFO}</li>
37 * <li>{@link #DEBUG}</li>
38 * <li>{@link #TRACE} (least specific, a lot of data)</li>
39 * <li>{@link #ALL} (least specific, all data)</li>
40 * </ul>
41 *
42 * Typically, configuring a level in a filter or on a logger will cause logging events of that level and those that are
43 * more specific to pass through the filter. A special level, {@link #ALL}, is guaranteed to capture all levels when
44 * used in logging configurations.
45 */
46 public final class Level implements Comparable<Level>, Serializable {
47
48 /**
49 * No events will be logged.
50 */
51 public static final Level OFF;
52
53 /**
54 * A severe error that will prevent the application from continuing.
55 */
56 public static final Level FATAL;
57
58 /**
59 * An error in the application, possibly recoverable.
60 */
61 public static final Level ERROR;
62
63 /**
64 * An event that might possible lead to an error.
65 */
66 public static final Level WARN;
67
68 /**
69 * An event for informational purposes.
70 */
71 public static final Level INFO;
72
73 /**
74 * A general debugging event.
75 */
76 public static final Level DEBUG;
77
78 /**
79 * A fine-grained debug message, typically capturing the flow through the application.
80 */
81 public static final Level TRACE;
82
83 /**
84 * All events should be logged.
85 */
86 public static final Level ALL;
87
88 /**
89 * @since 2.1
90 */
91 public static final String CATEGORY = "Level";
92
93 private static final ConcurrentMap<String, Level> LEVELS = new ConcurrentHashMap<>(); // SUPPRESS CHECKSTYLE
94
95 private static final long serialVersionUID = 1581082L;
96
97 static {
98 OFF = new Level("OFF", StandardLevel.OFF.intLevel());
99 FATAL = new Level("FATAL", StandardLevel.FATAL.intLevel());
100 ERROR = new Level("ERROR", StandardLevel.ERROR.intLevel());
101 WARN = new Level("WARN", StandardLevel.WARN.intLevel());
102 INFO = new Level("INFO", StandardLevel.INFO.intLevel());
103 DEBUG = new Level("DEBUG", StandardLevel.DEBUG.intLevel());
104 TRACE = new Level("TRACE", StandardLevel.TRACE.intLevel());
105 ALL = new Level("ALL", StandardLevel.ALL.intLevel());
106 }
107
108 private final String name;
109 private final int intLevel;
110 private final StandardLevel standardLevel;
111
112 private Level(final String name, final int intLevel) {
113 if (Strings.isEmpty(name)) {
114 throw new IllegalArgumentException("Illegal null or empty Level name.");
115 }
116 if (intLevel < 0) {
117 throw new IllegalArgumentException("Illegal Level int less than zero.");
118 }
119 this.name = name;
120 this.intLevel = intLevel;
121 this.standardLevel = StandardLevel.getStandardLevel(intLevel);
122 if (LEVELS.putIfAbsent(name, this) != null) {
123 throw new IllegalStateException("Level " + name + " has already been defined.");
124 }
125 }
126
127 /**
128 * Gets the integral value of this Level.
129 *
130 * @return the value of this Level.
131 */
132 public int intLevel() {
133 return this.intLevel;
134 }
135
136 /**
137 * Gets the standard Level values as an enum.
138 *
139 * @return an enum of the standard Levels.
140 */
141 public StandardLevel getStandardLevel() {
142 return standardLevel;
143 }
144
145 /**
146 * Compares this level against the levels passed as arguments and returns true if this level is in between the given
147 * levels.
148 *
149 * @param minLevel The minimum level to test.
150 * @param maxLevel The maximum level to test.
151 * @return True true if this level is in between the given levels
152 * @since 2.4
153 */
154 public boolean isInRange(final Level minLevel, final Level maxLevel) {
155 return this.intLevel >= minLevel.intLevel && this.intLevel <= maxLevel.intLevel;
156 }
157
158 /**
159 * Compares this level against the level passed as an argument and returns true if this level is the same or is less
160 * specific.
161 * <p>
162 * Concretely, {@link #ALL} is less specific than {@link #TRACE}, which is less specific than {@link #DEBUG}, which
163 * is less specific than {@link #INFO}, which is less specific than {@link #WARN}, which is less specific than
164 * {@link #ERROR}, which is less specific than {@link #FATAL}, and finally {@link #OFF}, which is the most specific
165 * standard level.
166 * </p>
167 *
168 * @param level
169 * The level to test.
170 * @return True if this level Level is less specific or the same as the given Level.
171 */
172 public boolean isLessSpecificThan(final Level level) {
173 return this.intLevel >= level.intLevel;
174 }
175
176 /**
177 * Compares this level against the level passed as an argument and returns true if this level is the same or is more
178 * specific.
179 * <p>
180 * Concretely, {@link #FATAL} is more specific than {@link #ERROR}, which is more specific than {@link #WARN},
181 * etc., until {@link #TRACE}, and finally {@link #ALL}, which is the least specific standard level.
182 * The most specific level is {@link #OFF}.
183 * </p>
184 *
185 * @param level The level to test.
186 * @return True if this level Level is more specific or the same as the given Level.
187 */
188 public boolean isMoreSpecificThan(final Level level) {
189 return this.intLevel <= level.intLevel;
190 }
191
192 @Override
193 @SuppressWarnings("CloneDoesntCallSuperClone")
194 // CHECKSTYLE:OFF
195 public Level clone() throws CloneNotSupportedException {
196 throw new CloneNotSupportedException();
197 }
198 // CHECKSTYLE:ON
199
200 @Override
201 public int compareTo(final Level other) {
202 return intLevel < other.intLevel ? -1 : (intLevel > other.intLevel ? 1 : 0);
203 }
204
205 @Override
206 public boolean equals(final Object other) {
207 return other instanceof Level && other == this;
208 }
209
210 public Class<Level> getDeclaringClass() {
211 return Level.class;
212 }
213
214 @Override
215 public int hashCode() {
216 return this.name.hashCode();
217 }
218
219 /**
220 * Gets the symbolic name of this Level. Equivalent to calling {@link #toString()}.
221 *
222 * @return the name of this Level.
223 */
224 public String name() {
225 return this.name;
226 }
227
228 @Override
229 public String toString() {
230 return this.name;
231 }
232
233 /**
234 * Retrieves an existing Level or creates on if it didn't previously exist.
235 *
236 * @param name The name of the level.
237 * @param intValue The integer value for the Level. If the level was previously created this value is ignored.
238 * @return The Level.
239 * @throws java.lang.IllegalArgumentException if the name is null or intValue is less than zero.
240 */
241 public static Level forName(final String name, final int intValue) {
242 final Level level = LEVELS.get(name);
243 if (level != null) {
244 return level;
245 }
246 try {
247 return new Level(name, intValue);
248 } catch (final IllegalStateException ex) {
249 // The level was added by something else so just return that one.
250 return LEVELS.get(name);
251 }
252 }
253
254 /**
255 * Return the Level associated with the name or null if the Level cannot be found.
256 *
257 * @param name The name of the Level.
258 * @return The Level or null.
259 */
260 public static Level getLevel(final String name) {
261 return LEVELS.get(name);
262 }
263
264 /**
265 * Converts the string passed as argument to a level. If the conversion fails, then this method returns
266 * {@link #DEBUG}.
267 *
268 * @param sArg The name of the desired Level.
269 * @return The Level associated with the String.
270 */
271 public static Level toLevel(final String sArg) {
272 return toLevel(sArg, Level.DEBUG);
273 }
274
275 /**
276 * Converts the string passed as argument to a level. If the conversion fails, then this method returns the value of
277 * <code>defaultLevel</code>.
278 *
279 * @param name The name of the desired Level.
280 * @param defaultLevel The Level to use if the String is invalid.
281 * @return The Level associated with the String.
282 */
283 public static Level toLevel(final String name, final Level defaultLevel) {
284 if (name == null) {
285 return defaultLevel;
286 }
287 final Level level = LEVELS.get(toUpperCase(name.trim()));
288 return level == null ? defaultLevel : level;
289 }
290
291 private static String toUpperCase(final String name) {
292 return name.toUpperCase(Locale.ENGLISH);
293 }
294
295 /**
296 * Return an array of all the Levels that have been registered.
297 *
298 * @return An array of Levels.
299 */
300 public static Level[] values() {
301 final Collection<Level> values = Level.LEVELS.values();
302 return values.toArray(new Level[values.size()]);
303 }
304
305 /**
306 * Return the Level associated with the name.
307 *
308 * @param name The name of the Level to return.
309 * @return The Level.
310 * @throws java.lang.NullPointerException if the Level name is {@code null}.
311 * @throws java.lang.IllegalArgumentException if the Level name is not registered.
312 */
313 public static Level valueOf(final String name) {
314 Objects.requireNonNull(name, "No level name given.");
315 final String levelName = toUpperCase(name.trim());
316 final Level level = LEVELS.get(levelName);
317 if (level != null) {
318 return level;
319 }
320 throw new IllegalArgumentException("Unknown level constant [" + levelName + "].");
321 }
322
323 /**
324 * Returns the enum constant of the specified enum type with the specified name. The name must match exactly an
325 * identifier used to declare an enum constant in this type. (Extraneous whitespace characters are not permitted.)
326 *
327 * @param enumType the {@code Class} object of the enum type from which to return a constant
328 * @param name the name of the constant to return
329 * @param <T> The enum type whose constant is to be returned
330 * @return the enum constant of the specified enum type with the specified name
331 * @throws java.lang.IllegalArgumentException if the specified enum type has no constant with the specified name, or
332 * the specified class object does not represent an enum type
333 * @throws java.lang.NullPointerException if {@code enumType} or {@code name} are {@code null}
334 * @see java.lang.Enum#valueOf(Class, String)
335 */
336 public static <T extends Enum<T>> T valueOf(final Class<T> enumType, final String name) {
337 return Enum.valueOf(enumType, name);
338 }
339
340 // for deserialization
341 protected Object readResolve() {
342 return Level.valueOf(this.name);
343 }
344 }