Java for Web学习笔记(五一):Log(3)代码中使用log4j2

常用方式

  Log4j2的等级有FATAL,ERROR,WARN,INFO,DEBUG,TRACE,另外还有全开全关的非登记ALL,OFF。

输出不同级别的log信息

//【1】创建实例。等同与LogManager.getLogger(MyServlet.class);可以logger.getName()来验证。这个名字就是log的category,每个类应该有自己的log实例
private static final Logger log = LogManager.getLogger();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
    if(request.getParameter("action") == null)
        log.error("No action specified."); //【2】输出error级别的log
    else
        log.info("action {}",action);      //【2】输出info级别的log,采用{}作为参数填入。
}

跟踪方法的输入参数和返回结果

private int test(String s1,String s2){
     log.entry(s1,s2);
     log.traceEntry("Parameters:{} {}",s1,s2);
    log.debug("s1={}, s2={}",s1,s2);
     return log.traceExit(4);
}
输出结果为:

17:08:39.969 [http-nio-8080-exec-4] [TRACE] LoginServlet test(LoginServlet.java:66) - Enter params(hello, world)
17:08:39.973 [http-nio-8080-exec-4] [TRACE] LoginServlet test(LoginServlet.java:67) - Enter Parameters:hello world
17:08:39.974 [http-nio-8080-exec-4] [DEBUG] LoginServlet test(LoginServlet.java:68) - s1=hello, s2=world
17:08:39.974 [http-nio-8080-exec-4] [TRACE] LoginServlet test(LoginServlet.java:69) - Exit with(4)

退出时关闭

  在Log4j2的2.6版本开始引入shutdown(),在2.6中,如果web app卸载时没有进行shutdown(),tomcat会发布内存泄漏风险的告警,如下:
六月 28, 2017 11:10:56 上午 org.apache.catalina.loader.WebappClassLoaderBase clearReferencesThreads
警告: The web application [customer-support] appears to have started a thread named [Log4j2-TF-3-Scheduled-1] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
 sun.misc.Unsafe.park(Native Method)
 java.util.concurrent.locks.LockSupport.parkNanos(Unknown Source)
 java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(Unknown Source)
 java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(Unknown Source)
 java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(Unknown Source)
 java.util.concurrent.ThreadPoolExecutor.getTask(Unknown Source)
 java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
 java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
 java.lang.Thread.run(Unknown Source)

有两种方式可以解决。

【方式一】收到安全退出,完成资源释放,执行shutdown()


//在ServletContextListener中关闭Log
/**
  * @see ServletContextListener#contextDestroyed(ServletContextEvent)
  */
public void contextDestroyed(ServletContextEvent sce)  { 
    LogManager.shutdown();
}
【方式二】进入log-web的jar包,由其协助log4j2自动完成
<dependency>
     <groupId>org.apache.logging.log4j</groupId>
     <artifactId>log4j-web</artifactId>
     <version>${log4j.version}</version>
</dependency>

自定义level

  Level具有优先级别(越小越高)以及Level名字。我们先来查看一下:

logger.info("FATAL :" + Level.FATAL.intLevel()  + "," + Level.FATAL.name());
logger.info("ERROR :" + Level.ERROR.intLevel()  + "," + Level.ERROR.name());
logger.info("WARN :" + Level.WARN.intLevel()  + "," + Level.WARN.name());
logger.info("INFO :" + Level.INFO.intLevel()  + "," + Level.INFO.name());
logger.info("DEBUG :" + Level.DEBUG.intLevel()  + "," + Level.DEBUG.name());
logger.info("TRACE :" + Level.TRACE.intLevel()  + "," + Level.TRACE.name());

14:16:46.408 [localhost-startStop-1] [INFO ] GlobalListener:43  - FATAL :100,FATAL
14:16:46.408 [localhost-startStop-1] [INFO ] GlobalListener:44  - ERROR :200,ERROR
14:16:46.408 [localhost-startStop-1] [INFO ] GlobalListener:45  - WARN :300,WARN
14:16:46.408 [localhost-startStop-1] [INFO ] GlobalListener:46  - INFO :400,INFO
14:16:46.408 [localhost-startStop-1] [INFO ] GlobalListener:47  - DEBUG :500,DEBUG
14:16:46.408 [localhost-startStop-1] [INFO ] GlobalListener:48  - TRACE :600,TRACE

步骤一:自定义Level

public class CustomLevels {
    public static final Level CONFIG = Level.forName("CONFIG", 350);
    public static final Level NOTICE = Level.forName("NOTICE", 450);
    public static final Level DIAG = Level.forName("DIAG", 550);
}

步骤二:设置自定义的level

public void initLogger() {
    Configurator.setLevel("CONFIG", CustomLevels.CONFIG);
    Configurator.setLevel("NOTICE", CustomLevels.NOTICE);
    Configurator.setLevel("DIAG", CustomLevels.DIAG);
}

步骤三:使用自定义的level

logger.log(CustomLevels.CONFIG, "Config customer logger.");
logger.log(CustomLevels.NOTICE,"NOTICE TEST");
logger.info(logger.isEnabled(CustomLevels.DIAG));

  输出为:

14:39:13.833 [ContainerBackgroundProcessor[StandardEngine[Catalina]]] [CONFIG] GlobalListener:42  - Config customer logger.
14:39:13.842 [ContainerBackgroundProcessor[StandardEngine[Catalina]]] [NOTICE] GlobalListener:43  - NOTICE TEST
14:39:13.843 [ContainerBackgroundProcessor[StandardEngine[Catalina]]] [INFO] GlobalListener:44  - false

使用MARKER

  相关配置。如果是动作是NEUTRAL,则表示继续往下匹配,如果一直没有匹配出来,就是ACCEPT。
<logger name="cn.wei.flowingflying" level="info" additivity="false">
    <appender-ref ref="WroxFileAppender" />
    <appender-ref ref="Console">
        <MarkerFilter marker="MARKER_CONSOLE" onMatch="ACCEPT" onMismatch="DENY" />
    </appender-ref>            
</logger>

  代码如下

logger.trace(MarkerManager.getMarker("MARKER_CONSOLE"), "Hello, TRACE");
logger.debug(MarkerManager.getMarker("MARKER_CONSOLE"), "Hello, DEBUG");
logger.info(MarkerManager.getMarker("MARKER_CONSOLE"), "Hello, INFO");

ThreadContext

  在上文提到%X{key},可以显示ThreadContext该key的值。当web访问的用户量很大时,log很多,我们需要区分哪些是某个request下引发的log。例如,我们希望知道是那个用户在进行操作。

步骤1:代码定义LoggingFilter

public void contextInitialized(ServletContextEvent sce)  { 
     ServletContext context = sce.getServletContext();

     //1)定义loggerFilter
     FilterRegistration.Dynamic registration = context.addFilter(
             "loggingFilter", new LoggingFilter()
     );

     registration.addMappingForUrlPatterns(
             EnumSet.of(DispatcherType.REQUEST, DispatcherType.INCLUDE,
                     DispatcherType.FORWARD, DispatcherType.ERROR),
             false, "/*"
     );

     //2)定义其他的Filter
     ... ...
}

步骤2:实现LoggingFilter,设置ThreadContext中map的值

public class LoggingFilter implements Filter {
    /**
     * 我们为每个ThreadContext增加一个id作为其唯一标识,并在Thread结束后全部清空。
     * 如果一个请求存在跳转,就可能多次经过这个doFilter()。
     * 在最外面那级,clear设置为true,并设置id和username,并在最外一级结束时清空数据。
     * 其实id对我们没太多意义,使用%t才看线程名字即可,可以只设置username。
     */
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException,ServletException{
        boolean clear = false;

        if(!ThreadContext.containsKey("id")){
            clear = true;
            ThreadContext.put("id", UUID.randomUUID().toString());
            HttpSession session = ((HttpServletRequest)request).getSession(false);
            if(session != null){
                ThreadContext.put("username", (String)session.getAttribute("username"));
            }            
        }
        try{
            chain.doFilter(request, response);
        }finally{
            if(clear){
                ThreadContext.clearAll();            
            }
        }
    }

    ... ...
}

步骤3:在log中显示username和id

<pattern>%d{HH:mm:ss.SSS} [%t] %X{id} %X{username} [%-5level] %c{1}(%M):%L %msg%n</pattern>

相关链接: 我的Professional Java for Web Applications相关文章

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值