dubbo2.5-spring4-mybastis3.2-springmvc4-mongodb3.4-redis3.2整合(四)Spring AOP中使用log4j实现http请求日志入mongodb

简介: 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010046908/article/details/53978250 1、面向切面编程的一些术语切面(Aspect):切面用于组织多个Advice,Advice放在切面中的定义。
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010046908/article/details/53978250

1、面向切面编程的一些术语

  • 切面(Aspect):切面用于组织多个Advice,Advice放在切面中的定义。
  • 连接点(Jionpoint):程序执行过程中的明确的点,如方法的调用,或异常的抛出。在SpringAOP中,连接点总是方法的调用。
  • 增强的处理(Advice):AOP框架在特定的切入点执行的增强处理。处理有around、brefore和after等类型。
  • 切入点(Pointcut):可以插入增强处理的连接点。简而言之,当某个连接点满足指定的要求时,该连接点将添加增强处理,该连接点也就变成了切入点。
pointcut xxxPointcut():execution(void H*.say*())
  • 引入:将方法或字段添加到被代理的类中,Spring允许将新的接口引入到任何被调用的处理对象中。例如:你可以使用一个引入,使任何对象实现IsModified接口,以此来简化缓存。
  • 目标对象:被AOP框架进行增强处理的对象,也被称为被增强对象,如果AOP框架采用的是动态的AOP实现 ,那该对象就是一个被代理对象。
  • AOP代理:AOP框架创建的对象,简单说,代理就是对目标对象的加强,Spring中的AOP代理可以是JDK的动态代理,也可以是cglib的动态代理,前者是实现接口 的目标对象的代理,后者为不实现接口的目标对象的代理。
  • 织入(Weaving):将增强处理添加到目标对象中,并创一个被增强对象(AOP代理)的过程就是织入。织入有l两种实现方式—–编译时增强(AspectJ)和运行时增强(如Spring AOP)。Spring和其他纯Java AOP框架一样,在运行时织入。

2、AOP的基本概念

AOP是从程序运行角度考虑程序的流程,提取业务处理的过程的切面,AOP面向的是程序运行中各个步骤,希望以更好的方式来组合业务处理的各个步骤。

AOP框架并不会与特定的代码耦合,AOP框架能处理程序执行中特定的切入点(PointCut),而不是某个具体类的耦合。

3、AOP框架具有如下的两个特征。

  • 各个步骤之间良好的隔离。

  • 源代码无关。

    注:Spring的AOP支持允许将一些通用任务如安全、事务、日志等进行集中式处理。从而提高了更多的复用。

4.如何在Spring中引入AOP功能

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${srping.version}</version>
</dependency>
<dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.7.2</version>
        </dependency>

5、实现Web层的日志切面

实现AOP的切面主要有以下几个要素:

  • 使用 @Aspect 注解将一个java类定义为切面类。
  • 使用 @Pointcut 定义一个切入点,可以是一个规则表达式,比如下例中某个package下的所有函数,也可以是一个注解等。
  • 根据需要在切入点不同位置的切入内容
  • 使用 @Before 在切入点开始处切入内容
  • 使用 @After 在切入点结尾处切入内容
  • 使用 @AfterReturning 在切入点return内容之后切入内容(可以用来对处理返回值做一些加工处理)
  • 使用 @Around 在切入点前后切入内容,并自己控制何时执行切入点自身的内容
  • 使用 @AfterThrowing 用来处理当切入内容部分抛出异常之后的处理逻辑
package com.lidong.util;

import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.mongodb.BasicDBObject;
/**
 1. 保存请求数据到mongodb
 2. @author lidong
 3. @createTime 2016年12月24日
 */
@Aspect
@Order(1)
@Component
public class WebLogAspect {

    private Logger logger = Logger.getLogger("mongodb");
    ThreadLocal<Long> startTime = new ThreadLocal<>();
    HttpServletRequest request;
    JoinPoint mJoinPoint;
    @Pointcut("execution (* com.lidong.*.controller.*.*(..))") 
    public void webLog(){}

    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
            startTime.set(System.currentTimeMillis());   
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        request = attributes.getRequest();
        mJoinPoint = joinPoint;
    }


    private BasicDBObject getBasicDBObject(HttpServletRequest request, JoinPoint joinPoint) {
        BasicDBObject r = new BasicDBObject();
        r.append("requestURL", request.getRequestURL().toString());
        r.append("requestURI", request.getRequestURI());
        r.append("queryString", request.getQueryString());
        r.append("remoteAddr", request.getRemoteAddr());
        r.append("remoteHost", request.getRemoteHost());
        r.append("remotePort", request.getRemotePort());
        r.append("localAddr", request.getLocalAddr());
        r.append("localName", request.getLocalName());
        r.append("method", request.getMethod());
        r.append("headers", getHeadersInfo(request));
        r.append("parameters", request.getParameterMap());
        r.append("classMethod", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        r.append("args", Arrays.toString(joinPoint.getArgs()));
        return r;
    }
  /**
     * 获取请求头部的信息
     * @param request
     * @return
     */
    private Map<String, String> getHeadersInfo(HttpServletRequest request) {
        Map<String, String> map = new HashMap<>();
        Enumeration headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String key = (String) headerNames.nextElement();
            String value = request.getHeader(key);
            map.put(key, value);
        }
        return map;
    }

    @AfterReturning(returning = "ret", pointcut = "webLog()")
    public void doAfterReturning(Object ret) throws Throwable {
             BasicDBObject logInfo = getBasicDBObject(request, mJoinPoint);    
             // 处理完请求,返回内容
             logInfo.append("response",ret.toString());
             logInfo.append("spend_time", (System.currentTimeMillis() - startTime.get()));
             logger.info(logInfo);
     }
}

注意:在WebLogAspect切面中,分别通过doBefore和doAfterReturning两个独立函数实现了切点头部和切点返回后执行的内容,若我们想统计请求的处理时间,就需要在doBefore处记录时间,并在doAfterReturning处通过当前时间与开始处记录的时间计算得到请求处理的消耗时间。

6、实现Web层的日志的日志保存到mongodb数据库中

6.1.通过自定义appender实现

思路:log4j提供的输出器实现自Appender接口,要自定义appender输出到MongoDB,只需要继承AppenderSkeleton类,并实现几个方法即可完成。

  • 引入mongodb的驱动,在pom.xml中引入下面依赖
<dependency>  
    <groupId>org.mongodb</groupId>
    <artifactId>mongodb-driver</artifactId>
    <version>3.2.2</version>
</dependency> 
  • 实现MongoAppender

编写MongoAppender类继承AppenderSkeleton,实现如下:

package com.lidong.util;

import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.spi.LoggingEvent;

import com.mongodb.BasicDBObject;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
/**
 * 自定义appender实现输出日志MongoDB
 * @author lidong
 *
 */
public class MongoAppender  extends AppenderSkeleton {

    private MongoClient mongoClient;
    private MongoDatabase mongoDatabase;
    private MongoCollection<BasicDBObject> logsCollection;
    /**
     * mongodb连接的url
     */
    private String connectionUrl;
    /**
     * 数据库的名称
     */
    private String databaseName;
    /**
     * 集合的名称
     */
    private String collectionName;

    @Override
    protected void append(LoggingEvent loggingEvent) {

        if(mongoDatabase == null) {
            MongoClientURI connectionString = new MongoClientURI(connectionUrl);
            mongoClient = new MongoClient(connectionString);
            mongoDatabase = mongoClient.getDatabase(databaseName);
            logsCollection = mongoDatabase.getCollection(collectionName, BasicDBObject.class);
        }
        //将日志插入到集合
        logsCollection.insertOne((BasicDBObject) loggingEvent.getMessage());

    }

    @Override
    public void close() {
        if(mongoClient != null) {
            mongoClient.close();
        }
    }

    @Override
    public boolean requiresLayout() {
        return false;
    }

    public MongoClient getMongoClient() {
        return mongoClient;
    }

    public void setMongoClient(MongoClient mongoClient) {
        this.mongoClient = mongoClient;
    }

    public MongoDatabase getMongoDatabase() {
        return mongoDatabase;
    }

    public void setMongoDatabase(MongoDatabase mongoDatabase) {
        this.mongoDatabase = mongoDatabase;
    }

    public MongoCollection<BasicDBObject> getLogsCollection() {
        return logsCollection;
    }

    public void setLogsCollection(MongoCollection<BasicDBObject> logsCollection) {
        this.logsCollection = logsCollection;
    }

    public String getConnectionUrl() {
        return connectionUrl;
    }

    public void setConnectionUrl(String connectionUrl) {
        this.connectionUrl = connectionUrl;
    }

    public String getDatabaseName() {
        return databaseName;
    }

    public void setDatabaseName(String databaseName) {
        this.databaseName = databaseName;
    }

    public String getCollectionName() {
        return collectionName;
    }

    public void setCollectionName(String collectionName) {
        this.collectionName = collectionName;
    }



}

6.3 在log4j.prperties中配置

# mongodb输出
log4j.logger.mongodb=INFO, mongodb
log4j.appender.mongodb=com.lidong.util.MongoAppender
log4j.appender.mongodb.connectionUrl=mongodb://127.0.0.1:27017
log4j.appender.mongodb.databaseName=logs
log4j.appender.mongodb.collectionName=logs_request

6.4在spring-mvc.xml中开启统一处理请求日志


    <!-- web统一管理  -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>
    <bean class="com.lidong.util.WebLogAspect" />

6.5在数据库中查看保存的日志

这里写图片描述

代码地址

相关文章
|
3月前
|
缓存 安全 Java
《深入理解Spring》过滤器(Filter)——Web请求的第一道防线
Servlet过滤器是Java Web核心组件,可在请求进入容器时进行预处理与响应后处理,适用于日志、认证、安全、跨域等全局性功能,具有比Spring拦截器更早的执行时机和更广的覆盖范围。
|
3月前
|
缓存 监控 Java
《深入理解Spring》拦截器(Interceptor)——请求处理的艺术
Spring拦截器是Web开发中实现横切关注点的核心组件,基于AOP思想,可在请求处理前后执行日志记录、身份验证、权限控制等通用逻辑。相比Servlet过滤器,拦截器更贴近Spring容器,能访问Bean和上下文,适用于Controller级精细控制。通过实现`HandlerInterceptor`接口的`preHandle`、`postHandle`和`afterCompletion`方法,可灵活控制请求流程。结合配置类注册并设置路径匹配与执行顺序,实现高效复用与维护。常用于认证鉴权、性能监控、统一异常处理等场景,提升应用安全性与可维护性。
|
9月前
|
NoSQL 安全 Java
深入理解 RedisConnectionFactory:Spring Data Redis 的核心组件
在 Spring Data Redis 中,`RedisConnectionFactory` 是核心组件,负责创建和管理与 Redis 的连接。它支持单机、集群及哨兵等多种模式,为上层组件(如 `RedisTemplate`)提供连接抽象。Spring 提供了 Lettuce 和 Jedis 两种主要实现,其中 Lettuce 因其线程安全和高性能特性被广泛推荐。通过手动配置或 Spring Boot 自动化配置,开发者可轻松集成 Redis,提升应用性能与扩展性。本文深入解析其作用、实现方式及常见问题解决方法,助你高效使用 Redis。
934 4
|
4月前
|
NoSQL Java 调度
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
分布式锁是分布式系统中用于同步多节点访问共享资源的机制,防止并发操作带来的冲突。本文介绍了基于Spring Boot和Redis实现分布式锁的技术方案,涵盖锁的获取与释放、Redis配置、服务调度及多实例运行等内容,通过Docker Compose搭建环境,验证了锁的有效性与互斥特性。
299 0
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
|
10月前
|
NoSQL Java 关系型数据库
微服务——SpringBoot使用归纳——Spring Boot 中集成Redis——Redis 介绍
本文介绍在 Spring Boot 中集成 Redis 的方法。Redis 是一种支持多种数据结构的非关系型数据库(NoSQL),具备高并发、高性能和灵活扩展的特点,适用于缓存、实时数据分析等场景。其数据以键值对形式存储,支持字符串、哈希、列表、集合等类型。通过将 Redis 与 Mysql 集群结合使用,可实现数据同步,提升系统稳定性。例如,在网站架构中优先从 Redis 获取数据,故障时回退至 Mysql,确保服务不中断。
389 0
微服务——SpringBoot使用归纳——Spring Boot 中集成Redis——Redis 介绍
|
9月前
|
监控 测试技术 Go
告别传统Log追踪!GOAT如何用HTTP接口重塑代码监控
本文介绍了GOAT(Golang Application Tracing)工具的使用方法,通过一个Echo问答服务实例,详细展示了代码埋点与追踪技术的应用。内容涵盖初始化配置、自动埋点、手动调整埋点、数据监控及清理埋点等核心功能。GOAT适用于灰度发布、功能验证、性能分析、Bug排查和代码重构等场景,助力Go项目质量保障与平稳发布。工具以轻量高效的特点,为开发团队提供数据支持,优化决策流程。
580 89
|
6月前
|
NoSQL Java Redis
Redis基本数据类型及Spring Data Redis应用
Redis 是开源高性能键值对数据库,支持 String、Hash、List、Set、Sorted Set 等数据结构,适用于缓存、消息队列、排行榜等场景。具备高性能、原子操作及丰富功能,是分布式系统核心组件。
615 2
|
8月前
|
消息中间件 缓存 NoSQL
基于Spring Data Redis与RabbitMQ实现字符串缓存和计数功能(数据同步)
总的来说,借助Spring Data Redis和RabbitMQ,我们可以轻松实现字符串缓存和计数的功能。而关键的部分不过是一些"厨房的套路",一旦你掌握了这些套路,那么你就像厨师一样可以准备出一道道饕餮美食了。通过这种方式促进数据处理效率无疑将大大提高我们的生产力。
271 32
|
6月前
|
JSON 前端开发 Java
Spring MVC 核心组件与请求处理机制详解
本文解析了 Spring MVC 的核心组件及请求流程,核心组件包括 DispatcherServlet(中央调度)、HandlerMapping(URL 匹配处理器)、HandlerAdapter(执行处理器)、Handler(业务方法)、ViewResolver(视图解析),其中仅 Handler 需开发者实现。 详细描述了请求执行的 7 步流程:请求到达 DispatcherServlet 后,经映射器、适配器找到并执行处理器,再通过视图解析器渲染视图(前后端分离下视图解析可省略)。 介绍了拦截器的使用(实现 HandlerInterceptor 接口 + 配置类)及与过滤器的区别
517 0
|
9月前
|
缓存 安全 Java
深入解析HTTP请求方法:Spring Boot实战与最佳实践
这篇博客结合了HTTP规范、Spring Boot实现和实际工程经验,通过代码示例、对比表格和架构图等方式,系统性地讲解了不同HTTP方法的应用场景和最佳实践。
878 5

推荐镜像

更多