显然,log4j2中的JSONLayout没有时间戳模式支持。通常它只有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"
}
}
}
}
}
记录示例,
{
"timeMillis" : 1482231551081,
"thread" : "main",
"level" : "debug",
"endOfBatch" : false,
"threadId" : 1,
"threadPriority" : 5,
"message" : "log4j might suck"
}
当我查看他们的API时,看起来过于冗长,并且看不到添加时间戳字段的更简单方法。
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));
}
}
架构看起来比我预期的要复杂得多:(,我正在追踪Logger
。
我还考虑过改变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();
}
以及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()));
}
}
我猜它可能有用,虽然它打破了几个核心的log4j核心测试。我基本上想知道添加额外json字段的技巧,但变化最小。
我看到像JSONEventLayoutV1这样的其他一些动作,这似乎与log4j json api完全不同,这是非常好的性能。
这是我尝试覆盖的失败,LogEvent
,https://github.com/prayagupd/sell-peace/blob/custom_timestamp/supply-peace/src/main/java/org/apache/logging/log4j/core/DnLogEvent.java
问题越来越长,我基本上想知道当我覆盖log4j2 api时不要错过的重要事项。
答案 0 :(得分:4)
如果只是添加一个包含时间戳的新字段,除了默认提供的timeMillis之外,为什么不尝试在新的自定义字段上使用Lookup。
JsonLayout配置可能如下所示:
<JsonLayout>
<KeyValuePair key="timestamp" value="$${date:yyyy-MM-dd'T'HH:mm:ss.SSSZ}" />
</JsonLayout>
$$是查找,date:
后面的字符是java SimpleDateFormat可以接受的格式。 < / p>
答案 1 :(得分:3)
所以,总之我需要写7个对象。流程如下
CustomLogEvent
-> LogEventToCustomLogEventConverter
-> CustomLogEventMixIn
-> CustomLog4jJsonModule
-> CustomLog4jJsonObjectMapper
-> CustomJacksonFactory
-> CustomJSONLayout
CustomJSONLayout
是我在我的log4j2.json
中使用的插件,支持params作为配置。
因此,我最终同时使用了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()));
}
}
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
使用 Log4jJsonModule
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);
}
}
追踪JsonLayout
的使用方式非常有用。
答案 2 :(得分:1)
要做的第一件事是在Log4j2 JIRA issue tracker上提出功能请求。这听起来像是可以使许多用户受益的东西,因此值得尝试在Log4j中修复它。
与此同时,我们来看看自定义解决方案。我不会更改LogEvent,这将导致脆弱的解决方案(例如,可能无法与Async Loggers和AsyncAppender一起使用)。如果要升级到更高版本的Log4j2,也可能会遇到麻烦。 LogEvent
已经拥有您需要的数据(timeMillis
),只需要格式化即可。
官方方法是创建自定义Json布局插件。您可以进行重写,也可以从复制代码开始。 (在JIRA票证中提出的另一个主题。)要改变的关键类可能是LogEventJsonMixIn。
Log4j2使用Jackson生成json字符串。您可能需要将LogEventJsonMixIn替换为提供格式化日期而不是原始millis的版本。杰克逊可能已经有了Deserialiser,否则你需要自己编写。 Log4j社区也可以提供更多想法。