springboot 实现记录业务日志和异常业务日志的操作
程序员文章站
2023-12-29 15:59:22
日志记录到redis展现形式1.基于注解的方式实现日志记录,扫描对应的方法实现日志记录@inherited@retention(retentionpolicy.runtime)@target({ele...
日志记录到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 @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; /** * 对比两个对象的变化的工具类 * * @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 */ @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; } /** * 字符串是否为空,空的定义如下 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.运行时异常的获取
@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; /** * @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; } } }
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。