如果你正在开发或维护 Erlang/OTP 应用,却苦于无法直观地监控运行时状态,这篇文章正是为你准备的。传统 Erlang 开发中,我们往往通过日志和命令行工具来推测系统运行状况,但这种方式就像盲人摸象,难以全面把握进程状态、监督树结构和系统负载。
observer_cli 的出现改变了这一局面。这个基于终端的实时监控工具,让 OTP 应用的运行时状态一目了然。与官方自带的 observer GUI 工具不同,observer_cli 专为服务器环境和命令行操作优化,无需图形界面即可提供完整的系统观测能力。
本文将带你深入掌握 observer_cli 的使用,从安装配置到实战应用,让你真正实现对 Erlang/OTP 应用的"透明化"监控。
1. 为什么 observer_cli 是 Erlang 开发者的必备工具
在分布式系统和高并发场景下,Erlang/OTP 的强大之处在于其进程模型和监督机制。但这也带来了监控复杂度:当系统中有成千上万个进程同时运行时,如何快速定位问题进程?如何理解监督树的结构关系?如何分析系统的瓶颈所在?
observer_cli 解决了三个核心痛点:
可视化监控盲区:传统方式下,开发者需要手动调用sys:get_status/1或erlang:process_info/1来获取单个进程信息,效率低下且难以形成整体认知。observer_cli 以分层方式展示整个监督树,让进程关系一目了然。
实时性能洞察:内存使用、进程数量、消息队列长度等关键指标实时更新,帮助开发者快速发现异常模式。比如某个进程的消息队列持续增长,往往意味着处理能力不足或发生了死锁。
生产环境友好:作为纯命令行工具,observer_cli 在服务器环境下无需任何图形依赖,通过 SSH 连接即可使用,非常适合生产环境的监控需求。
2. observer_cli 的核心功能与优势对比
observer_cli 提供了多层次监控视图,每个视图都针对特定监控需求设计:
2.1 系统概览视图
显示 CPU、内存、IO 等系统级指标,帮助快速了解整体负载情况。与erlang:memory/0等命令相比,observer_cli 以更直观的方式展示数据变化趋势。
2.2 进程监控视图
列出所有活跃进程的关键信息:内存占用、消息队列长度、缩减次数等。这对于发现"问题进程"特别有用——比如消息队列过长的进程往往是性能瓶颈的源头。
2.3 监督树视图
以树形结构展示应用监督关系,这是 observer_cli 的杀手级功能。你可以清晰地看到 supervisor 如何管理 worker 进程,以及整个应用的启动层次。
2.4 ETS 表监控
ETS 表是 Erlang 性能优化的关键,observer_cli 可以监控每个表的大小、类型和内存使用,帮助优化数据存储策略。
2.5 与官方 observer 的对比
| 特性 | observer_cli | 官方 observer |
|---|---|---|
| 使用环境 | 纯终端,适合服务器 | 需要图形界面 |
| 启动速度 | 快速,直接运行 | 相对较慢 |
| 功能完整性 | 核心监控功能齐全 | 功能更全面 |
| 远程连接 | SSH 直接使用 | 需要 X11 转发 |
| 资源占用 | 较低 | 相对较高 |
对于需要在生产环境进行监控的场景,observer_cli 无疑是更实用的选择。
3. 环境准备与安装部署
3.1 环境要求
- Erlang/OTP 21.0 或更高版本(推荐 OTP 24+)
- rebar3 或 erlang.mk 构建工具
- 终端支持 UTF-8 和颜色显示
3.2 安装方式
方式一:作为项目依赖安装(推荐)
在rebar.config中添加依赖:
{deps, [ {observer_cli, "1.7.0"} ]}.然后编译项目:
rebar3 compile方式二:全局安装
git clone https://github.com/zhongwencool/observer_cli.git cd observer_cli rebar3 compile将以下代码添加到你的 Erlang shell 启动脚本(~/.erlang)中:
%% 自动加载 observer_cli case code:which(observer_cli) of non_existing -> ObserverCliPath = "/path/to/observer_cli/ebin", code:add_patha(ObserverCliPath); _ -> ok end.3.3 验证安装
启动 Erlang shell 测试安装:
$ erl Erlang/OTP 25 [erts-13.0] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit] Eshell V13.0 (abort with ^G) 1> observer_cli:start().如果看到监控界面,说明安装成功。
4. 基础使用与界面导航
4.1 启动方式
在运行中的 Erlang 节点启动:
%% 方式1:直接启动 observer_cli:start(). %% 方式2:带参数启动 observer_cli:start(#{ type => system, % 系统视图 interval => 2000 % 刷新间隔2秒 }).在启动 Erlang 时直接开启:
erl -eval "observer_cli:start()" -sname mynode4.2 界面导航详解
observer_cli 采用分层界面设计,主要操作键位:
q - 退出当前视图或返回上级 tab - 切换视图/选项卡 ↑↓←→ - 上下左右选择 enter - 进入详细视图 r - 手动刷新系统视图界面示例:
System Overview (Refresh: 2000ms) ┌─────────────────────────────────────────────────────────────┐ │ Memory: Total=124MB, Process=45MB, System=79MB, Atom=0.8MB │ │ ProcCount: 345/limit=262144, RunQueue: 1, IO: 0.5% │ │ CPU: 15.2% (Avg: 12.1%), Ports: 23, ETS: 45 tables │ └─────────────────────────────────────────────────────────────┘5. 核心监控功能实战演示
5.1 监控自定义应用进程
假设我们有一个简单的 gen_server 应用:
-module(my_server). -behaviour(gen_server). -export([start_link/0, init/1, handle_call/3, handle_cast/2]). start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). init([]) -> {ok, #{count => 0}}. handle_call(get_count, _From, State = #{count := Count}) -> {reply, Count, State}; handle_call(increment, _From, State = #{count := Count}) -> NewState = State#{count => Count + 1}, {reply, ok, NewState}. handle_cast(reset, _State) -> {noreply, #{count => 0}}.启动应用后,在 observer_cli 的进程视图中可以找到我们的进程:
%% 启动应用 1> my_server:start_link(). {ok,<0.128.0>} %% 在observer_cli中查看进程信息 Processes View: ┌─────────────────────────────────────────────────────────────────────┐ │ PID Memory MsgQ Reductions Current Function │ │ <0.128.0> 2.1KB 0 124 my_server:handle_call/3 │ └─────────────────────────────────────────────────────────────────────┘5.2 分析监督树结构
创建一个简单的监督树:
-module(my_sup). -behaviour(supervisor). -export([start_link/0, init/1]). start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). init([]) -> ChildSpecs = [ #{id => my_server, start => {my_server, start_link, []}, restart => permanent, type => worker} ], {ok, {#{strategy => one_for_one}, ChildSpecs}}.在 observer_cli 的监督树视图中,可以看到清晰的层次关系:
Application Hierarchy: my_sup (supervisor) └── my_server (worker)5.3 监控系统负载与性能
通过系统视图监控关键指标:
%% 获取当前系统状态(编程方式) 1> observer_cli:system_info(). #{memory_total => 132456789, memory_processes => 45678901, run_queue => 2, port_count => 15, ets_count => 32}6. 高级功能与定制化配置
6.1 自定义监控视图
observer_cli 支持插件式开发,可以创建自定义监控面板:
-module(my_plugin). -export([display/1]). display(Opts) -> MyData = collect_my_metrics(), observer_cli_lib:parse_integer(Opts, interval, 1000), observer_cli_lib:render([ observer_cli_lib:title("Custom Metrics"), observer_cli_lib:description("My application metrics"), observer_cli_lib:table([ {"Active Users", maps:get(active_users, MyData)}, {"Queue Length", maps:get(queue_len, MyData)} ]) ]). collect_my_metrics() -> #{active_users => 150, queue_len => 23}.6.2 远程节点监控
监控远程 Erlang 节点:
%% 连接到远程节点 1> net_kernel:connect_node('remote@hostname'). true %% 启动observer_cli监控远程节点 2> observer_cli:start(#{node => 'remote@hostname'}).6.3 配置持久化
创建配置文件observer_cli.config:
[ {observer_cli, [ {default_interval, 3000}, {max_processes, 5000}, {sort_key, memory} % 默认按内存排序 ]} ].启动时加载配置:
observer_cli:start(#{config_file => "observer_cli.config"}).7. 常见问题与排查指南
7.1 启动问题排查
问题:启动时报错 "undefined function observer_cli:start/0"
原因:observer_cli 未正确加载到代码路径
解决方案:
%% 检查代码路径 1> code:get_path(). %% 手动添加路径 2> code:add_path("/path/to/observer_cli/ebin"). %% 验证模块是否加载 3> code:which(observer_cli). "/path/to/observer_cli/ebin/observer_cli.beam"问题:界面显示乱码
原因:终端不支持 UTF-8 或颜色
解决方案:
# 设置终端环境变量 export LANG=en_US.UTF-8 export TERM=xterm-256color7.2 性能问题排查
问题:observer_cli 本身占用过高 CPU
原因:刷新间隔过短或监控进程过多
解决方案:
%% 增加刷新间隔 observer_cli:start(#{interval => 5000}). % 5秒刷新 %% 限制监控的进程数量 observer_cli:start(#{max_processes => 1000}).问题:内存监控数据不准确
原因:Erlang 内存分配策略导致
解决方案:结合多个工具验证
%% 交叉验证内存数据 1> erlang:memory(). [{total,132456789},{processes,45678901},...] 2> recon_alloc:memory(used). 1324567897.3 常见错误代码表
| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
| 无法连接远程节点 | 节点未启动或网络问题 | 检查节点状态和防火墙 |
| 进程列表不完整 | 进程数量超过限制 | 调整 max_processes 参数 |
| 监控数据延迟 | 系统负载过高 | 增加刷新间隔或优化应用 |
| ETS 表显示异常 | 表权限限制 | 确保有读取权限 |
8. 生产环境最佳实践
8.1 安全监控策略
最小权限原则:在生产环境,为 observer_cli 创建专用账户,限制其系统访问权限。
%% 使用cookie加强安全 $ erl -setcookie myscretcookie -sname observer %% 在代码中验证权限 check_permission() -> case os:getenv("OBSERVER_ENABLED") of "true" -> ok; _ -> {error, unauthorized} end.网络隔离:监控流量应该通过专用网络通道,避免暴露在公网。
8.2 性能优化建议
合理设置刷新频率:
- 开发环境:1-2 秒
- 测试环境:3-5 秒
- 生产环境:10-30 秒
选择性监控:只监控关键进程和指标,减少性能开销。
observer_cli:start(#{ interval => 10000, focus_pids => [regisered_process1, registered_process2] }).8.3 监控告警集成
将 observer_cli 与现有监控系统集成:
%% 自定义告警检查 check_alerts() -> SysInfo = observer_cli:system_info(), case maps:get(run_queue, SysInfo) > 50 of true -> send_alert("High run queue detected"); false -> ok end. send_alert(Msg) -> %% 集成到Prometheus、Datadog等系统 error_logger:warning_msg("ALERT: ~s", [Msg]).8.4 日志与审计
记录监控会话用于后续分析:
%% 启用监控日志 observer_cli:start(#{ log_file => "/var/log/observer_cli.log", audit => true }).9. 与其他监控工具的协同使用
observer_cli 不是要替代现有监控工具,而是作为 Erlang 专项监控的补充:
9.1 与 recon 配合使用
recon 提供了更详细的进程内省能力:
%% 先用observer_cli发现可疑进程 %% 再用recon进行深入分析 {ok, Pid} = my_server:start_link(), %% observer_cli 发现进程内存异常 %% 使用recon进一步分析 recon:info(Pid).9.2 与系统监控工具集成
将关键指标导出到 Prometheus:
%% 自定义指标导出 export_metrics() -> SysInfo = observer_cli:system_info(), prometheus_gauge:set(erlang_memory_total, [], maps:get(memory_total, SysInfo)), prometheus_gauge:set(erlang_process_count, [], maps:get(process_count, SysInfo)).observer_cli 真正价值在于它让 Erlang/OTP 的运行时状态变得透明可见。通过本文的实战指南,你应该能够熟练使用这个工具来监控和调试你的 Erlang 应用。记住,好的监控不是事后排查,而是提前发现。将 observer_cli 纳入你的日常开发流程,让系统问题无处遁形。
建议在实际项目中逐步应用这些技巧,从开发环境开始,逐步扩展到测试和生产环境。观察不同负载下的系统表现,建立自己的性能基线,这样才能在问题出现时快速识别异常模式。