【问题标题】:Programmatic Custom Json Layout Log4j2程序化自定义 Json 布局 Log4j2
【发布时间】:2021-07-08 14:30:57
【问题描述】:

我有Java 桌面客户端应用程序,它利用Log4j2 framework. JSON 结构在使用自定义参数记录异常或信息时使用。出于这个原因,我正在尝试以编程方式配置 Log4j2。我知道建议使用基于文件的配置,但似乎最好以编程方式为我的案例初始化框架。

这是我正在尝试构建的结构。

  • 自定义 Json 布局
  • HttpAppender 将通过 HTTP 发送日志
  • 如果HttpAppender失败,会使用JdbcAppender写入数据库
  • 如果 JdbcAppender 失败,它将使用FileAppender 写入日志文件

我能够生成JsonLayout,但我无法添加自定义字段并从默认结构中删除现有字段。我可能已经检查了 50 多篇文章/问题,但没有一个对我有用。这是我当前的代码。

    public static void initializeLogger() 
    {
       ConfigurationBuilder<BuiltConfiguration> builder = 
               ConfigurationBuilderFactory.newConfigurationBuilder();

       builder.setStatusLevel(Level.DEBUG);
       builder.setConfigurationName("DefaultLogger");
            
       //Creating console appender just to see logging is there.
       AppenderComponentBuilder appenderBuilder = builder.newAppender("Console", "CONSOLE")
            .addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT);
       appenderBuilder.add(builder.newLayout("PatternLayout")
            .addAttribute("pattern", pattern)
            .addAttribute("AdditionalField.key", "asd")
            .addAttribute("AdditionalField.value", "qwe"));
       RootLoggerComponentBuilder rootLogger = builder.newRootLogger(Level.DEBUG);
       rootLogger.add(builder.newAppenderRef("Console"));
    
       builder.add(appenderBuilder);

       //Creating the file appender
       LayoutComponentBuilder layoutBuilder = builder.newLayout("JsonLayout")
            .addAttribute("compact", "false")
            .addAttribute("AdditionalField", builder.newKeyValuePair("asd", "qwe"));

       ComponentBuilder triggeringPolicy = builder.newComponent("Policies")
            .addComponent(builder.newComponent("SizeBasedTriggeringPolicy").addAttribute("size", "1KB"));
       appenderBuilder = builder.newAppender("LogToRollingFile", "RollingFile")
            .addAttribute("fileName", fileName)
            .addAttribute("filePattern", "applog-%d{MM-dd-yy-HH-mm-ss}.log.")
            .add(layoutBuilder)
            .addComponent(triggeringPolicy);
       builder.add(appenderBuilder);
    
       rootLogger.add(builder.newAppenderRef("LogToRollingFile"));
       builder.add(rootLogger);
    
       Configurator.reconfigure(builder.build());
}

此代码打印到控制台并将 Json 格式的日志记录写入文件。自定义值不会添加到 Json。我尝试通过其builder 创建 JsonLayout,但无法将其添加到构建器本身。

//Probably creating a new layout instance with additional fields
//might be helpful but I cannot use this in the builder above.
JsonLayout layout = JsonLayout.newBuilder()
    .setAdditionalFields(new KeyValuePair [] {
         new KeyValuePair("asd", "qwe"),
         new KeyValuePair("zxc", "rty"),
     }).build();

我需要添加到 Json 的参数是动态的,我不知道自定义节点的键和值。在这个阶段,我正在尝试在将其写入文件时创建一个自定义 Json 布局。

我可能做错了,所以请指教!我愿意接受建议。 谢谢。

【问题讨论】:

    标签: java json log4j2


    【解决方案1】:

    您可以使用以下类从日志中减去您想要的类中的任何属性。

         import com.fanap.midhco.applicationUtil.JsonUtil;
         import com.fasterxml.jackson.core.util.MinimalPrettyPrinter;
         import com.fasterxml.jackson.databind.ObjectWriter;
         import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
         import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
         import org.apache.logging.log4j.core.Layout;
         import org.apache.logging.log4j.core.LogEvent;
         import org.apache.logging.log4j.core.config.Node;
         import org.apache.logging.log4j.core.config.plugins.Plugin;
         import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
         import org.apache.logging.log4j.core.config.plugins.PluginFactory;
         import org.apache.logging.log4j.core.impl.Log4jLogEvent;
         import org.apache.logging.log4j.core.jackson.JsonConstants;
         import org.apache.logging.log4j.core.jackson.Log4jJsonObjectMapper;
         import org.apache.logging.log4j.core.layout.AbstractStringLayout;
         import org.apache.logging.log4j.core.util.StringBuilderWriter;
         import org.apache.logging.log4j.util.Strings;
         
         import java.io.IOException;
         import java.nio.charset.StandardCharsets;
         import java.util.HashSet;
         import java.util.Set;
         
         @Plugin(name = "MineCustLayout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE, printObject = true)
         public class MineCustLayout extends AbstractStringLayout {
         
             private final ObjectWriter objectWriter;
         
             @PluginFactory
             public static MineCustLayout createLayout(
                     @PluginAttribute(value = "locationInfo", defaultBoolean = false) final boolean locationInfo, @PluginAttribute(value = "eventEol", defaultBoolean = true) final boolean eventEol
             ) {
                 final SimpleFilterProvider filters = new SimpleFilterProvider();
                 final Set<String> except = new HashSet<>();
                 if (!locationInfo) {
                     except.add(JsonConstants.ELT_SOURCE);
                 }
                 except.add("loggerFqcn");
                 except.add("level");
                 except.add("timeMillis");
                 except.add("instant");
                 except.add("thread");
                 except.add("loggerName");
                 except.add("threadId");
                 except.add("threadPriority");
                 except.add("contextMap");
                 except.add("endOfBatch");
                 except.add(JsonConstants.ELT_NANO_TIME);
                 filters.addFilter(Log4jLogEvent.class.getName(), SimpleBeanPropertyFilter.serializeAllExcept(except));
                 final ObjectWriter writer = new Log4jJsonObjectMapper().writer(new MinimalPrettyPrinter());
                 return new MineCustLayout(writer.with(filters));
             }
         
             public MineCustLayout(ObjectWriter objectWriter) {
                 super(StandardCharsets.UTF_8, null, null);
                 this.objectWriter = objectWriter;
             }
         
         
             @Override
             public String toSerializable(LogEvent event) {
                 final StringBuilderWriter writer = new StringBuilderWriter();
                 try {
                     objectWriter.writeValue(writer, event);
         //            writer.write('\n');
                     return JsonUtil.getJsonObject(writer.toString()).get("message").toString() + '\n';
                 } catch (final IOException e) {
                     LOGGER.error(e);
                     return Strings.EMPTY;
                 }
             }
         }
    

    还有在配置里面:

         <RollingFile name="logFile_json" fileName="${sys:catalina.home}/logs/logFile_json.json"
                     filePattern="${sys:catalina.home}/logs/logFile_json-%d{MM-dd-yyyy}-%i.json">
            <MineCustLayout>
            </MineCustLayout>
            <Policies>
                <SizeBasedTriggeringPolicy size="50 MB"/>
            </Policies>
            <DefaultRolloverStrategy min="1" max="100"/>
         </RollingFile>
    

    【讨论】:

      【解决方案2】:

      可以通过调用LayoutComponentBuilderaddComponent 方法将自定义值作为键值对添加为组件。下面的代码 sn -p 展示了一个例子:

      ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
              AppenderComponentBuilder console =
                      builder.newAppender("stdout", "Console")
                              .addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT);
              LayoutComponentBuilder jsonLayout
                      = builder.newLayout("JsonLayout")
                      .addAttribute("complete", false)
                      .addAttribute("compact", false)
                      .addComponent(builder.newKeyValuePair("key1", "value1"))
                      .addComponent(builder.newKeyValuePair("key2", "value2"));
              console.add(jsonLayout);
              builder.add(console);
              RootLoggerComponentBuilder rootLogger
                      = builder.newRootLogger(Level.INFO);
              rootLogger.add(builder.newAppenderRef("stdout"));
              builder.add(rootLogger);
              builder.writeXmlConfiguration(System.out);
              return builder;
      

      这里是对应的xml输出:

      <?xml version='1.0' encoding='UTF-8'?>
      <Configuration>
          <Appenders>
              <Console name="stdout" target="SYSTEM_OUT">
                  <JsonLayout complete="false" compact="false">
                      <KeyValuePair key="key1" value="value1"/>
                      <KeyValuePair key="key2" value="value2"/>
                  </JsonLayout>
              </Console>
          </Appenders>
          <Loggers>
              <Root level="INFO">
                  <AppenderRef ref="stdout"/>
              </Root>
          </Loggers>
      </Configuration>
      

      使用 log4j 2.17.1 测试

      【讨论】:

        猜你喜欢
        • 2017-10-15
        • 1970-01-01
        • 2022-11-25
        • 1970-01-01
        • 2023-03-30
        • 1970-01-01
        • 2020-08-20
        • 2017-04-07
        • 2016-01-05
        相关资源
        最近更新 更多