zoukankan      html  css  js  c++  java
  • SpringBoot_操作日志记录

    SpringBoot使用AOP统一处理日志

    应用(记录用户操作日志): 有时候我们需要处理一些请求日志,或者对某些方法进行一些监控,如果出现例外情况应该进行怎么样的处理,现在,我们从spring boot中引入AOP

     

    1、开发准备

    环境:idea、jdk 1.8、springboot、mysql

    1.1 目录结构

    └─src
        └─main
            ├─java
            │  └─com
            │      └─example
            │          └─log
            │              │  LogApplication.java
            │              ├─annotation
            │              │      Log.java
            │              ├─aspect
            │              │      LogAspect.java
            │              ├─common
            │              │  ├─context
            │              │  │      BaseContext.java
            │              │  │      CallBack.java
            │              │  │      SpringContextHolder.java
            │              │  ├─enums
            │              │  │      Action.java
            │              │  └─utils
            │              │          CloseUtil.java
            │              │          ExceptionUtil.java
            │              │          FileUtil.java
            │              │          ServletUtil.java
            │              ├─controller
            │              │      LogController.java
            │              ├─domain
            │              │      SysLog.java
            │              ├─repository
            │              │      LogRepository.java
            │              └─service
            │                  │  LogService.java
            │                  └─impl
            │                          LogServiceImpl.java
            └─resources
                │  application.yml
                └─ip2region
                        ip2region.db (注意这个文件,作用:转换IP地址来源)
    resources/ip2region/ip2region.db 文件 下载:https://files.cnblogs.com/files/mmdz/ip2region.db.rar

    1.2 日志表

    准备sys_log日志表(mysql)

    DROP TABLE IF EXISTS `sys_log`;
    CREATE TABLE `sys_log` (
      `id` char(20) NOT NULL COMMENT '编号',
      `operator` varchar(255) DEFAULT NULL COMMENT '操作人',
      `operation_time` datetime DEFAULT NULL COMMENT '操作时间',
      `title` varchar(255) DEFAULT NULL COMMENT '编号',
      `method` varchar(255) DEFAULT NULL COMMENT '方法',
      `type` varchar(255) DEFAULT NULL COMMENT '请求方式',
      `params` varchar(255) DEFAULT NULL COMMENT '参数',
      `state` bit(1) DEFAULT NULL COMMENT '状态',
      `action` varchar(255) DEFAULT NULL COMMENT '操作',
      `request_ip` varchar(255) DEFAULT NULL COMMENT '请求ip',
      `address` varchar(255) DEFAULT NULL COMMENT 'ip来源',
      `browser` varchar(255) DEFAULT NULL COMMENT '浏览器',
      `time` bigint(10) DEFAULT NULL COMMENT '请求耗时',
      `error` text COMMENT '异常信息',
      `system` varchar(255) DEFAULT NULL COMMENT '操作系统',
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

    1.3 pom文件

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.5.3</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.example</groupId>
        <artifactId>log</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>log</name>
        <description>Demo project for Spring Boot</description>
        <properties>
            <java.version>1.8</java.version>
            <datasource.version>3.3.1</datasource.version>
            <mysql.version>8.0.22</mysql.version>
            <mybatis.plus.version>3.4.3</mybatis.plus.version>
            <swagger.version>2.9.2</swagger.version>
            <hutool.version>5.5.7</hutool.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <!-- 切 面 编 程 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>
            <!-- 数 据 库 操 作 框 架 -->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>${mybatis.plus.version}</version>
            </dependency>
            <!-- 数 据 库 连 接 工 具 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
                <scope>runtime</scope>
            </dependency>
            <!-- 常 用 工 具 类 -->
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>${hutool.version}</version>
            </dependency>
            <!-- Swagger UI 相关 -->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>${swagger.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>io.swagger</groupId>
                        <artifactId>swagger-annotations</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>io.swagger</groupId>
                        <artifactId>swagger-models</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>${swagger.version}</version>
            </dependency>
            <dependency>
                <groupId>io.swagger</groupId>
                <artifactId>swagger-annotations</artifactId>
                <version>1.5.21</version>
            </dependency>
            <dependency>
                <groupId>io.swagger</groupId>
                <artifactId>swagger-models</artifactId>
                <version>1.5.21</version>
            </dependency>
    
            <!-- 解析客户端操作系统、浏览器信息 -->
            <dependency>
                <groupId>nl.basjes.parse.useragent</groupId>
                <artifactId>yauaa</artifactId>
                <version>5.23</version>
            </dependency>
            <!-- 根据IP获取城市-Java调用“ip2region” -->
            <dependency>
                <groupId>org.lionsoul</groupId>
                <artifactId>ip2region</artifactId>
                <version>1.7.2</version>
            </dependency>
            <!-- lombok -->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <excludes>
                            <exclude>
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok</artifactId>
                            </exclude>
                        </excludes>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>

    1.4 application.yml

    spring:
      datasource:
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/log?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=GMT
        username: root
        password: 123456

    2、编码

    2.1 domain包

    SysLog(日志模型)

    package com.example.log.domain;
    
    import cn.hutool.core.util.ObjectUtil;
    import com.baomidou.mybatisplus.annotation.TableField;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import com.example.log.common.enums.Action;
    import lombok.Data;
    
    import java.io.Serializable;
    import java.time.LocalDateTime;
    
    /**
     * 日志模型
     * */
    @Data
    @TableName("sys_log")
    public class SysLog implements Serializable {
        /** 编号 */
        @TableId("id")
        private String id;
        /** 操作人 */
        @TableField("operator")
        private String operator;
        /** 操作时间 */
        @TableField("operation_time")
        private LocalDateTime operationTime;
        /** 标题 */
        @TableField("title")
        private String title;
        /** 请求方法 */
        @TableField("method")
        private String method;
        /** 请求方式 */
        @TableField("type")
        private String type;
        /** 请求参数 */
        @TableField("params")
        private String params;
        /** 状态(是否成功) */
        @TableField("state")
        private Boolean state;
        /** 操作类型 */
        @TableField("action")
        private Action action;
        /** 请求ip */
        @TableField("request_ip")
        private String requestIp;
        /** ip来源 */
        @TableField("address")
        private String address;
        /** 浏览器 */
        @TableField("browser")
        private String browser;
        /** 请求耗时 */
        @TableField("time")
        private Long time;
        /** 异常信息 */
        @TableField("error")
        private byte[] error;
        @TableField(exist = false)
        private String exceptionDetailStr;
        /** 系统 */
        @TableField("`system`")
        private String system;
    
        public String getExceptionDetailStr() {
            return new String(ObjectUtil.isNotNull(error) ? error : "".getBytes());
        }
    }

    2.2 annotation包

    package com.example.log.annotation;
    
    import com.example.log.common.enums.Action;
    import java.lang.annotation.*;
    
    /**
     * 日志 注解
     * */
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.METHOD})
    public @interface Log {
    
        /**
         * Title 默认输入
         * */
        String title() default "暂无标题";
    
        /**
         * Describe 默认输入
         * */
        String describe() default "暂无描述";
    
        /**
         * Action 操作类型
         * */
        Action action() default Action.QUERY;
    }

    2.3 common包

    2.3.1 enums

    Action 日 志 分 类

    package com.example.log.common.enums;
    
    /**
     * 日 志 分 类
     */
    public enum Action {
        /** 认证 */
        AUTH,
        /***/
        ADD,
        /***/
        REMOVE,
        /***/
        EDIT,
        /***/
        QUERY,
        /** 导入 */
        IMPORT,
        /** 导出 */
        REPORT,
        /** 上传 */
        UPLOAD
    }

    2.3.2 context

    CallBack

    package com.example.log.common.context;
    
    /**
     * @Desc: TODO
     *          针对某些初始化方法,在SpringContextHolder 初始化前时,<br>
     *          提交一个 提交回调任务。<br>
     *          在SpringContextHolder 初始化后,进行回调使用
     */
    public interface CallBack {
    
        /**
         * 回调执行方法
         */
        void executor();
    
        /**
         * 本回调任务名称
         * @return /
         */
        default String getCallBackName() {
            return Thread.currentThread().getId() + ":" + this.getClass().getName();
        }
    
    }

    SpringContextHolder

    package com.example.log.common.context;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.core.env.Environment;
    import java.util.ArrayList;
    import java.util.List;
    
    public class SpringContextHolder {
    
        private static ApplicationContext applicationContext = null;
        private static final List<CallBack> CALL_BACKS = new ArrayList<>();
        private static boolean addCallback = true;
    
        /**
         * 针对 某些初始化方法,在SpringContextHolder 未初始化时 提交回调方法。
         * 在SpringContextHolder 初始化后,进行回调使用
         *
         * @param callBack 回调函数
         */
        public synchronized static void addCallBacks(CallBack callBack) {
            if (addCallback) {
                SpringContextHolder.CALL_BACKS.add(callBack);
            } else {
                System.out.println("CallBack:" + callBack.getCallBackName() +" 已无法添加!立即执行");
                callBack.executor();
            }
        }
    
        /**
         * 获取SpringBoot 配置信息
         *
         * @param property     属性key
         * @param defaultValue 默认值
         * @param requiredType 返回类型
         * @return /
         */
        public static <T> T getProperties(String property, T defaultValue, Class<T> requiredType) {
            T result = defaultValue;
            try {
                result = getBean(Environment.class).getProperty(property, requiredType);
            } catch (Exception ignored) {}
            return result;
        }
    
        /**
         * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
         */
        public static <T> T getBean(Class<T> requiredType) {
            assertContextInjected();
            return applicationContext.getBean(requiredType);
        }
    
        /**
         * 检查ApplicationContext不为空.
         */
        private static void assertContextInjected() {
            if (applicationContext == null) {
                throw new IllegalStateException("applicaitonContext属性未注入, 请在applicationContext" +
                        ".xml中定义SpringContextHolder或在SpringBoot启动类中注册SpringContextHolder.");
            }
        }
    
    }

    BaseContext

    package com.example.log.common.context;
    
    import com.example.log.common.utils.ServletUtil;
    import com.example.log.domain.SysLog;
    import com.example.log.common.enums.Action;
    import com.example.log.service.LogService;
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.stereotype.Component;
    import org.springframework.transaction.annotation.Transactional;
    
    import javax.annotation.Resource;
    import java.time.LocalDateTime;
    
    /**
     * Base Context
     */
    @Component
    public class BaseContext {
    
        /**
         * 日 志 服 务
         */
        @Resource
        private LogService sysLogService;
    
        /**
         * 新增日志
         *
         * @param title  标题
         * @param methodName 请求方法
         * @param parameter 参数
         * @param action 动作
         * @param state  状态
         * @param time   请求耗时
         * @param error  异常
         */
        @Async
        @Transactional(rollbackFor = Exception.class)
        public void record(String title,
                           String methodName,
                           String parameter,
                           Action action,
                           Boolean state,
                           Long time,
                           byte[] error) {
            SysLog sysLog = new SysLog();
            sysLog.setOperator("");// 操作人
            sysLog.setOperationTime(LocalDateTime.now());// 操作时间
            sysLog.setTitle(title);//标题
            sysLog.setMethod(methodName);// 请求方法
            sysLog.setType(ServletUtil.getMethod());// 请求方式
            sysLog.setParams(parameter);// 参数
            sysLog.setState(state);// 状态(是否成功)
            sysLog.setAction(action);// 操作类型
            String ip = ServletUtil.getIp();
            sysLog.setRequestIp(ServletUtil.getIp());// 请求ip
            sysLog.setAddress(ServletUtil.getCityInfo(ip));// ip来源
            sysLog.setBrowser(ServletUtil.getBrowser());// 浏览器
            sysLog.setTime(time);// 请求耗时
            sysLog.setError(error);// 异常信息
            sysLog.setSystem(ServletUtil.getSystem());// 操作系统
            sysLogService.save(sysLog);
        }
    
    }

    2.3.3 utils

    CloseUtil

    package com.example.log.common.utils;
    
    import java.io.Closeable;
    
    /**
     * @Desc: TODO 用于关闭各种连接,缺啥补啥
     */
    public class CloseUtil {
    
        public static void close(Closeable closeable) {
            if (null != closeable) {
                try {
                    closeable.close();
                } catch (Exception e) {
                    // 静默关闭
                }
            }
        }
    
        public static void close(AutoCloseable closeable) {
            if (null != closeable) {
                try {
                    closeable.close();
                } catch (Exception e) {
                    // 静默关闭
                }
            }
        }
    }

    ExceptionUtil

    package com.example.log.common.utils;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.io.StringWriter;
    
    /**
     * @Desc: TODO 将日志堆栈信息输出到文件
     */
    public class ExceptionUtil {
    
        public static String getMessage(Exception e) {
            StringWriter sw = null;
            PrintWriter pw = null;
            try {
                sw = new StringWriter();
                pw = new PrintWriter(sw);
                // 将出错的栈信息输出到printWriter中
                e.printStackTrace(pw);
                pw.flush();
                sw.flush();
            } finally {
                if (sw != null) {
                    try {
                        sw.close();
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    }
                }
                if (pw != null) {
                    pw.close();
                }
            }
            return sw.toString();
        }
    
        /**
         * 获取堆栈信息
         */
        public static String getStackTrace(Throwable throwable){
            StringWriter sw = new StringWriter();
            try (PrintWriter pw = new PrintWriter(sw)) {
                throwable.printStackTrace(pw);
                return sw.toString();
            }
        }
    }

    FileUtil

    package com.example.log.common.utils;
    
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.InputStream;
    import java.io.OutputStream;
    
    /**
     * @Desc: TODO File工具类,扩展 hutool 工具包
     */
    public class FileUtil extends cn.hutool.core.io.FileUtil {
    
        /**
         * 系统临时目录
         * <br>
         * windows 包含路径分割符,但Linux 不包含,
         * 在windows \== 前提下,
         * 为安全起见 同意拼装 路径分割符,
         * <pre>
         *       java.io.tmpdir
         *       windows : C:Users/xxxAppDataLocalTemp
         *       linux: /temp
         * </pre>
         */
        public static final String SYS_TEM_DIR = System.getProperty("java.io.tmpdir") + File.separator;
    
        /**
         * inputStream 转 File
         */
        public static File inputStreamToFile(InputStream ins, String name){
            File file = new File(SYS_TEM_DIR + name);
            if (file.exists()) {
                return file;
            }
            OutputStream os = null;
            try {
                os = new FileOutputStream(file);
                int bytesRead;
                int len = 8192;
                byte[] buffer = new byte[len];
                while ((bytesRead = ins.read(buffer, 0, len)) != -1) {
                    os.write(buffer, 0, bytesRead);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                CloseUtil.close(os);
                CloseUtil.close(ins);
            }
            return file;
        }
    
    }

    ServletUtil

    package com.example.log.common.utils;
    
    import cn.hutool.http.HttpUtil;
    import cn.hutool.json.JSONUtil;
    import com.example.log.common.context.SpringContextHolder;
    import nl.basjes.parse.useragent.UserAgent;
    import nl.basjes.parse.useragent.UserAgentAnalyzer;
    import org.lionsoul.ip2region.DataBlock;
    import org.lionsoul.ip2region.DbConfig;
    import org.lionsoul.ip2region.DbSearcher;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.servlet.http.HttpServletRequest;
    import java.io.File;
    import java.net.InetAddress;
    import java.net.UnknownHostException;
    
    /**
     * Servlet 工具类
     * */
    public class ServletUtil {
    
        /**
         * IP归属地查询
         */
        private static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp?ip=%s&json=true";
        /**
         * 用于IP定位转换
         */
        private static final String REGION = "内网IP|内网IP";
    
        private static boolean ipLocal = false;
        private static File file = null;
        private static DbConfig config;
        private static final UserAgentAnalyzer userAgentAnalyzer = UserAgentAnalyzer
                .newBuilder()
                .hideMatcherLoadStats()
                .withCache(10000)
                .withField(UserAgent.AGENT_NAME_VERSION)
                .build();
    
        static {
            SpringContextHolder.addCallBacks(() -> {
                ipLocal = SpringContextHolder.getProperties("ip.local-parsing", false, Boolean.class);
                if (ipLocal) {
                    /*
                     * 此文件为独享 ,不必关闭
                     */
                    String path = "ip2region/ip2region.db";
                    String name = "ip2region.db";
                    try {
                        config = new DbConfig();
                        file = FileUtil.inputStreamToFile(new ClassPathResource(path).getInputStream(), name);
                    } catch (Exception e) {
                        e.printStackTrace();
    //                    log.error(e.getMessage(), e);
                    }
                }
            });
        }
    
        /**
         * Describe: Request 客户端地址(获取ip地址)
         *
         * @return {@link String}
         * */
        public static String getIp() {
            HttpServletRequest request = getRequest();
            String ipAddress = request.getHeader("x-forwarded-for");
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
                if ("127.0.0.1".equals(ipAddress) || "0:0:0:0:0:0:0:1".equals(ipAddress)) {
                    // 根据网卡取本机配置的IP
                    try {
                        ipAddress = InetAddress.getLocalHost().getHostAddress();
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                    }
                }
            }
            //对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
            if (ipAddress != null && ipAddress.length() > 15) { //"***.***.***.***".length() = 15
                if (ipAddress.indexOf(",") > 0) {
                    ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
                }
            }
            return ipAddress;
        }
    
        /**
         * 根据ip获取详细地址
         */
        public static String getCityInfo(String ip) {
            if (ipLocal) {
                return getLocalCityInfo(ip);
            } else {
                return getHttpCityInfo(ip);
            }
        }
    
        /**
         * 根据ip获取详细地址
         */
        public static String getHttpCityInfo(String ip) {
            String api = String.format(IP_URL, ip);
            cn.hutool.json.JSONObject object = JSONUtil.parseObj(HttpUtil.get(api));
            return object.get("addr", String.class);
        }
    
        /**
         * 根据ip获取详细地址
         */
        public static String getLocalCityInfo(String ip) {
            try {
                DataBlock dataBlock = new DbSearcher(config, file.getPath())
                        .binarySearch(ip);
                String region = dataBlock.getRegion();
                String address = region.replace("0|", "");
                char symbol = '|';
                if (address.charAt(address.length() - 1) == symbol) {
                    address = address.substring(0, address.length() - 1);
                }
                return address.equals(REGION) ? "内网IP" : address;
            } catch (Exception e) {
    //            log.error(e.getMessage(), e);
                e.printStackTrace();
            }
            return "";
        }
    
        /**
         * 获取 HttpServletRequest 对象
         *
         * @return {@link HttpServletRequest}
         * */
        private static HttpServletRequest getRequest(){
            ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            return servletRequestAttributes.getRequest();
        }
    
        /**
         * Request 请求方法(类型)
         *
         * @return {@link String}
         * */
        public static String getMethod(){
            return getRequest().getMethod();
        }
    
        /**
         * Request 请求头
         *
         * @param name 名称
         * @return {@link String}
         * */
        public static String getHeader(String name){
            return getRequest().getHeader(name);
        }
    
        /**
         * Request Agent
         *
         * @return {@link String}
         * */
        private static String getAgent(){
            return getHeader("User-Agent");
        }
    
        /**
         * Request 浏览器类型
         *
         * @return {@link String}
         * */
        public static String getBrowser(){
            String browser = "";
            String userAgent = getAgent();
            if (userAgent.contains("Firefox")) browser = "火狐浏览器";
            else if (userAgent.contains("Chrome")) browser = "谷歌浏览器";
            else if (userAgent.contains("Trident")) browser = "IE 浏览器";
            else browser = "你用啥浏览器";
            UserAgent.ImmutableUserAgent parse = userAgentAnalyzer.parse(userAgent);
            String value = parse.get(UserAgent.AGENT_NAME_VERSION).getValue();
            return browser + "(" + value + ")";
        }
    
        /**
         * Request 访问来源 ( 客户端类型 )
         *
         * @return {@link String}
         * */
        public static String getSystem(){
            String userAgent = getAgent();
            if (getAgent().toLowerCase().contains("windows" )) return "Windows";
            else if (userAgent.toLowerCase().contains("mac" )) return "Mac";
            else if (userAgent.toLowerCase().contains("x11" )) return "Unix";
            else if (userAgent.toLowerCase().contains("android" )) return "Android";
            else if (userAgent.toLowerCase().contains("iphone" )) return "IPhone";
            else return "UnKnown, More-Info: " + userAgent;
        }
    
    }

    2.4 aspect包(核心)

    Log 实现 Aop 切面类

    package com.example.log.aspect;
    
    import cn.hutool.json.JSONUtil;
    import com.example.log.annotation.Log;
    import com.example.log.common.context.BaseContext;
    import com.example.log.common.enums.Action;
    import com.example.log.common.utils.ExceptionUtil;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.Resource;
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * Log 实现 Aop 切面类
     */
    @Aspect
    @Component
    public class LogAspect {
    
        ThreadLocal<Long> currentTime = new ThreadLocal<>();
    
        /**
         * 基 础 上 下 文
         */
        @Resource
        private BaseContext context;
    
        /**
         * 配置切入点(切 面 编 程)
         */
        @Pointcut("@annotation(com.example.log.annotation.Log) || @within(com.example.log.annotation.Log)")
        public void logPointcut() {
            // 该方法无方法体,主要为了让同类中其他方法使用此切入点
        }
    
        /**
         * 处 理 系 统 日 志(配置环绕通知,使用在方法logPointcut()上注册的切入点)
         *
         * @param joinPoint join point for advice
         */
        @Around("logPointcut()")
        public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
            Object result = null;
            // 记录方法的执行时间
            currentTime.set(System.currentTimeMillis());
            // 执 行 方 法
            result = joinPoint.proceed();
            // 注解解析
            Log annotation = getAnnotation(joinPoint);
            String title = annotation.title();
            Action action = annotation.action();
            String describe = annotation.describe();
            // 获取方法名
            String methodName = getMethodName(joinPoint);
            // 获取参数
            String parameter = getParameterToJson((ProceedingJoinPoint) joinPoint);
            // 请求耗时
            Long time = System.currentTimeMillis() - currentTime.get();
            currentTime.remove();
    
            // 记 录 日 志
            context.record(title, methodName, parameter, action, true, time, null);
    
            return result;
        }
    
        /**
         * 配置异常通知
         *
         * @param joinPoint join point for advice
         * @param e         exception
         */
        @AfterThrowing(pointcut = "logPointcut()", throwing = "e")
        public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
            // 注解解析
            Log annotation = getAnnotation((ProceedingJoinPoint) joinPoint);
            String title = annotation.title();
            Action action = annotation.action();
            String describe = annotation.describe();
            // 获取方法名
            String methodName = getMethodName((ProceedingJoinPoint) joinPoint);
            // 获取参数
            String parameter = getParameterToJson((ProceedingJoinPoint) joinPoint);
            // 请求耗时
            Long time = System.currentTimeMillis() - currentTime.get();
            currentTime.remove();
            // 异常详细
            byte[] exceptionDetail = ExceptionUtil.getStackTrace(e).getBytes();
    
            // 记 录 日 志
            context.record(title, methodName, parameter, action, false, time, exceptionDetail);
        }
    
        /**
         * 获 取 注 解
         */
        public Log getAnnotation(ProceedingJoinPoint point) {
            MethodSignature signature = (MethodSignature) point.getSignature();
            Class<? extends Object> targetClass = point.getTarget().getClass();
            Log targetLog = targetClass.getAnnotation(Log.class);
            if (targetLog != null) {
                return targetLog;
            } else {
                Method method = signature.getMethod();
                Log log = method.getAnnotation(Log.class);
                return log;
            }
        }
    
        /**
         * 获 取 方法名
         */
        public String getMethodName(ProceedingJoinPoint point) {
            MethodSignature signature = (MethodSignature) point.getSignature();
            // 方法路径
            String methodName = point.getTarget().getClass().getName()+"."+signature.getName()+"()";
            return methodName;
        }
    
        /**
         * 获 取 参数(转换json格式)
         */
        public String getParameterToJson(ProceedingJoinPoint point) {
            List<Object> argList = new ArrayList<>();
            //参数值
            Object[] argValues = point.getArgs();
            //参数名称
            String[] argNames = ((MethodSignature)point.getSignature()).getParameterNames();
            if(argValues != null){
                for (int i = 0; i < argValues.length; i++) {
                    Map<String, Object> map = new HashMap<>();
                    String key = argNames[i];
                    map.put(key, argValues[i]);
                    argList.add(map);
                    map = null;
                }
            }
            if (argList.size() == 0) {
                return "";
            }
            return argList.size() == 1 ? JSONUtil.toJsonStr(argList.get(0)) : JSONUtil.toJsonStr(argList);
        }
    }

    2.5 三层架构

    2.5.1 controller

    package com.example.log.controller;
    
    import cn.hutool.json.JSON;
    import cn.hutool.json.JSONObject;
    import cn.hutool.json.JSONUtil;
    import com.example.log.annotation.Log;
    import com.example.log.service.LogService;
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiOperation;
    import lombok.RequiredArgsConstructor;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequiredArgsConstructor
    @RequestMapping("/api/logs")
    @Api(tags = "日志管理")
    public class LogController {
    
        /** 使用@RequiredArgsConstructor 基于构造函数注入*/
        private final LogService logService;
    
        /**
         * 查询日志列表
         */
        @GetMapping("list")
        @ApiOperation(value = "查询日志")
        public String list(){
            return JSONUtil.parse(logService.list()).toString();
        }
    
        /**
         * 测试
         */
        @GetMapping("demo")
        @Log(title = "测试")
        @ApiOperation(value = "测试")
        public String demo(@RequestParam String param){
            return param;
        }
    
    }

    2.5.2 service

    package com.example.log.service;
    
    import com.baomidou.mybatisplus.extension.service.IService;
    import com.example.log.domain.SysLog;
    
    public interface LogService extends IService<SysLog> {
    
    }
    package com.example.log.service.impl;
    
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.example.log.domain.SysLog;
    import com.example.log.repository.LogRepository;
    import com.example.log.service.LogService;
    import org.springframework.stereotype.Service;
    
    @Service
    public class LogServiceImpl extends ServiceImpl<LogRepository, SysLog> implements LogService {
    }

    2.5.2 repository

    package com.example.log.repository;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.example.log.domain.SysLog;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface LogRepository extends BaseMapper<SysLog> {
    }

     

     

    3、测试

    新增测试 http://127.0.0.1:8080//api/logs/demo?param=123

    查看数据 http://127.0.0.1:8080//api/logs/list

     

     

  • 相关阅读:
    优秀大整数
    洛谷—— P3908 异或之和
    洛谷—— P1869 愚蠢的组合数
    洛谷—— P1680 奇怪的分组
    洛谷—— P1609 最小回文数
    Something I like
    数学相关
    新博客测试中~
    P3369 【模板】普通平衡树
    2017 11.6 NOIP模拟赛
  • 原文地址:https://www.cnblogs.com/mmdz/p/15522775.html
Copyright © 2011-2022 走看看