生活随笔
收集整理的這篇文章主要介紹了
接口服务中的日志
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
?
先來看下日志對于一個接口服務(wù)的作用:
?監(jiān)控服務(wù)的狀態(tài),一般程序中會增加一些跟蹤或者提示性的日志,用來判斷服務(wù)執(zhí)行的詳細(xì)情況,特別是執(zhí)行一些復(fù)合功能的接口或者叫聚合接口非常有利于我們判斷接口的執(zhí)行情況 ?安全,用來分析調(diào)用者的身份信息,防止接口被非法惡意調(diào)用等 ?性能,可以統(tǒng)計每個接口的訪問執(zhí)行時間來分析系統(tǒng)的瓶頸 ?異常分析,對于線上的異常,在沒有debug的環(huán)境下,要想分析問題原因最有價值的就要算異常的堆棧信息 上面的這幾點需求,如果接口服務(wù)使用的是一些高級產(chǎn)品比如dubbo,其實它已經(jīng)實現(xiàn)了大部分功能,不需要人為的去全部處理功能。
? 安全,可利用訪問日志的功能來實現(xiàn),訪問日志中詳細(xì)的記錄了客戶端調(diào)用的時間,身份,調(diào)用時的詳細(xì)參數(shù)。 ? [DUBBO] [2016-08-27 14:47:06] 10.10.50.20:64948 -> 10.10.50.20:20960-
ProductFacadeService getDataDictionaryByType(DataDictionaryParamInfo)[{ "code":0,"type":1}], dubbo version: 2.8.1, current host: 10.10.50.20
??????? 上面的日志我們可以看到如下有用的信息:
???????? 1:調(diào)用方IP以及端口信息:10.10.50.20:64948
???????? 2:服務(wù)端的IP以及端口信息:10.10.50.20:20960
???????? 3:調(diào)用時間:2016-08-27 14:47:06
???????? 4:調(diào)用的接口方法:ProductFacadeService getDataDictionaryByType(DataDictionaryParamInfo) ???????? 5:調(diào)用的接口方法參數(shù):[{"code":0,"type":1}]
? 異常分析,會自動將產(chǎn)生的未捕獲異常信息記錄到日志中,不需要手工的去處理異常的記錄。上面訪問日志中的參數(shù)記錄信息也對分析線上問題提供了非常有力的幫助。 其實dubbo自己提供的日志功能已經(jīng)非常強(qiáng)了,大多數(shù)情況下已經(jīng)夠用,但如果想有一些更加強(qiáng)的功能就需要自己想想辦法了:
? 監(jiān)控服務(wù)的狀態(tài),這類跟蹤信息只能依靠程序邏輯來實現(xiàn),沒有其它捷徑 ? 性能,dubbo的訪問日志只記錄了接口調(diào)用的開始時間,沒有結(jié)束時間,要想統(tǒng)計某個方法或者某個請求的整體執(zhí)行時間是無能為力的 如果你的服務(wù)接口不是dubbo,比如是基于spring mvc 實現(xiàn)的rest api接口,那么就沒有上面dubbo幫你做的那些好用的日志功能了。但是spring mvc實現(xiàn)上面的日志需求方法也是很多的,典型的例子可以借助HandlerInterceptor這類攔截器,只需要實現(xiàn)這個接口,然后在實現(xiàn)類中增加日志功能即可:我們可以通過handler這個參數(shù)來獲取你想要的所有數(shù)據(jù),另外還有request以及response兩個信息豐富的對象供你使用。
public interface HandlerInterceptor { boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception; void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception;} ?
還有一種是自己實現(xiàn)一個AOP功能去解決我們的需求問題。AOP不是這篇的重點,我貼一張圖用來簡單展示AOP的作用(上面提到的HandlerInterceptor 也屬于AOP的具體應(yīng)用)。
將某些非業(yè)務(wù)的功能通過攔截的形式注入到流程中,而非緊密耦合在業(yè)務(wù)邏輯中,比如下面的記錄方法參數(shù)的代碼就是緊耦合,不推薦:
public ValueResult<ApplyIsvRoleInfo>
queryUserApplyIsvInfo(String appKey, String accessToken) {log.info( "receive queryUserApplyIsvInfo! and the params appKey: [" + appKey + "], accessToken:[" + accessToken + "]"
);...... do something
} AOP我這里使用aspectj,當(dāng)然也可以使用spring自帶的。
? 創(chuàng)建一個用于日志的注解,用來控制記錄的具體功能。比如有些方法是不推薦記錄返回記錄的,像列表查詢,也有利于精確的配置日志生效的范圍 @Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @
interface MethodLog { /** * 是否啟動日志功能* @return */ boolean enable()
default true ; /** * 是否記錄返回值* 列表數(shù)據(jù)不推薦記錄* @return */ boolean isLogResult()
default false ; /** * 是否記錄方法的參數(shù)名稱以及值* @return */ boolean isLogParameter()
default true ; /** * 是否記錄執(zhí)行時間* @return */ boolean isLogElapsed()
default true ;
} 創(chuàng)建一個服務(wù)端的日志攔截器,由于我們需要記錄方法的執(zhí)行時間,那么最適合的就是寫一個around的方法,在方法執(zhí)行前后記錄時間從而計算出時間差。對于參數(shù),我們也很容易實現(xiàn),根據(jù)反射我們可以輕松得到調(diào)用方法的所有參數(shù)名稱以及參數(shù)的具體值,下面是實現(xiàn)這個攔截器用的工具類介紹: ???????? a:計算時間,這里采用google提供的Stopwatch,這個東西對于從.NET轉(zhuǎn)JAVA的來講太親切了
Stopwatch sw=
Stopwatch.createStarted();Object result =
joinPoint.proceed();sw.stop(); long elapsed=sw.elapsed(TimeUnit.NANOSECONDS);
???? ??????? b:記錄參數(shù)內(nèi)容,對于object對象,我們將其序列化成json,可以利用jackson完成,其它的復(fù)雜點的就是通過反射來完成參數(shù)以及值。記錄日志根據(jù)注解的配置來判斷應(yīng)該記錄哪些日志項。
@Aspect
public class ServicesLoggerInterceptor {@Pointcut( "execution(* com.chanjet.csp.product.service.service.impl.*ServiceImpl*.*(..))"
) public void pointCut() {}@Around(value = "pointCut()"
) public Object around(ProceedingJoinPoint joinPoint)
throws Throwable {Stopwatch sw =
Stopwatch.createStarted();Object result =
joinPoint.proceed();sw.stop(); long elapsed=
sw.elapsed(TimeUnit.NANOSECONDS);MethodSignature methodSignature =
(MethodSignature) joinPoint.getSignature();Method targetMethod =
methodSignature.getMethod();MethodLog methodLog = targetMethod.getAnnotation(MethodLog.
class ); if (
null !=methodLog&&
methodLog.enable()){StringBuilder logLine =
new StringBuilder(joinPoint.getSignature().getName()); if (methodLog.isLogParameter()) {Object[] paramValues =
joinPoint.getArgs();CodeSignature codeSignature =
((CodeSignature) joinPoint.getSignature());String[] paramNames =
codeSignature.getParameterNames();logLine.append( "("
); if (paramNames.length != 0
) {AspectLogUtils.logParamValues(logLine, paramNames, paramValues);}logLine.append( ") - started"
);} if (methodLog.isLogResult()) {logLine.append( " Return Value : "
);logLine.append(AspectLogUtils.toString(result));} if (methodLog.isLogElapsed()) {logLine.append( " elapsed nanoseconds:" +
elapsed);}AspectLogUtils.getLogger().info(logLine.toString());} return result;}
} ?????? c:創(chuàng)建一個日志的工具類,用來實現(xiàn)具體的日志記錄。
public final class AspectLogUtils { private static Logger logger = LoggerFactory.getLogger(AspectLogUtils.
class ); private AspectLogUtils() {} public static Logger getLogger() { return logger;} public static void logParamValues(StringBuilder logLine, String[] paramNames, Object[] paramValues) { for (
int i = 0; i < paramValues.length; i++
) {logLine.append(paramNames[i]).append( "="
).append(toString(paramValues[i])); if (i < paramValues.length - 1
) {logLine.append( ", "
);}}} public static String toString(Object object) { return JsonUtils.toJsonString(object);}
} < bean class ="ServicesLoggerInterceptor" ></ bean > < aop:aspectj-autoproxy proxy-target-class ="true" />
INFO getDataDictionaryByType(dataDictionaryParamInfo={"type":1,"code":0}) - started elapsed nanoseconds:11491670
?
轉(zhuǎn)載于:https://www.cnblogs.com/ASPNET2008/p/5813518.html
總結(jié)
以上是生活随笔 為你收集整理的接口服务中的日志 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔 推薦給好友。