1. 程式人生 > >Spring Boot自定義註解+AOP實現日誌記錄

Spring Boot自定義註解+AOP實現日誌記錄

訪問Controller列印的日誌效果如下:

*********************************Request請求***************************************
ClassName     :  com.xxx.app.xxx.action.xxxx
RequestMethod :  createURL()
RequestParams :  ["xxx","789"]
RequestType   :  POST
Description   :  建立機器二維碼圖片URL
serverAddr    :  http://localhost:8090
RemoteAddr    :  0:0:0:0:0:0:0:1
DeviceName    :  Unknown
BrowserName   :  Unknown
UserAgent     :  PostmanRuntime/3.0.9
RequestUri    :  /retail/xxxx/createURL
Result        :  {http://www.test.com/?msg=&mno=xxx&deviceid=789&acid=689277759a7c40ec81a8fb74cd5c153a, success=true}

Service丟擲異常列印資訊如下:

*********************************Service異常***************************************
ClassName        :  com.xxx.app.retail.service.impl.xxx
Method           :  com.xxx.app.retail.service.impl.xxxx.goodsSync()
Params           :  [{"deviceId":"123456789","itemId":"1","pictUrl":"1111","reservePrice":"3","title":"2"};]
Description      :  販賣機商品同步
ExceptionName    :  java.lang.ArithmeticException
ExceptionMessage :  / by zero

步驟如下:

1)建立兩個註解,分別用了對Controller和Service實現日誌記錄

ControllerLogs註解

/**
 * @version: V1.0
 * @author: fendo
 * @className: ControllerLogs
 * @packageName: com.xx.commons.web.annotation
 * @description:  Controller日誌記錄
 * @data: 2018-05-21 15:58
 **/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ControllerLogs {
    /**
     * 描述
     */
    String description() default "";
}

ServiceLogs註解

/**
 * @version: V1.0
 * @author: fendo
 * @className: ControllerLogs
 * @packageName: com.xxxx.commons.web.annotation
 * @description: Service日誌記錄
 * @data: 2018-05-21 15:58
 **/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ServiceLogs {

    /**
     * 描述
     */
    String description() default "";
}

2)建立切面LogAspect

/**
 * projectName: xxxx
 * fileName: LogAspect.java
 * packageName: com.xxxx.logs.aop
 * date: 2018-05-31 10:15
 * copyright(c) xxxxxxxx
 */
package com.xxxx.utils.logs.aop;

import com.alibaba.fastjson.JSON;
import com.xxxx.utils.logs.annotation.ControllerLogs;
import com.xxxx.utils.logs.annotation.ServiceLogs;
import com.xxxx.utils.logs.utils.IpUtils;
import com.xxxx.utils.logs.utils.StringUtils;
import com.xxxx.utils.logs.utils.UserAgentUtils;
import eu.bitwalker.useragentutils.UserAgent;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;

import org.springframework.context.annotation.Configuration;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * @version: V1.0
 * @author: fendo
 * @className: LogAspect
 * @packageName: com.xxxx.logs.aop
 * @description: 日誌切點
 * @data: 2018-05-31 10:15  
 **/
@Aspect
@Configuration
public class LogAspect {

    /**
     * 本地異常日誌記錄物件
     */
    private  final Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * Service層切點
      */
    @Pointcut("@annotation(com.xxxx.utils.logs.annotation.ServiceLogs)")
    public void serviceAspect() {

    }

    /**
     * Controller層切點
     */
    @Pointcut("@annotation(com.xxxx.utils.logs.annotation.ControllerLogs)")
    public void controllerAspect() {

    }

    /**
     * 前置通知 用於攔截Controller層記錄使用者的操作
     *
     * @param joinPoint 切點
     */
    @Before("controllerAspect()")
    public void doBefore(JoinPoint joinPoint) {
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            //類名
            String className = joinPoint.getTarget().getClass().getName();
            //請求方法
            String method =  joinPoint.getSignature().getName() + "()";
            //方法引數
            String methodParam = JSON.toJSONString(joinPoint.getArgs());
            //方法描述
            String methodDescription = getControllerMethodDescription(joinPoint);
            StringBuilder sb = new StringBuilder(1000);
            sb.append("\n");
            sb.append("*********************************Request請求***************************************");
            sb.append("\n");
            sb.append("ClassName     :  ").append(className).append("\n");
            sb.append("RequestMethod :  ").append(method).append("\n");
            sb.append("RequestParams :  ").append(methodParam).append("\n");
            sb.append("RequestType   :  ").append(request.getMethod()).append("\n");
            sb.append("Description   :  ").append(methodDescription).append("\n");
            sb.append("serverAddr    :  ").append(request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()).append("\n");
            sb.append("RemoteAddr    :  ").append(IpUtils.getRemoteAddr(request)).append("\n");
            UserAgent userAgent = UserAgentUtils.getUserAgent(request);
            sb.append("DeviceName    :  ").append(userAgent.getOperatingSystem().getName()).append("\n");
            sb.append("BrowserName   :  ").append(userAgent.getBrowser().getName()).append("\n");
            sb.append("UserAgent     :  ").append(request.getHeader("User-Agent")).append("\n");
            sb.append("RequestUri    :  ").append(StringUtils.abbr(request.getRequestURI(), 255)).append("\n");
            logger.info(sb.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @AfterReturning(returning = "ret", pointcut = "controllerAspect()")
    public void doAfterReturning(Object ret) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        //請求方法
        String method = StringUtils.abbr(request.getRequestURI(), 255);
        StringBuilder sb = new StringBuilder(1000);
        // 處理完請求,返回內容
        sb.append("\n");
        sb.append("Result        :  ").append(ret);
        logger.info(sb.toString());
    }


    /**
     * 異常通知 用於攔截service層記錄異常日誌
     *
     * @param joinPoint
     * @param ex
     */
    @AfterThrowing(pointcut = "serviceAspect()", throwing = "ex")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable ex) {
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            //類名
            String className = joinPoint.getTarget().getClass().getName();
            //請求方法
            String method =  (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()");
            //方法引數
            String methodParam = Arrays.toString(joinPoint.getArgs());
            //方法描述
            String methodDescription = getServiceMthodDescription(joinPoint);
            //獲取使用者請求方法的引數並序列化為JSON格式字串
            String params = "";
            if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
                for (int i = 0; i < joinPoint.getArgs().length; i++) {
                    params += JSON.toJSONString(joinPoint.getArgs()[i]) + ";";
                }
            }
            StringBuilder sb = new StringBuilder(1000);
            sb.append("\n");
            sb.append("*********************************Service異常***************************************");
            sb.append("\n");
            sb.append("ClassName        :  ").append(className).append("\n");
            sb.append("Method           :  ").append(method).append("\n");
            sb.append("Params           :  ").append("[" + params + "]").append("\n");
            sb.append("Description      :  ").append(methodDescription).append("\n");
            sb.append("ExceptionName    :  ").append(ex.getClass().getName()).append("\n");
            sb.append("ExceptionMessage :  ").append(ex.getMessage()).append("\n");
            logger.info(sb.toString());
        } catch (Exception e1) {
            e1.printStackTrace();
        }


    }

    /**
     * 獲取註解中對方法的描述資訊 用於service層註解
     *
     * @param joinPoint 切點
     * @return 方法描述
     * @throws Exception
     */
    public static String getServiceMthodDescription(JoinPoint joinPoint)
            throws Exception {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class targetClass = Class.forName(targetName);
        Method[] methods = targetClass.getMethods();
        String description = "";
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == arguments.length) {
                    description = method.getAnnotation(ServiceLogs.class).description();
                    break;
                }
            }
        }
        return description;
    }

    /**
     * 獲取註解中對方法的描述資訊 用於Controller層註解
     *
     * @param joinPoint 切點
     * @return 方法描述
     * @throws Exception
     */
    public static String getControllerMethodDescription(JoinPoint joinPoint) throws Exception {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class targetClass = Class.forName(targetName);
        Method[] methods = targetClass.getMethods();
        String description = "";
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == arguments.length) {
                    description = method.getAnnotation(ControllerLogs.class).description();
                    break;
                }
            }
        }
        return description;
    }
}  

3)幾個工具類

DateUtils

/**
 * projectName: xxxx
 * fileName: DateUtils.java
 * packageName: utils
 * date: 2018-05-30 17:29
 * copyright(c) xxxx
 */
package com.xxxx.utils.logs.utils;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.FastDateFormat;

import java.lang.management.ManagementFactory;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;

/**
 * @version: V1.0
 * @author: fendo
 * @className: DateUtils
 * @packageName: utils
 * @description: 日期工具類
 * @data: 2018-05-30 17:29  
 **/
public class DateUtils extends org.apache.commons.lang3.time.DateUtils {

    private static String[] parsePatterns = {
            "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM-dd HH", "yyyy-MM",
            "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM/dd HH", "yyyy/MM",
            "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM.dd HH", "yyyy.MM",
            "yyyy年MM月dd日", "yyyy年MM月dd日 HH時mm分ss秒", "yyyy年MM月dd日 HH時mm分", "yyyy年MM月dd日 HH時", "yyyy年MM月",
            "yyyy"};

    /**
     * 得到日期字串 ,轉換格式(yyyy-MM-dd)
     */
    public static String formatDate(Date date) {
        return formatDate(date, "yyyy-MM-dd");
    }

    /**
     * 得到日期字串 預設格式(yyyy-MM-dd) pattern可以為:"yyyy-MM-dd" "HH:mm:ss" "E"
     */
    public static String formatDate(long dateTime, String pattern) {
        return formatDate(new Date(dateTime), pattern);
    }

    /**
     * 得到日期字串 預設格式(yyyy-MM-dd) pattern可以為:"yyyy-MM-dd" "HH:mm:ss" "E"
     */
    public static String formatDate(Date date, String pattern) {
        String formatDate = null;
        if (date != null){
            if (StringUtils.isBlank(pattern)) {
                pattern = "yyyy-MM-dd";
            }
            formatDate = FastDateFormat.getInstance(pattern).format(date);
        }
        return formatDate;
    }

    /**
     * 得到日期時間字串,轉換格式(yyyy-MM-dd HH:mm:ss)
     */
    public static String formatDateTime(Date date) {
        return formatDate(date, "yyyy-MM-dd HH:mm:ss");
    }

    /**
     * 得到當前日期字串 格式(yyyy-MM-dd)
     */
    public static String getDate() {
        return getDate("yyyy-MM-dd");
    }

    /**
     * 得到當前日期字串 格式(yyyy-MM-dd) pattern可以為:"yyyy-MM-dd" "HH:mm:ss" "E"
     */
    public static String getDate(String pattern) {
        return FastDateFormat.getInstance(pattern).format(new Date());
    }

    /**
     * 得到當前日期前後多少天,月,年的日期字串
     * @param pattern 格式(yyyy-MM-dd) pattern可以為:"yyyy-MM-dd" "HH:mm:ss" "E"
     * @param amont 數量,前為負數,後為正數
     * @param type 型別,可參考Calendar的常量(如:Calendar.HOUR、Calendar.MINUTE、Calendar.SECOND)
     * @return
     */
    public static String getDate(String pattern, int amont, int type) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.add(type, amont);
        return FastDateFormat.getInstance(pattern).format(calendar.getTime());
    }

    /**
     * 得到當前時間字串 格式(HH:mm:ss)
     */
    public static String getTime() {
        return formatDate(new Date(), "HH:mm:ss");
    }

    /**
     * 得到當前日期和時間字串 格式(yyyy-MM-dd HH:mm:ss)
     */
    public static String getDateTime() {
        return formatDate(new Date(), "yyyy-MM-dd HH:mm:ss");
    }

    /**
     * 得到當前年份字串 格式(yyyy)
     */
    public static String getYear() {
        return formatDate(new Date(), "yyyy");
    }

    /**
     * 得到當前月份字串 格式(MM)
     */
    public static String getMonth() {
        return formatDate(new Date(), "MM");
    }

    /**
     * 得到當天字串 格式(dd)
     */
    public static String getDay() {
        return formatDate(new Date(), "dd");
    }

    /**
     * 得到當前星期字串 格式(E)星期幾
     */
    public static String getWeek() {
        return formatDate(new Date(), "E");
    }

    /**
     * 日期型字串轉化為日期 格式   see to DateUtils#parsePatterns
     */
    public static Date parseDate(Object str) {
        if (str == null){
            return null;
        }
        try {
            return parseDate(str.toString(), parsePatterns);
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * 獲取過去的天數
     * @param date
     * @return
     */
    public static long pastDays(Date date) {
        long t = System.currentTimeMillis()-date.getTime();
        return t/(24*60*60*1000);
    }

    /**
     * 獲取過去的小時
     * @param date
     * @return
     */
    public static long pastHour(Date date) {
        long t = System.currentTimeMillis()-date.getTime();
        return t/(60*60*1000);
    }

    /**
     * 獲取過去的分鐘
     * @param date
     * @return
     */
    public static long pastMinutes(Date date) {
        long t = System.currentTimeMillis()-date.getTime();
        return t/(60*1000);
    }

    /**
     * 獲取兩個日期之間的天數
     *
     * @param before
     * @param after
     * @return
     */
    public static double getDistanceOfTwoDate(Date before, Date after) {
        long beforeTime = before.getTime();
        long afterTime = after.getTime();
        return (afterTime - beforeTime) / (1000 * 60 * 60 * 24);
    }

    /**
     * 獲取某月有幾天
     * @param date 日期
     * @return 天數
     */
    public static int getMonthHasDays(Date date){
        String yyyyMM = FastDateFormat.getInstance("yyyyMM").format(date);
        String year = yyyyMM.substring(0, 4);
        String month = yyyyMM.substring(4, 6);
        String day31 = ",01,03,05,07,08,10,12,";
        String day30 = "04,06,09,11";
        int day = 0;
        if (day31.contains(month)) {
            day = 31;
        } else if (day30.contains(month)) {
            day = 30;
        } else {
            int y = Integer.parseInt(year);
            if ((y % 4 == 0 && (y % 100 != 0)) || y % 400 == 0) {
                day = 29;
            } else {
                day = 28;
            }
        }
        return day;
    }

    /**
     * 獲取日期是當年的第幾周
     * @param date
     * @return
     */
    public static int getWeekOfYear(Date date){
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        return cal.get(Calendar.WEEK_OF_YEAR);
    }

    /**
     * 獲取一天的開始時間(如:2015-11-3 00:00:00.000)
     * @param date 日期
     * @return
     */
    public static Date getOfDayFirst(Date date) {
        if (date == null){
            return null;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }

    /**
     * 獲取一天的最後時間(如:2015-11-3 23:59:59.999)
     * @param date 日期
     * @return
     */
    public static Date getOfDayLast(Date date) {
        if (date == null){
            return null;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 999);
        return calendar.getTime();
    }

    /**
     * 獲取伺服器啟動時間
     * @return
     */
    public static Date getServerStartDate(){
        long time = ManagementFactory.getRuntimeMXBean().getStartTime();
        return new Date(time);
    }

    /**
     * 格式化為日期範圍字串
     * @param beginDate 2018-01-01
     * @param endDate 2018-01-31
     * @return 2018-01-01 ~ 2018-01-31
     */
    public static String formatDateBetweenString(Date beginDate, Date endDate){
        String begin = DateUtils.formatDate(beginDate);
        String end = DateUtils.formatDate(endDate);
        if (StringUtils.isNoneBlank(begin, end)){
            return begin + " ~ " + end;
        }
        return null;
    }

    /**
     * 解析日期範圍字串為日期物件
     * @param dateString 2018-01-01 ~ 2018-01-31
     * @return new Date[]{2018-01-01, 2018-01-31}
     */
    public static Date[] parseDateBetweenString(String dateString){
        Date beginDate = null; Date endDate = null;
        if (StringUtils.isNotBlank(dateString)){
            String[] ss = StringUtils.split(dateString, "~");
            if (ss != null && ss.length == 2){
                String begin = StringUtils.trim(ss[0]);
                String end = StringUtils.trim(ss[1]);
                if (StringUtils.isNoneBlank(begin, end)){
                    beginDate = DateUtils.parseDate(begin);
                    endDate = DateUtils.parseDate(end);
                }
            }
        }
        return new Date[]{beginDate, endDate};
    }

}

IpUtils類:

/**
 * projectName: xxxx
 * fileName: IpUtils.java
 * packageName: com.xxxx.logs.utils
 * date: 2018-05-31 10:48
 * copyright(c) xxxx
 */
package com.xxxx.utils.logs.utils;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.HttpServletRequest;

/**
 * @version: V1.0
 * @author: fendo
 * @className: IpUtils
 * @packageName: com.xxxx.logs.utils
 * @description: IP工具類
 * @data: 2018-05-31 10:48  
 **/
public class IpUtils {
    /**
     * 獲取客戶端IP地址
     * @param request
     * @return
     */
    public static String getRemoteAddr(HttpServletRequest request) {
        if (request == null) {
            return "unknown";
        }
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return StringUtils.split(ObjectUtils.toString(ip), ",")[0];
    }

}  

StringUtils類:

/**
 * projectName: xxxx
 * fileName: StringUtils.java
 * packageName: com.xxxx.logs.utils
 * date: 2018-05-31 10:53
 * copyright(c) xxxx
 */
package com.xxxx.utils.logs.utils;

import org.apache.commons.lang3.StringEscapeUtils;

import java.io.UnsupportedEncodingException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static org.apache.commons.lang3.StringUtils.isBlank;

/**
 * @version: V1.0
 * @author: fendo
 * @className: StringUtils
 * @packageName: com.xxxx.logs.utils
 * @description: 字串工具類, 繼承org.apache.commons.lang3.StringUtils類  
 * @data: 2018-05-31 10:53  
 **/
public class StringUtils {

    /**
     * 替換掉HTML標籤方法
     */
    public static String stripHtml(String html) {
        if (isBlank(html)){
            return "";
        }
        String regEx = "<.+?>";
        Pattern p = Pattern.compile(regEx);
        Matcher m = p.matcher(html);
        String s = m.replaceAll("");
        return s;
    }

    /**
     * 縮略字串(不區分中英文字元)
     * @param str 目標字串
     * @param length 擷取長度
     * @return
     */
    public static String abbr(String str, int length) {
        if (str == null) {
            return "";
        }
        try {
            StringBuilder sb = new StringBuilder();
            int currentLength = 0;
            for (char c : stripHtml(StringEscapeUtils.unescapeHtml4(str)).toCharArray()) {
                currentLength += String.valueOf(c).getBytes("GBK").length;
                if (currentLength <= length - 3) {
                    sb.append(c);
                } else {
                    sb.append("...");
                    break;
                }
            }
            return sb.toString();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return "";
    }
}  

TimeUtils類:

/**
 * projectName: xxxx
 * fileName: TimeUtils.java
 * packageName: utils
 * date: 2018-05-30 17:31
 * copyright(c) xxxx
 */
package com.xxxx.utils.logs.utils;

import org.apache.commons.lang3.time.DateFormatUtils;

import java.util.Arrays;
import java.util.Date;

/**
 * @version: V1.0
 * @author: fendo
 * @className: TimeUtils
 * @packageName: utils
 * @description: 時間計算工具類
 * @data: 2018-05-30 17:31  
 **/
public class TimeUtils {

    /**
     * 將時間轉換為字串(xx天,xx時,xx分,xx秒,大於360天顯示日期時間)
     */
    public static String formatDateAgo(long dateTime) {
        StringBuilder sb = new StringBuilder();
        if (dateTime < 1000){
            sb.append(dateTime).append("毫秒");
        }else{
            TimeUtils t = new TimeUtils(dateTime);
            int day = t.get(TimeUtils.DAY);
            int hour = t.get(TimeUtils.HOUR);
            int minute = t.get(TimeUtils.MINUTE);
            int second = t.get(TimeUtils.SECOND);
            if (day > 365){
                return DateUtils.formatDate(new Date(dateTime), "yyyy年MM月dd日 HH時mm分ss秒");
            }
            if (day > 0){
                sb.append(day).append("天");
            }
            if (hour > 0){
                sb.append(hour).append("時");
            }
            if (minute > 0){
                sb.append(minute).append("分");
            }
            if (second > 0){
                sb.append(second).append("秒");
            }
        }
        return sb.toString();
    }

    /**
     * 將過去的時間轉為為,剛剛,xx秒,xx分鐘,xx小時前、xx天前,大於3天的顯示日期
     */
    public static String formatTimeAgo(String dateTime) {
        return formatTimeAgo(DateUtils.parseDate(dateTime));
    }

    /**
     * 將過去的時間轉為為,剛剛,xx秒,xx分鐘,xx小時前、xx天前,大於3天的顯示日期
     */
    public static String formatTimeAgo(Date dateTime) {
        String interval = null;
        // 得出的時間間隔是毫秒
        long time = System.currentTimeMillis() - dateTime.getTime();
        // 如果時間間隔小於10秒則顯示“剛剛”time/10得出的時間間隔的單位是秒
        if (time / 1000 < 10 && time / 1000 >= 0) {
            interval = "剛剛";
        }
        // 如果時間間隔大於24小時則顯示多少天前
        else if (time / 3600000 < 24*4 && time / 3600000 >= 24) {
            int d = (int) (time / (3600000*24));// 得出的時間間隔的單位是天
            interval = d + "天前";
        }
        // 如果時間間隔小於24小時則顯示多少小時前
        else if (time / 3600000 < 24 && time / 3600000 >= 1) {
            int h = (int) (time / 3600000);// 得出的時間間隔的單位是小時
            interval = h + "小時前";
        }
        // 如果時間間隔小於60分鐘則顯示多少分鐘前
        else if (time / 60000 < 60 && time / 60000 >=1) {
            int m = (int) ((time % 3600000) / 60000);// 得出的時間間隔的單位是分鐘
            interval = m + "分鐘前";
        }
        // 如果時間間隔小於60秒則顯示多少秒前
        else if (time / 1000 < 60 && time / 1000 >=10) {
            int se = (int) ((time % 60000) / 1000);
            interval = se + "秒前";
        }
        // 大於3天的,則顯示正常的時間,但是不顯示秒
        else {
            interval = DateUtils.formatDate(dateTime,"yyyy-MM-dd");
        }
        return interval;
    }

    /**
     * 時間欄位常量,表示“秒”
     */
    public final static int SECOND = 0;

    /**
     * 時間欄位常量,表示“分”
     */
    public final static int MINUTE = 1;

    /**
     * 時間欄位常量,表示“時”
     */
    public final static int HOUR = 2;

    /**
     * 時間欄位常量,表示“天”
     */
    public final static int DAY = 3;

    /**
     * 各常量允許的最大值
     */
    private final int[] maxFields = { 59, 59, 23, Integer.MAX_VALUE - 1 };

    /**
     * 各常量允許的最小值
     */
    private final int[] minFields = { 0, 0, 0, Integer.MIN_VALUE };

    /**
     * 預設的字串格式時間分隔符
     */
    private String timeSeparator = ":";

    /**
     * 時間資料容器
     */
    private int[] fields = new int[4];

    /**
     * 無參構造,將各欄位置為 0
     */
    public TimeUtils() {
        this(0, 0, 0, 0);
    }

    /**
     * 使用時、分構造一個時間
     * @param hour      小時
     * @param minute    分鐘
     */
    public TimeUtils(int hour, int minute) {
        this(0, hour, minute, 0);
    }

    /**
     * 使用時、分、秒構造一個時間
     * @param hour      小時
     * @param minute    分鐘
     * @param second    秒
     */
    public TimeUtils(int hour, int minute, int second) {
        this(0, hour, minute, second);
    }

    /**
     * 使用一個字串構造時間<br>
     * Time time = new Time("14:22:23");
     * @param time      字串格式的時間,預設採用“:”作為分隔符
     */
    public TimeUtils(String time) {
        this(time, null);
    }

    /**
     * 使用時間毫秒構建時間
     * @param time
     */
    public TimeUtils(long time){
        this(new Date(time));
    }

    /**
     * 使用日期物件構造時間
     * @param date
     */
    public TimeUtils(Date date){
        this(DateFormatUtils.formatUTC(date, "HH:mm:ss"));
    }

    /**
     * 使用天、時、分、秒構造時間,進行全字元的構造
     * @param day       天
     * @param hour      時
     * @param minute    分
     * @param second    秒
     */
    public TimeUtils(int day, int hour, int minute, int second) {
        initialize(day, hour, minute, second);
    }

    /**
     * 使用一個字串構造時間,指定分隔符<br>
     * Time time = new Time("14-22-23", "-");
     * @param time      字串格式的時間
     */
    public TimeUtils(String time, String timeSeparator) {
        if(timeSeparator != null) {
            setTimeSeparator(timeSeparator);
        }
        parseTime(time);
    }

    /**
     * 設定時間欄位的值
     * @param field     時間欄位常量
     * @param value     時間欄位的值
     */
    public void set(int field, int value) {
        if(value < minFields[field]) {
            throw new IllegalArgumentException(value + ", time value must be positive.");
        }
        fields[field] = value % (maxFields[field] + 1);
        // 進行進位計算
        int carry = value / (maxFields[field] + 1);
        if(carry > 0) {
            int upFieldValue = get(field + 1);
            set(field + 1, upFieldValue + carry);
        }
    }

    /**
     * 獲得時間欄位的值
     * @param field     時間欄位常量
     * @return          該時間欄位的值
     */
    public int get(int field) {
        if(field < 0 || field > fields.length - 1) {
            throw new IllegalArgumentException(field + ", field value is error.");
        }
        return fields[field];
    }

    /**
     * 將時間進行“加”運算,即加上一個時間
     * @param time      需要加的時間
     * @return          運算後的時間
     */
    public TimeUtils addTime(TimeUtils time) {
        TimeUtils result = new TimeUtils();
        int up = 0;     // 進位標誌
        for (int i = 0; i < fields.length; i++) {
            int sum = fields[i] + time.fields[i] + up;
            up = sum / (maxFields[i] + 1);
            result.fields[i] = sum % (maxFields[i] + 1);
        }
        return result;
    }

    /**
     * 將時間進行“減”運算,即減去一個時間
     * @param time      需要減的時間
     * @return          運算後的時間
     */
    public TimeUtils subtractTime(TimeUtils time) {
        TimeUtils result = new TimeUtils();
        int down = 0;       // 退位標誌
        for (int i = 0, k = fields.length - 1; i < k; i++) {
            int difference = fields[i] + down;
            if (difference >= time.fields[i]) {
                difference -= time.fields[i];
                down = 0;
            } else {
                difference += maxFields[i] + 1 - time.fields[i];
                down = -1;
            }
            result.fields[i] = difference;
        }
        result.fields[DAY] = fields[DAY] - time.fields[DAY] + down;
        return result;
    }

    /**
     * 獲得時間欄位的分隔符
     * @return
     */
    public String getTimeSeparator() {
        return timeSeparator;
    }

    /**
     * 設定時間欄位的分隔符(用於字串格式的時間)
     * @param timeSeparator     分隔符字串
     */
    public void setTimeSeparator(String timeSeparator) {
        this.timeSeparator = timeSeparator;
    }

    private void initialize(int day, int hour, int minute, int second) {
        set(DAY, day);
        set(HOUR, hour);
        set(MINUTE, minute);
        set(SECOND, second);
    }

    private void parseTime(String time) {
        if(time == null) {
            initialize(0, 0, 0, 0);
            return;
        }
        String t = time;
        int field = DAY;
        set(field--, 0);
        int p = -1;
        while((p = t.indexOf(timeSeparator)) > -1) {
            parseTimeField(time, t.substring(0, p), field--);
            t = t.substring(p + timeSeparator.length());
        }
        parseTimeField(time, t, field--);
    }

    private void parseTimeField(String time, String t, int field) {
        if(field < SECOND || t.length() < 1) {
            parseTimeException(time);
        }
        char[] chs = t.toCharArray();
        int n = 0;
        for(int i = 0; i < chs.length; i++) {
            if(chs[i] <= ' ') {
                continue;
            }
            if(chs[i] >= '0' && chs[i] <= '9') {
                n = n * 10 + chs[i] - '0';
                continue;
            }
            parseTimeException(time);
        }
        set(field, n);
    }

    private void parseTimeException(String time) {
        throw new IllegalArgumentException(time + ", time format error, HH"
                + this.timeSeparator + "mm" + this.timeSeparator + "ss");
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder(16);
        sb.append(fields[DAY]).append(',').append(' ');
        buildString(sb, HOUR).append(timeSeparator);
        buildString(sb, MINUTE).append(timeSeparator);
        buildString(sb, SECOND);
        return sb.toString();
    }

    private StringBuilder buildString(StringBuilder sb, int field) {
        if(fields[field] < 10) {
            sb.append('0');
        }
        return sb.append(fields[field]);
    }

    @Override
    public int hashCode() {
        final int PRIME = 31;
        int result = 1;
        result = PRIME * result + Arrays.hashCode(fields);
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final TimeUtils other = (TimeUtils) obj;
        if (!Arrays.equals(fields, other.fields)) {
            return false;
        }
        return true;
    }
}

UserAgentUtils類:

/**
 * projectName: xxxx
 * fileName: UserAgentUtils.java
 * packageName: com.xxxx.logs.utils
 * date: 2018-05-31 10:49
 * copyright(c) xxxx
 */
package com.xxxx.utils.logs.utils;

import eu.bitwalker.useragentutils.Browser;
import eu.bitwalker.useragentutils.DeviceType;
import eu.bitwalker.useragentutils.UserAgent;

import javax.servlet.http.HttpServletRequest;

/**
 * @version: V1.0
 * @author: fendo
 * @className: UserAgentUtils
 * @packageName: com.xxxx.logs.utils
 * @description: 使用者代理
 * @data: 2018-05-31 10:49  
 **/
public class UserAgentUtils {
    /**
     * 獲取使用者代理物件
     * @param request
     * @return
     */
    public static UserAgent getUserAgent(HttpServletRequest request){
        return UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
    }

    /**
     * 獲取裝置型別
     * @param request
     * @return
     */
    public static DeviceType getDeviceType(HttpServletRequest request){
        return getUserAgent(request).getOperatingSystem().getDeviceType();
    }

    /**
     * 是否是PC
     * @param request
     * @return
     */
    public static boolean isComputer(HttpServletRequest request){
        return DeviceType.COMPUTER.equals(getDeviceType(request));
    }

    /**
     * 是否是手機
     * @param request
     * @return
     */
    public static boolean isMobile(HttpServletRequest request){
        return DeviceType.MOBILE.equals(getDeviceType(request));
    }

    /**
     * 是否是平板
     * @param request
     * @return
     */
    public static boolean isTablet(HttpServletRequest request){
        return DeviceType.TABLET.equals(getDeviceType(request));
    }

    /**
     * 是否是手機和平板
     * @param request
     * @return
     */
    public static boolean isMobileOrTablet(HttpServletRequest request){
        DeviceType deviceType = getDeviceType(request);
        return DeviceType.MOBILE.equals(deviceType) || DeviceType.TABLET.equals(deviceType);
    }

    /**
     * 獲取瀏覽型別
     * @param request
     * @return
     */
    public static Browser getBrowser(HttpServletRequest request){
        return getUserAgent(request).getBrowser();
    }

    /**
     * 是否IE版本是否小於等於IE8
     * @param request
     * @return
     */
    public static boolean isLteIE8(HttpServletRequest request){
        Browser browser = getBrowser(request);
        return Browser.IE5.equals(browser) || Browser.IE6.equals(browser)
                || Browser.IE7.equals(browser) || Browser.IE8.equals(browser);
    }
}  

4)POM.XML檔案

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>1.5.6.RELEASE</version>
            <type>pom</type>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <scope>provided</scope>
            <version>1.8.8</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.0.5.RELEASE</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.21</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.21</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.6</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>eu.bitwalker</groupId>
            <artifactId>UserAgentUtils</artifactId>
            <version>1.20</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.46</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

5)使用方式

    @PostMapping("/createURL")
    @ControllerLogs(description = "建立機器二維碼圖片URL")
    public Map<String,Object> createURL(@NotEmpty(message = "mno is required") String mno,
                                  @NotEmpty(message = "deviceid is required") String deviceid){
        return eightStatesService.createURL(mno,deviceid);
    }
	
	
    @Override
    @ServiceLogs(description = "商品同步")
    public Map<String, Object> goodsSync(GoodsRequestDTO data) {
        int a = 0/0;
        MachineGoods machineGoods = new MachineGoods();
        BeanUtils.copyProperties(data,machineGoods);
        machineGoods.setId(Identities.uuid2());
        machineGoods.setCt(new Date());
        machineGoods.setMt(new Date());
        activityTemplate.save(machineGoods);
        return ResultFactory.getSuccess();
    }

6)調整

後面用著用著發現,列印的日誌不是很全,特別是對於GET/POST的From提交,不知道key是什麼,只打印了value,所有做如下修改:

    public void doBefore(JoinPoint joinPoint) {
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

            //類名
            String className = joinPoint.getTarget().getClass().getName();
            //請求方法
            String method =  joinPoint.getSignature().getName() + "()";
            //方法引數
            String methodParam = JSON.toJSONString(joinPoint.getArgs());
            Map<String, String[]> params = request.getParameterMap();
            String decode = "";
            //針對get請求
            if(request.getQueryString()!=null){
                try {
                    decode = URLDecoder.decode(request.getQueryString(),"utf-8");
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
            }else{
                //針對post請求
                for (String key : params.keySet()) {
                    String[] values = params.get(key);
                    for (int i = 0; i < values.length; i++) {
                        String value = values[i];
                        decode += key + "=" + value + "&";
                    }
                }
            }
            //將String根據&轉成Map
            Map<String, Object> methodParamMap = transStringToMap(decode, "&", "=");
            //設定日期格式
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            //方法描述
            String methodDescription = getControllerMethodDescription(joinPoint);
            StringBuilder sb = new StringBuilder(1000);
            sb.append("\n");
            sb.append("*********************************Request請求***************************************");
            sb.append("\n");
            sb.append("ClassName     :  ").append(className).append("\n");
            sb.append("RequestMethod :  ").append(method).append("\n");
            sb.append("ContentType   :  ").append(("".equals(request.getContentType()) || request.getContentType() == null)?"FROM":request.getContentType()).append("\n");
            sb.append("RequestParams :  ").append(("".equals(decode) || decode == null)?methodParam:methodParamMap).append("\n");
            sb.append("RequestType   :  ").append(request.getMethod()).append("\n");
            sb.append("Description   :  ").append(methodDescription).append("\n");
            sb.append("ServerAddr    :  ").append(request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()).append("\n");
            sb.append("RemoteAddr    :  ").append(IpUtils.getRemoteAddr(request)).append("\n");
            UserAgent userAgent = UserAgentUtils.getUserAgent(request);
            sb.append("DeviceName    :  ").append(userAgent.getOperatingSystem().getName()).append("\n");
            sb.append("BrowserName   :  ").append(userAgent.getBrowser().getName()).append("\n");
            sb.append("UserAgent     :  ").append(request.getHeader("User-Agent")).append("\n");
            sb.append("RequestUri    :  ").append(StringUtils.abbr(request.getRequestURI(), 255)).append("\n");
            sb.append("**************************");
            sb.append(df.format(new Date()));
            sb.append("***********************************");
            sb.append("\n");
            logger.info(sb.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * String 轉Map
     * @param mapString 待轉的String
     * @param separator 分割符
     * @param pairSeparator 分離器
     * @return
     */
    public static Map<String, Object> transStringToMap(String mapString, String separator, String pairSeparator) {
        Map<String, Object> map = new HashMap<String, Object>();
        String[] fSplit = mapString.split(separator);
        for (int i = 0; i < fSplit.length; i++) {
            if (fSplit[i]==null||fSplit[i].length()==0) {
                continue;
            }
            String[] sSplit = fSplit[i].split(pairSeparator);
            String value = fSplit[i].substring(fSplit[i].indexOf('=') + 1, fSplit[i].length());
            map.put(sSplit[0], value);
        }
        return map;
    }