Making Extent Report Appender Thread Safe

23 Views Asked by At

I am working on an Automation Framework and using Extent Report as a reporting tool. I am logging Android Logs and User Journeys using Slf4j logback. I wanted to run my tests in parallel. What i want is to log Android logs and my user journeys with respect to my test. But because its not thread safe. So I am getting logs of one test in another. Here was my code for sequential tests:

Listener Class

ExtentTest test;
    ExtentReports extent=ExtentReporterNG.getReporterObject();
    AppiumDriver driver;
    
    @Override
    public void onTestStart(ITestResult result) {
        test=extent.createTest(result.getMethod().getMethodName());
        ExtentReportsAppender.setExtentTest(test);
        Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
        ExtentReportsAppender extentAppender = new ExtentReportsAppender();
        extentAppender.setName("ExtentReportsAppender");
        extentAppender.start();  // Start the appender
        rootLogger.addAppender(extentAppender);
        try {
            driver=(AppiumDriver) result.getTestClass().getRealClass().getField("driver")
                    .get(result.getInstance());
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            ((CanRecordScreen) driver).startRecordingScreen();
        }
        catch(Exception e) {
        }
    }
        
    @Override
    public void onTestSuccess(ITestResult result) {
        test.log(Status.PASS, "Test Passed");
        ((CanRecordScreen) driver).stopRecordingScreen();
        cleanupLogger();
    }
    
    @Override
    public void onTestFailure(ITestResult result) {
        test.fail(result.getThrowable());
        try {
            stopRecording(result.getMethod().getMethodName());
            }
            catch(Exception e) {
            }
        cleanupLogger();
    }
    
    @Override
    public void onTestSkipped(ITestResult result) {
        test.skip(result.getThrowable());
    }
    
    @Override
    public void onFinish(ITestContext context) {
        extent.flush();
    }
    
    private void cleanupLogger() {
        Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
        ExtentReportsAppender extentAppender = (ExtentReportsAppender) rootLogger.getAppender("ExtentReportsAppender");
        if (extentAppender != null) {
            rootLogger.detachAppender(extentAppender);  // Detach the appender
            extentAppender.stop();  // Stop the appender
        }
    }   
    
    public void stopRecording(String methodName) throws IOException {

           String media = ((CanRecordScreen) driver).stopRecordingScreen();
           String dirPath =   System.getProperty("user.dir")+File.separator+"videos";
           File videoDir = new File(dirPath);
           FileOutputStream stream = null;
           
        try {
          stream = new FileOutputStream(videoDir + File.separator +methodName+".mp4");
                stream.write(Base64.decodeBase64(media));
                stream.close();

            } catch (Exception e) {

            } finally {
                if(stream != null) {
                    stream.close();
                }
            }
        }

ExtentReportsAppender Class

public class ExtentReportsAppender extends AppenderBase<ILoggingEvent>{ 
    private static ExtentTest test;

    public static void setExtentTest(ExtentTest testInstance) {
        test = testInstance;
    }

    @Override
    protected void append(ILoggingEvent event) {
        if (test != null) {
            switch (event.getLevel().levelInt) {
                case Level.INFO_INT:
                    test.log(Status.INFO, event.getFormattedMessage());
                    break;
                case Level.WARN_INT:
                    test.log(Status.WARNING, event.getFormattedMessage());
                    break;
            }
        }
    }

Here's What I tried: I created a thread Local variable for Extent Test and my Appium Driver that i get from reflection. By this, the test in extent report and the video recording became Thread Safe but the Logs did not.

Listener Class

public class Listeners extends AppiumUtils implements ITestListener,IConfigurationListener{
    ExtentReports extent=ExtentReporterNG.getReporterObject();
    AppiumDriver driver;
    private ThreadLocal<AppiumDriver> driverThreadLocal = new ThreadLocal<>();
    private ThreadLocal<ExtentTest> testThreadLocal = new ThreadLocal<>();
    private ThreadLocal<ExtentReportsAppender> appenderThreadLocal = new ThreadLocal<>();

    
    @Override
    public void onTestStart(ITestResult result) {
        ExtentTest test=extent.createTest(result.getMethod().getMethodName());
        testThreadLocal.set(test);
        Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
        ExtentReportsAppender extentAppender = new ExtentReportsAppender(testThreadLocal.get());
        appenderThreadLocal.set(extentAppender);
        extentAppender.setName("ExtentReportsAppender");
        extentAppender.start();  // Start the appender
        rootLogger.addAppender(extentAppender);
        try {
            driver = (AppiumDriver) result.getTestClass().getRealClass().getField("driver")
                    .get(result.getInstance());
            driverThreadLocal.set(driver);
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            ((CanRecordScreen) driver).startRecordingScreen();
        }
        catch(Exception e) {
            
        }
     }
        
    @Override
    public void onTestSuccess(ITestResult result) {
        testThreadLocal.get().log(Status.PASS, "Test Passed");
        try {
            driver=driverThreadLocal.get();
        } catch (Exception e) {
            e.printStackTrace();
        }
        ((CanRecordScreen) driver).stopRecordingScreen();
        cleanupLogger();
    }
    
    @Override
    public void onTestFailure(ITestResult result) {
        testThreadLocal.get().fail(result.getThrowable());
        try {
            driver=driverThreadLocal.get();
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
        stopRecording(result.getMethod().getMethodName());
        }
        catch(Exception e) {
            
        }

        /*try {
            driver=(AppiumDriver) result.getTestClass().getRealClass().getField("driver")
                    .get(result.getInstance());
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
            try {
                
                test.addScreenCaptureFromBase64String(getScreenshotAsBase64(driver),"Screenshot");
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }*/
          cleanupLogger();
    }
    
    @Override
    public void onTestSkipped(ITestResult result) {
        testThreadLocal.get().fail(result.getThrowable());
    }
    
    @Override
    public void onFinish(ITestContext context) {
        extent.flush();
    }
    
    private void cleanupLogger() {
        Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
//      ExtentReportsAppender extentAppender = (ExtentReportsAppender) rootLogger.getAppender("ExtentReportsAppender");
        ExtentReportsAppender extentAppender=appenderThreadLocal.get();
        if (extentAppender != null) {
            rootLogger.detachAppender(extentAppender);  // Detach the appender
            extentAppender.stop();  // Stop the appender
        }
    }   
    
    public void stopRecording(String methodName) throws IOException {
           AppiumDriver driver = driverThreadLocal.get();
           String media = ((CanRecordScreen) driver).stopRecordingScreen();
           String dirPath =   System.getProperty("user.dir")+File.separator+"videos";
           File videoDir = new File(dirPath);
           FileOutputStream stream = null;
           
        try {
          stream = new FileOutputStream(videoDir + File.separator +methodName+".mp4");
                stream.write(Base64.decodeBase64(media));
                stream.close();

            } catch (Exception e) {

            } finally {
                if(stream != null) {
                    stream.close();
                }
            }
        }

ExtentReportsAppender Class

public class ExtentReportsAppender extends AppenderBase<ILoggingEvent>{ 
    private ExtentTest test;

    public ExtentReportsAppender(ExtentTest testInstance) {
        test = testInstance;
    }

    @Override
    protected void append(ILoggingEvent event) {
        if (test != null) {
            switch (event.getLevel().levelInt) {
                case Level.INFO_INT:
                    test.log(Status.INFO, event.getFormattedMessage());
                    break;
                case Level.WARN_INT:
                    test.log(Status.WARNING, event.getFormattedMessage());
                    break;
            }
        }
    }

I expected that

  • By setting the thread safe testInstance instead of a static test will print relevant test logs.
  • By Setting the AppenderThreadLocal on Test Start and by Getting it in Clean up logger will clean that test's logs
0

There are 0 best solutions below