请求日志几乎是所有大型企业级项目的必要的模块,请求日志对于我们来说后期在项目运行上线一段时间用于排除异常、请求分流处理、限制流量等。请求日志一般都会记录请求参数、请求地址、请求状态(Status Code)、SessionId、请求方法方式(Method)、请求时间、客户端IP地址、请求返回内容、耗时等等。如果你得系统还有其他个性化的配置,也可以完成记录。
新建项目并添加依赖: Web、JPA、MySQL、Druid,并配置druid(见第四章)
application.yml
server:
port: 9011
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
username: root
password: Sunlu1994
url: jdbc:mysql://localhost:3306/test?characterEncoding=utf-8
type: com.alibaba.druid.pool.DruidDataSource
max-active: 20 #最大活跃数
initial-size: 1 #初始化数量
max-wait: 60000 #最大连接等待时间
filters: stat #配置监控统计拦截的filters,去掉后监控界面的sql无法统计,wall用于防火墙
minIdle: 1
poolPreparedStatements: true #打开PSCache
maxOpenPreparedStatements: 20 #指定每个连接的PSCache大小
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 1 from dual
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 #打开mergeSql功能
jpa:
hibernate:
ddl-auto: update
show-sql: true
数据库中建表:
create table t_logger_infos(ali_id int primary key auto_increment,ali_client_ip varchar(20),ali_uri varchar(100),ali_type varchar(20),ali_method varchar(10),ali_param_data longtext,ali_session_id varchar(50),ali_time timestamp,ali_return_time varchar(50),ali_return_data longtext,ali_http_status_code varchar(10),ali_time_consuming int);
创建表的实体类
LoggerEntity.java
@Entity
@Table(name = "t_logger_infos")
public class LoggerEntity implements Serializable {
@Id
@GeneratedValue
@Column(name = "ali_id")
private Integer id;
@Column(name = "ali_client_ip")
private String ip;
@Column(name = "ali_uri")
private String uri;
@Column(name = "ali_type")
private String type;
@Column(name = "ali_method")
private String method;
@Column(name = "ali_param_data")
private String paramData;
@Column(name = "ali_session_id")
private String sessionId;
@Column(name = "ali_time")
private Timestamp time;
@Column(name = "ali_return_time")
private String returnTime;
@Column(name = "ali_return_data")
private String returnData;
@Column(name = "ali_http_status_code")
private String httpStatusCode;
@Column(name = "ali_time_consuming")
private Integer timeConsuming;
@Override
public String toString() {
return "LoggerEntity{" +
"id=" + id +
", ip='" + ip + '\'' +
", uri='" + uri + '\'' +
", type='" + type + '\'' +
", method='" + method + '\'' +
", paramData='" + paramData + '\'' +
", sessionId='" + sessionId + '\'' +
", time=" + time +
", returnTime='" + returnTime + '\'' +
", returnData='" + returnData + '\'' +
", httpStatusCode='" + httpStatusCode + '\'' +
", timeConsuming=" + timeConsuming +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
LoggerEntity that = (LoggerEntity) o;
return Objects.equals(id, that.id) &&
Objects.equals(ip, that.ip) &&
Objects.equals(uri, that.uri) &&
Objects.equals(type, that.type) &&
Objects.equals(method, that.method) &&
Objects.equals(paramData, that.paramData) &&
Objects.equals(sessionId, that.sessionId) &&
Objects.equals(time, that.time) &&
Objects.equals(returnTime, that.returnTime) &&
Objects.equals(returnData, that.returnData) &&
Objects.equals(httpStatusCode, that.httpStatusCode) &&
Objects.equals(timeConsuming, that.timeConsuming);
}
@Override
public int hashCode() {
return Objects.hash(id, ip, uri, type, method, paramData, sessionId, time, returnTime, returnData, httpStatusCode, timeConsuming);
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public String getParamData() {
return paramData;
}
public void setParamData(String paramData) {
this.paramData = paramData;
}
public String getSessionId() {
return sessionId;
}
public void setSessionId(String sessionId) {
this.sessionId = sessionId;
}
public Timestamp getTime() {
return time;
}
public void setTime(Timestamp time) {
this.time = time;
}
public String getReturnTime() {
return returnTime;
}
public void setReturnTime(String returnTime) {
this.returnTime = returnTime;
}
public String getReturnData() {
return returnData;
}
public void setReturnData(String returnData) {
this.returnData = returnData;
}
public String getHttpStatusCode() {
return httpStatusCode;
}
public void setHttpStatusCode(String httpStatusCode) {
this.httpStatusCode = httpStatusCode;
}
public Integer getTimeConsuming() {
return timeConsuming;
}
public void setTimeConsuming(Integer timeConsuming) {
this.timeConsuming = timeConsuming;
}
}
创建LoggerJPA实现JpaRepository
LoggerJpa.java
@Repository
public interface LoggerJpa extends JpaRepository<LoggerEntity,Integer> {
}
创建日志拦截器
我们上面的步骤有关请求日志的存储已经编写完成,那么我们接下来需要编写一个请求日志的拦截器,自定义SpringMVC拦截器需要实现HandlerIntercptor接口,并且实现内部的三个方法
LoggerInterceptor.java
//创建拦截器 要实现HandlerInterceptror接口
public class LoggerInterceptor implements HandlerInterceptor {
//请求开始时间标记
private static final String LOGGER_SEND_TIME="_send_time";
//请求日志实体标记
private static final String LOGGER_ENTITY="_logger_entity";
//调用请求的时候执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
//创建请求实体
LoggerEntity loggerEntity = new LoggerEntity();
//获取请求的sessionId
String sessionId = request.getRequestedSessionId();
//请求的路径
String path = request.getRequestURI();
//获取请求参数信息
String paramData = JSON.toJSONString(request.getParameterMap(),SerializerFeature.DisableCircularReferenceDetect,SerializerFeature.WriteMapNullValue);
//设置客户端ip
loggerEntity.setIp(LoggerUtils.getCliectIp(request));
//设置请求方法
loggerEntity.setMethod(request.getMethod());
//设置请求类型
loggerEntity.setType(LoggerUtils.getRequestType(request));
//设置请求参数的json字符串
loggerEntity.setParamData(paramData);
//设置请求地址
loggerEntity.setUri(path);
//设置sessionId
loggerEntity.setSessionId(sessionId);
//设置请求开始时间
request.setAttribute(LOGGER_SEND_TIME,System.currentTimeMillis());
//设置请求实体到request中,方便after调用
request.setAttribute(LOGGER_ENTITY,loggerEntity);
return true;
}
//controller调用之后执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
}
//viewResolve返回view到前台之前执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
//获取请求错误码
int status = response.getStatus();
//当前时间
long currentTime = System.currentTimeMillis();
//请求开始时间
long time = Long.parseLong(request.getAttribute(LOGGER_SEND_TIME).toString());
//获取本次请求日志实体
LoggerEntity loggerEntity = (LoggerEntity) request.getAttribute(LOGGER_ENTITY);
//设置请求时间差
loggerEntity.setTimeConsuming(Integer.valueOf(String.valueOf((currentTime-time))));
//设置返回时间
loggerEntity.setReturnTime(String.valueOf(currentTime));
//设置返回错误码
loggerEntity.setHttpStatusCode(String.valueOf(status));
//设置返回值
loggerEntity.setReturnData(JSON.toJSONString(LoggerUtils.LOGGER_RETURN,SerializerFeature.DisableCircularReferenceDetect,SerializerFeature.WriteMapNullValue));
//将日志写入到数据库
LoggerJpa loggerJpa = getDAO(LoggerJpa.class,request);
loggerJpa.save(loggerEntity);
System.out.println("写入成功");
}
//我们在拦截器内无法通过SpringBean的方式注入LoggerJPA,我只能通过另外一种形式。
//WebApplicationContextUtils
//这个工具类可以通过HttpServletRequest请求对象的上下文(ServetCotext)获取Spring管理的Bean
/*
创建了一个getDAO的方法,方法需要传入一个实体的类型,以及一个HttpServetRequest请求对象,
通过WebApplicationContextUtils内部的getRequiredWebApplicationContext方法获取到BeanFactory(实体工厂类)
,从而通过工厂实体的getBean方法就可以拿到SpringDataJPA为我们管理的LoggerJPA持久化数据接口实例。
*/
private <T> T getDAO(Class<T> clazz,HttpServletRequest request)
{
BeanFactory factory = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
return factory.getBean(clazz);
}
}
这里需要注意一点,我们在拦截器内无法通过SpringBean的方式注入LoggerJPA,我只能通过另外一种形式。(getDAO方法获取bean)
创建LoggerUtil工具类
LoggerUtils.java
public final class LoggerUtils {
public static final String LOGGER_RETURN = "_logger_return";
private LoggerUtils() {}
/**
* 获取客户端ip地址
* @param request
* @return
*/
public static String getCliectIp(HttpServletRequest request)
{
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.trim() == "" || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.trim() == "" || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.trim() == "" || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
// 多个路由时,取第一个非unknown的ip
final String[] arr = ip.split(",");
for (final String str : arr) {
if (!"unknown".equalsIgnoreCase(str)) {
ip = str;
break;
}
}
return ip;
}
/**
* 判断是否为ajax请求
* @param request
* @return
*/
public static String getRequestType(HttpServletRequest request) {
return request.getHeader("X-Requested-With");
}
}
我们处理日志请求时需要用到FastJson、HttpServet依赖,所以我们修改pom.xml配置文件加入FastJson开源组件以及HttpServlet的maven依赖
<!--HttpServet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax-servlet-api</artifactId>
</dependency>
<!--fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.15</version>
</dependency>
编写测试控制器在入口类中定义/login
@SpringBootApplication
@RestController
public class Chapter8Application {
public static void main(String[] args) {
SpringApplication.run(Chapter8Application.class, args);
}
//添加druid的sql监控
@Bean
@ConfigurationProperties(prefix="spring.datasource")
public DataSource druidDataSource() {
return new DruidDataSource();
}
@RequestMapping("/login")
public JSONObject login(HttpServletRequest request, String name){
JSONObject jsonObject = new JSONObject();
jsonObject.put("msg","登陆成功");
request.setAttribute(LoggerUtils.LOGGER_RETURN,jsonObject);
return jsonObject;
}
}
配置拦截器(要继承WebMvcConfigurerAdapter)
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggerInterceptor()).addPathPatterns("/**");
}
}
请求地址:http://127.0.0.1:9011/login?name=sun
链接:https://www.jianshu.com/p/890c23a1b3d7
來源:简书