Logback refers to the component that performs log event output as an Appender, and the implemented Appender must inherit ch.qos.logback.core.Appender Interface
The interfaces are as follows:
package ch.qos.logback.core; import ch.qos.logback.core.spi.ContextAware; import ch.qos.logback.core.spi.FilterAttachable; import ch.qos.logback.core.spi.LifeCycle; public interface Appender<E> extends LifeCycle, ContextAware, FilterAttachable { public String getName(); public void setName(String name); void doAppend(E event); }
The real type of the template parameter of the doAppend (E event) method depends on the logback module. In the logback classic, E is ILoggingEvent, while in the logback access module, E is AcessEvent. doAppend is the most important part of the logback framework. It is responsible for outputting logs to the specified device in a certain format.
The appender will eventually be responsible for outputting the log, but they may also leave the log formatting to the Layout or Encoder object. Each Layout and Encoder belongs to only one appender. Some Appenders have built-in fixed log formats, so layout/encoder declarations are not required. For example, SockerAppender simply serializes log events and then transmits them over the network.
AppenderBase( ch.qos.logback.core.AppenderBase )
AppenderBase is an abstract class inherited from the appender interface. It is the parent class of all Appenders currently provided by Logback. Although it is an abstract class, it actually implements the doAppender () method.
Let's take a look at the implementation of its doAppender method
public synchronized void doAppend(E eventObject) { // prevent re-entry. if (guard) { return; } try { guard = true; if ( !this.started) { if (statusRepeatCount++ < ALLOWED_REPEATS) { addStatus(new WarnStatus( "Attempted to append to non started appender [" + name + "].",this)); } return; } if (getFilterChainDecision(eventObject) == FilterReply.DENY) { return; } // ok, we now invoke the derived class's implementation of append this.append(eventObject); } finally { guard = false; } }
As you can see, the doAppend method is declared as a synchronization level. This makes it safe for different threads to use the same appender. When the doAppend method is accessed by a thread, subsequent doAppend () calls cannot be executed until the thread exits the method.
Because synchronization is not applicable to all situations, logback also provides an asynchronous implementation ch.qos.logback.core.UnsynchronizedAppenderBase
The implementation of asynchronous doAppend is discussed below. See the hyperlink for the source code:
- First, judge whether the guard is true. If it is true, exit immediately. If it is not true, it will be assigned to true in the next step. This is to prevent the same appender from outputting other logs before calling the append () method, resulting in repeated recursion. private ThreadLocal<Boolean> guard = new ThreadLocal<Boolean>(); The type of guard is a thread exclusive object
- Then, judge whether started is true. If false, output warning information and return. After completing the configuration, the start () method is called to detect the related attribute configuration, and the appender is activated without any exception. If the appender has any exceptions, a warning message will be recorded in the logback internal state management.
- After a series of filtering by the filter filter bound to it, the log event can be rejected and returned, or it can be received to enter the next step.
- Call append()
- Set guard=false to allow subsequent doAppend() function calls.
Down there! Let's talk about the Appender implementation of Logback.
OutputStreamAppender
OutputStreamAppender Attach log events to Java io. In OutputStream. This class is the parent class of ConsoleAppender and FileAppender, where FileAppender is the parent class of RollingFileAppender. But generally speaking, you can't instantiate this Appender.
First: ConsoleAppender
Like its name, this Appender outputs the log to the console, more accurately system Out or system err.
It contains the following parameters:
Property Name | Type | Description |
---|---|---|
encoder | Encoder | Determines the manner in which an event is written to the underlying OutputStreamAppender. Encoders are described in a dedicated chapter. |
target | String | Specify the output destination. Optional value: system Out or system err. Default: system out |
withJansi | boolean | Whether ANSI color codes (similar to the output string color control code of shell script in linux) are supported. The default is false. If set to true. For example: [31m stands for setting the foreground color to red. In windows, you need to provide "org.fusesource.jansi:jansi:1.9", which is supported by default in linux, mac os x. In the Eclipse IDE, you can try ANSI in Eclipse Console plug-in. |
Look at a simple example
<configuration> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default --> <encoder> <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="STDOUT" /> </root> </configuration>
Second: FileAppender
Output the log to a file. The target file depends on the file attribute. Whether to append the output depends on the append attribute.
Property Name | Type | Description |
---|---|---|
append | boolean | Whether to output in append mode. The default is true. |
encoder | Encoder | See OutputStreamAppender properties. |
file | String | Specify the file name. Note that in windows, the backslash \ needs to be escaped or can be used directly. For example, C: / temp / test.logger or c:\temp\test.log can be used. There is no default value. If the upper directory does not exist, FileAppender will be created automatically. |
prudent | boolean | Whether it works in cautious mode. In cautious mode, FileAppender will safely write logs to the specified file, even if there is another same FileAppender instance in different virtual machine JVMs. Default: false Set to true, which means that append will be automatically set to true The student depends on the file exclusive lock. Experiments show that using the file lock will increase the log writing consumption by three times. For example, when the student mode is off, it takes only 10 milliseconds to write a log to the file, but if the student mode is true, it will be close to 30 milliseconds. The student mode actually serializes I/O requests. Therefore, when the number of I / OS is large, such as 100 times / s or more, the delay will be obvious, so it should be avoided. In the networked file system, this consumption will be greater, which may lead to deadlock. |
By default, each log event will be immediately flushed to the output stream. This default method is relatively safe for data and can avoid the risk of log loss in the buffer due to the abnormal exit of the application. However, in order to increase the log throughput, you can also set the immediateFulsh property in the Encoder to false. The Encoder will be described in the next article Explained in the blog post.
Here is a simple example:
<configuration> <appender name="FILE" class="ch.qos.logback.core.FileAppender"> <file>testFile.log</file> <append>true</append> <!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder by default --> <encoder> <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="FILE" /> </root> </configuration>
We just use the < timestamp > tag to get the application startup time and use it as the file name of the new log file.
<configuration> <!-- Insert the current time formatted as "yyyyMMdd'T'HHmmss" under the key "bySecond" into the logger context. This value will be available to all subsequent configuration elements. --> <timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"/> <appender name="FILE" class="ch.qos.logback.core.FileAppender"> <!-- use the previously created timestamp to create a uniquely named log file --> <file>log-${bySecond}.txt</file> <encoder> <pattern>%logger{35} - %msg%n</pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="FILE" /> </root> </configuration>
The < timestamp > tag contains two necessary attributes: key and datePattern, and an optional attribute: timeReference.
- key is the name of the attribute as the name of the variable
- datePattern needs to conform to the Convention in SimpleDateFormat
- timeReference can reference the values of other variables
<configuration> <timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss" timeReference="contextBirth"/> ... </configuration>
Third: RollingFileAppender
RollingFileAppender inherits from FileAppender and provides the function of automatic switching of log target files. For example, date can be used as the condition for log segmentation.
RollingFileAppender has two important properties. RollingPolicy is responsible for how to switch logs and triggingpolicy is responsible for when to switch logs. In order for RollingFileAppender to work, these two properties must be set. However, if the implementation class of RollingPolicy also implements the triggingpolicy interface, only RollingPolicy can be set.
Here are its parameters:
Property Name | Type | Description |
---|---|---|
file | String | Specify the file name. Note that in windows, the backslash \ needs to be escaped or can be used directly. For example, C: / temp / test.logger or c:\temp\test.log can be used. There is no default value. If the upper directory does not exist, FileAppender will be created automatically. |
append | boolean | Whether to output in append mode. The default is true. |
encoder | Encoder | See OutputStreamAppender properties. |
rollingPolicy | RollingPolicy | The switching behavior of RollingFileAppender when log switching occurs. For example, log file name modification |
triggeringPolicy | TriggeringPolicy | Determine when log switching occurs, such as the date, and when the log file size reaches a certain value |
prudent | boolean | FixedWindowRollingPolicy Student mode is not supported. TimeBasedRollingPolicy supports the student mode, but the following two constraints need to be met: In the student mode, the compression of log files is not allowed or supported. The file property cannot be set. |
- In the student mode, the compression of log files is not allowed or supported.
- The file property cannot be set.
Let's take a look at what RollingPolicy is:
RollingPolicy is actually responsible for switching and renaming log files.
The interfaces are as follows:
package ch.qos.logback.core.rolling; import ch.qos.logback.core.FileAppender; import ch.qos.logback.core.spi.LifeCycle; public interface RollingPolicy extends LifeCycle { /*rollover Method to complete the archiving of the current log file */ public void rollover() throws RolloverFailure; /*getActiveFileName Method is responsible for calculating the new log file name*/ public String getActiveFileName(); /*getCompressionMode Method is responsible for the compression mode*/ public CompressionMode getCompressionMode(); /*Set parent appender*/ public void setParent(FileAppender appender); }
RollingPolicy has several common implementation classes:
TimeBasedRollingPolicy
TimeBasedRollingPolicy Perhaps the most popular log scrolling strategy. Its rolling strategy is time-based, for example, according to days and months.
Timebasedlollingpolicy inherits the RollingPolicy and triggingpolicy interfaces.
It contains a required attribute: fileNamePattern and several optional attributes.
Property Name | Type | Description |
---|---|---|
fileNamePattern | String | This required attribute determines the naming policy of archive logs when logs are scrolled. It consists of a file name and a% d transition character.% n D {} the curly brackets need to contain the time format conforming to the SimpleDateFormat convention. If it is not specified, it is% d directly, which is equivalent to% d {yyyy MM DD} by default. It should be noted that in the parent node of the RollingPolicy node, the value of the < File > node can be declared or ignored. If you declare the file attribute, you can separate the currently valid log files and archive log files. When set to, the name of the current valid log file will always be the value specified by the file attribute. When log scrolling occurs, change the name of the archive log according to the value of fileNamePattern, and then create a new valid log file named the value specified by the file attribute. If not specified, the currently valid log file name changes according to fileNamePattern. It should also be noted that in% d {}, both "/" and "\" are considered file separators. Multiple% d transitions: The value of fileNamePaatern allows multiple% D, but only one% d is used as the reference value for the main log rolling cycle. The remaining non primary% D needs to contain an "aux" parameter. For example: <fileNamePattern>/var/log/%d{yyyy/MM, aux}/myapplication.%d{yyyy-MM-dd}.log</fileNamePattern> Divide the directory according to the month and year, and then store the archive logs named according to the date and days of the corresponding month in the same month folder. This attribute value determines that log switching occurs at 0 o'clock every day. Time zone issues: You can convert the date to the time in the corresponding time zone. for example aFolder/test.%d{yyyy-MM-dd-HH, UTC}.log / / World coordinated time aFolder/test.%d{yyyy-MM-dd-HH, GMT}.log / / Greenwich mean time |
maxHistory | int | Optional parameter to declare the maximum retention time of archive logs. If you scroll the log based on month, when maxHisory is 6, the log will be kept for 6 months. Those older than 6 months will be deleted. The directory where the log exists will also be deleted appropriately. |
totalSizeCap | int | Optional parameter to declare the maximum storage capacity of archived logs. When this value is exceeded, the oldest archived log file will also be deleted. |
cleanHistoryOnStart | boolean | Optional parameter. The default value is false. If set to true, all archived log files will be deleted when the appender starts. |
Here are some common fileNamePattern values:
fileNamePattern | Rollover schedule | Example |
---|---|---|
/wombat/foo.%d | Rolling log at midnight every day, unspecified, the default is% d{yyyy-MM-dd} | If the file attribute is not set: on July 17, 2016, the log will be output to / wombat / foo In the log file on July 17, 2016, at midnight, the currently valid log file will be converted to / wombat / foo 2016-07-18 The file property is set to / wombat / foo Txt: on July 17, 2016, the log will be output to / wombat / foo Txt log file. At 24:00 midnight, the log file is archived and renamed / wombat / foo On July 18, 2016, the currently valid log file was recreated and named / wombat / foo txt |
/wombat/%d{yyyy/MM}/foo.txt | One watch per month | If the file attribute is not set: in July 2016, the log is output to / wombat / 2016 / 07 / foo txt. At 24 midnight on July 31, the log is redirected and output to / wombat / 2016 / 08 / foo txt. The file property is set to / wombat / foo Txt: the current valid log file is the value specified by file. At 24:00 at the end of the month, archive the log, recreate the file specified by file, and redirect the log output stream to this new file. |
/wombat/foo.%d{yyyy-ww}.log | It should be noted that the first day of each week is related to the regional setting | Similar to above |
/wombat/foo%d{yyyy-MM-dd_HH}.log | One watch per hour | Similar to above |
/wombat/foo%d{yyyy-MM-dd_HH-mm}.log | One watch per minute | Similar to above |
/wombat/foo%d{yyyy-MM-dd_HH-mm, UTC}.log | One watch per minute and UTC time | Similar to above, except that the time is formatted as UTC time |
/foo/%d{yyyy-MM,aux}/%d.log | One watch a day | Contains two% d, the first contains aux, which indicates that it is not the main scroll parameter, but is mainly used for folder segmentation. In other words, daily logs are stored in different paths by month |
TimeBaseRollingPolicy supports automatic compression of log files by setting the value of fileNamePattern gz or Open at the end of zip.
fileNamePattern | Rollover schedule | Example |
---|---|---|
/wombat/foo.%d.gz | Automatically compress and archive log files every day | When the file attribute is not set: on July 17, 2016, the log is output to / wombat / foo On July 17, 2016, at 24 midnight, the log file will be compressed and renamed / wombat / foo 2016-07-17. gz. The log is then redirected to / wombat / foo 2016-07-18 The file attribute is set to / wombat / foo Txt: the current valid log file name will never change. Scroll the log at 0 o'clock every day and compress the log. |
For many reasons, log scrolling is not clock driven, but depends on incoming log events. If fileNamePattern is set to YYY MM DD, if the first log event request occurs at 00:30 after 24:00, the real log rolling event occurs at 00:30. However, the implementation of this delay can be ignored.
The following is a simple configuration example:
<configuration> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>logFile.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- daily rollover --> <fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern> <!-- keep 30 days' worth of history capped at 3GB total size --> <maxHistory>30</maxHistory> <totalSizeCap>3GB</totalSizeCap> </rollingPolicy> <encoder> <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="FILE" /> </root> </configuration>
SizeAndTimeBasedRollingPolicy
Sometimes you not only want to specify the scrolling strategy by time, but also want to limit the size of each log file at the same time. The TimeBasedRoolingPolicy already provides the function of limiting the size of the total log file, while SizeAndTimeBasedRollingPolicy provides a more powerful ability to limit the size of a single log file.
Take a small example:
<configuration> <appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>mylog.txt</file> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <!-- rollover daily --> <fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern> <!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB --> <maxFileSize>100MB</maxFileSize> <maxHistory>60</maxHistory> <totalSizeCap>20GB</totalSizeCap> </rollingPolicy> <encoder> <pattern>%msg%n</pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="ROLLING" /> </root> </configuration>
It should be noted that in this example, fileNamePattern contains not only% d but also% i, both of which are necessary identifiers.% n i stands for log index number. Today's log has been expanded to a few copies, starting with 0.
In 1.1 Before version 7, SizeAndTimeBasedFNATP was used, and then SizeAndTimeBasedRollingPolicy was adopted. SizeAndTimeBasedRollingPolicy inherited SizeAndTimeBasedFNATP.
FixedWindowRollingPolicy the log rollingpolicy for fixed windows
Look at the following parameters first:
Property Name | Type | Description |
---|---|---|
minIndex | int | This parameter specifies the minimum value of the window index |
maxIndex | int | This parameter specifies the maximum value of the window index |
fileNamePattern | String | This parameter is no different from the previous fileNamePattern. The only thing to note is that it must contain the% i identifier, which is used to indicate the value of the current window index. For example, set fileNamePattern to“ MyLogFile%i ", minIndex is 1 and maxIndex is 3, the MyLogFile1.log, MyLogFile2.log and MyLogFile3.log. These three archive log files. |
Take a small example:
<configuration> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>test.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"> <fileNamePattern>tests.%i.log.zip</fileNamePattern> <minIndex>1</minIndex> <maxIndex>3</maxIndex> </rollingPolicy> <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <maxFileSize>5MB</maxFileSize> </triggeringPolicy> <encoder> <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="FILE" /> </root> </configuration>
Next, let's look at the interface of triggering policies
TriggeringPolicy Responsible for when RollingFileAppender logs roll
Interface Description:
package ch.qos.logback.core.rolling; import java.io.File; import ch.qos.logback.core.spi.LifeCycle; public interface TriggeringPolicy<E> extends LifeCycle { /*This method determines whether log scrolling occurs*/ public boolean isTriggeringEvent(final File activeFile, final <E> event); }
SizeBasedTriggeringPolicy
SizeBasedTriggeringPolicy Check the size of the current active log file. When it reaches a certain capacity, start log scrolling.
Sizebasedtriggingpolicy only receives one parameter, maxFileSize. The default value is 10MB
Acceptable B, KB, MB, GB
SocketAppender and SSLSocketAppender
So far, the appender we talked about can only output logs to local resources. In contrast, SocketAppender is designed to output logs to remote instances. SocketAppender outputs logs in clear text, while SSLSocketAppender transmits logs in encryption.
The type of log event being serialized is LoggingEventVO Inherits the ILoggingEvent interface. Remote logging is not intrusive. After deserialization reception, log events can be processed as if they were generated locally. Multiple sockerappenders can send logs to the same log server. SocketAppender does not need to be associated with a Layout because it just sends serialized log events to the remote log server. The sending operation of SocketAppender is based on TCP protocol. Therefore, if the remote server is reachable, the log will be processed by it. If the remote server is down or unreachable, the log will be discarded. When the remote server comes back to life, log sending will restart transparently. This transparent reconnection is achieved by a "connection" thread periodically trying to connect to the remote server.
Logging events is automatically buffered by TCP protocol. This means that if the network speed is faster than the generation speed of log requests, the network speed will not affect the application. However, if the network speed is too slow, the network speed will become a limit. In extreme cases, if the remote log server is unreachable, the application will eventually block. However, if the server can reach But the server is down. In this case, the application will not block, but just lose some log events.
It should be noted that even if the SocketAppender is not linked by the logger, it will not be recycled by gc because it still has references in the connector thread. A connector thread will exit only when the network is unreachable. In order to prevent this problem of garbage collection, we should display a statement to close the SocketAppender. It will survive for a long time and create / destroy a large number of SOCS The application of the SocketAppender instance should pay more attention to this problem. However, most applications can ignore this problem. If the JVM exits the SocketAppender before it is closed, or it is garbage collected, it may lead to the loss of some log data waiting in the pipeline that has not been transmitted. In order to prevent log loss, it is often a reliable way to call socket The close method of the appender or the stop method of LoggerContext is called before exiting the application.
Let's take a look at the properties of SocketAppender:
Property Name | Type | Description |
---|---|---|
includeCallerData | boolean | Include caller information If it is true, the following log output?:? Will be replaced with the caller's file name and line number. If it is false, it is a question mark. 2006-11-06 17:37:30,968 DEBUG [Thread-0] [?:?] chapters.appenders.socket.SocketClient2 - Hi |
port | int | Port number |
reconnectionDelay | Duration | If the reconnection delay is set to "10 seconds", it will wait 10 seconds after the connection u weapon fails before connecting. The default value is "30 seconds". If it is set to 0, the reconnection function will be turned off. |
queueSize | int | Set the number of buffered logs. If it is set to 0, the log sending is synchronous. If it is set to a value greater than 0, the logs will be put into the queue. The queue length reaches the specified value and sent uniformly. You can increase the service throughput. |
eventDelayLimit | Duration | Set the log timeout Discard time. When a value similar to "10 seconds" is set, if the log queue is full and the server is too late to receive for a long time, the log will be discarded when the retention time exceeds 10 seconds. Default: 100 milliseconds |
remoteHost | String | IP address of remote log server |
ssl | SSLConfiguration | This attribute node is only included in SSLSocketAppender. SSL configuration is provided. For details, see Using SSL |
The standard Logback Classic contains four receivers that can be used to receive logging evnets from SocketAppender.
- ServerSocketReceiver, and the SSL enabled replica SSLSeverSocketReceiver. See the detailed configuration Receivers
- Simplessocketserver, and SimpleSSLSocketServer, which allows SSL replicas.
Use SimpleSocketServer on the server side
SimpleSocketServer requires two command line parameters, port and configFile path.
java ch.qos.logback.classic.net.SimpleSocketServer 6000 \ src/main/java/chapters/appenders/socket/server1.xml
Simple configuration example of SocketAppender on the client:
<configuration> <appender name="SOCKET" class="ch.qos.logback.classic.net.SocketAppender"> <remoteHost>192.168.0.101</remoteHost> <port>8888</port> <reconnectionDelay>10000</reconnectionDelay> <includeCallerData>true</includeCallerData> </appender> <root level="DEBUG"> <appender-ref ref="SOCKET" /> </root> </configuration>
Use simplesssockets server on the server side
java -Djavax.net.ssl.keyStore=src/main/java/chapters/appenders/socket/ssl/keystore.jks \ -Djavax.net.ssl.keyStorePassword=changeit \ ch.qos.logback.classic.net.SimpleSSLSocketServer 6000 \ src/main/java/chapters/appenders/socket/ssl/server.xml
SSLSocketAppender configuration
<configuration debug="true"> <appender name="SOCKET" class="ch.qos.logback.classic.net.SSLSocketAppender"> <remoteHost>${host}</remoteHost> <port>${port}</port> <reconnectionDelay>10000</reconnectionDelay> <ssl> <trustStore> <location>${truststore}</location> <password>${password}</password> </trustStore> </ssl> </appender> <root level="DEBUG"> <appender-ref ref="SOCKET" /> </root> </configuration>
ServerSocketAppender and SSLSeverSocketAppender
The design concept of SocketAppender and SSLSockertAppender mentioned above is to let applications actively connect to the remote log server. However, sometimes it may be inconvenient or unscientific to let applications connect to the log server during initialization. Therefore, Logback also provides ServerSocketAppender and sslserverssocketappender. SocketAppender and ServerSocketAppender serialize Logging event uses the same method. The serialized objects are ILoggingEvent. The only difference is the master-slave conversion of connection initialization. SocketAppender is the active party to establish a connection with the log server, while ServerSocketAppender is passive. It listens to connection requests from the client.
ServerSocketAppender property sheet:
Property Name | Type | Description |
---|---|---|
address | String | Specify the address of the local network interface that the appender listens to. If not specified, it listens to all network interfaces |
includeCallerData | boolean | If true, the caller data will be transferred to the remote host. It is not transferred by default |
port | int | Listen to that port |
ssl | SSLConfiguration | This attribute is only supported by SSLServerSocketAppender and provides SSL configuration |
Take a small example:
<configuration debug="true"> <appender name="SERVER" class="ch.qos.logback.classic.net.server.ServerSocketAppender"> <port>${port}</port> <includeCallerData>${includeCallerData}</includeCallerData> </appender> <root level="debug"> <appender-ref ref="SERVER" /> </root> </configuration>
The main difference between this configuration and the previous SocketAppender configuration is that the < remotehost > attribute is missing, because the ServerSocketAppender does not actively open the connection with the log server, but passively waits for the connection request from the remote host.
The following is a configuration example of SSLSocketAppender:
<configuration debug="true"> <appender name="SERVER" class="ch.qos.logback.classic.net.server.SSLServerSocketAppender"> <port>${port}</port> <includeCallerData>${includeCallerData}</includeCallerData> <ssl> <keyStore> <location>${keystore}</location> <password>${password}</password> </keyStore> </ssl> </appender> <root level="debug"> <appender-ref ref="SERVER" /> </root> </configuration>
After the Socket log output, let's take a look at the mail based log output
SMTPAppender
SMTPAppender You can store the logging event in one or more fixed size buffers, and then send the logging event of appropriate size to the operation and maintenance personnel by mail when the event specified by the user arrives.
The detailed properties are as follows:
Property Name | Type | Description |
---|---|---|
smtpHost | String | The address of the SMTP server, which must be specified. For example, the SMTP server address of Netease is SMTP 163.com |
smtpPort | int | The port address of the SMTP server. Default: 25 |
to | String | Specify the mailbox to send to. You can set multiple < to > attributes and specify multiple destination mailboxes |
from | String | Specify the sender name. If set to "Adam Smith <smith@moral.org> ”, The sender of the message will be "Adam Smith"< smith@moral.org > ” |
subject | String | Specify the title of emial, which needs to meet the format requirements in PatternLayout. If set to“ Log:% logger -% MSG ", for example, when sending an email, the title is“ Log: com.foo.Bar - Hello World ". Default: "%logger{20} - %m". |
discriminator | Discriminator | Through the Discriminator, SMTPAppender can distribute the incoming logging event s to different buffers according to the return value of the Discriminator. By default, the same value is always returned to achieve the purpose of using one buffer. |
evaluator | IEvaluator | Specify the conditions that trigger log sending. Specify the implementation class of the EventEvaluator interface through < evaluator class =... / > by default, smtpappender uses OnErrorEvaluator, which means that when sending log requests of ERROR or higher level, mail will be sent. Logback provides several evaluators: Onerrorevaluatoronmarkerevaluatorjanineventevaluatorgeventevaluator (powerful) |
cyclicBufferTracker | CyclicBufferTracker | Specify a CyclicBufferTracker to track the cyclic buffer. It is based on the implementation of discriminator. If you do not specify it, a CyclicBufferTracker will be created by default, and the default setting of the cyclic buffer size is 256. You can also manually specify the default CyclicBufferTracker and modify the number of logging event s received by the default buffer through the < buffersize > property. |
username | String | Sending mail account, null by default |
password | String | Send mail password, null by default |
STARTTLS | boolean | If it is set to true, the appender will try to use the STARTTLS command. If the server supports it, it will convert the plaintext connection to an encrypted connection. It should be noted that the connection to the log server is unencrypted at first. Default: false |
SSL | boolean | If set to true, the appender will connect to the log server using SSL. Default: false |
charsetEncoding | String | Specifies the encoding format of the message information Default: UTF-8 |
localhost | String | If the SMTP host is not configured correctly, for example, it is not a complete address. In this case, the localhost attribute is required to provide the full path of the server (like the fully qualified name in java). For details, refer to the mail.smtp.localhost attribute in com.sun.mail.smtp |
asynchronousSending | boolean | This attribute determines whether email is sent asynchronously. Default: true, send asynchronously However, in some cases, it is necessary to send the error log email to the management personnel in a synchronous manner to prevent failure to maintain the application in time. |
includeCallerData | boolean | Default: false Specifies whether to include callerData in the log |
sessionViaJNDI | boolean | SMTPAppender relies on javax.mail.Session to send mail. By default, sessionViaJNDI is false. The creation of javax.mail.Session instance depends on the configuration information of SMTPAppender itself. If it is set to true, the reference is obtained through JNDI during the creation of Session. This has the advantages of better code reuse and simpler configuration. It should be noted that if you use JNDI to obtain the Session object, you need to ensure that the two jar packages mail.jar and activation.jar are removed |
jndiLocation | String | If sessionViaJNDI is set to true, jndiLocation specifies the resource name of JNDI. The default value is: "java:comp/env/mail/Session" |
- OnErrorEvaluator
- OnMarkerEvaluator
- JaninoEventEvaluator
- GEventEvaluator (powerful)
cyclicBufferTracker CyclicBufferTracker Specify a cyclicBufferTracker to track the cyclicbuffer. It is based on the implementation of discriminator. If you do not specify it, it will be created by default CyclicBufferTracker , the default setting of cyclic buffer size is 256. You can also manually specify the default CyclicBufferTracker and modify the number of logging event s received by the default buffer through the < buffersize > property. username String sends mail account, the default is null password String sends mail password, and the default is null startls Boolean. If it is set to true, the appender will Try to use the STARTTLS command. If the server supports it, the plaintext connection will be converted into an encrypted connection. Note that the connection to the log server is not encrypted at first. Default: false If SSL boolean is set to true, the appender will connect to the log server using SSL. Default: false charsetEncoding String specifies the encoding format of mail information Default: UTF-8 localhost String if the SMTP host is not configured correctly, for example, it is not a complete address. In this case, the localhost property is required to provide the full path of the server (like the fully qualified name in java). For details, see com.sun.mail.smtp The mail.smtp.localhost attribute asynchronousSending boolean in determines whether email is sent asynchronously. Default: true, send asynchronously However, in some cases, it is necessary to send the error log email to the management personnel in a synchronous manner to prevent failure to maintain the application in time. includeCallerData boolean default: false Specifies whether to include callerData in the log sessionViaJNDI Boolean SMTPAppender relies on javax.mail.Session to send mail. By default, sessionViaJNDI is false. The creation of javax.mail.Session instance depends on the configuration information of SMTPAppender itself. If it is set to true, the reference of Session is obtained through JNDI during the creation of Session. This can make your code reuse better and the configuration more concise. It should be noted that if you use JNDI to obtain the Session object, you need to ensure that the two jar packages mail.jar and activation.jar are removed jndiLocation String if sessionViaJNDI is set to true, jndiLocation specifies the resource name of JNDI. The default value is: "java:comp/env/mail/Session"
SMTPAppender only keeps the latest 256 logging events in the circular buffer. When the buffer is slow, it will start to discard the oldest logging events. Therefore, SMTPAppender can deliver up to 256 log events in a message at any time. SMTPAppender depends on JavaMail API, which in turn depends on IOC framework (dependency injection).
Original text:
You can download the JavaMail API and the JavaBeans Activation Framework from their respective websites. Make sure to place these two jar files in the classpath before trying the following examples.
Look at a small example
<configuration> <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"> <smtpHost>ADDRESS-OF-YOUR-SMTP-HOST</smtpHost> <to>EMAIL-DESTINATION</to> <to>ANOTHER_EMAIL_DESTINATION</to> <!-- additional destinations are possible --> <from>SENDER-EMAIL</from> <subject>TESTING: %logger{20} - %m</subject> <layout class="ch.qos.logback.classic.PatternLayout"> <pattern>%date %-5level %logger{35} - %message%n</pattern> </layout> </appender> <root level="DEBUG"> <appender-ref ref="EMAIL" /> </root> </configuration>
Custom circular buffer size: considering the default size of 256 logs, it may not be applicable, and we can also customize it.
<configuration> <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"> <smtpHost>${smtpHost}</smtpHost> <to>${to}</to> <from>${from}</from> <subject>%logger{20} - %m</subject> <layout class="ch.qos.logback.classic.html.HTMLLayout"/> <cyclicBufferTracker class="ch.qos.logback.core.spi.CyclicBufferTracker"> <!-- send just one log entry per email --> <bufferSize>1</bufferSize> </cyclicBufferTracker> </appender> <root level="DEBUG"> <appender-ref ref="EMAIL" /> </root> </configuration>
Event triggering mail log sending: by default, the Evaluator specifies OnErrorEvaluator, which indicates that log sending will be triggered when an event with level error or higher occurs.
We can also write our own log trigger, as long as we inherit from the EventEvaluator interface. It mainly implements the evaluate method. Here is an example:
Function: trigger log sending request every 1024 log events.
package chapters.appenders.mail; import ch.qos.logback.core.boolex.EvaluationException; import ch.qos.logback.core.boolex.EventEvaluator; import ch.qos.logback.core.spi.ContextAwareBase; public class CounterBasedEvaluator extends ContextAwareBase implements EventEvaluator { static int LIMIT = 1024; int counter = 0; String name; public boolean evaluate(Object event) throws NullPointerException, EvaluationException { counter++; if (counter == LIMIT) { counter = 0; return true; } else { return false; } } public String getName() { return name; } public void setName(String name) { this.name = name; } }
Using a custom Evaluator
<configuration> <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"> <evaluator class="chapters.appenders.mail.CounterBasedEvaluator" /> <smtpHost>${smtpHost}</smtpHost> <to>${to}</to> <from>${from}</from> <subject>%logger{20} - %m</subject> <layout class="ch.qos.logback.classic.html.HTMLLayout"/> </appender> <root level="DEBUG"> <appender-ref ref="EMAIL" /> </root> </configuration>
Label based Evaluator
When we talked about the SMTPAppender attribute earlier, we talked about the implementation of several evaluators provided by logback. Let's take a look at the use examples below.
Specify the label of the log event in the code
Marker notifyAdmin = MarkerFactory.getMarker("NOTIFY_ADMIN"); logger.error(notifyAdmin, "This is a serious an error requiring the admin's attention", new Exception("Just testing"));
The first is the OnmarkerEvaluator
<configuration> <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"> <evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator"> <marker>NOTIFY_ADMIN</marker> <!-- you specify add as many markers as you want --> <marker>TRANSACTION_FAILURE</marker> </evaluator> <smtpHost>${smtpHost}</smtpHost> <to>${to}</to> <from>${from}</from> <layout class="ch.qos.logback.classic.html.HTMLLayout"/> </appender> <root> <level value ="debug"/> <appender-ref ref="EMAIL" /> </root> </configuration>
Then JaninoEventEvaluator
<configuration> <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"> <evaluator class="ch.qos.logback.classic.boolex.JaninoEventEvaluator"> <expression> (marker != null) && (marker.contains("NOTIFY_ADMIN") || marker.contains("TRANSACTION_FAILURE")) </expression> </evaluator> ... same as above </appender> </configuration>
Finally, take a look at GEventEvaluator, which uses "?." Security caller, whose function will be called only when the object is not empty.
<configuration> <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"> <evaluator class="ch.qos.logback.classic.boolex.GEventEvaluator"> <expression> e.marker?.contains("NOTIFY_ADMIN") || e.marker?.contains("TRANSACTION_FAILURE") </expression> </evaluator> ... same as aboveffdfdvc </appender> </configuration>
The following SMTPAppender uses SSL
<configuration> <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"> <smtpHost>smtp.gmail.com</smtpHost> <smtpPort>465</smtpPort> <SSL>true</SSL> <username>YOUR_USERNAME@gmail.com</username> <password>YOUR_GMAIL_PASSWORD</password> <to>EMAIL-DESTINATION</to> <to>ANOTHER_EMAIL_DESTINATION</to> <!-- additional destinations are possible --> <from>YOUR_USERNAME@gmail.com</from> <subject>TESTING: %logger{20} - %m</subject> <layout class="ch.qos.logback.classic.PatternLayout"> <pattern>%date %-5level %logger{35} - %message%n</pattern> </layout> </appender> <root level="DEBUG"> <appender-ref ref="EMAIL" /> </root> </configuration>
The following SMTPAppender uses STARTTLS
<configuration> <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"> <smtpHost>smtp.gmail.com</smtpHost> <smtpPort>587</smtpPort> <STARTTLS>true</STARTTLS> <username>YOUR_USERNAME@gmail.com</username> <password>YOUR_GMAIL_xPASSWORD</password> <to>EMAIL-DESTINATION</to> <to>ANOTHER_EMAIL_DESTINATION</to> <!-- additional destinations are possible --> <from>YOUR_USERNAME@gmail.com</from> <subject>TESTING: %logger{20} - %m</subject> <layout class="ch.qos.logback.classic.PatternLayout"> <pattern>%date %-5level %logger - %message%n</pattern> </layout> </appender> <root level="DEBUG"> <appender-ref ref="EMAIL" /> </root> </configuration>
SMTPAppender and MDCDiscriminator
As mentioned earlier, the default Discriminator distributes logging event s to different buffers according to the return value.
The following is an implementation of the mdcddiscriminator using one of the logback discriminators to complete the function of distinguishing log buffers by remote host ip
<configuration> <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"> <smtpHost>ADDRESS-OF-YOUR-SMTP-HOST</smtpHost> <to>EMAIL-DESTINATION</to> <from>SENDER-EMAIL</from> <discriminator class="ch.qos.logback.classic.sift.MDCBasedDiscriminator"> <key>req.remoteHost</key> <defaultValue>default</defaultValue> </discriminator> <subject>${HOSTNAME} -- %X{req.remoteHost} %msg"</subject> <layout class="ch.qos.logback.classic.html.HTMLLayout"> <pattern>%date%level%thread%X{req.remoteHost}%X{req.requestURL}%logger%msg</pattern> </layout> </appender> <root> <level level="DEBUG"/> <appender-ref ref="EMAIL" /> </root> </configuration>
How to manage circular buffers in a busy system?
If you use a custom discriminator, you may create many new circular buffers, but maxNumberOfBuffers (default: 64). Whenever the number of buffers is greater than this value, the least recently updated buffer will be discarded by the "least recently updated algorithm". For security reasons, if the buffer is not updated within 30 minutes, it will also be discarded.
In an application with large TPS (transaction volume per second), if maxNumberOfBuffer is set too small, the number of logs per message will often be small. The reason is that the buffer may be created, destroyed and recreated. This cycle leads to a low amount of logs in the final buffer.
To prevent this vacillating effect, when SMTPAppender encounters a log event labeled "FINALIZE_SESSION", it will release the corresponding buffer. In this way, we can customize the buffer recycling strategy and no longer use the least recent update algorithm. You can also set the value of maxNumberOfBuffers to a larger value, such as 5121024, etc.
DBAppender
DBAppender Log events can be inserted into 3 data tables. They are logging_event,logging_event_property,logging_event_exception. These three data tables must exist before DBAppender works. Their SQL scripts can be found in the logback classic / SRC / main / Java / CH / QoS / logback / classic / db / script folder directory. This script is valid for most SQL databases. Except for a few, a few syntax differences need to be adjusted.
The following is the support information for logback and common databases:
RDBMS | tested version(s) | tested JDBC driver version(s) | supports getGeneratedKeys() method | is a dialect provided by logback |
---|---|---|---|---|
DB2 | untested | untested | unknown | NO |
H2 | 1.2.132 | - | unknown | YES |
HSQL | 1.8.0.7 | - | NO | YES |
Microsoft SQL Server | 2005 | 2.0.1008.2 (sqljdbc.jar) | YES | YES |
MySQL | 5.0.22 | 5.0.8 (mysql-connector.jar) | YES | YES |
PostgreSQL | 8.x | 8.4-701.jdbc4 | NO | YES |
Oracle | 10g | 10.2.0.1 (ojdbc14.jar) | YES | YES |
SQLLite | 3.7.4 | - | unknown | YES |
Sybase SQLAnywhere | 10.0.1 | - | unknown | YES |
Let's take a look at the table structure information of the three data tables:
logging_event
Field | Type | Description |
---|---|---|
timestamp | big int | Log event creation time |
formatted_message | text | Format the information of logging event The message that has been added to the logging event, after formatting with org.slf4j.impl.MessageFormatter, in case objects were passed along with the message. |
logger_name | varchar | name of logger |
level_string | varchar | Log event level |
reference_flag | smallint | This property is used by logback to determine whether the logging event contains exception or MDC property This value is determined by ch.qos. logback. classic. db. Calculated by dbhelper. When logging event contains MDC or context attribute, it is 1; When the exception is contained, it is 2; The first two are 3. |
caller_filename | varchar | The file name of the log event caller |
caller_class | varchar | The class name of the log event caller |
caller_method | varchar | Functions of log event callers |
caller_line | char | The number of lines of code that trigger log events |
event_id | int | The id of the log event in the database table. Primary key |
logging_event_property
Field | Type | Description |
---|---|---|
event_id | int | The id of the log event in the database table. Primary key |
mapped_key | varchar | key of MDC property |
mapped_value | text | value of MDC property |
logging_event_exception
Field | Type | Description |
---|---|---|
event_id | int | The id of the log event in the database table. Primary key |
i | smallint | The index of the line in the full stack trace. |
trace_line | varchar | The corresponding line |
Next, let's look at how to configure the database connection: the experiment shows that it takes about 10 milliseconds to insert a log without using the database connection pool technology, while it takes about 1 millisecond to use the connection pool. Therefore, it is recommended to use database connection pool to obtain database connection objects. The common one is C3P0.
The following is an example of using C3P0: (recommended)
<configuration> <appender name="DB" class="ch.qos.logback.classic.db.DBAppender"> <connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource"> <dataSource class="com.mchange.v2.c3p0.ComboPooledDataSource"> <driverClass>com.mysql.jdbc.Driver</driverClass> <jdbcUrl>jdbc:mysql://${serverName}:${port}/${dbName}</jdbcUrl> <user>${user}</user> <password>${password}</password> </dataSource> </connectionSource> </appender> <root level="DEBUG"> <appender-ref ref="DB" /> </root> </configuration>
In addition to C3P0, you can also use the ch.qos provided by logback logback. core. db. DriverManagerConnectionSource
<configuration debug="true"> <appender name="DB" class="ch.qos.logback.classic.db.DBAppender"> <connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource"> <dataSource class="${dataSourceClass}"> <!-- Joran cannot substitute variables that are not attribute values. Therefore, we cannot declare the next parameter like the others. --> <param name="${url-key:-url}" value="${url_value}"/> <serverName>${serverName}</serverName> <databaseName>${databaseName}</databaseName> </dataSource> <user>${user}</user> <password>${password}</password> </connectionSource> </appender> <root level="INFO"> <appender-ref ref="DB" /> </root> </configuration>
Of course, you can also use JNDI to configure the data source, and then obtain the connection object from the JNDI connection pool
<Context docBase="/path/to/app.war" path="/myapp"> ... <Resource name="jdbc/logging" auth="Container" type="javax.sql.DataSource" username="..." password="..." driverClassName="org.postgresql.Driver" url="jdbc:postgresql://localhost/..." maxActive="8" maxIdle="4"/> ... </Context> <configuration debug="true"> <appender name="DB" class="ch.qos.logback.classic.db.DBAppender"> <connectionSource class="ch.qos.logback.core.db.JNDIConnectionSource"> <!-- please note the "java:comp/env/" prefix --> <jndiLocation>java:comp/env/jdbc/logging</jndiLocation> </connectionSource> </appender> <root level="INFO"> <appender-ref ref="DB" /> </root> </configuration>
The following is the configuration of non connection pool. Just have a look:
This configuration will retrieve the database connection object every time the log is inserted.
<configuration> <appender name="DB" class="ch.qos.logback.classic.db.DBAppender"> <connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource"> <driverClass>com.mysql.jdbc.Driver</driverClass> <url>jdbc:mysql://host_name:3306/datebase_name</url> <user>username</user> <password>password</password> </connectionSource> </appender> <root level="DEBUG" > <appender-ref ref="DB" /> </root> </configuration>
SyslogAppender
SyslogAppender It is a very simple protocol: a syslog sender sends a small message to the syslog receiver. This receiver is usually called syslog daemon or syslog server. logback can send messages to remote receivers
Here are the configuration items
Property Name | Type | Description |
---|---|---|
syslogHost | String | Hostname of syslog server |
port | String | server port, default: 514 |
facility | String | The facility attribute is used to identify the source of the message Must be set to KERN, USER, MAIL, DAEMON, AUTH, SYSLOG, LPR, NEWS, UUCP, CRON, AUTHPRIV, FTP, NTP, AUDIT, ALERT, CLOCK, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. One of these strings. |
suffixPattern | String | Specify the message format to send. Default value: [%thread] %logger %msg . Referenced syntax PatternLayout |
stackTracePattern | String | The stackTracePattern property allows the customization of the string appearing just before each stack trace line. The default value for this property is "\t", i.e. the tab character. Any value accepted by PatternLayout is a valid value for stackTracePattern. |
throwableExcluded | boolean | Setting throwableExcluded to true will cause stack trace data associated with a Throwable to be omitted. By default, throwableExcluded is set to false so that stack trace data is sent to the syslog server. |
A small example
<configuration> <appender name="SYSLOG" class="ch.qos.logback.classic.net.SyslogAppender"> <syslogHost>remote_home</syslogHost> <facility>AUTH</facility> <suffixPattern>[%thread] %logger %msg</suffixPattern> </appender> <root level="DEBUG"> <appender-ref ref="SYSLOG" /> </root> </configuration>
When testing this example, you need to allow the remote syslog daemon to receive external request s. The default is rejected.
SiftingAppender
By name, SiftingAppender provides the function of filtering logs. You can filter the logs through the data of users' sessions, and then distribute them to different log files.
Let's take a look at an example to illustrate:
<configuration> <appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender"> <!-- in the absence of the class attribute, it is assumed that the desired discriminator type is ch.qos.logback.classic.sift.MDCBasedDiscriminator --> <discriminator> <key>userid</key> <defaultValue>unknown</defaultValue> </discriminator> <sift> <appender name="FILE-${userid}" class="ch.qos.logback.core.FileAppender"> <file>${userid}.log</file> <append>false</append> <layout class="ch.qos.logback.classic.PatternLayout"> <pattern>%d [%thread] %level %mdc %logger{35} - %msg%n</pattern> </layout> </appender> </sift> </appender> <root level="DEBUG"> <appender-ref ref="SIFT" /> </root> </configuration> logger.debug("Application started"); MDC.put("userid", "Alice"); logger.debug("Alice says hello");
This example stores logs with different user IDs in different log files by setting the MDC attribute.
SiftingAppender accomplishes these functions by creating a built-in appender, using < sift > < appender > < / appender > < / sift >. The SiftingAppender is responsible for managing the life cycle of these child Appenders. The SiftingAppender automatically closes and removes expired Appenders. If the built-in appender has not performed log output within the time specified by the timeout attribute, it will be considered expired.
In this example, if the discriminator is not specified, mdcbaseddediscriminator will be used by default. If the value of the specified key is null, then < DefaultValue > will be used
SitingAppender has the following two properties
Property Name | Type | Description |
---|---|---|
timeout | Duration | Set the expiration time when the built-in appender does not have log output. Default: 30 minutes |
maxAppenderCount | integer | Set the maximum number of built-in Appenders that can be created. The default value is integer MAX_ VALUE |
Setting an appropriate timeout and maxAppenderCount is not so easy. If it is too small, the built-in appender will be destroyed shortly after birth, and if it is too large, it will consume too many resources. Therefore, in many cases, we can also manually close the built-in appender by generating a marker to finalize_ The session log event. When the SiftingAppender sees the log event of this tag, it will end the bound appender. However, the destruction of the appender is not real-time, but wait a few seconds to prevent the loss of unprocessed logs. This is similar to the four wave process of TCP protocol.
Look at a small example
import org.slf4j.Logger; import static ch.qos.logback.classic.ClassicConstants.FINALIZE_SESSION_MARKER; void job(String jobId) { MDC.put("jobId", jobId); logger.info("Starting job."); ... do whather the job needs to do // will cause the nested appender reach end-of-life. It will // linger for a few seconds. logger.info(FINALIZE_SESSION_MARKER, "About to end the job"); try { .. perform clean up } catch(Exception e); // This log statement will be handled by the lingering appender. // No new appender will be created. logger.error("unexpected error while cleaning up", e); } }
AsyncAppender
Async appender records ILoggingEvents asynchronously. It is only equivalent to an event allocator, so it needs to cooperate with other Appenders to make a difference.
Note that AsyncAppender caches event s in BlockingQueue , a worker thread created by AsyncAppender will always get events from the head of the queue and then assign them to the adder uniquely associated with AsyncAppender. By default, if 80% of the queue is full, AsyncAppender will discard the log events of TRACE, DEBUG and INFO.
When the application is closed or redeployed, the AsyncAppender must be closed to stop, recycle and reuse the worker thread, and refresh the logging events in the buffer queue. What if you close AsyncAppender? You can close all Appenders, including AsyncAppender, by closing LoggerContext. AsyncAppender will wait for worker thread to refresh all log events within the time set by maxFlushTime property. If you find that the buffered event is discarded when the LoggerContext is closed, you may need to increase the waiting time. Setting maxFlushTime to 0 means that AsyncAppender waits until the worker thread flushes out all buffered events.
According to the exit mode of the JVM, the work of the worker thread in processing the buffered events can be interrupted, resulting in the stranding of the remaining unprocessed events. The common reason for this phenomenon is when the LoggerContext is not completely closed, or when the JVM terminates those atypical control flows (unknown). In order to avoid the interruption of the working thread due to these conditions, a shutdown hook can be inserted when the JVM is running. The function of this hook is to close the LoggerContext when the JVM starts shutdown.
The following is the property sheet of AsyncAppender
Property Name | Type | Description |
---|---|---|
queueSize | int | Set the maximum capacity of the blocking queue. The default is 256 events |
discardingThreshold | int | By default, when the blocking queue is occupied by more than 80%, AsyncAppender will discard log events with levels of TRACE, DEBUG and INFO. If you want to keep all levels of logs, you need to set it to 0 |
includeCallerData | boolean | It is expensive to extract CallerData. In order to improve performance, caller data is not provided by default. Only some data with low cost, such as thread name, will be retained. If set to true, caller data will be included |
maxFlushTime | int | Sets the maximum wait for refresh events, in milliseconds. When LoggerContext is closed, AsyncAppender will wait for the worker thread to complete the flush of events within this time, and the events that are not processed after timeout will be discarded. |
neverBlock | boolean | The default value is false. If the queue is filled, applications will be blocked in order to process all logs. If true, you will also choose to discard some message s in order not to block your application. |
By default, the maximum capacity of event queue is 256. If the queue is full, your application will be blocked until the queue can accommodate new logging event s. Therefore, when the AsyncAppender work queue is full, it can be called pseudo synchronization.
The following four situations can easily lead to the occurrence of AsyncAppender pseudo synchronization status:
- There are a large number of threads in the application
- A large number of logging events are generated per second
- Each logging event contains a large amount of data
- High latency in child Appenders
In order to avoid pseudo synchronization, improving queueSizes is generally effective, but it consumes the available memory of the application.
Having said so much, let's take a look at a small example
<configuration> <appender name="FILE" class="ch.qos.logback.core.FileAppender"> <file>myapp.log</file> <encoder> <pattern>%logger{35} - %msg%n</pattern> </encoder> </appender> <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender"> <appender-ref ref="FILE" /> </appender> <root level="DEBUG"> <appender-ref ref="ASYNC" /> </root> </configuration>
Write your own Appender
You can simply create your own appender by inheriting the parent class AppenderBase. AppenderBase has implemented support for filters, status and other functions shared by most Appenders. All we have to do is implement the append(Object evenObject) method.
Let's take an example
package chapters.appenders; import java.io.IOException; import ch.qos.logback.classic.encoder.PatternLayoutEncoder; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.AppenderBase; public class CountingConsoleAppender extends AppenderBase<ILoggingEvent> { static int DEFAULT_LIMIT = 10; int counter = 0; int limit = DEFAULT_LIMIT; PatternLayoutEncoder encoder; public void setLimit(int limit) { this.limit = limit; } public int getLimit() { return limit; } @Override public void start() { if (this.encoder == null) { addError("No encoder set for the appender named ["+ name +"]."); return; } try { encoder.init(System.out); } catch (IOException e) { } super.start(); } public void append(ILoggingEvent event) { if (counter >= limit) { return; } // output the events as formatted by our layout try { this.encoder.doEncode(event); } catch (IOException e) { } // prepare for next event counter++; } public PatternLayoutEncoder getEncoder() { return encoder; } public void setEncoder(PatternLayoutEncoder encoder) { this.encoder = encoder; } }
This example tells us two points
- All properties are set transparently in the configuration file through setter/getter. The start() method will be called automatically when logback reads the configuration file configuration, and is responsible for checking whether the relevant properties are set incorrectly.
- AppenderBase.doAppend() will call the append() method of the subclass to complete the log output. In particular, you can also call layout to format the log in the append() method.
After talking about so many things about logbakc classic, let's take a look at logback access
Most appenders existing in logback classic have similar equivalents in logback access. Most of them are similar, except that ILogginEvent becomes AccessEvent. Let's talk about their differences
In SMTPAppender, the Evaluator attribute is used by default. URLEvaluator is used. Let's take a look at an example
<appender name="SMTP" class="ch.qos.logback.access.net.SMTPAppender"> <layout class="ch.qos.logback.access.html.HTMLLayout"> <pattern>%h%l%u%t%r%s%b</pattern> </layout> <Evaluator class="ch.qos.logback.access.net.URLEvaluator"> <URL>url1.jsp</URL> <URL>directory/url2.html</URL> </Evaluator> <from>sender_email@host.com</from> <smtpHost>mail.domain.com</smtpHost> <to>recipient_email@host.com</to> </appender>
When the current URL matches one of the paths specified by multiple < URL > tags, it will trigger the sending of an email.
In DBAppender, there are also differences. First, only two tables are used, access_event and access_event_header. Their creation sql scripts can be found in logback access / SRC / main / Java / CH / QoS / logback / access / db / script.
See the structure in the table below
access_event
Field | Type | Description |
---|---|---|
timestamp | big int | The timestamp that was valid at the access event's creation. |
requestURI | varchar | The URI that was requested. |
requestURL | varchar | The URL that was requested. This is a string composed of the request method, the request URI and the request protocol. |
remoteHost | varchar | The name of the remote host. |
remoteUser | varchar | The name of the remote user. |
remoteAddr | varchar | The remote IP address. |
protocol | varchar | The request protocol, like HTTP or HTTPS. |
method | varchar | The request method, usually GET or POST. |
serverName | varchar | The name of the server that issued the request. |
event_id | int | The database id of the access event. |
access_event_header
Field | Type | Description |
---|---|---|
event_id | int | The database id of the corresponding access event. |
header_key | varchar | The header name, for example User-Agent. |
header_value | varchar | The header value, for example Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.1) Gecko/20061010 Firefox/2.0 |
In DBAppender, all attributes supported in the classic module are supported in the access module, and access also provides one more attribute
Property Name | Type | Description |
---|---|---|
insertHeaders | boolean | Tells the DBAppender to populate the database with the header information of all incoming requests. |
Here is an example
<configuration> <appender name="DB" class="ch.qos.logback.access.db.DBAppender"> <connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource"> <driverClass>com.mysql.jdbc.Driver</driverClass> <url>jdbc:mysql://localhost:3306/logbackdb</url> <user>logback</user> <password>logback</password> </connectionSource> <insertHeaders>true</insertHeaders> </appender> <appender-ref ref="DB" /> </configuration>
In the SiftingAppender, although most of them are similar, the default value of Discriminator is different. Logback access uses AccessEventDiscriminator by default instead of mdcbaseddediscriminator
The following is the Discriminator section of its configuration
<Discriminator class="ch.qos.logback.access.sift.AccessEventDiscriminator"> <Key>id</Key> <FieldName>SESSION_ATTRIBUTE</FieldName> <AdditionalKey>username</AdditionalKey> <defaultValue>NA</defaultValue> </Discriminator>
The value of < fieldname > can be set to COOKIE, REQUEST_ATTRIBUTE, SESSION_ATTRIBUTE, REMOTE_ ADDRESS, LOCAL_ PORT, REQUEST_ One of the URIs. And cookie, request_ ATTRIBUTE, SESSION_ If attribute is set to three values, the < AdditionalKey > ID must be set to get the attribute. When the value corresponding to the key set by AdditionalKey is Null, the value set by < DefaultValue > is used as the default value. The value set by < key > is equivalent to the obtained value. The key obtained in the child appender is equivalent to the key in the key value key value pair.
Here is a complete example:
<configuration> <appender name="SIFTING" class="ch.qos.logback.access.sift.SiftingAppender"> <Discriminator class="ch.qos.logback.access.sift.AccessEventDiscriminator"> <Key>id</Key> <FieldName>SESSION_ATTRIBUTE</FieldName> <AdditionalKey>username</AdditionalKey> <defaultValue>NA</defaultValue> </Discriminator> <sift> <appender name="ch.qos.logback:logback-site:jar:1.1.7" class="ch.qos.logback.core.FileAppender"> <file>byUser/ch.qos.logback:logback-site:jar:1.1.7.log</file> <layout class="ch.qos.logback.access.PatternLayout"> <pattern>%h %l %u %t \"%r\" %s %b</pattern> </layout> </appender> </sift> </appender> <appender-ref ref="SIFTING" /> </configuration>