访问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");}@Overridepublic 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]);}@Overridepublic int hashCode() {final int PRIME = 31;int result = 1;result = PRIME * result + Arrays.hashCode(fields);return result;}@Overridepublic 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根据&转成MapMap<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;}

Spring Boot自定义注解+AOP实现日志记录相关推荐

  1. 自定义注解-aop实现日志记录

    关于注解,平时接触的可不少,像是 @Controller.@Service.@Autowried 等等,不知道你是否有过这种疑惑,使用 @Service 注解的类成为我们的业务类,使用 @Contro ...

  2. java 自定义注解+AOP实现日志记录

    ssm版本: 1.首先自定义一个注解,该注解有两个属性,一个是模块名,一个是操作的内容.该注解是用来修饰Service层中的方法的. 2.创建一个切面类,该切面使用@Aspect和@Component ...

  3. JPOM - AOP+自定义注解实现操作日志记录

    文章目录 地址 版本 源码解析-AOP+自定义注解实现操作日志记录 地址 Gitee: https://gitee.com/dromara/Jpom 官网: https://jpom.io/ 一款简而 ...

  4. Spring Boot 如何使用 JUL 进行日志记录

    Spring Boot 如何使用 JUL 进行日志记录 在 Spring Boot 中,我们可以使用多种日志框架进行日志记录.其中,JUL (Java Util Logging) 是 Java 平台自 ...

  5. Spring boot logback的使用(日志记录)

    1 在spring boot中已经自动包含了logback的jar包,如果不是spring boot需要在maven中导入依赖 <!-- https://mvnrepository.com/ar ...

  6. Spring Boot 自定义注解支持EL表达式(基于 MethodBasedEvaluationContext 实现)

    自定义注解 自定义 DistributeExceptionHandler 注解,该注解接收一个参数 attachmentId . 该注解用在方法上,使用该注解作为切点,实现标注该注解的方法抛异常后的统 ...

  7. spring AOP自定义注解方式实现日志管理

    转:spring AOP自定义注解方式实现日志管理 今天继续实现AOP,到这里我个人认为是最灵活,可扩展的方式了,就拿日志管理来说,用Spring AOP 自定义注解形式实现日志管理.废话不多说,直接 ...

  8. spring中自定义注解(annotation)与AOP中获取注解___使用aspectj的@Around注解实现用户操作和操作结果日志

    spring中自定义注解(annotation)与AOP中获取注解 一.自定义注解(annotation) 自定义注解的作用:在反射中获取注解,以取得注解修饰的类.方法或属性的相关解释. packag ...

  9. Spring Boot中使用AOP统一处理Web请求日志

    AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是Spring框架中的一个重要内容,它通 ...

最新文章

  1. windows域控制器恢复
  2. linux线程同步(3)-读写锁
  3. 树莓派查看mysql的密码_树莓派安装MySQL 后若何获取登录密码
  4. matlab论坛真不活跃,MATLAB中文论坛常见问题归纳
  5. 门窗计算机公式,门窗天使软件怎么编辑公式 公式输入方法
  6. 光纤上网是如何实现的?—Vecloud微云
  7. HQChart使用教程70 -通达信语法检测/指标翻译
  8. 小学计算机课老师能教什么,小学信息技术试讲教案怎么写?抽到理论课怎么讲?...
  9. JAVA串口通信开发
  10. LocalDate转换成英文格式
  11. arduino和stm32哪个更好学?
  12. 智力杂志智力杂志社智力编辑部2023年第6期目录
  13. 后端开发、C++开发面经分类整理
  14. 基于matlab的立体图像编码解码算法仿真与分析
  15. nekohtml中解析中出现中文乱码问题
  16. Python开发redmine的缺陷统计图表
  17. 从扫码到刷脸是支付体验的提升
  18. android fm页面布局,荔枝FM Android客户端产品体验报告
  19. 西北大学计算机课表,西北大学课表.doc
  20. 嵌入式linux 声控,采用压电陶瓷片的声控照明灯

热门文章

  1. DB2密码过期的解决办法-创建新密码
  2. 1030 Travel Plan (30分)(俺是个粗人)
  3. 记账后,如何避免误删除账目
  4. 【Python爬虫案例学习9】python爬取免费优质IP归属地查询接口
  5. 大裁员下,程序员如何做“副业”?
  6. SAP SD模块配置
  7. 网络协议 7 - UDP 协议
  8. 前端地图分类(包括坐标系,GIS基础知识,2D与3D地图结构划分)
  9. jav音频格式转换 ffmpeg 微信录音amr转mp3
  10. linux 磁盘管理3板斧,df、du、fdisk:Linux磁盘管理三板斧的使用心得(2)