Markers
Markers allow to tag log statements with a
Marker
object, labeling them as belonging to a specific type.
For example, developers can use markers to tag log statements related to a particular subsystem or functionality.
By using markers, it is possible to filter log statements based on the Marker
and display only those log statements that are of interest, such as those
related to XML processing or SQL queries.
Markers offer more fine-grained control over log filtering beyond log levels or package names.
Creating Markers
Simple markers
To create a Marker
, create a field in your class using the MarkerManager.getMarker()
method:
private static final Marker SQL_MARKER = MarkerManager.getMarker("SQL");
Since a Marker
is reusable across multiple log statements, storing it in a static final
field makes it a constant.
Once created, use it as the first argument in the log statement:
LOGGER.debug(SQL_MARKER, "SELECT * FROM {}", table);
If you use the configuration example below, one can see the following log statement on your console:
10:42:30.982 (SQL) SELECT * FROM my_table
Parent and child markers
A marker can have zero or more parent markers, allowing for a hierarchy of markers.
To create such a hierarchy, you must use the addParents()
method on the Marker
object after you make the child marker.
private static final Marker QUERY_MARKER =
MarkerManager.getMarker("SQL_QUERY").addParents(SQL_MARKER);
private static final Marker UPDATE_MARKER =
MarkerManager.getMarker("UPDATE").addParents(SQL_MARKER);
Child markers do not differ from simple markers; one must pass them on as the first argument of a logging call.
LOGGER.debug(QUERY_MARKER, "SELECT * FROM {}", table);
LOGGER.debug(UPDATE_MARKER, "UPDATE {} SET {} = {}", table, column, value);
Messages marked with children’s markers behave as if they were both marked with the children’s marker and all its parents. If you use the configuration example below, you’ll see the following log statement on your console:
10:42:30.982 (SQL_QUERY[ SQL ]) SELECT * FROM my_table
10:42:30.982 (SQL_UPDATE[ SQL ]) UPDATE my_table SET column = value
Pitfalls
It is important to note that marker names must be unique, as Log4j registers them permanently by name. Developers are advised to avoid generic marker names, as they may conflict with those provided by third parties.
For technical reasons the
Marker.setParents(Marker…)
method can be called at runtime to modify the list of parents of the current marker.
However, we discourage such a practice and advise you to only use the method at initialization time.
It is also worth noting that markers without parents are more efficient to evaluate than markers with multiple parents. It is generally a good idea to avoid complex hierarchies of markers where possible.
Configuring filtering
Developers can use markers to filter the log statements delivered to log files. Marker processing is supported at least by Logback and the Log4j Core logging implementations. We will provide a sample configuration for both these backends.
Log4j Core
To filter messages by marker, you need to add
MarkerFilter
to your configuration file.
For example, you can use the configuration below to redirect all SQL-related logs to the SQL_LOG
appender,
regardless of the level of the events:
-
XML
-
JSON
-
YAML
-
Properties
log4j2.xml
<Appenders>
<Console name="SQL_LOG">
<PatternLayout pattern="%d{HH:mm:ss.SSS} (%marker) %m%n"/>
</Console>
</Appenders>
<MarkerFilter marker="SQL"
onMatch="ACCEPT"
onMismatch="NEUTRAL"/>(1)
<Loggers>
<Root level="INFO">
<AppenderRef ref="SQL_LOG">
<MarkerFilter marker="SQL"/>(2)
</AppenderRef>
</Root>
</Loggers>
log4j2.json
"Appenders": {
"Console": {
"name": "SQL_LOG",
"PatternLayout": {
"pattern": "%d{HH:mm:ss.SSS} (%marker) %m%n"
}
}
},
"MarkerFilter": { (1)
"marker": "SQL",
"onMatch": "ACCEPT",
"onMismatch": "NEUTRAL"
},
"Loggers": {
"Root": {
"level": "INFO",
"AppenderRef": {
"ref": "SQL_LOG",
"MarkerFilter": { (2)
"marker": "SQL"
}
}
}
}
log4j2.yaml
Appenders:
Console:
name: "SQL_LOG"
PatternLayout:
pattern: "%d{HH:mm:ss.SSS} (%marker) %m%n"
MarkerFilter: (1)
marker: "SQL"
onMatch: "ACCEPT"
onMismatch: "NEUTRAL"
Loggers:
Root:
level: "INFO"
AppenderRef:
ref: "SQL_LOG"
MarkerFilter: (2)
marker: "SQL"
log4j2.properties
appender.0.type = Console
appender.0.name = SQL_LOG
appender.0.layout.type = PatternLayout
appender.0.layout.pattern = %d{HH:mm:ss.SSS} (%marker) %m%n
(1)
filter.0.type = MarkerFilter
filter.0.marker = SQL
filter.0.onMatch = ACCEPT
filter.0.onMismatch = NEUTRAL
rootLogger.level = INFO
rootLogger.appenderRef.0.ref = SQL_LOG
(2)
rootLogger.appenderRef.0.filter.type = MarkerFilter
rootLogger.appenderRef.0.filter.marker = SQL
1 | Accepts all events marked with SQL regardless of their level, |
2 | Only allow events marked with SQL or one of its children to be sent to the SQL_LOG appender. |
Logback
Logback differentiates two kinds of filters: TurboFilter
s, which are applied before a log event is created, and
Filter
s, which are applied only when a log event reaches an appender.
See Logback filters for more information.
You can use a combination of MarkerFilter
, EvaluatorFilter
and OnMarkerEvaluator
to redirect all messages marked with SQL
to a specific appender, regardless of their level.
In order to do that, you can use a configuration as below:
logback.xml
<turboFilter class="ch.qos.logback.classic.turbo.MarkerFilter"> (1)
<Marker>SQL</Marker>
<OnMatch>ACCEPT</OnMatch>
</turboFilter>
<appender name="SQL_LOG" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} (%marker) %m%n</pattern>
</encoder>
<filter class="ch.qos.logback.core.filter.EvaluatorFilter"> (2)
<evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
<marker>SQL</marker>
</evaluator>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<root level="INFO">
<appender-ref ref="SQL_LOG"/>
</root>
1 | Accepts all events marked with SQL regardless of their level, |
2 | Only allow events marked with SQL or one of its children to be sent to the SQL_LOG appender.git |
Complete example
To try the examples on this page:
-
add MarkerExample.java to the
src/main/java/example
folder of your project, -
if your project uses Log4j Core add log4j2.xml to the
src/main/resources
folder of your project. -
if your project uses Logback add logback.xml to the
src/main/resources
folder of your project.