blob: e633b52f44a304472addbc4b1573ae69379e77e1 [file] [log] [blame]
////
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
////
= Custom Log Levels
Remko Popma <rpopma@apache.org>
[[top]]
[#DefiningLevelsInCode]
== Defining Custom Log Levels in Code
Log4j 2 supports custom log levels. Custom log levels can be defined in
code or in configuration. To define a custom log level in code, use the
`Level.forName()` method. This method creates a new level for the
specified name. After a log level is defined you can log messages at
this level by calling the `Logger.log()` method and passing the custom log
level:
[source,java]
----
// This creates the "VERBOSE" level if it does not exist yet.
final Level VERBOSE = Level.forName("VERBOSE", 550);
final Logger logger = LogManager.getLogger();
logger.log(VERBOSE, "a verbose message"); // use the custom VERBOSE level
// Create and use a new custom level "DIAG".
logger.log(Level.forName("DIAG", 350), "a diagnostic message");
// Use (don't create) the "DIAG" custom level.
// Only do this *after* the custom level is created!
logger.log(Level.getLevel("DIAG"), "another diagnostic message");
// Using an undefined level results in an error: Level.getLevel() returns null,
// and logger.log(null, "message") throws an exception.
logger.log(Level.getLevel("FORGOT_TO_DEFINE"), "some message"); // throws exception!
----
When defining a custom log level, the `intLevel` parameter (550 and 350
in the example above) determines where the custom level exists in
relation to the standard levels built-in to Log4j 2. For reference, the
table below shows the `intLevel` of the built-in log levels.
.Standard log levels built-in to Log4j
[cols=",>",options="header"]
|========================
|Standard Level |intLevel
|OFF |0
|FATAL |100
|ERROR |200
|WARN |300
|INFO |400
|DEBUG |500
|TRACE |600
|ALL |`Integer.MAX_VALUE`
|========================
[#DefiningLevelsInConfiguration]
== Defining Custom Log Levels in Configuration
Custom log levels can also be defined in configuration. This is
convenient for using a custom level in a logger filter or an appender
filter. Similar to defining log levels in code, a custom level must be
defined first, before it can be used. If a logger or appender is
configured with an undefined level, that logger or appender will be
invalid and will not process any log events.
The *CustomLevel* configuration element creates a custom level.
Internally it calls the same `Level.forName()` method discussed above.
.CustomLevel Parameters
[cols="m,,4",options="header"]
|===
|Parameter Name |Type |Description
|name
|String
|The name of the custom level. Note that level names are
case sensitive. The convention is to use all upper-case names.
|intLevel
|integer
|Determines where the custom level exists in relation
to the standard levels built-in to Log4j 2 (see the table above).
|===
The following example shows a configuration that defines some custom log
levels and uses a custom log level to filter log events sent to the
console.
[source,xml]
----
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<!-- Define custom levels before using them for filtering below. -->
<CustomLevels>
<CustomLevel name="DIAG" intLevel="350" />
<CustomLevel name="NOTICE" intLevel="450" />
<CustomLevel name="VERBOSE" intLevel="550" />
</CustomLevels>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d %-7level %logger{36} - %msg%n"/>
</Console>
<File name="MyFile" fileName="logs/app.log">
<PatternLayout pattern="%d %-7level %logger{36} - %msg%n"/>
</File>
</Appenders>
<Loggers>
<Root level="trace">
<!-- Only events at DIAG level or more specific are sent to the console. -->
<AppenderRef ref="Console" level="diag" />
<AppenderRef ref="MyFile" level="trace" />
</Root>
</Loggers>
</Configuration>
----
[#StandardLoggerInterface]
== Convenience Methods for the Built-in Log Levels
The built-in log levels have a set of convenience methods on the Logger
interface that makes them easier to use. For example, the Logger
interface has 24 `debug()` methods that support the DEBUG level:
[source,java]
----
// convenience methods for the built-in DEBUG level
debug(Marker, Message)
debug(Marker, Message, Throwable)
debug(Marker, Object)
debug(Marker, Object, Throwable)
debug(Marker, String)
debug(Marker, String, Object...)
debug(Marker, String, Throwable)
debug(Message)
debug(Message, Throwable)
debug(Object)
debug(Object, Throwable)
debug(String)
debug(String, Object...)
debug(String, Throwable)
// lambda support methods added in 2.4
debug(Marker, MessageSupplier)
debug(Marker, MessageSupplier, Throwable)
debug(Marker, String, Supplier<?>...)
debug(Marker, Supplier<?>)
debug(Marker, Supplier<?>, Throwable)
debug(MessageSupplier)
debug(MessageSupplier, Throwable)
debug(String, Supplier<?>...)
debug(Supplier<?>)
debug(Supplier<?>, Throwable)
----
Similar methods exist for the other built-in levels. Custom levels, in
contrast, need to pass in the log level as an extra parameter.
[source,java]
----
// need to pass the custom level as a parameter
logger.log(VERBOSE, "a verbose message");
logger.log(Level.forName("DIAG", 350), "another message");
----
It would be nice to have the same ease of use with custom levels, so
that after declaring the custom VERBOSE/DIAG levels, we could use code
like this:
[source,java]
----
// nice to have: descriptive methods and no need to pass the level as a parameter
logger.verbose("a verbose message");
logger.diag("another message");
logger.diag("java 8 lambda expression: {}", () -> someMethod());
----
The standard Logger interface cannot provide convenience methods for
custom levels, but the next few sections introduce a code generation
tool to create loggers that aim to make custom levels as easy to use as
built-in levels.
[#AddingOrReplacingLevels]
== Adding or Replacing Log Levels
We assume that most users want to _add_ custom level methods to the
Logger interface, in addition to the existing `trace()`, `debug()`, `info()`,
... methods for the built-in log levels.
There is another use case, Domain Specific Language loggers, where we
want to _replace_ the existing `trace()`, `debug()`, `info()`, ... methods
with all-custom methods.
For example, for medical devices we could have only `critical()`,
`warning()`, and `advisory()` methods. Another example could be a game
that has only `defcon1()`, `defcon2()`, and `defcon3()` levels.
If it were possible to hide existing log levels, users could customize
the Logger interface to match their requirements. Some people may not
want to have a FATAL or a TRACE level, for example. They would like to
be able to create a custom Logger that only has `debug()`, `info()`, `warn()`
and `error()` methods.
[#CustomLoggers]
== Generating Source Code for a Custom Logger Wrapper
Common Log4j usage is to get an instance of the `Logger` interface from
the `LogManager` and call the methods on this interface. However, the
custom log Levels are not known in advance, so Log4j cannot provide an
interface with convenience methods for these custom log Levels.
To solve this, Log4j ships with a tool that generates source code for a
Logger wrapper. The generated wrapper class has convenience methods for
each custom log level, making custom levels just as easy to use as the
built-in levels.
There are two flavors of wrappers: ones that _*extend*_ the Logger API
(adding methods to the built-in levels) and ones that _*customize*_ the
Logger API (replacing the built-in methods).
When generating the source code for a wrapper class, you need to
specify:
* the fully qualified name of the class to generate
* the list of custom levels to support and their `intLevel` relative
strength
* whether to extend `Logger` (and keep the existing built-in methods) or
have only methods for the custom log levels
You would then include the generated source code in the project where
you want to use custom log levels.
[#ExampleUsage]
== Example Usage of a Generated Logger Wrapper
Here is an example of how one would use a generated logger wrapper with
custom levels DIAG, NOTICE and VERBOSE:
[source,java]
----
// ExtLogger is a generated logger wrapper
import com.mycompany.myproject.ExtLogger;
public class MyService {
// instead of Logger logger = LogManager.getLogger(MyService.class):
private static final ExtLogger logger = ExtLogger.create(MyService.class);
public void demoExtendedLogger() {
// ...
logger.trace("the built-in TRACE level");
logger.verbose("a custom level: a VERBOSE message");
logger.debug("the built-in DEBUG level");
logger.notice("a custom level: a NOTICE message");
logger.info("the built-in INFO level");
logger.diag("a custom level: a DIAG message");
logger.warn("the built-in WARN level");
logger.error("the built-in ERROR level");
logger.fatal("the built-in FATAL level");
logger.notice("java 8 lambda expression only executed if NOTICE is enabled: {}", () -> someMethod());
// ...
}
...
}
----
[#CodeGen]
== Generating Extended Loggers
Use the following command to generate a logger wrapper that adds methods
to the built-in ones:
[source,sh,subs="attributes"]
----
java -cp log4j-core-{Log4jReleaseVersion}.jar org.apache.logging.log4j.core.tools.ExtendedLoggerGenerator \
com.mycomp.ExtLogger DIAG=350 NOTICE=450 VERBOSE=550 > com/mycomp/ExtLogger.java
----
This will generate source code for a logger wrapper that has the
convenience methods for the built-in levels _as well as_ the specified
custom levels. The tool prints the generated source code to the console.
By appending " > _filename_" the output can be redirected to a file.
NOTE: Prior to log4j-2.9, this tool was an inner class
`Generate$ExtendedLogger`. +
Under the bash shell on Unix/Mac/Linux the dollar character $ needs to
be escaped, so the class name should be between single quotes
'org.apache.logging.log4j.core.tools.Generate$ExtendedLogger’.
== Generating Custom Loggers
Use the following command to generate a logger wrapper that hides the
built-in levels and has only custom levels:
[source,sh,subs="attributes"]
----
java -cp log4j-core-{Log4jReleaseVersion}.jar org.apache.logging.log4j.core.tools.CustomLoggerGenerator \
com.mycomp.MyLogger DEFCON1=350 DEFCON2=450 DEFCON3=550 > com/mycomp/MyLogger.java
----
This will generate source code for a logger wrapper that _only_ has
convenience methods for the specified custom levels, _not_ for the
built-in levels. The tool prints the generated source code to the
console. By appending " > _filename_" the output can be redirected to a
file.
NOTE: Prior to log4j-2.9, this tool was an inner class `Generate$ExtendedLogger`.
Under the bash shell on Unix/Mac/Linux the dollar character $ needs to
be escaped, so the class name should be between single quotes
'org.apache.logging.log4j.core.tools.Generate$CustomLogger’.