#吐槽和说明 写以前我得吐槽一下开源的产品汪,博客下面的“是否对全部人可见:”麻烦改为是否我的可见!!!,我觉得那个是只能关注的人才可见的,结果是私人可见。java
为了把个人开源中国技能改回Java我这也是拼了....若是大家上一篇收藏了,这篇没收藏我会伤心的。web
上一篇SpringBoot中的日志配置,多环境日志配置 里面配置了多环境日志记录,这一篇是纯架构干货,记得收藏哦,纯代码,在上一篇的基础上修改spring
#设计方案及缘由 一个庞大的系统,或者一个访问量高的系统,在发生异常的时候,你们定位问题是怎么定位的呢?要求5分钟以内知道问题引发的缘由并给出初步结论?多台服务器经过F5分发的状况下呢?sql
其实你们都是去看日志,不过不少人用的都是默认日志记录,最可能是日志里记录了执行sql,虽然SpringBoot的自己日志很详细了,可是定位不快速,要想快速定位须要一个请求串号,我须要知道这个请求串号惟一标识,直接找到这个串号范围以内的日志内容,这比你打开10MB,30MB里面寻找快多了吧。api
#修改Logback日志记录格式缓存
先看一下上一节的日志输出格式,以下,使用的是PatternLayoutEncoder这个类,服务器
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </encoder>
PatternLayoutEncoder类里面用了PatternLayout这个类,这个类里面就是声明的格式字符串,架构
public class PatternLayoutEncoder extends PatternLayoutEncoderBase<ILoggingEvent> { public PatternLayoutEncoder() { } public void start() { PatternLayout patternLayout = new PatternLayout(); patternLayout.setContext(this.context); patternLayout.setPattern(this.getPattern()); patternLayout.setOutputPatternAsHeader(this.outputPatternAsHeader); patternLayout.start(); this.layout = patternLayout; super.start(); } }
PatternLayout类里面的格式app
public class PatternLayout extends PatternLayoutBase<ILoggingEvent> { public static final Map<String, String> defaultConverterMap = new HashMap(); public static final String HEADER_PREFIX = "#logback.classic pattern: "; static { defaultConverterMap.putAll(Parser.DEFAULT_COMPOSITE_CONVERTER_MAP); defaultConverterMap.put("d", DateConverter.class.getName()); defaultConverterMap.put("date", DateConverter.class.getName()); defaultConverterMap.put("r", RelativeTimeConverter.class.getName()); defaultConverterMap.put("relative", RelativeTimeConverter.class.getName()); defaultConverterMap.put("level", LevelConverter.class.getName()); ....
那咱们来创建一个格式化的类,IpConverter,这个类用来记录是那台服务器IP及机器名产生的日志。dom
/** * [@ClassName](https://my.oschina.net/u/3112573): BussinesLogger * @Description: 业务日志记录类 * [@author](https://my.oschina.net/arthor) dengzongyu * [@date](https://my.oschina.net/u/2504391) 2016年12月5日 下午4:29:48 * */ public class IpConverter extends ClassicConverter { /** * 业务系统机器名称 */ private static String hostName = ""; /** * 业务系统机器的IP */ private static String hostIp = ""; static { InetAddress ia = null; try { ia = InetAddress.getLocalHost(); hostIp = ia.getHostAddress(); hostName = ia.getHostName(); } catch (Exception e) { e.printStackTrace(); } } @Override public String convert(ILoggingEvent event) { return new StringBuilder(hostIp).append(":").append(hostName).toString(); } }
而后写一个类集成咱们上面提到的PatternLayout类,用来注入日志记录格式,格式多了个ip。
/** * @ClassName: GAPatternlayout * @Description: Logback配置文件注入 * @author dengzongyu * @date 2016年12月5日 下午5:07:18 * */ public class GAPatternlayout extends PatternLayout { static{ defaultConverterMap.put("ip", IpConverter.class.getName()); } }
接下来修改logback-spring.xml配置文件,修改的位置就是修改那两个格式输出的位置,类改成咱们本身写的,这样子输出出来的格式就是:服务器IP:服务器名字:日期,线程名,级别从左显示5个字符宽度:日志消息等...如今咱们日志里面容易定位出是哪台服务器产生的异常了。
<layout class="jldp.portal.framework.logback.GAPatternlayout"> <Pattern>%ip:%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -- %msg %n </Pattern> </layout>
#Web请求日志记录,日志记录串 上面那些基本够用了,可是咱们想更细致一些,好比用户是请求哪一个API接口,请求参数是什么,请求接口响应时间等等.... 这个讲解都在代码注释里面了,上面就不重复了,直接上代码。里面有个IPUtil这个是就是获取客户端IP的工具类,网上一大堆,就不贴了,各位根据本身的须要来,这个是我本身写的,我以为够了,大家能够设计的更加完美一些,为了未来大数据分析考虑,写成JSON格式的更好是不。
/** * @ClassName: WebRequestLogRecording * @Description: Web请求日志记录 * @author dengzongyu * @date 2016年12月6日 下午3:40:25 * */ @Aspect @Component @Order(-5) public class WebRequestLogRecording { private static Logger logger = LoggerFactory.getLogger(WebRequestLogRecording.class); /** * 每次请求的串 */ private ThreadLocal<String> requestUUID = new ThreadLocal<String>(); private ThreadLocal<Long> startTime = new ThreadLocal<Long>(); /** * * WebRequestLog: 定义拦截点 * @author dengzongyu * @since JDK 1.8 */ @Pointcut("execution(public * com.dengzy..controller..*.*(..))") public void webRequestLog(){} /** * * doBefore:进入切入点前进行操做. * * @author dengzongyu * @since JDK 1.8 */ @Before("webRequestLog()") public void doBefore(JoinPoint joinPoint){ //获取UUID标识 String REQUEST_UUID = java.util.UUID.randomUUID().toString().replaceAll("-", "").toUpperCase(); requestUUID.set(REQUEST_UUID); startTime.set(System.currentTimeMillis()); ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); request.setAttribute("REQUEST_UUID", REQUEST_UUID); StringBuffer sbStr = new StringBuffer("ClentRequestInfoStar===REQUEST_UUID:") .append(REQUEST_UUID).append(",URL:")//监控参数 .append(request.getRequestURL()).append(",") .append("HTTP_TYPE:").append(request.getMethod()).append(",") .append("CLASS_METHOD:").append(joinPoint.getSignature().getName()).append(",") .append("CLIENT_IP:").append(IPUtils.getClientIpAddress(request)).append(",") .append("PARAMS:"); Enumeration<String> enu=request.getParameterNames(); while(enu.hasMoreElements()){ String elementType = enu.nextElement(); sbStr.append(elementType).append("=").append(request.getParameter(elementType)).append("&"); } logger.info(sbStr.deleteCharAt(sbStr.length()-1).toString()); } @AfterReturning(returning = "result", pointcut = "webRequestLog()") public void doAfterReturning(Object result) { StringBuffer sbStr = new StringBuffer("ClentRequestInfoEnd===REQUEST_UUID:") .append(requestUUID.get()).append(",REQUEST_TIME:"+(System.currentTimeMillis()-startTime.get())).append("ms"); logger.info(sbStr.toString()); } }
我测试了一下,咱们上面整合完后的日志打印状况,注意日志里面的REQUEST_UUID,这个就是一个请求串,咱们只须要找REQUEST_UUID这个对应的开始于结束之间的日志内容就能够看出问题了哦。
192.168.135.1:LAPTOP-796KKEL9:10:38:11.667 [http-nio-8080-exec-1] INFO j.p.f.logback.WebRequestLogRecording -- ClentRequestInfoStar===REQUEST_UUID:1E3B70307FB443B4A5710382F2B8C225,URL:http://localhost:8080/api/investment/fsdt/baseTypeController/getBaseTypeByKey,HTTP_TYPE:GET,CLASS_METHOD:getBaseTypeByKey,CLIENT_IP:0:0:0:0:0:0:0:1,PARAMS:dtbaseid=char >>>>1212066477842432 819010491752910848 819010493019590656 819010494290464768 819010495569727488 819010496840601600 没有缓存的时候,进入方法进行查询啦 192.168.135.1:LAPTOP-796KKEL9:10:38:13.657 [http-nio-8080-exec-1] ERROR c.g.i.i.c.InvestBaseTypeController -- 1E3B70307FB443B4A5710382F2B8C225**********查询条件不能为空 192.168.135.1:LAPTOP-796KKEL9:10:38:14.007 [http-nio-8080-exec-1] ERROR c.g.i.i.c.InvestBaseTypeController -- >>>>>>>>>>>>>2 192.168.135.1:LAPTOP-796KKEL9:10:38:14.020 [http-nio-8080-exec-1] INFO j.p.f.logback.WebRequestLogRecording -- ClentRequestInfoEnd===REQUEST_UUID:1E3B70307FB443B4A5710382F2B8C225,REQUEST_TIME:2395ms