Java:如何将原始 JSON 记录为 JSON 并避免在使用 logback / slf4j 进行记录期间进行转义
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15538688/
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
Java: How to log raw JSON as JSON and avoid escaping during logging with logback / slf4j
提问by mt_
I'm using SLF4J with Logback in a JAX-RS application... I want to log to JSON in such a way that my message is not encoded again but printed raw into the logfile:
我在 JAX-RS 应用程序中使用带有 Logback 的 SLF4J ......我想以这样的方式登录到 JSON,我的消息不会再次编码,而是原始打印到日志文件中:
At the moment it looks like this:
目前它看起来像这样:
{"@timestamp":1363834123012,"@message":"{\"text\":\"From MLK to Barack
Ob...\n\"}"
But I want to have this:
但我想要这个:
{"@timestamp":1363834123012,"@message": { "text ": "From MLK to Barack
Ob...\n\}
The reason is I want to parse the JSON again and want to avoid the unescaping of the data.
原因是我想再次解析 JSON 并希望避免数据的转义。
I've written a custom logback encoder but I found no way to avoid the escaping. Can I pass a object to logback and change the settings based on the type of the object?
我写了一个自定义的 logback 编码器,但我发现没有办法避免转义。我可以将对象传递给 logback 并根据对象的类型更改设置吗?
Edit: I've found a way - not exactly elegant - as requested a SSCE:
编辑:我找到了一种方法 - 不完全优雅 - 按照 SSCE 的要求:
In my Application
在我的应用程序中
// SLF4J Logger
private static Logger logger = LoggerFactory.getLogger(MyClass.class);
// A logback? Marker
private Marker foo = MarkerFactory.getMarker("foo");
// Hymanson ObjectMapper()
ObjectMapper mapper = new ObjectMapper();
// Log something...
logger.info(foo, mapper.writeValueAsString(json));
I've used a variation of the Logstash-Encoder found here: https://github.com/logstash/logstash-logback-encoder
我使用了此处找到的 Logstash-Encoder 的变体:https: //github.com/logstash/logstash-logback-encoder
package my.package;
import static org.apache.commons.io.IOUtils.*;
import java.io.IOException;
import java.util.Map;
import java.util.Map.Entry;
import org.codehaus.Hymanson.JsonGenerator.Feature;
import org.codehaus.Hymanson.JsonNode;
import org.codehaus.Hymanson.map.ObjectMapper;
import org.codehaus.Hymanson.node.ObjectNode;
import org.slf4j.Marker;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.classic.spi.ThrowableProxyUtil;
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.encoder.EncoderBase;
public class JsonEncoder extends EncoderBase<ILoggingEvent> {
private static final ObjectMapper MAPPER = new ObjectMapper().configure(
Feature.ESCAPE_NON_ASCII, true);
private static Marker M;
private boolean immediateFlush = true;
@Override
public void doEncode(ILoggingEvent event) throws IOException {
M = event.getMarker();
ObjectNode eventNode = MAPPER.createObjectNode();
eventNode.put("@timestamp", event.getTimeStamp());
//
if (M != null) {
if (M.getName().equals("foo")) {
JsonNode j = MAPPER.readTree(event.getFormattedMessage());
eventNode.put("@foo", j);
}
} else {
eventNode.put("@message", event.getFormattedMessage());
}
eventNode.put("@fields", createFields(event));
write(MAPPER.writeValueAsBytes(eventNode), outputStream);
write(CoreConstants.LINE_SEPARATOR, outputStream);
if (immediateFlush) {
outputStream.flush();
}
}
private ObjectNode createFields(ILoggingEvent event) {
// not important here
return fieldsNode;
}
@Override
public void close() throws IOException {
write(LINE_SEPARATOR, outputStream);
}
public boolean isImmediateFlush() {
return immediateFlush;
}
public void setImmediateFlush(boolean immediateFlush) {
this.immediateFlush = immediateFlush;
}
}
It's works now! Yeah! But I guess it's not the best way to do it (serialize, deserialize the JSON...)
它现在起作用了!是的!但我想这不是最好的方法(序列化,反序列化 JSON ......)
采纳答案by Ryan Stewart
Logback doesn't do anything unusual with JSON. It's just a string that gets logged as normal. The escaping is probably happening on your end, unless you're talking about some kind of JSON Appender that's writing it out in that format. I'm pretty sure Logback itself doesn't have anything like that, so you'd want to look at wherever you got the Appender from instead if that's your problem. An SSCCEwould help with further troubleshooting.
Logback 不会对 JSON 做任何不寻常的事情。它只是一个正常记录的字符串。转义可能发生在您的一端,除非您谈论的是某种以该格式将其写出的 JSON Appender。我很确定 Logback 本身没有类似的东西,所以如果这是你的问题,你会想看看你从哪里得到 Appender。一个SSCCE将有助于进一步的故障排除。
回答by Bernhard Kern
If you have a Json formatted messages, the upper solutions work, but are not so nice, since you don′t want to call a logstash specific code, each time you use your logger in the code.
如果你有一个 Json 格式的消息,上面的解决方案可以工作,但不是那么好,因为你不想每次在代码中使用你的记录器时调用一个特定的代码。
Just adding a
只需添加一个
net.logstash.logback.encoder.LogstashEncoder
is not enough, since the message itsself stays escaped. To solve this, try the following in your logback.xml:
是不够的,因为消息本身保持转义。要解决此问题,请在 logback.xml 中尝试以下操作:
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp/>
<version/>
<loggerName/>
<pattern>
<pattern>
{
"jsonMessage": "#asJson{%message}"
}
</pattern>
</pattern>
</providers>
</encoder>
The #asJson pattern will unescape your message.
#asJson 模式将转义您的消息。
回答by AxelW
Use the RawJsonAppendingMarker:
使用 RawJsonAppendingMarker:
log.trace(net.logstash.logback.marker.Markers.appendRaw("jsonMessage", jsonString), null);
回答by HaVonTe
I ran into the same problem. I solved it with
我遇到了同样的问题。我解决了
<encoder
class="net.logstash.logback.encoder.LogstashEncoder">
</encoder
instead of
代替
<encoder
class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
In my java code I used:
在我使用的java代码中:
SRV_PERF_LOGGER.info(net.logstash.logback.marker.Markers.appendRaw("message", Hymanson.writeValueAsString(dto)), null);
回答by bsautner
here is an updated (2016) groovy logback config that dumps out your logs in json format to a file, and debug lines in the console. Took me all day to figure out so i thought i'd update the thread.
这是一个更新的 (2016) groovy logback 配置,它将 json 格式的日志转储到文件中,并在控制台中调试行。花了我一整天的时间才弄清楚,所以我想我会更新线程。
import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.core.ConsoleAppender
import ch.qos.logback.core.rolling.FixedWindowRollingPolicy
import ch.qos.logback.core.rolling.RollingFileAppender
import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy
import net.logstash.logback.encoder.LogstashEncoder
import static ch.qos.logback.classic.Level.INFO
import static ch.qos.logback.classic.Level.WARN
def PROJECT_ID = "com.foo"
appender("file", RollingFileAppender) {
file = "/tmp/logs/${PROJECT_ID}.json"
encoder(LogstashEncoder)
rollingPolicy(FixedWindowRollingPolicy) {
maxIndex = 1
fileNamePattern = "logs/${PROJECT_ID}.json.%i"
}
triggeringPolicy(SizeBasedTriggeringPolicy) {
maxFileSize = "1MB"
}
}
appender("STDOUT", ConsoleAppender) {
encoder(PatternLayoutEncoder) {
pattern = "%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n"
}
}
logger("com.foo", INFO, ["STDOUT", "file"], false)
root(WARN, ["STDOUT", "file"])
回答by avec
Just came over this myself and found an article with a few recommandations on logging.
我自己刚刚过来,发现了一篇文章,其中有一些关于日志记录的建议。
If you use maven put this dependency into pom.xml
如果您使用 maven 将此依赖项放入 pom.xml
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>3.4</version>
</dependency>
And put something like this into logback.xml
把这样的东西放进去 logback.xml
<configuration>
<property name="PROJECT_ID" value="example"/>
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>logs/${PROJECT_ID}.json</File>
<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<maxIndex>1</maxIndex>
<FileNamePattern>logs/${PROJECT_ID}.json.%i</FileNamePattern>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>1MB</MaxFileSize>
</triggeringPolicy>
</appender>
<logger name="eu.kielczewski" additivity="false" level="INFO">
<appender-ref ref="file"/>
</logger>
<root level="WARN">
<appender-ref ref="file"/>
</root>
</configuration>
This creates example.json
file under logs/
. The file is rolled once when it reaches 1MB in size.
这将example.json
在logs/
. 当文件大小达到 1MB 时,将滚动一次。
LOGGER.debug(append("object", someObject), "log message");
回答by Tadhg
I can't see the original code that's causing your problem, but I suspect it might look like this
我看不到导致您出现问题的原始代码,但我怀疑它可能看起来像这样
JsonNode logOutput;
String messageJSONAsString;
...
...
logOutput.put("@message", messageJSONAsString);
logger.info(objectMapper.writeValueAsString(logOutput);
This will produce escaped JSON in your output because when you put the message into the output JsonNode, Hymanson will re-escape it for you to make sure the output is valid JSON.
这将在您的输出中生成转义的 JSON,因为当您将消息放入输出 JsonNode 时,Hymanson 将为您重新转义它以确保输出是有效的 JSON。
The solution here is to put the message in your output as an ObjectNode rather than as a string. Usually you already have access to the object as an Object, in which case you can do
此处的解决方案是将消息作为 ObjectNode 而不是字符串放在您的输出中。通常你已经可以访问对象作为对象,在这种情况下你可以做
ObjectNode jsonObject = objectMapper.valueToTree(messageObject);
logOutput.put("@message", jsonObject)
Otherwise, if your message is a JSON string, then parse it and add it to the output
否则,如果您的消息是 JSON 字符串,则对其进行解析并将其添加到输出中
logoutput.put("@message", objectMapper.readTree(messageJSONAsString));