如何在春季批处理中的工作中检索taskexecutionID?

发布于 2025-02-11 19:39:21 字数 423 浏览 2 评论 0 原文

(出于记录目的,)我需要检索(任务)执行ID 在Spring Cloud数据流中的作业执行中。特别是,我可以访问多个“作业”对象( Joblauncher,JobExplorer,Jobrepository,JobExecution ...),它们似乎与任务没有关联。此外,类似 @value(“ {$ spring.cloud.task.task.execution Id:}”)似乎不起作用。

(For logging purposes,) I need to retrieve the (task) execution id inside a job execution in Spring Cloud Data Flow. In particular, I have access to several "job" objects (JobLauncher, JobExplorer, JobRepository, JobExecution...), which don't seem to keep a correlation to the task. Moreover, something like @Value("{$spring.cloud.task.executionid:}") doesn't seem to work.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

一向肩并 2025-02-18 19:39:21

您可以使用

您可以让任何BEAN实施 taskexecutionListener (或使用注释 @beforetask @aftertask @failedtask )和从作为参数传递给其方法的 taskexecution 中获取ID。

You can use a Task Execution Listener.

You can let any bean implement TaskExecutionListener (or use the annotations @BeforeTask, @AfterTask, @FailedTask) and take the id from the TaskExecution that is passed as argument to its methods.

东风软 2025-02-18 19:39:21

我不知道您愿意做什么,但是您可以使用Spring AOP和Log4J2来完成此类任务。

首先,将log4j2添加到项目中,然后编辑log4j config file(log4j2.xml),您可以在其中通过在模式标签中指定该数字(| thread ID:%tid |)来记录运行线程的ID:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="DEBUG">
    <Appenders>
        <Console name="LogToConsole" target="SYSTEM_OUT">
            <PatternLayout disableAnsi="false"  charset="UTF-8" pattern="%highlight{%-5p|%d{ISO8601}{GMT}|thread id:%tid|%X{Slf4jMDCFilter.UUID}| [%t] %logger{36} - %msg%n}{FATAL=red blink, ERROR=red, WARN=yellow bold, INFO=blue, DEBUG=green bold, TRACE=blue}"/>
        </Console>
    </Appenders>
    <Loggers>
        <!-- avoid duplicated logs with additivity=false -->
        <Logger name="com.obs.dqsc.api" level="info" additivity="false">
            <AppenderRef ref="LogToConsole"/>
        </Logger>
        <Root level="info"  additivity="false">
            <AppenderRef ref="LogToConsole"/>
        </Root>
    </Loggers>
</Configuration>

并创建一个用于类的类。方面登录物,我已经为我的情况创建了一个,它可以使用通用方法记录所有内容,您可以使用它:

@Aspect
@Component
public class LoggingAspect {
    /**
     * @param joinPoint the execution of a method in a given layer
     * @return the execution result after proceeding the joinPoint
     * @throws Throwable if an exception occurred while proceeding a joinPoint
     */
    @Around("com.obs.dqsc.api.config.AspectConfig.allLayers() && com.obs.dqsc.api.config.AspectConfig.notToLog()")
    public Object logMethod(final ProceedingJoinPoint joinPoint) throws Throwable {
        final Class<?> targetClass = joinPoint.getTarget().getClass();
        final Logger logger = LoggerFactory.getLogger(targetClass);
        final String className = targetClass.getSimpleName();

        logger.info(getPreMessage(joinPoint, className));

        final StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        final Object retVal = joinPoint.proceed();
        stopWatch.stop();

        logger.info(getPostMessage(joinPoint, className, stopWatch.getTotalTimeMillis()));
        return retVal;
    }

    /**
     * @param joinPoint the execution of a method in a given layer
     * @param className the class where logger is based
     * @return the message to be printed in log4j appender
     */
    private static String getPreMessage(final JoinPoint joinPoint, final String className) {
        final StringBuilder builder = new StringBuilder()
                .append("Entered in ").append(className).append(".")
                .append(joinPoint.getSignature().getName())
                .append("(");
        appendTo(builder, joinPoint);
        return builder
                .append(")")
                .toString();
    }

    /**
     * @param joinPoint the execution of a method in a given layer
     * @param className the class where logger is based
     * @return the message to be printed in log4j appender
     */
    private static String getPostMessage(final JoinPoint joinPoint, final String className, final long millis) {
        return "Exit from " + className + "." +
                joinPoint.getSignature().getName() +
                "(..); Execution time: " +
                millis +
                " ms;"
                ;
    }

    /**
     * @param builder   to accumulate parameters of a method in one string
     * @param joinPoint the execution of a method
     */
    private static void appendTo(final StringBuilder builder, final JoinPoint joinPoint) {
        final Object[] args = joinPoint.getArgs();
        for (int i = 0; i < args.length; i++) {
            if (i != 0) {
                builder.append(", ");
            }
            builder.append(args[i]);
        }
    }

}

您还需要为上面的类创建连接点,以指定应用程序中的软件包或层检查:

@Configuration
@EnableAspectJAutoProxy
public class AspectConfig {
    /**
     * A join point is in the web layer if the method is defined
     * in a type in the com.obs.dqsc.api.controller package or any sub-package
     * under that.
     */
    @Pointcut("within(com.obs.dqsc.api.controller..*)")
    public void initControllerLayer() {
        //nested comment to bypass sonar role
    }

    /**
     * A join point is in the service layer if the method is defined
     * in a type in the com.obs.dqsc.api.service package or any sub-package
     * under that.
     */
    @Pointcut("within(com.obs.dqsc.api.service..*)")
    public void inServiceLayer() {
        //nested comment to bypass sonar role
    }

    /**
     * A join point is in the data access layer if the method is defined
     * in a type in the com.obs.dqsc.api.repository package or any sub-package
     * under that.
     */
    @Pointcut("within(com.obs.dqsc.api.repository..*)")
    public void initRepositoryLayer() {
        //nested comment to bypass sonar role
    }

    /**
     * A business service is the execution of any method defined on a service
     * interface. This definition assumes that interfaces are placed in the
     * "service" package, and that implementation types are in sub-packages.
     * <p>
     * If you group service interfaces by functional area (for example,
     * in packages com.obs.dqsc.api.repository or com.obs.dqsc.api.service) then
     * the pointcut expression "execution(* com.obs.dqsc.api.service.*.*(..))"
     * could be used instead.
     * <p>
     * Alternatively, you can write the expression using the 'bean'
     * PCD, like so "bean(*Service)". (This assumes that you have
     * named your Spring service beans in a consistent fashion.)
     */
    @Pointcut("execution(* com.obs.dqsc.api.service.*.*(..))")
    public void businessServiceOperations() {
        //nested comment to bypass sonar role
    }

    /**
     * A data access operation is the execution of any method defined on a
     * dao interface. This definition assumes that interfaces are placed in the
     * "dao" package, and that implementation types are in sub-packages.
     */
    @Pointcut("execution(* com.obs.dqsc.api.repository.*.*(..))")
    public void repositoryOperations() {
        //nested comment to bypass sonar role
    }

    /**
     * the execution of any method defined on the web layer if the method is defined
     * in a type in the com.obs.dqsc.api.controller package or any sub-package
     * under that.
     */
    @Pointcut("execution(* com.obs.dqsc.api.controller.*.*(..))")
    public void controllerOperations() {
        //nested comment to bypass sonar role
    }

    /**
     * exclude any class annotated with @NoLogging
     */
    @Pointcut("!@target(com.obs.dqsc.api.util.annotation.NoLogging)")
    public void notToLog(){
        //nested comment to bypass sonar role
    }

    /**
     * All functional layers of the application
     */
    @Pointcut("com.obs.dqsc.api.config.AspectConfig.initRepositoryLayer()" +
            "|| com.obs.dqsc.api.config.AspectConfig.inServiceLayer() " +
            "|| com.obs.dqsc.api.config.AspectConfig.initControllerLayer()" +
            "|| com.obs.dqsc.api.config.AspectConfig.repositoryOperations()" +
            "|| com.obs.dqsc.api.config.AspectConfig.businessServiceOperations()" +
            "|| com.obs.dqsc.api.config.AspectConfig.controllerOperations()"
            )
    public void allLayers() {
        //nested comment to bypass sonar role
    }

仅此而已。

不要忘记在log4j2和spring aop的pom.xml中添加所需的依赖项。

这是您可能需要在pom.xml中包含的依赖项的小概述:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
</dependency>

最后,控制台中的日志将看起来像:

提示:如果您想每个请求都有UID,则必须有兴趣阅读此blog

I don't know what you are willing to do exactly, but you could use spring AOP and log4j2 to achieve such task.

Start by adding log4j2 to your project and edit the log4j config file (log4j2.xml) where you could log the id of the running threads by specifying that in the pattern tag (|thread id:%tid|) :

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="DEBUG">
    <Appenders>
        <Console name="LogToConsole" target="SYSTEM_OUT">
            <PatternLayout disableAnsi="false"  charset="UTF-8" pattern="%highlight{%-5p|%d{ISO8601}{GMT}|thread id:%tid|%X{Slf4jMDCFilter.UUID}| [%t] %logger{36} - %msg%n}{FATAL=red blink, ERROR=red, WARN=yellow bold, INFO=blue, DEBUG=green bold, TRACE=blue}"/>
        </Console>
    </Appenders>
    <Loggers>
        <!-- avoid duplicated logs with additivity=false -->
        <Logger name="com.obs.dqsc.api" level="info" additivity="false">
            <AppenderRef ref="LogToConsole"/>
        </Logger>
        <Root level="info"  additivity="false">
            <AppenderRef ref="LogToConsole"/>
        </Root>
    </Loggers>
</Configuration>

And create a class for aspect login matter, I've already created one for my case that logs everything using a generic method, you could use it:

@Aspect
@Component
public class LoggingAspect {
    /**
     * @param joinPoint the execution of a method in a given layer
     * @return the execution result after proceeding the joinPoint
     * @throws Throwable if an exception occurred while proceeding a joinPoint
     */
    @Around("com.obs.dqsc.api.config.AspectConfig.allLayers() && com.obs.dqsc.api.config.AspectConfig.notToLog()")
    public Object logMethod(final ProceedingJoinPoint joinPoint) throws Throwable {
        final Class<?> targetClass = joinPoint.getTarget().getClass();
        final Logger logger = LoggerFactory.getLogger(targetClass);
        final String className = targetClass.getSimpleName();

        logger.info(getPreMessage(joinPoint, className));

        final StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        final Object retVal = joinPoint.proceed();
        stopWatch.stop();

        logger.info(getPostMessage(joinPoint, className, stopWatch.getTotalTimeMillis()));
        return retVal;
    }

    /**
     * @param joinPoint the execution of a method in a given layer
     * @param className the class where logger is based
     * @return the message to be printed in log4j appender
     */
    private static String getPreMessage(final JoinPoint joinPoint, final String className) {
        final StringBuilder builder = new StringBuilder()
                .append("Entered in ").append(className).append(".")
                .append(joinPoint.getSignature().getName())
                .append("(");
        appendTo(builder, joinPoint);
        return builder
                .append(")")
                .toString();
    }

    /**
     * @param joinPoint the execution of a method in a given layer
     * @param className the class where logger is based
     * @return the message to be printed in log4j appender
     */
    private static String getPostMessage(final JoinPoint joinPoint, final String className, final long millis) {
        return "Exit from " + className + "." +
                joinPoint.getSignature().getName() +
                "(..); Execution time: " +
                millis +
                " ms;"
                ;
    }

    /**
     * @param builder   to accumulate parameters of a method in one string
     * @param joinPoint the execution of a method
     */
    private static void appendTo(final StringBuilder builder, final JoinPoint joinPoint) {
        final Object[] args = joinPoint.getArgs();
        for (int i = 0; i < args.length; i++) {
            if (i != 0) {
                builder.append(", ");
            }
            builder.append(args[i]);
        }
    }

}

You will need also to create join points for the class above where you specify the packages or layers in your application you want to inspect:

@Configuration
@EnableAspectJAutoProxy
public class AspectConfig {
    /**
     * A join point is in the web layer if the method is defined
     * in a type in the com.obs.dqsc.api.controller package or any sub-package
     * under that.
     */
    @Pointcut("within(com.obs.dqsc.api.controller..*)")
    public void initControllerLayer() {
        //nested comment to bypass sonar role
    }

    /**
     * A join point is in the service layer if the method is defined
     * in a type in the com.obs.dqsc.api.service package or any sub-package
     * under that.
     */
    @Pointcut("within(com.obs.dqsc.api.service..*)")
    public void inServiceLayer() {
        //nested comment to bypass sonar role
    }

    /**
     * A join point is in the data access layer if the method is defined
     * in a type in the com.obs.dqsc.api.repository package or any sub-package
     * under that.
     */
    @Pointcut("within(com.obs.dqsc.api.repository..*)")
    public void initRepositoryLayer() {
        //nested comment to bypass sonar role
    }

    /**
     * A business service is the execution of any method defined on a service
     * interface. This definition assumes that interfaces are placed in the
     * "service" package, and that implementation types are in sub-packages.
     * <p>
     * If you group service interfaces by functional area (for example,
     * in packages com.obs.dqsc.api.repository or com.obs.dqsc.api.service) then
     * the pointcut expression "execution(* com.obs.dqsc.api.service.*.*(..))"
     * could be used instead.
     * <p>
     * Alternatively, you can write the expression using the 'bean'
     * PCD, like so "bean(*Service)". (This assumes that you have
     * named your Spring service beans in a consistent fashion.)
     */
    @Pointcut("execution(* com.obs.dqsc.api.service.*.*(..))")
    public void businessServiceOperations() {
        //nested comment to bypass sonar role
    }

    /**
     * A data access operation is the execution of any method defined on a
     * dao interface. This definition assumes that interfaces are placed in the
     * "dao" package, and that implementation types are in sub-packages.
     */
    @Pointcut("execution(* com.obs.dqsc.api.repository.*.*(..))")
    public void repositoryOperations() {
        //nested comment to bypass sonar role
    }

    /**
     * the execution of any method defined on the web layer if the method is defined
     * in a type in the com.obs.dqsc.api.controller package or any sub-package
     * under that.
     */
    @Pointcut("execution(* com.obs.dqsc.api.controller.*.*(..))")
    public void controllerOperations() {
        //nested comment to bypass sonar role
    }

    /**
     * exclude any class annotated with @NoLogging
     */
    @Pointcut("!@target(com.obs.dqsc.api.util.annotation.NoLogging)")
    public void notToLog(){
        //nested comment to bypass sonar role
    }

    /**
     * All functional layers of the application
     */
    @Pointcut("com.obs.dqsc.api.config.AspectConfig.initRepositoryLayer()" +
            "|| com.obs.dqsc.api.config.AspectConfig.inServiceLayer() " +
            "|| com.obs.dqsc.api.config.AspectConfig.initControllerLayer()" +
            "|| com.obs.dqsc.api.config.AspectConfig.repositoryOperations()" +
            "|| com.obs.dqsc.api.config.AspectConfig.businessServiceOperations()" +
            "|| com.obs.dqsc.api.config.AspectConfig.controllerOperations()"
            )
    public void allLayers() {
        //nested comment to bypass sonar role
    }

And that's all.

Don't forget to add required dependencies in your pom.xml for log4j2 and spring aop.

Here is a small overview of the dependencies you might need to include in your pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
</dependency>

Finally the logs in the console will look like that:
enter image description here

Hint: if you want to have UID per request, you must be interested in reading this blog

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文