1

我们正在 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) Spring XD 运行作业 (xd.job.name = " fooJob ") 和流 (xd.stream.name = " barStream ") 的日志输出:

...
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 或 NDC中设置$name,但我们不知道如何在每个流或每个作业的基础上设置它(MDC 和 NDC 在每个-线程基础)。

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

4

2 回答 2

0

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

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

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

底线是我们(还)没有解决方案,但可以随意打开一个新功能JIRA Issue

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

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

唯一的问题是,postSend()如果在preSend(). Spring Integration 4.1(应该在下周发布;目前作为候选发布版增加了对调用interceptor.afterSendCompletion()的支持,无论是否发生异常。

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

如果这可行,并且您想贡献它,我们很乐意考虑它。

于 2014-11-03T16:11:44.527 回答
0

我最终创建了一个自定义 Logback格式转换器

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}

于 2018-02-02T14:51:03.163 回答