I have those specific requirements :
The only solution I've found so far is to use have 5 markers :
final Marker traceMarker = MarkerFactory.getMarker("TRACE");
final Marker debugMarker = MarkerFactory.getMarker("DEBUG");
final Marker infoMarker = MarkerFactory.getMarker("INFO");
final Marker warnMarker = MarkerFactory.getMarker("WARN");
final Marker errorMarker = MarkerFactory.getMarker("ERROR");
final Marker fatalMarker = MarkerFactory.getMarker("FATAL");
And log passing the marker everytime :
logger.info(infoMarker, "!!! INFO World !!!");
logger.error(errorMarker, "!!! ERROR World !!!");
logger.error(fatalMarker, "!!! FATAL World !!!");
And modify the PatternLayout to totally remove the LogLevel and always log the Marker, such as :
PatternLayout:
Pattern: "%d{ISO8601_BASIC} %marker [%t] %logger{3.} - %msg%n"
I kind of think this solution is a hack... It would also remove the log level of any external library using the LogLevel the right way.
Summary : This solution isn't a good solution.
UPDATE : I tried another solution, writing a RewritePolicy :
public class FatalRewritePolicy implements RewritePolicy {
public static final String FATAL = "FATAL";
@Override
public LogEvent rewrite(final LogEvent logEvent) {
final Marker marker = logEvent.getMarker();
if (marker == null)
return logEvent;
// Log Level is final in the LogEvent, there's no way we can modify it.
Level level = logEvent.getLevel();
return null;
}
}
There seems to be no way to change the LogLevel of a LogEvent in Log4j2 (which make sense).
Summary : Still no solution.
Here is what I did for log4j which I also recommend for log4j2....
Write your own custom slf4j static binder aka bridge. This requires a little work but is well worth it for a variety of complicated reasons 1 that I will blog about one day.
Here is what you do.
if (marker.contains("FATAL")) fatal(....);
log4j-slf4j-impl
from your project and use your new code in place.<soap-box-rant>
I honestly think slf4j is severely flawed because
logger.fatal(...)
.fatal
is missing. It is not 80/20. Markers are for the 1% and fatal
is for the 99%.</soap-box-rant>
1 one of them being able to actually be part of the boot process of your logging framework opposed to hard to determine almost arbitrary static initialization
Here's the closest working solution I came with some colleagues :
Here's the Java sample :
Marker fatal = MarkerFactory.getMarker("FATAL");
// Usage example
final Logger logger = LoggerFactory.getLogger(FatalLogger.class);
logger.log(fatal, "this is a fatal message");
// Log sample :
20150514T115144,279 FATAL [main] FatalLogger - this is a fatal message
Here's the YAML sample :
Configuration:
status: debug
Appenders:
RandomAccessFile:
- name: APPLICATION_APPENDER
fileName: logs/application.log
PatternLayout:
Pattern: "%d{ISO8601_BASIC} %-5level %msg%n"
- name: FATAL_APPENDER
fileName: logs/application.log
PatternLayout:
Pattern: "%d{ISO8601_BASIC} FATAL %msg%n"
Routing:
name: ROUTING_APPENDER
Routes:
pattern: "$${marker:}"
Route:
- key: FATAL
ref: FATAL_APPENDER
- ref: APPLICATION_APPENDER #DefaultRoute
Loggers:
Root:
level: trace
AppenderRef:
- ref: ROUTING_APPENDER
I know the question is for log4j. I found this page when looking in relation to logback. Here's what sl4j recommends: https://www.slf4j.org/faq.html#fatal.
Marker
is not really what you want here. Marker
is for "enriching" log messages, making them more easily searchable. You are trying to change the log level/priority, which is a little different.
You're using logger.error()
which will log the message as an ERROR
level.
If there is no FATAL
level pre-defined (usually there is, such as logger.fatal()
), then use the generic logger.log()
which allows you to specify the log level.
logger.fatal(yourMessage);
OR
logger.log(priorityLevel, yourMessage);
From the SLF4J website:
The Marker interface, part of the org.slf4j package, renders the FATAL level largely redundant. If a given error requires attention beyond that allocated for ordinary errors, simply mark the logging statement with a specially designated marker which can be named "FATAL" or any other name to your liking.
http://www.slf4j.org/faq.html#fatal
So, with SLF4J, it is not possible to have a FATAL
log level. I strongly disagree with the rationale behind this decision, but it is what it is.
You could add "FATAL" at the beginning of your message. For example:
LOGGER.error("FATAL: database connection lost.");
You do lose some things, like filtering based on level, but this may be ok for many people, especially since you're unlikely to filter out FATAL statements (debug and trace, sure).