監控日誌採集的一些TIPS

需求: 將日誌按照不同的模塊和日誌級別輸入到不同的日誌文件裏.
    實現方式1:
        最初的想法是用 LoggerFactory.getLogger(logName),然後爲不同的logName 定義不同的logger指向不同的FileAppender
        缺點:由於Logger 的名字改變,不再能根據每個類的名字動態調整日誌級別,對錯誤排查影響較大
    
    
    實現方式2:
        利用Log4j2的Lookup功能動態構建文件名 參考https://logging.apache.org/log4j/2.x/manual/lookups.html
        缺點是這個只能在Logger 定義之前指定一個模塊:如
        static {
            System.setProperty("module", "module1");
        }
        public static final Logger logger = LoggerFactory.getLogger(Module1.class);

        
        沒辦法在運行時動態切換日誌文件
    
    實現方式3:
        按利用log4j2 的路由功能 (RoutingAppender)可以根據上下文變量將日誌動態路由到的不同的文件
        具體配置如下:
        <RollingFile name="RollingFilePre" fileName="test-module1.log" filePattern="test-module1.%d{yyyy-MM-dd}.%i.log" append="true">
            <DynaJsonLayout compact="true" eventEol="true"/>    
            <Policies>
                <SizeBasedTriggeringPolicy size="1048576"/>
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
            </Policies>
            <DefaultRolloverStrategy max="200"/>
        </RollingFile>     
        <RollingFile name="RollingFileRule" fileName="test-module2.log" filePattern="test-module2.%d{yyyy-MM-dd}.%i.log" append="true">
            <DynaJsonLayout compact="true" eventEol="true"/>
            <Policies>
                <SizeBasedTriggeringPolicy size="1048576"/>
                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
            </Policies>
            <DefaultRolloverStrategy max="200"/>
        </RollingFile>     
        <Routing name="Routing" >
            <Routes pattern="${sys:module}">
                <Route key="val1" ref="RollingFilePre">
                </Route>
                <Route key="val2" ref="RollingFileRule">
                </Route>
            </Routes>
        </Routing>

        
        這樣可以在程序中隨時調用 System.setProperty() 來切換日誌文件, 也可以通過其他選項比如ThreadContext property 來動態切換
    
        注意:如果需要程序中動態切換文件,route 和 異步日誌 async 會有一定的衝突,切換時可能數據還在內存隊列裏,這樣可能打不到正確的文件裏
        
        按日誌級別分文件則是另外一種思路,利用ThresholdFilter 將日誌同時輸出到A,B兩個Appender, 再根據日誌級別過濾. 比如A只接收Error 級別的日誌
        B只接收Error以下級別的日誌,奇怪的是Log4j2 沒有把兩者統一起來
        <root level="debug">  
            <appender-ref ref="Console"></appender-ref>
            <appender-ref ref="RoutingFileAsync"/>  
        </root>
        <Async name="RoutingFileAsync" bufferSize="300000">
            <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
            <AppenderRef ref="Routing"/>
            <ArrayBlockingQueue/>
        </Async>
        <Console name="Console" target="SYSTEM_OUT">  
            <ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="ACCEPT"/>
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>  
        </Console>      

    
LOGSTASH採集:
     利用Log4j2   JsonLayout,程序中會有一個工具類將錯誤日誌格式變成JSON的,類似於 {"errorCode":xxx, "errorMessage":xxx}
     但是JsonLayout 會把日誌報文統一放到message 屬性中,這樣在日誌記錄進入Elasticsearch 仍然無法直接被搜索,我們的期望是將
     JSON消息報文中的屬性展開成多個頂級屬性而不是嵌套在message中
     一種思路是在日誌從KAFKA到Elasticsearch 的時候做一些轉換,不確定Logstash 的mutate是否支持
     另一種思路是定製一個自己的JsonLayout 重載toSerializable方法來自定義輸出的格式和內容, 具體實現步驟如下
     1.定義 DynaLogEvent 繼承了log4j 原生的 LogEvent, 增加了一個Map 類型的 attributes 屬性,如果原生Log4jEvent 中的Message是Json格式則就把解析出的鍵值對存到
     attributes 中  
    
     2. 定義了 DynaJsonLayout 通過 Plugin Annotation 註冊到Log4j2 中,同時log4j2.xml 中需要聲明 <configuration packages="org.apache.logging.log4j.core.layout">
     來指定掃描插件的路徑 DynaJsonLayout 主要就是重載了 toSerializable 方法,將原生的 LogEvent 轉化爲 DynaLogEvent 並通過 json ObjectWriter 輸出成json 格式
     ObjectWriter 使用了定製的 DynaLog4jJsonObjectMapper, 通過工廠類DynaJacksonFactory來構建,DynaJsonLayout 初始化時將 DynaLog4jJsonObjectMapper
     注入ObjectWriter 中
    
     3. 實踐中發現attributes 並不能被Log4jJsonObjectMapper正確的序列化 (注:Log4j2的Log4jJsonObjectMapper使用了大量jackson的高級技巧來定製LogEvent的序列化,
     如Module, Mixin 等,大家有興趣可以閱讀下代碼,這裏不詳細展開了), 所以爲DynaLogEvent中的attributes屬性添加了JsonIgnore Annotation 避免它被自動序列化,
     並定製了DynaLogEventSerializer 來自定義序列化 attributes, DynaLogEventSerializer通過 DynaLog4jJsonModule 註冊到 DynaLog4jJsonObjectMapper 中
    
     源代碼:https://github.com/wispershadow/myopensources/tree/master/jsonlogger   

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章