[笔记系列文章说明]: 该类型的文章是笔者学习过程中整理的学习笔记.
logback的配置详解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
| <?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="5 seconds" debug="true"> <property name="app.name" value="app" /> <property name="log.level" value="debug" /> <property name="log.maxHistory" value="30" /> <property name="log.filePath" value="${catalina.base}/logs/webapps" /> <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSSS} [%thread] %-5level %logger{50}-%msg%n" /> <property name="log.pattern2" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" />
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>${log.pattern2}</pattern> </encoder> </appender>
<appender name="FILE1" class="ch.qos.logback.core.rolling.RollingFileAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> <charset>utf-8</charset> </encoder> <file>log/output.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"> <fileNamePattern>rolllog/output.log.%i</fileNamePattern> </rollingPolicy> <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <MaxFileSize>1KB</MaxFileSize> </triggeringPolicy> </appender>
<appender name="FILE2" class="ch.qos.logback.core.rolling.RollingFileAppender"> <encoder> <pattern>%d [%t] %-5level %logger{36} [%file : %line] - %msg%n</pattern> <charset>utf-8</charset> </encoder> <file>log/${app.name}.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"> <fileNamePattern>rolllog/${app.name}.log.%i</fileNamePattern> </rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <MaxFileSize>1KB</MaxFileSize> </triggeringPolicy> </appender> <appender name="file.async" class="ch.qos.logback.classic.AsyncAppender"> <discardingThreshold>0</discardingThreshold> <queueSize>256</queueSize> <includeCallerData>true</includeCallerData> <appender-ref ref="FILE2" /> </appender>
<appender name="file.async.other" class="ch.qos.logback.classic.AsyncAppender"> <discardingThreshold>0</discardingThreshold> <queueSize>256</queueSize> <includeCallerData>true</includeCallerData> <appender-ref ref="FILE1" /> </appender>
<logger name="ch.qos" level="%{log.level}" additivity="false"> <appender-ref ref="file.async" /> <appender-ref ref="file.async.other" /> </logger> <root level="INFO"> <appender-ref ref="CONSOLE" /> <appender-ref ref="FILE1" /> <appender-ref ref="FILE2" /> </root> </configuration>
|
MDC,NDC
什么是MDC?
Mapped Diagnostic Context
线程独立自定义Map存储上下文信息,子线程拷贝父线程Map信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 1.保存信息到上下文 MDC.put(key, value);
2.从上下文获取设置的信息 MDC.get(key);
3.清楚上下文中指定的key的信息 MDC.remove(key);
4.清除所有 clear()
5.输出模板,注意是大写[%X{key}] log4j.appender.consoleAppender.layout.ConversionPattern = %-4r [%t] %5p %c %x - %m - %X{key}%n
|
什么是NDC?
Nested Diagnostic Context
线程独立自定义栈存储上下文信息,子线程拷贝父线程栈信息
1 2 3 4 5 6 7 8 9 10 11
| 1.开始调用 NDC.push(message);
2.删除栈顶消息 NDC.pop();
3.清除全部的消息,必须在线程退出前显示的调用,否则会导致内存溢出。 NDC.remove();
4.输出模板,注意是小写的[%x] log4j.appender.stdout.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ssS}] [%x] : %m%n
|
logback中的MDC
单机MDC使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.encoder.PatternLayoutEncoder; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.ConsoleAppender; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC;
public class SimpleMDC { static public void main(String[] args) { Logger logger = LoggerFactory.getLogger(SimpleMDC.class); programmaticConfiguration(); logger.info("今天的天气真不错"); logger.info("是的, 雨好大啊"); MDC.put("one", "one"); MDC.put("two", "two"); logger.info("今天的天气真不错"); logger.debug("是的, 雨好大啊"); MDC.put("one", "three"); MDC.put("two", "four"); logger.info("今天的天气真不错"); logger.info("是的, 雨好大啊"); MDC.remove("one"); MDC.remove("two"); }
static void programmaticConfiguration() { LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); loggerContext.reset(); PatternLayoutEncoder layout = new PatternLayoutEncoder(); layout.setContext(loggerContext); layout.setPattern("%X{one} %X{two} - %m%n"); layout.start(); ConsoleAppender<ILoggingEvent> appender = new ConsoleAppender<ILoggingEvent>(); appender.setContext(loggerContext); appender.setEncoder(layout); appender.start(); ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("root"); root.addAppender(appender); } }
|
跨服务MDC使用(web + dubbo)
- 使用Spring拦截器HandlerInterceptorAdapter初始化MDC trace
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
public class TraceLogInterceptor extends HandlerInterceptorAdapter {
private static final String TRACE_KEY = "traceId";
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception { String a = UUID.randomUUID().toString().replace("-", ""); MDC.put(TRACE_KEY, a); HttpLogLocalCache.setMsg( a); return true; }
@Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { MDC.clear(); } }
|
- 使用Dubbo Filter拦截, RpcContext传递
调用者设置值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class TraceConsumerFilter implements Filter { private static Logger logger = LoggerFactory.getLogger(TraceConsumerFilter.class); private static final String TRACE_KEY = "traceId"; @Override public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { LoggerFactory.getLogger(invoker.getInterface().getName()).info(" [dubbo consumer] 服务调用-请求|method:{}|参数:{}| client[ip:{}-protocol:{}]", invocation.getMethodName(), invocation.getArguments(), RpcContext.getContext().getRemoteAddressString(), invoker.getUrl().getProtocol()); try { logger.debug("MDC-traceId:" + MDC.get(TRACE_KEY)); RpcContext.getContext().setAttachments(MDC.getCopyOfContextMap()); } catch (Exception ex) { logger.error("dubbo consumer MDC-traceId:reset fail"); } Result result = invoker.invoke(invocation); LoggerFactory.getLogger(invoker.getInterface().getName()).info(" [dubbo consumer] 服务调用-返回|method:{}|结果:{}| client[ip:{}-protocol:{}]", invocation.getMethodName(), result, RpcContext.getContext().getRemoteAddressString(), invoker.getUrl().getProtocol()); return result; }
}
|
消费者获取值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| public class TraceProviderFilter implements Filter {
@Override public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException { RpcContext context = RpcContext.getContext(); MDC.setContextMap(context.getAttachments()); LoggerFactory.getLogger(invoker.getInterface().getName()).info(" [dubbo provider] 服务调用-接收|method:{}|参数:{}| client[ip:{}-protocol:{}]", inv.getMethodName(), inv.getArguments(), RpcContext.getContext().getRemoteAddressString(), invoker.getUrl().getProtocol()); long start = System.currentTimeMillis(); Result result = invoker.invoke(inv); long time = System.currentTimeMillis() - start; Object rval = result.getValue(); String logtxt = null; if (rval != null) { Class<?> clazz = rval.getClass(); if (List.class.isAssignableFrom(clazz)) { int size = ((List) rval).size(); if (size > 5) { logtxt = size + "(SIZE)"; } else { logtxt = JSON.toJSONString(rval, new LogArgumentFilter()); } } else if (Set.class.isAssignableFrom(clazz)) { int size = ((Set) rval).size(); if (size > 5) { logtxt = size + "(SIZE)"; } else { logtxt = JSON.toJSONString(rval, new LogArgumentFilter()); } } else { logtxt = JSON.toJSONString(rval, new LogArgumentFilter()); } } LoggerFactory.getLogger(invoker.getInterface().getName()).info(" [dubbo provider] 服务调用-返回|method:{}|结果:{}| client[ip:{}-protocol:{}]", inv.getMethodName(), logtxt, RpcContext.getContext().getRemoteAddressString(), invoker.getUrl().getProtocol()); LoggerFactory.getLogger(invoker.getInterface().getName()).info(" [dubbo provider 服务性能] 统计|method:{}|耗时:{} ms|host:{}", inv.getMethodName(), time, RpcContext.getContext().getRemoteAddressString()); try { MDC.clear(); } catch (Exception ex) { LoggerFactory.getLogger(TraceProviderFilter.class).error("dubbo provider MDC-traceId:reset fail"); } return result; } }
|
调用者配置 spring-dubbo-reference.xml
1
| <dubbo:consumer filter="traceConsumerFilter" version="1.0" timeout="3000" check="${dubbo.reference.check}"/>
|
提供者者配置 spring-dubbo-provider.xml
1
| <dubbo:provider protocol="dubbo" filter="traceProviderFilter" version="1.0" timeout="3500" delay="-1" />
|
增加dubbo filter配置
1 2 3 4
| 路径: src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter 内容: traceConsumerFilter=com.qctrip.monitor.common.base.dubbo.spi.TraceConsumerFilter traceProviderFilter=com.qctrip.monitor.common.base.dubbo.spi.TraceProviderFilter
|