2016-12-20 123 views
1

顯然,log4j2中的JSONLayout沒有時間戳記模式支持。通常它只有JSON格式選項,但沒有這樣的pattern選項。Log4j2 JSONLayout時間戳記模式

{ 
    "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字段以最小的改變的技巧。

我看到幾個其他impls像JSONEventLayoutV1,這似乎是完全不同的impl比log4j json api,這是非常好的性能明智。

這裏是我的失敗嘗試覆蓋,LogEventhttps://github.com/prayagupd/sell-peace/blob/custom_timestamp/supply-peace/src/main/java/org/apache/logging/log4j/core/DnLogEvent.java

的問題越來越長,我基本上想知道重要的事情不是我重寫log4j2 API錯過。

回答

1

首先要做的是在Log4j2 JIRA issue tracker上提出功能請求。這聽起來像是可以讓許多用戶受益的東西,所以值得嘗試將其固定在Log4j中。

同時,讓我們看看自定義解決方案。我不會更改LogEvent,這會導致一個脆弱的解決方案(例如,可能不適用於Async Loggers和AsyncAppender)。當你想升級到更高版本的Log4j2時,你也可能遇到麻煩。 LogEvent已經有你需要的數據(timeMillis),它只需要格式化。

官方的方式是創建一個自定義的Json佈局插件。您可以進行重寫,也可以先複製代碼。 (在JIRA票證中提出的另一個話題。)要改變的關鍵類別可能是LogEventJsonMixIn

Log4j2使用Jackson生成json字符串。您可能需要將LogEventJsonMixIn替換爲提供格式化日期而非原始毫秒的版本。傑克遜可能已經有了反序列化器,否則你需要自己寫。 Log4j社區也可以提供更多的想法。

+0

感謝Remko。我可能會創建一個JIRA問題。我最終像你說的那樣做了,在'LogEvent'周圍創建了Wrapper'JsonLogEvent',它返回格式化的時間。而'CustomLogEventMixIn擴展LogEventMixIn'具有'timestamp' json鍵。 – prayagupd

+0

也許對別人有用。如果留在StackOverflow上,每個人都需要將其作爲自定義解決方案來實現。可能會很好地融入到Log4j中。 –

1

所以,總之我需要編寫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使用是非常有益的。