目录
一:java Web项目中的日志系统
二:日志系统的分类
三:springboot整合slf4j
1.添加mavne支持
2.配置xml以及制定yml
3.代码测试
4.日志记录
四:使用Aop的方法记录日志,并且异步方法执行日记记录
1.定义一个异步线程池
2.在日志的插入上生成为异步方法
3.环绕通知封装日志对象
4.说明
一:java Web项目中的日志系统
定位到问题的发生点、发生时间、发生原因等。在互联网项目生产环境中,有很多异常或者错误是不可复现但是极其严重的。同时一些用户日志可以帮助我们分析用户习惯兴趣等。因此,一个合理的日志框架的设计尤为重要的,(以下包括以后内容都是个人扯扯,有问题希望能提出)。
在一个系统中,日志一般分为2中类型:系统日志和操作日志。其中各种分类以及用途如下:
系统日志:用来记录系统行为用来快速定位问题等。
系统行为:用来记录系统运行的链路日志。
异常日志:异常发生定位。
错误日志:错误发生定位。
操作日志:用来记录用户的行为,以便分析用户的行为、兴趣等(在互联网项目中尤为重要)。
说了这么多了,我们直接来个思维导图的便于理解一下吧。
二:日志系统的分类
在java中我们或多或少的遇到了许多了日志框架:log4j,log4j2,slf4j等等。
一般日志框架简单的分为2类:
日志门面:commons-logging,slf4j
日志实现:log4j,log4j2,logback
毋庸置疑,一旦在项目中引入多个日志框架一定会引起冲突的,实际上在解决日志系统的冲突上使用了一种解决手段:日志系统桥接器。日志系统桥接器使用了日志门面,强行劫持 其他日志框架到门面框架的实现上。
三:springboot整合slf4j
1.添加mavne支持
<!-- 日志工具类 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
2.配置xml以及制定yml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="log.path" value="./fangList_logs" />
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统日志输出 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-info.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 用户访问日志输出 -->
<appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-user.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 显示形成的sql、使用的参数、结果集 -->
<logger name="java.sql" level="debug" />
<logger name="org.springframework.jdbc" level="debug" />
<logger name="cn.com" level="info" />
<root level="info">
<appender-ref ref="console" />
</root>
<!--系统操作日志-->
<root level="info">
<appender-ref ref="file_info" />
<appender-ref ref="file_error" />
</root>
<!--系统用户操作日志-->
<logger name="sys-user" level="info">
<appender-ref ref="sys-user"/>
</logger>
</configuration>
logging:
level:
com.moudle.user.dao: debug
org.springframework: WARN
org.spring.springboot.dao: debug
3.代码测试
4.日志记录
四:使用Aop的方法记录日志,并且异步方法执行日记记录
上面主要说明了将slf4j整合到springboot中,下面将Aop形式来处理日志。
1.定义一个异步线程池
package commons.utils.Thread;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
/**
* springboot的异步线程池
* @author Owner
*
*/
@EnableAsync
@Configuration
class TaskPollConfig{
@Bean("taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(200);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("taskExecutor-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(60);
return executor;
}
}
2.在日志的插入上生成为异步方法
我这里日志插入有其他的一些未处理,这里适应插入用户来模拟的
3.环绕通知封装日志对象
package com.config.Aspect.log;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.apache.catalina.servlet4preview.http.HttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import commons.json.JSON;
import commons.result.DataResult;
import lombok.extern.java.Log;
import lombok.extern.slf4j.Slf4j;
/**
* 日志切面
* ClassName: LogAscpect
* Function: 一句话描述功能.
* auth: monxz
* date: 2019年9月18日 下午3:46:23
*
*
*/
@Aspect
@Component
@Slf4j
public class LogAscpect {
@Pointcut("execution(public * com.moudle.*.controller.*.*(..))")
public void sysLog() {}
@Around("sysLog()")
public Object saveSysLog(ProceedingJoinPoint proceedingJoinPoint) {
//从切面织入点处通过反射机制获取织入点处的方法
MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
//获取切入点所在的方法
Method method = signature.getMethod();
//获取请求的类名
String className = proceedingJoinPoint.getTarget().getClass().getName();
//获取请求的方法名
String methodName = method.getName();
//请求的参数
Object[] args = proceedingJoinPoint.getArgs();
//获取用户名
//获取用户ip地址
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = (HttpServletRequest) attributes.getRequest();
// 记录下请求内容
log.info("URL : " + request.getRequestURL().toString());
log.info("HTTP_METHOD : " + request.getMethod());
log.info("IP : " + request.getRemoteAddr());
/**
*
* TODO:将上述的参数封装成日志对象,并且日志插入
*
*/
//开始调用时间
// 计时并调用目标函数
long start = System.currentTimeMillis();
Long time = System.currentTimeMillis() - start;
Object result ;
try {
result= proceedingJoinPoint.proceed();
return result;
} catch (Throwable e) {
e.printStackTrace();
return new DataResult().buildFail("系统异常");
}
}
}
4.说明
在这里我们将日志插入进行异步插入,不会影响到主程序的运行。