prometheus 指令

[!INFO] Git Commit: 5ea0bc3 | Updated: 2026-10-01

本 mod 只注册 1 个指令:/prometheus(别名 /prom),用于在不重启服务器的情况下 启停导出器。本 mod 没有其它指令,也没有权限节点(沿用 Forge 的权限等级体系)。

注册

@Mod.EventHandler
public void onServerStarting(FMLServerStartingEvent event) {
    event.registerServerCommand(new ForgePrometheusCommand());
    this.mc_server = event.getServer();
}

PrometheusExporterMod.java:220-227。在 FMLServerStartingEvent 中注册,即世界载入时。 由于 onPreInitialization 已在客户端侧提前 return(:207), 该指令只在服务端存在。

基本信息

属性 值 来源
指令名 prometheus PrometheusCommand.java:50
别名 prom PrometheusCommand.java:17
权限等级 ExporterConfig.collector.command_permission_level,默认 4 ForgePrometheusCommand.java:125
用法 /prometheus <start|stop|restart> PrometheusCommand.java:25

权限等级直接返回配置值而非硬编码 4,因此把 command_permission_level 调低(如设为 0) 可以让所有玩家重启导出器。范围 0–4 见 配置。

三个子命令

CommandArg 枚举定义三个子命令(PrometheusCommand.java:55-112):

子命令 内部枚举 动作 实现
start START 启动导出器 execStart(ForgePrometheusCommand.java:58-70)
stop STOP 停止导出器 execStop(:77-85)
restart RESTART 先 stop 再 start execRestart(:48-51)

解析用两个静态映射表(PrometheusCommand.java:64-72),均在接口初始化时构建一次:

public static final String[] ARG_VALUES = Arrays.stream(CommandArg.values())
    .map(CommandArg::getValue).toArray(String[]::new);
private static final Map<String, CommandArg> FROM = Arrays.stream(CommandArg.values())
    .collect(Collectors.toMap(CommandArg::getValue, v -> v));

ARG_VALUES 是 public 的(因此有 @SuppressWarnings("ArraysAsListWithZeroOrOneArgument")), 供 tab 补全使用(ForgePrometheusCommand.java:34-41)。

参数校验

if (args.length != 1) throw new WrongUsageException(MSG_USAGE);
try { cmd = CommandArg.from(args[0]); }
catch (IllegalArgumentException e) { throw new WrongUsageException(MSG_USAGE); }

ForgePrometheusCommand.java:136-146。必须恰好 1 个参数——多于或少于都报用法错误。 CommandArg.from 对未知值抛 IllegalArgumentException(PrometheusCommand.java:101), 被转成 WrongUsageException。参数大小写敏感:/prometheus START 会失败。

幂等性:不会抛异常

start / stop 各自先检查状态,状态不符时不报错而是发提示消息:

操作 当前状态 行为 消息
start 已停止 startExporter() Prometheus exporter started.
start 已在运行 跳过 Prometheus exporter is already running.
stop 在运行 stopExporter() Prometheus exporter stopped.
stop 已停止 跳过 Prometheus exporter is already stopped.
restart 两者皆可 stop + start 依 stop / start 各自的状态决定

ForgePrometheusCommand.java:58-85。这与 mod 公开 API 的行为不同—— PrometheusExporterMod.startExporter() / stopExporter() 在状态不符时 会抛 IllegalStateException(PrometheusExporterMod.java:267-269、:287-289)。 指令层做了一层保护,使得 restart 在任何状态下都安全。

restart 无独立消息,实现为先调 execStop 再调 execStart(:49-50); 由于两者的「状态不符则跳过」逻辑,若导出器本就未运行,restart 只等同于 start。

两类消息,两种发送方式

方法 实现 接收者 用到的消息
sendAdminMessage func_152373_a(sender, this, msgFormat, msgParams) 命令来源方(OP) started / stopped 两条成功消息
sendChatMessage sender.addChatMessage(new ChatComponentTranslation(...)) 命令来源方 already running / already stopped 两条状态提示

ForgePrometheusCommand.java:162-175。两者都用 ChatComponentTranslation, 但成功消息走 func_152373_a(1.7.10 中 CommandBase 的「发给管理员」通道,展开为带 commands.generic.usage 风格的输出),状态提示直接 addChatMessage。

sendChatMessage 声明为 private 却带 Object... msgParams 变参,而两处调用点 (:68、:83)都不传变参——变参在本 mod 中从未被使用。

消息不可本地化

全部 5 条消息都是 PrometheusCommand 接口中的硬编码英文字符串常量 (PrometheusCommand.java:25-50)。这源于一条失效的 TODO(:19-20):

// TODO: I can't get ChatComponentTranslation to work with strings defined in
// "assets/prometheus_exporter/lang/en_us.json".

该 TODO 引用的 assets/prometheus_exporter/lang/en_us.json 在仓库中并不存在。 由于消息以 ChatComponentTranslation 构造,Minecraft 会把英文字符串当作翻译键去查 lang 文件;查不到时回退为原字符串,所以英文玩家能看到正常文本,但中文/其它语言环境下 这 5 条消息不会翻译。这是从上游继承的既有问题,GTNH 移植未修复。 本 mod 的资源目录中没有任何语言文件。

端口占用不阻断启动

start 走 startExporter() → initHttpServer(),其中 BindException 被捕获:

try {
    this.http_server = new HTTPServer(address, port, true);
    LOG.info("Listening on {}:{}", address, port);
} catch (BindException e) {
    LOG.error("Failed to start prometheus exporter, port {} already in use.", port);
}

PrometheusExporterMod.java:176-181。端口被占用时只记一条 error 日志,不抛异常、不提示玩家, 且 http_server 保持 null——但 initCollectors() 仍会执行(startExporter 中顺序是先 HTTP 后采集器,:272-275),is_running 仍被置为 true(:277)。

后果:此时执行 /prometheus start 会收到 already running(状态已是 running), 而实际没有可抓取的端点。排查端口冲突必须看服务端日志, 指令反馈在此场景下具有误导性。

new HTTPServer(address, port, true) 的第三个参数为 true,即daemon 模式—— 源码注释(:172-173)说明这是必需的,否则 Minecraft 服务器进程无法正常退出。