背景
之前的文章中,对日志记录做过简单的介绍。日常开发中,我们常用的日志有两种,一种是业务日志,该类型主要用于记录系统中某些业务的变化或属性的改变,比如业务流转过程中记录状态的变化或对象属性的变化,此类型主要用于查询业务轨迹使用;另一种是系统日志,主要记录方法的调用信息,比如方法名称,参数以及调用者的名称、ip、调用时间等信息。本文结合项目中的实际情况,介绍下如何使用Spring的aop原理实现系统级日志管理。
核心代码
1.声明系统日志切面类并交给Spring容器管理
代码如下:
@Aspect
@Component
public class SysLogAspect {
@Autowired
private SysLogService sysLogService;
@Pointcut("execution(* com.xx.xx.*.service.*.*(..))")
public void logPointCut() {
}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
long beginTime = System.currentTimeMillis();
// 执行方法
Object result = point.proceed();
// 执行时长(毫秒)
long time = System.currentTimeMillis() - beginTime;
// 保存日志
saveSysLog(point, time);
return result;
}
private void saveSysLog(ProceedingJoinPoint joinPoint, long time) {
String userName = BaseController.getUsername();//BaseController为项目中的基类,主要存储用户登录后的一些基本信息,该类可有可无,根据自己的实际情况而定
//判断是否SysLogService的调用操作,SysLogService本身不再记录日志
if(joinPoint.getTarget() instanceof SysLogService) { //SysLogService,自己声明的系统日志接口
return ;
}
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
SysLog sysLog = new SysLog();
// 请求的方法名
String className = joinPoint.getTarget().getClass().getName();
String methodName = signature.getName();
sysLog.setMethod(className + "." + methodName + "()");
// 请求的参数
Object[] args = joinPoint.getArgs();
try{
String params = JSONObject.toJSONString(args[0]);
sysLog.setParams(params);
} catch (Exception e){
}
// 获取request
HttpServletRequest request = HttpUtils.getHttpServletRequest();
// 设置IP地址
sysLog.setIp(IPUtils.getIpAddr(request));
// 用户名
sysLog.setUserName(userName);
// 执行时长(毫秒)
sysLog.setTime(time);
// 保存系统日志
sysLogService.save(sysLog);
}
}
复制代码
2.声明ip工具类
代码如下:
public class IPUtils {
private static Logger logger = LoggerFactory.getLogger(IPUtils.class);
/**
* 获取IP地址
* 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址
* 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址
*/
public static String getIpAddr(HttpServletRequest request) {
String ip = null;
try {
ip = request.getHeader("x-forwarded-for");
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
} catch (Exception e) {
logger.error("IPUtils ERROR ", e);
}
return ip;
}
}
复制代码
3.声明HttpUtils工具类
代码如下:
public class HttpUtils {
private static final Log log = LogFactory.getLog(HttpUtils.class);
/**
* 获取HttpServletRequest对象
* @return
*/
public static HttpServletRequest getHttpServletRequest() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
}
/**
* 输出信息到浏览器
* @param response
* @throws IOException
*/
public static void print(HttpServletResponse response, int code, String msg) throws IOException {
response.setContentType("application/json; charset=utf-8");
HttpResult result = HttpResult.error(code, msg);
String json = JSONObject.toJSONString(result);
response.getWriter().print(json);
response.getWriter().flush();
response.getWriter().close();
}
public static String sendpost(String serverUrl, String data) {
StringBuilder responseBuilder = null;
BufferedReader reader = null;
OutputStreamWriter wr = null;
String re=null;
try {
URL url = new URL(serverUrl);
URLConnection e = url.openConnection();
e.setDoOutput(true);
e.setConnectTimeout(5000);
wr = new OutputStreamWriter(e.getOutputStream());
wr.write(data);
wr.flush();
reader = new BufferedReader(new InputStreamReader(e.getInputStream()));
responseBuilder = new StringBuilder();
String line = null;
while((line = reader.readLine()) != null) {
responseBuilder.append(line).append("\n");
}
re=responseBuilder.toString();
log.debug("http请求返回结果:"+re);
} catch (IOException e1) {
log.error("", e1);
} finally {
if(wr != null) {
try {
wr.close();
} catch (IOException e2) {
log.error("close error", e2);
}
}
if(reader != null) {
try {
reader.close();
} catch (IOException e3) {
log.error("close error", e3);
}
}
}
return re;
}
}
复制代码
4.测试结果
备注:由于系统被频繁使用,每个接口被调用时都会生成一条数据,所以系统日志表中的数据量会特别大,要定期备份数据并清除一些旧数据,否则表中的数据会越来越多影响性能。