<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ## 前言 * 系統一大,就會拆分成多個獨立的進程,比如使用微服務架構,也就成了分布式系統。 * 傳統的日志系統比如log4j已經無法滿足,我們需要將這些日志合并展示到一個統一的地方。 * 如此一來,分布式日志收集系統就登場了。 * 現在用的較多的技術組合為 ElasticSearch+ logstash(基于java)+kibana(基于JRuby, logstash已自帶),也就是大家常說的`ELK`。 * 但是此系統較為重量級并不是很適合輕量級微服務架構,SpringBlade封裝了一個相對好拓展的日志系統,下面我們來具體看一下把!~ ## 實現思路 ### 全局統一異常 * 使用`@RestControllerAdvice`,捕獲異常后統一返回封裝好的格式。 * 異常分成兩種:已知異常與未知異常,未知異常是著重需要關注的,所以會將未知異常入庫,方便排查 * 以下為全局異常核心代碼 ~~~ @Slf4j @Configuration @ConditionalOnClass({Servlet.class, DispatcherServlet.class}) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) @RestControllerAdvice public class BladeRestExceptionTranslator { @ExceptionHandler(MissingServletRequestParameterException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public R handleError(MissingServletRequestParameterException e) { log.warn("缺少請求參數", e.getMessage()); String message = String.format("缺少必要的請求參數: %s", e.getParameterName()); return R.failure(ResultCode.PARAM_MISS, message); } ............... ............... ............... @ExceptionHandler(ServiceException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public R handleError(ServiceException e) { log.error("業務異常", e); return R.failure(e.getResultCode(), e.getMessage()); } @ExceptionHandler(SecureException.class) @ResponseStatus(HttpStatus.UNAUTHORIZED) public R handleError(SecureException e) { log.error("認證異常", e); return R.failure(e.getResultCode(), e.getMessage()); } @ExceptionHandler(Throwable.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public R handleError(Throwable e) { log.error("服務器異常", e); //發送服務異常事件 ErrorLogPublisher.publishEvent(e, UrlUtil.getPath(WebUtil.getRequest().getRequestURI())); return R.failure(ResultCode.INTERNAL_SERVER_ERROR, (Func.isEmpty(e.getMessage()) ? ResultCode.INTERNAL_SERVER_ERROR.getMessage() : e.getMessage())); } } ~~~ * 大家可以看下最后幾行的 `ErrorLogPublisher.publishEvent`方法,可以看出,該方法使用了Spring的Event事件驅動,進行解耦。異步操作日志的收集、入庫等。代碼如下 ~~~ /** * 異常信息事件發送 * * @author Chill */ public class ErrorLogPublisher { public static void publishEvent(Throwable error, String requestUri) { HttpServletRequest request = WebUtil.getRequest(); LogError logError = new LogError(); logError.setRequestUri(requestUri); if (Func.isNotEmpty(error)) { logError.setStackTrace(Exceptions.getStackTraceAsString(error)); logError.setExceptionName(error.getClass().getName()); logError.setMessage(error.getMessage()); StackTraceElement[] elements = error.getStackTrace(); if (Func.isNotEmpty(elements)) { StackTraceElement element = elements[0]; logError.setMethodName(element.getMethodName()); logError.setMethodClass(element.getClassName()); logError.setFileName(element.getFileName()); logError.setLineNumber(element.getLineNumber()); } } Map<String, Object> event = new HashMap<>(16); event.put(EventConstant.EVENT_LOG, logError); event.put(EventConstant.EVENT_REQUEST, request); SpringUtil.publishEvent(new ErrorLogEvent(event)); } } ~~~ * 跟蹤到 `SpringUtil.publishEvent(new ErrorLogEvent(event));` * 查看 `ErrorLogEvent`代碼 ~~~ /** * 錯誤日志事件 * * @author Chill */ public class ErrorLogEvent extends ApplicationEvent { public ErrorLogEvent(Map<String, Object> source) { super(source); } } ~~~ * 對應錯誤日志處理`ErrorLogListener `代碼 ~~~ /** * 異步監聽錯誤日志事件 * * @author Chill */ @Slf4j @AllArgsConstructor public class ErrorLogListener { private final ILogClient logService; private final ServerInfo serverInfo; private final BladeProperties bladeProperties; @Async @Order @EventListener(ErrorLogEvent.class) public void saveErrorLog(ErrorLogEvent event) { Map<String, Object> source = (Map<String, Object>) event.getSource(); LogError logError = (LogError) source.get(EventConstant.EVENT_LOG); HttpServletRequest request = (HttpServletRequest) source.get(EventConstant.EVENT_REQUEST); logError.setUserAgent(request.getHeader(WebUtil.USER_AGENT_HEADER)); logError.setMethod(request.getMethod()); logError.setParams(WebUtil.getRequestParamString(request)); logError.setServiceId(bladeProperties.getName()); logError.setServerHost(serverInfo.getHostName()); logError.setServerIp(serverInfo.getIPWithPort()); logError.setEnv(bladeProperties.getEnv()); logError.setCreateBy(SecureUtil.getUserAccount(request)); logError.setCreateTime(LocalDateTime.now()); logService.saveErrorLog(logError); } } ~~~ * 到了`ErrorLogListener `其實就可以進行拓展了,可以像現在這樣,直接調用日志接口入庫,也可以集成消息隊列,提高性能。最終,我們可以到數據庫中看到如下數據 ![](https://box.kancloud.cn/447d37c892af12f167e294b4a7c67cee_1557x845.png) ![](https://box.kancloud.cn/8a4041b5a5fc7ca86502783bcb728f99_1280x747.png) * 錯誤產生在哪個服務名,服務的環境,服務的ip,服務的host,異常堆棧詳情,異常類,異常所在方法,所在行數,傳的參數,操作人,操作時間等等信息一目了然。 * 簡單的幾處代碼,如若集成消息隊列或redis,我相信足以滿足輕量級微服務架構的需求。 ### API日志 * 自動生成錯誤日志的講完了,我們需要根據注解主動生成日志。 * 對于這一類我們一般使用AOP來實現功能,切到對應注解后,獲取當前請求的信息,然后再通過Event異步入庫 * AOP代碼如下 ~~~ /** * 操作日志使用spring event異步入庫 * * @author Chill */ @Slf4j @Aspect public class ApiLogAspect { @Around("@annotation(apiLog)") public Object around(ProceedingJoinPoint point, ApiLog apiLog) throws Throwable { //獲取類名 String className = point.getTarget().getClass().getName(); //獲取方法 String methodName = point.getSignature().getName(); // 發送異步日志事件 long beginTime = System.currentTimeMillis(); //執行方法 Object result = point.proceed(); //執行時長(毫秒) long time = System.currentTimeMillis() - beginTime; //記錄日志 ApiLogPublisher.publishEvent(methodName, className, apiLog, time); return result; } } ~~~ * 注解代碼如下 ~~~ /** * 操作日志注解 * * @author Chill */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ApiLog { /** * 日志描述 * * @return {String} */ String value() default "日志記錄"; } ~~~ * 日志發送器代碼如下 ~~~ /** * API日志信息事件發送 * * @author Chill */ public class ApiLogPublisher { public static void publishEvent(String methodName, String methodClass, ApiLog apiLog, long time) { HttpServletRequest request = WebUtil.getRequest(); LogApi logApi = new LogApi(); logApi.setType(BladeConstant.LOG_NORMAL_TYPE); logApi.setTitle(apiLog.value()); logApi.setTime(String.valueOf(time)); logApi.setMethodClass(methodClass); logApi.setMethodName(methodName); Map<String, Object> event = new HashMap<>(16); event.put(EventConstant.EVENT_LOG, logApi); event.put(EventConstant.EVENT_REQUEST, request); SpringUtil.publishEvent(new ApiLogEvent(event)); } } ~~~ * 日志事件代碼如下 ~~~ /** * 系統日志事件 * * @author Chill */ public class ApiLogEvent extends ApplicationEvent { public ApiLogEvent(Map<String, Object> source) { super(source); } } ~~~ * 日志監聽器代碼如下 ~~~ /** * 異步監聽日志事件 * * @author Chill */ @Slf4j @Component @AllArgsConstructor public class ApiLogListener { private final ILogClient logService; private final ServerInfo serverInfo; private final BladeProperties bladeProperties; @Async @Order @EventListener(ApiLogEvent.class) public void saveApiLog(ApiLogEvent event) { Map<String, Object> source = (Map<String, Object>) event.getSource(); LogApi logApi = (LogApi) source.get(EventConstant.EVENT_LOG); HttpServletRequest request = (HttpServletRequest) source.get(EventConstant.EVENT_REQUEST); logApi.setServiceId(bladeProperties.getName()); logApi.setServerHost(serverInfo.getHostName()); logApi.setServerIp(serverInfo.getIPWithPort()); logApi.setEnv(bladeProperties.getEnv()); logApi.setRemoteIp(WebUtil.getIP(request)); logApi.setUserAgent(request.getHeader(WebUtil.USER_AGENT_HEADER)); logApi.setRequestUri(UrlUtil.getPath(request.getRequestURI())); logApi.setMethod(request.getMethod()); logApi.setParams(WebUtil.getRequestParamString(request)); logApi.setCreateBy(SecureUtil.getUserAccount(request)); logApi.setCreateTime(LocalDateTime.now()); logService.saveApiLog(logApi); } } ~~~ * 由此可見,思路其實同異常日志一致,都是獲取請求相關信息后進行事件驅動解耦,異步將日志入庫,若要增加其性能與并發能力,可以采用集成消息隊列或者Redis,相信這些都難不倒大家 <br>
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看