当前位置: 代码网 > it编程>编程语言>Java > Spring Boot/Spring Cloud 应用日志书写详细实例指南

Spring Boot/Spring Cloud 应用日志书写详细实例指南

2026年03月20日 Java 我要评论
spring boot/spring cloud 应用日志书写详细指南1. 日志框架选择与配置1.1 推荐日志框架组合<!-- spring boot 默认使用,无需额外引入 --><

spring boot/spring cloud 应用日志书写详细指南

1. 日志框架选择与配置

1.1 推荐日志框架组合

<!-- spring boot 默认使用,无需额外引入 -->
<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-logging</artifactid>
</dependency>
<!-- 如需使用 log4j2 -->
<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-log4j2</artifactid>
</dependency>

1.2 基础配置

# application.yml
logging:
  level:
    com.yourcompany: info
    org.springframework: warn
    org.hibernate: error
  pattern:
    console: "%d{yyyy-mm-dd hh:mm:ss.sss} [%thread] %-5level %logger{36} - %msg%n"
    file: "%d{yyyy-mm-dd hh:mm:ss.sss} [%thread] %-5level %logger{36} - %msg%n"
  file:
    name: logs/application.log
  logback:
    rollingpolicy:
      max-file-size: 10mb
      max-history: 30

2. 日志级别规范

2.1 级别定义与使用场景

@component
public class userservice {
    private static final logger logger = loggerfactory.getlogger(userservice.class);
    public user createuser(userdto userdto) {
        // trace: 详细的调试信息,通常在生产环境关闭
        logger.trace("开始创建用户,输入参数: {}", userdto);
        // debug: 调试信息,用于开发阶段问题定位
        logger.debug("用户数据验证通过,准备持久化");
        try {
            user user = userrepository.save(converttoentity(userdto));
            // info: 重要的业务流程节点
            logger.info("用户创建成功,用户id: {}, 用户名: {}", user.getid(), user.getusername());
            return user;
        } catch (dataintegrityviolationexception e) {
            // warn: 非预期但可处理的情况
            logger.warn("用户数据重复,用户名: {}", userdto.getusername(), e);
            throw new businessexception("用户已存在");
        } catch (exception e) {
            // error: 系统错误和异常情况
            logger.error("用户创建失败,用户名: {}", userdto.getusername(), e);
            throw new systemexception("系统内部错误");
        }
    }
}

3. 日志内容规范

3.1 结构化日志格式

@service
@slf4j // 推荐使用 lombok @slf4j 注解
public class orderservice {
    public order createorder(orderrequest request) {
        // 关键业务操作开始日志
        log.info("创建订单开始 | 用户id:{} | 商品数量:{} | 总金额:{}", 
                request.getuserid(), 
                request.getitems().size(),
                request.gettotalamount());
        try {
            // 业务逻辑处理
            order order = processorder(request);
            // 成功日志包含关键业务id
            log.info("订单创建成功 | 订单id:{} | 状态:{} | 用户id:{}", 
                    order.getid(), 
                    order.getstatus(),
                    order.getuserid());
            return order;
        } catch (inventoryexception e) {
            // 异常日志包含上下文信息和异常
            log.error("库存不足创建订单失败 | 用户id:{} | 错误码:{}", 
                     request.getuserid(), 
                     "insufficient_inventory", e);
            throw e;
        }
    }
}

3.2 json 结构化日志(推荐用于微服务)

@component
public class structuredlogger {
    public void logpaymentevent(paymentevent event) {
        map<string, object> logdata = new hashmap<>();
        logdata.put("event", "payment_processed");
        logdata.put("orderid", event.getorderid());
        logdata.put("amount", event.getamount());
        logdata.put("paymentmethod", event.getpaymentmethod());
        logdata.put("timestamp", instant.now().tostring());
        logdata.put("traceid", mdc.get("traceid"));
        log.info(json.tojsonstring(logdata));
    }
    public void logerror(string errorcode, string message, string module, throwable t) {
        map<string, object> errorlog = new hashmap<>();
        errorlog.put("level", "error");
        errorlog.put("errorcode", errorcode);
        errorlog.put("message", message);
        errorlog.put("module", module);
        errorlog.put("timestamp", instant.now().tostring());
        errorlog.put("stacktrace", getstacktrace(t));
        errorlog.put("traceid", mdc.get("traceid"));
        log.error(json.tojsonstring(errorlog));
    }
    private string getstacktrace(throwable t) {
        stringwriter sw = new stringwriter();
        t.printstacktrace(new printwriter(sw));
        return sw.tostring();
    }
}

4. 日志上下文信息

4.1 使用 mdc (mapped diagnostic context)

@aspect
@component
@slf4j
public class loggingaspect {
    @around("execution(* com.yourcompany..*controller.*(..))")
    public object logcontroller(proceedingjoinpoint joinpoint) throws throwable {
        // 设置请求上下文
        mdc.put("traceid", generatetraceid());
        mdc.put("userid", getcurrentuserid());
        mdc.put("clientip", getclientip());
        string classname = joinpoint.gettarget().getclass().getsimplename();
        string methodname = joinpoint.getsignature().getname();
        log.info("请求开始 | 类:{} | 方法:{}", classname, methodname);
        long starttime = system.currenttimemillis();
        try {
            object result = joinpoint.proceed();
            long executiontime = system.currenttimemillis() - starttime;
            log.info("请求完成 | 类:{} | 方法:{} | 耗时:{}ms", 
                    classname, methodname, executiontime);
            return result;
        } catch (exception e) {
            long executiontime = system.currenttimemillis() - starttime;
            log.error("请求异常 | 类:{} | 方法:{} | 耗时:{}ms | 异常:{}", 
                     classname, methodname, executiontime, e.getmessage(), e);
            throw e;
        } finally {
            // 清理 mdc
            mdc.clear();
        }
    }
}

4.2 请求链路追踪

@component
public class tracefilter implements filter {
    @override
    public void dofilter(servletrequest request, servletresponse response, 
                        filterchain chain) throws ioexception, servletexception {
        httpservletrequest httprequest = (httpservletrequest) request;
        string traceid = httprequest.getheader("x-trace-id");
        if (traceid == null || traceid.isempty()) {
            traceid = generatetraceid();
        }
        mdc.put("traceid", traceid);
        mdc.put("requesturi", httprequest.getrequesturi());
        mdc.put("useragent", httprequest.getheader("user-agent"));
        try {
            chain.dofilter(request, response);
        } finally {
            mdc.clear();
        }
    }
}

5. 性能优化建议

5.1 避免日志性能问题

@service
@slf4j
public class performancesensitiveservice {
    // 不好的写法:在调试级别进行字符串拼接
    public void badlogging(user user) {
        // 即使日志级别高于debug,字符串拼接仍然会执行
        log.debug("用户信息: " + user.tostring() + ", 时间: " + system.currenttimemillis());
    }
    // 好的写法:使用参数化日志
    public void goodlogging(user user) {
        // 只有在debug级别启用时才会进行字符串处理
        log.debug("用户信息: {}, 时间: {}", user, system.currenttimemillis());
    }
    // 对于复杂的日志消息,使用条件判断
    public void conditionallogging(list<user> users) {
        if (log.isdebugenabled()) {
            log.debug("处理用户列表: {}", formatuserlist(users)); // formatuserlist 比较耗时
        }
    }
}

6. 异常日志处理

6.1 统一的异常日志处理

@restcontrolleradvice
@slf4j
public class globalexceptionhandler {
    @exceptionhandler(businessexception.class)
    public responseentity<errorresponse> handlebusinessexception(businessexception e) {
        // warn 级别记录业务异常
        log.warn("业务异常 | 错误码:{} | 错误信息:{} | 请求路径:{}", 
                e.geterrorcode(), e.getmessage(), getcurrentrequestpath());
        return responseentity.status(httpstatus.bad_request)
                .body(new errorresponse(e.geterrorcode(), e.getmessage()));
    }
    @exceptionhandler(exception.class)
    public responseentity<errorresponse> handlesystemexception(exception e) {
        // error 级别记录系统异常
        log.error("系统异常 | 异常类型:{} | 请求路径:{}", 
                 e.getclass().getsimplename(), getcurrentrequestpath(), e);
        return responseentity.status(httpstatus.internal_server_error)
                .body(new errorresponse("system_error", "系统内部错误"));
    }
}

7. 日志配置文件示例

7.1 logback 配置 (logback-spring.xml)

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <property name="log_pattern" value="%d{yyyy-mm-dd hh:mm:ss.sss} [%thread] %-5level %logger{36} [%x{traceid}] - %msg%n"/>
    <property name="log_path" value="logs"/>
    <!-- 控制台输出 -->
    <appender name="console" class="ch.qos.logback.core.consoleappender">
        <encoder>
            <pattern>${log_pattern}</pattern>
        </encoder>
    </appender>
    <!-- 文件输出 -->
    <appender name="file" class="ch.qos.logback.core.rolling.rollingfileappender">
        <file>${log_path}/application.log</file>
        <encoder>
            <pattern>${log_pattern}</pattern>
        </encoder>
        <rollingpolicy class="ch.qos.logback.core.rolling.sizeandtimebasedrollingpolicy">
            <filenamepattern>${log_path}/application.%d{yyyy-mm-dd}.%i.log</filenamepattern>
            <maxfilesize>10mb</maxfilesize>
            <maxhistory>30</maxhistory>
            <totalsizecap>1gb</totalsizecap>
        </rollingpolicy>
    </appender>
    <!-- 错误日志单独文件 -->
    <appender name="error_file" class="ch.qos.logback.core.rolling.rollingfileappender">
        <file>${log_path}/error.log</file>
        <filter class="ch.qos.logback.classic.filter.thresholdfilter">
            <level>error</level>
        </filter>
        <encoder>
            <pattern>${log_pattern}</pattern>
        </encoder>
        <rollingpolicy class="ch.qos.logback.core.rolling.sizeandtimebasedrollingpolicy">
            <filenamepattern>${log_path}/error.%d{yyyy-mm-dd}.%i.log</filenamepattern>
            <maxfilesize>10mb</maxfilesize>
            <maxhistory>30</maxhistory>
        </rollingpolicy>
    </appender>
    <root level="info">
        <appender-ref ref="console"/>
        <appender-ref ref="file"/>
        <appender-ref ref="error_file"/>
    </root>
    <!-- 特定包日志级别 -->
    <logger name="com.yourcompany" level="debug"/>
    <logger name="org.springframework" level="warn"/>
    <logger name="org.hibernate" level="warn"/>
</configuration>

8. 最佳实践总结

8.1 必须遵循的原则

  1. 适当的日志级别:生产环境使用 info,开发环境使用 debug
  2. 有意义的日志消息:包含足够的上下文信息
  3. 统一格式:团队使用统一的日志格式
  4. 性能考虑:避免在日志中执行耗时操作
  5. 安全考虑:不要记录敏感信息(密码、token等)

8.2 推荐的日志场景

// 1. 方法入口和出口
log.info("方法开始: {},参数: {}", methodname, parameters);
log.info("方法结束: {},结果: {}", methodname, result);
// 2. 业务流程关键节点
log.info("订单状态变更 | 订单id:{} | 原状态:{} | 新状态:{}", orderid, oldstatus, newstatus);
// 3. 外部调用
log.info("调用外部服务开始 | 服务名:{} | 请求参数:{}", servicename, request);
log.info("调用外部服务结束 | 服务名:{} | 响应结果:{}", servicename, response);
// 4. 异常情况
log.error("数据库操作失败 | sql:{} | 参数:{}", sql, params, e);
// 5. 定时任务
log.info("定时任务开始 | 任务名:{} | 执行时间:{}", taskname, starttime);
log.info("定时任务结束 | 任务名:{} | 执行结果:{} | 耗时:{}ms", taskname, result, costtime);

通过遵循这些指南,可以建立统一、可维护、高效的日志系统,大大提高系统的可观测性和问题排查效率。

spring boot/cloud 全场景日志书写示例

1. 控制器层日志

1.1 rest controller 完整示例

@restcontroller
@requestmapping("/api/v1/users")
@slf4j
public class usercontroller {
    @getmapping("/{userid}")
    public responseentity<uservo> getuser(@pathvariable long userid,
                                         @requestheader("user-agent") string useragent) {
        // 请求入口日志
        log.info("查询用户开始 | 用户id:{} | 客户端:{} | 线程:{}", 
                userid, useragent, thread.currentthread().getname());
        long starttime = system.currenttimemillis();
        try {
            uservo user = userservice.getuserbyid(userid);
            // 成功响应日志
            log.info("查询用户成功 | 用户id:{} | 用户名:{} | 耗时:{}ms", 
                    userid, user.getusername(), system.currenttimemillis() - starttime);
            return responseentity.ok(user);
        } catch (usernotfoundexception e) {
            // 业务异常日志
            log.warn("用户不存在 | 用户id:{} | 耗时:{}ms", 
                    userid, system.currenttimemillis() - starttime, e);
            return responseentity.notfound().build();
        } catch (exception e) {
            // 系统异常日志
            log.error("查询用户异常 | 用户id:{} | 异常类型:{} | 耗时:{}ms", 
                     userid, e.getclass().getsimplename(), 
                     system.currenttimemillis() - starttime, e);
            return responseentity.internalservererror().build();
        }
    }
    @postmapping
    public responseentity<createuserresponse> createuser(@valid @requestbody createuserrequest request,
                                                        httpservletrequest httprequest) {
        string clientip = httprequest.getremoteaddr();
        log.info("创建用户开始 | 用户名:{} | 邮箱:{} | 客户端ip:{}", 
                request.getusername(), request.getemail(), clientip);
        // 敏感信息脱敏
        log.debug("创建用户详细参数 | 用户名:{} | 角色:{} | 部门:{}", 
                 request.getusername(), request.getrole(), request.getdepartment());
        long starttime = system.currenttimemillis();
        try {
            uservo user = userservice.createuser(request);
            log.info("创建用户成功 | 用户id:{} | 用户名:{} | 耗时:{}ms", 
                    user.getid(), user.getusername(), system.currenttimemillis() - starttime);
            return responseentity.status(httpstatus.created)
                    .body(new createuserresponse(user.getid(), "创建成功"));
        } catch (duplicateuserexception e) {
            log.warn("创建用户失败-用户已存在 | 用户名:{} | 邮箱:{}", 
                    request.getusername(), request.getemail(), e);
            return responseentity.badrequest()
                    .body(new createuserresponse(null, "用户已存在"));
        }
    }
    @putmapping("/{userid}/status")
    public responseentity<void> updateuserstatus(@pathvariable long userid,
                                                @requestbody updatestatusrequest request) {
        log.info("更新用户状态开始 | 用户id:{} | 原状态:{} | 新状态:{}", 
                userid, getcurrentstatus(userid), request.getstatus());
        userservice.updatestatus(userid, request.getstatus());
        log.info("更新用户状态成功 | 用户id:{} | 新状态:{}", userid, request.getstatus());
        return responseentity.ok().build();
    }
}

2. 服务层日志

2.1 业务服务完整示例

@service
@slf4j
@transactional
public class orderservice {
    public order createorder(createorderrequest request) {
        log.info("创建订单开始 | 用户id:{} | 商品数量:{} | 总金额:{}", 
                request.getuserid(), request.getitems().size(), request.gettotalamount());
        // 参数验证日志
        if (log.isdebugenabled()) {
            log.debug("订单详细参数 | 商品列表:{} | 收货地址:{}", 
                     request.getitems(), masksensitiveinfo(request.getaddress()));
        }
        try {
            // 1. 验证库存
            log.debug("开始验证库存 | 商品数量:{}", request.getitems().size());
            inventoryservice.validatestock(request.getitems());
            log.debug("库存验证通过");
            // 2. 计算价格
            log.debug("开始计算订单价格");
            bigdecimal finalamount = calculatefinalamount(request);
            log.debug("价格计算完成 | 最终金额:{}", finalamount);
            // 3. 创建订单
            order order = buildorder(request, finalamount);
            order = orderrepository.save(order);
            log.info("订单创建完成 | 订单id:{} | 订单号:{} | 状态:{}", 
                    order.getid(), order.getorderno(), order.getstatus());
            // 4. 扣减库存
            log.debug("开始扣减库存 | 订单id:{}", order.getid());
            inventoryservice.deductstock(order.getid(), request.getitems());
            log.debug("库存扣减完成");
            // 5. 发送创建事件
            eventpublisher.publishevent(new ordercreatedevent(order));
            log.debug("订单创建事件已发布");
            log.info("订单流程全部完成 | 订单id:{} | 总耗时:{}ms", 
                    order.getid(), system.currenttimemillis() - starttime);
            return order;
        } catch (inventoryexception e) {
            log.error("创建订单失败-库存不足 | 用户id:{} | 错误详情:{}", 
                     request.getuserid(), e.getmessage(), e);
            throw new businessexception("库存不足,创建订单失败");
        } catch (paymentexception e) {
            log.error("创建订单失败-支付异常 | 用户id:{} | 错误码:{}", 
                     request.getuserid(), e.geterrorcode(), e);
            throw new businessexception("支付处理失败");
        } catch (exception e) {
            log.error("创建订单失败-系统异常 | 用户id:{} | 异常类型:{}", 
                     request.getuserid(), e.getclass().getsimplename(), e);
            throw new systemexception("系统繁忙,请稍后重试");
        }
    }
    public void processexpiredorders() {
        log.info("开始处理过期订单 | 线程:{}", thread.currentthread().getname());
        int processedcount = 0;
        int successcount = 0;
        int failurecount = 0;
        long starttime = system.currenttimemillis();
        try {
            list<order> expiredorders = orderrepository.findexpiredorders();
            log.info("找到过期订单数量:{}", expiredorders.size());
            for (order order : expiredorders) {
                processedcount++;
                try {
                    log.debug("处理过期订单 | 订单id:{} | 订单号:{}", order.getid(), order.getorderno());
                    cancelorder(order.getid(), "系统自动取消-超时未支付");
                    successcount++;
                    log.debug("过期订单处理成功 | 订单id:{}", order.getid());
                } catch (exception e) {
                    failurecount++;
                    log.error("处理过期订单失败 | 订单id:{} | 异常:{}", order.getid(), e.getmessage(), e);
                }
            }
            log.info("过期订单处理完成 | 总数:{} | 成功:{} | 失败:{} | 总耗时:{}ms", 
                    processedcount, successcount, failurecount, 
                    system.currenttimemillis() - starttime);
        } catch (exception e) {
            log.error("处理过期订单任务异常 | 异常类型:{}", e.getclass().getsimplename(), e);
            throw e;
        }
    }
}

3. 数据访问层日志

3.1 repository 日志示例

@repository
@slf4j
public class userrepository {
    public user findbyusername(string username) {
        log.debug("根据用户名查询用户 | 用户名:{}", username);
        long starttime = system.currenttimemillis();
        try {
            user user = usermapper.selectbyusername(username);
            if (user == null) {
                log.debug("用户不存在 | 用户名:{} | 耗时:{}ms", 
                         username, system.currenttimemillis() - starttime);
            } else {
                log.debug("用户查询成功 | 用户id:{} | 用户名:{} | 耗时:{}ms", 
                         user.getid(), username, system.currenttimemillis() - starttime);
            }
            return user;
        } catch (dataaccessexception e) {
            log.error("数据库查询异常 | sql:查询用户 | 参数:{} | 异常类型:{}", 
                     username, e.getclass().getsimplename(), e);
            throw new repositoryexception("用户查询失败", e);
        }
    }
    @transactional
    public void updateuserstatus(long userid, userstatus newstatus) {
        userstatus oldstatus = getuserstatus(userid);
        log.info("更新用户状态 | 用户id:{} | 原状态:{} | 新状态:{}", userid, oldstatus, newstatus);
        try {
            int affectedrows = usermapper.updatestatus(userid, newstatus);
            if (affectedrows == 0) {
                log.warn("更新用户状态失败-记录不存在 | 用户id:{}", userid);
                throw new entitynotfoundexception("用户不存在");
            }
            log.info("用户状态更新成功 | 用户id:{} | 影响行数:{}", userid, affectedrows);
        } catch (dataintegrityviolationexception e) {
            log.error("更新用户状态失败-数据完整性异常 | 用户id:{} | 新状态:{}", userid, newstatus, e);
            throw new repositoryexception("数据完整性约束冲突", e);
        }
    }
    public page<user> findusersbycondition(userquery query, pageable pageable) {
        if (log.isdebugenabled()) {
            log.debug("分页查询用户 | 查询条件:{} | 分页参数:{}", 
                     query.tostring(), pageable.tostring());
        }
        long starttime = system.currenttimemillis();
        try {
            page<user> result = usermapper.selectbycondition(query, pageable);
            log.info("用户分页查询完成 | 总数:{} | 当前页:{} | 耗时:{}ms", 
                    result.gettotalelements(), result.getnumber(), 
                    system.currenttimemillis() - starttime);
            return result;
        } catch (dataaccessexception e) {
            log.error("用户分页查询异常 | 查询条件:{} | 异常类型:{}", 
                     query, e.getclass().getsimplename(), e);
            throw new repositoryexception("用户查询失败", e);
        }
    }
}

4. 外部服务调用日志

4.1 http 客户端调用

@component
@slf4j
public class paymentserviceclient {
    public paymentresponse createpayment(paymentrequest request) {
        string requestid = generaterequestid();
        log.info("调用支付服务开始 | 请求id:{} | 订单号:{} | 金额:{} | 端点:{}", 
                requestid, request.getorderno(), request.getamount(), "/api/payments");
        long starttime = system.currenttimemillis();
        try {
            // 记录请求详情(敏感信息脱敏)
            if (log.isdebugenabled()) {
                log.debug("支付请求详情 | 请求id:{} | 支付方式:{} | 银行编码:{}", 
                         requestid, request.getpaymentmethod(), 
                         maskbankcode(request.getbankcode()));
            }
            paymentresponse response = resttemplate.postforobject(
                paymentserviceurl + "/api/payments", request, paymentresponse.class);
            log.info("支付服务调用成功 | 请求id:{} | 支付状态:{} | 耗时:{}ms", 
                    requestid, response.getstatus(), system.currenttimemillis() - starttime);
            return response;
        } catch (httpclienterrorexception e) {
            log.warn("支付服务调用失败-客户端错误 | 请求id:{} | 状态码:{} | 响应:{}", 
                    requestid, e.getstatuscode(), e.getresponsebodyasstring(), e);
            throw new paymentexception("支付请求参数错误");
        } catch (httpservererrorexception e) {
            log.error("支付服务调用失败-服务端错误 | 请求id:{} | 状态码:{} | 异常:{}", 
                     requestid, e.getstatuscode(), e.getmessage(), e);
            throw new paymentexception("支付服务暂时不可用");
        } catch (resourceaccessexception e) {
            log.error("支付服务调用失败-网络异常 | 请求id:{} | 异常类型:{}", 
                     requestid, e.getclass().getsimplename(), e);
            throw new paymentexception("网络连接超时");
        } catch (exception e) {
            log.error("支付服务调用失败-未知异常 | 请求id:{} | 异常类型:{}", 
                     requestid, e.getclass().getsimplename(), e);
            throw new paymentexception("支付处理失败");
        }
    }
}

4.2 feign client 调用

@feignclient(name = "inventory-service")
@slf4j
public class inventoryserviceclient {
    @postmapping("/api/inventory/deduct")
    responseentity<inventoryresponse> deductstock(@requestbody deductrequest request);
    // feign 配置类中的日志
    @component
    @slf4j
    public class feignlogger extends logger {
        @override
        protected void log(string configkey, string format, object... args) {
            if (log.isinfoenabled()) {
                log.info(string.format(methodtag(configkey) + format, args));
            }
        }
    }
}

5. 消息队列处理日志

5.1 rabbitmq 消费者

@component
@slf4j
public class ordermessageconsumer {
    @rabbitlistener(queues = "order.create.queue")
    public void handleordercreate(ordercreatemessage message, channel channel, 
                                @header(amqpheaders.delivery_tag) long deliverytag) {
        log.info("收到订单创建消息 | 消息id:{} | 订单号:{} | 路由键:{}", 
                message.getmessageid(), message.getorderno(), "order.create");
        long starttime = system.currenttimemillis();
        try {
            // 业务处理
            orderservice.processordercreate(message);
            // 手动确认消息
            channel.basicack(deliverytag, false);
            log.info("订单消息处理成功 | 消息id:{} | 订单号:{} | 耗时:{}ms", 
                    message.getmessageid(), message.getorderno(), 
                    system.currenttimemillis() - starttime);
        } catch (businessexception e) {
            log.warn("订单消息处理失败-业务异常 | 消息id:{} | 订单号:{} | 错误:{}", 
                    message.getmessageid(), message.getorderno(), e.getmessage());
            // 业务异常,拒绝消息并不再重试
            channel.basicreject(deliverytag, false);
        } catch (exception e) {
            log.error("订单消息处理失败-系统异常 | 消息id:{} | 订单号:{} | 异常类型:{}", 
                     message.getmessageid(), message.getorderno(), 
                     e.getclass().getsimplename(), e);
            // 系统异常,拒绝消息并重新入队
            channel.basicreject(deliverytag, true);
        }
    }
}

6. 定时任务日志

6.1 分布式定时任务

@component
@slf4j
public class scheduledtasks {
    @scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点执行
    @schedulerlock(name = "datacleanuptask", lockatmostfor = "30m")
    public void datacleanuptask() {
        string taskid = uuid.randomuuid().tostring();
        log.info("数据清理任务开始 | 任务id:{} | 执行时间:{}", 
                taskid, localdatetime.now().format(datetimeformatter.iso_local_date_time));
        long starttime = system.currenttimemillis();
        int successcount = 0;
        int failurecount = 0;
        try {
            // 清理过期订单
            log.debug("开始清理过期订单 | 任务id:{}", taskid);
            int ordercount = orderservice.cleanexpiredorders();
            log.debug("过期订单清理完成 | 清理数量:{}", ordercount);
            successcount++;
            // 清理日志文件
            log.debug("开始清理日志文件 | 任务id:{}", taskid);
            int logcount = logservice.cleanoldlogs();
            log.debug("日志文件清理完成 | 清理数量:{}", logcount);
            successcount++;
            log.info("数据清理任务完成 | 任务id:{} | 成功子任务:{} | 失败子任务:{} | 总耗时:{}ms", 
                    taskid, successcount, failurecount, 
                    system.currenttimemillis() - starttime);
        } catch (exception e) {
            log.error("数据清理任务异常 | 任务id:{} | 异常类型:{}", 
                     taskid, e.getclass().getsimplename(), e);
            throw e;
        }
    }
}

7. 安全认证日志

7.1 jwt 认证日志

@component
@slf4j
public class jwttokenprovider {
    public string generatetoken(userdetails userdetails) {
        log.debug("生成jwt token开始 | 用户名:{} | 角色:{}", 
                 userdetails.getusername(), userdetails.getauthorities());
        string token = dogeneratetoken(userdetails);
        log.info("jwt token生成成功 | 用户名:{} | token前缀:{}", 
                userdetails.getusername(), token.substring(0, 20));
        return token;
    }
    public boolean validatetoken(string token) {
        log.debug("验证jwt token | token长度:{}", token.length());
        try {
            boolean isvalid = dovalidatetoken(token);
            if (isvalid) {
                log.debug("jwt token验证通过");
            } else {
                log.warn("jwt token验证失败");
            }
            return isvalid;
        } catch (expiredjwtexception e) {
            log.warn("jwt token已过期 | 过期时间:{}", e.getclaims().getexpiration());
            return false;
        } catch (malformedjwtexception e) {
            log.warn("jwt token格式错误");
            return false;
        } catch (exception e) {
            log.error("jwt token验证异常 | 异常类型:{}", e.getclass().getsimplename(), e);
            return false;
        }
    }
}

8. 文件操作日志

8.1 文件上传下载

@service
@slf4j
public class fileservice {
    public fileuploadresponse uploadfile(multipartfile file, string businesstype) {
        log.info("文件上传开始 | 文件名:{} | 文件大小:{} | 业务类型:{}", 
                file.getoriginalfilename(), file.getsize(), businesstype);
        long starttime = system.currenttimemillis();
        try {
            // 文件验证
            validatefile(file);
            log.debug("文件验证通过 | 文件名:{}", file.getoriginalfilename());
            // 上传处理
            string filepath = savefile(file);
            log.debug("文件保存完成 | 存储路径:{}", filepath);
            fileuploadresponse response = new fileuploadresponse(filepath, "上传成功");
            log.info("文件上传成功 | 文件名:{} | 文件路径:{} | 耗时:{}ms", 
                    file.getoriginalfilename(), filepath, 
                    system.currenttimemillis() - starttime);
            return response;
        } catch (filesizeexception e) {
            log.warn("文件上传失败-文件大小超限 | 文件名:{} | 实际大小:{} | 限制大小:{}", 
                    file.getoriginalfilename(), file.getsize(), e.getlimitsize());
            throw new businessexception("文件大小超过限制");
        } catch (filetypeexception e) {
            log.warn("文件上传失败-文件类型不支持 | 文件名:{} | 文件类型:{}", 
                    file.getoriginalfilename(), e.getfiletype());
            throw new businessexception("不支持的文件类型");
        } catch (ioexception e) {
            log.error("文件上传失败-io异常 | 文件名:{} | 异常类型:{}", 
                     file.getoriginalfilename(), e.getclass().getsimplename(), e);
            throw new systemexception("文件上传失败");
        }
    }
}

9. 缓存操作日志

9.1 redis 缓存操作

@service
@slf4j
public class cacheservice {
    public <t> t getfromcache(string key, class<t> clazz) {
        log.debug("查询缓存开始 | 缓存键:{} | 类型:{}", key, clazz.getsimplename());
        long starttime = system.currenttimemillis();
        try {
            t value = redistemplate.opsforvalue().get(key);
            if (value == null) {
                log.debug("缓存未命中 | 缓存键:{} | 耗时:{}ms", 
                         key, system.currenttimemillis() - starttime);
            } else {
                log.debug("缓存命中 | 缓存键:{} | 耗时:{}ms", 
                         key, system.currenttimemillis() - starttime);
            }
            return value;
        } catch (redisconnectionfailureexception e) {
            log.error("缓存查询失败-连接异常 | 缓存键:{}", key, e);
            throw new cacheexception("缓存服务不可用");
        } catch (exception e) {
            log.error("缓存查询失败-未知异常 | 缓存键:{} | 异常类型:{}", 
                     key, e.getclass().getsimplename(), e);
            throw new cacheexception("缓存查询失败");
        }
    }
    public void settocache(string key, object value, duration ttl) {
        log.debug("设置缓存开始 | 缓存键:{} | ttl:{}", key, ttl);
        try {
            redistemplate.opsforvalue().set(key, value, ttl);
            log.debug("缓存设置成功 | 缓存键:{}", key);
        } catch (exception e) {
            log.error("缓存设置失败 | 缓存键:{} | 异常类型:{}", 
                     key, e.getclass().getsimplename(), e);
            // 缓存设置失败不应该影响主流程
        }
    }
}

10. 全局异常处理日志

10.1 统一异常处理器

@restcontrolleradvice
@slf4j
public class globalexceptionhandler {
    @exceptionhandler(businessexception.class)
    public responseentity<errorresponse> handlebusinessexception(businessexception e, 
                                                               httpservletrequest request) {
        string errorid = generateerrorid();
        log.warn("业务异常处理 | 错误id:{} | 错误码:{} | 错误信息:{} | 请求路径:{} | 客户端ip:{}", 
                errorid, e.geterrorcode(), e.getmessage(), 
                request.getrequesturi(), getclientip(request));
        return responseentity.status(httpstatus.bad_request)
                .body(new errorresponse(errorid, e.geterrorcode(), e.getmessage()));
    }
    @exceptionhandler(authenticationexception.class)
    public responseentity<errorresponse> handleauthexception(authenticationexception e,
                                                           httpservletrequest request) {
        string errorid = generateerrorid();
        log.warn("认证异常处理 | 错误id:{} | 错误信息:{} | 请求路径:{} | 客户端ip:{}", 
                errorid, e.getmessage(), request.getrequesturi(), getclientip(request));
        return responseentity.status(httpstatus.unauthorized)
                .body(new errorresponse(errorid, "unauthorized", "认证失败"));
    }
    @exceptionhandler(exception.class)
    public responseentity<errorresponse> handlesystemexception(exception e,
                                                             httpservletrequest request) {
        string errorid = generateerrorid();
        log.error("系统异常处理 | 错误id:{} | 异常类型:{} | 请求路径:{} | 客户端ip:{} | 堆栈跟踪:{}", 
                 errorid, e.getclass().getsimplename(), request.getrequesturi(), 
                 getclientip(request), getstacktrace(e));
        // 生产环境隐藏详细错误信息
        string message = isproduction() ? "系统内部错误" : e.getmessage();
        return responseentity.status(httpstatus.internal_server_error)
                .body(new errorresponse(errorid, "system_error", message));
    }
}

11. 配置类日志

11.1 应用启动配置

@springbootapplication
@slf4j
public class application {
    public static void main(string[] args) {
        springapplication app = new springapplication(application.class);
        // 启动前日志
        log.info("应用启动开始 | 环境:{} | 版本:{} | 启动时间:{}", 
                getactiveprofile(), getappversion(), localdatetime.now());
        try {
            configurableapplicationcontext context = app.run(args);
            // 启动成功日志
            environment env = context.getenvironment();
            string port = env.getproperty("server.port");
            string contextpath = env.getproperty("server.servlet.context-path", "");
            log.info("应用启动成功 | 环境:{} | 端口:{} | 上下文路径:{} | 启动耗时:{}ms", 
                    getactiveprofile(), port, contextpath, 
                    system.currenttimemillis() - starttime);
            // 记录重要的配置信息
            log.info("数据源配置 | url:{} | 用户名:{}", 
                    maskdatabaseurl(env.getproperty("spring.datasource.url")),
                    env.getproperty("spring.datasource.username"));
        } catch (exception e) {
            log.error("应用启动失败 | 异常类型:{}", e.getclass().getsimplename(), e);
            system.exit(1);
        }
    }
}

这些示例覆盖了 spring boot/cloud 应用中绝大多数常见的日志场景,提供了完整的、生产可用的日志记录模式。在实际开发中,可以根据具体业务需求进行调整和扩展。

到此这篇关于spring boot/spring cloud 应用日志书写详细指南的文章就介绍到这了,更多相关spring boot日志书写内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com