每个 Java 项目都需要日志。每个 Java 开发者都有一两个关于日志配置的"战争故事"------那个膨胀到 200 行的 XML、互相打架的桥接包、深夜因为日志级别没控制好打满磁盘的报警。
Solon 换了一种做法:保留行业标准(SLF4J),但用一段 YAML 替代了 XML 配置的混乱。日志配置不再是一个需要单独学习的"副项目",而是应用配置的自然延伸。
全栈:SLF4J 直通
Solon 原生使用 SLF4J 作为日志门面,没加任何私有包装层:
java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class OrderService {
static final Logger log = LoggerFactory.getLogger(OrderService.class);
public void createOrder(Order order) {
log.info("创建订单: {}", order.getId());
}
}
没有桥接,没有适配器,没有额外依赖。如果你用过 SLF4J,你已经在用 Solon 的日志 API 了。
五个插件,一套配置
Solon 提供了五种日志实现,每种都对接同一套配置模型。切换实现只需换一个依赖:
| 插件 | 添加器支持 | 适用场景 |
|---|---|---|
solon-logging-simple |
console, cloud | 测试/轻量,不产生本地日志文件 |
solon-logging-logback |
console, file, cloud | 生产推荐(Logback 1.3.x, Java 8+) |
solon-logging-logback-jakarta |
console, file, cloud | Jakarta 命名空间(Logback 1.5.x, Java 11+) |
solon-logging-log4j2 |
console, file, cloud | 团队统一使用 Log4j2 |
water-solon-cloud-plugin |
console, cloud | Water 服务治理平台对接 |
加入一个依赖就完成了配置:
xml
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon-logging-logback</artifactId>
</dependency>
就这样。不需要 logback.xml,不需要 log4j2.xml------除非你需要高级定制。
配置:一段 YAML 搞定
所有日志配置集中在 solon.logging.appender 和 solon.logging.logger 两个字段下:
yaml
solon:
app:
name: demoapp
solon.logging.appender:
console:
level: TRACE
enable: true
pattern: "%highlight{%-5level %d{yyyy-MM-dd HH:mm:ss.SSS} #%5X{pid} [-%t][*%X{traceId}]%tags[%logger{20}]:} %n%msg%n"
file:
name: "logs/${solon.app.name}"
level: INFO
enable: true
extension: ".log"
maxFileSize: "10 MB"
maxHistory: "7"
pattern: "%-5level %d{yyyy-MM-dd HH:mm:ss.SSS} #%5X{pid} [-%t][*%X{traceId}]%tags[%logger{20}]: %n%msg%n"
cloud:
level: INFO
enable: true
框架内置三个添加器:
- console --- 标准输出,默认启用
- file --- 滚动日志文件,默认启用
- cloud --- 远程日志服务,默认关闭
按包控制日志级别
yaml
solon.logging.logger:
"root":
level: DEBUG
"com.zaxxer.hikari":
level: WARN
"com.demo.order":
level: INFO
优先级规则很简单:appender 级别 > logger 级别 > root 级别,更具体的配置优先。
五个级别,标准语义
text
TRACE < DEBUG < INFO < WARN < ERROR
和你一直在用的级别一样。
MDC:SLF4J 标准支持
Solon 直接继承 SLF4J 的 MDC 能力,没有封装私有 API。默认日志 pattern 中已经包含了 %X{pid}(进程 ID)和 %X{traceId}(分布式链路追踪 ID),这些由框架自动填充。
如果需要自定义上下文,用标准 SLF4J 方式:
java
import org.slf4j.MDC;
MDC.put("orderId", order.getId());
log.info("处理支付");
MDC.remove("orderId");
没有 Solon 专属的 MDC 包装类,就是原生 SLF4J。
自定义添加器
内置添加器不够用时,继承 AppenderBase:
java
package demo.log;
import org.noear.solon.logging.event.AppenderBase;
import org.noear.solon.logging.event.LogEvent;
public class JsonAppender extends AppenderBase {
@Override
public void append(LogEvent logEvent) {
String json = toJson(logEvent);
send(json);
}
}
在 app.yml 中注册:
yaml
solon.logging.appender:
json:
level: INFO
class: demo.log.JsonAppender
异步批量日志:PersistentAppenderBase
对于高吞吐场景,Solon 提供了 PersistentAppenderBase------一个将日志事件缓冲后批量刷盘的基类:
java
package demo.log;
import org.noear.solon.logging.persistent.PersistentAppenderBase;
import org.noear.solon.logging.event.LogEvent;
import org.noear.solon.Solon;
public class DatabaseAppender extends PersistentAppenderBase {
LogService logService;
public DatabaseAppender() {
Solon.context().getBeanAsync(LogService.class, bean -> {
logService = bean;
});
}
@Override
public void onEvents(List<LogEvent> list) {
if (logService != null) {
logService.insertList(list);
}
}
}
适合将日志写入数据库、Elasticsearch 或其他远程存储,不阻塞请求线程。批量写入在高负载下能显著降低 I/O 开销。
高级定制:XML 逃生门
如果 YAML 不满足你的需求,在 classpath 根目录放一个 logback-solon.xml 或 log4j2-solon.xml 即可。框架会自动识别,且支持环境变体(logback-solon-dev.xml、logback-solon-prod.xml)。
也可以用标准的 logback.xml 或 log4j2.xml------但这样会失去按环境切换的能力。
云端日志:一个插件的事
分布式环境下,引入 water-solon-cloud-plugin 并启用 cloud 添加器:
yaml
solon.logging.appender:
cloud:
level: INFO
enable: true
日志自动上报 Water 服务治理平台,无需额外部署日志采集 agent。
诚实地说
Solon 的日志模块不会替代完整的可观测性管道(Logstash + Elasticsearch、Loki + Grafana)。但对绝大多数项目来说,它覆盖了所有日常需求:
- ✅ SLF4J 原生,告别桥接依赖
- ✅ YAML 驱动,告别 XML 样板代码
- ✅ 三个内置添加器(console / file / cloud)
- ✅ 自定义添加器支持
- ✅ 异步批量持久化
- ✅ MDC 开箱即用
- ✅ 按包控制日志级别
- ✅ 环境感知配置切换
- ✅ Logback/Log4j2 XML 逃生门
设计哲学和 Solon 的其他模块一样:用最少的配置解决最常见的问题,需要定制时绝不挡路。
如果你想了解完整的插件列表和配置参考,官方文档在 solon.noear.org。