springboot 实现记录业务日志和异常业务日志的操作
短信预约 -IT技能 免费直播动态提醒
日志记录到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
@Component
public 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;
@Component
public 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.运行时异常的获取
@ControllerAdvice
public 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;
}
}
}
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341