日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

html打印日志_SpringBoot 2.X Kotlin系列之AOP统一打印日志

發布時間:2024/9/30 javascript 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 html打印日志_SpringBoot 2.X Kotlin系列之AOP统一打印日志 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在開發項目中,我們經常會需要打印日志,這樣方便開發人員了解接口調用情況及定位錯誤問題,很多時候對于Controller或者是Service的入參和出參需要打印日志,但是我們又不想重復的在每個方法里去使用logger打印,這個時候希望有一個管理者統一來打印,這時Spring AOP就派上用場了,利用切面的思想,我們在進入、出入Controller或Service時給它切一刀實現統一日志打印。

SpringAOP不僅可以實現在不產生新類的情況下打印日志,還可以管理事務、緩存等。具體可以了解官方文檔。https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#aop-api

基礎概念

在使用SpringAOP,這里還是先簡單講解一些基本的知識吧,如果說的不對請及時指正,這里主要是根據官方文檔來總結的。本章內容主要涉及的知識點。

Pointcut: 切入點,這里用于定義規則,進行方法的切入(形象的比喻就是一把刀)。

JoinPoint: 連接點,用于連接定義的切面。

Before: 在之前,在切入點方法執行之前。

AfterReturning: 在切入點方法結束并返回時執行。

這里除了SpringAOP相關的知識,還涉及到了線程相關的知識點,因為我們需要考慮多線程中它們各自需要保存自己的變量,所以就用到了ThreadLocal。

依賴引入

這里主要是用到aop和mongodb,在pom.xml文件中加入以下依賴即可:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency>

相關實體類

/*** 請求日志實體,用于保存請求日志*/ @Document class WebLog {var id: String = ""var request: String? = nullvar response: String? = nullvar time: Long? = nullvar requestUrl: String? = nullvar requestIp: String? = nullvar startTime: Long? = nullvar endTime: Long? = nullvar method: String? = nulloverride fun toString(): String {return ObjectMapper().writeValueAsString(this)} }/*** 業務對象,上一章講JPA中有定義*/ @Document class Student {@Idvar id :String? = nullvar name :String? = nullvar age :Int? = 0var gender :String? = nullvar sclass :String ?= nulloverride fun toString(): String {return ObjectMapper().writeValueAsString(this)} }

定義切面

定義切入點

/*** 定義一個切入,只要是為io.intodream..web下public修飾的方法都要切入*/ @Pointcut(value = "execution(public * io.intodream..web.*.*(..))") fun webLog() {}

定義切入點的表達式還可以使用within、如:

/*** 表示在io.intodream.web包下的方法都會被切入*/ @Pointcut(value = "within(io.intodream.web..*")

定義一個連接點

/*** 切面的連接點,并聲明在該連接點進入之前需要做的一些事情*/ @Before(value = "webLog()") @Throws(Throwable::class) fun doBefore(joinPoint: JoinPoint) {val webLog = WebLog()webLog.startTime = System.currentTimeMillis()val attributes = RequestContextHolder.getRequestAttributes() as ServletRequestAttributes?val request = attributes!!.requestval args = joinPoint.argsval paramNames = (joinPoint.signature as CodeSignature).parameterNamesval params = HashMap<String, Any>(args.size)for (i in args.indices) {if (args[i] !is BindingResult) {params[paramNames[i]] = args[i]}}webLog.id = UUID.randomUUID().toString()webLog.request = params.toString()webLog.requestUrl = request.requestURI.toString()webLog.requestIp = request.remoteAddrwebLog.method = request.methodwebRequestLog.set(webLog)logger.info("REQUEST={} {}; SOURCE IP={}; ARGS={}", request.method,request.requestURL.toString(), request.remoteAddr, params) }

方法結束后執行

@AfterReturning(returning = "ret", pointcut = "webLog()") @Throws(Throwable::class) fun doAfterReturning(ret: Any) {val webLog = webRequestLog.get()webLog.response = ret.toString()webLog.endTime = System.currentTimeMillis()webLog.time = webLog.endTime!! - webLog.startTime!!logger.info("RESPONSE={}; SPEND TIME={}MS", ObjectMapper().writeValueAsString(ret), webLog.time)logger.info("webLog:{}", webLog)webLogRepository.save(webLog)webRequestLog.remove() }

這里的主要思路是,在方法執行前,先記錄詳情的請求參數,請求方法,請求ip, 請求方式及進入時間,然后將對象放入到ThreadLocal中,在方法結束后并取到對應的返回對象且計算出請求耗時,然后將請求日志保存到mongodb中。

完成的代碼

package io.intodream.kotlin07.aspectimport com.fasterxml.jackson.databind.ObjectMapper import io.intodream.kotlin07.dao.WebLogRepository import io.intodream.kotlin07.entity.WebLog import org.aspectj.lang.JoinPoint import org.aspectj.lang.annotation.AfterReturning import org.aspectj.lang.annotation.Aspect import org.aspectj.lang.annotation.Before import org.aspectj.lang.annotation.Pointcut import org.aspectj.lang.reflect.CodeSignature import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Autowired import org.springframework.core.annotation.Order import org.springframework.stereotype.Component import org.springframework.validation.BindingResult import org.springframework.web.context.request.RequestContextHolder import org.springframework.web.context.request.ServletRequestAttributes import java.util.*/*** {描述}** @author yangxianxi@gogpay.cn* @date 2019/4/10 19:06**/ @Aspect @Order(5) @Component class WebLogAspect {private val logger:Logger = LoggerFactory.getLogger(WebLogAspect::class.java)private val webRequestLog: ThreadLocal<WebLog> = ThreadLocal()@Autowired lateinit var webLogRepository: WebLogRepository/*** 定義一個切入,只要是為io.intodream..web下public修飾的方法都要切入*/@Pointcut(value = "execution(public * io.intodream..web.*.*(..))")fun webLog() {}/*** 切面的連接點,并聲明在該連接點進入之前需要做的一些事情*/@Before(value = "webLog()")@Throws(Throwable::class)fun doBefore(joinPoint: JoinPoint) {val webLog = WebLog()webLog.startTime = System.currentTimeMillis()val attributes = RequestContextHolder.getRequestAttributes() as ServletRequestAttributes?val request = attributes!!.requestval args = joinPoint.argsval paramNames = (joinPoint.signature as CodeSignature).parameterNamesval params = HashMap<String, Any>(args.size)for (i in args.indices) {if (args[i] !is BindingResult) {params[paramNames[i]] = args[i]}}webLog.id = UUID.randomUUID().toString()webLog.request = params.toString()webLog.requestUrl = request.requestURI.toString()webLog.requestIp = request.remoteAddrwebLog.method = request.methodwebRequestLog.set(webLog)logger.info("REQUEST={} {}; SOURCE IP={}; ARGS={}", request.method,request.requestURL.toString(), request.remoteAddr, params)}@AfterReturning(returning = "ret", pointcut = "webLog()")@Throws(Throwable::class)fun doAfterReturning(ret: Any) {val webLog = webRequestLog.get()webLog.response = ret.toString()webLog.endTime = System.currentTimeMillis()webLog.time = webLog.endTime!! - webLog.startTime!!logger.info("RESPONSE={}; SPEND TIME={}MS", ObjectMapper().writeValueAsString(ret), webLog.time)logger.info("webLog:{}", webLog)webLogRepository.save(webLog)webRequestLog.remove()} }

這里定義的是Web層的切面,對于Service層我也可以定義一個切面,但是對于Service層的進入和返回的日志我們可以把級別稍等調低一點,這里改debug,具體實現如下:

package io.intodream.kotlin07.aspectimport com.fasterxml.jackson.databind.ObjectMapper import org.aspectj.lang.JoinPoint import org.aspectj.lang.annotation.AfterReturning import org.aspectj.lang.annotation.Aspect import org.aspectj.lang.annotation.Before import org.aspectj.lang.annotation.Pointcut import org.aspectj.lang.reflect.CodeSignature import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.core.annotation.Order import org.springframework.stereotype.Component import org.springframework.validation.BindingResult/*** service層所有public修飾的方法調用返回日志** @author yangxianxi@gogpay.cn* @date 2019/4/10 17:33**/ @Aspect @Order(2) @Component class ServiceLogAspect {private val logger: Logger = LoggerFactory.getLogger(ServiceLogAspect::class.java)/****/@Pointcut(value = "execution(public * io.intodream..service.*.*(..))")private fun serviceLog(){}@Before(value = "serviceLog()")fun deBefore(joinPoint: JoinPoint) {val args = joinPoint.argsval codeSignature = joinPoint.signature as CodeSignatureval paramNames = codeSignature.parameterNamesval params = HashMap<String, Any>(args.size).toMutableMap()for (i in args.indices) {if (args[i] !is BindingResult) {params[paramNames[i]] = args[i]}}logger.debug("CALL={}; ARGS={}", joinPoint.signature.name, params)}@AfterReturning(returning = "ret", pointcut = "serviceLog()")@Throws(Throwable::class)fun doAfterReturning(ret: Any) {logger.debug("RESPONSE={}", ObjectMapper().writeValueAsString(ret))} }

接口測試

這里就不在貼出Service層和web的代碼實現了,因為我是拷貝之前將JPA那一章的代碼,唯一不同的就是加入了切面,切面的加入并不影響原來的業務流程。

執行如下請求:

我們會在控制臺看到如下日志

2019-04-14 19:32:27.208 INFO 4914 --- [nio-9000-exec-1] i.i.kotlin07.aspect.WebLogAspect : REQUEST=POST http://localhost:9000/api/student/; SOURCE IP=0:0:0:0:0:0:0:1; ARGS={student={"id":"5","name":"Rose","age":17,"gender":"Girl","sclass":"Second class"}} 2019-04-14 19:32:27.415 INFO 4914 --- [nio-9000-exec-1] org.mongodb.driver.connection : Opened connection [connectionId{localValue:2, serverValue:4}] to localhost:27017 2019-04-14 19:32:27.431 INFO 4914 --- [nio-9000-exec-1] i.i.kotlin07.aspect.WebLogAspect : RESPONSE={"id":"5","name":"Rose","age":17,"gender":"Girl","sclass":"Second class"}; SPEND TIME=239MS 2019-04-14 19:32:27.431 INFO 4914 --- [nio-9000-exec-1] i.i.kotlin07.aspect.WebLogAspect : webLog:{"id":"e7b0ca1b-0a71-4fa0-9f5f-95a29d4d54a1","request":"{student={"id":"5","name":"Rose","age":17,"gender":"Girl","sclass":"Second class"}}","response":"{"id":"5","name":"Rose","age":17,"gender":"Girl","sclass":"Second class"}","time":239,"requestUrl":"/api/student/","requestIp":"0:0:0:0:0:0:0:1","startTime":1555241547191,"endTime":1555241547430,"method":"POST"}

查看數據庫會看到我們的請求日志已經寫入了:

這里有一個地方需要注意,在Service層的實現,具體如下:

return studentRepository.findById(id).get()

這里的findById會返回一個Optional<T>對象,如果沒有查到數據,我們使用get獲取數據會出現異常java.util.NoSuchElementException: No value present,可以改為返回對象可以為空只要在返回類型后面加一個?即可,同時調用Optional的ifPresent進行安全操作。

總結

以上是生活随笔為你收集整理的html打印日志_SpringBoot 2.X Kotlin系列之AOP统一打印日志的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。