当前位置:   article > 正文

日志处理 三:Filter+自定义注解实现 系统日志跟踪功能_filter获取请求的自定义注解

filter获取请求的自定义注解

文章有点长,可以先看目录。
代码有点多,如果感兴趣或者需要的话,欢迎和我交流。


一般情况下,对来自浏览器的请求的拦截,是利用Filter实现的,这种方式可以实现Bean预处理、后处理。 

这里是利用springmvc的拦截器开发了log功能,用于跟踪、记录系统用户的操作轨迹,以便日后的认责。

该功能使用很方便,是可配置的、细粒度的日志记录功能。之所以细粒度,因为level分为三层,默认包层(rootLogLevel默认值TRACE),自定义包层(customLogLevel),具体方法层(@Log默认值TRACE)

 

简单介绍SpringMVCHandlerInterceptorAdapter

Spring MVC的拦截器不仅可实现Filter的所有功能,还可以更精确的控制拦截精度。 

Spring为我们提供了org.springframework.web.servlet.handler.HandlerInterceptorAdapter这个适配器,继承此类,可以非常方便的实现自己的拦截器。他有三个方法:

  1. public boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler)
  2. throws Exception{
  3. return true;
  4. }
  5. public voidpostHandle(
  6. HttpServletRequest request, HttpServletResponse response, Objecthandler, ModelAndView modelAndView)
  7. throwsException {
  8. }
  9. public voidafterCompletion(
  10. HttpServletRequest request, HttpServletResponse response, Objecthandler, Exception ex)
  11. throwsException {
  12. }


分别实现预处理、后处理(调用了Service并返回ModelAndView,但未进行页面渲染)、返回处理(已经渲染了页面) 

在preHandle中,可以进行编码、安全控制等处理; 

在postHandle中,有机会修改ModelAndView; 

在afterCompletion中,可以根据ex是否为null判断是否发生了异常,进行日志记录。 

 

配置:spring-mvc.xml

  1. <!--系统日志跟踪功能 -->
  2. <beanid="log4JDBCImpl"class="com.ketayao.ketacustom.log.impl.Log4JDBCImpl" >
  3. <propertyname="logEntityService" ref="logEntityServiceImpl"/>
  4. <propertyname="rootLogLevel" value="ERROR"/>
  5. <!--自定义日志级别 -->
  6. <propertyname="customLogLevel">
  7. <map>
  8. <entrykey="com.ketayao.ketacustom" value="TRACE" />
  9. <entrykey="com.sample" value="INFO" />
  10. </map>
  11. </property>
  12. </bean>
  13. <mvc:interceptors>
  14. <mvc:interceptor>
  15. <mvc:mappingpath="/management/**" />
  16. <mvc:mappingpath="/login/timeout/success"/>
  17. <beanclass="com.ketayao.ketacustom.log.spring.LogInterceptor" >
  18. <propertyname="logAPI" ref="log4JDBCImpl"/>
  19. </bean>
  20. </mvc:interceptor>
  21. </mvc:interceptors>


 

mvc:interceptors

这个标签用于注册一个自定义拦截器或者是WebRequestInterceptors.

可以通过定义URL来进行路径请求拦截,可以做到较为细粒度的拦截控制。

 

 

日志的JDBC实现

LogAPI:自定义LogAPI接口

  1. /**
  2. * 自定义LogAPI接口
  3. * 定义日志记录和日志级别规范
  4. */
  5. publicinterface LogAPI {
  6. voidlog(String message, LogLevel logLevel);
  7. voidlog(String message, Object[] objects, LogLevel logLevel);
  8. /**
  9. *
  10. * 得到全局日志等级
  11. * @return
  12. */
  13. LogLevelgetRootLogLevel();
  14. /**
  15. *
  16. * 得到自定义包的日志等级
  17. * @return
  18. */
  19. Map<String,LogLevel> getCustomLogLevel();
  20. }


 

LogLevel:日志级别枚举类型

  1. /**
  2. * 值越大,等级越高。
  3. */
  4. publicenum LogLevel {
  5. TRACE("TRACE"),
  6. DEBUG("DEBUG"),
  7. INFO("INFO"),
  8. WARN("WARN"),
  9. ERROR("ERROR");
  10. privateString value;
  11. LogLevel(Stringvalue) {
  12. this.value= value;
  13. }
  14. publicString value() {
  15. returnthis.value;
  16. }
  17. }


由代码发现,枚举值后面有属性值,这是Enums的构造函数的用法

 

Log4JDBCImpl:实现LogAPI接口

  1. /**
  2. * 全局日志等级<包日志等级<类和方法日志等级
  3. * @author <ahref="mailto:ketayao@gmail.com">ketayao</a>
  4. * Version 2.1.0
  5. * @since 2013-5-3 下午4:41:55
  6. */
  7. publicclass Log4JDBCImpl implements LogAPI {
  8. privateLogLevel rootLogLevel = LogLevel.ERROR;
  9. privateLogEntityService logEntityService;
  10. privateMap<String, LogLevel> customLogLevel = Maps.newHashMap();
  11. /**
  12. *
  13. * @param message
  14. * @param objects
  15. * @param logLevel
  16. * @seecom.ketayao.ketacustom.log.impl.LogAdapter#log(java.lang.String,java.lang.Object[], com.ketayao.ketacustom.log.LogLevel)
  17. */
  18. @Override
  19. publicvoid log(String message, Object[] objects, LogLevel logLevel){
  20. MessageFormatmFormat = new MessageFormat(message);
  21. Stringresult = mFormat.format(objects);
  22. if(!StringUtils.isNotBlank(result)) {
  23. return;
  24. }
  25. Subjectsubject = SecurityUtils.getSubject();
  26. ShiroDbRealm.ShiroUsershiroUser = (ShiroDbRealm.ShiroUser)subject.getPrincipal();
  27. //result= shiroUser.toString() + ":" + result;
  28. LogEntitylogEntity = new LogEntity();
  29. logEntity.setCreateTime(newDate());
  30. logEntity.setUsername(shiroUser.getLoginName());
  31. logEntity.setMessage(result);
  32. logEntity.setIpAddress(shiroUser.getIpAddress());
  33. logEntity.setLogLevel(logLevel);
  34. logEntityService.save(logEntity);
  35. }
  36. publicvoid setRootLogLevel(LogLevel rootLogLevel) {
  37. this.rootLogLevel= rootLogLevel;
  38. }
  39. /**
  40. *
  41. * @return
  42. * @seecom.ketayao.ketacustom.log.LogTemplate#getRootLogLevel()
  43. */
  44. @Override
  45. publicLogLevel getRootLogLevel() {
  46. returnrootLogLevel;
  47. }
  48. publicvoid setCustomLogLevel(Map<String, LogLevel> customLogLevel) {
  49. this.customLogLevel= customLogLevel;
  50. }
  51. @Override
  52. publicMap<String, LogLevel> getCustomLogLevel() {
  53. returncustomLogLevel;
  54. }
  55. publicvoid setLogEntityService(LogEntityService logEntityService) {
  56. this.logEntityService= logEntityService;
  57. }
  58. @Override
  59. publicvoid log(String message, LogLevel logLevel) {
  60. log(message,null,logLevel);
  61. }
  62. }


 

 

日志的业务逻辑的记录

Log:自定义注解

  1. /**
  2. *
  3. */
  4. @Documented
  5. @Target({METHOD})
  6. @Retention(RUNTIME)
  7. public@interface Log {
  8. /**
  9. *
  10. * 日志信息
  11. * @return
  12. */
  13. Stringmessage();
  14. /**
  15. *
  16. * 日志记录等级
  17. * @return
  18. */
  19. LogLevellevel() default LogLevel.TRACE;
  20. /**
  21. *
  22. * 是否覆盖包日志等级
  23. * 1.为false不会参考level属性。
  24. * 2.为true会参考level属性。
  25. * @return
  26. */
  27. booleanoverride() default false;
  28. }


 

LogUitl:将request放入ThreadLocal用于LOG_ARGUMENTS注入

  1. /**
  2. *将request放入ThreadLocal用于LOG_ARGUMENTS注入。
  3. */
  4. publicabstract class LogUitl {
  5. //用于存储每个线程的request请求
  6. privatestatic final ThreadLocal<HttpServletRequest> LOCAL_REQUEST = newThreadLocal<HttpServletRequest>();
  7. publicstatic void putRequest(HttpServletRequest request) {
  8. LOCAL_REQUEST.set(request);
  9. }
  10. publicstatic HttpServletRequest getRequest() {
  11. returnLOCAL_REQUEST.get();
  12. }
  13. publicstatic void removeRequest() {
  14. LOCAL_REQUEST.remove();
  15. }
  16. /**
  17. * 将LogMessageObject放入LOG_ARGUMENTS。
  18. * 描述
  19. * @param logMessageObject
  20. */
  21. publicstatic void putArgs(LogMessageObject logMessageObject) {
  22. HttpServletRequestrequest = getRequest();
  23. request.setAttribute(SecurityConstants.LOG_ARGUMENTS,logMessageObject);
  24. }
  25. /**
  26. * 得到LogMessageObject。
  27. * 描述
  28. * @param logMessageObject
  29. */
  30. publicstatic LogMessageObject getArgs() {
  31. HttpServletRequestrequest = getRequest();
  32. return(LogMessageObject)request.getAttribute(SecurityConstants.LOG_ARGUMENTS);
  33. }
  34. }


 

TaskController :处理操作Task模块的用户请求逻辑

  1. /**
  2. *莫紧张,仅仅是一个例子。
  3. */
  4. @Controller
  5. @RequestMapping("/management/sample/task")
  6. publicclass TaskController {
  7. @Autowired
  8. privateTaskService taskService;
  9. @Autowired
  10. privateValidator validator;
  11. privatestatic final String CREATE = "management/sample/task/create";
  12. privatestatic final String UPDATE = "management/sample/task/update";
  13. privatestatic final String LIST = "management/sample/task/list";
  14. privatestatic final String VIEW = "management/sample/task/view";
  15. @RequiresPermissions("Task:save")
  16. @RequestMapping(value="/create",method=RequestMethod.GET)
  17. publicString preCreate(Map<String, Object> map) {
  18. returnCREATE;
  19. }
  20. /**
  21. * LogMessageObject的write用法实例。
  22. */
  23. @Log(message="添加了{0}任务,LogMessageObject的isWritten为true。",level=LogLevel.INFO)
  24. @RequiresPermissions("Task:save")
  25. @RequestMapping(value="/create",method=RequestMethod.POST)
  26. public@ResponseBody String create(Task task) {
  27. BeanValidators.validateWithException(validator,task);
  28. taskService.save(task);
  29. //加入一个LogMessageObject,该对象的isWritten为true,会记录日志。
  30. LogUitl.putArgs(LogMessageObject.newWrite().setObjects(newObject[]{task.getTitle()}));
  31. returnAjaxObject.newOk("任务添加成功!").toString();
  32. }
  33. /**
  34. * LogMessageObject的ignore用法实例,ignore不会记录日志。
  35. */
  36. @Log(message="你永远不会看见该日志,LogMessageObject的isWritten为false。",level=LogLevel.INFO)
  37. @RequiresPermissions("Task:edit")
  38. @RequestMapping(value="/update/{id}",method=RequestMethod.GET)
  39. publicString preUpdate(@PathVariable Long id, Map<String, Object> map) {
  40. Tasktask = taskService.get(id);
  41. map.put("task",task);
  42. //加入一个LogMessageObject,该对象的isWritten为false,不会记录日志。
  43. LogUitl.putArgs(LogMessageObject.newIgnore());
  44. returnUPDATE;
  45. }
  46. /**
  47. * Log的level用法实例
  48. *1.level分为三层,默认包层(rootLogLevel默认值TRACE),自定义包层(customLogLevel),具体方法层(@Log默认值TRACE)
  49. *2.参考顺序:默认包层->自定义包层->具体方法层->LogMessageObject
  50. * 3.有自定义包层的level等级会忽略默认包层
  51. * 4.@Log的level大于等于自定义包层或者默认的level会输出日志;小于则不会。
  52. */
  53. @Log(message="Log的level用法实例,LogLevel.TRACE小于自定义包层LogLevel.INFO,不会输出日志。",level=LogLevel.TRACE)
  54. @RequiresPermissions("Task:edit")
  55. @RequestMapping(value="/update",method=RequestMethod.POST)
  56. public@ResponseBody String update(Task task) {
  57. BeanValidators.validateWithException(validator,task);
  58. taskService.update(task);
  59. returnAjaxObject.newOk("任务修改成功!").toString();
  60. }
  61. /**
  62. * Log的override用法实例
  63. * 假如override为true,会忽略掉level
  64. *
  65. * 批量删除展示
  66. */
  67. @Log(message="Log的override用法实例,override为true,会忽略掉level。删除了{0}任务。",level=LogLevel.TRACE, override=true)
  68. @RequiresPermissions("Task:delete")
  69. @RequestMapping(value="/delete",method=RequestMethod.POST)
  70. public@ResponseBody String deleteMany(Long[] ids) {
  71. String[]titles = new String[ids.length];
  72. for(int i = 0; i < ids.length; i++) {
  73. Tasktask = taskService.get(ids[i]);
  74. taskService.delete(task.getId());
  75. titles[i]= task.getTitle();
  76. }
  77. LogUitl.putArgs(LogMessageObject.newWrite().setObjects(newObject[]{Arrays.toString(titles)}));
  78. returnAjaxObject.newOk("任务删除成功!").setCallbackType("").toString();
  79. }
  80. @RequiresPermissions("Task:view")
  81. @RequestMapping(value="/list",method={RequestMethod.GET, RequestMethod.POST})
  82. publicString list(Page page, String keywords, Map<String, Object> map) {
  83. List<Task>tasks = null;
  84. if(StringUtils.isNotBlank(keywords)) {
  85. tasks= taskService.find(page, keywords);
  86. } else{
  87. tasks= taskService.findAll(page);
  88. }
  89. map.put("page",page);
  90. map.put("tasks",tasks);
  91. map.put("keywords",keywords);
  92. returnLIST;
  93. }
  94. /**
  95. * 自定look权限,实例。
  96. * 描述
  97. * @param id
  98. * @param map
  99. * @return
  100. */
  101. @RequiresPermissions("Task:look")
  102. @RequestMapping(value="/view/{id}",method={RequestMethod.GET})
  103. publicString view(@PathVariable Long id, Map<String, Object> map) {
  104. Tasktask = taskService.get(id);
  105. map.put("task",task);
  106. returnVIEW;
  107. }
  108. }


 

使用Filter拦截,将日志信息持久化

LogInterceptor:        继承HandlerInterceptorAdapter,覆盖三个方法实现

  1. /**
  2. * 继承HandlerInterceptorAdapter,覆盖三个方法实现
  3. */
  4. publicclass LogInterceptor extends HandlerInterceptorAdapter {
  5. privatefinal static Logger LOGGER = LoggerFactory.getLogger(LogInterceptor.class);
  6. privateLogAPI logAPI;
  7. /**
  8. * 将request存入LogUitl中的LOCAL_REQUEST。
  9. * @param request
  10. * @param response
  11. * @param handler
  12. * @return
  13. * @throws Exception
  14. */
  15. @Override
  16. publicboolean preHandle(HttpServletRequest request,
  17. HttpServletResponseresponse, Object handler) throws Exception {
  18. LogUitl.putRequest(request);
  19. returntrue;
  20. }
  21. @Override
  22. publicvoid postHandle(HttpServletRequest request,
  23. HttpServletResponseresponse, Object handler,
  24. ModelAndViewmodelAndView) throws Exception {
  25. if(!(handler instanceof HandlerMethod)) {
  26. return;
  27. }
  28. finalHandlerMethod handlerMethod = (HandlerMethod)handler;
  29. Methodmethod = handlerMethod.getMethod();
  30. finalLog log = method.getAnnotation(Log.class);
  31. if (log!= null) {
  32. //得到LogMessageObject
  33. finalLogMessageObject logMessageObject = LogUitl.getArgs();
  34. //另起线程异步操作
  35. newThread(new Runnable() {
  36. @Override
  37. publicvoid run() {
  38. try {
  39. LogLevellastLogLevel = logAPI.getRootLogLevel();
  40. //先对自定义包等级做判断
  41. Map<String,LogLevel> customLogLevel = logAPI.getCustomLogLevel();
  42. if(!customLogLevel.isEmpty()) {
  43. Class<?>clazz = handlerMethod.getBean().getClass();
  44. StringpackageName = clazz.getPackage().getName();
  45. Set<String>keys = customLogLevel.keySet();
  46. for(String key : keys) {
  47. if(packageName.startsWith(key)) {
  48. lastLogLevel= customLogLevel.get(key);
  49. break;
  50. }
  51. }
  52. }
  53. LogMessageObjectdefaultLogMessageObject = logMessageObject;
  54. if(defaultLogMessageObject == null) {
  55. defaultLogMessageObject= LogMessageObject.newWrite();
  56. }
  57. if(defaultLogMessageObject.isWritten()) { // 判断是否写入log
  58. //覆盖,直接写入日志
  59. if(log.override()) {
  60. logAPI.log(log.message(),defaultLogMessageObject.getObjects(), log.level());
  61. }else {
  62. //不覆盖,参考方法的日志等级是否大于等于最终的日志等级
  63. if(!log.override() && log.level().compareTo(lastLogLevel) >= 0 ) {
  64. logAPI.log(log.message(),defaultLogMessageObject.getObjects(), log.level());
  65. }
  66. }
  67. }
  68. }catch (Exception e) {
  69. LOGGER.error(Exceptions.getStackTraceAsString(e));
  70. }
  71. }
  72. }).start();
  73. }
  74. }
  75. /**
  76. * 清除LogUitl中的LOCAL_REQUEST。
  77. * @param request
  78. * @param response
  79. * @param handler
  80. * @param ex
  81. * @throws Exception
  82. * @seeorg.springframework.web.servlet.handler.HandlerInterceptorAdapter#afterCompletion(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse, java.lang.Object, java.lang.Exception)
  83. */
  84. @Override
  85. publicvoid afterCompletion(HttpServletRequest request,
  86. HttpServletResponseresponse, Object handler, Exception ex)
  87. throwsException {
  88. LogUitl.removeRequest();
  89. }
  90. publicvoid setLogAPI(LogAPI logAPI) {
  91. this.logAPI= logAPI;
  92. }



看看最终得到的日志数据

       





声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/342123
推荐阅读
相关标签
  

闽ICP备14008679号