当前位置: 代码网 > it编程>编程语言>Java > SpringBoot实现日志系统的完整代码

SpringBoot实现日志系统的完整代码

2026年01月23日 Java 我要评论
一、日志系统:程序员的“侦探助手”如果你的程序突然“挂掉”了,你却不知道它死前经历了什么——这比看悬疑电影看到一半停电还难受!日

一、日志系统:程序员的“侦探助手”

如果你的程序突然“挂掉”了,你却不知道它死前经历了什么——这比看悬疑电影看到一半停电还难受!日志系统就是你的“侦探助手”,它悄咪咪地记录着程序的一举一动,就像:

  • 摄像头:谁在什么时候访问了哪个接口
  • 记事本:程序想了什么、做了什么、遇到了什么挫折
  • 告密者:偷偷告诉你“老板,数据库又连不上了!”
  • 时间机器:能让你穿越回错误发生的瞬间

springboot的日志系统就像一个“智能管家”,你不配置它也能工作,但配置好了它就能变成“超级管家”!

二、详细步骤:打造你的“程序监控室”

第1步:创建springboot项目

# 用spring initializr创建一个新项目
# 或者用ide的spring initializr功能
# 记得勾选:
# - spring web (因为我们要写接口)
# - lombok (减少代码量,程序员要懒一点)

第2步:基础配置 - 给日志系统“定规矩”

application.yml(或application.properties)中添加:

# application.yml
spring:
  application:
    name: log-system-demo

logging:
  # 日志级别:trace < debug < info < warn < error
  level:
    root: info  # 根日志级别
    com.example.demo: debug  # 我们的包用debug级别
    org.springframework.web: info
    org.hibernate: warn
    
  # 文件输出配置(让日志有个“家”)
  file:
    name: logs/my-app.log  # 日志文件路径
    max-size: 10mb  # 单个文件最大10mb
    max-history: 30  # 保留30天的日志
    
  # 控制台输出美化(让日志“颜值”更高)
  pattern:
    console: "%d{yyyy-mm-dd hh:mm:ss} - %magenta([%thread]) - %highlight(%-5level) - %cyan(%logger{36}) - %msg%n"
    file: "%d{yyyy-mm-dd hh:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
    
  # 日志分组(给日志“分班”)
  group:
    web: org.springframework.core.codec, org.springframework.http
    sql: org.hibernate.sql, org.springframework.jdbc

第3步:创建日志工具类

package com.example.demo.utils;

import lombok.extern.slf4j.slf4j;
import org.springframework.stereotype.component;

@component
@slf4j  // lombok的魔法注解,自动生成log对象
public class logutil {
    
    /**
     * 记录方法进入(就像进门前喊“我进来啦!”)
     */
    public void methodenter(string methodname, object... params) {
        log.debug("方法 {} 被调用,参数: {}", methodname, params);
    }
    
    /**
     * 记录方法退出(出门说“我走啦!”)
     */
    public void methodexit(string methodname, object result) {
        log.debug("方法 {} 执行完成,返回值: {}", methodname, result);
    }
    
    /**
     * 记录业务关键点(重要的事说三遍?不,记一遍就行)
     */
    public void businesslog(string template, object... args) {
        log.info("业务日志: " + template, args);
    }
    
    /**
     * 记录异常(错误发生时大喊“着火啦!”)
     */
    public void error(string message, throwable e) {
        log.error("发生异常: {} - 异常详情: ", message, e);
    }
    
    /**
     * 慢查询警告(程序说“我...有点卡...”)
     */
    public void slowquery(long costtime, string query) {
        if (costtime > 1000) {  // 超过1秒
            log.warn("慢查询警告! 耗时: {}ms, sql: {}", costtime, query);
        }
    }
}

第4步:创建aop切面 - 给所有方法“装上摄像头”

package com.example.demo.aop;

import com.example.demo.utils.logutil;
import lombok.requiredargsconstructor;
import lombok.extern.slf4j.slf4j;
import org.aspectj.lang.proceedingjoinpoint;
import org.aspectj.lang.annotation.around;
import org.aspectj.lang.annotation.aspect;
import org.aspectj.lang.annotation.pointcut;
import org.springframework.stereotype.component;
import org.springframework.web.context.request.requestcontextholder;
import org.springframework.web.context.request.servletrequestattributes;

import javax.servlet.http.httpservletrequest;
import java.util.arrays;

@aspect
@component
@slf4j
@requiredargsconstructor
public class logaspect {
    
    private final logutil logutil;
    
    /**
     * 切点:所有controller层的方法
     */
    @pointcut("execution(* com.example.demo.controller..*.*(..))")
    public void controllerpointcut() {}
    
    /**
     * 切点:所有service层的方法
     */
    @pointcut("execution(* com.example.demo.service..*.*(..))")
    public void servicepointcut() {}
    
    /**
     * 环绕通知:controller层日志
     */
    @around("controllerpointcut()")
    public object logcontroller(proceedingjoinpoint joinpoint) throws throwable {
        // 获取请求信息
        servletrequestattributes attributes = 
            (servletrequestattributes) requestcontextholder.getrequestattributes();
        
        string requesturl = "unknown";
        string httpmethod = "unknown";
        string ip = "unknown";
        
        if (attributes != null) {
            httpservletrequest request = attributes.getrequest();
            requesturl = request.getrequesturl().tostring();
            httpmethod = request.getmethod();
            ip = request.getremoteaddr();
        }
        
        string methodname = joinpoint.getsignature().getname();
        string classname = joinpoint.gettarget().getclass().getsimplename();
        object[] args = joinpoint.getargs();
        
        // 记录请求开始
        log.info("\n========== 请求进入 ==========");
        log.info("url: {} {}", httpmethod, requesturl);
        log.info("ip: {}", ip);
        log.info("类: {}.{}", classname, methodname);
        log.info("参数: {}", arrays.tostring(args));
        
        long starttime = system.currenttimemillis();
        object result;
        
        try {
            // 执行原方法
            result = joinpoint.proceed();
            long costtime = system.currenttimemillis() - starttime;
            
            // 记录请求完成
            log.info("请求成功,耗时: {}ms", costtime);
            log.info("返回结果: {}", result);
            log.info("========== 请求结束 ==========\n");
            
            return result;
            
        } catch (exception e) {
            long costtime = system.currenttimemillis() - starttime;
            
            // 记录异常
            log.error("请求失败,耗时: {}ms", costtime);
            log.error("异常信息: {}", e.getmessage());
            log.info("========== 请求异常结束 ==========\n");
            
            throw e;
        }
    }
    
    /**
     * 环绕通知:service层日志
     */
    @around("servicepointcut()")
    public object logservice(proceedingjoinpoint joinpoint) throws throwable {
        string methodname = joinpoint.getsignature().getname();
        object[] args = joinpoint.getargs();
        
        logutil.methodenter(methodname, args);
        long starttime = system.currenttimemillis();
        
        try {
            object result = joinpoint.proceed();
            long costtime = system.currenttimemillis() - starttime;
            
            logutil.methodexit(methodname, result);
            logutil.slowquery(costtime, methodname + " 方法执行");
            
            return result;
        } catch (exception e) {
            logutil.error("service方法执行失败: " + methodname, e);
            throw e;
        }
    }
}

第5步:创建controller和service - 让日志系统“有活干”

// usercontroller.java
package com.example.demo.controller;

import com.example.demo.service.userservice;
import lombok.requiredargsconstructor;
import lombok.extern.slf4j.slf4j;
import org.springframework.web.bind.annotation.*;

@restcontroller
@requestmapping("/users")
@slf4j
@requiredargsconstructor
public class usercontroller {
    
    private final userservice userservice;
    
    @getmapping("/{id}")
    public string getuser(@pathvariable long id) {
        log.info("查询用户,id: {}", id);
        return userservice.getuserbyid(id);
    }
    
    @postmapping
    public string createuser(@requestbody string userdata) {
        log.info("创建用户,数据: {}", userdata);
        // 模拟业务异常
        if ("bad".equals(userdata)) {
            throw new runtimeexception("用户数据不合法!");
        }
        return "用户创建成功: " + userdata;
    }
}

// userservice.java
package com.example.demo.service;

import com.example.demo.utils.logutil;
import lombok.requiredargsconstructor;
import org.springframework.stereotype.service;

@service
@requiredargsconstructor
public class userservice {
    
    private final logutil logutil;
    
    public string getuserbyid(long id) {
        logutil.businesslog("根据id查询用户,id: {}", id);
        
        // 模拟数据库查询
        try {
            thread.sleep(50);  // 模拟耗时
            if (id == 999) {
                throw new runtimeexception("用户不存在!");
            }
            return "用户" + id;
        } catch (interruptedexception e) {
            logutil.error("查询用户时发生异常", e);
            return "查询失败";
        }
    }
}

第6步:创建全局异常处理 - 给错误“擦屁股”

package com.example.demo.handler;

import com.example.demo.utils.logutil;
import lombok.requiredargsconstructor;
import lombok.extern.slf4j.slf4j;
import org.springframework.web.bind.annotation.exceptionhandler;
import org.springframework.web.bind.annotation.restcontrolleradvice;

import javax.servlet.http.httpservletrequest;
import java.util.hashmap;
import java.util.map;

@restcontrolleradvice
@slf4j
@requiredargsconstructor
public class globalexceptionhandler {
    
    private final logutil logutil;
    
    @exceptionhandler(exception.class)
    public map<string, object> handleexception(httpservletrequest request, exception e) {
        // 记录异常日志
        logutil.error("全局异常捕获", e);
        
        // 返回友好错误信息
        map<string, object> result = new hashmap<>();
        result.put("success", false);
        result.put("message", "服务器开小差了,请稍后再试!");
        result.put("path", request.getrequesturi());
        result.put("timestamp", system.currenttimemillis());
        
        // 开发环境显示详细错误
        if (isdevelopment()) {
            result.put("error", e.getmessage());
            result.put("stacktrace", e.getstacktrace());
        }
        
        return result;
    }
    
    private boolean isdevelopment() {
        // 这里可以根据配置判断环境
        return true;  // 假设是开发环境
    }
}

第7步:创建日志查看接口(可选) - 给日志开个“后门”

package com.example.demo.controller;

import org.springframework.web.bind.annotation.*;

import java.io.bufferedreader;
import java.io.filereader;
import java.io.ioexception;
import java.util.arraylist;
import java.util.list;

@restcontroller
@requestmapping("/log")
public class logcontroller {
    
    @getmapping("/tail")
    public list<string> getlogtail(@requestparam(defaultvalue = "100") int lines) {
        list<string> result = new arraylist<>();
        string logfile = "logs/my-app.log";
        
        try (bufferedreader reader = new bufferedreader(new filereader(logfile))) {
            list<string> alllines = new arraylist<>();
            string line;
            
            while ((line = reader.readline()) != null) {
                alllines.add(line);
            }
            
            // 获取最后n行
            int start = math.max(0, alllines.size() - lines);
            for (int i = start; i < alllines.size(); i++) {
                result.add(alllines.get(i));
            }
            
        } catch (ioexception e) {
            result.add("读取日志文件失败: " + e.getmessage());
        }
        
        return result;
    }
}

第8步:配置文件分离(高级技巧) - 给不同环境“穿不同衣服”

# application-dev.yml (开发环境)
logging:
  level:
    root: debug  # 开发环境详细日志
  file:
    name: logs/dev-app.log
    
# application-prod.yml (生产环境)
logging:
  level:
    root: info  # 生产环境精简日志
    com.example.demo: warn  # 自己的包只记录警告
  file:
    name: /var/log/my-app/app.log  # linux系统标准日志目录

三、启动和测试

1. 启动应用

# 设置激活的环境
java -jar demo.jar --spring.profiles.active=dev

2. 测试接口

# 正常请求
curl http://localhost:8080/users/1

# 触发异常
curl -x post http://localhost:8080/users -d "bad"

# 查看日志
curl http://localhost:8080/log/tail?lines=50

3. 观察控制台输出

你会看到彩色高亮的日志:

2026-01-21 10:30:25 - [http-nio-8080-exec-1] - info  - c.e.demo.controller.usercontroller - 查询用户,id: 1
2026-01-21 10:30:25 - [http-nio-8080-exec-1] - debug - c.e.demo.aop.logaspect - 方法 getuserbyid 被调用,参数: [1]

四、高级功能扩展

1. 添加日志脱敏(保护敏感信息)

@component
public class logsensitivefilter {
    public string filtersensitive(string logcontent) {
        // 脱敏手机号
        logcontent = logcontent.replaceall("(1[3-9]\\d{9})", "$1****");
        // 脱敏身份证
        logcontent = logcontent.replaceall("(\\d{4})\\d{10}(\\w{4})", "$1**********$2");
        // 脱敏邮箱
        logcontent = logcontent.replaceall("(\\w{3})(\\w+)(@\\w+\\.\\w+)", "$1****$3");
        return logcontent;
    }
}

2. 集成elk(日志分析全家桶)

# 添加logstash依赖
dependencies:
  implementation 'net.logstash.logback:logstash-logback-encoder:7.0'

配置logback-spring.xml

<appender name="logstash" class="net.logstash.logback.appender.logstashtcpsocketappender">
    <destination>localhost:5000</destination>
    <encoder class="net.logstash.logback.encoder.loggingeventcompositejsonencoder">
        <providers>
            <timestamp/>
            <loglevel/>
            <loggername/>
            <message/>
            <mdc/>
            <stacktrace/>
        </providers>
    </encoder>
</appender>

3. 自定义appender(发送到企业微信/钉钉)

public class dingtalkappender extends appenderbase<iloggingevent> {
    @override
    protected void append(iloggingevent event) {
        if (event.getlevel().isgreaterorequal(level.error)) {
            string message = string.format("【系统告警】\n时间: %s\n级别: %s\n消息: %s",
                new date(event.gettimestamp()),
                event.getlevel(),
                event.getformattedmessage());
            // 调用钉钉机器人api
            sendtodingtalk(message);
        }
    }
}

五、总结:日志系统的“生存法则”

1.日志不是越多越好

就像吃饭不是越多越好一样,日志也要“适量”:

  • debug级别:开发环境用,生产环境关掉
  • info级别:记录关键业务路径
  • warn级别:需要关注但不紧急的问题
  • error级别:必须立即处理的问题

2.日志要“有意义”

糟糕的日志:用户操作完成 好的日志:用户[张三]于[2026-01-21 10:30:25]完成了订单[202601210001]的支付,金额[299.00]元

3.结构化日志是趋势

{
  "timestamp": "2026-01-21t10:30:25.123z",
  "level": "info",
  "service": "user-service",
  "traceid": "abc-123-def-456",
  "userid": "user_001",
  "action": "place_order",
  "details": {
    "orderid": "202601210001",
    "amount": 299.00
  }
}

4.性能很重要

  • 使用异步日志:asyncappender
  • 避免在日志中拼接大字符串
  • 生产环境关掉不必要的日志级别

5.安全不能忘

  • 敏感信息必须脱敏
  • 日志文件要设置权限
  • 生产环境日志不能包含调试信息

6.监控告警要跟上

  • 错误日志实时告警
  • 慢查询统计
  • 接口调用量监控

六、最后

  • 写日志就像写日记:不仅要记录“做了什么”,还要记录“为什么这么做”
  • 日志是给“未来的你”看的:想象一下凌晨3点被报警电话叫醒,清晰的日志能让你少掉几根头发
  • 日志不是万能的:关键业务逻辑该有监控还要有监控,该有告警还要有告警
  • 定期review日志:就像定期体检,能发现潜在问题

一个好的日志系统就像一位可靠的“副驾驶”,在你开车的路上,它不会打扰你,但会在你需要的时候,准确地告诉你:

  • “前面有坑!”(error)
  • “油不多了”(warn)
  • “风景不错”(info)
  • “我在记录一切”(debug)

去给你的springboot应用装上这个“智能行车记录仪”吧!你的程序会感谢你,你的同事会感谢你,凌晨三点被叫醒处理问题的那个你,更会感谢你!

以上就是springboot实现日志系统的完整代码的详细内容,更多关于springboot日志系统的资料请关注代码网其它相关文章!

(0)

相关文章:

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

发表评论

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