springboot 实现记录业务日志和异常业务日志的操作

日志记录到redis展现形式

1.基于注解的方式实现日志记录,扫描对应的方法实现日志记录

@Inherited@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface BussinessLog {     /**     * 业务的名称,例如:"修改菜单"     */    String value() default "";     /**     * 被修改的实体的唯一标识,例如:菜单实体的唯一标识为"id"     */    String key() default "id";     /**     * 业务类型     */    String type() default "0";     /**     * 字典(用于查找key的中文名称和字段的中文名称)     */    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; /** * 对比两个对象的变化的工具类 * * @author ... * @Date 2017/3/31 10:36 */public class Contrast {     //记录每个修改字段的分隔符    public static final String separator = ";;;";     /**     * 比较两个对象,并返回不一致的信息     *     * @author ...     * @Date 2017/5/9 19:34     */    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;    }     /**     * 比较两个对象pojo1和pojo2,并输出不一致信息     *     * @author ...     * @Date 2017/5/9 19:34     */    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;    }     /**     * 比较两个对象pojo1和pojo2,并输出不一致信息     *     * @author ...     * @Date 2017/5/9 19:34     */    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;    }     /**     * 解析多个key(逗号隔开的)     *     * @author ...     * @Date 2017/5/16 22:19     */    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; /** * Spring的ApplicationContext的持有者,可以用静态方法的方式获取spring容器中的bean */@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;    }    /**     * 字符串是否为空,空的定义如下 1、为null <br>     * 2、为""<br>     */    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; /** * @Description: * @Auther: wj * @Date: 2019/5/28 13:56 */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 实现记录业务日志和异常业务日志的操作

相关文章:

你感兴趣的文章:

标签云: