JAVA實現通用日志記錄


原文:http://blog.csdn.net/jinzhencs/article/details/51882751

 

前言: 
之前想在filter層直接過濾httpServerletRequest請求進行日志處理,但是之后再getWriter()的 時候報 
already been call異常。查了下,才發現原來流形式的只能讀取一次。。就好像食物,吃了就沒了。。 
所以在filter和inteceptor里面是沒法通過獲取request的流來進行日志記錄的。

於是還是准備用通用的方法:controller層aop進行切面記錄日志。

使用Aop記錄操作日志

第一步:添加Aop

/**
 * 統一日志處理Handler
 * @author Mingchenchen
 *
 */
public class LogAopHandler {
    @Autowired
    private AuditLogDao auditLogDao;

    /**
     * controller層面記錄操作日志
     * 注意此處是aop:around的 因為需要得到請求前的參數以及請求后接口返回的結果
     * @throws Throwable 
     */
    public Object doSaveLog(ProceedingJoinPoint joinPoint) throws Throwable { 
        MethodSignature method = (MethodSignature) joinPoint.getSignature();
        String methodName = method.getName();
        Object[] objects = joinPoint.getArgs();
        String requestBody = null;
        if (objects!=null && objects.length>0) {
            for (Object object : objects) {
                if (object == null) {
                    requestBody = null;//POST接口參數為空 比如刪除XXX
                }else if (object instanceof String) {
                    requestBody = (String) object;//有些接口直接把參數轉換成對象了
                }else {
                    requestBody = JSONObject.toJSONString(object);
                }
            }
        }

        //只記錄POST方法的日志
        boolean isNeedSaveLog = false;
        //此處不能用getAnnotationByType 是JAVA8的特性,因為注解能夠重名,所以得到的是數組
        RequestMapping annotation = method.getMethod().getAnnotation(RequestMapping.class);
        for (RequestMethod requestMethod : annotation.method()) {
            if (requestMethod==RequestMethod.POST) {
                isNeedSaveLog = true;
            }
        }

        JSONObject requestBodyJson = null;
        try {
            requestBodyJson = JSONObject.parseObject(requestBody);
        } catch (Exception e) {
            //do nothing 即POST請求沒傳body
        }
        HttpServletRequest request = RequestContextUtil.getRequestByCurrentContext();
        String userName = RequestContextUtil.getUserNameByCurrentContext();
        if (StringUtil.isEmpty(userName)) {
            try {
                userName = DmsCache.get(requestBodyJson.getString("userName")).getName();
            } catch (Exception e) {
                userName = RequestContextUtil.getAsynUserInfoByAutoDeploy().getName();
            }
        }

        //得到request的參數后讓方法執行它 
        //注意around的情況下需要返回result 否則將不會返回值給請求者
        Object result = joinPoint.proceed(objects);
        try {
            JSONObject resultJson = JSONObject.parseObject(result.toString());
            if (isNeedSaveLog) {//如果是POST請求 則記錄日志
                LogTypeEnum logTypeEnum = LogTypeEnum.getDesByMethodName(methodName);
                if (logTypeEnum != null) {
                    AuditLogEntity auditLogEntity = new AuditLogEntity();
                    auditLogEntity.setUuid(StringUtil.createRandomUuid());
                    auditLogEntity.setOperator(userName);
                    auditLogEntity.setRequestIp(request.getRemoteAddr());
                    auditLogEntity.setRequestUrl(request.getRequestURI().replace("/cloud-master", ""));
                    auditLogEntity.setEventType(logTypeEnum.getKey());
                    auditLogEntity.setEventDesc(logTypeEnum.getDescription());
                    auditLogEntity.setRequest(requestBody);
                    int isSuccess = "200".equals(resultJson.getString("code")) ? 1 : 0;
                    auditLogEntity.setSuccessFlag(isSuccess);
                    auditLogEntity.setResponse(result.toString());
                    auditLogEntity.setCreateTime(new Date());
                    auditLogDao.insert(auditLogEntity);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }  
}

 

第二步:在spring的xml中聲明

<!-- 記錄操作日志 -->
    <bean id="operationLogAop" class="com.ming.learn.core.aop.LogAopHandler"/>
     <aop:config>
       <aop:aspect id="logAOP" ref="operationLogAop">
         <aop:pointcut id="target" expression="execution(* com.ming.learn..*Controller.*(..))"/>
         <aop:around method="doSaveLog" pointcut-ref="target"/>
       </aop:aspect>
     </aop:config>

 

如此一來,核心步驟就完成了,剩下的就是自己組裝需要記錄的東西了。

 

第三步:寫Dao、Entity、Mapper

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * 日志審計
 * @author Mingchenchen
 *
 */
@Table(name="audit_log")
public class AuditLogEntity {
    @Id
    private String uuid;

    @Column(name="event_type")
    private String eventType;//事件類型

    @Column(name="event_desc")
    private String eventDesc;//事件中文描述

    @Column(name="operator")
    private String operator;//操作者

    @Column(name="request_ip")
    private String requestIp;//客戶端地址

    @Column(name="request_url")
    private String requestUrl;//請求地址

    @Column(name="request")
    private String request;//請求body

    @Column(name="response")
    private String response;//請求返回值

    @Column(name="create_time")
    private Date createTime;

    public String getUuid() {
        return uuid;
    }

    public void setUuid(String uuid) {
        this.uuid = uuid;
    }

    public String getEventType() {
        return eventType;
    }

    public void setEventType(String eventType) {
        this.eventType = eventType;
    }

    public String getEventDesc() {
        return eventDesc;
    }

    public void setEventDesc(String eventDesc) {
        this.eventDesc = eventDesc;
    }

    public String getOperator() {
        return operator;
    }

    public void setOperator(String operator) {
        this.operator = operator;
    }

    public String getRequestIp() {
        return requestIp;
    }

    public void setRequestIp(String requestIp) {
        this.requestIp = requestIp;
    }

    public String getRequestUrl() {
        return requestUrl;
    }

    public void setRequestUrl(String requestUrl) {
        this.requestUrl = requestUrl;
    }

    public String getRequest() {
        return request;
    }

    public void setRequest(String request) {
        this.request = request;
    }

    public String getResponse() {
        return response;
    }

    public void setResponse(String response) {
        this.response = response;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }
}

 

第四步:根據Controller的方法名稱定制響應的事件類型

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 操作日志類型
 * @author Mingchenchen
 *
 */
public enum LogTypeEnum {
    //用戶
    COMMON_LOGIN("login","login","登錄");
    //其他

    private String methodName;//方法名稱與controller一致
    private String key;//保存到數據庫的事件類型
    private String description;//保存到數據庫的描述
    private LogTypeEnum(String methodName,String key,String description){
        this.methodName = methodName;
        this.key = key;
        this.description = description;
    }
    public String getMethodName() {
        return methodName;
    }
    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }
    public String getKey() {
        return key;
    }
    public void setKey(String key) {
        this.key = key;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }

    /**
     * 根據方法名返回
     * @param methodName
     * @return
     */
    public static LogTypeEnum getDesByMethodName(String methodName){
        return innerMap.map.get(methodName);
    }

    /**
     * 內部類 用戶保存所有的enum 無須通過Enum.values()每次遍歷
     * @author Mingchenchen
     *
     */
    private static class innerMap{
        private static Map<String, LogTypeEnum> map = new ConcurrentHashMap<>(128);

        static{
            //初始化整個枚舉類到Map
            for (LogTypeEnum logTypeEnum : LogTypeEnum.values()) {
                map.put(logTypeEnum.getMethodName(), logTypeEnum);
            }
        }
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM