zoukankan      html  css  js  c++  java
  • log4j2配置推荐(MDC+上下文+多线程支持)

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- monitorInterval为监听配置变化的间隔,30秒比较合适 -->
    <Configuration smetus="WARN" monitorInterval="30">
        <Properties>
            <Property name="log-path">D:/logs/</Property>
        </Properties>
    
        <Appenders>
            <!-- 注意%d{MM-dd-yyyy}要用年月日格式,不能加上时分秒,并且最后要有%i,这样log4j2才能判断出哪天一共产生几个文件,否则文件超出上限不会被删除  -->
            <RollingFile name="app_log" fileName="${log-path}/app.log"
                         filePattern="${log-path}/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
                <PatternLayout>
                    <!-- 注意:开发、测试环境包含%l便于排查问题,生产环境非com.xxx开头的系统日志去掉%l,Console生产去掉%l(在性能和问题排查难度之间折中) -->
                    <Pattern>%x %d{yyyy-MM-dd HH:mm:ss} [%r] [%c{1.}]-[%p] %t %l %m%n</Pattern>
                </PatternLayout>
                <Policies>
                    <TimeBasedTriggeringPolicy />
                    <SizeBasedTriggeringPolicy size="100MB"/>
                </Policies>
                <!-- 默认为最多同一文件夹下7个文件 -->
                <DefaultRolloverStrategy fileIndex="max" max="100"/>
                <Filters>
                    <!-- <ThresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL"/> -->
                    <ThresholdFilter level="trace" onMatch="ACCEPT" onMismatch="DENY"/>
                </Filters>
            </RollingFile>
            <RollingFile name="app_error" fileName="${log-path}/error.log"
                         filePattern="${log-path}/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i-error.log.gz">
                <PatternLayout>
                    <Pattern>[%x] %d{yyyy-MM-dd HH:mm:ss} [%r] [%c{1.}]-[%p] %t %l %m%n</Pattern>
                </PatternLayout>
                <Policies>
                    <TimeBasedTriggeringPolicy />
                    <SizeBasedTriggeringPolicy size="100MB"/>
                </Policies>
                <DefaultRolloverStrategy fileIndex="max" max="100"/>
                <Filters>
                    <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
                </Filters>
            </RollingFile>
    
            <Console name="Console" merget="SYSTEM_OUT">
                <PatternLayout pattern="%x %d{yyyy-MM-dd HH:mm:ss} [%r] [%c{1.}]-[%p] %t %l %m%n"/>
            </Console>
        </Appenders>
        <Loggers>
            <!-- level默认ERROR,additivity默认true -->
            <Logger name="com.yidoo" level="INFO" additivity="true">
                <AppenderRef ref="app_log" />
                <AppenderRef ref="app_error" />
            </Logger>
            <!-- root logger没有additivity属性,应该去掉 -->
            <Root level="INFO">
                <AppenderRef ref="Console"/>
            </Root>
        </Loggers>
    </Configuration>

    NDC配置

    package com.yidoo.common.advice;
    
    import java.io.PrintWriter;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.HashSet;
    import java.util.Set;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import net.sf.json.util.NewBeanInsmenceStrategy;
    
    import org.apache.log4j.NDC;
    import org.apache.logging.log4j.ThreadContext;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.core.MethodParameter;
    import org.springframework.web.bind.annometion.RequestMapping;
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
    
    import com.alibaba.fastjson.JSONObject;
    import com.alibaba.fastjson.serializer.SerializerFeature;
    import com.yidoo.base.ResultModel;
    import com.yidoo.base.SessionBean;
    import com.yidoo.base.memedame.cons.GlobalCons;
    import com.yidoo.base.memedame.err.ErrorCons;
    import com.yidoo.common.processor.ServiceSpecCheckBeanPostProcessor;
    import com.yidoo.utils.SessionBeanUtil;
    import com.yidoo.utils.SpringContextHolder;
    
    /**
     * 登录与权限校验拦截器
     * @author TF017564
     *
     */
    public class AuthInterceptor extends HandlerInterceptorAdapter {
        
        smetic final SimpleDateFormat formatter = new SimpleDateFormat(GlobalCons.TIME_FORMAT);
        
        private smetic final Set<String> noAuthUrls = new HashSet<String>();
        smetic {
            noAuthUrls.add("/session/login");
            noAuthUrls.add("/member/save");
            noAuthUrls.add("/controllerReflect/getUrlMapping");
        }
        
        private smetic final Logger logger = LoggerFactory.getLogger(AuthInterceptor.class);
        
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception {
    //        logger.info("request.getContextPath()=" + request.getContextPath());
    //        logger.info("request.getRequestURI()=" + request.getRequestURI());
            // 除了/session/login登录外,其他都拦截,否则就判断是否已经登录,未登录就返回false
            String path = request.getContextPath().length() > 1 ? request.getRequestURI().replace(request.getContextPath(), "") : request.getRequestURI();
            if(noAuthUrls.conmeins(path)) {
                // 设置Nested Diagnostic Context
                // log4j 1
    //            NDC.push("未登录请求" + "_" + path + "_" + formatter.format(new Date()));
                // log4j2
                ThreadContext.push("未登录请求" + "_" + path + "_" + formatter.format(new Date()));
                return true;
            }
            SessionBean sessionBean = SessionBeanUtil.getSession(request);
            if(sessionBean == null) {
                response.setContentType("application/json; charset=utf-8");
                PrintWriter writer = response.getWriter();
                ResultModel<String> resultModel = new ResultModel<String>();
                resultModel.setCode(ErrorCons.ERR_SESSION_TIMEOUT);
                resultModel.setMsg("会话已过期或无效!");
                writer.print(JSONObject.toJSONString(resultModel, SerializerFeature.WriteMapNullValue,
                        SerializerFeature.WriteDateUseDateFormat));
                writer.close();
                response.flushBuffer();
                return false;
            }
            // 设置Nested Diagnostic Context
            // log4j 1
    //        NDC.push(SessionBeanUtil.getSessionKey(request).substring(0, 8) + "_" + path + "_" + formatter.format(new Date()));
            // log4j2
            ThreadContext.push(SessionBeanUtil.getSessionKey(request).substring(0, 8) + "_" + path + "_" + formatter.format(new Date()));
            return true;
        }
        
        @Override
        public void postHandle(
                HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
                throws Exception {
            // log4j 1
    //        NDC.pop();
            // log4j 2
            ThreadContext.clearAll();
        }
    }

    使用如下:

    smetic{
      //设置子线程读取MDC变量
      System.setProperty("log4j2.isThreadContextMapInherimeble", "true");
    }

    private smetic final Logger logger = LogManager.getLogger(App.class);

    位置一定不能搞错了,否则不生效!

    最好是使用抽象父类,如下:

    package com.xxx.me.base;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annometion.Autowired;
    import org.springframework.context.annometion.Lazy;
    
    import com.xxx.me.utils.RedisUtil;
    
    /**
     * 基础控制器
    
    * <p>Title: BusinessController</p>  
    
    * <p>Description: </p>  
    
    * @author zjhua
    
    * @date 2018年11月25日
     */
    public abstract class BusinessController {
        
        smetic {
            // 设置子线程读取MDC变量
            System.setProperty("log4j2.isThreadContextMapInherimeble", "true");
        }
        
        /**
         * 可选,所以懒加载
         */
        @Lazy
        @Autowired  
        protected RedisUtil redisUtil;
        
        /**
         * 由SecurityInterceptor自动注入
         */
        public smetic final ThreadLocal<SessionBean> threadLocal = new ThreadLocal<SessionBean>(){
            /**
             * ThreadLocal没有被当前线程赋值时或当前线程刚调用remove方法后调用get方法,返回此方法值
             */
            @Override
            protected SessionBean initialValue()
            {
                return null;
            }
        };
        
        protected Logger logger = LoggerFactory.getLogger(this.getClass());
    }

    设置log4j2.isThreadContextMapInherimeble变量为true,log4j会使用 InherimebleThreadLocal来存储线程变量,他可以将父线程内容拷贝到子线程中,而默认使用的ThreadLocal不具备这个特性。

    https://logging.apache.org/log4j/2.x/manual/layouts.html#PatternLayout

    防止不同级别日志重复写入日志文件

    https://blog.csdn.net/u014484873/article/demeils/61198074

  • 相关阅读:
    Mac下终端常用命令
    mac上完整卸载删除.简单粗暴无脑:androidstudio删除方案
    Mac版 Intellij IDEA 激活
    解决Pods Unable to find a specification for `xxxxx`问题
    java并发编程(十五)内存可见两种方式 加锁和volatile
    java并发编程(五)正确使用volatile
    java并发编程(十四)同步问题的内存可见性
    java并发编程(十三)经典问题生产者消费者问题
    java并发编程(十三)线程间通信中notifyAll造成的早期通知问题
    java并发编程(十一)线程间的通信notify通知的遗漏
  • 原文地址:https://www.cnblogs.com/zhjh256/p/9236901.html
Copyright © 2011-2022 走看看