1.pom.xml (注意,这里要把spring boot自己依赖的log.jar包去掉不然报错)
Caused by: java.lang.IllegalStateException: Detected both log4j-over-slf4j.jar AND bound slf4j-log4j12.jar on the class path, preempting StackOverflowError. See also http://www.slf4j.org/codes.html#log4jDelegationLoop for more details.
at org.slf4j.impl.Log4jLoggerFactory.
去掉方法
<exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions>
<!-- 核心模块,包括自动配置支持、日志和YAML -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<!-- Spring Boot在所有内部日志中使用Commons Logging,我们其使用其他日志时,需要在spring-boot-starter依赖上 exclusions来排除不需要的依赖。-->
<!--<exclusions>-->
<!--<exclusion>-->
<!--<groupId>org.springframework.boot</groupId>-->
<!--<artifactId>spring-boot-starter-logging</artifactId>-->
<!--</exclusion>-->
<!--</exclusions>-->
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-logging</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- spring boot 使用Thymeleaf模板引擎渲染web依赖 spring boot 官方推荐 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!-- Spring Boot在所有内部日志中使用Commons Logging,我们其使用其他日志时,需要在spring-boot-starter依赖上 exclusions来排除不需要的依赖。 <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j</artifactId> <version>1.3.2.RELEASE</version> </dependency> <!-- aop相关依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
2. application.properties
#Servlet端口号 server.port=8088 # AOP # Add @EnableAspectJAutoProxy. spring.aop.auto=true # Whether subclass-based (CGLIB) proxies are to be created (true) as opposed to standard Java interface-based proxies (false). # 即当我们需要使用CGLIB来实现AOP的时候,需要配置spring.aop.proxy-target-class=true,不然默认使用的是标准Java的实现。 spring.aop.proxy-target-class=false
3.log4j.properties
# LOG4J配置 #OFF,systemOut,logFile,logDailyFile,logRollingFile,logMail,logDB,ALL # level是日志记录的优先级,分为OFF,TRACE,DEBUG,INFO,WARN,ERROR,FATAL,ALL # Log4j建议只使用四个级别,优先级从低到高分别是DEBUG,INFO,WARN,ERROR ,注:log4j.rootLogger 给定的级别会继承,且重定义级别不能优先于父定义的级别 # systemOut,logFile,logDailyFile,logRollingFile 指定日志信息输出到哪个地方; log4j.rootLogger=DEBUG,systemOut,logFile,logDailyFile,logRollingFile #输出到控制台(控制台) log4j.appender.systemOut= org.apache.log4j.ConsoleAppender log4j.appender.systemOut.layout= org.apache.log4j.PatternLayout log4j.appender.systemOut.layout.ConversionPattern= [%-5p][%-22d{yyyy-MM-dd HH:mm:ssS}][%l]%n%m%n #指定日志消息的输出最低层次 log4j.appender.systemOut.Threshold= DEBUG log4j.appender.systemOut.ImmediateFlush= TRUE log4j.appender.systemOut.Target= System.out #输出到文件(文件) log4j.appender.logFile= org.apache.log4j.FileAppender log4j.appender.logFile.layout= org.apache.log4j.PatternLayout log4j.appender.logFile.layout.ConversionPattern= [%-5p][%-22d{yyyy-MM-dd HH:mm:ssS}][%l]%n%m%n log4j.appender.logFile.Threshold= DEBUG log4j.appender.logFile.ImmediateFlush= TRUE log4j.appender.logFile.Append= TRUE log4j.appender.logFile.File=logs/log4j.log log4j.appender.logFile.Encoding= UTF-8 #按DatePattern输出到文件(每天产生一个日志文件) log4j.appender.logDailyFile= org.apache.log4j.DailyRollingFileAppender log4j.appender.logDailyFile.layout= org.apache.log4j.PatternLayout log4j.appender.logDailyFile.layout.ConversionPattern= [%-5p][%-22d{yyyy-MM-dd HH:mm:ssS}][%l]%n%m%n log4j.appender.logDailyFile.Threshold= DEBUG log4j.appender.logDailyFile.ImmediateFlush= TRUE log4j.appender.logDailyFile.Append= TRUE log4j.appender.logDailyFile.File= logs/log4j log4j.appender.logDailyFile.DatePattern= '_'yyyy-MM-dd'.log' log4j.appender.logDailyFile.Encoding= UTF-8 #设定文件大小输出到文件(文件大小到达指定尺寸的时候产生一个新的文件) log4j.appender.logRollingFile= org.apache.log4j.RollingFileAppender log4j.appender.logRollingFile.layout= org.apache.log4j.PatternLayout log4j.appender.logRollingFile.layout.ConversionPattern= [%-5p][%-22d{yyyy-MM-dd HH:mm:ssS}][%l]%n%m%n log4j.appender.logRollingFile.Threshold= DEBUG log4j.appender.logRollingFile.ImmediateFlush= TRUE log4j.appender.logRollingFile.Append= TRUE log4j.appender.logRollingFile.File= logs/log4j.log log4j.appender.logRollingFile.MaxFileSize= 1MB log4j.appender.logRollingFile.MaxBackupIndex= 10 log4j.appender.logRollingFile.Encoding= UTF-8
4.Application.java
package com.guilf; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * 注:启动类不要放在main/java 根目录下,启动会报错 */ @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class,args); } }
5.WebLogAspect.java
package com.guilf.aspect; import org.apache.tomcat.util.http.fileupload.RequestContext; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.util.Arrays; import java.util.Date; /** * 日志切面类. * Created by hong on 2017/5/19. */ /** * 可以使用@Order注解指定切面的优先级,值越小优先级越高 **/ @Order(2) /** * 注解将一个java类定义为切面类 **/ @Aspect @Component public class WebLogAspect { private final Logger logger = LoggerFactory.getLogger(this.getClass()); /** * 根据需要在切入点不同位置的切入内容: * 使用 @Before 在切入点开始处切入内容 ( 前置通知 ) * 使用 @After 在切入点结尾处切入内容 ( 后置通知 ) * 使用 @AfterReturning 在切入点return内容之后切入内容(可以用来对处理返回值做一些加工处理) ( 配置后置返回通知 ) * 使用 @Around 在切入点前后切入内容,并自己控制何时执行切入点自身的内容 ( 环绕通知 ) * 使用 @AfterThrowing 用来处理当切入内容部分抛出异常之后的处理逻辑 ( 异常通知 ) */ /** * 定义个方法,注解上@Pointcut 及其规则表达式,定义成一个切入点(如下例中某个package下的所有函数,也可以是一个注解等。); * Controller层切点 */ @Pointcut("execution(public * com.guilf.mvc..*.*(..))") public void webLog() { } /** * 前置通知 用于拦截Controller层记录的参数 * * @param joinPoint * @Before 接收的参数是前面定义的切入点方法 */ @Before("webLog()") public void doBefore(JoinPoint joinPoint) { logger.info("==========执行 weblog() 切入点 前置通知==============="); // 接收到请求,记录请求内容 ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = requestAttributes.getRequest(); // 记录下请求内容 logger.info("URL : " + request.getRequestURL().toString()); logger.info("HTTP_METHOD : " + request.getMethod()); logger.info("IP : " + request.getLocalAddr()); logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); } /** * 后置通知 */ @After("webLog()") public void after(JoinPoint joinPoint) { logger.info("==========执行 weblog() 切入点 后置通知==============="); logger.info("PARAM : " + Arrays.toString(joinPoint.getArgs())); } /** * 环绕通知 用来监控目标执行前后情况(例如:用来监控切入点执行时间). * 注: 在使用@Around 时,需要有返回值,不然同时使用 @AfterReturning 获取不到返回参数 . * 环绕通知需要携带ProceedingJoinPoint类型的参数 * * @param joinPoint */ // @Around("webLog()") // public Object around(ProceedingJoinPoint joinPoint) { // logger.info("[==========开始 weblog() 切入点 环绕通知===============]"); // // long start = System.currentTimeMillis(); // Object ret = null; // try { // // 环绕通知 ProceedingJoinPoint 执行proceed方法的作用是让目标方法执行 // // 环绕通知=前置+目标方法执行+后置通知,proceed方法就是用于启动目标方法执行的 // // 也就是说,环绕通知可以用来代替同时使用前置、后置通知的情况 // ret = joinPoint.proceed(); // long end = System.currentTimeMillis(); // // // 其作用是因为Debug,Info和Trace一般会打印比较详细的信息,而且打印的次数较多,如果我们不加log.isDebugEnabled()等 // // 进行预先判断,当系统loglevel设置高于Debug或Info或Trace时,虽然系统不会打应出这些级别的日志,但是每次预先拼接log打印中的参数字符串,影响系统的性能。 // if (logger.isInfoEnabled()) { // logger.info("监控到========= " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName() + "执行时间:" + (end - start) + "ms!"); // } // logger.info("[==========结束 weblog() 切入点 环绕通知===============]"); // } catch (Throwable throwable) { // throwable.printStackTrace(); // } // // return ret; // } /** * 后置返回通知 用来监控返回值 */ @AfterReturning(pointcut = "webLog()", returning = "ret") public void afterReturn(JoinPoint joinPoint, Object ret) { logger.info("==========开始 weblog() 切入点 环绕通知==============="); logger.info("==========开始 weblog() 切入点 环绕通知==============="); logger.info("=========监控到 " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName() + "返回的值:"); logger.info(ret.toString()); } /** * 异常通知 用于拦截异常,记录日志 * * @param joinPoint * @param ex */ @AfterThrowing(pointcut = "webLog()", throwing = "ex") public void AfterThrowing(JoinPoint joinPoint, Throwable ex) { logger.info("==========开始 weblog() 切入点 异常通知==============="); logger.info("=========监控到 " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); logger.info("发生异常: " + ex.getMessage()); logger.info("请求参数: "); // Stream.of(joinPoint.getArgs()).forEach(arg -> logger.info(arg.toString())); logger.info("异常时间: " + new Date()); } }
6.HelloController.java
扫描二维码关注公众号,回复:
2526186 查看本文章
package com.guilf.mvc; import com.guilf.exception.MyException; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class HelloController { @RequestMapping("/helloEx") public String hello() throws Exception { throw new Exception("发生异常"); } @RequestMapping("myhello") public String myHello() throws MyException { throw new MyException("发生异常"); } @RequestMapping("/json") @ResponseBody public String json(){ return "json"; } @RequestMapping("/index") public String index(){ return "index"; } }
7.启动的话可以看到打印的日志。日志在项目里,log会记录日志,
8.前台抛错
9. ErrorInfo.java
package com.guilf.dto; public class ErrorInfo<T> { public static final Integer OK = 0; public static final Integer ERROR = 500; private Integer code; private String message; private String url; private T data; public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public static Integer getOK() { return OK; } public static Integer getERROR() { return ERROR; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } }
10.MyException.java
package com.guilf.exception; public class MyException extends Exception { public MyException(String message) { super(message); } }
11.MyExceptionHandler.java
package com.guilf.exception; import com.guilf.dto.ErrorInfo; import org.springframework.ui.Model; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; /** * @ClassName: MyExceptionHandler * @Description: (统一异常处理) * * @version v1.1 */ @ControllerAdvice public class MyExceptionHandler { /** * 异常处理, 返回视图 * @param req * @param e * @return * @throws Exception */ @ExceptionHandler(value = Exception.class) public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("exception", e); modelAndView.addObject("url", req.getRequestURL()); modelAndView.setViewName("error"); return modelAndView; } /** * 异常处理,返回自定义的异常对象json * @param req * @param e * @return * @throws Exception */ @ExceptionHandler(value = MyException.class) @ResponseBody public ErrorInfo<String> jsonErrorHandler(HttpServletRequest req, MyException e) throws Exception { ErrorInfo<String> r = new ErrorInfo<>(); r.setMessage(e.getMessage()); r.setCode(ErrorInfo.ERROR); r.setData("Some Data"); r.setUrl(req.getRequestURL().toString()); return r; } @ModelAttribute public Model newUser(Model model) { System.out.println("============应用到所有@RequestMapping注解方法,在其执行之前把返回值放入Model"); model.addAttribute("user","guilf"); return model; } @InitBinder public void initBinder(WebDataBinder binder) { System.out.println("============应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器"); } }
12 error.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"/> <title>Title</title> </head> <body> 请求地址: <span th:text="${url}"></span> <p>发生了异常...</p> </body> </html>
13 index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"/> <title>Title</title> </head> <body> hello world! </body> </html>
log日志