当前位置: 首页 > 知识库问答 >
问题:

多线程JBehave日志记录混乱

傅献
2023-03-14

虽然很清楚如何配置多线程jBehave run,但我不清楚如何处理日志记录混乱。

这里有哪些选项?

共有1个答案

祁晟
2023-03-14

>

  • Rederect应用程序的输出为std out(JBehave的一个已经存在)。注意遵循=true

    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.follow=true
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} | %-5.5p | %-16.16t | %-32.32c{1} | %-64.64C %4L | %m%n
    
    log4j.rootLogger=error, stdout
    log4j.logger.com.company.app.interesting.module=debug
    ...
    

    线程文件输出

    @SuppressWarnings("resource")
    public class ThreadFileOutput extends PrintStream {
    
        private static ThreadLocal<FileOutputStream> threadOutput = new ThreadLocal<>();
        private static PrintStream stdout = System.out;
        private static PrintStream stderr = System.err;
    
        static {
            System.setOut(new ThreadFileOutput(stdout));
            System.setErr(new ThreadFileOutput(stderr));
        }
    
        public ThreadFileOutput(OutputStream out) {
            super(out);
        }
    
        public static void startThreadOutputRedirect(FileOutputStream stream) {
            threadOutput.set(stream);
        }
    
        public static void stopThreadOutputRedirect() {
            FileOutputStream stream = threadOutput.get();
            if (stream != null) {
                threadOutput.set(null);
                try {
                    stream.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    
        public static void forceOut(String line) {
            stdout.println(line);
        }
    
        public static void forceErr(String line) {
            stderr.println(line);
        }
    
        @Override
        public void write(byte[] b) throws IOException {
            FileOutputStream stream = threadOutput.get();
            if (stream != null) {
                try {
                    stream.write(b);
                } catch (IOException e) {
                    threadOutput.set(null);
                    throw new RuntimeException(e);
                }
            } else {
                super.write(b);
            }
        }
    
        @Override
        public void write(int b) {
            FileOutputStream stream = threadOutput.get();
            if (stream != null) {
                try {
                    stream.write(b);
                } catch (IOException e) {
                    threadOutput.set(null);
                    throw new RuntimeException(e);
                }
            } else {
                super.write(b);
            }
        }
    
        @Override
        public void write(byte[] buf, int off, int len) {
            FileOutputStream stream = threadOutput.get();
            if (stream != null) {
                try {
                    stream.write(buf, off, len);
                } catch (IOException e) {
                    threadOutput.set(null);
                    throw new RuntimeException(e);
                }
            } else {
                super.write(buf, off, len);
            }
        }
    
        @Override
        public void flush() {
            FileOutputStream stream = threadOutput.get();
            if (stream != null) {
                try {
                    stream.flush();
                } catch (IOException e) {
                    threadOutput.set(null);
                    throw new RuntimeException(e);
                }
            } else {
                super.flush();
            }
        }
    }
    

    在测试之前开始将线程输出重定向到文件,并在测试之后停止

    startThreadOutputRedirect(new FileOutputStream(new File(workDirRelative(story.getPath())))); 
    stopThreadOutputRedirect();
    

    /**
     * JBehave to TC integration.
     */
    public class TeamCityReporter extends NullStoryReporter {
    
        private static final LookupTranslator ESCAPE_TABLE = new LookupTranslator(new String[][] {
                { "'", "|'" },
                { "\n", "|n" },
                { "\r", "|r" },
                { "\\u", "|0x" },
                { "|", "||" },
                { "[", "|[" },
                { "]", "|]" }
        });
    
        private ThreadLocal<Story> story = new ThreadLocal<>();
        private ThreadLocal<String> scenario = new ThreadLocal<>();
    
        @Override
        @SuppressWarnings("resource")
        public void beforeStory(Story story, boolean givenStory) {
            this.story.set(story);
            this.scenario.set(null);
    
            try {
                startThreadOutputRedirect(new FileOutputStream(new File(workDirRelative(story.getPath()))));
            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            }
    
            forceOut(format("##teamcity[testSuiteStarted name='%s']", escape(story.getPath())));
            out.println(story.getPath());
    
            super.beforeStory(story, givenStory);
        }
    
        @Override
        public void afterStory(boolean givenStory) {
            forceOut(format("##teamcity[testSuiteFinished name='%s']", escape(story.get().getPath())));
            stopThreadOutputRedirect();
    
            super.afterStory(givenStory);
        }
    
        @Override
        public void beforeScenario(String scenario) {
            this.scenario.set(scenario);
    
            forceOut(format("##teamcity[testStarted name='%s']", escape(scenario)));
            out.println(scenario);
    
            super.beforeScenario(scenario);
        }
    
        @Override
        public void afterScenario() {
            forceOut(format("##teamcity[testFinished name='%s']", escape(scenario.get())));
    
            this.scenario.set(null);
            super.afterScenario();
        }
    
        @Override
        public void beforeStep(String step) {
            out.println(format("\n%s\n", step));
            super.beforeStep(step);
        }
    
        @Override
        public void storyNotAllowed(Story story, String filter) {
            forceOut(format("##teamcity[message text='story not allowed %s' status='WARNING']", escape(story.getName())));
            out.println(format("\n(Not allowed) %s\n", story.getPath()));
            super.storyNotAllowed(story, filter);
        }
    
        @Override
        public void failed(String step, Throwable cause) {
            forceOut(format("##teamcity[testFailed  name='%s' message='%s' details='%s']", new String[] { escape(scenario.get()), escape(getRootCauseMessage(cause)), escape(getStackTrace(cause)) }));
            out.println(format("\n(Failed) %s\n", step));
            cause.printStackTrace();
            super.failed(step, cause);
        }
    
        @Override
        public void pending(String step) {
            forceOut(format("##teamcity[testFailed  name='%s' message='Step in PENDING state: %s']", escape(scenario.get()), escape(step)));
            out.println(format("\n(Pending) %s\n", step));
            super.pending(step);
        }
    
        @Override
        public void notPerformed(String step) {
            out.println(format("\n(Not performed) %s\n", step));
            super.notPerformed(step);
        }
    
        private static String escape(String string) {
            return ESCAPE_TABLE.translate(string);
        }
    }
    

    启用并行JBehave执行

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = {
        ...
    })
    public class Stories extends JUnitStories {
    
        @Before
        public void setUp() throws Exception {
            configuredEmbedder()
                    // turn on parallel test execution
                    .useExecutorService(newFixedThreadPool(30, new ThreadFactoryBuilder()
                        .setDaemon(true)
                        .build()));
    
            configuredEmbedder()
                    .embedderControls()
                    ...
                    // don't use it this way not to produce multiThreading = true and delayed StoryReporter callbacks
                    // and you will see your application logging 'for each jbehave step'
                    // .useThreads(30);
        }
    
        @Override
        public Configuration configuration() {
            return new MostUsefulConfiguration()
                    ...
                    .useStoryReporterBuilder(new StoryReporterBuilder()
                            ...
                            .withFormats(HTML)
                            .withReporters(teamCityReporter));
        }
    }
    

    因此,每个并行测试都会有一个日志文件,该文件同时具有测试输出和应用程序输出(只有测试运行程序线程执行的代码)
    奖励-TeamCityReporter(JBehave到TC集成)将成功实时统计正在运行的并行测试,并在TC GUI上报告任何测试失败。将测试输出目录配置为TC工件路径,以访问每个测试输出。

  •  类似资料:
    • 问题内容: 场景是这样的: 我们正在使用JBehave和Selenium进行系统,集成和端到端测试。我正在检查超过20个值的页面上的计算结果以进行验证。使用Junit Assert,如果其中一个值不正确的第一个实例,则整个测试将失败。我想做的是,如果遇到断言失败,那么测试将继续执行,这样我就可以整理一次测试而不是多次测试中所有不正确的值。 为此,我捕获了断言并将未通过验证的所有内容写到日志文件中。

    • 我有以下控制台附加器; 问题是我在这里使用的模式输出了一个空的ThreadContext()。我不想使用特定的密钥名称(例如,),因为系统非常广泛,密钥集也不同。示例输出: 2017-09-26 10:39:55396[main]信息:启动内部HTTP客户端{}

    • 我想在我的应用程序中使用SLF4J+logback用于两个目的--日志和审计。 14:41:57.978[main]信息AUDIT_LOGGER-110欢迎使用main 如何确保审核消息在审核记录器下只出现一次?

    • 我目前正在研究事件/activity日志记录系统,我正在使用方法拦截器将其作为一个方面来实现。目前,系统/框架假定每个方法都是一个activity,但我想对此进行扩展,以便一个activity可以跨越多个方法调用。为了做到这一点,首先想到的方法是为所有相关的方法调用提供一些上下文。但是,我只知道在所有方法调用都在单个线程的上下文中(像Log4j的MDC/NDC)时这样做的方法。是否有任何方法为多线

    • 问题内容: 我正在考虑将Redis用于Web应用程序日志记录目的。我用谷歌搜索,有人将日志转储到Redis队列/列表中,然后将计划的工作人员转储到磁盘中。 http://nosql.mypopescu.com/post/8652869828/another-redis-use-case- centralized-logging 我希望寻求理解,为什么不直接使用Redis持久化到磁盘?如果我分配了一

    • logging 模块自 2.3 版以来一直是 Python 标准库的一部分。在 PEP 282 中有对它的简洁描述。除了 基础日志教程 之外,这些文档是非常难以阅读的。 日志记录一般有两个目的: 诊断日志 记录与应用程序操作相关的日志。例如,当用户遇到程序报错时, 可通过搜索诊断日志以获得上下文信息。 审计日志 为商业分析而记录的日志。从审计日志中,可提取用户的交易信息, 并结合其他用户资料构成用