log4j2 - Can one appender manage more than 1 file "simultaneously"?

156 views Asked by At

I want to create an appender that logs each user actions into a different file per user using the MDC / ThreadContext to save the user name and use it to name the file later on.

So for User1 we'd have "web_debug_user1_yyyy-MM-DD", for user2 "web_debug_user2_yyyy-MM-DD".

Those users can be logged in the application at the same time.

Here is the relevant part of the config:

<Properties>
        <Property name="logPath">/data/logs</Property>
        <Property name="rollingFileName">web_debug</Property>
        <Property name="rollingFileNameError">web_Error</Property>
        <Property name="patternLog">%d %-5p [%c] %m [SID: %X{sessionId}]%n</Property>
        <Property name="patternLogUser">%d %-5p &lt;%X{userId}&gt;&lt;%X{customerID}&gt;&lt;%X{oid}&gt; [%c] %m [SID: %X{sessionId}]%n</Property>
    </Properties>
<Appenders>
<RollingFile name="rollingFileUser"
    filePattern="${logPath}/${rollingFileName}_$${ctx:userId}%d{yyyy-MM-dd}_%i.txt">
    <PatternLayout pattern="${patternLogUser}" />
    <Policies>
        <UserLoggingTriggerPolicy />
        <TimeBasedTriggeringPolicy interval="1"/>
    </Policies>
    <DirectWriteRolloverStrategy />
</RollingFile>

And here is the custom policy UserLoggingTriggeringPolicy which sets the fileName property on the manager each time an user event is launched.

@Plugin(name = "UserLoggingTriggerPolicy", category = "Core")
public class UserLoggingTriggerPolicy implements TriggeringPolicy {

private RollingFileManager manager;
private String typelog;
private File[] debugFilesUser;

@Override
public void initialize(RollingFileManager manager) {
    this.manager = manager;
    this.typelog = manager.getFileName().contains("debug") ? "debug" : "Error";
    this.debugFilesUser = null;
}

@Override
public boolean isTriggeringEvent(LogEvent arg0) {
    return isRolling();
}

public boolean isRolling() {
    boolean roll = false;
    if (!this.manager.getFileName().contains(MDC.get("userId"))) {
        ((DirectFileRolloverStrategy) manager.getRolloverStrategy()).clearCurrentFileName();
        ((DirectFileRolloverStrategy) manager.getRolloverStrategy()).getCurrentFileName(manager);
    }
    File f = new File(this.manager.getFileName());
    File folder = new File(f.getParent());
    if (debugFilesUser == null) {
        getFiles(folder);
    }
    if ((debugFilesUser.length != 0 && debugFilesUser[debugFilesUser.length - 1].length() / 1024 / 1024 > 10)
            || !f.exists()) {
        debugFilesUser = null;
        roll = true;
    }

    return roll;
}

private void getFiles(File folder) {
    debugFilesUser = folder.listFiles(new FilenameFilter() {

        @Override
        public boolean accept(File dir, String name) {
            if (name.contains(MDC.get("userId")) && name.contains(typelog)) {
                return true;
            }
            return false;
        }
    });
}

@PluginFactory
public static UserLoggingTriggerPolicy createPolicy() {
    return new UserLoggingTriggerPolicy();
}

}

Thing is that it does not seem enough to change the FileName of the FileManager given that it still points to the same OutputStream thus logging all different user messages into the file belonging to the first logged in user in the app. After some debugging found out that the OutputStream only changes under 2 circumstances: Initialization of the FileManager and RollOver. Then forced a rollOver in the custom policy when the current user changes but it ended up creating a new file after each roll and not writing in the previously existing ones, so in the span of 10 minutes had like 20-30 different files.

So the question(s) is(are): Is there any way to make an appender use a previous file, let's say "to rollback", and not only create a new one?

Was my approach wrong?

Thanks.

0

There are 0 answers