Java 使用TestNG框架和Log4J为每个测试类保存唯一的日志

Java 使用TestNG框架和Log4J为每个测试类保存唯一的日志,java,maven,log4j,testng,Java,Maven,Log4j,Testng,我有一个自动化项目(Java、Maven和TestNG、log4j)。 我正在尝试为每个测试类创建日志文件(LoginTest将创建LoginTest.log,HomeTest将创建HomeTest.log,等等),为此我实现了自己的日志机制,但在运行结束时,我只得到最后一个类测试日志文件(对于运行的最后一个测试类) 这是我的逻辑: public class TestLogger extends LoggerFormat { private static final DateT

我有一个自动化项目(Java、Maven和TestNG、log4j)。 我正在尝试为每个测试类创建日志文件(LoginTest将创建LoginTest.log,HomeTest将创建HomeTest.log,等等),为此我实现了自己的日志机制,但在运行结束时,我只得到最后一个类测试日志文件(对于运行的最后一个测试类)

这是我的逻辑:

    public class TestLogger extends LoggerFormat {

    private static final DateTimeFormatter TIMESTAMP_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final ZoneId DEFAULT_ZONE_ID = ZoneId.of(EnvConf.getDefaultTimeZone());
    private final ThreadLocal<Map<String, List<String>>> testsLogMap = new ThreadLocal<>();

    public void info(ITestContext context, String messageFormat, Object... args) {
        String msg = handleFormatMsg(messageFormat, args);
        super.info(msg);
        log(Level.INFO, context, msg);
    }

    public void error(ITestContext context, Throwable t, String messageFormat, Object... args) {
        String msg = handleFormatMsg(messageFormat, args);
        super.error(msg, t);
        log(Level.ERROR, context, msg, t);
    }

    public void error(ITestContext context, String messageFormat, Object... args) {
        String msg = handleFormatMsg(messageFormat, args);
        super.error(msg);
        log(Level.ERROR, context, msg);
    }

    public void warn(ITestContext context, String messageFormat, Object... args) {
        String msg = handleFormatMsg(messageFormat, args);
        super.warn(msg);
        log(Level.WARN, context, msg);
    }

    public void warn(ITestContext context, Throwable t, String messageFormat, Object... args) {
        String msg = handleFormatMsg(messageFormat, args);
        super.warn(msg, t);
        log(Level.WARN, context, msg, t);
    }

    public void debug(ITestContext context, String messageFormat, Object... args) {
        String msg = handleFormatMsg(messageFormat, args);
        super.debug(msg);
        if (LoggerFactory.isDebug()) {
            log(Level.DEBUG, context, msg);
        }
    }

    public void debug(ITestContext context, Throwable t, String messageFormat, Object... args) {
        String msg = handleFormatMsg(messageFormat, args);
        super.debug(msg, t);
        if (LoggerFactory.isDebug()) {
            log(Level.DEBUG, context, msg, t);
        }
    }

    private void log(Level level, ITestContext context, Object message, Throwable t) {
        message = String.format("%s\n%s", message, throwableToString(t));
        log(level, context, message);
    }

    private void log(Level level, ITestContext context, Object message) {
        Map<String, List<String>> logsMap = getLogsMap();
        if (!logsMap.containsKey(context.getName())) {
            logsMap.put(context.getName(), new ArrayList<>());
        }

        logsMap.get(context.getName()).add(formatMsg(message, level));
        if (level.toInt() != Level.DEBUG.toInt()) {
            Reporter.log((String) message, 0);
        }
    }

    private static String formatMsg(Object message, Level level) {
        LocalDateTime dateTime = LocalDateTime.now(DEFAULT_ZONE_ID);
        return String.format("[%s][%s]%s",
                TIMESTAMP_FORMAT.format(dateTime),
                level, message);
    }

    public List<String> getAndDeleteLogsByTest(String testName) {
        return getLogsMap().remove(testName);
    }

    private Map<String, List<String>> getLogsMap() {
        if (testsLogMap.get() == null) {
            testsLogMap.set(new HashMap<>());
        }
        return testsLogMap.get();
    }


    private static String throwableToString(Throwable t) {
        StringBuilder builder = new StringBuilder();
        for (StackTraceElement traceElement : t.getStackTrace()) {
            builder.append(traceElement.toString()).append('\n');
        }
        return builder.toString();
    }


    private static String handleFormatMsg(String message, Object... args) {
        if (args.length == 0 && message.contains("%")) {
            message = message.replaceAll("%", "%%");
        }
        return String.format(message, args);
    }
}
public类TestLogger扩展了LoggerFormat{
私有静态最终DateTimeFormatter TIMESTAMP_FORMAT=DateTimeFormatter.of模式(“yyyy-MM-dd HH:MM:ss”);
私有静态final ZoneId DEFAULT_ZONE_ID=ZoneId.of(EnvConf.getDefaultTimeZone());
private final ThreadLocal testsLogMap=new ThreadLocal();
公共无效信息(ITestContext上下文、字符串消息格式、对象…参数){
字符串msg=handleformatsg(messageFormat,args);
超级资讯(msg),;
日志(Level.INFO、context、msg);
}
公共无效错误(ITestContext上下文、Throwable t、String messageFormat、Object…args){
字符串msg=handleformatsg(messageFormat,args);
超级错误(msg,t);
日志(Level.ERROR、context、msg、t);
}
公共无效错误(ITestContext上下文、字符串messageFormat、对象…args){
字符串msg=handleformatsg(messageFormat,args);
超级错误(msg);
日志(Level.ERROR、context、msg);
}
public void warn(ITestContext上下文、字符串messageFormat、对象…args){
字符串msg=handleformatsg(messageFormat,args);
超级警告(msg);
日志(Level.WARN、context、msg);
}
public void warn(ITestContext上下文、Throwable t、String messageFormat、Object…args){
字符串msg=handleformatsg(messageFormat,args);
超级警告(msg,t);
日志(Level.WARN、context、msg、t);
}
公共void调试(ITestContext上下文、字符串messageFormat、对象…args){
字符串msg=handleformatsg(messageFormat,args);
super.debug(msg);
if(LoggerFactory.isDebug()){
日志(Level.DEBUG、context、msg);
}
}
public void debug(ITestContext上下文、Throwable t、String messageFormat、Object…args){
字符串msg=handleformatsg(messageFormat,args);
super.debug(msg,t);
if(LoggerFactory.isDebug()){
日志(Level.DEBUG,context,msg,t);
}
}
私有void日志(级别、ITestContext上下文、对象消息、Throwable t){
message=String.format(“%s\n%s”,message,throwableToString(t));
日志(级别、上下文、消息);
}
私有void日志(级别、ITestContext上下文、对象消息){
Map logsMap=getLogsMap();
如果(!logsMap.containsKey(context.getName())){
logsMap.put(context.getName(),newArrayList());
}
logsMap.get(context.getName()).add(formatMsg(消息,级别));
if(level.toInt()!=level.DEBUG.toInt()){
Reporter.log((字符串)消息,0);
}
}
私有静态字符串formatMsg(对象消息,级别){
LocalDateTime-dateTime=LocalDateTime.now(默认区域ID);
返回字符串。格式(“[%s][%s]%s”,
时间戳格式(dateTime),
级别、消息);
}
公共列表getAndDeleteLogsByTest(字符串测试名){
返回getLogsMap().remove(testName);
}
私有映射getLogsMap(){
if(testsLogMap.get()==null){
testsLogMap.set(新的HashMap());
}
返回testsLogMap.get();
}
私有静态字符串throwableToString(Throwable t){
StringBuilder=新的StringBuilder();
对于(StackTraceElement跟踪元素:t.getStackTrace()){
append(traceElement.toString()).append('\n');
}
返回builder.toString();
}
私有静态字符串handleFormatMg(字符串消息、对象…参数){
if(args.length==0&&message.contains(“%”)){
message=message.replaceAll(“%”,“%”);
}
返回String.format(消息,参数);
}
}
这是我的基本测试类:

@Listeners({NeoTestListener.class})
public class BaseTest {

    private static final Browser BROWSER = Browser.valueOf(EnvConf.getProperty("ui.browser.type"));
    private static final File SCREENSHOTS_FOLDER = new File(EnvConf.getProperty("test_output.screenshots.folder"));
//    private static final File DOWNLOADS_FOLDER = new File(EnvConf.getProperty("workspace.tests.downloads"));
    private static final File DOWNLOADS_FOLDER = new File(EnvConf.getProperty("test_output.logs.folder"));
    private static final String ADMIN_USERNAME = EnvConf.getProperty("hackeruso.admin.user.email");
    private static final String ADMIN_PASSWORD = EnvConf.getProperty("hackeruso.admin.user.password");

    protected static DriverWrapper driver;
    protected LoginPage loginPage;
    protected Date testStartTime;
    protected ITestContext context;
    protected TopBar topBar;
    protected final File testTempFolder;

    static {
        if (!SCREENSHOTS_FOLDER.exists()) {
            FileUtil.createFolder(SCREENSHOTS_FOLDER, true);
        }

        if(!DOWNLOADS_FOLDER.exists()) {
            FileUtil.createFolder(DOWNLOADS_FOLDER, false);
        }
    }

    protected static String randSuffix(String prefix){
        return prefix + "_" + String.valueOf(System.nanoTime()).substring(9);
    }

    public BaseTest() {
        this.testTempFolder = new File(DOWNLOADS_FOLDER, randSuffix(getClass().getSimpleName()));
        FileUtil.createFolder(testTempFolder, false);
//        testTempFolder.deleteOnExit();
    }

    @BeforeClass
    public final void baseSetup(ITestContext context) throws IOException {
        this.context = context;
        driver = DriverWrapper.open(BROWSER, DOWNLOADS_FOLDER);
        loginPage = new LoginPage(driver);
        info("<!!! '%s' START !!!>" , context.getName());
        testStartTime = new Date();
        this.context.setAttribute("test_start_time", testStartTime);
        info("testStartTime=[%s]" , testStartTime);
    }

    private void printBrowserLog() {

        List<LogEntry> serverLogLines = driver.manage().logs().get(LogType.BROWSER).getAll();
        if (serverLogLines.size() > 0) {
            Log.i("<---------Browser [SERVER] log start--------->");
            for (LogEntry entry : serverLogLines) {
                Log.e(entry.toString());
            }
            Log.i("<---------Browser [SERVER] log end------------>");
        }
    }

    @AfterClass
    public final void baseTeardown(ITestContext context) {
        Date testEndTime = new Date();
        if (driver != null) {
            printBrowserLog();
            driver.quit();
        }
        info("<!!! '%s' END !!!>" , context.getName());
        info("testEndTime=[%s]" , testEndTime);
    }

    public static DriverWrapper getDriver() {
        return driver;
    }

    protected void info(String message , Object...args){
        TESTS_LOG.info(context , message , args);
    }

    public void error(Throwable t , String messageFormat , Object...args) {
        TESTS_LOG.error(context , t , messageFormat , args);
    }

    public void error(String messageFormat , Object...args) {
        TESTS_LOG.error(context , messageFormat , args);
    }

    protected void warn(String messageFormat , Object...args) {
        TESTS_LOG.warn(context , messageFormat , args);
    }

    public void warn(Throwable t ,String messageFormat , Object...args) {
        TESTS_LOG.warn(context , t , messageFormat , args);
    }

    protected void debug(String messageFormat , Object...args) {
        TESTS_LOG.debug(context , messageFormat , args);
    }

    public void debug(Throwable t , String messageFormat , Object...args) {
        TESTS_LOG.debug(context , t , messageFormat , args);
    }
}
@Listeners({NeoTestListener.class})
公共类基类测试{
私有静态最终浏览器Browser=Browser.valueOf(EnvConf.getProperty(“ui.Browser.type”);
私有静态最终文件截图\文件夹=新文件(EnvConf.getProperty(“test\ u output.SCREENSHOTS.FOLDER”);
//私有静态最终文件下载\文件夹=新文件(EnvConf.getProperty(“workspace.tests.DOWNLOADS”);
private static final File DOWNLOADS_FOLDER=新文件(EnvConf.getProperty(“test_output.logs.FOLDER”);
私有静态最终字符串ADMIN_USERNAME=EnvConf.getProperty(“hackeruso.ADMIN.user.email”);
私有静态最终字符串ADMIN_PASSWORD=EnvConf.getProperty(“hackeruso.ADMIN.user.PASSWORD”);
受保护的静态驱动器振打器驱动器;
受保护的登录页面登录页面;
受保护日期testStartTime;
受保护的ITestContext上下文;
保护顶杆顶杆;
受保护的最终文件testTempFolder;
静止的{
如果(!SCREENSHOTS_FOLDER.exists()){
FileUtil.createFolder(屏幕截图\文件夹,true);
}
如果(!DOWNLOADS_FOLDER.exists()){
createFolder(下载文件夹,false);
}
}
受保护的静态字符串后缀(字符串前缀){
返回前缀+String.valueOf(System.nanoTime()).substring(9);
}
公共基线测试(){
this.testTempFolder=新文件(下载_文件夹,randSuffix(getClass().getSimpleName());
createFolder(testTempFolder,false);
//testTempFolder.deleteOnExit();
}
@课前
公共最终void baseSetup(ITestContext上下文)引发IOException{
this.context=上下文;
driver=DriverWrapper.open(浏览器,下载文件夹);
登录页面=新登录页面(驱动程序);
信息(“”,context.getN
public class NeoTestListener implements ITestListener {
    private final File SCREENSHOTS_FOLDER = new File(EnvConf.getProperty("test_output.screenshots.folder"));
    private static final SimpleDateFormat FOLDER_NAME_FORMAT = new SimpleDateFormat("dd_MM_HH_mm_ss");
    private static final SimpleDateFormat LOG_NAME_FORMAT = new SimpleDateFormat("dd_MM_HH_mm_ss");
    private static final File TESTS_LOGS_FOLDER = new File(EnvConf.getProperty("test_output.logs.folder"));

    static {
        if (!TESTS_LOGS_FOLDER.exists()) {
            FileUtil.createFolder(TESTS_LOGS_FOLDER, true);
        }
    }

    private static String getTestMethodName(ITestResult iTestResult){
        return iTestResult.getMethod().getConstructorOrMethod().getName();
    }

    @Attachment
    public byte[] saveFailureScreenShot(DriverWrapper driver){
        return driver.getScreenshotAsByte();
    }

    @Attachment(value = "{0}", type = "text/plain")
    public static String saveTextLog(String message){
        return message;
    }

    @Override
    public void onStart(ITestContext context) {
        context.setAttribute("WebDriver", getDriver());

    }

    @Override
    public void onFinish(ITestContext context) {
    }

    @Override
    public void onTestStart(ITestResult result) {
        TESTS_LOG.info(result.getName() +" " +  result.getTestClass() );
//        TESTS_LOG.info("[Test: " + getTestClassName(result.getTestContext())+ " Started]");
    }

    @Override
    public void onTestSuccess(ITestResult result) {
    }

    @Override
    public void onTestFailure(ITestResult result) {
        TESTS_LOG.error(String.format("I am in onTestFailure method:=[%s] failed", getTestMethodName(result)));
        Object testClass = result.getInstance();
        DriverWrapper driver = getDriver();
        takeScreenshot(getTestMethodName(result));

        //Allure ScreenShot and SaveTestLog
        TESTS_LOG.info(String.format("Screenshot for class=[%s], method=[%s]", getTestClassName(result.getTestContext()), getTestMethodName(result)));
        saveFailureScreenShot(driver);
        try {
            saveLogTextFile(result);
        } catch (IOException e) {
            e.printStackTrace();
        }
        saveTextLog(getTestMethodName(result) + " failed and screenshot taken!");
    }

    @Attachment
    private byte[] saveLogTextFile(ITestResult result) throws IOException {
        return saveToLogFile(result.getTestContext());
    }

    @Override
    public void onTestSkipped(ITestResult result) {
    }

    @Override
    public void onTestFailedButWithinSuccessPercentage(ITestResult result) {
    }

    @Override
    public void onTestFailedWithTimeout(ITestResult result) {
    }

    private void takeScreenshot(String filePrefix){
        File dest = new File(SCREENSHOTS_FOLDER , filePrefix + "_" + FOLDER_NAME_FORMAT.format(new Date()) + ".png");
        takeScreenshot(dest, getDriver());
    }

    private void takeScreenshot(File destFile, DriverWrapper driver){
        File scrFile = driver.getScreenshotAsFile();
        Path src = Paths.get(scrFile.toURI());
        Path dest = Paths.get(destFile.toURI());
        try {
            Files.copy(src, dest , StandardCopyOption.REPLACE_EXISTING);
            TESTS_LOG.info("[[ATTACHMENT|" + destFile.getAbsolutePath() + "]]");
        } catch (IOException e) {
            TESTS_LOG.error("Failed to save screen shot at file: " + destFile.getName());
            TESTS_LOG.error(e.getMessage());
        }
    }

    private byte[] saveToLogFile(ITestContext context) throws IOException {
        File logFile = createLogFile(context);
        boolean created = FileUtil.createNewFile(logFile);

        if(created){
            List<String> testLogLines = TESTS_LOG.getAndDeleteLogsByTest(context.getName());
            if(testLogLines == null){
                TESTS_LOG.error(context, "test=[%s] don't have any log lines!" , context.getName());
            }else{
                FileUtil.writeToFile(logFile.getAbsolutePath(), testLogLines);
            }
        }else{
            TESTS_LOG.error(context, " failed to create test log file=[%s]", logFile.getAbsolutePath());
        }

        return FileUtils.readFileToByteArray(logFile);
    }

    private static File createLogFile(ITestContext context){
        return new File(TESTS_LOGS_FOLDER, String.format("%s_%s.log",getTestClassName(context), LOG_NAME_FORMAT.format(context.getStartDate())));
    }

    private static String getTestClassName(ITestContext context){
        return context.getAllTestMethods()[0].getInstance().getClass().getSimpleName();
    }

}
public class ForgotPasswordTest extends BaseTest {
    private String verifyEmailURL = "";
    private static final String AUTOMATION_EMAIL= EnvConf.getProperty("automation.email.user");
    private static final String AUTOMATION_EMAIL_PASSWORD=EnvConf.getProperty("automation.email.password");
    private static final String AUTOMATION_WEBAPP_USER = "AUTOMATION_TESTER";
    private  ResetPasswordPage rpp;
    private final static String NEW_PASSWORD = "1Qaz2wsx3edc";
    private final static String ENVIRONMENT_BASE_URL = EnvConf.getProperty("base.url");

    @BeforeClass
    public void setup() {
        topBar = new TopBar(driver);
        rpp = new ResetPasswordPage(driver);
    }

    @Test(priority = 1)
    public void navigateToLoginPage(){
        loginPage.navigateAndVerify();
    }

    @Test(priority=2)
    public void sendChangePasswordInstructions() {
        ForgotPasswordPage fpp = loginPage.clickAndVerifyForgotPasswordButtonAction();
        fpp.sendForgotPasswordInstructions(AUTOMATION_EMAIL);
        Assert.assertTrue(loginPage.getForgotMsgAlertText().contains("Thank You, An Email Has Been Send"));
        info("Sending forget password instructions phase is successful!");
    }

    @Test(priority=3)
    public void verifyEmail(){
        verifyEmailURL = "";
        String regex = "href=\"([^\"]*)" ;
        String from = "<SOME_URL>";
        String subject = getSubject();
        String msg = MailHelper.getMessagesFromGmail( AUTOMATION_EMAIL, AUTOMATION_EMAIL_PASSWORD, from, subject, testStartTime);
        Pattern linkPattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
        Matcher pageMatcher = linkPattern.matcher(msg);
        while (pageMatcher.find()) {
            if (pageMatcher.group(1).contains("reset-password")) {
                verifyEmailURL = pageMatcher.group(1);
                verifyEmailURL = ENVIRONMENT_BASE_URL.concat("/").concat(verifyEmailURL.split("/")[3]);
                info("Verify URL: " + verifyEmailURL);
            }
        }

        info("Verifying email address is successful!");

    }

    @Test(priority = 4)
    public void navigateToResetPasswordScreen(){
        navigateTo(verifyEmailURL);
    }

    @Test(priority=5)
    public void connectToUpdateNewPassword() {

        rpp.changeNewPassword(NEW_PASSWORD);
        info("Changing password is successful!");
    }

    @Test(priority=6)
    public void verifyPasswordChanged() {
        signIn(AUTOMATION_EMAIL, NEW_PASSWORD, true);
        assertTrue(topBar.verifyExistenceTopBarNavigationItem(TopRightNavBarItem.SUPPORT));
        info(String.format("Password for user=[%s] changed successfully", AUTOMATION_WEBAPP_USER));

    }

    private String getSubject(){
        return "Reset Password";
    }

}
println(): First test...
22:32:25.537 [main] INFO igb.so.so65465538.FirstTest - log.info():  First test...
println(): Second test...
22:32:25.561 [main] INFO igb.so.so65465538.SecondTest - log.info():  Second test...