微服务框架(八)Spring Boot AOP 日志切面实现

简介:   此系列文章将会描述Java框架Spring Boot、服务治理框架Dubbo、应用容器引擎Docker,及使用Spring Boot集成Dubbo、Mybatis等开源框架,其中穿插着Spring Boot中日志切面等技术的实现,然后通过gitlab-CI以持续集成为Docker镜像。   本文为使用Spring Boot AOP 实现日志切面、分离INFO和ERROR级别日志

  此系列文章将会描述Java框架Spring Boot、服务治理框架Dubbo、应用容器引擎Docker,及使用Spring Boot集成Dubbo、Mybatis等开源框架,其中穿插着Spring Boot中日志切面等技术的实现,然后通过gitlab-CI以持续集成为Docker镜像。
  本文为使用Spring Boot AOP 实现日志切面、分离INFO和ERROR级别日志

本系列文章中所使用的框架版本为Spring Boot 2.0.3-RELEASE,Spring 5.0.7-RELEASE,Dubbo 2.6.2。

通用日志组件

为了便于记录日志,实现了通用的日志组件,通过使用注解@Loggable标记方法即可编制入日志切面中

日至切面及日志输出规则均已集成于Maven Archetype

通用日志组件通过以下配置引用

日志切面

根据定义日志切点(@Loggable)环绕处理逻辑:

  1. 根据注解值获取相应的日志对象
  2. 记录日志信息
@Aspect
@Component
public class LogAspect {
   

    private static final Map<Class<?>, Logger> loggerHolder = new ConcurrentHashMap<Class<?>, Logger>();

    @Pointcut(value = "@annotation(com.linghit.common.log.annotation.Loggable)")
    public void log() {
   
    }

    /**
     * 
     * around:根据日志注解类型为方法调用记录日志 <br/>
     */
    @Around("log()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
   
        AbstractLogBean logBean = null;

        Method method = MethodSignature.class.cast(joinPoint.getSignature())
                .getMethod();
        Annotation[] annotations = method.getAnnotations();
        for (Annotation annotation : annotations) {
   
            if (annotation instanceof Loggable) {
   
                Loggable loggable = Loggable.class.cast(annotation);
                logBean = getLogBean(loggable.value(), joinPoint);
                break;
            }
        }

        Object retVal = null;
        if (null == logBean) {
   
            retVal = joinPoint.proceed(joinPoint.getArgs());
        } else {
   
            logBean.setEventName(StringUtils.isBlank(logBean.getEventName()) ? method
                    .getName() : logBean.getEventName() + "_"
                    + method.getName());
            logBean.setRequest(joinPoint.getArgs());
            try {
   
                retVal = joinPoint.proceed(joinPoint.getArgs());
            } catch (Exception e) {
   
                retVal = Resp.createError(RespCode.BUSINESS_INVALID,
                        "service.fail", "服务失败");

                logBean.setE(e);

            } finally {
   

                logBean.setResponse(retVal);

                Logger logger = getLogger(joinPoint.getTarget().getClass());
                LogUtils.log(logger, logBean);
            }
        }

        return retVal;

    }

    private AbstractLogBean getLogBean(DataAccessType type, JoinPoint joinPoint) {
   
        AbstractLogBean logBean = null;
        switch (type.getValue()) {
   
        case "MySQL":
            logBean = DataAccessLogBean.newDataAccessMysqlLogBean();
            break;
        case "Http":
            logBean = DataAccessLogBean.newDataAccessHttpLogBean();
            break;
        case "Redis":
            logBean = DataAccessLogBean.newDataAccessRedisLogBean();
            break;
        default:
            logBean = new ServiceAccessLogBean(joinPoint.getTarget().getClass()
                    .getSimpleName(), DataAccessType.DUBBO.getValue(),
                    RpcContextUtils.getClientIp(),
                    RpcContextUtils.getLocalAddress());
        }
        return logBean;
    }

    private Logger getLogger(Class<?> clazz) {
   
        if (!loggerHolder.containsKey(clazz)) {
   
            loggerHolder.put(clazz, LoggerFactory.getLogger(clazz));
        }
        return loggerHolder.get(clazz);
    }
}

日志注解

@Target({
    ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Loggable {
   

    /**
     * Name of the logType in which the logging takes place.
     * <p>
     * May be used to determine the target cache (or caches), matching the
     * qualifier value (or the bean name(s)) of (a) specific bean definition.
     */
    DataAccessType value() default DataAccessType.DUBBO;
}

切面扫描及使用

Spring Boot启动类扫描日志切面组件,及配置切面代理为true

@EnableAspectJAutoProxy(proxyTargetClass = true)
@ComponentScan("com.example.service.handler.LogAspect")

使用时只需在相应方法处加入@Loggable注解

@Loggable
public void test{
   }

相关依赖

Spring Boot AOP 起步依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>${springboot.version}</version>
</dependency>

日志分离术

为了便于查看及采集错误日志,下述配置设置INFOERROR日志输出至不同文件

filePattern中需含有%i,每次rollover时,计数器将每次加1,若达到max的值,将删除旧的文件

log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="info" monitorInterval="30"
    name="Log4j2Config">
    <Properties>
        <Property name="PATTERN">[%p] [%d{yyyy-MM-dd HH:mm:ss}][%c{10}]%m%n
        </Property>
        <Property name="filePatch">./log/${projectName}/</Property>
        <Property name="fileName">${projectName}.log</Property>
        <Property name="errorFileName">${projectName}-error.log</Property>
    </Properties>
    <Appenders>
        <!-- 类型名为Console,名称为必须属性 -->
        <Console name="STDOUT">
            <PatternLayout charset="UTF-8" pattern="${PATTERN}" />
        </Console>

        <RollingFile name="DailyRollingFile" fileName="${filePatch}${fileName}"
            filePattern="${filePatch}${fileName}.%d{yyyy-MM-dd}.%i">
            <PatternLayout charset="UTF-8" pattern="${PATTERN}" />
            <Filters>
                <!--如果是error级别拒绝 -->
                <ThresholdFilter level="error" onMatch="DENY"
                    onMismatch="NEUTRAL" />
                <!--如果是debug\info\warn输出 -->
                <ThresholdFilter level="debug" onMatch="ACCEPT"
                    onMismatch="DENY" />
            </Filters>
            <Policies>
                <!-- 一般与 filePattern联用 以日志的命名精度来确定单位 这里用yyyy-MM-dd来记录 所以1 表示是以天为周期存储文件 -->
                <TimeBasedTriggeringPolicy interval="1"
                    modulate="true" />
                <!-- 日志文件大小 <SizeBasedTriggeringPolicy size="1 MB" /> -->
            </Policies>

            <!-- 最多保留文件数 -->
            <DefaultRolloverStrategy max="7" />
        </RollingFile>

        <RollingFile name="ErrorDailyRollingFile" fileName="${filePatch}${errorFileName}"
            filePattern="${filePatch}${errorFileName}.%d{yyyy-MM-dd}.%i">
            <PatternLayout charset="UTF-8" pattern="${PATTERN}" />
            <Filters>
                <ThresholdFilter level="error" onMatch="ACCEPT"
                    onMismatch="DENY" />
            </Filters>
            <Policies>
                <!-- 一般与 filePattern联用 以日志的命名精度来确定单位 这里用yyyy-MM-dd来记录 所以1 表示是以天为周期存储文件 -->
                <TimeBasedTriggeringPolicy interval="1"
                    modulate="true" />
                <!-- 日志文件大小 <SizeBasedTriggeringPolicy size="1 MB" /> -->
            </Policies>

            <!-- 最多保留文件数 -->
            <DefaultRolloverStrategy max="7" />
        </RollingFile>

        <!-- Socket Apppender配置,通过TCP协议连接Logstash <Socket name="LOGSTASH" host="127.0.0.1" 
            port="9001" protocol="TCP"> <PatternLayout pattern="${PATTERN}" /> </Socket> -->

    </Appenders>

    <Loggers>
        <!-- root loggerConfig设置 -->
        <AsyncRoot level="info" additivity="false">
            <AppenderRef ref="STDOUT" />
            <AppenderRef ref="DailyRollingFile" />
            <AppenderRef ref="ErrorDailyRollingFile" />
        </AsyncRoot>
    </Loggers>

</Configuration>

日志输出效果如下:

log
    project.name
        service.log
        service.log.2018-8-10
        service-error.log
        service-error.log.2018-8-10
相关实践学习
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
相关文章
|
3月前
|
数据可视化 Java BI
将 Spring 微服务与 BI 工具集成:最佳实践
本文探讨了 Spring 微服务与商业智能(BI)工具集成的潜力与实践。随着微服务架构和数据分析需求的增长,Spring Boot 和 Spring Cloud 提供了构建可扩展、弹性服务的框架,而 BI 工具则增强了数据可视化与实时分析能力。文章介绍了 Spring 微服务的核心概念、BI 工具在企业中的作用,并深入分析了两者集成带来的优势,如实时数据处理、个性化报告、数据聚合与安全保障。同时,文中还总结了集成过程中的最佳实践,包括事件驱动架构、集中配置管理、数据安全控制、模块化设计与持续优化策略,旨在帮助企业构建高效、智能的数据驱动系统。
223 1
将 Spring 微服务与 BI 工具集成:最佳实践
|
3月前
|
安全 Java Ruby
我尝试了所有后端框架 — — 这就是为什么只有 Spring Boot 幸存下来
作者回顾后端开发历程,指出多数框架在生产环境中难堪重负。相比之下,Spring Boot凭借内置安全、稳定扩展、完善生态和企业级支持,成为构建高可用系统的首选,真正经受住了时间与规模的考验。
304 2
|
3月前
|
存储 安全 Java
管理 Spring 微服务中的分布式会话
在微服务架构中,管理分布式会话是确保用户体验一致性和系统可扩展性的关键挑战。本文探讨了在 Spring 框架下实现分布式会话管理的多种方法,包括集中式会话存储和客户端会话存储(如 Cookie),并分析了它们的优缺点。同时,文章还涵盖了与分布式会话相关的安全考虑,如数据加密、令牌验证、安全 Cookie 政策以及服务间身份验证。此外,文中强调了分布式会话在提升系统可扩展性、增强可用性、实现数据一致性及优化资源利用方面的显著优势。通过合理选择会话管理策略,结合 Spring 提供的强大工具,开发人员可以在保证系统鲁棒性的同时,提供无缝的用户体验。
|
3月前
|
消息中间件 Java 数据库
Spring 微服务中的数据一致性:最终一致性与强一致性
本文探讨了在Spring微服务中实现数据一致性的策略,重点分析了最终一致性和强一致性的定义、优缺点及适用场景。结合Spring Boot与Spring Cloud框架,介绍了如何根据业务需求选择合适的一致性模型,并提供了实现建议,帮助开发者在分布式系统中确保数据的可靠性与同步性。
278 0
|
2月前
|
安全 前端开发 Java
《深入理解Spring》:现代Java开发的核心框架
Spring自2003年诞生以来,已成为Java企业级开发的基石,凭借IoC、AOP、声明式编程等核心特性,极大简化了开发复杂度。本系列将深入解析Spring框架核心原理及Spring Boot、Cloud、Security等生态组件,助力开发者构建高效、可扩展的应用体系。(238字)
|
2月前
|
监控 Cloud Native Java
Spring Boot 3.x 微服务架构实战指南
🌟蒋星熠Jaxonic,技术宇宙中的星际旅人。深耕Spring Boot 3.x与微服务架构,探索云原生、性能优化与高可用系统设计。以代码为笔,在二进制星河中谱写极客诗篇。关注我,共赴技术星辰大海!(238字)
Spring Boot 3.x 微服务架构实战指南
|
2月前
|
负载均衡 Java API
《深入理解Spring》Spring Cloud 构建分布式系统的微服务全家桶
Spring Cloud为微服务架构提供一站式解决方案,涵盖服务注册、配置管理、负载均衡、熔断限流等核心功能,助力开发者构建高可用、易扩展的分布式系统,并持续向云原生演进。
|
2月前
|
消息中间件 缓存 Java
Spring框架优化:提高Java应用的性能与适应性
以上方法均旨在综合考虑Java Spring 应该程序设计原则, 数据库交互, 编码实践和系统架构布局等多角度因素, 旨在达到高效稳定运转目标同时也易于未来扩展.
147 8
|
3月前
|
消息中间件 Java Kafka
消息队列比较:Spring 微服务中的 Kafka 与 RabbitMQ
本文深入解析了 Kafka 和 RabbitMQ 两大主流消息队列在 Spring 微服务中的应用与对比。内容涵盖消息队列的基本原理、Kafka 与 RabbitMQ 的核心概念、各自优势及典型用例,并结合 Spring 生态的集成方式,帮助开发者根据实际需求选择合适的消息中间件,提升系统解耦、可扩展性与可靠性。
255 1
消息队列比较:Spring 微服务中的 Kafka 与 RabbitMQ
|
3月前
|
Prometheus 监控 Java
日志收集和Spring 微服务监控的最佳实践
在微服务架构中,日志记录与监控对系统稳定性、问题排查和性能优化至关重要。本文介绍了在 Spring 微服务中实现高效日志记录与监控的最佳实践,涵盖日志级别选择、结构化日志、集中记录、服务ID跟踪、上下文信息添加、日志轮转,以及使用 Spring Boot Actuator、Micrometer、Prometheus、Grafana、ELK 堆栈等工具进行监控与可视化。通过这些方法,可提升系统的可观测性与运维效率。
384 1
日志收集和Spring 微服务监控的最佳实践