欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-03-03 12:20:24
日志记录到redis展现形式1.基于注解的方式实现日志记录,扫描对应的方法实现日志记录@inherited@retention(retentionpolicy.runtime)@target({ele...

日志记录到redis展现形式

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

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

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

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.扫描的方法,基于注解实现方法扫描并且记录日志

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

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;
        }
    }
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。