In the previous chapter, Managing logs with LogBack, we covered in detail how to store log generation files. However, in the actual development, it is not the most convenient to use the file to store logs for quick query. In addition to log files, an excellent system also needs to persist operation logs to monitor the operation records of the platform. Today we are going to learn how to log using APO.
To make logging more flexible, we will use custom annotations for logging important operations.
Log record table
The log table contains the following fields: service module, operation type, interface address, processing status, error information, and operation time. The database design is as follows:
CREATE TABLE `sys_oper_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'Log primary key',
`title` varchar(50) CHARACTER SET utf8 DEFAULT ' ' COMMENT 'Module title',
`business_type` int(2) DEFAULT '0' COMMENT 'Business Type (0 others 1 New 2 Modified 3 Deleted)',
`method` varchar(255) CHARACTER SET utf8 DEFAULT ' ' COMMENT 'Method name',
`status` int(1) DEFAULT '0' COMMENT 'Operating status (0 normal 1 Abnormal)',
`error_msg` varchar(2000) CHARACTER SET utf8 DEFAULT ' ' COMMENT 'Error message',
`oper_time` datetime DEFAULT NULL COMMENT 'Operation time'.PRIMARY KEY (`id`)
) ENGINE=InnoDB CHARSET=utf8mb4 CHECKSUM=1 COMMENT='Operation Logging'
Copy the code
The corresponding entity class is as follows:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SysOperLog implements Serializable {
private static final long serialVersionUID = 1L;
/** Log primary key */
private Long id;
/** Operation module */
private String title;
/** Service type (0 other 1 Added 2 Modified 3 Deleted) */
private Integer businessType;
/** Request method */
private String method;
/** Error message */
private String errorMsg;
private Integer status;
/** Operation time */
private Date operTime;
}
Copy the code
Custom annotation and processing
The custom annotation contains two attributes, business module title and operation type businessType.
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
/** * module */
String title(a) default "";
/** * function */
BusinessType businessType(a) default BusinessType.OTHER;
}
Copy the code
Use AOP to handle custom annotations
@Aspect
@Component
@Slf4j
public class LogAspect {
@Autowired
private AsyncLogService asyncLogService;
// Configure the weave point
@Pointcut("@annotation(com.javatrip.aop.annotation.Log)")
public void logPointCut(a) {}
/** * execute ** after the request is processed@paramJoinPoint tangent point * /
@AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) {
handleLog(joinPoint, null, jsonResult);
}
/** * Intercepts the exception operation **@paramJoinPoint tangent point *@paramE * /
@AfterThrowing(value = "logPointCut()", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
handleLog(joinPoint, e, null);
}
protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult) {
try {
// Get comments
Log controllerLog = getAnnotationLog(joinPoint);
if (controllerLog == null) {
return;
}
SysOperLog operLog = new SysOperLog();
operLog.setStatus(0);
if(e ! =null) {
operLog.setStatus(1);
operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0.2000));
}
// Set method name
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
operLog.setMethod(className + "." + methodName + "()");
// Handle setting the parameters on the annotations
getControllerMethodDescription(joinPoint, controllerLog, operLog);
// Save the database
asyncLogService.saveSysLog(operLog);
} catch (Exception exp) {
log.error("== Pre-notification exception ==");
log.error("Log Exception {}", exp); }}/** * Get the method description in the annotation for the Controller layer annotation **@paramThe log log *@paramOperLog Operation log *@throws Exception
*/
public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog) {
// Set the action
operLog.setBusinessType(log.businessType().ordinal());
// Set the title
operLog.setTitle(log.title());
}
/** * if there are annotations, if so, get */
private Log getAnnotationLog(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if(method ! =null) {
return method.getAnnotation(Log.class);
}
return null; }}Copy the code
Enumeration classes for operation types:
public enum BusinessType {
/** * other */
OTHER,
/** * add */
INSERT,
/** * modify */
UPDATE,
/** * delete */
DELETE,
}
Copy the code
The operation logs are saved asynchronously. For convenience, I use the jdbcTemplate directly to save the operation in the service.
@Service
public class AsyncLogService {
@Autowired
private JdbcTemplate jdbcTemplate;
/** * Save system logs */
@Async
public void saveSysLog(SysOperLog log) {
String sql = "INSERT INTO sys_oper_log(title,business_type,method,STATUS,error_msg,oper_time) VALUES(? ,? ,? ,? ,? ,?) ";
jdbcTemplate.update(sql,new Object[]{log.getTitle(),log.getBusinessType(),log.getMethod(),log.getStatus(),log.getErrorMsg(),newDate()}); }}Copy the code
Write interface tests
Test the effect by writing custom annotations on business methods
@RestController
@RequestMapping("person")
public class PersonController {
@GetMapping("/{name}")
@Log(title = "system",businessType = BusinessType.OTHER)
public Person getPerson(@PathVariable("name") String name, @RequestParam int age){
return new Person(name,age);
}
@PostMapping("add")
@Log(title = "system",businessType = BusinessType.INSERT)
public int addPerson(@RequestBody Person person){
if(StringUtils.isEmpty(person)){
return -1;
}
return 1;
}
@PutMapping("update")
@Log(title = "system",businessType = BusinessType.UPDATE)
public int updatePerson(@RequestBody Person person){
if(StringUtils.isEmpty(person)){
return -1;
}
return 1;
}
@DeleteMapping("/{name}")
@Log(title = "system",businessType = BusinessType.DELETE)
public int deletePerson(@PathVariable(name = "name") String name){
if(StringUtils.isEmpty(name)){
return -1;
}
return 1; }}Copy the code
Of course, you can also store the request parameters and response results in the database, so that you can see the operation record of the specific interface.
The sample code for this article has been uploaded togithub, point astar
Support!
Spring Boot series tutorial directory
Spring-boot-route (I) Several ways for Controller to receive parameters
Spring-boot-route (2) Several methods of reading configuration files
Spring-boot-route (3) Upload multiple files
Spring-boot-route (4) Global exception processing
Spring-boot-route (5) Integrate Swagger to generate interface documents
Spring-boot-route (6) Integrate JApiDocs to generate interface documents
Spring-boot-route (7) Integrate jdbcTemplate operation database
Spring-boot-route (8) Integrating mybatis operation database
Spring-boot-route (9) Integrate JPA operation database
Spring-boot-route (10) Switching between multiple data sources
Spring-boot-route (11) Encrypting database configuration information
Spring-boot-route (12) Integrate REDis as cache
Spring-boot-route RabbitMQ
Spring-boot-route Kafka
Spring-boot-route (15) Integrate RocketMQ
Spring-boot-route (16) Use logback to produce log files
Spring-boot-route (17) Use AOP to log operations
Spring-boot-route (18) Spring-boot-adtuator monitoring applications
Spring-boot-route (19) Spring-boot-admin Monitoring service
Spring-boot-route (20) Spring Task Implements simple scheduled tasks
Spring-boot-route (21) Quartz Implements dynamic scheduled tasks
Spring-boot-route (22) Enables email sending
Spring-boot-route (23) Developed wechat official accounts
Spring-boot-route (24) Distributed session consistency processing
Spring-boot-route (25) two lines of code to achieve internationalization
Spring-boot-route (26) Integrate webSocket
This series of articles are frequently used in the work of knowledge, after learning this series, to cope with daily development more than enough. If you want to know more, just scan the qr code below and let me know. I will further improve this series of articles!