重构使用aop的方式记录异常日志-引入Spring AOP

This commit is contained in:
2021-03-22 21:36:14 +08:00
parent 0b5d9243e8
commit af9a4a86a1
11 changed files with 144 additions and 73 deletions

View File

@@ -20,6 +20,18 @@ interface ICommandApp<TCommand : ICommand, TKey> : IApplication {
var service: IService<TKey>? var service: IService<TKey>?
@DeleteMapping("/{id:.+}")
fun remove(@PathVariable id: TKey): HttpMessage {
return this.safeExecute("删除${this.name}[id: $id]失败") {
if (this.service != null)
it.data = this.service!!.remove(id)
else {
it.status = Status.Error
it.message = "没有对应服务,无法执行该操作"
}
}
}
@PostMapping @PostMapping
fun add(@RequestBody command: TCommand): HttpMessage { fun add(@RequestBody command: TCommand): HttpMessage {
return this.safeExecute("添加${this.name}数据失败 - ${if (jsonSerializer != null) jsonSerializer?.serialize(command) else ""}") { return this.safeExecute("添加${this.name}数据失败 - ${if (jsonSerializer != null) jsonSerializer?.serialize(command) else ""}") {
@@ -33,18 +45,6 @@ interface ICommandApp<TCommand : ICommand, TKey> : IApplication {
} }
} }
@DeleteMapping("/{id:.+}")
fun remove(@PathVariable id: TKey): HttpMessage {
return this.safeExecute("删除${this.name}[id: $id]失败") {
if (this.service != null)
it.data = this.service!!.remove(id)
else {
it.status = Status.Error
it.message = "没有对应服务,无法执行该操作"
}
}
}
@PutMapping("/{id:.+}") @PutMapping("/{id:.+}")
fun update(@PathVariable id: TKey, @RequestBody command: TCommand): HttpMessage { fun update(@PathVariable id: TKey, @RequestBody command: TCommand): HttpMessage {
return this.safeExecute("更新${this.name}失败 - ${if (jsonSerializer != null) jsonSerializer?.serialize(command) else ""}") { return this.safeExecute("更新${this.name}失败 - ${if (jsonSerializer != null) jsonSerializer?.serialize(command) else ""}") {

View File

@@ -11,24 +11,21 @@ import org.springframework.stereotype.Component
import java.io.File import java.io.File
@Component @Component
class EmailMessenger : IEmailMessenger { class EmailMessenger(var mailSender: JavaMailSender?) : IEmailMessenger {
@Autowired @Value("\${spring.mail.host:}")
private lateinit var mailSender: JavaMailSender
@Value("\${spring.mail.host}")
var host = "" var host = ""
@Value("\${spring.mail.port}") @Value("\${spring.mail.port:}")
var port = "" var port = ""
@Value("\${spring.mail.sender}") @Value("\${spring.mail.sender:}")
var sender = "" var sender = ""
@Value("\${spring.mail.username}") @Value("\${spring.mail.username:}")
var username = "" var username = ""
@Value("\${spring.mail.password}") @Value("\${spring.mail.password:}")
var password = "" var password = ""
@Autowired @Autowired
@@ -42,13 +39,20 @@ class EmailMessenger : IEmailMessenger {
* @param receivers 邮件接受者 * @param receivers 邮件接受者
* @param files 附件 * @param files 附件
*/ */
override fun sendMessage(subject: String, content: String, receivers: List<String>, files: Map<String, String>): Map<String, Boolean> { override fun sendMessage(
subject: String,
content: String,
receivers: List<String>,
files: Map<String, String>
): Map<String, Boolean> {
val result = mutableMapOf<String, Boolean>() val result = mutableMapOf<String, Boolean>()
this.check() this.check()
receivers.forEach { receiver -> receivers.forEach { receiver ->
result[receiver] = true result[receiver] = true
if (this.mailSender == null)
throw Exception("没有配置JavaMailSender的实现实例请重新配置")
try { try {
val mail = mailSender.createMimeMessage() val mail = this.mailSender!!.createMimeMessage()
val mimeMessageHelper = MimeMessageHelper(mail, true, "utf-8") val mimeMessageHelper = MimeMessageHelper(mail, true, "utf-8")
mimeMessageHelper.setFrom(sender) mimeMessageHelper.setFrom(sender)
mimeMessageHelper.setTo(receiver) mimeMessageHelper.setTo(receiver)
@@ -59,7 +63,7 @@ class EmailMessenger : IEmailMessenger {
val file = FileSystemResource(File(path)) val file = FileSystemResource(File(path))
mimeMessageHelper.addAttachment(name, file) mimeMessageHelper.addAttachment(name, file)
} }
mailSender.send(mail) //发送 this.mailSender!!.send(mail) //发送
} catch (e: Exception) { } catch (e: Exception) {
logger.error(e, "发送邮件[$subject]至地址[$receiver]失败") logger.error(e, "发送邮件[$subject]至地址[$receiver]失败")
result[receiver] = false result[receiver] = false

View File

@@ -1,7 +0,0 @@
package com.synebula.gaea.app.component.aop
import java.lang.Exception
interface AnnotationHandler {
fun handle(clazz: Class<Any>, func: String, args: Array<Any>, exception: Exception?)
}

View File

@@ -1,68 +1,86 @@
package com.synebula.gaea.app.component.aop package com.synebula.gaea.app.component.aop
import com.synebula.gaea.app.component.HttpMessage import com.synebula.gaea.app.component.HttpMessage
import com.synebula.gaea.app.component.aop.annotation.SafeExec import com.synebula.gaea.app.component.aop.annotation.ExceptionMessage
import com.synebula.gaea.app.component.aop.annotation.Handler
import com.synebula.gaea.app.component.aop.annotation.ModuleName
import com.synebula.gaea.data.message.Status import com.synebula.gaea.data.message.Status
import com.synebula.gaea.log.ILogger
import org.aspectj.lang.JoinPoint import org.aspectj.lang.JoinPoint
import org.aspectj.lang.ProceedingJoinPoint import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.* import org.aspectj.lang.annotation.*
import org.springframework.stereotype.Component import org.springframework.stereotype.Component
import org.springframework.web.context.request.RequestContextHolder import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.context.request.ServletRequestAttributes import org.springframework.context.ApplicationContext
import java.util.* import com.fasterxml.jackson.databind.ObjectMapper
@Aspect @Aspect
@Component @Component
class AppAspect { class AppAspect {
private val mapper = ObjectMapper()
@Autowired
lateinit var logger: ILogger
@Autowired
lateinit var applicationContext: ApplicationContext
@Pointcut("within(com.synebula..app.*)") @Pointcut("within(com.synebula..app.*)")
fun log() { fun log() {
} }
//后置异常通知 /**
@AfterThrowing("log()") * 后置异常通知
fun throws(point: JoinPoint): Any { */
@AfterThrowing("log()", throwing = "ex")
fun throws(point: JoinPoint, ex: Throwable) {
val clazz = point.signature.declaringType val clazz = point.signature.declaringType
println("${clazz.name} - ${point.signature.name}异常:${point.signature.name}") logger.error(
return "error" ex,
"${clazz.name}.${point.signature.name} exception${ex.message} args${mapper.writeValueAsString(point.args)}"
)
} }
//后置最终通知,final增强不管是抛出异常或者正常退出都会执行 /**
@After("log()") * 环绕通知,环绕增强相当于MethodInterceptor
fun after(point: JoinPoint) { */
println("方法最后执行.....")
}
//环绕通知,环绕增强相当于MethodInterceptor
@Around("log()") @Around("log()")
fun around(point: ProceedingJoinPoint): Any? { fun around(point: ProceedingJoinPoint): Any? {
val clazz = point.signature.declaringType val clazz = point.signature.declaringType
val func = clazz.methods.find { val func = clazz.methods.find {
it.name == point.signature.name it.name == point.signature.name
}!! }!!
val clazzAnnotations = clazz.annotations
val funcAnnotations = func.annotations ?: arrayOf() val funcAnnotations = func.annotations ?: arrayOf()
val attributes = RequestContextHolder.getRequestAttributes() as ServletRequestAttributes var exceptionMessage = func.name
val request = attributes.request //遍历方法注解
// 记录下请求内容 for (funcAnnotation in funcAnnotations) {
println("URL : " + request.requestURL.toString()) val annotations = funcAnnotation.annotationClass.annotations
println("HTTP_METHOD : " + request.method)
println("IP : " + request.remoteAddr) //尝试寻找方法注解的处理类
println("CLASS_METHOD : " + clazz.name + "." + point.signature.name) val handler = annotations.find { it is Handler }
println("ARGS : " + Arrays.toString(point.args)) if (handler != null && handler is Handler) {
val handleClazz = applicationContext.getBean(handler.value.java)
handleClazz.handle(clazz, func, point.args)
}
if (funcAnnotation is ExceptionMessage)
exceptionMessage = funcAnnotation.message
}
return try { return try {
val res = point.proceed() val res = point.proceed()
res res
} catch (ex: Throwable) { } catch (ex: Throwable) {
val msg = HttpMessage(Status.Success) //找到类的模块名称,否则使用类名
for (item in funcAnnotations) { var moduleName = clazz.name
if (item.annotationClass == SafeExec::javaClass) { val name = clazz.annotations.find { it is ModuleName }
if (name != null && name is ModuleName) {
moduleName = name.value
} }
} val message = "$moduleName - $exceptionMessage"
println("方法${point.signature}异常: $msg - ${ex.message}") logger.error(ex, "$message, args: ${mapper.writeValueAsString(point.args)}")
return "error" return HttpMessage(Status.Error, message)
} }
} }
} }

View File

@@ -0,0 +1,8 @@
package com.synebula.gaea.app.component.aop.annotation
import com.synebula.gaea.app.component.aop.handler.AccessLogHandler
@Target(AnnotationTarget.FUNCTION)
@Handler(AccessLogHandler::class)
@Retention(AnnotationRetention.RUNTIME)
annotation class AccessLog

View File

@@ -3,8 +3,8 @@ package com.synebula.gaea.app.component.aop.annotation
/** /**
* 标记方法安全执行由AOP负责try catch异常 * 标记方法安全执行由AOP负责try catch异常
* *
* @param errorMessage 异常消息 * @param message 异常消息
*/ */
@Target(AnnotationTarget.FUNCTION) @Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
annotation class SafeExec(val errorMessage: String) annotation class ExceptionMessage(val message: String)

View File

@@ -1,5 +1,8 @@
package com.synebula.gaea.app.component.aop.annotation package com.synebula.gaea.app.component.aop.annotation
import com.synebula.gaea.app.component.aop.handler.AnnotationHandler
import kotlin.reflect.KClass import kotlin.reflect.KClass
annotation class Handler(val value: KClass<Any>) @Target(AnnotationTarget.ANNOTATION_CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class Handler(val value: KClass<out AnnotationHandler>)

View File

@@ -0,0 +1,8 @@
package com.synebula.gaea.app.component.aop.annotation
/**
* 模块的业务名称
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class ModuleName(val value: String)

View File

@@ -0,0 +1,29 @@
package com.synebula.gaea.app.component.aop.handler
import com.fasterxml.jackson.databind.ObjectMapper
import com.synebula.gaea.log.ILogger
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import org.springframework.web.context.request.RequestContextHolder
import org.springframework.web.context.request.ServletRequestAttributes
import java.lang.reflect.Method
@Component
class AccessLogHandler : AnnotationHandler {
private val mapper = ObjectMapper()
@Autowired
lateinit var logger: ILogger
override fun handle(clazz: Class<Any>, func: Method, args: Array<Any>, exception: Exception?) {
val attributes = RequestContextHolder.getRequestAttributes() as ServletRequestAttributes
val request = attributes.request
logger.info(
"${request.method} ${request.requestURL} from ${request.remoteAddr}, call function ${clazz.name}.${func.name}, args: ${
mapper.writeValueAsString(
args
)
}"
)
}
}

View File

@@ -0,0 +1,8 @@
package com.synebula.gaea.app.component.aop.handler
import java.lang.Exception
import java.lang.reflect.Method
interface AnnotationHandler {
fun handle(clazz: Class<Any>, func: Method, args: Array<Any>, exception: Exception? = null)
}

View File

@@ -11,8 +11,8 @@ import com.synebula.gaea.query.type.Order
*/ */
data class Params(var page: Int = 1, var size: Int = 10) { data class Params(var page: Int = 1, var size: Int = 10) {
private var _parameters = mutableMapOf<String, Any>() private var _parameters = linkedMapOf<String, Any>()
private var _orders = mutableMapOf<String, Order>() private var _orders = linkedMapOf<String, Order>()
/** /**
* 数据索引从0开始。表示数据在总量的第几条。index = (page - 1) * size * 数据索引从0开始。表示数据在总量的第几条。index = (page - 1) * size
@@ -24,13 +24,13 @@ data class Params(var page: Int = 1, var size: Int = 10) {
/** /**
* 排序条件。 * 排序条件。
*/ */
var orders: Map<String, Order> var orders: LinkedHashMap<String, Order>
set(value) { set(value) {
this._orders = value.toMutableMap() this._orders = value
} }
get() { get() {
if (this._parameters.keys.count { it.startsWith("@") } > 0) { if (this._parameters.keys.count { it.startsWith("@") } > 0) {
val params = mutableMapOf<String, Any>() val params = linkedMapOf<String, Any>()
this._parameters.forEach { this._parameters.forEach {
if (it.key.startsWith("@")) { if (it.key.startsWith("@")) {
this._orders[it.key.removePrefix("@")] = Order.valueOf(it.value.toString()) this._orders[it.key.removePrefix("@")] = Order.valueOf(it.value.toString())
@@ -45,13 +45,13 @@ data class Params(var page: Int = 1, var size: Int = 10) {
/** /**
* 查询条件。 * 查询条件。
*/ */
var parameters: Map<String, Any> var parameters: LinkedHashMap<String, Any>
set(value) { set(value) {
this._parameters = value.toMutableMap() this._parameters = value
} }
get() { get() {
if (this._parameters.keys.count { it.startsWith("@") } > 0) { if (this._parameters.keys.count { it.startsWith("@") } > 0) {
val params = mutableMapOf<String, Any>() val params = linkedMapOf<String, Any>()
this._parameters.forEach { this._parameters.forEach {
if (it.key.startsWith("@")) { if (it.key.startsWith("@")) {
this._orders[it.key.removePrefix("@")] = Order.valueOf(it.value.toString()) this._orders[it.key.removePrefix("@")] = Order.valueOf(it.value.toString())