Java:如何将原始JSON记录为JSON,并避免在使用logback / slf4j进行日志记录期间转义

我在JAX-RS应用程序中使用带有Logback的SLF4J …我想以这样的方式登录到JSON,即我的消息不再被编码,而是原始打印到日志文件中:

目前它看起来像这样:

{"@timestamp":1363834123012,"@message":"{\"text\":\"From MLK to Barack Ob...\n\"}" 

但我希望有这个:

  {"@timestamp":1363834123012,"@message": { "text ": "From MLK to Barack Ob...\n\} 

原因是我想再次解析JSON并希望避免数据的转义。

我写了一个自定义的logback编码器,但我发现无法避免转义。 我可以传递对象进行回溯并根据对象的类型更改设置吗?

编辑:我找到了一种方式 – 不完全优雅 – 按照SSCE的要求:

在我的申请中

 // SLF4J Logger private static Logger logger = LoggerFactory.getLogger(MyClass.class); // A logback? Marker private Marker foo = MarkerFactory.getMarker("foo"); // Jackson ObjectMapper() ObjectMapper mapper = new ObjectMapper(); // Log something... logger.info(foo, mapper.writeValueAsString(json)); 

我使用了这里找到的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.jackson.JsonGenerator.Feature; import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.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 { 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; } } 

它现在有效! 是啊! 但我想这不是最好的方法(序列化,反序列化JSON ……)

使用JSON,Logback不会发生任何exception。 它只是一个正常记录的字符串。 逃避可能发生在你的最后,除非你在谈论某种以这种格式写出来的JSON Appender。 我很确定Logback本身没有这样的东西,所以如果那是你的问题,那么你想看看你从哪里得到Appender。 SSCCE将有助于进一步排除故障。

如果你有一个Json格式的消息,上面的解决方案可以工作,但不是很好,因为你不想每次在代码中使用你的记录器时调用特定于logstash的代码。

只需添加一个

 net.logstash.logback.encoder.LogstashEncoder 

是不够的,因为消息本身仍然存在 。 要解决此问题,请在logback.xml中尝试以下操作:

        { "jsonMessage": "#asJson{%message}" }     

#asJson模式将消除您的消息。

我遇到了同样的问题。 我用它解决了

   

代替

  

在我使用的java代码中:

 SRV_PERF_LOGGER.info(net.logstash.logback.marker.Markers.appendRaw("message", jackson.writeValueAsString(dto)), null); 

这是一个更新的(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"]) 

使用RawJsonAppendingMarker:

 log.trace(net.logstash.logback.marker.Markers.appendRaw("jsonMessage", jsonString), null); 

刚刚过来了,发现了一篇关于日志记录的文章。

如果使用maven将此依赖项放入pom.xml

  net.logstash.logback logstash-logback-encoder 3.4  

并把这样的东西放到logback.xml

    logs/${PROJECT_ID}.json   1 logs/${PROJECT_ID}.json.%i   1MB          

这将在logs/下创建example.json文件。 当文件大小达到1MB时,文件将被滚动一次。

 LOGGER.debug(append("object", someObject), "log message"); 

我无法看到导致您出现问题的原始代码,但我怀疑它可能看起来像这样

 JsonNode logOutput; String messageJSONAsString; 

 logOutput.put("@message", messageJSONAsString); logger.info(objectMapper.writeValueAsString(logOutput); 

这将在您的输出中生成转义的JSON,因为当您将消息放入输出JsonNode时,Jackson将为您重新转义它以确保输出是有效的JSON。

这里的解决方案是将消息作为ObjectNode而不是字符串放在输出中。 通常,您已经可以作为Object访问该对象,在这种情况下您可以执行此操作

 ObjectNode jsonObject = objectMapper.valueToTree(messageObject); logOutput.put("@message", jsonObject) 

否则,如果您的消息是JSON字符串,则解析它并将其添加到输出

 logoutput.put("@message", objectMapper.readTree(messageJSONAsString));