有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java多线程JBehave日志

虽然多线程jBehave运行非常清晰,
我不清楚如何处理乱七八糟的日志

这里有什么选择


共 (1) 个答案

  1. # 1 楼答案

    1. 将应用程序的输出重新定向到std out(JBehave的输出已经存在)。注意follow=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
      ...
      
    2. 每线程文件输出

      @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();
              }
          }
      }
      
    3. 在测试之前开始将线程输出重定向到文件,并在测试之后停止

      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);
          }
      }
      
    4. 启用并行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-to-TC集成)将成功实时统计正在运行的并行测试,并在TC GUI上报告任何测试失败。将测试输出目录配置为TC工件路径,以访问每个测试输出