相信各位同道在写代码的时候,肯定会写一些日志打印,因为这对往后的运维而言,至关重要的。

那么我们请求一个restfull接口的时候,哪些信息是应该被日志记录的呢?

以下做了一个基本的简单例子,这里只是示例说明基本常规实现记录的信息,根据项目的真实情况选用 :

1 . Http请求拦截器(Filter) : 从 HttpServletRequest获取基本的请求信息

package name.ealen.config;

import name.ealen.util.CommonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order; import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.UUID; /**
* Created by EalenXie on 2018/9/7 15:56.
* Http请求拦截器,日志打印请求相关信息
*/
@Configuration
public class FilterConfiguration { private static final Logger log = LoggerFactory.getLogger(FilterConfig.class); @Bean
@Order(Integer.MIN_VALUE)
@Qualifier("filterRegistration")
public FilterRegistrationBean filterRegistration() {
FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
registration.setFilter(controllerFilter());
registration.addUrlPatterns("/*");
return registration;
} private Filter controllerFilter() {
return new Filter() {
@Override
public void init(FilterConfig filterConfig) {
log.info("ControllerFilter init Success");
} @Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String requestId = request.getHeader("Request-Id");
if (requestId == null) requestId = request.getRequestedSessionId();
if (requestId == null) requestId = UUID.randomUUID().toString();
if (!"OPTIONS".equalsIgnoreCase(request.getMethod())) {
System.out.println();
log.info("Http Request Request-Id : " + requestId);
log.info("Http Request Information : {\"URI\":\"" + request.getRequestURL() +
"\",\"RequestMethod\":\"" + request.getMethod() +
"\",\"ClientIp\":\"" + CommonUtil.getIpAddress(request) +
"\",\"Content-Type\":\"" + request.getContentType() +
"\",\"UserAgent\":\"" + request.getHeader("user-agent") +
"\"}");
}
chain.doFilter(request, response);
} @Override
public void destroy() {
log.info("ControllerFilter destroy");
}
};
}
}

2 . Controller的拦截AOP : 获取 请求的对象,请求参数,返回数据,请求返回状态,内部方法耗时。

package name.ealen.config;

import com.alibaba.fastjson.JSON;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component; import javax.annotation.Resource; /**
* Created by EalenXie on 2018/9/7 14:19.
* AOP打印日志 : 请求的对象,请求参数,返回数据,请求状态,内部方法耗时
*/
@Aspect
@Component
public class ControllerInterceptor { private static final Logger log = LoggerFactory.getLogger(ControllerInterceptor.class); @Resource
private Environment environment; private String getAppName() {
try {
return environment.getProperty("spring.application.name");
} catch (Exception ignore) {
return "unnamed";
}
} /**
* 注意 : pjp.proceed()执行的异常请务必抛出,交由ControllerAdvice捕捉到并处理
*/
@Around(value = "execution (* name.ealen.web.*.*(..))")
public Object processApiFacade(ProceedingJoinPoint pjp) throws Throwable {
long startTime = System.currentTimeMillis();
String name = pjp.getTarget().getClass().getSimpleName();
String method = pjp.getSignature().getName();
Object result;
try {
result = pjp.proceed();
log.info("RequestTarget : " + getAppName() + "." + name + "." + method);
Object[] requestParams = pjp.getArgs();
if (requestParams.length > 0) { //日志打印请求参数
try {
log.info("RequestParam : {}", JSON.toJSON(requestParams));
} catch (Exception e) {
for (Object param : requestParams) {
try {
log.info("RequestParam : {}", JSON.toJSON(param));
} catch (Exception ig) {
log.info("RequestParam : {}", param.toString());
}
}
}
}
} finally {
log.info("Internal Method Cost Time: {}ms", System.currentTimeMillis() - startTime);
}
return result;
} }

3 . 全局的异常处理返回Advice :

package name.ealen.config;

import com.alibaba.fastjson.JSON;
import name.ealen.util.CommonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException; import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map; /**
* Created by EalenXie on 2018/11/8 16:25.
* 全局异常、错误返回处理
*/
@ControllerAdvice
public class ControllerExceptionListener { private final Logger log = LoggerFactory.getLogger(ControllerExceptionListener.class); @ExceptionHandler(value = Throwable.class)
public ResponseEntity Throwable(Throwable throwable, HttpServletRequest request) {
Map<String, String> resultMap = getThrowable(throwable);
if (request != null) {
Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
resultMap.put("Requester-Ip", CommonUtil.getIpAddress(request));
resultMap.put("Requester-Agent", request.getHeader("user-agent"));
if (statusCode != null) {
new ResponseEntity<>(JSON.toJSON(resultMap).toString(), HttpStatus.valueOf(statusCode));
}
}
return new ResponseEntity<>(JSON.toJSON(resultMap).toString(), HttpStatus.INTERNAL_SERVER_ERROR);
} @ExceptionHandler(value = HttpServerErrorException.class)
public ResponseEntity HttpServerErrorException(HttpServerErrorException serverError) {
Map<String, String> resultMap = getThrowable(serverError);
HttpStatus status = serverError.getStatusCode();
resultMap.put("responseBody", "" + serverError.getResponseBodyAsString());
resultMap.put("statusCode", "" + status.toString());
resultMap.put("statusText", "" + serverError.getStatusText());
resultMap.put("statusReasonPhrase", "" + status.getReasonPhrase());
return new ResponseEntity<>(JSON.toJSON(resultMap).toString(), status);
} @ExceptionHandler(value = HttpClientErrorException.class)
public ResponseEntity HttpClientErrorException(HttpClientErrorException clientError) {
Map<String, String> resultMap = getThrowable(clientError);
HttpStatus status = clientError.getStatusCode();
resultMap.put("responseBody", "" + clientError.getResponseBodyAsString());
resultMap.put("statusCode", "" + clientError.getStatusCode().toString());
resultMap.put("statusText", "" + clientError.getStatusText());
resultMap.put("statusReasonPhrase", "" + status.getReasonPhrase());
return new ResponseEntity<>(JSON.toJSON(resultMap).toString(), status);
} /**
* 公共异常信息
*/
private Map<String, String> getThrowable(Throwable throwable) {
Map<String, String> resultMap = new HashMap<>();
resultMap.put("throwable", "" + throwable);
resultMap.put("throwableTime", "" + CommonUtil.getCurrentDateTime());
resultMap.put("message", "" + throwable.getMessage());
resultMap.put("localizedMessage", "" + throwable.getLocalizedMessage());
log.error("Exception : {}", JSON.toJSON(resultMap));
throwable.printStackTrace();
return resultMap;
}
}

4 . 提供一个简单的restfull接口 :

package name.ealen.web;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; /**
* Created by EalenXie on 2018/9/7 14:24.
*/
@RestController
public class SayHelloController { @RequestMapping("/sayHello")
public String sayHello() {
return "hello world";
} @RequestMapping("/say")
public ResponseEntity<?> say(@RequestBody Object o) {
return new ResponseEntity<>(o, HttpStatus.OK);
} }

4 . 使用Postman进行基本测试 :

5 . 控制台可以看到基本效果 :

以上只是关于Controller应该记录日志的一个简单的例子,完整代码可见 https://github.com/EalenXie/springboot-controller-logger

感谢各位提出意见和支持。

AOP与Filter拦截请求打印日志实用例子的更多相关文章

  1. spring aop实现拦截接口请求打印日志

    在spring配置 1编写自己的注解类 2.编写注解解析类 3.配置spring aop代理 (下面我使用注解 如使用配置 配置切点即可,有两种代理默认jdk代理 设置true 为cglib代理) / ...

  2. javaweb利用filter拦截请求

    项目上有个小需求,要限制访问者的IP,屏蔽未授权的登录请求.该场景使用过滤器来做再合适不过了. SecurityFilter.java: package com.lichmama.webdemo.fi ...

  3. spring boot aop打印http请求回复日志包含请求体

    一.引入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...

  4. springboot aop + logback + 统一异常处理 打印日志

    1.src/resources路径下新建logback.xml 控制台彩色日志打印 info日志和异常日志分不同文件存储 每天自动生成日志 结合myibatis方便日志打印(debug模式) < ...

  5. (转)Spring Boot中使用AOP统一处理Web请求日志

    AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是Spring框架中的一个重要内容,它通 ...

  6. 46. Spring Boot中使用AOP统一处理Web请求日志

    在之前一系列的文章中都是提供了全部的代码,在之后的文章中就提供核心的代码进行讲解.有什么问题大家可以给我留言或者加我QQ,进行咨询. AOP为Aspect Oriented Programming的缩 ...

  7. SpringBoot 配置 AOP 打印日志

    在项目开发中,日志系统是必不可少的,用AOP在Web的请求做入参和出参的参数打印,同时对异常进行日志打印,避免重复的手写日志,完整案例见文末源码. 一.Spring AOP AOP(Aspect-Or ...

  8. 转:Spring Boot中使用AOP统一处理Web请求日志

    在spring boot中,简单几步,使用spring AOP实现一个拦截器: 1.引入依赖: <dependency> <groupId>org.springframewor ...

  9. SpringBoot2.0 使用AOP统一处理Web请求日志(完整版)

    一,加入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...

随机推荐

  1. vuex 的基本使用

    工程目录 主要关注store 文件夹下的文件 store/index.js import Vue from 'vue' import Vuex from 'vuex' // import getter ...

  2. CPU使用率过高问题定位

    (1)top 命令 ->查询出CPU使用率最高的 PID编号. (2)top -H p PID编号 ->能查询出所有线程的CPU使用率的列表(线程编号也在PID列). (3)jstack ...

  3. windows10 ubuntu子系统运行docker出现的问题

    前一篇笔记记录了安装的过程及错误处理办法,但是在正式使用docker时,却又出现新的问题: “docker: Cannot connect to the Docker daemon at unix:/ ...

  4. [十一集训] Day1 (2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018))

    A Altruistic Amphibians 原题 题目大意: n只青蛙在高度为d的井中,每只有跳跃距离.重量和高度,每只青蛙可以借助跳到别的青蛙的背上而跳出井,每只青蛙能承受的最大重量是自身重量, ...

  5. springmvc数据的封装

    spring封装是进行orm封装,可以进行定义数据类型,数据名与接收名相同,进行接收,或者定义类,类的属性名与接收名相同 单个数据类型如图下: 对象类型封装: 其他:乱码处理 在中文字符乱码,需要规定 ...

  6. CEF4Delphi 常用设置

    CEF4Delphi是由 SalvadorDíazFau 创建的一个开源项目,用于在基于Delphi的应用程序中嵌入基于Chromium的浏览器. CEF4Delphi 基于Henri Gourves ...

  7. Nvidia Jetson TX2开发板学习历程( 2 )- 更换pip源,提高下载速度

    通过将pip的源更换为国内源,来提高下载速度,这也将成为今后学习过程下载Python包的基础,建议前期一定要完成! 知名的国内源 清华:https://pypi.tuna.tsinghua.edu.c ...

  8. flink ETL数据处理

    Flink ETL 实现数据清洗   一:需求(针对算法产生的日志数据进行清洗拆分) 1. 算法产生的日志数据是嵌套json格式,需要拆分 2.针对算法中的国家字段进行大区转换 3.最后把不同类型的日 ...

  9. python 获取mysql数据库列表以及用户权限

    一.需求分析 需要统计出当前数据库的所有数据库名,以及每个用户的授权信息. 获取所有数据库 在mysql里面,使用命令: show databases 就可以获取所有数据库了 获取所有用户 执行命令: ...

  10. AGC028E High Elements 贪心、DP、线段树

    传送门 看到要求"字典序最小"的方案,一个很直观的想法是按位贪心,那么我们需要check的就是当某一个数放在了第一个序列之后是否还存在方案. 假设当前两个序列的最大值和前缀最值数量 ...