今天就跟大家聊聊有关springboot中怎么实现记录业务日志和异常业务日志操作,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。
日志记录到redis展现形式
1.基于注解的方式实现日志记录,扫描对应的方法实现日志记录
@Inherited@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface BussinessLog { String value() default ""; String key() default "id"; String type() default "0"; Class<? extends AbstractDictMap> dict() default SystemDict.class; }
2.扫描的方法,基于注解实现方法扫描并且记录日志
3.基于@Aspect注解,实现日志扫描,并且记录日志
import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.Signature;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.aspectj.lang.reflect.MethodSignature;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Component; import java.lang.reflect.Method;import java.util.Map; @Aspect@Componentpublic class LogAop { private Logger log = LoggerFactory.getLogger(this.getClass()); @Pointcut(value = "@annotation(com.stylefeng.guns.core.common.annotion.BussinessLog)") public void cutService() { } @Around("cutService()") public Object recordSysLog(ProceedingJoinPoint point) throws Throwable { //先执行业务 Object result = point.proceed(); try { handle(point); } catch (Exception e) { log.error("日志记录出错!", e); } return result; } private void handle(ProceedingJoinPoint point) throws Exception { //获取拦截的方法名 Signature sig = point.getSignature(); MethodSignature msig = null; if (!(sig instanceof MethodSignature)) { throw new IllegalArgumentException("该注解只能用于方法"); } msig = (MethodSignature) sig; Object target = point.getTarget(); Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes()); String methodName = currentMethod.getName(); //如果当前用户未登录,不做日志 ShiroUser user = ShiroKit.getUser(); if (null == user) { return; } //获取拦截方法的参数 String className = point.getTarget().getClass().getName(); Object[] params = point.getArgs(); //获取操作名称 BussinessLog annotation = currentMethod.getAnnotation(BussinessLog.class); String bussinessName = annotation.value(); String key = annotation.key(); Class dictClass = annotation.dict(); StringBuilder sb = new StringBuilder(); for (Object param : params) { sb.append(param); sb.append(" & "); } //如果涉及到修改,比对变化 String msg; if (bussinessName.contains("修改") || bussinessName.contains("编辑")) { Object obj1 = LogObjectHolder.me().get(); Map<String, String> obj2 = HttpContext.getRequestParameters(); msg = Contrast.contrastObj(dictClass, key, obj1, obj2); } else { Map<String, String> parameters = HttpContext.getRequestParameters(); AbstractDictMap dictMap = (AbstractDictMap) dictClass.newInstance(); msg = Contrast.parseMutiKey(dictMap, key, parameters); } log.info("[记录日志][RESULT:{}]",user.getId()+bussinessName+className+methodName+msg.toString()); LogManager.me().executeLog(LogTaskFactory.bussinessLog(user.getId(), bussinessName, className, methodName, msg)); }}
4.比较两个对象的工具类
import java.beans.PropertyDescriptor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.Date;import java.util.Map; public class Contrast { //记录每个修改字段的分隔符 public static final String separator = ";;;"; public static String contrastObj(Object pojo1, Object pojo2) { String str = ""; try { Class clazz = pojo1.getClass(); Field[] fields = pojo1.getClass().getDeclaredFields(); int i = 1; for (Field field : fields) { if ("serialVersionUID".equals(field.getName())) { continue; } PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz); Method getMethod = pd.getReadMethod(); Object o1 = getMethod.invoke(pojo1); Object o2 = getMethod.invoke(pojo2); if (o1 == null || o2 == null) { continue; } if (o1 instanceof Date) { o1 = DateUtil.getDay((Date) o1); } if (!o1.toString().equals(o2.toString())) { if (i != 1) { str += separator; } str += "字段名称" + field.getName() + ",旧值:" + o1 + ",新值:" + o2; i++; } } } catch (Exception e) { e.printStackTrace(); } return str; } public static String contrastObj(Class dictClass, String key, Object pojo1, Map<String, String> pojo2) throws IllegalAccessException, InstantiationException { AbstractDictMap dictMap = (AbstractDictMap) dictClass.newInstance(); String str = parseMutiKey(dictMap, key, pojo2) + separator; try { Class clazz = pojo1.getClass(); Field[] fields = pojo1.getClass().getDeclaredFields(); int i = 1; for (Field field : fields) { if ("serialVersionUID".equals(field.getName())) { continue; } PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz); Method getMethod = pd.getReadMethod(); Object o1 = getMethod.invoke(pojo1); Object o2 = pojo2.get(StrKit.firstCharToLowerCase(getMethod.getName().substring(3))); if (o1 == null || o2 == null) { continue; } if (o1 instanceof Date) { o1 = DateUtil.getDay((Date) o1); } else if (o1 instanceof Integer) { o2 = Integer.parseInt(o2.toString()); } if (!o1.toString().equals(o2.toString())) { if (i != 1) { str += separator; } String fieldName = dictMap.get(field.getName()); String fieldWarpperMethodName = dictMap.getFieldWarpperMethodName(field.getName()); if (fieldWarpperMethodName != null) { Object o1Warpper = DictFieldWarpperFactory.createFieldWarpper(o1, fieldWarpperMethodName); Object o2Warpper = DictFieldWarpperFactory.createFieldWarpper(o2, fieldWarpperMethodName); str += "字段名称:" + fieldName + ",旧值:" + o1Warpper + ",新值:" + o2Warpper; } else { str += "字段名称:" + fieldName + ",旧值:" + o1 + ",新值:" + o2; } i++; } } } catch (Exception e) { e.printStackTrace(); } return str; } public static String contrastObjByName(Class dictClass, String key, Object pojo1, Map<String, String> pojo2) throws IllegalAccessException, InstantiationException { AbstractDictMap dictMap = (AbstractDictMap) dictClass.newInstance(); String str = parseMutiKey(dictMap, key, pojo2) + separator; try { Class clazz = pojo1.getClass(); Field[] fields = pojo1.getClass().getDeclaredFields(); int i = 1; for (Field field : fields) { if ("serialVersionUID".equals(field.getName())) { continue; } String prefix = "get"; int prefixLength = 3; if (field.getType().getName().equals("java.lang.Boolean")) { prefix = "is"; prefixLength = 2; } Method getMethod = null; try { getMethod = clazz.getDeclaredMethod(prefix + StrKit.firstCharToUpperCase(field.getName())); } catch (NoSuchMethodException e) { System.err.println("this className:" + clazz.getName() + " is not methodName: " + e.getMessage()); continue; } Object o1 = getMethod.invoke(pojo1); Object o2 = pojo2.get(StrKit.firstCharToLowerCase(getMethod.getName().substring(prefixLength))); if (o1 == null || o2 == null) { continue; } if (o1 instanceof Date) { o1 = DateUtil.getDay((Date) o1); } else if (o1 instanceof Integer) { o2 = Integer.parseInt(o2.toString()); } if (!o1.toString().equals(o2.toString())) { if (i != 1) { str += separator; } String fieldName = dictMap.get(field.getName()); String fieldWarpperMethodName = dictMap.getFieldWarpperMethodName(field.getName()); if (fieldWarpperMethodName != null) { Object o1Warpper = DictFieldWarpperFactory.createFieldWarpper(o1, fieldWarpperMethodName); Object o2Warpper = DictFieldWarpperFactory.createFieldWarpper(o2, fieldWarpperMethodName); str += "字段名称:" + fieldName + ",旧值:" + o1Warpper + ",新值:" + o2Warpper; } else { str += "字段名称:" + fieldName + ",旧值:" + o1 + ",新值:" + o2; } i++; } } } catch (Exception e) { e.printStackTrace(); } return str; } public static String parseMutiKey(AbstractDictMap dictMap, String key, Map<String, String> requests) { StringBuilder sb = new StringBuilder(); if (key.indexOf(",") != -1) { String[] keys = key.split(","); for (String item : keys) { String fieldWarpperMethodName = dictMap.getFieldWarpperMethodName(item); String value = requests.get(item); if (fieldWarpperMethodName != null) { Object valueWarpper = DictFieldWarpperFactory.createFieldWarpper(value, fieldWarpperMethodName); sb.append(dictMap.get(item) + "=" + valueWarpper + ","); } else { sb.append(dictMap.get(item) + "=" + value + ","); } } return StrKit.removeSuffix(sb.toString(), ","); } else { String fieldWarpperMethodName = dictMap.getFieldWarpperMethodName(key); String value = requests.get(key); if (fieldWarpperMethodName != null) { Object valueWarpper = DictFieldWarpperFactory.createFieldWarpper(value, fieldWarpperMethodName); sb.append(dictMap.get(key) + "=" + valueWarpper); } else { sb.append(dictMap.get(key) + "=" + value); } return sb.toString(); } } }
5.根据输入方法获取数据字典的数据
import java.lang.reflect.Method; public class DictFieldWarpperFactory { public static Object createFieldWarpper(Object parameter, String methodName) { IConstantFactory constantFactory = ConstantFactory.me(); try { Method method = IConstantFactory.class.getMethod(methodName, parameter.getClass()); return method.invoke(constantFactory, parameter); } catch (Exception e) { try { Method method = IConstantFactory.class.getMethod(methodName, Integer.class); return method.invoke(constantFactory, Integer.parseInt(parameter.toString())); } catch (Exception e1) { throw new RuntimeException("BizExceptionEnum.ERROR_WRAPPER_FIELD"); } } } }
6.对应获取数据字典的方法
public interface IConstantFactory { String getWordStatus(Integer DATA_STATUS); }
import com.qihoinfo.dev.log.util.SpringContextHolder;import org.anyline.service.AnylineService;import org.springframework.context.annotation.DependsOn;import org.springframework.stereotype.Component; @Component@DependsOn("springContextHolder")public class ConstantFactory implements IConstantFactory { private AnylineService anylineService = SpringContextHolder.getBean(AnylineService.class); public static IConstantFactory me() { return SpringContextHolder.getBean("constantFactory"); } @Override public String getWordStatus(Integer DATA_STATUS) { if ("1".equals(DATA_STATUS.toString())) { return "启用"; } else { return "停用"; } } }
7.spring根据方法名获取对应容器中的对象
import org.springframework.beans.BeansException;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import org.springframework.stereotype.Component; @Componentpublic class SpringContextHolder implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringContextHolder.applicationContext = applicationContext; } public static ApplicationContext getApplicationContext() { assertApplicationContext(); return applicationContext; } @SuppressWarnings("unchecked") public static <T> T getBean(String beanName) { assertApplicationContext(); return (T) applicationContext.getBean(beanName); } public static <T> T getBean(Class<T> requiredType) { assertApplicationContext(); return applicationContext.getBean(requiredType); } private static void assertApplicationContext() { if (SpringContextHolder.applicationContext == null) { throw new RuntimeException("applicaitonContext属性为null,请检查是否注入了SpringContextHolder!"); } } }
8.字符串工具类
public class StrKit { public static String firstCharToLowerCase(String str) { char firstChar = str.charAt(0); if (firstChar >= 'A' && firstChar <= 'Z') { char[] arr = str.toCharArray(); arr[0] += ('a' - 'A'); return new String(arr); } return str; } public static String firstCharToUpperCase(String str) { char firstChar = str.charAt(0); if (firstChar >= 'a' && firstChar <= 'z') { char[] arr = str.toCharArray(); arr[0] -= ('a' - 'A'); return new String(arr); } return str; } public static String removeSuffix(String str, String suffix) { if (isEmpty(str) || isEmpty(suffix)) { return str; } if (str.endsWith(suffix)) { return str.substring(0, str.length() - suffix.length()); } return str; } public static boolean isEmpty(String str) { return str == null || str.length() == 0; }}
import java.io.IOException;import java.io.PrintWriter;import java.io.StringWriter; public class ToolUtil { public static final int SALT_LENGTH = 6; public ToolUtil() { } public static String getExceptionMsg(Throwable e) { StringWriter sw = new StringWriter(); try { e.printStackTrace(new PrintWriter(sw)); } finally { try { sw.close(); } catch (IOException var8) { var8.printStackTrace(); } } return sw.getBuffer().toString().replaceAll("\\$", "T"); }}
9.获取数据字典的类
import java.util.HashMap; public abstract class AbstractDictMap { protected HashMap<String, String> dictory = new HashMap<>(); protected HashMap<String, String> fieldWarpperDictory = new HashMap<>(); public AbstractDictMap() { put("ID", "主键ID"); init(); initBeWrapped(); } public abstract void init(); protected abstract void initBeWrapped(); public String get(String key) { return this.dictory.get(key); } public void put(String key, String value) { this.dictory.put(key, value); } public String getFieldWarpperMethodName(String key) { return this.fieldWarpperDictory.get(key); } public void putFieldWrapperMethodName(String key, String methodName) { this.fieldWarpperDictory.put(key, methodName); }}
public class SystemDict extends AbstractDictMap { @Override public void init() { } @Override protected void initBeWrapped() { }}
public class WordMap extends AbstractDictMap { @Override public void init() { put("EN", "英文"); put("CN", "中文"); put("SHORT", "简称"); put("REMARK", "备注"); put("DATA_STATUS", "状态"); } @Override protected void initBeWrapped() { putFieldWrapperMethodName("DATA_STATUS","getWordStatus"); } }
10.获取缓存对象的bean
import org.springframework.context.annotation.Scope;import org.springframework.stereotype.Component;import org.springframework.web.context.WebApplicationContext; import java.io.Serializable; @Component@Scope(scopeName = WebApplicationContext.SCOPE_SESSION)public class LogObjectHolder implements Serializable{ private Object object = null; public void set(Object obj) { this.object = obj; } public Object get() { return object; } public static LogObjectHolder me(){ LogObjectHolder bean = SpringContextHolder.getBean(LogObjectHolder.class); return bean; } }
11.运行时异常的获取
@ControllerAdvicepublic class GlobalExceptionHandler extends BasicMemberJSONController { private Logger log = LoggerFactory.getLogger(this.getClass()); @ExceptionHandler(RuntimeException.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ResponseBody public String notFount(RuntimeException e) { String userName = curManage().get("USERNAME").toString(); LogManager.me().executeLog(LogTaskFactory.exceptionLog(userName, e)); log.error("运行时异常:", e); return fail(); }}
12.使用线程池创建操作日志
import java.util.Date; public class LogFactory { public static DataRow createOperationLog(LogType logType, String userName, String bussinessName, String clazzName, String methodName, String msg, LogSucceed succeed) { DataRow operationLog = new DataRow(); operationLog.put("log_type", logType.getMessage()); operationLog.put("USER_NAME", userName); operationLog.put("log_name", bussinessName); operationLog.put("CLASS_NAME", clazzName); operationLog.put("METHOD", methodName); operationLog.put("CREATE_TIME", new Date()); operationLog.put("SUCCEED", succeed.getMessage()); if (msg.length() > 800) { msg = msg.substring(0, 800); operationLog.put("MESSAGE", msg); } else { operationLog.put("MESSAGE", msg); } return operationLog; }}
import java.util.TimerTask;import java.util.concurrent.ScheduledThreadPoolExecutor;import java.util.concurrent.TimeUnit; public class LogManager { //日志记录操作延时 private final int OPERATE_DELAY_TIME = 10; //异步操作记录日志的线程池 private ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(10); private LogManager() { } public static LogManager logManager = new LogManager(); public static LogManager me() { return logManager; } public void executeLog(TimerTask task) { executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS); } }
public enum LogSucceed { SUCCESS("成功"), FAIL("失败"); String message; LogSucceed(String message) { this.message = message; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
import com.qihoinfo.dev.log.annotation.RedisDb;import com.qihoinfo.dev.log.util.ToolUtil;import org.anyline.entity.DataRow;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.context.annotation.DependsOn;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.stereotype.Component; import java.util.TimerTask; @Component@DependsOn("springContextHolder")public class LogTaskFactory { private static Logger logger = LoggerFactory.getLogger(LogManager.class); private static StringRedisTemplate redisTemplate = RedisDb.getMapper(StringRedisTemplate.class); public static TimerTask bussinessLog(final String userName, final String bussinessName, final String clazzName, final String methodName, final String msg) { return new TimerTask() { @Override public void run() { DataRow operationLog = LogFactory.createOperationLog( LogType.BUSSINESS, userName, bussinessName, clazzName, methodName, msg, LogSucceed.SUCCESS); try { redisTemplate.opsForList().rightPush("sys_operation_log", operationLog.getJson()); } catch (Exception e) { logger.error("创建业务日志异常!", e); } } }; } public static TimerTask exceptionLog(final String userName, final Exception exception) { return new TimerTask() { @Override public void run() { String msg = ToolUtil.getExceptionMsg(exception); DataRow operationLog = LogFactory.createOperationLog( LogType.EXCEPTION, userName, "", null, null, msg, LogSucceed.FAIL); try { redisTemplate.opsForList().rightPush("sys_operation_log", operationLog.getJson()); } catch (Exception e) { logger.error("创建异常日志异常!", e); } } }; } }
public enum LogType { EXCEPTION("异常日志"), BUSSINESS("业务日志"); String message; LogType(String message) { this.message = message; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
13.将日志记录到redis数据库
package com.qihoinfo.dev.log.annotation; import com.qihoinfo.dev.log.util.SpringContextHolder;import org.springframework.data.redis.core.StringRedisTemplate; public class RedisDb<T> { private Class<T> clazz; private StringRedisTemplate baseMapper; private RedisDb(Class clazz) { this.clazz = clazz; this.baseMapper = (StringRedisTemplate) SpringContextHolder.getBean(clazz); } public static <T> RedisDb<T> create(Class<T> clazz) { return new RedisDb<T>(clazz); } public StringRedisTemplate getMapper() { return this.baseMapper; } public static <T> T getMapper(Class<T> clazz) { return SpringContextHolder.getBean(clazz); } }
import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.util.Enumeration;import java.util.HashMap;import java.util.Map; public class HttpContext { public HttpContext() { } public static String getIp() { HttpServletRequest request = getRequest(); return request == null ? "127.0.0.1" : request.getRemoteHost(); } public static HttpServletRequest getRequest() { ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); return requestAttributes == null ? null : requestAttributes.getRequest(); } public static HttpServletResponse getResponse() { ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); return requestAttributes == null ? null : requestAttributes.getResponse(); } public static Map<String, String> getRequestParameters() { HashMap<String, String> values = new HashMap(); HttpServletRequest request = getRequest(); if (request == null) { return values; } else { Enumeration enums = request.getParameterNames(); while (enums.hasMoreElements()) { String paramName = (String) enums.nextElement(); String paramValue = request.getParameter(paramName); values.put(paramName, paramValue); } return values; } }}
看完上述内容,你们对springboot中怎么实现记录业务日志和异常业务日志操作有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注编程网行业资讯频道,感谢大家的支持。