springboot通过aop实现日志打印

1、通过springboot创建web项目gktjweb ,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 http://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.1.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.zr</groupId>
    <artifactId>gktjweb</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>gktjweb</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.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-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>net.sf.json-lib</groupId>
            <artifactId>json-lib</artifactId>
            <version>2.3</version>
            <classifier>jdk15</classifier>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.7</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.31</version>
        </dependency>
        <dependency>
            <groupId>com.thoughtworks.xstream</groupId>
            <artifactId>xstream</artifactId>
            <version>1.4.10</version>
        </dependency>
        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.8.3</version>
        </dependency>
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>

        <dependency>
            <groupId>net.sf.ezmorph</groupId>
            <artifactId>ezmorph</artifactId>
            <version>1.0.6</version>
        </dependency>

        <dependency>
            <groupId>org.json</groupId>
            <artifactId>json</artifactId>
            <version>20160810</version>
        </dependency>
        <dependency>
            <groupId>de.odysseus.staxon</groupId>
            <artifactId>staxon</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>xom</groupId>
            <artifactId>xom</artifactId>
            <version>1.1</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.7.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.7.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.6</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.6</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

2、引入日志文件:logback-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
	<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
	<property name="LOG_HOME" value="D:/gktjlog" />
	<!-- 控制台输出 -->
	<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
		<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
			<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
		</encoder>
	</appender>
	<!-- 按照每天生成日志文件 -->
	<appender name="FILE"  class="ch.qos.logback.core.rolling.RollingFileAppender">
		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
			<!--日志文件输出的文件名-->
			<FileNamePattern>${LOG_HOME}/gktj.log.%d{yyyy-MM-dd}.log</FileNamePattern>
			<!--日志文件保留天数-->
			<MaxHistory>30</MaxHistory>
		</rollingPolicy>
		<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
			<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
			<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
		</encoder>
		<!--日志文件最大的大小-->
		<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
			<MaxFileSize>100MB</MaxFileSize>
		</triggeringPolicy>
	</appender>

	<!-- 日志输出级别 -->
	<root level="INFO">
		<appender-ref ref="STDOUT" />
		<appender-ref ref="FILE" />
	</root>
	<!--日志异步到数据库 -->
	<!--<appender name="DB" class="ch.qos.logback.classic.db.DBAppender">-->
	<!--&lt;!&ndash;日志异步到数据库 &ndash;&gt;-->
	<!--<connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">-->
	<!--&lt;!&ndash;连接池 &ndash;&gt;-->
	<!--<dataSource class="com.mchange.v2.c3p0.ComboPooledDataSource">-->
	<!--<driverClass>com.mysql.jdbc.Driver</driverClass>-->
	<!--<url>jdbc:mysql://127.0.0.1:3306/databaseName</url>-->
	<!--<user>root</user>-->
	<!--<password>root</password>-->
	<!--</dataSource>-->
	<!--</connectionSource>-->
	<!--</appender>-->
</configuration>

3、创建controller层

package com.zr.gktjweb.controller;

import com.zr.gktjweb.annotation.Log;
import com.zr.gktjweb.common.HttpClientUtil;
import com.zr.gktjweb.common.ResponseBean;
import com.zr.gktjweb.constant.SysCode;
import com.zr.gktjweb.model.SysUser;
import com.zr.gktjweb.util.MapUtil;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.zr.gktjweb.util.JSONUtils;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;

@RestController
public class LoginController {
    @Value("${baseurl}")
    private String baseurl;
    @Value("${login_url}")
    private String loginUrl;
    private static final Logger LOGGER = LoggerFactory.getLogger(LoginController.class);
    @ApiOperation(value = "登录", notes = "登录验证")
    @ApiImplicitParams({@ApiImplicitParam(name = "username", value = "姓名", required = true, dataType = "String"),
            @ApiImplicitParam(name = "password", value = "密码", required = true, dataType = "String")
    })
    @RequestMapping(value = "/login.do", method = RequestMethod.POST)
    @Log(value = "登录", description = "登录操作")
    public ResponseBean login(HttpSession session, String username, String password) {
        String url = baseurl + loginUrl;
        Map<String, String> map = new HashMap<>();
        map.put("username", username);
        map.put("password", password);
        String result = "";
        //请求服务失败
        try {
            result = HttpClientUtil.doPost(url, map);
        } catch (Exception e) {
            e.printStackTrace();
            LOGGER.error("用户登录",e);
            return new ResponseBean(SysCode.errorCode, "系统异常,请联系管理员", "");
        }
        JSONObject json = JSONObject.fromObject(result);
        //登录验证未通过
        if (!json.getString("code").equals(SysCode.successCode + "")) {
            ResponseBean responseBean = JSONUtils.jsonToObject(ResponseBean.class, result);
            return responseBean;
        }
        //验证通过,在session中设置token
        JSONObject dataJson = (JSONObject) json.get("data");
        String userJson = dataJson.getString("user");
        SysUser sysUser = JSONUtils.jsonToObject(SysUser.class, userJson);
        JSONObject tokenJson = (JSONObject) dataJson.get("token");
        String token = tokenJson.getString("token");
        session.setAttribute("token", token);
        session.setAttribute("user", sysUser);
        String url1 = "/pages/index.html";
        Map<String, Object> resMap = new HashMap<>();
        resMap.put("url", url1);
        return new ResponseBean(SysCode.successCode, "登录成功", resMap);
    }

    /**
     * 注销登录
     *
     * @param request
     * @return
     */
    @GetMapping("/loginout.do")
    public String loginOut(HttpServletRequest request) {
        request.getSession().invalidate();
        return "/";
    }
}

4、创建日志注解Log

package com.zr.gktjweb.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
    String value() default "";
    String description() default "";
}

5、创建切面:aspect

package com.zr.gktjweb.aspect;

import com.zr.gktjweb.model.SysUser;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;

/**
 * web请求日志切面类---专门针对控制层,如谁被请求了,花了多少时间,请求发送的参数,返回得值等
 */
@Aspect     // 表示一个切面bean
@Component  // bean容器的组件注解。虽然放在contrller包里,但它不是控制器。如果注入service,但我们又没有放在service包里
@Order(3)   // 有多个日志时,ORDER可以定义切面的执行顺序(数字越大,前置越后执行,后置越前执行)
public class WebLogAspect {
    //定义一个日志记录器
    Logger logger = LoggerFactory.getLogger(this.getClass());
    //定义一个线程本地变量,记录各个线程开始的时间
    ThreadLocal<Long> starttime = new ThreadLocal<Long>();

    /**
     * 定义一个切入点
     */
    @Pointcut("execution(public * com.*.*.controller.*.*(..))")
    public void weblog() {
    }

    @Before("weblog()")
    public void doBefore(JoinPoint joinPoint) {
        starttime.set(System.currentTimeMillis());

        //获取servlet请求对象---因为这不是控制器,这里不能注入HttpServletRequest,但springMVC本身提供ServletRequestAttributes可以拿到
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        SysUser sysUser=(SysUser) request.getSession().getAttribute("user");
        String userName="";
        if(sysUser!=null) {
            userName = sysUser.getUsername();
        }
        StringBuffer beforeInfo = new StringBuffer();
        String log= LogRecord.getOperationLog(joinPoint.getTarget());
        beforeInfo.append(log)
                .append("执行前:\r\n")
                .append("用户:"+userName+"\r\n")
                .append("URL:" + request.getRequestURL().toString() + "\r\n")
                .append("Method:" + request.getMethod() + "\r\n")
                .append("CLASS_METHOD:" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName() + "\r\n")
                .append("ARGS:" + Arrays.toString(joinPoint.getArgs()));
        logger.info(beforeInfo.toString());
    }

    //方法的返回值注入ret
    @AfterReturning(returning = "ret", pointcut = "weblog()")
    public void doafter(Object ret) {
        StringBuffer afterInfo = new StringBuffer();
        afterInfo.append("执行后:\r\n")
                .append("RESPONSE:" + ret + "\r\n")      // 响应的内容---方法的返回值responseEntity
                .append("SPEND:" + (System.currentTimeMillis() - starttime.get()));
        logger.info(afterInfo.toString());
    }

    /**
     * 在方法出现异常时会执行的代码
     * 可以访问到异常对象,可以指定在出现特定异常时在执行通知代码
     */
    @AfterThrowing(pointcut = "weblog()", throwing = "ex")
    public void afterThrowing(JoinPoint joinPoint, Exception ex) {
        String methodName = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
        logger.error(methodName, ex);
    }
}
package com.zr.gktjweb.aspect;




import com.zr.gktjweb.annotation.Log;

import java.lang.reflect.Method;

public class LogRecord {
    public static String getOperationLog(Object obj){
        Class<?> clazz=obj.getClass();
        Method[] methods=clazz.getMethods();
        StringBuffer result= new StringBuffer();
        for (int i = 0; i < methods.length; i++) {
            Method method=methods[i];
            Log logAnnotaion=(Log)method.getAnnotation(Log.class);
            if(logAnnotaion!=null){
                result.append("\r\n模块:"+logAnnotaion.value()+"\r\n")
               .append("描述:"+logAnnotaion.description()+"\r\n");
            }

        }
    return result.toString();
    }

}

 

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值