Commit f28f78a7a1488dca023b4996ca678423e1ad6469

Authored by 云中非
1 parent f7cfc05c

feat: 日志功能

1、操作日志功能
2、异常日志功能
1 1 package org.thingsboard.server.config.yunteng;
2 2
  3 +import com.datastax.oss.driver.api.core.uuid.Uuids;
3 4 import lombok.RequiredArgsConstructor;
  5 +import lombok.extern.slf4j.Slf4j;
  6 +import org.apache.http.HttpHeaders;
4 7 import org.springframework.http.HttpStatus;
5 8 import org.springframework.security.access.AccessDeniedException;
6   -import org.springframework.security.authentication.AccountExpiredException;
  9 +import org.springframework.security.core.Authentication;
  10 +import org.springframework.security.core.context.SecurityContextHolder;
7 11 import org.springframework.web.bind.MethodArgumentNotValidException;
8 12 import org.springframework.web.bind.annotation.ControllerAdvice;
9 13 import org.springframework.web.bind.annotation.ExceptionHandler;
  14 +import org.thingsboard.server.common.data.EntityType;
  15 +import org.thingsboard.server.common.data.asset.Asset;
  16 +import org.thingsboard.server.common.data.audit.ActionType;
  17 +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
  18 +import org.thingsboard.server.common.data.exception.ThingsboardException;
  19 +import org.thingsboard.server.common.data.id.EntityId;
10 20 import org.thingsboard.server.common.data.yunteng.core.exception.*;
11 21 import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage;
  22 +import org.thingsboard.server.common.data.yunteng.dto.SysLogOperateDTO;
  23 +import org.thingsboard.server.dao.audit.AuditLogService;
12 24 import org.thingsboard.server.exception.yunteng.YunTengErrorResponseHandler;
  25 +import org.thingsboard.server.service.security.model.SecurityUser;
  26 +import org.thingsboard.server.utils.yunteng.LogUtils;
13 27
14 28 import javax.servlet.http.HttpServletRequest;
15 29 import javax.servlet.http.HttpServletResponse;
16 30 import java.util.Objects;
  31 +import java.util.UUID;
17 32
18 33 @ControllerAdvice(basePackages = "org.thingsboard.server.controller.yunteng")
19 34 @RequiredArgsConstructor
  35 +@Slf4j
20 36 public class ControllerExceptionHandler {
21 37
  38 + private final AuditLogService auditLogService;
22 39 private final YunTengErrorResponseHandler errorResponseHandler;
23 40
24 41 @ExceptionHandler(YunTengException.class)
25   - public void handleYunTengException(YunTengException ex, HttpServletResponse response) {
  42 + public void handleYunTengException(YunTengException ex,HttpServletRequest request, HttpServletResponse response) {
  43 + produceLog( request, ex);
26 44 errorResponseHandler.handle(ex, response);
27 45 }
28 46
29 47 @ExceptionHandler(MethodArgumentNotValidException.class)
30 48 public void handleMethodArgumentNotValidException(
31   - MethodArgumentNotValidException ex, HttpServletResponse response) {
  49 + MethodArgumentNotValidException ex,HttpServletRequest request, HttpServletResponse response) {
  50 + produceLog( request, ex);
32 51 errorResponseHandler.handle(
33 52 new YunTengException(
34 53 ErrorMessage.INVALID_PARAMETER.setMessage(
... ... @@ -40,6 +59,7 @@ public class ControllerExceptionHandler {
40 59 @ExceptionHandler(YtDataValidationException.class)
41 60 public void handleDataValidationException(
42 61 YtDataValidationException ex, HttpServletRequest request, HttpServletResponse response) {
  62 + produceLog( request, ex);
43 63 YunTengException YunTengException =
44 64 new YunTengException(
45 65 ErrorMessage.BAD_PARAMETER.setMessage(ex.getMessage()), HttpStatus.BAD_REQUEST);
... ... @@ -54,7 +74,8 @@ public class ControllerExceptionHandler {
54 74 }
55 75
56 76 @ExceptionHandler(AccessDeniedException.class)
57   - public void handleAccessDeniedException(AccessDeniedException ex, HttpServletResponse response) {
  77 + public void handleAccessDeniedException(AccessDeniedException ex,HttpServletRequest request, HttpServletResponse response) {
  78 + produceLog( request, ex);
58 79 errorResponseHandler.handle(
59 80 new YunTengException(
60 81 ErrorMessage.ACCESS_DENIED.setMessage(ex.getMessage()), HttpStatus.FORBIDDEN),
... ... @@ -63,7 +84,8 @@ public class ControllerExceptionHandler {
63 84
64 85 @ExceptionHandler(NoneTenantAssetException.class)
65 86 public void handleNoneTenantAssetException(
66   - NoneTenantAssetException ex, HttpServletResponse response) {
  87 + NoneTenantAssetException ex, HttpServletRequest request, HttpServletResponse response) {
  88 + produceLog( request, ex);
67 89 errorResponseHandler.handle(
68 90 new YunTengException(
69 91 ErrorMessage.NONE_TENANT_ASSET.setMessage(ex.getMessage()), HttpStatus.NOT_FOUND),
... ... @@ -77,4 +99,58 @@ public class ControllerExceptionHandler {
77 99 ErrorMessage.SEND_DESTINATION_NOT_FOUND, HttpStatus.PRECONDITION_FAILED),
78 100 response);
79 101 }
  102 +
  103 +
  104 +
  105 + protected SecurityUser getCurrentUser() throws ThingsboardException {
  106 + Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
  107 + if (authentication != null && authentication.getPrincipal() instanceof SecurityUser) {
  108 + return (SecurityUser) authentication.getPrincipal();
  109 + } else {
  110 + throw new ThingsboardException("You aren't authorized to perform this operation!", ThingsboardErrorCode.AUTHENTICATION);
  111 + }
  112 + }
  113 +
  114 + /**
  115 + * 生产日志并缓存到队列中
  116 + *
  117 + * @param request 响应头
  118 + * @param e 异常信息
  119 + */
  120 + void produceLog(HttpServletRequest request, Exception e) {
  121 +
  122 + try {
  123 + SecurityUser currentUser = getCurrentUser();
  124 +
  125 + Asset entity = new Asset();
  126 + entity.setName(e.getMessage());
  127 +
  128 + //请求相关信息
  129 + SysLogOperateDTO additionalInfo = new SysLogOperateDTO();
  130 + additionalInfo.setApi(request.getRequestURI());
  131 + additionalInfo.setClientIp(LogUtils.clientIP(request));
  132 + additionalInfo.setServerIp(LogUtils.serverIP());
  133 + additionalInfo.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT));
  134 + additionalInfo.setMethod(request.getMethod());
  135 +
  136 +
  137 + EntityId operateId = new EntityId() {
  138 + @Override
  139 + public UUID getId() {
  140 + return Uuids.timeBased();
  141 + }
  142 +
  143 + @Override
  144 + public EntityType getEntityType() {
  145 + return EntityType.RUNNING_EXCEPTION;
  146 + }
  147 + };
  148 +
  149 +
  150 + auditLogService.logEntityAction(currentUser.getTenantId(),currentUser.getCustomerId(),currentUser.getId(), currentUser.getName(), operateId,entity, ActionType.LOG_EXCEPTION,e,additionalInfo);
  151 + } catch (ThingsboardException ex) {
  152 + log.error("异常日志记录异常【{}】",ex);
  153 + }
  154 +
  155 + }
80 156 }
... ...
... ... @@ -19,6 +19,7 @@ import org.thingsboard.server.common.data.yunteng.dto.SceneLinkageDTO;
19 19 import org.thingsboard.server.common.data.yunteng.dto.TriggerDTO;
20 20 import org.thingsboard.server.common.data.yunteng.utils.tools.YtPageData;
21 21 import org.thingsboard.server.controller.BaseController;
  22 +import org.thingsboard.server.controller.yunteng.aspect.annotation.AutoLog;
22 23 import org.thingsboard.server.dao.yunteng.service.SceneLinkageService;
23 24
24 25 import java.net.URI;
... ... @@ -71,6 +72,7 @@ public class YtSceneLinkageController extends BaseController {
71 72
72 73 @ApiOperation("查询(分页列表)")
73 74 @GetMapping(params = {PAGE_SIZE, PAGE})
  75 + @AutoLog("场景联动分页")
74 76 public YtPageData<SceneLinkageDTO> page(
75 77 @RequestParam(PAGE_SIZE) int pageSize,
76 78 @RequestParam(PAGE) int page,
... ...
  1 +package org.thingsboard.server.controller.yunteng.aspect;
  2 +
  3 +
  4 +import com.datastax.oss.driver.api.core.uuid.Uuids;
  5 +import lombok.extern.slf4j.Slf4j;
  6 +import org.apache.http.HttpHeaders;
  7 +import org.aspectj.lang.JoinPoint;
  8 +import org.aspectj.lang.ProceedingJoinPoint;
  9 +import org.aspectj.lang.annotation.AfterThrowing;
  10 +import org.aspectj.lang.annotation.Around;
  11 +import org.aspectj.lang.annotation.Aspect;
  12 +import org.aspectj.lang.annotation.Pointcut;
  13 +import org.aspectj.lang.reflect.MethodSignature;
  14 +import org.springframework.core.annotation.Order;
  15 +import org.springframework.security.core.Authentication;
  16 +import org.springframework.security.core.context.SecurityContextHolder;
  17 +import org.springframework.stereotype.Component;
  18 +import org.springframework.web.context.request.RequestAttributes;
  19 +import org.springframework.web.context.request.RequestContextHolder;
  20 +import org.springframework.web.context.request.ServletRequestAttributes;
  21 +import org.thingsboard.server.common.data.EntityType;
  22 +import org.thingsboard.server.common.data.asset.Asset;
  23 +import org.thingsboard.server.common.data.audit.ActionStatus;
  24 +import org.thingsboard.server.common.data.audit.ActionType;
  25 +import org.thingsboard.server.common.data.audit.AuditLog;
  26 +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
  27 +import org.thingsboard.server.common.data.exception.ThingsboardException;
  28 +import org.thingsboard.server.common.data.id.EntityId;
  29 +import org.thingsboard.server.common.data.yunteng.dto.SysLogOperateDTO;
  30 +import org.thingsboard.server.controller.yunteng.aspect.annotation.AutoLog;
  31 +import org.thingsboard.server.dao.audit.AuditLogService;
  32 +import org.thingsboard.server.service.security.model.SecurityUser;
  33 +import org.thingsboard.server.utils.yunteng.LogUtils;
  34 +
  35 +import javax.servlet.http.HttpServletRequest;
  36 +import java.lang.reflect.Method;
  37 +import java.util.UUID;
  38 +
  39 +/**
  40 + *
  41 + */
  42 +@Aspect
  43 +@Order()
  44 +@Component
  45 +@Slf4j
  46 +public class OperateLogAspect {
  47 + private final AuditLogService auditLogService;
  48 +
  49 + public OperateLogAspect(AuditLogService auditLogService) {
  50 + this.auditLogService = auditLogService;
  51 + }
  52 +
  53 +
  54 + /**
  55 + * AOP监听的切入点
  56 + * 注解所在方法的方法体为空
  57 + * 匹配规则可以和逻辑运算符(&&、||、!)配合使用
  58 + * 匹配规则:注解【"@annotation(com.lifeontrip.base.annotation.RecordLog)"】
  59 + * 匹配规则:方法【"execution(public * com.lifeontrip.base.controller..*.*(..))"】
  60 + * 匹配规则:注解【“@within(com.lifeontrip.base.annotation.RecordLog)”】
  61 + * 匹配规则:代理对象【“@this”】
  62 + * 匹配规则:类注解【“@target”】
  63 + * 匹配规则:方法入参【“@args”】
  64 + */
  65 + @Pointcut("@annotation(org.thingsboard.server.controller.yunteng.aspect.annotation.AutoLog)")
  66 + public void accessLog() {
  67 + }
  68 +
  69 +
  70 + @Around("accessLog()")
  71 + public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
  72 +
  73 + /**执行切入点所在方法的业务逻辑*/
  74 + Object result = joinPoint.proceed();
  75 +
  76 +
  77 + produceLog(joinPoint, result);
  78 + return result;
  79 +
  80 + }
  81 +
  82 +
  83 + /**
  84 + * 切入点方法执行异常,调用该方法
  85 + *
  86 + * @param joinPoint 切入点相关信息的实例
  87 + * @param e 切入点抛出的异常
  88 + */
  89 + @AfterThrowing(value = "accessLog()", throwing = "e")
  90 + public void doException(JoinPoint joinPoint, Exception e) {
  91 + log.error("日志功能AOP异常【{}】", e);
  92 + }
  93 +
  94 +
  95 + /**
  96 + * 生产日志并缓存到队列中
  97 + *
  98 + * @param joinPoint 切入点相关信息的实例
  99 + * @param jsonResult 切入点执行后返回的结果
  100 + */
  101 + void produceLog(final JoinPoint joinPoint, Object jsonResult) throws ThingsboardException {
  102 +
  103 + SecurityUser currentUser = getCurrentUser();
  104 +
  105 + //方法注解
  106 + MethodSignature signature = (MethodSignature) joinPoint.getSignature();
  107 + Method method = signature.getMethod();
  108 + AutoLog methodAnnotate = method.getAnnotation(AutoLog.class);
  109 +
  110 + Asset entity = new Asset();
  111 + if (methodAnnotate != null) {
  112 + entity.setName(methodAnnotate.value());
  113 + }
  114 + RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
  115 + if(requestAttributes == null){
  116 + return;
  117 + }
  118 + HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
  119 + //请求相关信息
  120 + SysLogOperateDTO additionalInfo = new SysLogOperateDTO();
  121 +
  122 +
  123 + additionalInfo.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT));
  124 + additionalInfo.setData(request.getMethod());
  125 + additionalInfo.setApi(request.getRequestURI());
  126 + additionalInfo.setClientIp(LogUtils.clientIP(request));
  127 + additionalInfo.setServerIp(LogUtils.serverIP());
  128 + additionalInfo.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT));
  129 + additionalInfo.setMethod(request.getMethod());
  130 +
  131 +
  132 + EntityId operateId = new EntityId() {
  133 + @Override
  134 + public UUID getId() {
  135 + return Uuids.timeBased();
  136 + }
  137 +
  138 + @Override
  139 + public EntityType getEntityType() {
  140 + return EntityType.REST_API;
  141 + }
  142 + };
  143 +
  144 +
  145 + auditLogService.logEntityAction(currentUser.getTenantId(),currentUser.getCustomerId(),currentUser.getId(), currentUser.getName(), operateId,entity, ActionType.LOG_OPERATE,null,additionalInfo);
  146 + }
  147 +
  148 + protected SecurityUser getCurrentUser() throws ThingsboardException {
  149 + Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
  150 + if (authentication != null && authentication.getPrincipal() instanceof SecurityUser) {
  151 + return (SecurityUser) authentication.getPrincipal();
  152 + } else {
  153 + throw new ThingsboardException("You aren't authorized to perform this operation!", ThingsboardErrorCode.AUTHENTICATION);
  154 + }
  155 + }
  156 +
  157 +}
... ...
  1 +package org.thingsboard.server.controller.yunteng.aspect.annotation;
  2 +
  3 +import java.lang.annotation.*;
  4 +
  5 +/**
  6 + * 操作日志注解
  7 + *
  8 + * @author Administrator
  9 + */
  10 +@Target(ElementType.METHOD)
  11 +@Retention(RetentionPolicy.RUNTIME)
  12 +@Documented
  13 +public @interface AutoLog {
  14 +
  15 + /**
  16 + * 功能名称
  17 + */
  18 + String value() default "";
  19 +
  20 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.utils.yunteng;
  17 +
  18 +import lombok.Data;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.apache.commons.lang3.StringUtils;
  21 +
  22 +import javax.servlet.http.HttpServletRequest;
  23 +import java.io.Serializable;
  24 +import java.net.InetAddress;
  25 +import java.net.UnknownHostException;
  26 +
  27 +@Data
  28 +@Slf4j
  29 +public class LogUtils implements Serializable {
  30 +
  31 +
  32 + public static String clientIP(HttpServletRequest request) {
  33 + String ip = null;
  34 + try {
  35 + ip = request.getHeader("x-forwarded-for");
  36 + if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
  37 + ip = request.getHeader("Proxy-Client-IP");
  38 + }
  39 + if (StringUtils.isEmpty(ip) || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  40 + ip = request.getHeader("WL-Proxy-Client-IP");
  41 + }
  42 + if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
  43 + ip = request.getHeader("HTTP_CLIENT_IP");
  44 + }
  45 + if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
  46 + ip = request.getHeader("HTTP_X_FORWARDED_FOR");
  47 + }
  48 + if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
  49 + ip = request.getRemoteAddr();
  50 + }
  51 + } catch (Exception e) {
  52 + log.error("客户端IP异常【{}】", e);
  53 + }
  54 +
  55 + return ip;
  56 + }
  57 +
  58 +
  59 + /**
  60 + * 当前主机IP
  61 + * @return
  62 + * @throws UnknownHostException
  63 + */
  64 + public static String serverIP() {
  65 + String ip = null;
  66 + try {
  67 + ip = InetAddress.getLocalHost().getHostAddress();
  68 + } catch (UnknownHostException e) {
  69 + log.error("服务端IP异常【{}】 ", e);
  70 + }
  71 + return ip;
  72 + }
  73 +
  74 +}
... ...
... ... @@ -562,6 +562,8 @@ audit-log:
562 562 "edge": "${AUDIT_LOG_MASK_EDGE:W}"
563 563 "tb_resource": "${AUDIT_LOG_MASK_RESOURCE:W}"
564 564 "ota_package": "${AUDIT_LOG_MASK_OTA_PACKAGE:W}"
  565 + "running_exception": "${AUDIT_LOG_RUNNING_EXCEPTION:W}"
  566 + "rest_api": "${AUDIT_LOG_REST_API:W}"
565 567 sink:
566 568 # Type of external sink. possible options: none, elasticsearch
567 569 type: "${AUDIT_LOG_SINK_TYPE:none}"
... ...
... ... @@ -19,5 +19,7 @@ package org.thingsboard.server.common.data;
19 19 * @author Andrew Shvayka
20 20 */
21 21 public enum EntityType {
22   - TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, TENANT_PROFILE, DEVICE_PROFILE, API_USAGE_STATE, TB_RESOURCE, OTA_PACKAGE, EDGE, RPC;
  22 + TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, TENANT_PROFILE, DEVICE_PROFILE, API_USAGE_STATE, TB_RESOURCE, OTA_PACKAGE, EDGE, RPC
  23 + //Thingskit function
  24 + , RUNNING_EXCEPTION, REST_API;
23 25 }
... ...
... ... @@ -48,7 +48,10 @@ public enum ActionType {
48 48 PROVISION_SUCCESS(false),
49 49 PROVISION_FAILURE(false),
50 50 ASSIGNED_TO_EDGE(false), // log edge name
51   - UNASSIGNED_FROM_EDGE(false);
  51 + UNASSIGNED_FROM_EDGE(false),
  52 + //Thingskit function
  53 + LOG_EXCEPTION(false),
  54 + LOG_OPERATE(false);
52 55
53 56 private final boolean isRead;
54 57
... ...
  1 +package org.thingsboard.server.common.data.yunteng.dto;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +
  8 +@EqualsAndHashCode(callSuper = false)
  9 +@Data
  10 +public class SysLogOperateDTO {
  11 +
  12 +
  13 + private String serverIp;
  14 +
  15 +
  16 + private String remark;
  17 +
  18 +
  19 + private String clientIp;
  20 + private String clientAddr;
  21 + private String userAgent;
  22 + private String api;
  23 + private String data;
  24 + private String method;
  25 +}
... ...