#吐槽和說明 寫以前我得吐槽一下開源的產品汪,博客下面的「是否對全部人可見:」麻煩改爲是否我的可見!!!,我覺得那個是隻能關注的人才可見的,結果是私人可見。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