slf4j.jar日志使用详解--项目开发中经常使用
2015-07-29 11:50
330 查看
What about Maven transitive dependencies?
As an author of a library built with Maven, you might want to test your application using a binding, say slf4j-log4j12 or logback-classic, without forcing log4j or logback-classic as a dependency upon your users. This is rather easy to accomplish.
Given that your library's code depends on the SLF4J API, you will need to declare slf4j-api as a compile-time (default scope) dependency.
Limiting the transitivity of the SLF4J binding used in your tests can be accomplished by declaring the scope of the SLF4J-binding dependency as "test". Here is an example:
Thus, as far as your users are concerned you are exporting slf4j-api as a transitive dependency of your library, but not any SLF4J-binding or any underlying logging system.
Note that as of SLF4J version 1.6, in the absence of an SLF4J binding, slf4j-api will default to a no-operation implementation.
How do I exclude commons-logging as a Maven dependency?
alternative 1) explicit exclusion
Many software projects using Maven declare commons-logging as a dependency. Therefore, if you wish to migrate to SLF4J or use jcl-over-slf4j, you would need to exclude commons-logging in all of your project's dependencies which transitively depend on commons-logging. Dependency
exclusion is described in the Maven documentation. Excluding commons-logging explicitly for multiple dependencies distributed on several pom.xml files can be a cumbersome and a relatively error prone process.
alternative 2) provided scope
Commons-logging can be rather simply and conveniently excluded as a dependency by declaring it in the provided scope within the pom.xml file of your project. The actual commons-logging classes would be provided by jcl-over-slf4j. This translates into
the following pom file snippet:
The first dependency declaration essentially states that commons-logging will be "somehow" provided by your environment. The second declaration includes jcl-over-slf4j into your project. As jcl-over-slf4j is a perfect binary-compatible replacement for commons-logging,
the first assertion becomes true.
Unfortunately, while declaring commons-logging in the provided scope gets the job done, your IDE, e.g. Eclipse, will still place commons-logging.jar on your project's class path as seen by your IDE. You would need to make sure that jcl-over-slf4j.jar is
visible before commons-logging.jar by your IDE.
alternative 3) empty artifacts
An alternative approach is to depend on an empty commons-logging.jar artifact. This clever approach
first was imagined and initially supported by Erik van Oosten.
The empty artifact is available from a http://version99.qos.ch a high-availability Maven repository, replicated on several
hosts located in different geographical regions.
The following declaration adds the version99 repository to the set of remote repositories searched by Maven. This repository contains empty artifacts for commons-logging and log4j. By the way, if you use the version99 repository, please drop us a line at <version99
AT qos.ch>.
Declaring version 99-empty of commons-logging in the
the commons-logging exclusion problem. The classes for commons-logging will be provided by jcl-over-slf4j. The following lines declare commons-logging version 99-empty (in the dependency management section) and declare jcl-over-slf4j as a dependency.
Why don't the printing methods in the Logger interface accept message of type
Object, but only messages of type String?
In SLF4J 1.0beta4, the printing methods such as debug(), info(), warn(), error() in the Logger interface were
modified so as to accept only messages of type String instead of Object.
Thus, the set of printing methods for the DEBUG level became:
Previously, the first argument in the above methods was of type
This change enforces the notion that logging systems are about decorating and handling messages of type String, and not any arbitrary type (Object).
Just as importantly, the new set of method signatures offer a clearer differentiation between the overloaded methods whereas previously the choice of the invoked method due to Java overloading rules were not always easy to follow.
It was also easy to make mistakes. For example, previously it was legal to write:
Unfortunately, the above call did not print the stack trace of the exception. Thus, a potentially crucial piece of information could be lost. When the first parameter is restricted to be of type String, then only the method
can be used to log exceptions. Note that this method ensures that every logged exception is accompanied with a descriptive message.
Can I log an exception without an accompanying message?
In short, no.
If
You might legitimately argue that not all exceptions have a meaningful message to accompany them. Moreover, a good exception should already contain a self explanatory description. The accompanying message may therefore be considered redundant.
While these are valid arguments, there are three opposing arguments also worth considering. First, on many, albeit not all occasions, the accompanying message can convey useful information nicely complementing the description contained in the exception. Frequently,
at the point where the exception is logged, the developer has access to more contextual information than at the point where the exception is thrown. Second, it is not difficult to imagine more or less generic messages, e.g. "Exception caught", "Exception follows",
that can be used as the first argument for
line would look inconsistent without a message.
In short, if the user were allowed to log an exception without an accompanying message, it would be the job of the logging system to invent a message. This is actually what the throwing(String
sourceClass, String sourceMethod, Throwable thrown) method in java.util.logging package does. (It decides on its own that accompanying message is the string "THROW".)
It may initially appear strange to require an accompanying message to log an exception. Nevertheless, this is common practice in all log4j derived systems such as java.util.logging, logkit, etc. and of course log4j itself. It seems that the current
consensus considers requiring an accompanying message as a good a thing (TM).
What is the fastest way of (not) logging?
SLF4J supports an advanced feature called parameterized logging which can significantly boost logging performance for disabled logging statement.
For some Logger
incurs the cost of constructing the message parameter, that is converting both integer
This, regardless of whether the message will be logged or not.
One possible way to avoid the cost of parameter construction is by surrounding the log statement with a test. Here is an example.
This way you will not incur the cost of parameter construction if debugging is disabled for
the logger is enabled or not, twice: once in
1% of the time it takes to actually log a statement.
Better yet, use parameterized messages
There exists a very convenient alternative based on message formats. Assuming
After evaluating whether to log or not, and only if the decision is affirmative, will the logger implementation format the message and replace the '{}' pair with the string value of
form does not incur the cost of parameter construction in case the log statement is disabled.
The following two lines will yield the exact same output. However, the second form will outperform the first form by a factor of at least 30, in case of a disabled logging statement.
A two argument variant is also available.
For example, you can write:
If three or more arguments need to be passed, you can make use of the
the printing methods. For example, you can write:
This form incurs the hidden cost of construction of an Object[] (object array) which is usually very small. The one and two argument variants do not incur this hidden cost and exist solely for this reason (efficiency). The slf4j-api would be smaller/cleaner
with only the Object... variant.
Array type arguments, including multi-dimensional arrays, are also supported.
SLF4J uses its own message formatting implementation which differs from that of the Java platform. This is justified by the fact that SLF4J's implementation performs about 10 times faster but at the cost of being non-standard and less flexible.
Escaping the "{}" pair
The "{}" pair is called the formatting anchor. It serves to designate the location where arguments need to be substituted within the message pattern.
SLF4J only cares about the formatting anchor, that is the '{' character immediately followed by '}'. Thus, in case your message contains the '{' or the '}' character, you do not have to do anything special unless the '}' character immediately follows
'}'. For example,
which will print as "Set {1,2} differs from 3".
You could have even written,
which would have printed as "Set {1,2} differs from {3}".
In the extremely rare case where the the "{}" pair occurs naturally within your text and you wish to disable the special meaning of the formatting anchor, then you need to escape the '{' character with '\', that is the backslash character. Only the '{' character
should be escaped. There is no need to escape the '}' character. For example,
will print as "Set {} differs from 3". Note that within Java code, the backslash character needs to be written as '\\'.
In the rare case where the "\{}" occurs naturally in the message, you can double escape the formatting anchor so that it retains its original meaning. For example,
will print as "File name is C:\file.zip".
How can I log the string contents of a single (possibly complex) object?
In relatively rare cases where the message to be logged is the string form of an object, then the parameterized printing method of the appropriate level can be used. Assuming
complexity, for a log statement of level DEBUG, you can write:
The logging system will invoke
will be advantageously avoided.
Why doesn't the
for the FATAL level?
The Marker interface, part of the
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.
Here is an example,
While markers are part of the SLF4J API, only logback supports markers off the shelf. For example, if you add the
add marker data to its output. Marker data can be used to filter messages or even trigger an
outgoing email at the end of an individual transaction.
In combination with logging frameworks such as log4j and java.util.logging which do not support markers, marker data will be silently ignored.
Markers add a new dimension with infinite possible values for processing log statements compared to five values, namely ERROR, WARN, INFO, DEBUG and TRACE, allowed by levels. At present time, only logback supports marker data. However, nothing prevents other
logging frameworks from making use of marker data.
Why was the TRACE level introduced only in SLF4J version 1.4.0?
The addition of the TRACE level has been frequently and hotly debated request. By studying various projects, we observed that the TRACE level was used to disable logging output from certain classes without needing to configure logging for those classes.
Indeed, the TRACE level is by default disabled in log4j and logback as well most other logging systems. The same result can be achieved by adding the appropriate directives in configuration files.
Thus, in many of cases the TRACE level carried the same semantic meaning as DEBUG. In such cases, the TRACE level merely saves a few configuration directives. In other, more interesting occasions, where TRACE carries a different meaning than DEBUG, Marker objects
can be put to use to convey the desired meaning. However, if you can't be bothered with markers and wish to use a logging level lower than DEBUG, the TRACE level can get the job done.
Note that while the cost of evaluating a disabled log request is in the order of a few
request might be evaluated millions of times. If the log request is enabled, then it will overwhelm the target destination with massive output. If the request is disabled, it will waste resources.
In short, although we still discourage the use of the TRACE level because alternatives exist or because in many cases log requests of level TRACE are wasteful, given that people kept asking for it, we decided to bow to popular demand.
Does the SLF4J logging API support I18N (internationalization)?
Yes, as of version 1.5.9, SLF4J ships with a package called
logging support as a thin layer built upon the CAL10N API.
Is it possible to retrieve loggers without going through the static methods in
Yes.
The
in
However, nothing prevents you from using your own
is using by invoking the
Thus, if SLF4J binding conventions do not fit your needs, or if you need additional flexibility, then do consider using the
In the presence of an exception/throwable, is it possible to parameterize a logging
statement?
Yes, as of SLF4J 1.6.0, but not in previous versions. The SLF4J API supports parametrization in the presence of an exception, assuming the exception is the last parameter. Thus,
will print the
method taking a String and two Object arguments. SLF4J, in accordance with the programmer's most probable intention, will interpret
In SLF4J versions prior to 1.6.0, the
If the exception is not the last argument, it will be treated as a plain object and its stack trace will NOT be printed. However, such situations should not occur in practice.
How do I make my logging framework SLF4J compatible?
Adding supporting for the SLF4J is surprisingly easy. Essentially, you coping an existing binding and tailoring it a little (as explained below) does the trick.
Assuming your logging system has notion of a logger, called say
Refer to slf4j-jcl, slf4j-jdk14, and slf4j-log4j12 modules for examples of adapters.
Once you have written an appropriate adapter, say
This factory should return instances
Once you have the adapter, namely
so that it returns an new instance of
For Marker or MDC support, you could use the one of the existing NOP implementations.
In summary, to create an SLF4J binding for your logging system, follow these steps:
start with a copy of an existing module,
create an adapter between your logging system and
create a factory for the adapter created in the previous step,
modify
How can my logging system add support for the
Markers constitute a revolutionary concept which is supported by logback but not other existing logging systems. Consequently, SLF4J conforming logging systems are allowed to ignore marker data passed by the user.
However, even though marker data may be ignored, the user must still be allowed to specify marker data. Otherwise, users would not be able to switch between logging systems that support markers and those that do not.
The
taking marker data simply invoke the corresponding method without the Marker argument, discarding any Marker data passed as argument. Your SLF4J adapters can extend
the methods in
How does SLF4J's version check mechanism work?
The version check performed by SLF4J API during its initialization is an elective process. Conforming SLF4J implementations may choose not to participate, in which case, no version check will be performed.
However, if an SLF4J implementation decides to participate, than it needs to declare a variable called REQUESTED_API_VERSION within its copy of the
be equal to the version of the slf4j-api.jar it is compiled with. If the implementation is upgraded to a newer version of slf4j-api, than you also need to update the value of REQUESTED_API_VERSION.
For each version, SLF4J API maintains a list of compatible versions. SLF4J will emit a version mismatch warning only if the requested version is not found in the compatibility list. So even if your SLF4J binding has a different release schedule than SLF4J,
assuming you update the SLF4J version you use every 6 to 12 months, you can still participate in the version check without incurring a mismatch warning. For example, logback has a different release schedule but still participates in version checks.
As of SLF4J 1.5.5, all bindings shipped within the SLF4J distribution, e.g. slf4j-log4j12, slf4j-simple and slf4j-jdk14, declare the REQUESTED_API_VERSION field with a value equal to their SLF4J version. It follows that, for example if slf4j-simple-1.5.8.jar
is mixed with slf4j-api-1.6.0.jar, given that 1.5.8 is not on the compatibility list of SLF4J version 1.6.x, a version mismatch warning will be issued.
Note that SLF4J versions prior to 1.5.5 did not have a version check mechanism. Only slf4j-api-1.5.5.jar and later can emit version mismatch warnings.
Should Logger members of a class be declared as static?
We
Here is a summary of the pros and cons of each approach.
Static logger members cost a single variable reference for all instances of the class whereas an instance logger member will cost a variable reference for every instance of the class. For simple classes instantiated thousands of times there might be a noticeable
difference.
However, more recent logging systems, e.g log4j or logback, support a distinct logger context for each application running in the application server. Thus, even if a single copy of log4j.jar or logback-classic.jar is deployed in the server,
the logging system will be able to differentiate between applications and offer a distinct logging environment for each application.
More specifically, each time a logger is retrieved by invoking
within the same application retrieving a logger by a given name will always return the same logger. For a given name, a different logger will be returned only for different applications.
If the logger is static, then it will only be retrieved once when the hosting class is loaded into memory. If the hosting class is used in only in one application, there is not much to be concerned about. However, if the hosting class is shared between several
applications, then all instances of the shared class will log into the context of the application which happened to first load the shared class into memory - hardly the behavior expected by the user.
Unfortunately, for non-native implementations of the SLF4J API, namely with slf4j-log4j12, log4j's repository selector will not be able to do its job properly because slf4j-log4j12, a non-native SLF4J binding, will store logger instances in a map, short-circuiting
context-dependent logger retrieval. For native SLF4J implementations, such as logback-classic, repository selectors will work as expected.
The Apache Commons wiki contains an informative article covering the same question.
Logger serialization
Contrary to static variables, instance variables are serialized by default. As of SLF4J version 1.5.3, logger instances survive serialization. Thus, serialization of the host class no longer requires any special action, even when loggers are declared as instance
variables. In previous versions, logger instances needed to be declared as
Summary
In summary, declaring logger members as static variables requires less CPU time and have a slightly smaller memory footprint. On the other hand, declaring logger members as instance variables requires more CPU time and have a slightly higher memory overhead.
However, instance variables make it possible to create a distinct logger environment for each application, even for loggers declared in shared libraries. Perhaps more important than previously mentioned considerations, instance variables are IOC-friendly whereas
static variables are not.
See also related discussion in the commons-logging wiki.
Is there a recommended idiom for declaring a logger in a class?
The following is the recommended logger declaration idiom. For reasons explained above, it is left to the
user to determine whether loggers are declared as static variables or not.
Unfortunately, give that the name of the hosting class is part of the logger declaration, the above logger declaration idiom is not is not resistant to cut-and-pasting between classes.
As an author of a library built with Maven, you might want to test your application using a binding, say slf4j-log4j12 or logback-classic, without forcing log4j or logback-classic as a dependency upon your users. This is rather easy to accomplish.
Given that your library's code depends on the SLF4J API, you will need to declare slf4j-api as a compile-time (default scope) dependency.
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.12</version> </dependency>
Limiting the transitivity of the SLF4J binding used in your tests can be accomplished by declaring the scope of the SLF4J-binding dependency as "test". Here is an example:
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.12</version> <scope>test</scope> </dependency>
Thus, as far as your users are concerned you are exporting slf4j-api as a transitive dependency of your library, but not any SLF4J-binding or any underlying logging system.
Note that as of SLF4J version 1.6, in the absence of an SLF4J binding, slf4j-api will default to a no-operation implementation.
How do I exclude commons-logging as a Maven dependency?
alternative 1) explicit exclusion
Many software projects using Maven declare commons-logging as a dependency. Therefore, if you wish to migrate to SLF4J or use jcl-over-slf4j, you would need to exclude commons-logging in all of your project's dependencies which transitively depend on commons-logging. Dependency
exclusion is described in the Maven documentation. Excluding commons-logging explicitly for multiple dependencies distributed on several pom.xml files can be a cumbersome and a relatively error prone process.
alternative 2) provided scope
Commons-logging can be rather simply and conveniently excluded as a dependency by declaring it in the provided scope within the pom.xml file of your project. The actual commons-logging classes would be provided by jcl-over-slf4j. This translates into
the following pom file snippet:
<dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>1.7.12</version> </dependency>
The first dependency declaration essentially states that commons-logging will be "somehow" provided by your environment. The second declaration includes jcl-over-slf4j into your project. As jcl-over-slf4j is a perfect binary-compatible replacement for commons-logging,
the first assertion becomes true.
Unfortunately, while declaring commons-logging in the provided scope gets the job done, your IDE, e.g. Eclipse, will still place commons-logging.jar on your project's class path as seen by your IDE. You would need to make sure that jcl-over-slf4j.jar is
visible before commons-logging.jar by your IDE.
alternative 3) empty artifacts
An alternative approach is to depend on an empty commons-logging.jar artifact. This clever approach
first was imagined and initially supported by Erik van Oosten.
The empty artifact is available from a http://version99.qos.ch a high-availability Maven repository, replicated on several
hosts located in different geographical regions.
The following declaration adds the version99 repository to the set of remote repositories searched by Maven. This repository contains empty artifacts for commons-logging and log4j. By the way, if you use the version99 repository, please drop us a line at <version99
AT qos.ch>.
<repositories> <repository> <id>version99</id> <!-- highly available repository serving empty artifacts --> <url>http://version99.qos.ch/</url> </repository> </repositories>
Declaring version 99-empty of commons-logging in the
<dependencyManagement>section of your project will direct all transitive dependencies for commons-logging to import version 99-empty, thus nicely addressing
the commons-logging exclusion problem. The classes for commons-logging will be provided by jcl-over-slf4j. The following lines declare commons-logging version 99-empty (in the dependency management section) and declare jcl-over-slf4j as a dependency.
<dependencyManagement> <dependencies> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>99-empty</version> </dependency> ... other declarations... </dependencies> </dependencyManagement> <!-- Do not forget to declare a dependency on jcl-over-slf4j in the --> <!-- dependencies section. Note that the dependency on commons-logging --> <!-- will be imported transitively. You don't have to declare it yourself. --> <dependencies> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>1.7.12</version> </dependency> ... other dependency declarations </dependencies>
About the SLF4J API
Why don't the printing methods in the Logger interface accept message of typeObject, but only messages of type String?
In SLF4J 1.0beta4, the printing methods such as debug(), info(), warn(), error() in the Logger interface were
modified so as to accept only messages of type String instead of Object.
Thus, the set of printing methods for the DEBUG level became:
debug(String msg); debug(String format, Object arg); debug(String format, Object arg1, Object arg2); debug(String msg, Throwable t);
Previously, the first argument in the above methods was of type
Object.
This change enforces the notion that logging systems are about decorating and handling messages of type String, and not any arbitrary type (Object).
Just as importantly, the new set of method signatures offer a clearer differentiation between the overloaded methods whereas previously the choice of the invoked method due to Java overloading rules were not always easy to follow.
It was also easy to make mistakes. For example, previously it was legal to write:
logger.debug(new Exception("some error"));
Unfortunately, the above call did not print the stack trace of the exception. Thus, a potentially crucial piece of information could be lost. When the first parameter is restricted to be of type String, then only the method
debug(String msg, Throwable t);
can be used to log exceptions. Note that this method ensures that every logged exception is accompanied with a descriptive message.
Can I log an exception without an accompanying message?
In short, no.
If
eis an
Exception, and you would like to log an exception at the ERROR level, you must add an accompanying message. For example,
logger.error("some accompanying message", e);
You might legitimately argue that not all exceptions have a meaningful message to accompany them. Moreover, a good exception should already contain a self explanatory description. The accompanying message may therefore be considered redundant.
While these are valid arguments, there are three opposing arguments also worth considering. First, on many, albeit not all occasions, the accompanying message can convey useful information nicely complementing the description contained in the exception. Frequently,
at the point where the exception is logged, the developer has access to more contextual information than at the point where the exception is thrown. Second, it is not difficult to imagine more or less generic messages, e.g. "Exception caught", "Exception follows",
that can be used as the first argument for
error(String msg, Throwable t)invocations. Third, most log output formats display the message on a line, followed by the exception on a separate line. Thus, the message
line would look inconsistent without a message.
In short, if the user were allowed to log an exception without an accompanying message, it would be the job of the logging system to invent a message. This is actually what the throwing(String
sourceClass, String sourceMethod, Throwable thrown) method in java.util.logging package does. (It decides on its own that accompanying message is the string "THROW".)
It may initially appear strange to require an accompanying message to log an exception. Nevertheless, this is common practice in all log4j derived systems such as java.util.logging, logkit, etc. and of course log4j itself. It seems that the current
consensus considers requiring an accompanying message as a good a thing (TM).
What is the fastest way of (not) logging?
SLF4J supports an advanced feature called parameterized logging which can significantly boost logging performance for disabled logging statement.
For some Logger
logger, writing,
logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
incurs the cost of constructing the message parameter, that is converting both integer
iand
entry[i]to a String, and concatenating intermediate strings.
This, regardless of whether the message will be logged or not.
One possible way to avoid the cost of parameter construction is by surrounding the log statement with a test. Here is an example.
if(logger.isDebugEnabled()) { logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i])); }
This way you will not incur the cost of parameter construction if debugging is disabled for
logger. On the other hand, if the logger is enabled for the DEBUG level, you will incur the cost of evaluating whether
the logger is enabled or not, twice: once in
debugEnabledand once in
debug. This is an insignificant overhead because evaluating a logger takes less than
1% of the time it takes to actually log a statement.
Better yet, use parameterized messages
There exists a very convenient alternative based on message formats. Assuming
entryis an object, you can write:
Object entry = new SomeObject(); logger.debug("The entry is {}.", entry);
After evaluating whether to log or not, and only if the decision is affirmative, will the logger implementation format the message and replace the '{}' pair with the string value of
entry. In other words, this
form does not incur the cost of parameter construction in case the log statement is disabled.
The following two lines will yield the exact same output. However, the second form will outperform the first form by a factor of at least 30, in case of a disabled logging statement.
logger.debug("The new entry is "+entry+"."); logger.debug("The new entry is {}.", entry);
A two argument variant is also available.
For example, you can write:
logger.debug("The new entry is {}. It replaces {}.", entry, oldEntry);
If three or more arguments need to be passed, you can make use of the
Object...variant of
the printing methods. For example, you can write:
logger.debug("Value {} was inserted between {} and {}.", newVal, below, above);
This form incurs the hidden cost of construction of an Object[] (object array) which is usually very small. The one and two argument variants do not incur this hidden cost and exist solely for this reason (efficiency). The slf4j-api would be smaller/cleaner
with only the Object... variant.
Array type arguments, including multi-dimensional arrays, are also supported.
SLF4J uses its own message formatting implementation which differs from that of the Java platform. This is justified by the fact that SLF4J's implementation performs about 10 times faster but at the cost of being non-standard and less flexible.
Escaping the "{}" pair
The "{}" pair is called the formatting anchor. It serves to designate the location where arguments need to be substituted within the message pattern.
SLF4J only cares about the formatting anchor, that is the '{' character immediately followed by '}'. Thus, in case your message contains the '{' or the '}' character, you do not have to do anything special unless the '}' character immediately follows
'}'. For example,
logger.debug("Set {1,2} differs from {}", "3");
which will print as "Set {1,2} differs from 3".
You could have even written,
logger.debug("Set {1,2} differs from {{}}", "3");
which would have printed as "Set {1,2} differs from {3}".
In the extremely rare case where the the "{}" pair occurs naturally within your text and you wish to disable the special meaning of the formatting anchor, then you need to escape the '{' character with '\', that is the backslash character. Only the '{' character
should be escaped. There is no need to escape the '}' character. For example,
logger.debug("Set \\{} differs from {}", "3");
will print as "Set {} differs from 3". Note that within Java code, the backslash character needs to be written as '\\'.
In the rare case where the "\{}" occurs naturally in the message, you can double escape the formatting anchor so that it retains its original meaning. For example,
logger.debug("File name is C:\\\\{}.", "file.zip");
will print as "File name is C:\file.zip".
How can I log the string contents of a single (possibly complex) object?
In relatively rare cases where the message to be logged is the string form of an object, then the parameterized printing method of the appropriate level can be used. Assuming
complexObjectis an object of certain
complexity, for a log statement of level DEBUG, you can write:
logger.debug("{}", complexObject);
The logging system will invoke
complexObject.toString()method only after it has ascertained that the log statement was enabled. Otherwise, the cost of
complexObject.toString()conversion
will be advantageously avoided.
Why doesn't the
org.slf4j.Loggerinterface have methods
for the FATAL level?
The Marker interface, part of the
org.slf4jpackage,
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.
Here is an example,
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.Marker; import org.slf4j.MarkerFactory; class Bar { void foo() { Marker fatal = MarkerFactory.getMarker("FATAL"); Logger logger = LoggerFactory.getLogger("aLogger"); try { ... obtain a JDBC connection } catch (JDBException e) { logger.error(fatal, "Failed to obtain JDBC connection", e); } } }
While markers are part of the SLF4J API, only logback supports markers off the shelf. For example, if you add the
%markerconversion word to its pattern, logback's
PatternLayoutwill
add marker data to its output. Marker data can be used to filter messages or even trigger an
outgoing email at the end of an individual transaction.
In combination with logging frameworks such as log4j and java.util.logging which do not support markers, marker data will be silently ignored.
Markers add a new dimension with infinite possible values for processing log statements compared to five values, namely ERROR, WARN, INFO, DEBUG and TRACE, allowed by levels. At present time, only logback supports marker data. However, nothing prevents other
logging frameworks from making use of marker data.
Why was the TRACE level introduced only in SLF4J version 1.4.0?
The addition of the TRACE level has been frequently and hotly debated request. By studying various projects, we observed that the TRACE level was used to disable logging output from certain classes without needing to configure logging for those classes.
Indeed, the TRACE level is by default disabled in log4j and logback as well most other logging systems. The same result can be achieved by adding the appropriate directives in configuration files.
Thus, in many of cases the TRACE level carried the same semantic meaning as DEBUG. In such cases, the TRACE level merely saves a few configuration directives. In other, more interesting occasions, where TRACE carries a different meaning than DEBUG, Marker objects
can be put to use to convey the desired meaning. However, if you can't be bothered with markers and wish to use a logging level lower than DEBUG, the TRACE level can get the job done.
Note that while the cost of evaluating a disabled log request is in the order of a few
nanoseconds, the use of the TRACE level (or any other level for that matter) is discouraged in tight loops where the log
request might be evaluated millions of times. If the log request is enabled, then it will overwhelm the target destination with massive output. If the request is disabled, it will waste resources.
In short, although we still discourage the use of the TRACE level because alternatives exist or because in many cases log requests of level TRACE are wasteful, given that people kept asking for it, we decided to bow to popular demand.
Does the SLF4J logging API support I18N (internationalization)?
Yes, as of version 1.5.9, SLF4J ships with a package called
org.slf4j.cal10nwhich adds localized/internationalized
logging support as a thin layer built upon the CAL10N API.
Is it possible to retrieve loggers without going through the static methods in
LoggerFactory?
Yes.
LoggerFactoryis essentially a wrapper around an
ILoggerFactoryinstance.
The
ILoggerFactoryinstance in use is determined according to the static binding conventions of the SLF4J framework. See the getSingleton() method
in
LoggerFactoryfor details.
However, nothing prevents you from using your own
ILoggerFactoryinstance. Note that you can also obtain a reference to the
ILoggerFactorythat the
LoggerFactoryclass
is using by invoking the
LoggerFactory.getILoggerFactory()method.
Thus, if SLF4J binding conventions do not fit your needs, or if you need additional flexibility, then do consider using the
ILoggerFactoryinterface as an alternative to inventing your own logging API.
In the presence of an exception/throwable, is it possible to parameterize a logging
statement?
Yes, as of SLF4J 1.6.0, but not in previous versions. The SLF4J API supports parametrization in the presence of an exception, assuming the exception is the last parameter. Thus,
String s = "Hello world"; try { Integer i = Integer.valueOf(s); } catch (NumberFormatException e) { logger.error("Failed to format {}", s, e); }
will print the
NumberFormatExceptionwith its stack trace as expected. The java compiler will invoke the error
method taking a String and two Object arguments. SLF4J, in accordance with the programmer's most probable intention, will interpret
NumberFormatExceptioninstance as a throwable instead of an unused
Objectparameter.
In SLF4J versions prior to 1.6.0, the
NumberFormatExceptioninstance was simply ignored.
If the exception is not the last argument, it will be treated as a plain object and its stack trace will NOT be printed. However, such situations should not occur in practice.
Implementing the SLF4J API
How do I make my logging framework SLF4J compatible?Adding supporting for the SLF4J is surprisingly easy. Essentially, you coping an existing binding and tailoring it a little (as explained below) does the trick.
Assuming your logging system has notion of a logger, called say
MyLogger, you need to provide an adapter for
MyLoggerto
org.slf4j.Loggerinterface.
Refer to slf4j-jcl, slf4j-jdk14, and slf4j-log4j12 modules for examples of adapters.
Once you have written an appropriate adapter, say
MyLoggerAdapter, you need to provide a factory class implementing the
org.slf4j.ILoggerFactoryinterface.
This factory should return instances
MyLoggerAdapter. Let
MyLoggerFactorybe the name of your factory class.
Once you have the adapter, namely
MyLoggerAdapter, and a factory, namely
MyLoggerFactory, the last remaining step is to modify the
StaticLoggerBinderclass
so that it returns an new instance of
MyLoggerFactory. You will also need to modify the
loggerFactoryClassStrvariable.
For Marker or MDC support, you could use the one of the existing NOP implementations.
In summary, to create an SLF4J binding for your logging system, follow these steps:
start with a copy of an existing module,
create an adapter between your logging system and
org.slf4j.Loggerinterface
create a factory for the adapter created in the previous step,
modify
StaticLoggerBinderclass to use the factory you created in the previous step
How can my logging system add support for the
Markerinterface?
Markers constitute a revolutionary concept which is supported by logback but not other existing logging systems. Consequently, SLF4J conforming logging systems are allowed to ignore marker data passed by the user.
However, even though marker data may be ignored, the user must still be allowed to specify marker data. Otherwise, users would not be able to switch between logging systems that support markers and those that do not.
The
MarkerIgnoringBaseclass can serve as a base for adapters or native implementations of logging systems lacking marker support. In
MarkerIgnoringBase, methods
taking marker data simply invoke the corresponding method without the Marker argument, discarding any Marker data passed as argument. Your SLF4J adapters can extend
MarkerIgnoringBaseto quickly implement
the methods in
org.slf4j.Loggerwhich take a
Markeras the first argument.
How does SLF4J's version check mechanism work?
The version check performed by SLF4J API during its initialization is an elective process. Conforming SLF4J implementations may choose not to participate, in which case, no version check will be performed.
However, if an SLF4J implementation decides to participate, than it needs to declare a variable called REQUESTED_API_VERSION within its copy of the
StaticLoggerBinderclass. The value of this variable should
be equal to the version of the slf4j-api.jar it is compiled with. If the implementation is upgraded to a newer version of slf4j-api, than you also need to update the value of REQUESTED_API_VERSION.
For each version, SLF4J API maintains a list of compatible versions. SLF4J will emit a version mismatch warning only if the requested version is not found in the compatibility list. So even if your SLF4J binding has a different release schedule than SLF4J,
assuming you update the SLF4J version you use every 6 to 12 months, you can still participate in the version check without incurring a mismatch warning. For example, logback has a different release schedule but still participates in version checks.
As of SLF4J 1.5.5, all bindings shipped within the SLF4J distribution, e.g. slf4j-log4j12, slf4j-simple and slf4j-jdk14, declare the REQUESTED_API_VERSION field with a value equal to their SLF4J version. It follows that, for example if slf4j-simple-1.5.8.jar
is mixed with slf4j-api-1.6.0.jar, given that 1.5.8 is not on the compatibility list of SLF4J version 1.6.x, a version mismatch warning will be issued.
Note that SLF4J versions prior to 1.5.5 did not have a version check mechanism. Only slf4j-api-1.5.5.jar and later can emit version mismatch warnings.
General questions about logging
Should Logger members of a class be declared as static?We
usedto recommend that loggers members be declared as instance variables instead of static. After further analysis, we no longer recommend one approach over the other.
Here is a summary of the pros and cons of each approach.
Advantages for declaring loggers as static | Disadvantages for declaring loggers as static |
---|---|
common and well-established idiom less CPU overhead: loggers are retrieved and assigned only once, at hosting class initialization less memory overhead: logger declaration will consume one reference per class | For libraries shared between applications, not possible to take advantage of repository selectors. It should be noted that if the SLF4J binding and the underlying API ships with each application (not shared between applications), then each application will still have its own logging environment. not IOC-friendly |
Advantages for declaring loggers as instance variables | Disadvantages for declaring loggers as instance variables |
Possible to take advantage of repository selectors even for libraries shared between applications. However, repository selectors only work if the underlying logging system is logback-classic. Repository selectors do not work for the SLF4J+log4j combination. IOC-friendly | Less common idiom than declaring loggers as static variables higher CPU overhead: loggers are retrieved and assigned for each instance of the hosting class higher memory overhead: logger declaration will consume one reference per instance of the hosting class |
Explanation
Static logger members cost a single variable reference for all instances of the class whereas an instance logger member will cost a variable reference for every instance of the class. For simple classes instantiated thousands of times there might be a noticeabledifference.
However, more recent logging systems, e.g log4j or logback, support a distinct logger context for each application running in the application server. Thus, even if a single copy of log4j.jar or logback-classic.jar is deployed in the server,
the logging system will be able to differentiate between applications and offer a distinct logging environment for each application.
More specifically, each time a logger is retrieved by invoking
LoggerFactory.getLogger()method, the underlying logging system will return an instance appropriate for the current application. Please note that
within the same application retrieving a logger by a given name will always return the same logger. For a given name, a different logger will be returned only for different applications.
If the logger is static, then it will only be retrieved once when the hosting class is loaded into memory. If the hosting class is used in only in one application, there is not much to be concerned about. However, if the hosting class is shared between several
applications, then all instances of the shared class will log into the context of the application which happened to first load the shared class into memory - hardly the behavior expected by the user.
Unfortunately, for non-native implementations of the SLF4J API, namely with slf4j-log4j12, log4j's repository selector will not be able to do its job properly because slf4j-log4j12, a non-native SLF4J binding, will store logger instances in a map, short-circuiting
context-dependent logger retrieval. For native SLF4J implementations, such as logback-classic, repository selectors will work as expected.
The Apache Commons wiki contains an informative article covering the same question.
Logger serialization
Contrary to static variables, instance variables are serialized by default. As of SLF4J version 1.5.3, logger instances survive serialization. Thus, serialization of the host class no longer requires any special action, even when loggers are declared as instance
variables. In previous versions, logger instances needed to be declared as
transientin the host class.
Summary
In summary, declaring logger members as static variables requires less CPU time and have a slightly smaller memory footprint. On the other hand, declaring logger members as instance variables requires more CPU time and have a slightly higher memory overhead.
However, instance variables make it possible to create a distinct logger environment for each application, even for loggers declared in shared libraries. Perhaps more important than previously mentioned considerations, instance variables are IOC-friendly whereas
static variables are not.
See also related discussion in the commons-logging wiki.
Is there a recommended idiom for declaring a logger in a class?
The following is the recommended logger declaration idiom. For reasons explained above, it is left to the
user to determine whether loggers are declared as static variables or not.
package some.package; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class MyClass { final (static) Logger logger = LoggerFactory.getLogger(MyClass.class); ... etc }
Unfortunately, give that the name of the hosting class is part of the logger declaration, the above logger declaration idiom is not is not resistant to cut-and-pasting between classes.
相关文章推荐
- CSS布局页面之盒子模型
- LeetCode--Recover Binary Search Tree
- 设计模式之桥接模式 Boost::scoped_ptr
- UITextFiled上方宽度解决
- 多项式求解(霍纳规则(Horner Rule))
- 【SSH快速进阶】——struts2简单的实例
- PHP 如何阻止用户上传成人照片或者裸照
- 探讨JavaScript中的Rest参数和参数默认值
- The Minimum Length KMP
- Android开发之adb命令
- JS的框架Polymer中的dom-if和is属性使用说明
- getenv putenv setenv和unsetenv详解
- appium进行ios的自动化测试,appium的安装
- RGCDQ(求质因子个数+规律)
- 四种主流聚类方法之我见
- Hive 简介及优化策略
- Lua的面向对象——类和继承
- synchronized的JAVA底层实现
- windows + maven + eclipse
- RSA不对称加密,公钥加密私钥解密,私钥加密公钥解密