【问题标题】:Add ${xd.job.name} and ${xd.stream.name} to Spring XD logs将 ${xd.job.name} 和 ${xd.stream.name} 添加到 Spring XD 日志
【发布时间】:2014-11-03 15:01:24
【问题描述】:

我们正在 Spring XD v1.0.0.RELEASE 上运行多个流和作业。
相应的流和作业模块将消息记录到 Spring XD 的全局日志文件中,位于 $XD_HOME/logs
我们需要区分每个作业和流创建的日志消息。用 ${xd.job.name}${xd.stream.name} 的值标记每个日志行应该对我们有用。例如,

1) 在一个配置文件(Spring XD 配置、XML 上下文文件、Java 类...随便)中,设置一个 Java 上下文(?)变量:

if(moduleType == "job") {
  name = ${xd.job.name};
} else if(moduleType == "stream") {
  name = ${xd.stream.name};
} else {
  name = "XD";
}

2) 相应地配置 $XD_HOME/config 下的 log4j 属性文件:

log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2} [${name}] - %m%n

3) 运行作业 (xd.job.name = "fooJob") 和流 (xd.stream.name = "barStream) 的 Spring XD 的日志输出"):

...
13:07:41,169  DEBUG task-scheduler-1 myjobimpl.TransactionManager [fooJob] - Initiating transaction commit
...
13:08:43,215  INFO task-scheduler-15 mystreamimpl.JsonDecoder [barStream] - Decoding JSON: {...}
...
14:08:42,569  INFO DeploymentsPathChildrenCache-0 monitor.IntegrationMBeanExporter [XD] - Summary on shutdown: MessageChannelMonitor: [name=input, sends=0]

4) 由其他库(例如 Spring Data、Apache Commons 等)记录的任何消息都应具有 [fooJob]、[barStream] 或 [XD],与调用库的作业或流的值相同。

到目前为止,我们提出的唯一半生不熟的解决方案是在 log4j 的 MDC or NDC 中设置 $name,但我们不知道如何在基于每个流或每个作业(MDC 和 NDC 在每个线程的基础上工作)。

有没有办法将作业或流名称添加到相应的日志行中?

【问题讨论】:

    标签: logging spring-batch spring-integration spring-xd


    【解决方案1】:

    在看到您的other question 后,我们在内部讨论了这个问题。

    我们还讨论了 MDC,我们可以在总线实现中设置它,但它只适用于处理器和接收器。我们需要 Spring Integration/Spring Batch 中的代码,以使其也适用于源和作业。

    这也是许多人不想要的开销,所以它必须是可选的。

    底线是我们(还)没有解决方案,但请随时打开新功能JIRA Issue

    编辑:在下面回复您的评论...

    嗯...对于处理器和接收器,您可以将ChannelInterceptor 添加到input 通道并将名称推送到preSend() 并将其弹出postSend()。对于来源,您可以在 output 频道上执行相同操作。

    唯一的问题是如果在preSend() 之后抛出异常,则不会调用postSend()。 Spring Integration 4.1(应该下周发布;目前以release candidate 的形式提供)adds support 用于调用interceptor.afterSendCompletion(),无论是否发生异常都会调用。

    注意:在获取对通道的引用时,要添加拦截器,您必须使用 ChannelInterceptorAware 来完成,因为 bean 可能被代理。

    如果这可行,并且您想contribute it,我们很乐意考虑。

    【讨论】:

    • 感谢您的帮助。我正在考虑创建一个 XD 插件来修改(AOP?)模块上下文(流或作业)的每个 bean,方法是为每个 bean 的每个方法添加一个之前和之后的建议。他们应该分别执行 NDC.push($name)NDC.pop()。在运行流或作业时,每次 bean 方法开始或结束时,其线程都会被“标记”或“未标记”,相应的日志消息也会如此。我不知道这是否可能,IMO 这是一个丑陋的黑客。你能提供一些帮助吗?
    • 再次感谢,你的看起来是一个更好的流解决方案,也许这些方面的东西也可以用于工作;-)
    • hmmmm.... 当您进入基于反应器的流处理器(基本上任何由 BroadcasterMessageHandler 包装的东西)时,这会变得更加棘手。代码在单独的线程中运行,因此 MDC/NDC 可能无法正常工作。我们也在努力寻找解决方案......
    【解决方案2】:

    我最终创建了一个自定义 Logback formatting converter

    public class SpringPropertyConverter extends ClassicConverter {
    
        private String key;
        private String defaultValue = "";
    
        @Override
        public void start() {
            String[] keyInfo = extractDefaultReplacement(getFirstOption());
            key = keyInfo[0];
            if (keyInfo[1] != null) {
                defaultValue = keyInfo[1];
            }
            super.start();
        }
    
        @Override
        public void stop() {
            key = null;
            super.stop();
        }
    
        @Override
        public String convert(ILoggingEvent event) {
            SpringLogbackPropertyResolver resolver = SpringLogbackPropertyResolver.getInstance();
            if (resolver == null) {
                return defaultValue;
            }
            return resolver.getProperty(key, defaultValue);
        }
    }
    

    由持有 Environment 实例的 bean 支持:

    @Service
    public class SpringLogbackPropertyResolver {
    
        private static volatile SpringLogbackPropertyResolver instance;
    
        private final Environment env;
        private final Map<String, String> map;
    
        public SpringLogbackPropertyResolver(@Autowired Environment env) {
            this.env = env;
            this.map = new ConcurrentHashMap<>();
            this.instance = this;
        }
    
        public String getProperty(String key, String defaultValue){
            return map.computeIfAbsent(key, k -> env.getProperty(key, defaultValue));
        }
    
        public static SpringLogbackPropertyResolver getInstance() {
            return instance;
        }
    }
    

    然后在xd-container-logback.groovy注册转换器:

    conversionRule("spring", SpringPropertyConverter)
    

    然后可以在带有spring前缀%spring{xd.stream.name:-bar}的日志模式中使用它

    【讨论】:

      猜你喜欢
      • 2017-08-03
      • 2017-02-28
      • 2021-09-25
      • 1970-01-01
      • 1970-01-01
      • 2021-08-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多