ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Spring Boot SQL日志打印全攻略:MyBatis/JPA配置与Logback实战

Spring Boot SQL日志打印全攻略:MyBatis/JPA配置与Logback实战 Spring Boot 项目里打印 SQL 日志这件事说大不大说小不小。我见过不少同事在 IDEA 控制台里翻来翻去找不到一条 SQL 输出最后发现是 MyBatis 的 log-impl 没配也有人把日志框架从 Logback 切到 Log4j2 之后SQL 日志莫名消失排查半天才意识到是 logger name 的包路径写错了。这篇文章就把 Spring Boot 项目里打印 SQL 日志和结果的完整方案讲清楚覆盖 MyBatis、MyBatis-Plus、JPA/Hibernate 三种主流持久层框架同时讲透基于配置文件application.yml和基于 Logbacklogback-spring.xml两种配置方式的差异与选择最后附上我实际踩过的坑和排查思路。适合谁看正在做 Spring Boot 项目、发现 SQL 日志打不出来或者不知道日志该怎么独立归档的开发者尤其是刚接触 MyBatis 和 JPA 不久、对日志体系还没完全理清的同学。读完你不仅能照着配还能明白为什么这样配。1. 先搞清楚 Spring Boot 的日志体系再谈 SQL 日志这一步很多人会跳过直接去搜Spring Boot 打印 SQL 日志然后抄一段配置结果换了个框架就失效了。根本原因在于不同持久层框架对日志的处理机制完全不一样。1.1 SLF4J 门面与 Spring Boot 默认的 LogbackSpring Boot 的默认日志实现是 Logback但项目代码里通常直接用的是 SLF4J 的 API。你可以把 SLF4J 理解成一个插座规范Logback、Log4j2、java.util.logging 都是不同的插头只要插头符合规范插座就能用。Spring Boot 的 spring-boot-starter-logging 已经帮你把 Logback 和 SLF4J 绑定好了所以你在 pom.xml 里看不到 Logback 依赖代码里照样能打日志因为 starter 帮你在后台装配了。理解这个机制对配置 SQL 日志非常重要。因为你配置 SQL 日志时本质上有两条路走应用程序的日志框架Logback/Log4j2用 logging.level 来控制 SQL 日志的级别。不走日志框架直接通过持久层框架自身的 stdout 输出到控制台。这两条路的配置入口完全不同而且很多教程把它们混在一起讲导致新手抄配置时很容易张冠李戴。举个例子MyBatis 的 StdOutImpl 是直接 System.out 输出根本不经过 Logback所以你在 logging.level 里怎么调级别都没用。而 Slf4jImpl 是把日志交给了 SLF4J这时才受 logging.level 管控。这就是为什么同样的打印 SQL 日志需求配置方式却千差万别。1.2 日志级别是 SQL 日志输出的总开关日志级别从低到高是 trace、debug、info、warn、error。如果不理解级别你可能会遇到一个非常常见的困惑明明代码里什么都正常为什么 SQL 日志就是不出来原因很简单。Spring Boot 默认的 root 日志级别是 info而很多持久层框架的 SQL 日志默认是 debug 级别比如 Hibernate 的 org.hibernate.SQL 就是 debug。debug 低于 info不会输出。所以你必须在 application.yml 里把对应 logger 的级别降到 debug 或更低SQL 日志才会浮现。这是 SQL 日志问题的核心开关。你后面所有配置本质上都是在做一件事把特定 logger 的级别调到能输出 SQL 的程度并指定输出的目的地。另外注意一点项目里如果有比较多的 debug 日志直接调 root 的级别到 debug 会造成日志暴涨所以实践上永远只针对 mapper 包或者框架的 SQL logger 单独调级别不要全局放开。1.3 持久层框架不同日志行为天差地别MyBatis、MyBatis-Plus、JPA/Hibernate 三者的日志机制差异非常大我用一张表总结一下后面每个框架再细讲持久层框架SQL 日志走日志框架关键 logger/配置项默认级别结果集日志MyBatis可选通过 log-impl 指定mapper 接口包路径debug支持输出行数MyBatis-Plus同 MyBatismapper 接口包路径debug支持JPA/Hibernate走日志框架org.hibernate.SQLdebug通过 trace 级输出参数JPA/Hibernate不走日志框架spring.jpa.show-sqlinfostdout不支持这张表记住一个核心结论MyBatis 系要看 log-impl 和包路径JPA/Hibernate 系要看 spring.jpa.show-sql 和 org.hibernate.SQL 的级别。理解了这一点后面就是照着配置的事了。2. MyBatis 系 SQL 日志配置实操MyBatis 和 MyBatis-Plus 是 Java 后端用得最多的持久层框架SQL 日志配置也几乎是同一个套路。我这里直接给出最实用的配置组合。2.1 方式一走 Logback用 logging.level 控制 mapper 包这种方法是我最推荐的因为日志会统一进入 Logback后续不管是想输出到文件、按天归档、还是接日志采集系统全都顺理成章。首先确保 application.yml 中有这样一段配置logging: level: com.example.project.mapper: debug这里 com.example.project.mapper 是你自己的 Mapper 接口所在的包路径不是 XML 文件所在路径。很多刚接触的同学把 resource 下的 mapper XML 目录路径填进来结果怎么配都不生效就是这个原因。同时MyBatis 需要指定日志实现为 Slf4jImpl这样它才会走日志框架而不是直接 stdoutmybatis: configuration: log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl如果你是 MyBatis-Plus配置几乎一样只是前缀改成 mybatis-plusmybatis-plus: configuration: log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl然后重启项目执行一条 SQL你就会在控制台看到类似这样的输出 Preparing: SELECT id, name, age FROM user WHERE id ? Parameters: 1(Long) Columns: id, name, age Row: 1, 张三, 25 Total: 1能看到 Preparing预编译 SQL和 Parameters绑定参数就说明配置成功了。注意这时即使你的 root 日志级别是 warnmapper 包下的 SQL 日志也照样输出因为 logging.level 下的包路径优先级高于 root。2.2 方式二用 StdOutImpl 快速验证如果不关心日志归档只是想在本地调试时快速看到 SQL可以直接用 MyBatis 的 StdOutImplmybatis: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl这个实现会把 SQL 日志直接打印到标准输出IDEA 控制台里显示效果完全一样。好处是零学习成本、配置一行搞定坏处是日志不经过 Logback你无法用 logging.level 控制、无法归档、也无法接入日志采集系统。我在开发环境用过一段时间的 StdOutImpl后来切到生产环境做日志采集时被迫全部改回 Slf4jImpl。坦白说如果从一开始就用 Slf4jImpl后面不用返工。注意StdOutImpl 与 logging.level 互不干扰。即使你设了 logging.level.com.example.project.mapperdebug也没办法让 StdOutImpl 的输出受控因为它根本不走 SLF4J。2.3 方式三把 SQL 日志单独输出到文件这个需求比较常见排查线上问题时不希望 SQL 日志跟其他业务日志混在一起而是单独放一个 sql.log方便 grep。这时必须配合 Logback 配置。先看 application.yml 部分的写法logging: level: com.example.project.mapper: debug然后在 resources 下创建 logback-spring.xml核心配置如下?xml version1.0 encodingUTF-8? configuration !-- 单独的文件 appender -- appender nameSQL_FILE classch.qos.logback.core.rolling.RollingFileAppender filelogs/sql.log/file rollingPolicy classch.qos.logback.core.rolling.TimeBasedRollingPolicy fileNamePatternlogs/sql.%d{yyyy-MM-dd}.log/fileNamePattern maxHistory30/maxHistory /rollingPolicy encoder pattern%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n/pattern /encoder /appender !-- 只针对 mapper 包 -- logger namecom.example.project.mapper levelDEBUG additivityfalse appender-ref refSQL_FILE/ /logger /configuration这里有个关键细节additivityfalse。如果你不写这一句SQL 日志不仅会写入 sql.log还会继续向上传播到 root logger然后在控制台打一份、在其他业务日志文件里再打一份。很多同学配置后日志重复就是栽在这个属性上。这个方案在生产环境非常实用。我把 mapper 包下的日志单独落盘后运维同学查问题只需要看 sql.log 一个文件效率提升明显。daily 滚动保留 30 天磁盘压力可控。3. JPA/Hibernate 的 SQL 日志配置套路用 Spring Data JPA 的同学经常会遇到另一种困惑spring.jpa.show-sqltrue 配置了SQL 确实能看到但日志里没有参数值全是问号占位符排查起来很难受。这其实是 Hibernate 的日志机制决定的。3.1 show-sql 只是权宜之计真正可控的是日志级别先看最简单的配置方式spring: jpa: show-sql: true properties: hibernate.format_sql: trueshow-sqltrue 会把 SQL 输出到控制台format_sqltrue 会把 SQL 格式化换行看起来更美观。但问题有两个输出走的是 System.out不是 Logback不受日志级别控制也无法归档。SQL 里的参数值不会显示你只能看到类似 select u.* from user u where u.id? 的占位符。所以 show-sql 更适合本地开发快速验证生产环境不推荐。真正可控的方式是走日志框架logging: level: org.hibernate.SQL: debug这时 SQL 会通过 Logback 输出并且受日志框架管控可以归档、可以采集。但注意参数值仍然不会显示在 SQL 那一行里Hibernate 把参数绑定和结果集处理放在了更细的级别。3.2 在日志里看到参数值org.hibernate.type.descriptor.sql如果你不光要看 SQL还要看每个参数实际传进来的值需要额外开启 trace 级别logging: level: org.hibernate.SQL: debug org.hibernate.type.descriptor.sql: trace这样配置后每次 SQL 执行会在日志里输出一行绑定参数的信息类似bind [1, 张三]我实际排查一个分页查询的分页参数传错问题时就是因为 trace 级别下可以看到每个参数的实际类型和值很快就定位到是 Pageable 的页码参数传了 0 导致查出的数据不符合预期。这在 debug 级别下根本看不出来。不过这里有个性能层面的考虑trace 级别的日志输出对高并发接口有一定开销生产环境不建议长期开启。我的做法是本地开发开 trace生产只开 debug遇到疑难问题时临时把某个接口的 logger 级别动态调成 trace排查完立刻降回来。3.3 JPA 配置对照表把 JPA 的几种配置方式和效果整理成一张表方便你按需选择配置方式输出位置是否显示参数是否可归档适合场景spring.jpa.show-sqltrue标准输出否否本地开发快速看 SQLlogging.level.org.hibernate.SQLdebug日志框架否是生产环境常规监控再加 org.hibernate.type.descriptor.sqltrace日志框架是是排查参数类问题配合 format_sqltrue日志框架视上表是需要阅读复杂 SQL4. Logback 配置文件实战从基础到进阶讲完了持久层框架侧接下来重点说 Logback 本身。很多项目在运行一段时间后日志管理需求会从能打印升级到能控制、能归档、能分流这时 logback-spring.xml 就是绕不开的环节。4.1 logback-spring.xml 与 logback.xml 的区别Spring Boot 官方推荐使用 logback-spring.xml而不是 logback.xml。原因在于 logback-spring.xml 支持 Spring Boot 的 profile 特性比如springProfile namedev !-- 开发环境专用配置 -- /springProfile springProfile nameprod !-- 生产环境专用配置 -- /springProfile这样同一份配置文件可以针对不同环境输出不同格式、不同文件目录。例如开发环境 SQL 日志直接输出到控制台生产环境 SQL 日志输出到独立文件并做滚动归档。如果你用 logback.xml就没有这个能力需要维护多份配置文件。另外还需要注意配置文件的命名位置不能错。Spring Boot 默认会从 classpath 下加载 logback-spring.xml所以要放在 resources 目录下。如果你用的是 yml 配置项 logging.config可以指定自定义路径但一般没必要改。4.2 通过 logger 精确控制持久层日志Logback 的 logger 节点是控制日志行为的核心。可以把它理解成一个筛选规则name 属性指定包名或类名level 属性指定日志级别appender-ref 指定输出到哪里。可以这样配置logger namecom.example.project.mapper levelDEBUG/这个配置意味着 com.example.project.mapper 包下的所有 debug 级别及以上日志都会被收集然后向上传递给 root logger 进行输出控制台、文件等。如果你不想让 MyBatis 的内部日志轰炸你的文件还可以单独把 MyBatis 框架自身的日志级别调高logger nameorg.apache.ibatis levelINFO/这在排错时很实用。MyBatis 在 debug 级别下会输出大量内部状态日志信息量很大但也很吵调成 INFO 后清爽很多而我们真正关注的业务 mapper 包日志不受影响。4.3 多环境配置推荐模板我做项目时常用的模板是dev 环境 SQL 日志走控制台prod 环境 SQL 日志走独立文件同时保留必要的文件滚动策略。贴合 Spring Boot profile 的写法推荐参考这段configuration !-- 控制台 appender -- appender nameCONSOLE classch.qos.logback.core.ConsoleAppender encoder pattern%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n/pattern charsetUTF-8/charset /encoder /appender !-- SQL 专用文件 appender -- appender nameSQL_FILE classch.qos.logback.core.rolling.RollingFileAppender filelogs/sql.log/file rollingPolicy classch.qos.logback.core.rolling.TimeBasedRollingPolicy fileNamePatternlogs/sql.%d{yyyy-MM-dd}.log/fileNamePattern maxHistory30/maxHistory totalSizeCap2GB/totalSizeCap /rollingPolicy encoder pattern%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n/pattern /encoder /appender logger namecom.example.project.mapper levelDEBUG additivityfalse springProfile nameprod appender-ref refSQL_FILE/ /springProfile springProfile namedev appender-ref refCONSOLE/ /springProfile /logger root levelINFO appender-ref refCONSOLE/ /root /configuration这里有几个细节需要说明totalSizeCap 控制所有归档日志的总大小上限避免日志无限膨胀撑爆磁盘。2GB 是我按业务量拍的一个合理值你可以根据实际情况调整。maxHistory 是保留天数30 天对于线上排障来说够用。如果 additivity 保持默认 trueSQL 日志会在 SQL_FILE 输出之外再往 root 的 CONSOLE 打一份这不一定是你想要的注意按需求决定。4.4 异步输出 SQL 日志的取舍高并发系统里同步写文件日志会占用接口响应时间所以 Logback 提供了 AsyncAppender 来实现异步日志。原理是先把日志事件放进一个阻塞队列后台线程负责真正写文件主线程无需等待磁盘 IO 完成。如果你的系统并发较高且希望 SQL 日志不影响主流程性能可以这样配置appender nameASYNC_SQL classch.qos.logback.classic.AsyncAppender queueSize2048/queueSize discardingThreshold0/discardingThreshold appender-ref refSQL_FILE/ /appender注意 discardingThreshold 这个参数官方默认值是队列容量的 20%意思是当队列剩余容量低于这个比例时会直接丢弃 INFO 及以下级别的日志事件来保护系统。但 SQL 日志本身是我们排查问题的依据直接丢弃会让问题无处可查。所以建议把 discardingThreshold 设为 0也就是队列满时阻塞等待而不是丢弃。同时 queueSize 要根据 QPS 估算。一个简单的估算方法假设每秒执行 500 条 SQL每条日志平均 200 字节那么每秒产生 100KB 日志2048 的队列容量足够缓冲几秒的积压其实已经够用了。如果 QPS 高一个量级可以调到 8192但要注意内存占用会相应增加。我实际项目中用 AsyncAppender 配合独立 SQL 文件在日均百万级 SQL 执行的系统上跑过性能影响可以忽略日志一条不少。不过这里也提醒一句文件 appender 的磁盘 IO 能力是系统整体瓶颈异步只是把压力后移磁盘本身还是要监控的。5. 慢 SQL 日志从打印到监控的一步之遥SQL 日志打出来之后很多同学的下一步诉求是能不能自动把执行时间超过阈值的 SQL 标记出来这就是慢 SQL 日志。借助 Logback 或者 MyBatis 的拦截器机制这个需求并不复杂。5.1 基于 Logback 的耗时输出Logback 的 pattern 里有一个 %r 或 %relative 参数可以输出从应用启动到当前日志事件的时间差单位毫秒。不过这个时间差跟单条 SQL 执行耗时没有直接关系所以用它监控慢 SQL 并不靠谱。更实用的方案是结合 MyBatis 拦截器拦截 Executor 层的 query 和 update 方法计算执行耗时。核心代码可以参考这段Intercepts({ Signature(type Executor.class, method update, args {MappedStatement.class, Object.class}), Signature(type Executor.class, method query, args {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}) }) public class SlowSqlInterceptor implements Interceptor { private static final Logger log LoggerFactory.getLogger(SlowSqlInterceptor.class); private long slowSqlMillis 3000; Override public Object intercept(Invocation invocation) throws Throwable { long start System.currentTimeMillis(); try { return invocation.proceed(); } finally { long cost System.currentTimeMillis() - start; if (cost slowSqlMillis) { MappedStatement ms (MappedStatement) invocation.getArgs()[0]; BoundSql boundSql ms.getBoundSql(); log.warn(慢 SQL 执行耗时: {} ms, SQL: {}, cost, formatSql(boundSql.getSql())); } } } private String formatSql(String sql) { return sql.replaceAll(\\s, ); } }然后在 MyBatis 配置里注册这个拦截器。Spring Boot 场景下如果用的是 MyBatis-Plus直接在配置类里声明一个 bean 即可用的是原生 MyBatis可以通过 org.apache.ibatis.session.Configuration 的 addInterceptor 注册。这里有几个执行上的细节需要注意Optional 判断要放在 try 之外否则慢 SQL 判断本身也会被算进耗时。反射获取 BoundSql 时还需要处理参数实际项目中可以借助 ms.getConfiguration().newMetaObject 等方式获取完整的可执行 SQL这里给的是简化版。slowSqlMillis 阈值可以通过配置类注入不要在代码里写死。生产环境一般建议先设 1 秒观察一个周期后再调整。5.2 慢 SQL 日志与独立文件结合拦截器打出 warn 级别日志后再利用 Logback 的 logger 配置把它引导到独立文件这样慢 SQL 和普通 SQL 分开定位问题的效率更高。logger namecom.example.project.interceptor.SlowSqlInterceptor levelWARN additivityfalse appender-ref refSLOW_SQL_FILE/ /logger这本质上是 Logback 的精确路由能力。把拦截器类当成普通 logger 来定向输出跟前面把 mapper 包输出到独立文件是同一个套路只是粒度细到了类级别。我实际做过一次比较极端的应用把慢 SQL 日志接到一个单独的告警文件配合脚本每分钟检测文件是否有新增内容一旦发现就触发告警。虽然不如专业 APM 工具强大但在小团队、预算有限的情况下确实能用最低成本解决慢 SQL 的监控问题。6. 常见问题排查与避坑指南配置方式都讲完了最后总结我这些年工作中遇到的高频问题。这些问题在官方文档里很难直接搜到答案属于典型的踩过才知道系列。6.1 问题速查表现象根本原因解决方案配置 logging.level 后 SQL 日志不出现MyBatis 还在用 StdOutImpl 或其他非 Slf4j 实现将 log-impl 改为 Slf4jImpl改了 mapper 包路径配置没反应包路径写错或写成了 XML 文件路径确认包路径是 Mapper 接口包不是 resource 下的 XML 路径SQL 日志在控制台打印两份Logback 里没有设置 additivityfalse为对应 logger 设置 additivityfalseSQL 日志出现在业务日志文件但控制台没有appender 配置指向了文件没有指向 CONSOLE检查 logger 的 appender-ref 配置按需增加 CONSOLE日志里只有 Preparing没有 Parameters级别开到了 debug 但缺少参数绑定输出检查 MyBatis log-impl 是否生效或考虑换框架级别的日志输出JPA 的 SQL 有占位符没有参数值只开了 org.hibernate.SQL debug额外开启 org.hibernate.type.descriptor.sql trace配置了 logback-spring.xml 但完全不生效文件名或路径不对确认文件在 classpath 下且名称为 logback-spring.xmlprofile 相关配置不生效配置文件是 logback.xml 而不是 logback-spring.xml重命名为 logback-spring.xml6.2 配置不生效的三个常见原因第一个原因是依赖冲突。如果你的项目里手动引入了 Log4j2 的依赖同时又不小心排除了 spring-boot-starter-logging那么 Logback 的配置很可能完全不生效。检查方法很简单在 IDE 的依赖树里搜 ch.qos.logback如果没有这个依赖说明 Logback 已经不在运行时环境里了。第二个原因是包路径大小写问题。MyBatis 的 mapper 接口包路径必须和实际代码完全一致不能凭印象写一个看起来差不多的路径。我见过有人把 com.example.mapper 写成了 com.example.Mapper排查了很久。第三个原因是配置被 profile 覆盖。Spring Boot 支持多份配置文件比如 application-dev.yml 和 application-prod.yml。如果你在 application.yml 里配了 logging.level.mapperdebug但 application-dev.yml 里又把 root 级别设为 error实际运行时的效果取决于 profile 激活情况。要养成习惯所有日志级别配置集中在同一份主配置文件中或者明确知道 profile 之间的覆盖规则。6.3 日志打印重复问题的分析与解决日志重复是最高频的坑而且成因不止一种。除了前面说的 additivity 问题还有一种隐蔽的场景项目里不光有 logback-spring.xml还保留了 logback.xml两份配置文件同时存在时 Spring Boot 会优先加载 logback.xml导致你的 logback-spring.xml 完全失效。排查日志重复或日志配置不生效时第一步不是看代码而是看启动日志里有没有这一行Logging system: Logback如果启动日志里显示的日志实现不是 Logback说明你的依赖体系出了问题。如果是 Logback再看加载的配置文件路径Spring Boot 会在启动时打印类似Logging configuration: classpath:logback-spring.xml的信息。这两行信息能定位 80% 的问题。还有一个容易忽略的点某些中间件会自带日志配置。比如 Dubbo、Spring Cloud Gateway 等框架可能在依赖里带了自己的 logback 配置文件通过 Maven 的 dependency 管理把项目里的配置文件覆盖掉。遇到这种情况可以通过排除依赖或者指定 logging.config 来强制指定配置路径。6.4 参数结果日志的隐私与安全提醒SQL 日志里会打印参数值和查询结果包括用户手机号、身份证、姓名等敏感信息。开发环境打出来没问题但生产环境日志文件如果被未授权人员拿到就是严重的数据泄露事件。建议至少做到两点生产环境只输出 SQL 语句不输出参数值和结果集。MyBatis 系可以使用日志级别控制不开启 trace 或调整 log-implJPA/Hibernate 系则不开 trace。如果业务确实需要完整 SQL 参数日志比如金融支付类系统的对账需求必须对日志文件做权限管控日志采集链路要做脱敏处理。这里有一个安全细节MyBatis 的 StdOutImpl 输出结果集时日志里会显示完整的结果行。相比 Slf4jImpl 只输出 Preparing 和 ParametersStdOutImpl 的敏感信息暴露面更大。从这个角度讲我也更推荐生产环境使用 Slf4jImpl 而不是 StdOutImpl。6.5 动态调日志级别的实用技巧最后分享一个小技巧配置文件的日志级别是静态的但排查线上问题时往往需要临时调高某个包的日志级别又不想重启应用。Spring Boot 提供了 actuator 的 loggers 端点可以动态修改。依赖里加上 spring-boot-starter-actuator然后通过 POST 请求即可curl -X POST http://localhost:8080/actuator/loggers/com.example.project.mapper \ -H Content-Type: application/json \ -d {configuredLevel:DEBUG}这个操作在生产环境非常实用。比如某个 SQL 接口突然变慢你临时把 mapper 包级别调到 debug查看日志后马上调回 info全程不用重启对在线业务零影响。需要注意的是这个端点在生产环境暴露时有安全风险必须配合 Spring Security 做权限控制或者至少限制内网访问。写在最后Spring Boot 打印 SQL 日志这件事配置本身不难难点在于搞清楚不同持久层框架的日志机制差异以及怎么让日志输出体系跟业务监控需求匹配。我个人的经验是不管用什么框架生产环境永远优先走 Logback 体系而不是依赖框架自身的 stdout 输出。统一的日志链路后续做文件归档、日志采集、慢 SQL 监控才能游刃有余。最后再分享一个自己的习惯每次新项目启动先花两分钟确认日志配置文件被正确加载、mapper 包路径正确、SQL 日志能正常输出再开始写业务代码。这个习惯帮我避免了无数次项目写完了才发现日志体系有问题的返工。你的项目如果现在还在为 SQL 日志发愁照着上面的配置走一遍应该五分钟之内就能看到自己想要的输出。
返回列表