使用 log4j2 编写自定义 json 消息的最佳方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/39647900/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Best way to write custom json messages using log4j2
提问by pcvnes
I have been using log4j for different kind of projects and have some experience with log4j2. All implementations used the default appender and layout. Currently i need to write a application which writes in json format. So i tried the log4j2 JSONLayout layout by setting up a very simple log4j2 logger.
我一直在将 log4j 用于不同类型的项目,并且对 log4j2 有一些经验。所有实现都使用默认的 appender 和布局。目前我需要编写一个以 json 格式写入的应用程序。所以我通过设置一个非常简单的 log4j2 记录器来尝试 log4j2 JSONLayout 布局。
public class JSONLogger {
private static final Logger LOGGER = LogManager.getLogger();
public static void main(String[] args) {
JSONLogger jsonlogger = new JSONLogger() ;
}
public JSONLogger() {
LOGGER.log(Level.FATAL, "hi mum!") ;
int val1 = 10, val2 = 11, val3 = 12;
LOGGER.log(Level.FATAL,"val1={}, val2={}, val3={}", val1, val2, val3);
}
}
jsonLoggerProperties.xml
jsonLoggerProperties.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="DEBUG">
<Properties>
<Property name="log-path">/Users/petervannes/NetBeansProjects/JSONLogger/logfiles</Property>
</Properties>
<Appenders>
<RollingFile name="json_file_appender" fileName="${log-path}/jsonlogger.json"
filePattern="${log-path}/%d{yyyyMMdd}_jsonlogger-%i.json" >
<JSONLayout complete="true" compact="false"/>
<Policies>
<SizeBasedTriggeringPolicy size="1 KB" />
</Policies>
<DefaultRolloverStrategy max="4"/>
</RollingFile>
</Appenders>
<Loggers>
<root level="debug" additivity="false">
<AppenderRef ref="json_file_appender"/>
</root>
</Loggers>
</Configuration>
Resulting in an log entry similar to;
导致类似于的日志条目;
, {
"timeMillis" : 1474573600359,
"thread" : "main",
"level" : "FATAL",
"loggerName" : "JSONLogger",
"message" : "val1=10, val2=11, val3=12",
"contextStack" : [ "fieldName2" ],
"endOfBatch" : false,
"loggerFqcn" : "org.apache.logging.log4j.spi.AbstractLogger",
"threadId" : 1,
"threadPriority" : 5
}
What i need is to log to a JSON format like this;
我需要的是登录到这样的 JSON 格式;
, {
"DateTime" : "08/01/2016 21:33:22.334",
"level" : "FATAL",
"Summary" : "Something has gone wrong",
"ChainManager" : "Manager A",
"Optionals" : { "Key_1": "Value1",
"Key_2": "Value2" }
}
Is this possibile with the log4j2 JSONLayout or is there any other layout i can use to get this format ?
这是否可以与 log4j2 JSONLayout 一起使用,或者我可以使用其他任何布局来获取这种格式吗?
回答by Karsten R.
The question is about writing a custom json messages using log4j2.
问题是关于使用 log4j2 编写自定义 json 消息。
This is possible since version 2.11 of log4j2 version:
这是可能的,因为 log4j2 版本的 2.11 版:
https://issues.apache.org/jira/browse/LOG4J2-2190
https://issues.apache.org/jira/browse/LOG4J2-2190
The new parameter of for JSONLayoutis called
JSONLayout的新参数称为
objectMessageAsJsonObject
objectMessageAsJsonObject
. Sample project files;
. 示例项目文件;
log4j2.properties
log4j2.properties
status = error
appender.ana_whitespace.type = RollingFile
appender.ana_whitespace.name = ana_whitespace
appender.ana_whitespace.fileName = ${sys:es.logs.base_path:-target}${sys:file.separator}ana_whitespace.log
appender.ana_whitespace.layout.type = JsonLayout
appender.ana_whitespace.layout.propertiesAsList = false
appender.ana_whitespace.layout.compact = false
appender.ana_whitespace.layout.eventEol = true
appender.ana_whitespace.layout.objectMessageAsJsonObject = true
appender.ana_whitespace.layout.complete= true
appender.ana_whitespace.layout.properties= true
appender.ana_whitespace.filePattern = ${sys:es.logs.base_path:-target}${sys:file.separator}ana_whitespace-%d{yyyy-MM-dd}.log
appender.ana_whitespace.filter.1.type = MarkerFilter
appender.ana_whitespace.filter.1.onMismatch=DENY
appender.ana_whitespace.filter.1.onMatch=ACCEPT
appender.ana_whitespace.filter.1.marker=ANA_WHITESPACE
appender.ana_whitespace.policies.type = Policies
appender.ana_whitespace.policies.time.type = TimeBasedTriggeringPolicy
appender.ana_whitespace.policies.time.interval = 1
appender.ana_whitespace.policies.time.modulate = true
appender.ana_whitespace.policies.size.type = SizeBasedTriggeringPolicy
appender.ana_whitespace.policies.size.size = 10 MB
rootLogger.level = info
rootLogger.appenderRef.ana_whitespace.ref = ana_whitespace
Example Java code
示例 Java 代码
package de.es.stemmer;
import java.io.IOException;
import java.util.Map;
import java.util.TreeMap;
import org.apache.http.client.ClientProtocolException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.message.ObjectMessage;
public class JsonLoggerTest {
final static Logger log = LogManager.getLogger(JsonLoggerTest.class);
final static Marker MARKER_WHITESPACE = MarkerManager.getMarker("ANA_WHITESPACE");
public static void main(String[] args) throws ClientProtocolException, IOException {
System.setProperty("es.logs.base_path", "target");
System.setProperty("es.logs.cluster_name", "_cluster");
LoggerContext.getContext().reconfigure();
ThreadContext.put("orig", "MDC_origValue");
ThreadContext.put("source", "MDC_sourceSnippet");
Map<String, String> map = new TreeMap<>();
map.put("orig", "msg_origValue");
map.put("source", "msg_sourceSnippet");
ObjectMessage msg = new ObjectMessage(map);
log.info(MARKER_WHITESPACE, msg);
ThreadContext.remove("orig");
ThreadContext.remove("source");
}
}
JSON Log Entry
JSON 日志条目
[
{
"thread" : "main",
"level" : "INFO",
"loggerName" : "de.es.stemmer.JsonLoggerTest",
"marker" : {
"name" : "ANA_WHITESPACE"
},
"message" : {
"orig" : "msg_origValue",
"source" : "msg_sourceSnippet"
},
"endOfBatch" : false,
"loggerFqcn" : "org.apache.logging.log4j.spi.AbstractLogger",
"instant" : {
"epochSecond" : 1526576578,
"nanoOfSecond" : 184000000
},
"contextMap" : {
"orig" : "MDC_origValue",
"source" : "MDC_sourceSnippet"
},
"threadId" : 1,
"threadPriority" : 5
}
]
回答by pcvnes
I found a solution which works for me; slf4j-json-logger. It is a slf4j framework, so should be included in the pom.xml. Sample project files;
我找到了一个对我有用的解决方案;slf4j-json-logger。它是一个 slf4j 框架,所以应该包含在 pom.xml 中。示例项目文件;
pom.xml
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.reddipped</groupId>
<artifactId>JSONLogger_2</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.6</maven.compiler.source>
<maven.compiler.target>1.6</maven.compiler.target>
<mainClass>com.reddipped.jsonlogger_2.Test</mainClass>
<slf4j.version>1.7.21</slf4j.version>
<!-- current log4j 2 release -->
<log4j.version>2.6.2</log4j.version>
</properties>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- Binding for Log4J -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${log4j.version}</version>
</dependency>
<!-- Log4j API and Core implementation required for binding -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
</dependency>
<!-- Logger slf4j-json-logger -->
<dependency>
<groupId>com.savtheitroadtech.logging</groupId>
<artifactId>slf4j-json-logger</artifactId>
<version>2.0.2</version>
</dependency>
</dependencies>
</project>
log4j2.xml
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--
Use java property log4j.configurationFile to specify log4j2.xml location
if not available in classpath
- Dlog4j.configurationFile="/Users/petervannes/NetBeansProjects/JSONLogger_2/src/mann/java/resources/log4j2.xml"
-->
<configuration status="trace">
<Properties>
<Property name="log-path">/Users/petervannes/NetBeansProjects/JSONLogger_2/logfiles</Property>
</Properties>
<appenders>
<RollingFile name="RollingFile" fileName="${log-path}/jsonlogger.json"
filePattern="${log-path}/%d{yyyyMMdd}_jsonlogger-%i.json" >
<PatternLayout>
<pattern>%m%n</pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="1 KB" />
</Policies>
<DefaultRolloverStrategy max="4"/>
</RollingFile>
</appenders>
<Loggers>
<Logger name="JSONLogger" level="debug" additivity="false">
<AppenderRef ref="RollingFile" />
</Logger>
<Root level="debug">
<AppenderRef ref="RollingFile" />
</Root>
</Loggers>
</configuration>
Example Java code
示例 Java 代码
package com.reddipped.jsonlogger_2;
import com.savtheitroadtech.logging.slf4j.json.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
/**
*
* @author petervannes
*/
public class Test {
public static void main(String[] args) {
Test t = new Test() ;
}
public Test() {
LoggerFactory.setIncludeLoggerName(false);
LoggerFactory.setDateFormatString("yyyy-MM-dd HH:mm:ss.SSS");
com.savtheitroadtech.logging.slf4j.json.logger.Logger LOGGER = LoggerFactory.getLogger("JSONLogger");
Map<String, String> optionalFields = new HashMap();
optionalFields.put("CaseNumber", "C12.12343");
optionalFields.put("Step","Assignment") ;
optionalFields.put("Department","BPM") ;
String LOB = "Business Administration" ;
String Service = "DocumentService" ;
String Process = "AddAttachements" ;
String Reason = "Technical" ;
LOGGER.error().message("Conversion error 'incompatible PDF document'")
.field("LOB",LOB)
.field("Service", Service)
.field("Process",Process)
.field("Reason", Reason)
.map("OptionalFields", optionalFields).log() ;
}
}
JSON Log Entry
JSON 日志条目
{
"message": "Conversion error 'incompatible PDF document'",
"LOB": "Business Administration",
"Service": "DocumentService",
"Process": "AddAttachements",
"Reason": "Technical",
"OptionalFields": {
"Step": "Assignment",
"Department": "BPM",
"CaseNumber": "C12.12343"
},
"level": "ERROR",
"thread_name": "main",
"class": "com.reddipped.jsonlogger_2.Test",
"@timestamp": "2016-09-23 10:18:06.623"
}
回答by D.B.
If you're looking for a way to generate customized JSON log messages without any of the "noise" added by log4j2 you could create an implementation of the Messageinterface and use a different layout - for example PatternLayout. Below is an example of this approach:
如果您正在寻找一种方法来生成自定义的 JSON 日志消息而没有 log4j2 添加的任何“噪音”,您可以创建Message界面的实现并使用不同的布局 - 例如PatternLayout. 下面是这种方法的一个例子:
First a class to implement Messageinferface
首先一个类来实现Messageinferface
import java.util.HashMap;
import java.util.Map;
import org.apache.logging.log4j.message.Message;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class JSONMessage implements Message {
private static final long serialVersionUID = 538439258494853324L;
private String messageString;
private static final Gson GSON = new GsonBuilder()
.setPrettyPrinting()
.create();
public JSONMessage(){
this(null);
}
public JSONMessage(Object msgObj){
parseMessageAsJson(msgObj);
}
public JSONMessage(String msgStr){
Map<String,String> msgObj = new HashMap<>();
msgObj.put("message", msgStr);
parseMessageAsJson(msgObj);
}
private void parseMessageAsJson(Object msgObj){
messageString = GSON.toJson(msgObj);
}
@Override
public String getFormattedMessage() {
return messageString;
}
@Override
public String getFormat() {
return messageString;
}
@Override
public Object[] getParameters() {
return null;
}
@Override
public Throwable getThrowable() {
return null;
}
}
Next the log4j2.xml configuration:
接下来是 log4j2.xml 配置:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" name="App">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%m%n" />
</Console>
</Appenders>
<Loggers>
<Root level="trace">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>
Now a simple application class to generate a log event
现在一个简单的应用程序类来生成日志事件
import java.util.HashMap;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class App {
private static final Logger logger = LogManager.getLogger();
public static void main( String[] args )
{
HashMap<String,Object> msgMap = new HashMap<>();
msgMap.put("someInt", 123);
msgMap.put("note", "Maybe you put a message here");
HashMap<String,Object> anotherMap = new HashMap<>();
anotherMap.put("key1", "value1");
anotherMap.put("key2", "value2");
msgMap.put("map", anotherMap);
logger.info(new JSONMessage(msgMap));
}
}
Here is some sample output:
这是一些示例输出:
{
"note": "Maybe you put a message here",
"map": {
"key1": "value1",
"key2": "value2"
},
"someInt": 123
}
Note that I'm using Gsonto create the JSON output, but you could use any library you want. Also note that this code does not generate "complete" JSON in that it does not add the square brackets at the start and end of the log or the comma between message objects.
请注意,我使用Gson创建 JSON 输出,但您可以使用任何您想要的库。另请注意,此代码不会生成“完整”的 JSON,因为它不会在日志的开头和结尾添加方括号或消息对象之间的逗号。

