With the rapid development of Internet and big data, distributed log system and log analysis system have been widely used. In almost all applications today, various logging frameworks are used to record the running information of programs. For this reason, it is important that you, as an engineer, become familiar with the prevailing logging framework.
The presence or absence of logs does not affect the performance of an application, but an application without logs is incomplete and even flawed. An elegant logging system can record operation tracks, monitor system health and trace system failures. In recent interviews, I found that many candidates who have worked for many years are still half-informed about the mainstream log framework, and their daily applications are still stuck in the level of copy and paste. Therefore, I am writing this article in the hope of helping readers.
This article will provide a comprehensive and systematic introduction to the Java logging framework, including the following:
- The significance and value of logs;
- Evolution of Java logging frameworks;
- Log facade and log system;
- The choice of logging framework;
- Rules and precautions to be followed in the use of logs;
- Log usage examples and common errors.
Part I: A detailed look at the mainstream Java logging frameworks
1. Significance and value of logs
- In the development debugging phase: the logging system helps to locate problems faster.
- During application o&M: The log system records most abnormal information. By collecting the log information, the system running status can be monitored and warned in real time.
- In the data analysis stage: logs usually contain a large amount of user data, including click behavior, interest preferences, etc., based on these data, users can be “painted”, and then help strategic decisions. As big data technology matures, massive log analysis has been widely used in Internet companies.
2. Evolution of the Java logging framework
During development, engineers had to face a very real problem: Java’s “messy” logging framework. Why “chaos”? The reason is that early Java logging frameworks were not standardized, and many applications used multiple logging frameworks at the same time. The development of the Java logging framework can be roughly divided into the following stages:
- Log4j
Apache Log4j is a Java-based logging tool that is a project of the Apache Software Foundation. Prior to jdk1.3, there was no logging framework in place and Java engineers had to use the original system.out.println (), system.err.println (), or e.prinintstacktrace (). The running status of the application is recorded by writing debug logs to the StdOut stream and error logs to the ErrOut stream. This original logging approach has obvious drawbacks, not only that it cannot be customized, but also that the output of logs is not fine-grained. For this reason, in 1999, Ceki Gulcu created the Log4j project, which has almost become the de facto standard for Java logging frameworks.
- JUL
Log4j As a member of the Apache Foundation, Apache wanted to introduce Log4j into the JDK, but Sun refused. Sun then mimicked Log4j by introducing JUL (java.util.logging) in jdk1.4.
- Commons Logging
To understand the interface and implementation of decoupled Logging, Apache introduced JCL(Jakarta Commons Logging), also known as Commons Logging, in 2002. Commons Logging defines a set of Logging interfaces, implemented by Log4j or JUL. Commons Logging is based on dynamic binding to implement Logging. It only needs to use the interface code defined by Commons Logging. When the program runs, it uses the ClassLoader to find and load the underlying log library.
- Slf4j&Logback
Ceki Gulcu disagreed with the Apache Foundation on commons-logging standards, and later left Apache to create Slf4j and Logback. Slf4j is a logging facade, only provides interfaces, can support Logback, JUL, log4j and other logging implementation, Logback provides specific implementation, compared with log4j, it has faster execution speed and more complete functionality.
- Log4j 2
In order to maintain its position in the Java logging world and prevent JCL and Log4j from being replaced by Slf4j and Logback, Apache launched Log4j 2 in 2014. Log4j 2 is incompatible with Log4j and has been significantly improved by a lot of deep optimization.
3. Log facade and log system
As mentioned above, the most common Logging frameworks are Log4j, Log4j 2, Commons Logging, Slf4j, Logback, and JUL. These logging frameworks can be divided into two types: facade logging and logging systems.
- Log facade: provides only log-related interface definitions, that is, corresponding apis, but does not provide specific interface implementations. The log facade can be used to dynamically or statically specify specific log framework implementation, freeing interface and implementation coupling and enabling users to flexibly select specific log implementation framework.
- Logging system: In contrast to the log facade, it provides a specific log interface for application programs to print logs.
As shown in the figure above, Commons-Logging and Slf4j belong to the Logging facade framework, while Log4j, Logback, and JUL belong to the specific Logging system framework. Reading so far, the reader must be wondering – why so designed? Why not make it simple? Why is it divided into facade and implementation?
Before we answer these questions, let’s briefly review the facade pattern (also known as the facade pattern, or positive pattern). The core of facade mode is: the communication between external clients and a subsystem must be carried out through a unified facade object, which makes the subsystem easier to use. Its essence is to provide a unified high-level interface for a group of interfaces in the subsystem, as shown in the figure below:The core of the Facade pattern is the Facade object Facade, which has the following characteristics:
- Know the responsibilities and functions of all submodules.
- Delegating requests from clients to subsystems has no specific business logic of its own.
- Does not participate in the implementation of business logic within subsystems.
Having looked at the basics of facade patterns, I return to my original question — why should a logging framework use facade patterns? In fact, the answer is very simple, in the project development often encountered such a scenario:
- We use the Logback logging system in our own system;
- Our system uses A.jar. The logging system used in A.jar is Log4j.
- Our system also uses B.jar, and the log system used in B.Jar is JUL.
In the above scenario, our system needs to support and maintain Logback, Log4j, and JUL at the same time, which is quite tedious. To solve this problem, you can introduce an adaptation layer that decides which logging system to use, and the caller in the application simply prints the logs without caring about how they were printed. Obviously, Slf4j and Commons-logging are the adaptation layers, while JUL, Log4j and Logback are concrete implementations of printing logs. In other words, the logging facade (adaptation layer) only needs to provide the logging interface, and the specific implementation of the logging system is left to other logging frameworks, thus avoiding the need to maintain complex logging systems.
4. Avoid circular dependencies
Slf4j was designed by Ceki Gulcu, the author of Slf4j, because he felt that the COMMONs-logging API was not well designed and performance was not high enough. In order for Slf4j to be compatible with various types of logging system implementations, he also designed quite a number of Adapters and Bridges to connect, as shown in the following figure:
These Adapters and Bridges are not described in detail here, but you can refer to the above figure to find the corresponding JAR packages if necessary. Only one issue that arises from this is the circular dependency of the logging framework. If slf4J-log4j12-xx. jar, log4j-xx.jar, slf4j-log4j12-xx.jar, slf4j-api-xx.jar, log4j-xx.jar, slf4j-api-xx.jar, log4j-xx.jar, The log4j-over-slf4j-xx.jar packages, in which case slf4J-API calls appear in an infinite loop (as shown in the figure below).
For this reason, when introducing logging framework dependencies, try to avoid the following combinations:
- The JCL – over – slf4j and slf4j – JCL
- Log4j – over – slf4j and slf4j – log4j12
- Jul – to – slf4j and slf4j – jdk14
5. Choose the logging framework
5.1 Relationship between Logging Frameworks
Before introducing the use of the logging framework, let’s briefly review the previous four sections. Commons Logging and Slf4j are Logging facades. Log4j and Logback are concrete logging implementations. It can be simply understood as interface and interface implementation. Callers only need to pay attention to the interface without paying attention to the specific implementation, thus achieving decoupling. The whole log framework mainly includes three parts: log facade, log adapter, and log library. The relationship between them is shown as follows:
5.2 Selecting the Log Framework
Common combinations are Slf4j and Logback, Commons Logging and Log4j, and Logback must be used in conjunction with Slf4j. Since Logback and Slf4j are the same author, the compatibility is self-evident. Here is a short story: Apache has tried to convince Log4j and other logs to be written to the commons-logging standard, but due to problems with commons-logging’s classloading mechanism (it uses a ClassLoader to find and load the underlying Logging library), It is also not implementation-friendly, so the authors of Log4j developed Slf4j, which is distinct from Commons-logging.
As for how to choose a logging framework, if it is a new project (no historical baggage, no need to switch logging framework), it is recommended to use Slf4j and Logback, which has the following advantages:
-
The Slf4j implementation mechanism determines that Slf4j is less restrictive and more widely used. Compared to Commons-logging, Slf4j statically binds the local Log library at compile time, making it much more versatile.
-
Logback has better performance. Logback claims that some key operations, such as deciding whether or not to log a log statement, have significantly improved performance, taking only 3 nanoseconds in Logback versus 30 nanoseconds in Log4j.
-
Slf4j supports parameterization, uses placeholders, and more concise code, as shown in the following example.
// Common practice for commons-logging is if(log.isdebugenabled ()){log.debug(“User name: “+ user.getName() +” Buy goods ID: ” + good.getId()); } // In Slf4j camp, you just need to do this: log.debug(“User name: {},buy goods ID: {}”, user.getName(), good.getid ());
- Logback provides all documentation for free, whereas Log4j provides only part of the documentation for free and requires users to purchase paid documentation.
- MDC (Mapped Diagnostic Contexts) Use Filter to add service information, such as the current user name, to the MDC. You can use this variable in the log format definition. Specifically, when diagnosing a problem, you often need to log out. If Log4j is used, the logging level can only be reduced, but this will generate a large number of logs, affecting application performance. If Logback is used and the log level remains unchanged while filtering out special cases such as Alice, logs will be logged at DEBUG level and other users can continue logged at WARN level. All you need to do is add four lines of XML configuration.
- Automatically compress logs. RollingFileAppender automatically compresses log files that have already been typed when a new file is created. The compression process is asynchronous, so applications are almost unaffected during compression.
For example: If you use Slf4j and Logback directly, you can use the following configuration for integration:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j-api.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback.version}</version>
</dependency>
Copy the code
For existing projects, the facade adapter needs to be determined based on the logging library used to use Slf4j. Slf4j itself only provides a slF4J-API-version. jar package, which is mainly an abstract interface for logging, and the JAR package itself does not implement the abstract interface. For different logging implementations (e.g. Logback, Log4j, etc.), encapsulate different bridge components (e.g. Logback-classic-version.jar, slf4J-log4j12-version.jar), This gives you the flexibility to choose your own logging implementation in your project.
For example, if an existing project uses the Log4j log library, you can perform the following operations to integrate the log library:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j-api.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j-log4j12.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
Copy the code
Here is the Slf4j invocation diagram with other logging components:
See the following figure for specific access modes:
If logging is directly printed in older code using an interface not provided by the Slf4j logging library, you need to introduce a logging library adapter to bridge the legacy API. In the real world, we often encounter situations where different components use different Logging frameworks. For example, the Spring Framework uses Commons Logging and XSocket relies on Java Util Logging.
If you use different components in the same project, how do you resolve the case where different components depend on different logging components? This requires a unified logging scheme, a unified use of Slf4j, and a redirection of their log output to Slf4j, which then passes logs to specific logging implementation tools based on the binder. Slf4j comes with several bridge modules that redirect apis in Log4j, JCL, and java.util.logging to Slf4j.
For example, if the Log4j log library interface is used to print logs in the old code, the following configuration needs to be introduced:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>${log4j-over-slf4j.version}</version>
</dependency>
Copy the code
The bridge mode is shown below:
5.3 Exclude log dependencies of third-party packages that depend on the project
In practice, the project will introduce some third-party components as needed, such as Spring, which uses Commons Logging for its own Logging implementation. If Slf4j+Logback is used, In this case, Commons Logging should be excluded from your project. The following three options are commonly used, each with its own advantages and disadvantages. You can choose the best solution for your project based on the actual situation.
-
Solution 1: Adopt maven’s Exclusion solution
org.springframework spring-core commons-logging commons-logging ${springframework.version}
The advantage of such a solution is that Exclusion is native to Maven, but the disadvantage is that if multiple components rely on Commons-Logging, it will be cumbersome to add Exclusion in many places.
-
In Maven, declare commons-logging scope as provided
Commons – logging Commons – logging 1.1.1 provided
This approach, while concise, has the disadvantage of causing the IDE to place commons-logging under the CLASspath when debugging code, which can lead to exceptions at runtime.
-
Solution 3: Add a virtual version number like 99.0-does-not-exist to maven private server
Commons – logging Commons logging – 99.0 – does not exist
The advantage of this solution is that the declaration method is relatively simple, and there is no problem when debugging the code using the IDE. The drawback is that 99.0-does-not-exist May not exist in the Maven central repository, so you need to publish it in your Maven private server.
The second part: the standards and precautions to be followed in the use of logs
In the first part, I introduced common logging frameworks. As a continuation of the topic of logging, this part will introduce the use of logging with specific cases.
1. Log format and level
When using the log framework, you can customize the log printing format and log level in the log configuration file based on application requirements. The logback.xml configuration example is shown below, which explains the key information in the configuration file. Examples of other logging frameworks will be described in the next section.
<? The XML version = "1.0" encoding = "utf-8"? > <configuration debug="false"> <! <property name="LOG_HOME" value="/home" /> <! - the console output - > < appender name = "STDOUT" class = "ch. Qos. Logback. Core. ConsoleAppender" > < encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <! -- Format output: %d indicates the date, %thread indicates the thread name, %-5level indicates the level of 5 characters from the left, % MSG: Log messages, SSS} [%thread] %-5level % Logger {50} - % MSG %n</pattern> </encoder> </appender> <! Generated according to the daily log FILE - > < appender name = "FILE" class = "ch. Qos. Logback. Core. Rolling. RollingFileAppender" > < rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <! ${LOG_HOME}/ testWeb.log.%d{YYYY-MM-DD}. Log </FileNamePattern> <! -- Log file retention days --> <MaxHistory>30</MaxHistory> </rollingPolicy> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <! - Log output format: % D indicates the date and time, %thread indicates the thread name, %-5level: indicates the level. The width is 5 characters from the left. % Logger {50} indicates that the maximum length of the logger name is 50 characters. % MSG: -> <pattern>%d{YYYY-MM-DD HH: MM :ss.SSS} [%thread] %-5level % Logger {50} - % MSG %n</pattern> </ coder> <! -- the largest size in the log file -- > < triggeringPolicy class = "ch. Qos. Logback. Core. Rolling. SizeBasedTriggeringPolicy" > <MaxFileSize>10MB</MaxFileSize> </triggeringPolicy> </appender> <! <root level="INFO"> <appender-ref ref="STDOUT" /> </root> </configuration>Copy the code
1.1 Log Naming and Retention Period
In the open Java development manual of Alibaba, log specifications are also introduced. Here, the author introduces two of them, namely, log naming and log retention period.
-
Log naming method:
AppName_logType_logName. The log.
AppName indicates the application name. LogType indicates the logType. The recommended types are stats, monitor, and visit. LogName is the description of a log file. This name helps you quickly and clearly understand the type and purpose of a log file, facilitating search and classification.
- Log retention period:
Determining the retention period of logs is a difficult problem. If logs are stored for a long time, a large amount of storage resources will be consumed, and the disk pressure will be excessive, affecting system stability. If logs are stored for a short time, log data may be lost and cannot be traced when problems occur. The ali Java development manual recommends that log files be saved for at least 15 days. In actual applications, you can adjust log files based on their importance, file size, and disk space. In addition, you can monitor and dump logs periodically.
1.2 Log Levels
Generally, the priority of logging is OFF, FATAL, ERROR, WARN, INFO, DEBUG, ALL, or custom. Log4j recommends using only four levels, with the highest priority being ERROR, WARN, INFO, and DEBUG. By setting a log level in the configuration file of the log framework, you can enable or disable the corresponding level of log information in applications. For example, if the log level is set to INFO, only logs whose level is equal to or higher than this level are processed. All DEBUG logs in the application program are not displayed. Note that the log level is not only related to the “level of detail”, but also related to application scenarios and service objects. Common log levels are described as follows:
- ALL: Prints ALL logs.
- OFF: Disables all log output.
- ERROR: Indicates the ERROR information, including the type, content, location, and scenario of the ERROR, and whether the ERROR can be recovered. Errors are output only when they affect the normal running of the system.
- WARN: Serves as a warning that while the application is currently running properly, there are hidden risks.
- INFO: records the basic operating process, operating status, and key information about the system.
- DEBUG: Displays details about the system running process and status.
- TRACE: Details about system structure and content, such as the contents of key objects, function call parameters, results, and so on.
1.3 Log Format
You can configure the log output format to control the content and style of the output log information. The log configuration file logback. XML is used as an example. The pattern label defines the log output format.
parameter
meaning
%d
The date or time of the log output time, for example, %d{YYYY-MM-DD HH: MM: ss.sss}
%thread
Prints the name of the thread that generated the log event
%-5level
Log level, 5 character width from left
%msg
Outputs log messages, that is, the messages specified in the output code
%n
Output a carriage return newline character, “\r\n” for Windows and “\n” for Unix
In addition to the above default log format parameters, Logback also supports custom log format configurations. For example, you want to print IP addresses for each log as follows:
-
Step1: create a new class IPLogConfig and override the convert method to obtain IP.
public class IPLogConfig extends ClassicConverter { @Override public String convert(ILoggingEvent event) { try { return InetAddress.getLocalHost().getHostAddress(); } catch (UnknownHostException e) { e.printStackTrace(); } return null; }}
Step2: modify the logback. XML configuration.
<! - the location of the configuration rule class - > < conversionRule conversionWord = "IP" converterClass = "com. Test. Conf. IPLogConfig" / > < appender name = "Console" class="ch.qos.logback.core.ConsoleAppender"> <layout> <pattern>[ip=%ip] %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </layout> </appender>Copy the code
Compared with the log output format configuration of Logback, the output format configuration of log4j is different. The main parameters are as follows:
parameter
meaning
%m
Prints the message specified in the code
%p
The output priority is DEBUG, INFO, WARN, ERROR, FATAL
%r
Output The number of milliseconds elapsed from the application startup to the output of the log information
%c
The category to which the output belongs is usually the full name of its class
%t
Prints the name of the thread that generated the log event
%n
Output a carriage return newline character, “\r\n” for Windows and “\n” for Unix
%d
The default value is ISO8601. You can also specify a format after it, for example, % D {YY MMMM DD HH:mm:ss,SSS}.
%l
Output the location of the log event, including the category name, the thread in which it occurred, and the number of lines in the code
%F
Name of the file where the output log message was generated
2. Precautions for using logs
This section uses examples to describe basic log applications, such as exception recording, object instance recording, log monitoring, and log classification.
2.1 How can I Record an Exception Log
Be sure to output exception stack information when logging exceptions. Without complete stack information, it can be difficult for maintenance personnel to locate the problem if an exception occurs in the application.
For example, an application splits function points on a processing link. Exceptions are thrown if each function point fails. Record the exception log by category at the service entrance.
try {
this.startOrderProcess(request, result, processName);
} catch (ProductBizException e) {
if (CollectionUtils.isNotEmpty(e.getErrorMessages())) {
e.getErrorMessages().forEach(errorMessage -> {
log.error("biz process error" + e.getMessage(), e);
});
}
} catch (ProductSystemException ex) {
log.error("system error" + ex.getMessage(), ex);
} catch (TMFRuntimeException e) {
ErrorMessage errorMessage = e.getErrorMessage();
if (null != errorMessage) {
log.error("tmf runtime error" + e.getMessage(), e);
}
}
Copy the code
2.2 How to Record Object Instances
Make sure that the instance class overrides the toString method, otherwise it simply outputs the object’s hashCode value, which is meaningless. Alternatively, Java reflection can be used to retrieve the corresponding properties. The main benefit is that there is no need to modify the toString method when adding or modifying properties, and fastJSON is usually used to convert objects into JSON objects.
Example: In a project, there is a debug level log message that needs to record the service caller’s request parameters, so the ProductQueryRequest object instance overrides the toString method to get the complete object information.
Logger. debug(" QueryRequest: {}", productQueryRequest); Public String toString() {return "ProductQueryRequest{" + "queryOption=" + queryOption + ", productIds=" + productIds + ", MemberId=" + MemberId + '}'; }Copy the code
2.3 How are Logs classified
You are advised to classify logs into monitoring logs, statistics logs, and access logs.
- Monitoring log:
The monitoring logs do not contain statistics. Of course, statistics and access information can be configured as monitoring information. Monitoring logs are important for developers, because monitoring logs are directly related to system stability and operation difficulty. Typical monitoring logs have request entry and exit; External service calls and returns; Resource consumption operations: such as reading and writing files; Fault tolerant behavior: such as cloud hard disk copy repair operation; Program abnormalities: such as database connection failure; Background operations: threads that periodically perform deletion; Start, close, configuration load, etc.
- Statistical log:
User access statistics, such as user IP, the amount of data uploaded and downloaded, QPS request, REQUEST RT, etc. Statistical logs need to be strictly formatted for statistical purposes. In practice, log formats should be designed for specific log analysis platforms to facilitate statistical analysis of data.
- Access log:
This type of log is generally directly recorded in the Nginx layer, such as the access. Log of the application, and the data format is basically uniform. Statistical analysis of access logs can also be performed using relevant log analysis platforms.
2.4 How can I Determine the Log Level
In practice, the log levels are debug, INFO, WARN, and ERROR. How to determine the log level?
- Error:
Record important error messages (note: failure to pass the parameter check is not an error). In general, an exception can be considered an error. For example, the user information query service fails to obtain the user information, the file information is read and IOException is returned, and the function module is abnormal.
- Warn:
Record important information, such as failed verification of request parameters, some unimportant exceptions, abnormal condition branches (business process expectations are inconsistent), and empty results obtained by request services (potential risks).
- Info:
Record information that can be used to collect and monitor service data, and locate common problems, such as system status change logs, core processes and key actions of service processes, and status changes of service processes.
- The debug:
Record debugging information, for example, request/ Response. Generally, debug logs are enabled during development and early commissioning. As the system becomes stable, the debug function is disabled. The debug function is enabled only when difficult problems occur.
2.5 How Do I Monitor Log Data
By monitoring keywords in logs, you can discover system faults and report alarms in a timely manner, which is critical to system operation and maintenance. Service monitoring and alarm is a big topic. This section only introduces some issues that need to be paid attention to in log monitoring and alarm:
- If it is not necessary, the alarm will not be sent. Only the errors that need to be handled by O&M immediately need to be sent to the alarm. The reason for doing this is to avoid long-term alarm harassment so that operation and maintenance personnel are no longer sensitive to the alarm, and finally become a “Wolf is crying” story;
- Specify alarm keywords, such as ERROR, rather than a variety of complex rules. The reason for this is that log monitoring is essentially a continuous string matching operation. If there are too many and too complex rules, it may affect online services.
- For some early warning operations, for example, a service needs to retry several times before it succeeds, or a user’s quota is about to be used up, you can send an alarm email every day.
- Every time the system fails, it is necessary to timely check whether the log alarm is sensitive, whether the description of the log alarm is accurate, and constantly optimize the log alarm;
Monitoring Configuration Specifications
project
specification
coverage
Major problems, failures, financial losses and user complaints should be covered by 100% monitoring
Layered monitoring
Monitoring includes system monitoring, JVM monitoring, key middleware monitoring, cluster link monitoring, dependent upstream and downstream services monitoring, and its own services monitoring
Multidimensional analysis
The monitoring modes include: offline and pre-delivery environment can automatically monitor full services, online important functions in a short period of time, single machine monitoring, automatic execution of full online functions, online traffic error rate and other market analysis indicators real-time monitoring, offline analysis service indicators market monitoring
The rate of false positives
After monitoring configuration, it is necessary to follow up data results and set up early warning reasonably. After early warning configuration, it is necessary to continue optimization until false positives no longer appear
2.6 Log File size
The log file size should not be too large. A large log file reduces the efficiency of log monitoring and fault locating. Therefore, log files need to be split. To be specific, whether to split log files by day or by hour depends on the number of logs. The principle is to facilitate the development or operation and maintenance personnel to quickly search for logs. As shown in the following configuration, the size of a log file is defined as 20M. The log file is divided by day and the data generated in the last 15 days is retained.
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxHistory>15</maxHistory>
<maxFileSize>20MB</maxFileSize>
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
Copy the code
To prevent log files from occupying the entire disk space, you need to periodically delete log files. For example, on receiving a disk alarm, you can delete or dump log files from a week ago. In practice, log dumping and deletion should be automated. When the disk space usage exceeds the specified threshold, the system dumps or deletes logs based on the date marked in the log file name.
Part three: Log usage examples and common errors
Following on from the previous two articles, this section introduces examples of the use of several major logging frameworks and common errors. In order to facilitate readers to understand, the error case in the paper to complete the information, and gives the test code, interested readers, can quickly practice through the example.
1. An example of logging framework
1.1 Log4j Usage Examples
-
Maven depends on:
Log4j log4j 1.2.17
- Configuration file:
log4j.properties
Log4j. rootLogger = debug,stdout,INFO Not in the actual application configuration # # # log4j. Appender. Stdout = org.. Apache log4j. ConsoleAppender log4j. Appender. Stdout. Target = System. Out log4j.appender.stdout.layout = org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{YYYY-MM-DD HH: MM :ss,SSS} method:% L %n%m% N ### Output INFO level logs to the specified file log4j.appender.info = org.apache.log4j.DailyRollingFileAppender log4j.appender.INFO.File = ~/Code/logs/log4j.log log4j.appender.INFO.Append = true log4j.appender.INFO.Threshold = info log4j.appender.INFO.layout = org.apache.log4j.PatternLayout log4j.appender.INFO.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%nCopy the code
-
Test code:
public class Log4jTest { private static final Logger LOGGER = Logger.getLogger(Log4jTest.class);
public static void main(String[] args) { LOGGER.info("log test"); try { ((Object) null).toString(); } catch (Exception e) { LOGGER.info("exception info", e); } LOGGER.info("This is log4j. Thread=" + Thread.currentThread().getName()); } Copy the code
}
-
Log output:
2019-10-31 21:05:36 [ main:0 ] – [ INFO ] log test 2019-10-31 21:05:36 [ main:5 ] – [ INFO ] exception info java.lang.NullPointerException at Log4jTest.main(Log4jTest.java:18) 2019-10-31 21:05:36 [ main:9 ] – [ INFO ] This is log4j. Thread=main
1.2 Commons-logging Usage examples
In this case, commons-Logging (also known as JCL) is used as the Logging facade to provide a unified Logging interface, with Log4j as the concrete Logging implementation.
-
Maven depends on:
Commons-logging Commons-logging 1.2 log4j log4j 1.2.17
- The configuration file
1.commons-logging.properties
org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger
Copy the code
2.log4j.properties
# # # set log4j rootLogger = debug, stdout, and output information to the console INFO # # # # # # log4j. Appender. Stdout = org.. Apache log4j. ConsoleAppender log4j.appender.stdout.Target = System.out log4j.appender.stdout.layout = org.apache.log4j.PatternLayout Log4j. Appender. Stdout. Layout. ConversionPattern = [% - 5 p] % d {MM - dd yyyy - HH: MM: ss, SSS} method: % l % n % m % n output INFO # # # . Level above the logs to the specified File log4j appenders. INFO = org.. Apache log4j. DailyRollingFileAppender log4j. Appender. INFO. The File = ~ / Code/logs/jcllog4j. The log log4j appenders. INFO. Append = true. Log4j appenders. INFO. The Threshold = INFO log4j.appender.INFO.layout = org.apache.log4j.PatternLayout log4j.appender.INFO.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%nCopy the code
-
Test code:
public class JclTest { private static final Log LOGGER = LogFactory.getLog(JclTest.class);
public static void main(String[] args) { LOGGER.info("log test"); try { ((Object) null).toString(); } catch (Exception e) { LOGGER.info("exception info", e); } LOGGER.info("This is jcl log. Thread=" + Thread.currentThread().getName()); } Copy the code
}
-
Log output:
2019-10-31 21:30:42 [ main:0 ] – [ INFO ] log test 2019-10-31 21:30:42 [ main:4 ] – [ INFO ] exception info java.lang.NullPointerException at JclTest.main(JclTest.java:19) 2019-10-31 21:30:42 [ main:6 ] – [ INFO ] This is jcl log. Thread=main
1.3 SlF4J Usage Examples
In this example, Slf4j is used as the logging facade to provide a unified logging interface and Logback as the concrete logging implementation.
-
Maven depends on:
Org.slf4j slf4J-API 1.7.28 ch.qos. Logback Logback -classic 1.2.3 ch.qos. Logback logback-core 1.2.3
- Configuration file:
logback.xml
<configuration debug="true" scan="true" scanPeriod="1 seconds">
<contextName>logback</contextName>
<property name="app.name" value="~/Code"/>
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
<encoder>
<pattern>%d [%thread] %-5level %logger{36} [%file : %line] - %msg%n</pattern>
</encoder>
</appender>
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${app.name}/logs/slf4jlogback.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${app.name}/logs/slf4jlogback.%d{yyyy-MM-dd.HH}.log.gz</fileNamePattern>
<maxHistory>60</maxHistory>
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>100MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%d [%thread] %-5level %logger{36} [%file : %line] - %msg%n</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="stdout"/>
<appender-ref ref="file"/>
</root>
</configuration>
Copy the code
-
Test code:
public class Slf4jTest { private static final Logger LOGGER = LoggerFactory.getLogger(Slf4jTest.class);
public static void main(String[] args) { LOGGER.info("log test"); try { ((Object) null).toString(); } catch (Exception e) { LOGGER.info("exception info", e); } LOGGER.info("This is slf4jLogback log. Thread=" + Thread.currentThread().getName()); } Copy the code
}
-
Log output:
019-10-31 21:26:17,806 [main] INFO Slf4jTest [slf4jtest.java: [main] INFO Slf4jTest [slf4jtest.java: 22] – exception info java.lang.NullPointerException: Null at slf4jtest. main(slf4jtest. Java :19) 2019-10-31 21:26:26:17,811 [main] INFO Slf4jTest [slf4jtest. Java: 24] – This is slf4jLogback log. Thread=main
2. Common error cases
2.1 No Binder Is Found
As shown below, the application uses the Slf4j standard interface and targets Log4j as the logging framework, but does not introduce a binder (SLF4J-log4j12).
-
Maven depends on:
Org. slf4j slf4j-API 1.7.28 log4j log4j 1.2.17
Running the test code will result in an error telling you that the binder failed to load, as follows:
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Copy the code
2.2 Multiple binders are configured
As shown in the figure below, the application uses the Slf4j standard interface and targets Log4j as the logging framework, but introduces two log binders.The maven dependencies in question are as follows:
<! Slf4j --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.28</version> </dependency> <! -- Log4j2 --> <dependency> <groupId>log4j</ artifactId> <version>1.2.17</version> </dependency> <! Slf4j </groupId> <artifactId> slf4j12 </artifactId> <version>1.7.28</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> The < version > 2.12.1 < / version > < / dependency >Copy the code
In this case, the log cannot be printed as expected. The application loads the default log configuration and prints the log to the console. The detailed error information is as follows:
SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found the binding in [the jar: file: / Users/test/m2 / repository/org/slf4j/slf4j - log4j12 1.7.28 / slf4j - log4j12-1.7.28. Jar! /org/slf4j/impl/StaticLoggerBinder.class] SLF4J: Found binding in [the jar file: / Users/test/m2 / repository/org/apache/logging/log4j/log4j - slf4j - impl / 2.12.1 / log4j - slf4j - impl - 2.12.1. Jar! /org/slf4j/impl/StaticLoggerBinder.class] SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation. SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]Copy the code
2.3 Cyclic Dependencies
As shown below, the application uses the Slf4j standard interface and targets Log4j as the logging framework, but introduces two log binders: log4J-over-SLf4J and SLf4J-log4j12, which will have circular dependencies.Maven configuration with cyclic dependencies is as follows:
<! Slf4j --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.28</version> </dependency> <! -- Log4j2 --> <dependency> <groupId>log4j</ artifactId> <version>1.2.17</version> </dependency> <! Slf4j </groupId> <artifactId> slf4j12 </artifactId> <version>1.7.28</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>log4j-over-slf4j</artifactId> The < version > 1.7.28 < / version > < / dependency >Copy the code
If the cyclic dependency problem occurs, the log system cannot be started and applications cannot be started. The following error information is displayed:
SLF4J: Detected both log4j-over-slf4j.jar AND bound slf4j-log4j12.jar on the class path, preempting StackOverflowError.
SLF4J: See also http://www.slf4j.org/codes.html#log4jDelegationLoop for more details.
Exception in thread "main" java.lang.ExceptionInInitializerError
at org.slf4j.impl.StaticLoggerBinder.<init>(StaticLoggerBinder.java:72)
at org.slf4j.impl.StaticLoggerBinder.<clinit>(StaticLoggerBinder.java:45)
at org.slf4j.LoggerFactory.bind(LoggerFactory.java:150)
at org.slf4j.LoggerFactory.performInitialization(LoggerFactory.java:124)
at org.slf4j.LoggerFactory.getILoggerFactory(LoggerFactory.java:412)
at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:357)
at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:383)
at Log4jTest.<clinit>(Log4jTest.java:13)
Caused by: java.lang.IllegalStateException: Detected both log4j-over-slf4j.jar AND bound slf4j-log4j12.jar on the class path, preempting StackOverflowError. See also http://www.slf4j.org/codes.html#log4jDelegationLoop for more details.
at org.slf4j.impl.Log4jLoggerFactory.<clinit>(Log4jLoggerFactory.java:54)
... 8 more
Copy the code