Spring Boot自定義註解+AOP實現日誌記錄
阿新 • • 發佈:2019-01-06
訪問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;
}