需求: 将日志按照不同的模块和日志级别输入到不同的日志文件里.
    实现方式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)可以根据上下文变量将日志动态路由到的不同的文件
        具体配置如下:
        
                
            
                
                
            

            
        
    
        
            
            
                
                
            

            
        
    
        
            
                
                

                
                

            

        

        
        这样可以在程序中随时调用 System.setProperty() 来切换日志文件, 也可以通过其他选项比如ThreadContext property 来动态切换
    
        注意:如果需要程序中动态切换文件,route 和 异步日志 async 会有一定的冲突,切换时可能数据还在内存队列里,这样可能打不到正确的文件里
        
        按日志级别分文件则是另外一种思路,利用ThresholdFilter 将日志同时输出到A,B两个Appender, 再根据日志级别过滤. 比如A只接收Error 级别的日志
        B只接收Error以下级别的日志,奇怪的是Log4j2 没有把两者统一起来
          
            
              
        

        
            
            
            
        

          
            
              
        
     

    
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 中需要声明
     来指定扫描插件的路径 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