Apache Camel MDC 从 Body 添加字段

Apache Camel MDC add field from Body

我正在使用 apache camel 并希望使用 MDC 将某些键添加到我的日志中。我浏览了官方 Camel MDC Logging documentation 非常棒。我可以毫不费力地记录我的 routeId。我还需要从 Camel's Body 添加一个字段。

最坏的情况我可以在所有路由中手动添加它,但我想知道是否有可能以更简单的方式将字段从正文添加到 MDC?

欢迎任何想法。我真的很希望能够做到这一点,而不必进入每条路线并添加一个班轮。

更新:

在我的项目中实现了自定义 MDCUnitOfWork 和工厂。我能够看到 CustomUnitOfWorkFactory 创建我的 CustomUnitOfWork,然后设置 MDC 值。

但是我注意到这只发生在路线的开头。

在我的用例中,我轮询 Amazon SQS 作为我的第一条路线。我这里没有所需的信息。在第一条路线中,我构建了我的 Context 并将其设置为 Camel body,这是我需要在 MDC 中设置的信息所在的位置。

是否也可以在第二条路线之前创建 UnitOfWork?

您可以配置自定义 UnitOfWorkFactory 以创建扩展 MDCUnitOfWork 的自定义 UnitOfWork,您可以在其中向 MDC 添加自定义信息。

您可以从 Java 或在 XML 中配置 CamelContext 上的 UnitOfWorkFactory,只需添加一个 <bean>,Camel 会检测并使用它

我们想从我们的 Camel 路由中获得类似的东西 - 记录特定属性并 headers 使用 MDC。不幸的是,我们的路由被处理了,CustomMDCUnitOfWork 没有启动。我们最终实现了一个 org.apache.camel.spi.InterceptStrategy 来添加 MDC 值。如果有更好的方法来处理交易路线,我很乐意知道..!

这是基于克劳斯推荐的代码的完整实现。我们使用的是spring开机,不过根据你的需要调整

自动注册一个简单的 bean

@Bean
public CamelContextConfiguration contextConfiguration() {
    return new CamelContextConfiguration() {
        @Override
        public void beforeApplicationStart(CamelContext context) {
            context.setUseMDCLogging(true);
            context.setUnitOfWorkFactory(MyUnitOfWork::new);
        }

        @Override
        public void afterApplicationStart(CamelContext camelContext) {
        }
    };
}

然后,创建您的自定义工作单元class

public class MyUnitOfWork extends MDCUnitOfWork {
    public MyUnitOfWork(Exchange exchange) {
        super(exchange);
        if( exchange.getProperty("myProp") != null){
            MDC.put("myProp", (String) exchange.getProperty("myProp"));
        }
    }
}

在您的 logback/log4j 配置中,像这样使用值 myProp:

%X{myProp}

它应该开始记录

我遇到了 MDC 中缺少 headers 的问题,之前评论中提供的解决方案无效。这是针对我的情况的有效解决方案:除默认 headers 之外的所有 headers 都在线程之间丢失(MDC 是线程本地的)。由于 Camel MDCUnitOfWork 默认实现有缺陷,在另一个线程中传递给 MDC 的唯一 headers 是默认的 Camel headers.

您必须将 MDCLogging 设置为 true 并实现您自己的 UnitOfWork,就像之前评论中显示的那样:

 camelContext.setUseMDCLogging(true);
 camelContext.setUnitOfWorkFactory(CustomUnitOfWork::new);

CustomUnitOfWork 的实现要复杂得多,因为我们需要覆盖很多默认的 MDCUnitOfWork 行为(确保导入 org.slf4j.MDC):

class CustomUnitOfWork extends MDCUnitOfWork {

static final String CUSTOM_FIELD_NAME = "customField";

private String customField;

CustomUnitOfWork(Exchange exchange) {
    super(exchange);
    customField = (String) exchange.getIn().getHeader("customFieldPath123");

    MDC.put(CUSTOM_FIELD_NAME, customField);
}

@Override
public UnitOfWork newInstance(Exchange exchange) {
    return new CustomUnitOfWork(exchange);
}

@Override
public void clear() {
    super.clear();

    if (customField != null) {
        MDC.put(CUSTOM_FIELD_NAME, customField);
    } else {
        MDC.remove(CUSTOM_FIELD_NAME);
    }

}

@Override
public void stop() throws Exception {
    super.stop();
    clear();
}

@Override
public AsyncCallback beforeProcess(Processor processor, Exchange exchange, AsyncCallback callback) {
    return new CustomMDCCallback(callback);
}

private class CustomMDCCallback implements AsyncCallback {

    private final AsyncCallback delegate;
    private final String breadcrumbId;
    private final String exchangeId;
    private final String messageId;
    private final String correlationId;
    private final String routeId;
    private final String camelContextId;

    private final String customField;


    private CustomMDCCallback(AsyncCallback delegate) {
        this.delegate = delegate;
        this.exchangeId = MDC.get(MDC_EXCHANGE_ID);
        this.messageId = MDC.get(MDC_MESSAGE_ID);
        this.breadcrumbId = MDC.get(MDC_BREADCRUMB_ID);
        this.correlationId = MDC.get(MDC_CORRELATION_ID);
        this.camelContextId = MDC.get(MDC_CAMEL_CONTEXT_ID);
        this.routeId = MDC.get(MDC_ROUTE_ID);

        this.customField = MDC.get(CUSTOM_FIELD_NAME);

    }

    @Override
    public void done(boolean doneSync) {
        try {
            if (!doneSync) {
                checkAndPut(breadcrumbId, MDC_BREADCRUMB_ID);
                checkAndPut(exchangeId, MDC_EXCHANGE_ID);
                checkAndPut(messageId, MDC_MESSAGE_ID);
                checkAndPut(correlationId, MDC_CORRELATION_ID);
                checkAndPut(camelContextId, MDC_CAMEL_CONTEXT_ID);

                checkAndPut(customField, CUSTOM_FIELD_NAME);

            }
            checkAndPut(routeId, MDC_ROUTE_ID);

        } finally {
            delegate.done(doneSync);
        }
    }

    private void checkAndPut(String value, String fieldName) {
        if (value != null) {
            MDC.put(fieldName, value);
        }
    }
}
}

如果您看一下 Camel MDCUnitOfWork class,您会发现代码非常相似。