[英]Java: How to log raw JSON as JSON and avoid escaping during logging with 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<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;
}
}
它现在有效! 是啊! 但我想这不是最好的方法(序列化,反序列化JSON ......)
如果你有一个Json格式的消息,上面的解决方案可以工作,但不是很好,因为你不想在每次在代码中使用你的记录器时调用特定于logstash的代码。
只需添加一个
net.logstash.logback.encoder.LogstashEncoder
是不够的,因为消息本身仍然存在 。 要解决此问题,请在logback.xml中尝试以下操作:
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp/>
<version/>
<loggerName/>
<pattern>
<pattern>
{
"jsonMessage": "#asJson{%message}"
}
</pattern>
</pattern>
</providers>
</encoder>
#asJson模式将消除您的消息。
使用RawJsonAppendingMarker:
log.trace(net.logstash.logback.marker.Markers.appendRaw("jsonMessage", jsonString), null);
我遇到了同样的问题。 我用它解决了
<encoder
class="net.logstash.logback.encoder.LogstashEncoder">
</encoder
代替
<encoder
class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
在我使用的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"])
使用JSON,Logback不会发生任何异常。 它只是一个正常记录的字符串。 逃避可能发生在你的最后,除非你在谈论某种以这种格式写出来的JSON Appender。 我很确定Logback本身没有这样的东西,所以如果那是你的问题,那么你想看看你从哪里得到Appender。 SSCCE将有助于进一步排除故障。
刚刚过来了,发现了一篇关于日志记录的文章。
如果你使用maven将这个依赖项放到pom.xml
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>3.4</version>
</dependency>
把这样的东西放到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>
这将在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));
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.