springboot logback日志+异常+阿里云日志 aliyun-log-logback-appender

这篇具有很好参考价值的文章主要介绍了springboot logback日志+异常+阿里云日志 aliyun-log-logback-appender。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

  1. 前言

最近有个新项目用了,springboot3.0,以前项目日志保存得方式是阿里云云服务自动读取日志文件,最近项目部署得方式可能有变化,所以新项目用logback+aliyun-log-logback-appender得方式保存到阿里云日志服务。用logback得原因主要是懒,spring默认就是这个,其他还要各种配置和兼容。

重点

  1. 通过配置MDC控制保存到阿里云的数据,logback-spring.xml要配置对应的mdcFields

  1. 通过ContentCachingRequestWrapper和ContentCachingResponseWrapper取入参和返回数据,这两个不需要太多代码

  1. RestControllerAdvice+ExceptionHandler全局捕获异常并处理

  1. gradle

    implementation 'com.google.protobuf:protobuf-java:2.5.0'
    implementation 'com.aliyun.openservices:aliyun-log-logback-appender:0.1.18'
    implementation 'org.slf4j:slf4j-nop:1.7.25'

阿里云日志应该只需要这么多得引入。

  1. logback-spring.xml 日志配置

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
    <!-- 项目名称 -->
    <property name="PROJECT_NAME" value="apiv3"/>

    <!-- 控制台输出 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] %highlight([%-5level] [%thread] %logger{50} - %msg%n)</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!--为了防止进程退出时,内存中的数据丢失,请加上此选项-->
    <shutdownHook class="ch.qos.logback.core.hook.DefaultShutdownHook"/>

    <appender name="apiv3Log" class="com.aliyun.openservices.log.logback.LoghubAppender">
        <!--必选项-->
        <!-- 账号及网络配置 -->
        <endpoint>cn-beijing.log.aliyuncs.com</endpoint>
        <accessKeyId>####</accessKeyId>
        <accessKeySecret>###</accessKeySecret>

        <!-- sls 项目配置 -->
        <project>#####</project>
        <!--开发环境区分 -->
        <springProfile name="dev">
            <logStore>####dev</logStore>
        </springProfile>
         <!--开发环境区分 -->
        <springProfile name="test">
            <logStore>###test</logStore>
        </springProfile>
        <!--必选项 (end)-->

        <!-- 可选项 -->
        <!--        <topic>your topic</topic>-->
        <!--        <source>your source</source>-->

        <!-- 可选项 详见 '参数说明'-->
        <totalSizeInBytes>104857600</totalSizeInBytes>
        <maxBlockMs>0</maxBlockMs>
        <ioThreadCount>8</ioThreadCount>
        <batchSizeThresholdInBytes>524288</batchSizeThresholdInBytes>
        <batchCountThreshold>4096</batchCountThreshold>
        <lingerMs>2000</lingerMs>
        <retries>10</retries>
        <baseRetryBackoffMs>100</baseRetryBackoffMs>
        <maxRetryBackoffMs>50000</maxRetryBackoffMs>

        <!-- 可选项 通过配置 encoder 的 pattern 自定义 log 的格式 -->
<!--        <encoder>-->
<!--            <pattern>%d %-5level [%thread] %X{traceId} %logger{0}: %msg</pattern>&ndash;&gt;-->
<!--            <pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] %highlight([%-5level] [%thread] %logger{50} - %msg%n)</pattern>-->
<!--            <charset>UTF-8</charset>-->
<!--        </encoder>-->

        <!-- 可选项 设置 time 字段呈现的格式 -->
        <timeFormat>yyyy-MM-dd'T'HH:mmZ</timeFormat>
        <!-- 可选项 设置 time 字段呈现的时区 -->
        <timeZone>UTC+8</timeZone>
        <mdcFields>
            TraceId,#####
        </mdcFields>
    </appender>
    <!--TRACE < DEBUG < INFO < WARN < ERROR < FATAL-->
    <!-- 开发环境下的日志配置 -->
    <springProfile name="dev">
        <root level="INFO">
            <appender-ref ref="CONSOLE"/>
            <appender-ref ref="apiv3Log"/>
        </root>
    </springProfile>
    <springProfile name="test">
        <root level="INFO">
            <!--            <appender-ref ref="CONSOLE"/>-->
            <appender-ref ref="apiv3Log"/>
        </root>
    </springProfile>

    <!-- 生产环境下的日志配置 -->
    <springProfile name="prod">
        <root level="INFO">
           。。。。。。
        </root>
    </springProfile>
</configuration>

这是logback-spring.xml 基本配置。放在resources目录中。

###基本都是阿里云中得一些参数信息,熟悉阿里云日志的,都明白。

mdcFields也是我最近才发现,它会把MDC中的中对应不为null的字段保存在阿里云中。

  1. HttpFilter

因为在日志中要记录请求的参数和返回值,aop实现有点负责,所以用了HttpFilter

@Component
@WebFilter(filterName = "accessLogFilter", urlPatterns = "/*")
@Order(-9999)        // 保证最先执行
public class AccessLogFilter extends HttpFilter {

    private static final Logger logger = LoggerFactory.getLogger(AccessLogAspect.class);

    @Override
    protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        ContentCachingRequestWrapper cachingRequestWrapper = new ContentCachingRequestWrapper(request);
        ContentCachingResponseWrapper cachingResponseWrapper = new ContentCachingResponseWrapper(response);

        UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("user-agent"));
        // 接收到请求,记录请求内容````
        MDC.put("TraceId", TraceIDUtils.getTraceId());
        //客户端类型
        MDC.put("ClientType", userAgent.getOperatingSystem().getDeviceType().getName());
        //客户端操作系统类型
        MDC.put("OsType", userAgent.getOperatingSystem().getName());
        MDC.put("ClientMethod", request.getMethod());
        MDC.put("ClientIp", IpUtil.getIpAddress(request));
        MDC.put("ClientParam", request.getQueryString());
        MDC.put("ClientUrl", request.getRequestURL().toString());
        MDC.put("ClientUri", request.getRequestURI());
        //记录请求开始时间
        long start = RequestTimeUtil.getStart();
        MDC.put("RequestStart", String.valueOf(start));
        //请求头参数
        Map<Object, Object> headerMap = new HashMap<>();
        Enumeration<String> enumeration = cachingRequestWrapper.getHeaderNames();
        while (enumeration.hasMoreElements()) {
            String name = enumeration.nextElement();
            String value = request.getHeader(name);
            headerMap.put(name, value);
        }
        MDC.put("RequestHeader", JSON.toJSONString(headerMap));
        MDC.put("LogType", LogTypEnum.requestStart.name());
        MDC.put("LogTime", String.valueOf(System.currentTimeMillis()));
        //日志输出
        logger.info("--------------request start--------------");
        //这里清理掉MDC 防止变成下一个log输出的垃圾数据
        MDC.remove("LogTime");
        MDC.remove("ClientType");
        MDC.remove("OsType");
        MDC.remove("ClientMethod");
        MDC.remove("ClientIp");
        MDC.remove("ClientParam");
        MDC.remove("ClientUrl");
        MDC.remove("ClientUri");
        MDC.remove("RequestStart");
        MDC.remove("LogType");
        MDC.remove("RequestHeader");
        super.doFilter(cachingRequestWrapper, cachingResponseWrapper, chain);
        String responseBody = new String(cachingResponseWrapper.getContentAsByteArray(), StandardCharsets.UTF_8);

        MDC.put("LogType", LogTypEnum.requestEnd.name());
        MDC.put("RequestParamMap", JSON.toJSONString(cachingRequestWrapper.getParameterMap()));
        String requestBodyParam = new String(cachingRequestWrapper.getContentAsByteArray(), StandardCharsets.UTF_8);
        MDC.put("RequestBodyParam", requestBodyParam);
        MDC.put("ReturnData", responseBody);
        long end = RequestTimeUtil.getEnd();
        MDC.put("RequestEnd", String.valueOf(end));
        MDC.put("RequestProcessTime", String.valueOf(end - start));
        MDC.put("LogTime", String.valueOf(System.currentTimeMillis()));
        logger.info("--------------request end--------------");
        // 这一步很重要,把缓存的响应内容,输出到客户端
        cachingResponseWrapper.copyBodyToResponse();
        MDC.clear();
        RequestTimeUtil.remove();
        TraceIDUtils.remove();
    }

找了很久才找到ContentCachingRequestWrapper和ContentCachingResponseWrapper这两个可以取入参和返回数据的类,不用动其他代码就可以实现了。

其实到这里最基础的请求日志,已经实现了。

springboot  logback日志+异常+阿里云日志 aliyun-log-logback-appender

大概就是这样子的。

  1. 其他aop的日志也是差不多的方式去处理。

  1. 异常处理

RestControllerAdvice+ExceptionHandler全局捕获异常,ResultError是自定义异常,主要处理一些assert抛出的错误。


@RestControllerAdvice
public class ExceptionControllerAdvice {

    private static final Logger logger = LoggerFactory.getLogger(AccessLogAspect.class);

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        // 从异常对象中拿到ObjectError对象
        ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
        // 然后提取错误提示信息进行返回
        return Result.fail(GlobalResultEnum.fail.getCode(), objectError.getDefaultMessage());
    }


    @ExceptionHandler(ResultError.class)
    public Result<Object> APIExceptionHandler(ResultError e) {
        MDC.put("ResponseStatus", String.valueOf(e.getResultEnum().getCode()));
        MDC.put("ResponseGlobalStatus", String.valueOf(e.getResultEnum().getGlobalStatus()));
        MDC.put("ResponseMsg", e.getResultEnum().getMsg());
        MDC.put("LogType", LogTypEnum.resultError.name());
        MDC.put("LogTime", String.valueOf(System.currentTimeMillis()));
        logger.info(JSON.toJSONString(e.getErrorMessage()));
        MDC.remove("LogTime");
        MDC.remove("ResponseStatus");
        MDC.remove("ResponseGlobalStatus");
        MDC.remove("ResponseMsg");
        MDC.remove("LogType");
        return Result.of(e);
    }

}
public class ResultError extends RuntimeException {

    @Getter
    private ResultEnum resultEnum;
    @Getter
    private Object errorMessage;

    public ResultError(ResultEnum resultEnum) {
        super(resultEnum.getMsg());
        this.resultEnum = resultEnum;
    }

    public ResultError(ResultEnum resultEnum, Object errorMessage) {
        super(resultEnum.getMsg());
        this.resultEnum = resultEnum;
        this.errorMessage = errorMessage;
    }

}
public interface ResultEnum {

    /**
     * 状态码
     * @return int
     */
    int getCode();

    /**
     * 全局状态码
     * @return int
     */
    int getGlobalStatus();

    /**
     * 描述信息
     * @return String
     */
    String getMsg();

}

ResultEnum 是全局状态接口,所有的状态都可以继承这个类,实现类似这样。

public enum GlobalResultEnum implements ResultEnum {
    success(100, "success"),
    fail(99, "fail");

    private final int code;
    private final int globalStatus = 0;
    private final String msg;

    GlobalResultEnum(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }


    /**
     * 状态码
     *
     * @return int
     */
    @Override
    public int getCode() {
        return code;
    }

    /**
     * 全局状态码
     *
     * @return int
     */
    @Override
    public int getGlobalStatus() {
        return globalStatus + code;
    }

    /**
     * 描述信息
     *
     * @return String
     */
    @Override
    public String getMsg() {
        return msg;
    }
}

全局定义唯一状态码(globalStatus),定义全局状态接口(ResultEnum)

也挺麻烦,还需要优化。

写的不是很清晰,欢迎讨论。文章来源地址https://www.toymoban.com/news/detail-495924.html

到了这里,关于springboot logback日志+异常+阿里云日志 aliyun-log-logback-appender的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • springboot中logback日志配置

    Spring Boot使用Apache的Commons Logging作为内部的日志框架,其仅仅是一个日志接口,在实际应用中需要为该接口来指定相应的日志实现。 Spring Boot从1.4版本开始内置的日志框架就是Logback;Spring Boot 2.x 默认采用了slf4j+logback的形式,slf4j也是个通用的日志门面。 SpringBoot可以适配所有

    2024年02月16日
    浏览(47)
  • springboot增加logback日志记录ip

    1、增加logback配置文件: 2、增加获取ip工具类: 3、增加logbackxml配置  完整xml:

    2024年02月11日
    浏览(49)
  • SpringBoot设置日志输出级别及Logback日志工具输出到文件

    1.SpringBoot设置日志输出级别 越高包含的就越多,输出的信息也就越多 2.Logback日志工具使用 1.首先需要把你yaml文件里面关于日志的配置去掉,避免冲突报错 2.在resource目录下创建logback-spring.xml文件 2.运行后指定路径就会生成.log文件 3.此时一般默认只有info信息写入,其他两个

    2024年02月16日
    浏览(62)
  • SpringBoot整合Logback日志框架配置全解析

    SpringBoot使用 Commons Logging 进行所有内部日志的记录,但默认配置也提供了对常用日志的支持,如 Java Util Logging,Log4J2,和Logback. 每种logger都可以通过配置使用控制台或文件输出日志内容。 Logback是log4j框架的作者开发的新一代日志框架,它效率更高、能够适应诸多的运行环境

    2024年02月02日
    浏览(61)
  • 【二十八】springboot整合logback实现日志管理

            本章节是记录logback在springboot项目中的简单使用,本文将会演示如何通过logback将日志记录到日志文件或输出到控制台等管理操作。将会从以下几个方面进行讲解。最后实现将特定级别的特定日志保存到日志文件。 一、依赖 以上版本只是测试时使用,实际版本根据

    2024年02月20日
    浏览(35)
  • springboot集成Logback 日志写入数据库

    引入maven依赖 注意:springboot内部是有Logback的包,但是本人使用的时候缺少部分类文件,因此单独映入了一次 建表 logback有三张表logging_event、logging_event_exception、logging_event_property,不需要自己创建,只需要在如下位置找到自己保存的库表生成sql复制运行即可. 添加配置文件(logback-sp

    2024年02月07日
    浏览(51)
  • SpringBoot运行中动态修改logback日志级别

    思路:写一个api接口,通过api接口调用的方式动态修改logback的log日志打印级别 这里提供2个接口,分别是修改logback全局日志级别 ,和单独修改某个package包的日志级别

    2024年02月12日
    浏览(43)
  • SpringBoot多环境配置与添加logback日志

    一个项目会有多个运行环境 所以SpringBoot提供了可以适应多个环境的配置文件  每个文件对应一个端口号 application-dev.yml 开发环境 端口8090 application-test.yml 测试环境 端口8091 application-prod.yml 生产环境 端口8092 在application中选择使用哪个端口号 每个文件设置端口号: 2、添加lo

    2024年01月17日
    浏览(51)
  • 日志采集 logback集成logstash ELK springboot

    logstash依赖  给logback配置logstash的那台机器的ip和服务的端口  在logstash那台机器上配置  在安装了logstash的服务器里,找到logstash文件目录,执行./logstash -f logstash.conf即可启动logstash 以上配置即可实现 logback 到 logstash。 下面是升级配置 ===========================分割线===============

    2024年02月12日
    浏览(47)
  • 6.3 SpringBoot日志进阶实战 Logback配置详解

    在上一篇文章中,我和你介绍了SpringBoot快速入门Slf4j + Logback实战,遗留的问题是如何将日志输出到文件。 今天这篇文章分享了我在SpringBoot中使用Logback配置日志的经验和方法,并提供了详细的代码示例和解释,包括: 滚动文件、异步日志记录、动态指定属性、日志级别、配

    2024年02月07日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包