无法为 class 的实例创建单独的日志文件,它们分别在各自的线程上 运行 - log4j2

Unable to create separate log file for instances of a class which are running on their own threads separately - log4j2

很抱歉提出这样一个常见问题,但是在应用这些解决方案后我无法解决我的问题。

所以基本上,我已经创建了一个网络,它允许我分别在它们自己的单独线程上执行不同的服务器。现在问题是它除了日志记录外工作正常。

What I want to achieve: I want to have separate log file per server.. based on the id of that server, for example main.log , server1.log , server2.log , server3.log , server4.log , etc...

目前发生了什么? 我尝试过的两种主要方法:-

尝试了选项 1:- Log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{DEFAULT} [%t] %-5level %logger{36} %l - %msg%n"/>
        </Console>
        <File name="AppMain" filename="logs/main.log" append="false">
            <PatternLayout pattern="%d{DEFAULT} [%t] %-5level %logger{36} %l - %msg%n"/>
        </File>
        <Routing name="RoutingAppender">
            <Routes pattern="$${sys:logFilename}">
                <Route>
                    <File name="Rolling-${sys:logFilename}" fileName="logs/${sys:logFilename}.log" append="false">
                        <PatternLayout pattern="%d{DEFAULT} [%t] %-5level %logger{36} %l - %msg%n"/>
                    </File>
                </Route>

                <!-- By having this set to ${ctx:logFileName} it will match when filename
                     is not set in the context -->
                <Route ref="Console" key="${sys:logFilename}"/>
            </Routes>
        </Routing>

    </Appenders>
    <Loggers>
        <Root level="info">
            <!--<AppenderRef ref="Console"/>-->
            <AppenderRef ref="Console"/>
            <AppenderRef ref="AppMain"/>
        </Root>
        <Logger name="ServerRouting" level="all" additivity="false">
            <!--            <AppenderRef ref="Console"/>-->
            <AppenderRef ref="RoutingAppender"/>
        </Logger>

    </Loggers>
</Configuration>

Server.java:-

public class Server {
    private static Logger logger;
    private long id;
    private SomeClass someClass;

    public Server(long id, SomeClass someClass) {
        this.id=id;
        this.someClass=someClass;
        System.setProperty("logFilename",  "server"+id);
        logger = LogManager.getLogger(Constants.DYNAMIC_SERVERS);
        this.someClass.setLogger(logger);
    }

   public void bindSockets(String servername, int port) throws IOException { /*Some functionality*/}

   public void start() throws IOException { /*Some functionality*/}

}

Constants.java

public class Constants {

    static {
        System.setProperty("log4j.configurationFile", "log4j2.xml");
    }

    public static String APP_NAME = "AppMain";
    public static String DYNAMIC_SERVERS = "ServerRouting";

}

使用这种方法 - 当我启动 4 个服务器时,我得到:

main.log , server2.log , server3.log , server4.log However the internal entries are all mixed up , and no file for server1 (i.e. server1.log) is created

尝试了选项 2:- Log4j2.xml 我替换了早期日志中的 Routes 块

   <Routes pattern="$${ctx:ROUTINGKEY}">

        <!-- This route is chosen if ThreadContext has value 'special' for key ROUTINGKEY. -->
        <Route>
            <RollingFile name="Rolling-${ctx:ROUTINGKEY}" fileName="logs/${ctx:ROUTINGKEY}.log"
                         filePattern="./logs/${date:yyyy-MM}/${ctx:ROUTINGKEY}-special-%d{yyyy-MM-dd}-%i.log.gz">
                <PatternLayout>
                    <pattern>%d{ISO8601} [%t] %p %c{3} - %m%n</pattern>
                </PatternLayout>
                <Policies>
                    <TimeBasedTriggeringPolicy interval="6" modulate="true"/>
                    <SizeBasedTriggeringPolicy size="10 MB"/>
                </Policies>
            </RollingFile>
        </Route>
    </Routes>

Server.java:- 我在这里更改的是,我没有使用 System.setProperty(),而是使用 ThreadContext.put(),将日志文件名发送到 log4j2

public class Server {
    private static Logger logger;
    private long id;
    private SomeClass someClass;

    public Server(long id, SomeClass someClass) {
        this.id=id;
        this.someClass=someClass;
        ThreadContext.put("ROUTINGKEY", "server"+id);
        logger = LogManager.getLogger(Constants.DYNAMIC_SERVERS);
        this.someClass.setLogger(logger);
    }

   public void bindSockets(String servername, int port) throws IOException { /*Some functionality*/}

   public void start() throws IOException { /*Some functionality*/}

}

使用这种方法 - 当我启动 4 个服务器时,我得到:只有这两个文件

main.log , ${ctx:ROUTINGKEY}.log Instead of 4 , as mentioned in beginning. Where, all the internal entries for all servers have been added to single file i.e. ${ctx:ROUTINGKEY}.log

结论 谁能告诉我我做错了什么?这将是一个巨大的帮助。 显然欢迎新的建议,如果我的问题有歧义,请告诉我。

我认为有两个问题。

  1. 您在 Server class 中将 logger 声明为 static,因此在该 class 的所有实例之间共享记录器。
  2. LogManager.getLogger 方法不是线程安全的,以这种方式使用时必须同步。

下面是一些适用于我的示例代码和配置:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.ThreadContext;

public class Server {
    private Logger logger;
    private long id;

    public Server(long id, Object lock) {
        this.id=id;
        ThreadContext.put("ROUTINGKEY", "server"+id);
        synchronized(lock){
            logger = LogManager.getLogger(Constants.DYNAMIC_SERVERS);
        }
    }

    public void doStuff(){
        logger.info("Server "+ id +" did some stuff");
    }
}

A class 在单独的线程中启动 4 个服务器:

public class Main {

    public static void main(String[] args) {
        final Object lock = new Object();

        Thread t1 = new Thread(new Runnable(){
            public void run(){
                Server s = new Server(1,lock);
                s.doStuff();
            }
        });

        Thread t2 = new Thread(new Runnable(){
            public void run(){
                Server s = new Server(2,lock);
                s.doStuff();
            }
        });

        Thread t3 = new Thread(new Runnable(){
            public void run(){
                Server s = new Server(3,lock);
                s.doStuff();
            }
        });

        Thread t4 = new Thread(new Runnable(){
            public void run(){
                Server s = new Server(4,lock);
                s.doStuff();
            }
        });

        t1.start();
        t2.start();
        t3.start();
        t4.start();

        try {
            t1.join();
            t2.join();
            t3.join();
            t4.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

为了完整起见,常量class:

public class Constants {

    public static String APP_NAME = "AppMain";
    public static String DYNAMIC_SERVERS = "ServerRouting";

}

和log4j2.xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{DEFAULT} [%t] %-5level %logger{36} %l - %msg%n" />
        </Console>
        <File name="AppMain" filename="logs/main.log" append="false">
            <PatternLayout pattern="%d{DEFAULT} [%t] %-5level %logger{36} %l - %msg%n" />
        </File>
        <Routing name="RoutingAppender">
            <Routes pattern="$${ctx:ROUTINGKEY}">

                <!-- This route is chosen if ThreadContext has value 'special' for key 
                    ROUTINGKEY. -->
                <Route>
                    <RollingFile name="Rolling-${ctx:ROUTINGKEY}" fileName="logs/${ctx:ROUTINGKEY}.log"
                        filePattern="logs/${date:yyyy-MM}/${ctx:ROUTINGKEY}-special-%d{yyyy-MM-dd}-%i.log.gz">
                        <PatternLayout>
                            <pattern>%d{ISO8601} [%t] %p %c{3} - %m%n</pattern>
                        </PatternLayout>
                        <Policies>
                            <TimeBasedTriggeringPolicy interval="6"
                                modulate="true" />
                            <SizeBasedTriggeringPolicy size="10 MB" />
                        </Policies>
                    </RollingFile>
                </Route>
            </Routes>
        </Routing>

    </Appenders>
    <Loggers>
        <Root level="info">
            <!--<AppenderRef ref="Console"/> -->
            <AppenderRef ref="Console" />
            <AppenderRef ref="AppMain" />
        </Root>
        <Logger name="ServerRouting" level="all" additivity="false">
            <!-- <AppenderRef ref="Console"/> -->
            <AppenderRef ref="RoutingAppender" />
        </Logger>

    </Loggers>
</Configuration>

运行 上面的 Main class 和 class 路径上的 log4j2.xml 生成 5 个日志文件 - 每个服务器一个,"main" 日志 - 每个服务器日志仅包含该服务器生成的日志。主日志为空,因为没有事件路由到那里。

希望对您有所帮助!