简体   繁体   English

Log4j2 JSONLayout 时间戳模式

[英]Log4j2 JSONLayout timestamp pattern

Apparently, JSONLayout in log4j2 doesn't have timestamp pattern support.显然, log4j2中的 JSONLayout 不支持时间戳模式。 Normally it only has JSON formatting options, but nothing as such pattern option.通常它只有 JSON 格式选项,但没有这样的pattern选项。

{
  "configuration": {
    "name": "logggg",
    "packages" : "logger.savemyjob",
    "appenders": {
      "RollingFile": {
        "name": "rollingStone",
        "fileName": "async_rolled.log",
        "filePattern": "async_rolled-%d{MM-dd-yy-HH-mm-ss}-%i.log.gz",
        "immediateFlush" : false,
         "JSONLayout": {
            "complete": true,
            "compact": false,
            "eventEol": true
         },
        "SizeBasedTriggeringPolicy": {
          "size": "10 MB"
        },
        "DefaultRolloverStrategy": {
          "max": "10"
        }
      }
    },
    "loggers": {
      "root": {
        "level": "debug",
        "appender-ref": {
          "ref": "rollingStone"
        }
      }
    }
  }
}

Log Example,日志示例,

{
  "timeMillis" : 1482231551081,
  "thread" : "main",
  "level" : "debug",
  "endOfBatch" : false,
  "threadId" : 1,
  "threadPriority" : 5, 
  "message" : "log4j might suck"
}

And when I looked at their API, looks too verbose and don't see quite an easier way adding a timestamp field.当我查看他们的 API 时,它看起来过于冗长而且没有找到更简单的添加时间戳字段的方法。

JsonLayout plugin seems to be the one I need to override, since its final can't even extend but otherwise I have to copy the whole dependent classes. JsonLayout插件似乎是我需要覆盖的插件,因为它的final甚至不能扩展,否则我必须复制整个依赖类。

@Plugin(name = "JsonLayout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE, printObject = true)
public final class JsonLayout extends AbstractJacksonLayout {

protected JsonLayout(final Configuration config, final boolean locationInfo, final boolean properties,
            final boolean encodeThreadContextAsList,
            final boolean complete, final boolean compact, final boolean eventEol, final String headerPattern,
            final String footerPattern, final Charset charset) {
        super(config, new JacksonFactory.JSON(encodeThreadContextAsList).newWriter(locationInfo, properties, compact),
                charset, compact, complete, eventEol,
                PatternLayout.createSerializer(config, null, headerPattern, DEFAULT_HEADER, null, false, false),
                PatternLayout.createSerializer(config, null, footerPattern, DEFAULT_FOOTER, null, false, false));
    }

}

The architecture looks more complicated than I expected:(, I am tracing from the Logger .该体系结构看起来比我预期的要复杂:(,我正在从Logger进行跟踪。

I also considered changing the LogEvent itself,我还考虑过更改LogEvent本身,

public interface LogEvent extends Serializable {

    @Deprecated
    Map<String, String> getContextMap();

    ReadOnlyStringMap getContextData();

    ThreadContext.ContextStack getContextStack();

    String getLoggerFqcn();

    Level getLevel();

    String getLoggerName();

    Marker getMarker();

    Message getMessage();

    long getTimeMillis();

    StackTraceElement getSource();

    String getThreadName();

    long getThreadId();

    int getThreadPriority();

    Throwable getThrown();

    ThrowableProxy getThrownProxy();

    boolean isEndOfBatch();

    boolean isIncludeLocation();

    void setEndOfBatch(boolean endOfBatch);

    void setIncludeLocation(boolean locationRequired);

    long getNanoTime();

    String getTimestamp();
}

and also MutableLogEvent还有MutableLogEvent

public class MutableLogEvent implements LogEvent, ReusableMessage {

    public void initFrom(final LogEvent event) {

        SimpleDateFormat standardDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        this.timestamp = standardDateFormat.format(new Date(event.getTimeMillis()));
    }
}

I'm guessing it might work, though it broke few core log4j-core tests.我猜它可能会起作用,尽管它打破了几个核心 log4j-core 测试。 I basically want to know the tricks to add extra json field with minimum change.我基本上想知道以最少的更改添加额外的 json 字段的技巧。

I see few other impls like JSONEventLayoutV1 , which seems to be totally different impl than log4j json api which is pretty good performance wise.我几乎没有看到像JSONEventLayoutV1这样的其他 impl,它似乎与 log4j json api 完全不同,后者的性能非常好。

Here's my failed attempt to override, LogEvent , https://github.com/prayagupd/sell-peace/blob/custom_timestamp/supply-peace/src/main/java/org/apache/logging/log4j/core/DnLogEvent.java这是我失败的覆盖尝试, LogEventhttps://github.com/prayagupd/sell-peace/blob/custom_timestamp/supply-peace/src/main/java/org/apache/logging/log4j/core/DnLogEvent.java

The questions is getting longer, I basically want to know the important things not to miss when I override the log4j2 api.问题越来越长,我基本上想知道重写log4j2 api时不要错过的重要事情。

If this is just a matter of adding a new field containing the timestamp, apart from the timeMillis provided by default, why don't You try to use Lookups on the new custom field. 如果只是添加包含时间戳的新字段,除了默认提供的timeMillis之外,为什么不尝试在新的自定义字段上使用Lookup。

The JsonLayout configuration may then looks like this: JsonLayout配置可能如下所示:

<JsonLayout>
    <KeyValuePair key="timestamp" value="$${date:yyyy-MM-dd'T'HH:mm:ss.SSSZ}" />
</JsonLayout>

The $$ is the Lookup , and the characters behind date: are the format that java SimpleDateFormat can accept. $$是查找date:后面的字符是java SimpleDateFormat可以接受的格式

So, In short I needed to write 7 objects. 所以,简而言之,我需要编写7个对象。 The flow is as below 流程如下

CustomLogEvent 
    -> LogEventToCustomLogEventConverter 
       -> CustomLogEventMixIn 
           -> CustomLog4jJsonModule 
                  -> CustomLog4jJsonObjectMapper 
                      -> CustomJacksonFactory 
                          -> CustomJSONLayout

CustomJSONLayout is the plugin I would use in my log4j2.json that supports params as config. CustomJSONLayout是我将在我的log4j2.json中使用的插件,它支持params作为配置。

So, I ended up using a Inheritence and composition at the same time for LogEvent . 所以,我最终为LogEvent使用了一个继承和组合。

public class JsonLogEvent implements LogEvent{

    static final String TIMESTAMP_FORMAT = "yyyy-MM-dd HH:mm:ss";
    static final DateFormat isoDateFormat = new SimpleDateFormat(TIMESTAMP_FORMAT);

    private LogEvent wrappedLogEvent;

    public JsonLogEvent(LogEvent wrappedLogEvent) {
        this.wrappedLogEvent = wrappedLogEvent;
    }

    public String getTimestamp() {
        return isoDateFormat.format(new Date(this.getTimeMillis()));
    }
}

And CustomLogEventMixIn , that has timestamp as key. CustomLogEventMixIn ,它有timestamp作为键。

@JsonSerialize(converter = JsonLogEvent.LogEventToCustomLogEventConverter.class)
@JsonRootName(XmlConstants.ELT_EVENT)
@JsonFilter("org.apache.logging.log4j.core.impl.Log4jLogEvent")
@JsonPropertyOrder({"timestamp", "threadName", "level", "loggerName", "marker", "message", "thrown",
        XmlConstants.ELT_CONTEXT_MAP, JsonConstants.ELT_CONTEXT_STACK, "loggerFQCN", "Source", "endOfBatch", "timeMillis" })
abstract class CustomLogEventMixIn extends LogEventMixIn {

    @JsonProperty("timestamp")
    public abstract String getTimestamp();

    private static final long serialVersionUID = 1L;

}

public static class LogEventToCustomLogEventConverter extends StdConverter<LogEvent, JsonLogEvent> {

    @Override
    public JsonLogEvent convert(LogEvent value) {
        return new JsonLogEvent(value);
    }
}

LogEventMixIn is used by Log4jJsonModule Log4jJsonModule使用LogEventMixIn

public class CustomLog4jJsonModule extends Log4jJsonModule {

    private static final long serialVersionUID = 1L;

    CustomLog4jJsonModule() {
        super();
    }

    @Override
    public void setupModule(final SetupContext context) {
        super.setupModule(context);

        context.setMixInAnnotations(LogEvent.class, CustomLogEventMixIn.class);
    }
}

public class CustomLog4jJsonObjectMapper extends ObjectMapper {

    public CustomLog4jJsonObjectMapper() {
        this.registerModule(new CustomLog4jJsonModule());
        this.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
    }

}

Tracking down how JsonLayout being used was very helpful. 追踪JsonLayout的使用方式非常有帮助。

The first thing to do is to raise a feature request on the Log4j2 JIRA issue tracker . 首先要做的是在Log4j2 JIRA问题跟踪器上提出功能请求。 This sounds like something that could benefit many users so it's worth trying to get it fixed in Log4j itself. 这听起来像是可以使许多用户受益的东西,因此值得尝试在Log4j中修复它。

Meanwhile, let's look at a custom solution. 同时,让我们来看一个自定义解决方案。 I wouldn't change LogEvent, this will result in a fragile solution (may not work with Async Loggers and AsyncAppender for example). 我不会更改LogEvent,这将导致脆弱的解决方案(例如,可能无法与Async Loggers和AsyncAppender一起使用)。 Also you may run into trouble when you want to upgrade to later versions of Log4j2. 如果要升级到更高版本的Log4j2,也可能会遇到麻烦。 The LogEvent already has the data that you need ( timeMillis ), it just needs to get formatted. LogEvent已经拥有您需要的数据( timeMillis ),它只需要进行格式化。

The official way is to create a custom Json layout plugin. 官方的方法是创建一个自定义的Json布局插件。 You could do a rewrite or you can start with copying code. 您可以进行重写,也可以从复制代码开始。 (Another topic to raise in the JIRA ticket.) The key class to change is probably LogEventJsonMixIn . (在JIRA票证中提出的另一个主题。)要更改的关键类可能是LogEventJsonMixIn

Log4j2 uses Jackson to generate the json strings. Log4j2使用Jackson生成json字符串。 You probably need to replace LogEventJsonMixIn with a version that gives a formatted date instead of the raw millis. 您可能需要将LogEventJsonMixIn替换为提供格式化日期而不是原始millis的版本。 Jackson may have a Deserialiser for this already, otherwise you need to write your own. 杰克逊可能已经有了Deserialiser,否则你需要自己编写。 The Log4j community may also be able to give more ideas. Log4j社区也可以提供更多想法。

You can use the code below您可以使用下面的代码

<KeyValuePair key="@timestamp" value="$${date:dd-MM-yyyy HH:mm:ss}"/>

All configurations in log4j2.xml log4j2.xml中的所有配置

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
    <Properties>
        <Property name="activeProfile">${sys:spring.profiles.active}</Property>
    </Properties>
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <JSONLayout eventEol="true" complete="false" locationInfo="true" includeTimeMillis="true">
                <KeyValuePair key="@timestamp" value="$${date:dd-MM-yyyy HH:mm:ss}"/>
                <KeyValuePair key="requestId" value="$${ctx:X-Request-Id}"/>
            </JSONLayout>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console" />
        </Root>
    </Loggers>
</Configuration>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM