简体   繁体   English

如何在运行时更改特定用户/线程的日志级别

[英]How to Change log level for particular users/threads at runtime

I'm using slf4j with either log4j 2.0 or logback as the implementation. 我正在使用slf4j与log4j 2.0或logback作为实现。 For example, my servlet has a logger with level ERROR, and my server spawns 100 threads of the servlet. 例如,我的s​​ervlet有一个级别为ERROR的记录器,我的服务器产生了servlet的100个线程。 I will get a list of special users at runtime. 我将在运行时获得一个特殊用户列表。 When I detect some of the special users connected in. I want to change the log level for those special users/threads to DEBUG, and leave other threads' log level unaffected (still ERROR). 当我检测到一些连接的特殊用户时。我想将这些特殊用户/线程的日志级别更改为DEBUG,并使其他线程的日志级别不受影响(仍然是ERROR)。

I know the TurboFilter in logback and DynamicThresholdFilter in log4j 2.0, but since I will only get the special users list at runtime, I cannot use them. 我知道logback中的TurboFilter和log4j 2.0中的DynamicThresholdFilter,但由于我只在运行时获取特殊用户列表,所以我无法使用它们。

Here is my application: 这是我的申请:

package com.example.logging;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServlet;

import org.slf4j.*;

public class App extends HttpServlet {

    private final Logger Logger = LoggerFactory.getLogger(App.class);
    Map<String, String> map = new HashMap<String, String>();

    public App() {
        map.put("user1", "DEBUG");
        map.put("user2", "DEBUG");
        map.put("user3", "ERROR");
    }

    public void writeToLogFile(String userName) {

        if (map.containsKey(userName)) {
            // do something so that I can change the logger to the corresponding log level
        }

        Logger.error(userName + " error message");

        // the logger is of level ERROR, so by default, this log event will not happen
        // but I want it to happen for special users
        if (Logger.isDebugEnabled()) {
            Logger.debug(userName + " debug message");
        }
    }
}

Here is my log configuration in log4j2.xml 这是我在log4j2.xml中的日志配置

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="ERROR">
<Appenders>
    <Console name="Console" target="SYSTEM_OUT">
        <PatternLayout pattern="%-5level %class{36} %M %msg%xEx%n" />
    </Console>
</Appenders>
<Loggers>
    <Logger name="com.example.logging.App" level="ERROR" additivity="false">
            <AppenderRef ref="Console" />
    </Logger>
    <Root level="DEBUG">
        <AppenderRef ref="Console" />
    </Root>
</Loggers> 
</Configuration>

If I call the methods below: 如果我调用以下方法:

App myApp = new App();
// assume the below 4 methods are called concurrently
myApp.writeToLogFile("user1");
myApp.writeToLogFile("user2");
myApp.writeToLogFile("user3");
myApp.writeToLogFile("user4");

The expected output should be: 预期产量应为:

ERROR com.example.logging.App writeToLogFile - user1 error message
DEBUG com.example.logging.App writeToLogFile - user1 debug message
ERROR com.example.logging.App writeToLogFile - user2 error message
DEBUG com.example.logging.App writeToLogFile - user2 debug message
ERROR com.example.logging.App writeToLogFile - user3 error message
ERROR com.example.logging.App writeToLogFile - user4 error message

While the already existing answer might work (haven't tried it personally), after intensive searching, I found a very easy and neat trick to do what you are requesting. 虽然现有的答案可能有效(没有亲自尝试过),但经过深入搜索后,我发现了一个非常简单明了的技巧来完成你的要求。

The DynamicThresholdFilter can be used with conditions to switch the log level at run time. DynamicThresholdFilter可以与条件一起使用,以在运行时切换日志级别。 This, combined with log4j2's ThreadContext , you can do quite nifty things. 这与log4j2的ThreadContext相结合,你可以做很多漂亮的事情。

You would have to populate a particular key in the ThreadContext at the beginning of a server call processing (somewhere in doFilter method of your HttpServlet class) based on your custom logic of user names. 您必须根据用户名的自定义逻辑,在服务器调用处理开始时(在HttpServlet类的doFilter方法中的某个位置)填充ThreadContext中的特定键。 This would look something like: 这看起来像是这样的:

ThreadContext.put("customLogLevel", "debug");

Then in your log4j2.xml file, you put this as a global filter, right below the root Configuration tag: 然后在log4j2.xml文件中,将其作为全局过滤器放在根Configuration标记的正下方:

<DynamicThresholdFilter key="customLogLevel" onMatch="ACCEPT" onMismatch="NEUTRAL">
    <KeyValuePair key="debug" value="DEBUG"/>
    <KeyValuePair key="error" value="ERROR"/>
    <KeyValuePair key="info" value="INFO"/>
</DynamicThresholdFilter>

Now based on the value of the key customLogLevel in the ThreadContext that you set at the beginning of a call, all the log calls in that thread will have log level corresponding to the matching KeyValuePair line. 现在,根据您在调用开始时设置的ThreadContext中的键customLogLevel的值,该customLogLevel中的所有日志调用都将具有与匹配的KeyValuePair行对应的日志级别。 So in the example above, all log calls in the thread would have level as DEBUG . 因此在上面的示例中,线程中的所有日志调用都将具有DEBUG级别。

I've met the same problem, and I end up using my own filter by making changes to DynamicThresholdFilter 我遇到了同样的问题,最后通过更改DynamicThresholdFilter来使用我自己的过滤器

Changes to the application: 对应用程序的更改:

package com.example.logging;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServlet;

import org.slf4j.*;

public class App extends HttpServlet {

    private final Logger Logger = LoggerFactory.getLogger(App.class);
    Map<String, String> map = new HashMap<String, String>();

    public App() {
        map.put("user1", "Debug");
        map.put("user2", "Debug");
        map.put("user3", "Error");
    }

    public void writeToLogFile(String userName) {
        // if the user is in the map, we put it into ThreadConext for filtering
        if (map.containsKey(userName)) {
            MDC.put("level", map.get(userName));
        }

        Logger.error(userName + " error message");

        if (Logger.isDebugEnabled()) {
            Logger.debug(userName + " debug message");
        }

            // remember to remove it
        MDC.remove("level");
    }

}

Here is the newly defined filter based on DynamicThresholdFilter, let's call it DynamicThresholdUserFilter, you can compare it to the source code of DynamicThresholdFilter 这是基于DynamicThresholdFilter的新定义的过滤器,我们称之为DynamicThresholdUserFilter,您可以将它与DynamicThresholdFilter的源代码进行比较

package com.example.logging.log4j2.plugin;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.filter.AbstractFilter;
import org.apache.logging.log4j.message.Message;

/**
 * Compare against a log level that is associated with an MDC value.
 */
@Plugin(name = "DynamicThresholdUserFilter", category = "Core", elementType = "filter", printObject = true)
public final class DynamicThresholdUserFilter extends AbstractFilter {
    private Level defaultThreshold = Level.ERROR;
    private final String key;

    private DynamicThresholdUserFilter(final String key, final Level defaultLevel,
                                   final Result onMatch, final Result onMismatch) {
        super(onMatch, onMismatch);
        if (key == null) {
            throw new NullPointerException("key cannot be null");
        }
        this.key = key;
        this.defaultThreshold = defaultLevel;
    }

    public String getKey() {
        return this.key;
    }

    @Override
    public Result filter(final Logger logger, final Level level, final Marker marker, final String msg,
                         final Object... params) {
        return filter(level);
    }

    @Override
    public Result filter(final Logger logger, final Level level, final Marker marker, final Object msg,
                         final Throwable t) {
        return filter(level);
    }

    @Override
    public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg,
                         final Throwable t) {
        return filter(level);
    }

    @Override
    public Result filter(final LogEvent event) {
        return filter(event.getLevel());
    }

    /* biggest change here */
    private Result filter(final Level level) {
        final String value = ThreadContext.get(key);
        if (value != null) {
            Level ctxLevel = Level.toLevel(value);
            if (ctxLevel == null) {
                // in case the level is invalid
                ctxLevel = defaultThreshold;
            }
            return level.isAtLeastAsSpecificAs(ctxLevel) ? onMatch : onMismatch;
        }
        return Result.NEUTRAL;

    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append("key=").append(key);
        sb.append(", default=").append(defaultThreshold);
        return sb.toString();
    }

    /**
     * Create the DynamicThresholdFilter.
     * @param key The name of the key to compare.
     * @param pairs An array of value and Level pairs.
     * @param levelName The default Level.
     * @param match The action to perform if a match occurs.
     * @param mismatch The action to perform if no match occurs.
     * @return The DynamicThresholdFilter.
     */
    @PluginFactory
    public static DynamicThresholdUserFilter createFilter(
            @PluginAttribute("key") final String key,
            @PluginAttribute("defaultThreshold") final String levelName,
            @PluginAttribute("onMatch") final String match,
            @PluginAttribute("onMismatch") final String mismatch) {
        final Result onMatch = Result.toResult(match);
        final Result onMismatch = Result.toResult(mismatch);
        final Level level = Level.toLevel(levelName, Level.ERROR);
        return new DynamicThresholdUserFilter(key, level, onMatch, onMismatch);
    }
}

Add the DynamicThresholdUserFilter and package name to your configuration file 将DynamicThresholdUserFilter和包名称添加到配置文件中

<?xml version="1.0" encoding="UTF-8"?>
<!-- add the package name of the filter-->
<Configuration status="ERROR" packages="com.example.logging.plugin">
    <!-- configuration of the new defined filter -->
    <DynamicThresholdUserFilter key="level" defaultThreshold="ERROR" onMatch="ACCEPT" onMismatch="NEUTRAL" />
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%-5level %class{36} %M %msg%xEx%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Logger name="com.example.logging.App" level="ERROR" additivity="false">
            <AppenderRef ref="Console" />
        </Logger>
        <Root level="debug">
            <AppenderRef ref="Console" />
        </Root>
    </Loggers>
</Configuration>

The newly defined filter is pretty similar to DynamicThresholdFilter. 新定义的过滤器与DynamicThresholdFilter非常相似。 The difference is DynamicThresholdFilter uses the predefined level in configuration file as the dynamic threshold, while this filter uses the level programmatically defined in the map . 不同之处在于DynamicThresholdFilter使用配置文件中的预定义级别作为动态阈值,而此过滤器使用地图中以编程方式定义的级别。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Heroku-使用多个Dynos更改日志级别运行时 - Heroku - Change Log Level Runtime with Several Dynos 无法在运行时更改日志级别(log4j2) - Can't change log level at runtime (log4j2) 如何在运行时更改日志级别而不重新启动Vert.x应用程序 - How do I change log level in runtime without restarting Vert.x application 如何在运行时更改 log4j2.xml 中的记录器级别而不重新启动 tomcat 服务 - How to change logger level in log4j2.xml at runtime without restarting tomcat service 如何在java.util.logging中获取记录器,以便我可以在运行时更改其日志级别? - How to get loggers in java.util.logging so i can change their log level at runtime? 如何更改 KafkaStream 的日志级别 - How to change log level of KafkaStream 如何在运行时更改kubernetes托管服务中的Java slf4j记录器上的日志级别? - How to change log level on a Java slf4j logger in a kubernetes managed service in runtime? 如何在不重新启动 Spring Boot 应用程序的情况下在运行时更改日志级别 - how do I change log level in runtime without restarting spring boot application 我们可以在运行时更改log4j的日志记录级别吗? - can we change the logging level of log4j at runtime Log4j2 不会在运行时更改日志记录级别 - Log4j2 does not change logging level at runtime
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM