Apache Chainsaw 2.1.0 快速入门与集成示例
Apache Chainsaw 2.1.0 快速入门实测:五分钟加载真实日志文件、实时追尾验证、无图形 Java 集成与 UDP 网络收发示例,全部命令与输出均来自真实环境。
Apache Chainsaw 2.1.0 快速入门与集成示例
目标:从零启动 Apache Chainsaw 2.1.0,加载一份真实应用日志并验证实时追尾;同时给出无图形环境下的 Java 集成示例(LogFilePatternReceiver 解析、UDP 网络收发),全部实测通过。
一、环境信息
| 项目 | 值 |
|---|---|
| 操作系统 | Ubuntu 22.04(x86_64) |
| 实测服务器 | chainsaw-ubuntu-01(实验专用环境) |
| Java 运行时 | OpenJDK 17.0.20.1(openjdk-17-jdk) |
| Chainsaw 版本 | Apache Chainsaw 2.1.0(standalone 包,2021-06-12 发布) |
| 显示环境 | Xvfb :99(1280x1024x24 虚拟显示) |
| 演示日志 | /root/chainsaw-demo/app.log(模拟应用服务器日志,含中文与异常堆栈) |
Chainsaw 是 Log4j 生态的日志分析 GUI:以"接收器(Receiver)+ 事件表 + 过滤器/颜色规则"为核心模型,支持从文件、UDP、组播、Socket、数据库等来源实时接收 log4j 事件。版本说明:官方下载目录当前仅有 2.1.0 一个版本,项目处于低维护状态,本文即以 2.1.0 为准。
安装步骤(下载、校验、解压、JDK 兼容性处理)见《Ubuntu 22.04 安装 Apache Chainsaw 指南》,本文直接从已安装的 /opt/apache-chainsaw-2.1.0 出发。
二、准备演示环境
2.1 演示日志文件
一份贴近真实形态的应用日志:4 个 INFO/WARN/ERROR/DEBUG 级别样例、一条异常堆栈(首行 + 两个 \tat 帧):
mkdir -p /root/chainsaw-demo
cat > /root/chainsaw-demo/app.log <<'EOF'
2026-10-15 10:00:00,001 INFO [com.example.service.UserService] 用户登录成功 userId=1001 ip=192.168.10.25
2026-10-15 10:00:02,417 WARN [com.example.service.UserService] 登录重试次数达到阈值 userId=1002 retries=5
2026-10-15 10:00:05,090 ERROR [com.example.dao.UserDao] 查询用户失败: connection timeout after 3000ms
2026-10-15 10:00:07,552 DEBUG [com.example.dao.UserDao] SELECT * FROM t_user WHERE id=1001
2026-10-15 10:00:09,878 INFO [com.example.web.OrderController] 订单提交成功 orderId=202610159876
2026-10-15 10:00:11,204 ERROR [com.example.web.OrderController] 提交订单异常
java.sql.SQLException: Connection refused
at com.example.dao.UserDao.query(UserDao.java:88)
at com.example.web.OrderController.submit(OrderController.java:41)
EOF
2.2 接收器配置文件
Chainsaw 自身不解析任意格式的文本日志,需要告诉它"字段怎么排布"。LogFilePatternReceiver 的格式模板与日志行一一对应:
cat > /root/chainsaw-demo/chainsaw-gui-config.xml <<'EOF'
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<plugin name="demoApp" class="org.apache.log4j.varia.LogFilePatternReceiver">
<param name="logFormat" value="TIMESTAMP LEVEL [LOGGER] MESSAGE"/>
<param name="timestampFormat" value="yyyy-MM-dd HH:mm:ss,SSS"/>
<param name="fileURL" value="file:///root/chainsaw-demo/app.log"/>
<param name="tailing" value="true"/>
</plugin>
</log4j:configuration>
EOF
四个参数的含义:logFormat 是列模板(大写关键字为占位符,方括号等字面字符原样匹配);timestampFormat 是时间戳的 SimpleDateFormat;fileURL 支持本地 file:// 与远程 URL(VFS,含 sftp);tailing=true 表示读完整个文件后继续轮询文件尾部(轮询间隔 waitMillis 默认 2000 毫秒,实测从退出时自动保存的 receiver-config.xml 中确认)。
三、五分钟上手:启动并加载真实日志
3.1 启动命令
LogUI.main 会把第一个命令行参数当作 log4j 配置 URL 立即加载,其中的 <plugin> 注册为接收器——这是免交互启动的标准方式:
export DISPLAY=:99
export JAVA_OPTS='--add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.base/java.time=ALL-UNNAMED --add-opens java.base/java.math=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.util.concurrent=ALL-UNNAMED --add-opens java.desktop/java.awt=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED'
/opt/apache-chainsaw-2.1.0/bin/chainsaw file:///root/chainsaw-demo/chainsaw-gui-config.xml >/tmp/chainsaw-run.log 2>&1 &
sleep 40
ps aux | grep 'chainsaw[.]LogUI' | grep -v grep | wc -l
实测回显:
1
进程存活。若在普通桌面环境直接运行,去掉 DISPLAY 与 JAVA_OPTS(Java 11 无需 add-opens;Java 17 必需,原因见安装指南 FAQ)。
3.2 界面导览(截图验证)
对虚拟显示整屏截图:
DISPLAY=:99 import -window root /root/chainsaw-demo/shot1-main.png
ls -l /root/chainsaw-demo/shot1-main.png
实测回显:
-rw-r--r-- 1 root root 27342 Oct 15 15:03 /root/chainsaw-demo/shot1-main.png
截图证据:/tmp/commons-batch1-b2/chainsaw/ubuntu-shot1-main.png。界面自左至右、自上而下依次为:
| 区域 | 实测内容 |
|---|---|
| 菜单栏 | File、View、Current tab、Connect to、Help |
| 工具栏 | 暂停、重新加载等操作按钮 |
| 标签页 | Welcome(欢迎/帮助)、Zeroconf(局域网发现)、file-/root/chainsaw-demo/app.log(日志标签页,以文件路径命名)、chainsaw-log(Chainsaw 自身运行日志) |
| 事件表 | 列:ID、TIMESTAMP、MARKER、LEVEL、LOGGER、MESSAGE、THROWABLE、THREAD;实测 6 行事件全部呈现 |
| 颜色规则 | WARN 行黄色底、ERROR 行红色底、DEBUG 行灰色底(默认颜色规则自动生效) |
| 左侧面板 | Root Logger 树(可见 com 节点) |
| 状态栏 | Looks like ZeroConf is available... WooHoo!、Filtered/Total: 6:6(过滤数/总数) |
6 行日志、1 条异常堆栈全部正确入表,中英文混排渲染正常,无需任何手工点击。
3.3 实时追尾实测
保持 Chainsaw 运行,向被监听文件追加 3 行新日志:
cat >> /root/chainsaw-demo/app.log <<'EOF'
2026-10-15 10:05:21,330 WARN [com.example.service.AuthService] 密码连续错误锁定账号 userId=1007
2026-10-15 10:05:24,871 ERROR [com.example.service.AuthService] 鉴权服务调用 LDAP 超时 elapsed=5000ms
2026-10-15 10:05:27,118 INFO [com.example.service.AuthService] 账号解锁成功 userId=1007
EOF
sleep 8
DISPLAY=:99 import -window root /root/chainsaw-demo/shot2-tailing.png
截图证据:/tmp/commons-batch1-b2/chainsaw/ubuntu-shot2-tailing.png。实测结果:事件表从 6 行增长到 9 行,新增的 3 行(锁定账号 WARN 黄底、LDAP 超时 ERROR 红底、解锁成功 INFO)自动出现在表尾,状态栏计数变为 Filtered/Total: 9:9,并出现事件速率 1/s。从追加到入表约 2~4 秒(与默认 waitMillis=2000 轮询间隔吻合)。
3.4 正常退出与配置落盘
File 菜单实测包含:Load Chainsaw configuration、Open log4j XML-formatted file (xml or zip)…(Ctrl+O)、Open util.logging XML-formatted file…、Open remote log4j/util.logging XML-formatted file…、Save displayed events as…(Ctrl+S)、MRU…、Exit(Ctrl+X)。(菜单截图证据:/tmp/commons-batch1-b2/chainsaw/ubuntu-shot3-filemenu.png)
通过 File → Exit 正常退出后,$HOME/.chainsaw/ 下生成(实测文件清单):
chainsaw.settings.properties receiver-config.xml
chainsaw.settings.xml mru.xml
tab-settings.xml Default.colors
file-%2Froot%2Fchainsaw-demo%2Fapp.log.xml
file-%2Froot%2Fchainsaw-demo%2Fapp.log.xml.colors
file-%2Froot%2Fchainsaw-demo%2Fapp.log.xml.properties
chainsaw-log.xml
其中 receiver-config.xml 即退出时自动保存的接收器配置(含 waitMillis=2000 等运行参数),下次启动可通过 File → Load Chainsaw configuration 或命令行参数复用。
💡 技术拓展:
receiver-config.xml采用 log4j 原生 XML 配置语法,<plugin>元素正是 log4j 1.x 的插件机制。这意味着同一份配置既能给 GUI 用,也能在自研 Java 程序里经 PluginRegistry 复用,两端行为一致。
四、无图形方式集成:Java 程序调用接收器
服务器上没有显示环境、或需要在自研工具里复用 Chainsaw 的解析能力时,可直接调用其接收器 API。Chainsaw 的事件模型与 log4j 1.x 完全一致,Receiver.doPost() 的分发逻辑是:按事件 logger 名取 Logger,级别过阈值后 callAppenders() 沿层级传播——因此把自定义 Appender 挂到自建 LoggerRepository 的根上即可收集全部事件。
4.1 编译期准备
classpath 使用 standalone 包 repo/ 中的全部 jar(或 bin 包根目录的 10 个 jar)。注意编译与运行都要用 Java 9+(主 jar 字节码 major 53)。
4.2 完整示例代码
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Hierarchy;
import org.apache.log4j.Level;
import org.apache.log4j.spi.LoggerRepository;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.RootLogger;
import org.apache.log4j.varia.LogFilePatternReceiver;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class QuickStartDemo {
static final SimpleDateFormat TS = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS");
static class Collect extends AppenderSkeleton {
final List<LoggingEvent> events = new ArrayList<>();
protected void append(LoggingEvent e) { events.add(e); }
public void close() {}
public boolean requiresLayout() { return false; }
}
public static void main(String[] args) {
LoggerRepository repo = new Hierarchy(new RootLogger(Level.DEBUG));
repo.getLogger("org.apache.log4j").setLevel(Level.OFF); // 屏蔽接收器自身日志
Collect cap = new Collect();
repo.getRootLogger().addAppender(cap); // 事件收集器挂根
LogFilePatternReceiver r = new LogFilePatternReceiver();
r.setName("demoApp");
r.setLoggerRepository(repo);
r.setFileURL("file:///root/chainsaw-demo/app.log");
r.setLogFormat("TIMESTAMP LEVEL [LOGGER] MESSAGE");
r.setTimestampFormat("yyyy-MM-dd HH:mm:ss,SSS");
r.setTailing(false); // 一次性读完整文件
r.setUseCurrentThread(true); // 当前线程同步处理,便于测试
r.activateOptions();
System.out.println("解析事件数=" + cap.events.size());
for (LoggingEvent e : cap.events) {
System.out.println("[" + TS.format(new Date(e.getTimeStamp())) + "] "
+ e.getLevel() + " " + e.getLoggerName() + " - "
+ e.getRenderedMessage().replace("\n", "\\n"));
}
System.out.println("首事件属性表=" + cap.events.get(0).getProperties());
}
}
说明两点:repo.getLogger("org.apache.log4j").setLevel(Level.OFF) 用于屏蔽接收器把自身工作日志(attempting to load file、processing complete 等)也当事件投递的行为(实测确认存在该行为);setUseCurrentThread(true) 让文件在 activateOptions() 调用线程内同步处理完,测试无需等待后台线程。
4.3 实测输出
cd /root/chainsaw-demo
# dist/ 为 bin 包解压目录(10 个 jar 平铺,见安装指南方案A)
CP=$(ls dist/*.jar | tr '\n' ':' | sed 's/:$//')
javac -encoding UTF-8 -cp "$CP" QuickStartDemo.java
java -Dfile.encoding=UTF-8 -cp ".:$CP" QuickStartDemo
实测回显(app.log 为 9 行版本,含后补的 3 行):
解析事件数=9
[2026-10-15 10:00:00,001] INFO com.example.service.UserService - 用户登录成功 userId=1001 ip=192.168.10.25
[2026-10-15 10:00:02,417] WARN com.example.service.UserService - 登录重试次数达到阈值 userId=1002 retries=5
[2026-10-15 10:00:05,090] ERROR com.example.dao.UserDao - 查询用户失败: connection timeout after 3000ms
[2026-10-15 10:00:07,552] DEBUG com.example.dao.UserDao - SELECT * FROM t_user WHERE id=1001
[2026-10-15 10:00:09,878] INFO com.example.web.OrderController - 订单提交成功 orderId=202610159876
[2026-10-15 10:00:11,204] ERROR com.example.web.OrderController - 提交订单异常\njava.sql.SQLException: Connection refused
[2026-10-15 10:05:21,330] WARN com.example.service.AuthService - 密码连续错误锁定账号 userId=1007
[2026-10-15 10:05:24,871] ERROR com.example.service.AuthService - 鉴权服务调用 LDAP 超时 elapsed=5000ms
[2026-10-15 10:05:27,118] INFO com.example.service.AuthService - 账号解锁成功 userId=1007
首事件属性表={hostname=file, application=/root/chainsaw-demo/app.log, receiver=demoApp}
与 GUI 呈现完全一致:时间戳、级别、logger、消息逐字段正确;异常首行并入 MESSAGE;每个事件自动携带 hostname、application(此处为文件 URL)、receiver(接收器名)三个属性,与 GUI 事件表右侧的属性列一致。
4.4 tailing 模式的无头验证
把 setTailing(true) 并去掉 setUseCurrentThread(true),接收器转入后台轮询线程。实测:启动时 1 条事件,追加 2 行后约 2 秒轮询周期内自动收集到 3 条:
初始事件数=1
已追加 2 行, 等待接收器轮询...
追加后事件数=3
[2026-10-15 11:00:00,100] INFO com.example.boot.App - 应用启动完成 port=8080
[2026-10-15 07:38:32,410] WARN com.example.boot.App - 堆内存使用率超过 80% used=82%
[2026-10-15 07:38:32,910] ERROR com.example.boot.App - 健康检查失败: downstream unreachable
TailingDemo DONE
这与 GUI 的实时追尾(3.3 节)互为印证。
五、与 Log4j 1.x 应用集成:UDP 网络收发
比读文件更贴近生产的方式:应用侧用 log4j 的 UDPAppender 把事件发往网络,Chainsaw 用 UDPReceiver 接收。同一台服务器上收发两端分离实测:
// 关键片段(完整实测程序见第四章模式,收集器与 Repository 相同)
LoggerRepository repo = new Hierarchy(new RootLogger(Level.DEBUG));
repo.getLogger("org.apache.log4j").setLevel(Level.OFF);
Collect cap = new Collect();
repo.getRootLogger().addAppender(cap);
UDPReceiver r = new UDPReceiver();
r.setName("udpDemo");
r.setPort(14560);
r.setLoggerRepository(repo);
r.activateOptions();
Thread.sleep(800);
Logger send = Logger.getLogger("udp.source"); // 应用侧,默认仓库
send.setAdditivity(false);
UDPAppender ap = new UDPAppender("127.0.0.1", 14560);
ap.setLayout(new XMLLayout());
send.addAppender(ap);
send.info("UDP 转发消息 one");
send.warn("UDP 转发消息 two");
send.error("UDP 转发消息 three", new IllegalStateException("bad state"));
Thread.sleep(3000);
实测回显:
== UDPReceiver@14560 事件数=3
[2026-10-15 07:39:49,259] INFO udp.source - UDP 转发消息 one
[2026-10-15 07:39:49,267] WARN udp.source - UDP 转发消息 two
[2026-10-15 07:39:49,267] ERROR udp.source - UDP 转发消息 three
3 条 UDP 事件全部到达。要点:UDPAppender 的 Layout 必须是 XMLLayout(事件按 XML 编码成数据报,UDPReceiver 内部用 XMLDecoder 解码);GUI 中对应操作是在"Load events into Chainsaw"对话框里选择 UDP Receiver 并填端口 14560,事件同样实时入表。
六、验证与观测要点清单
启动集成后,建议按以下实测可观测点逐项确认:
- 进程存活:
ps aux | grep 'chainsaw[.]LogUI'数量为 1; - 标签页出现:以 fileURL 命名的日志标签页(
file-/root/chainsaw-demo/app.log); - 计数正确:状态栏
Filtered/Total与日志行数一致(实测 6:6 → 9:9); - 字段正确:时间戳/级别/logger/消息与源文件逐行核对(实测见 4.3 输出);
- 颜色规则生效:WARN 黄底、ERROR 红底(默认规则,无需配置);
- 实时性:追加日志后 2~4 秒入表(waitMillis=2000 轮询);
- 异常呈现:堆栈首行进 MESSAGE 列,
\tat帧进 THROWABLE 列(GUI 实测;无头 API 下堆栈帧不进入 ThrowableInformation,见 Receivers 详解篇的 limitations 说明)。
七、常见问题(FAQ)
Q1:启动后没有出现日志标签页,也没有报错?
命令行参数必须是合法 URL(file:///...,三道斜杠)。相对路径会被静默忽略,源码中 main 方法对 args[0] 直接 new URL(),非法 URL 抛异常但 GUI 仍会以"无接收器"模式启动。
Q2:事件表出现但行数比文件行数少?
检查 logFormat 与日志行是否严格对齐(分隔符、方括号、空格数量)。对不上号的行会被丢弃而非报错——这是 LogFilePatternReceiver 的设计行为。
Q3:界面上接收器列表为空、总弹"Load events into Chainsaw"对话框? 该对话框仅在"启动时无任何接收器"时出现。要么走命令行配置 URL,要么在对话框里手工配置一次后正常退出(配置自动保存,下次启动不再弹)。
Q4:Java 17 下启动即退出,日志有 InaccessibleObjectException?
缺 --add-opens,完整 11 项清单见安装指南。Java 11 则不需要。
Q5:能同时监听多个文件吗?
可以。配置文件里放多个 <plugin>(name 各异、fileURL 各异),每个接收器一个标签页;实测退出时自动保存的 receiver-config.xml 也是这个结构。
八、参考链接
- Apache Chainsaw 官方下载目录:https://downloads.apache.org/logging/chainsaw/
- Chainsaw 2.1.0 包内离线快速导览:
/opt/apache-chainsaw-2.1.0/site/quicktour.html - LogFilePatternReceiver 类文档(发行包 src/ 内):
src/main/java/org/apache/log4j/varia/LogFilePatternReceiver.java - log4j 1.2 API(LoggingEvent/AppenderSkeleton):https://logging.apache.org/log4j/1.2/apidocs/
- 本系列:《Ubuntu 22.04 安装 Apache Chainsaw 指南》《Apache Chainsaw 2.1.0 常用功能与 Receivers 详解》