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

 

posted @ 2018-06-29 12:01  zhjh256  阅读(4963)  评论(0编辑  收藏  举报