OneUptime 入站请求监控器(Incoming Request Monitor)实战指南:心跳检测与外部告警接入
OneUptime 入站请求监控器Incoming Request Monitor实战指南心跳检测与外部告警接入【免费下载链接】oneuptimeComplete open-source monitoring and observability platform.项目地址: https://gitcode.com/GitHub_Trending/on/oneuptimeOneUptime 的**入站请求监控器Incoming Request Monitor**为你的服务提供一个专用 URL其他系统通过 HTTP 请求向该 URL 上报状态。OneUptime 会根据你配置的监控条件Criteria评估每一次请求从而切换监控状态、声明事故Incident并通知值班团队on-call rota。本文基于官方文档并结合仓库源码IncomingRequestCriteria.ts、IncomingRequestIncidentGrouping.ts深入讲解其工作原理帮助你完成心跳Heartbeat检测、后台任务守护以及将 Prometheus Alertmanager、Grafana 等第三方告警系统接入 OneUptime 的完整落地。入站请求监控器覆盖两类截然不同的任务心跳监控Heartbeat Monitoringcron 任务、Worker 或设备按固定计划调用该 URL当心跳停止到达时OneUptime 自动开启事故。接收其他系统的告警Receiving Alerts from Another SystemPrometheus Alertmanager、Grafana 或任何能 POST JSON 的系统把告警推入OneUptime 将每一条告警转化为带值班升级on-call escalation与自动恢复automatic resolution的事故。两者使用同一种监控器类型真正把它们区分开来的是你所配置的监控条件。适用场景一览入站请求监控器为你的服务提供一个唯一的回调 URL可以用于监控 cron 任务与计划任务是否如期执行验证后台 Worker 是否仍在运行监控位于防火墙之后、外部无法主动访问的服务这类场景下主动探测不可行只能依赖服务反向上报接收来自 Prometheus Alertmanager、Grafana 及其他告警系统的告警追踪任何具备 HTTP 能力的系统的存活信号。创建入站请求监控器在 OneUptime 面板中创建步骤非常简单进入 OneUptime 面板的Monitors监控器点击Create Monitor创建监控器选择Incoming Request入站请求作为监控器类型系统为该监控器生成一个Secret Key密钥和一个 URL打开该监控器点击左侧菜单中的Documentation文档复制 URL配置你的服务向该 URL 发送请求按下文所述配置监控条件。请求 URL 与安全模型每个入站请求监控器拥有一个唯一 URL格式如下https://oneuptime.com/heartbeat/YOUR_SECRET_KEY如果你自托管 OneUptime请将https://oneuptime.com替换为你自己的实例地址。请求方式约定支持发送GET或POST请求HEAD请求被接受并按 GET 处理其他 HTTP 方法一律返回 404路径中的 Secret Key 是唯一凭证无需任何 Header 或 Token。警告任何知道该 URL 的人都可以把监控器标记为“健康”因此务必把它当作机密对待。此外你发送的每个 Header 都会被存储在监控器上任何能读取该监控器的人都能看到——不要把 API Key 或 Token 放在 Header 中发送到这个端点。值得特别强调的是响应语义OneUptime 会立即返回一个空的200然后把请求放入队列异步处理。这个响应在任何校验发生之前就已写出因此返回200并不代表请求已被接受——错误的密钥、已删除的监控器、被禁用的监控器同样会返回200。要确认请求确实到达请查看监控器自身的时间线timeline。从数据模型看每一条被处理的入站请求会生成一个IncomingMonitorRequest包含项目 ID、监控器 ID、请求 Header、请求体、请求方法、接收时间等字段并携带onlyCheckForIncomingRequestReceivedAt这类控制标志见 IncomingMonitorRequest.ts用于区分“心跳到达时间检查”与“完整请求内容检查”。发送请求体如果你需要在请求体内部引用字段——例如在事故标题中使用{{requestBody.status}}、在事故分组中使用 JSON 路径、或在 JavaScript Expression 条件中引用——请务必发送Content-Type: application/json本文档通篇都假定使用该格式。application/x-www-form-urlencoded类型的请求体也会被解析但只会解析为扁平的顶层字段其他任何 Content-Type或不发送 Content-Type都不会被解析所有对requestBody的引用都会解析为空值请求体最大支持50 MB不要使用Content-Encoding: gzip压缩请求体——压缩后的内容会被原样存储而不解析其中的路径将无法解析。在源码中请求体可能是已解析的 JSON 对象也可能是原始 JSON 字符串IncomingRequestIncidentGrouping.ts 的getRequestBodyObject会同时处理这两种形态字符串会尝试JSON.parse仅当结果是对象且非数组时才返回非 JSON 或非对象的请求体例如纯字符串心跳返回null此时分组逻辑直接跳过no-op。发送心跳使用 curl# 简单 GET 请求 curl https://oneuptime.com/heartbeat/YOUR_SECRET_KEY # 携带自定义请求体的 POST 请求 curl -X POST https://oneuptime.com/heartbeat/YOUR_SECRET_KEY \ -H Content-Type: application/json \ -d {status: healthy, version: 1.2.3}从 cron 任务发送# 添加到 crontab每 5 分钟发送一次心跳 */5 * * * * curl -s https://oneuptime.com/heartbeat/YOUR_SECRET_KEY /dev/null从应用代码发送// Node.js 示例 const https require(https); https.get(https://oneuptime.com/heartbeat/YOUR_SECRET_KEY);# Python 示例 import requests requests.get(https://oneuptime.com/heartbeat/YOUR_SECRET_KEY)监控条件Criteria你可以配置监控条件决定你的服务何时被视为在线online、降级degraded或离线offline。每个条件过滤器criteria filter包含三个要素Filter Type过滤类型——查看什么Filter Condition过滤条件——如何比较Value值。开箱即用的默认条件新建的入站请求监控器会自带两条基于请求体的默认条件条件Filter TypeFilter ConditionValue作用OfflineRequest BodyContainserror标记监控器离线并开启事故OnlineRequest BodyNot Containserror标记监控器在线这适用于最常见的场景——发送方在自己的负载payload中上报健康状态请求体提到error就让监控器宕机下一次不含error的请求再把它拉回在线。完全无请求体的请求被视为“不含error”因此一条普通的心跳 ping 就能让监控器保持在线。你可以把 Value 改成发送方实际会发送的内容如status:firing、FAILED等匹配是区分大小写的子串测试。请注意这套默认条件不是死闸开关dead-mans switch——请求停止到达时没有任何条件会触发。如果你希望“静默”也能触发告警需要按下文说明额外添加一条Incoming Request / Not Recieved In Minutes条件。可用的 Filter TypesFilter Type检查内容说明Incoming Request是否在某个时间窗口内收到过请求唯一一种在“没有任何请求到达”时也能触发的检查Request Body请求体子串匹配。对象型请求体会被按紧凑 JSONcompact JSON比较Request Header请求 Header 的名称与 Header 名称精确匹配比较前转小写Request Header Value请求 Header 的值与 Header 值精确匹配比较前转小写JavaScript Expression针对requestBody与requestHeaders的任意表达式最灵活的选项参见 JavaScript Expressions以上 Filter Type 在源码中对应CheckOn枚举的IncomingRequest、RequestBody、RequestHeader、RequestHeaderValue、JavaScriptExpression取值见 CriteriaFilter.ts。Filter Conditions每种 Filter Type 提供自己的一组条件。Incoming Request以下按面板中的拼写还原注意Recieved为面板原文拼写Recieved In Minutes—— 在指定的分钟数内收到过请求Not Recieved In Minutes—— 在指定的分钟数内没有收到任何请求。Request Body、Request Header、Request Header Value提供Contains与Not Contains。JavaScript Expression提供Evaluates To True。注意Header 名称与 Header 值在比较前都会转成小写且匹配针对的是完整名称或完整值不是子串。要写content-type而不是Content-Type写application/json而不是application/JSON。只有Request Body才是真正的子串匹配。对象型请求体按去除空格的紧凑 JSON比较所以Request Body / Contains过滤器必须写成status:firing——从格式化pretty-printed的负载中直接复制status: firing永远无法匹配。条件示例10 分钟内没有心跳即标记离线死闸开关Filter TypeIncoming RequestFilter ConditionNot Recieved In MinutesValue10根据请求体内容标记降级Filter TypeRequest BodyFilter ConditionContainsValuestatus:degraded两个关键行为警告警告 1只有至少一个条件检查Incoming Request时监控器才会在后台被重新评估。如果监控器的条件只检查 Request Body、Request Header 或 JavaScript Expression它只会在请求到达时被评估其他时间一概不评估——因此它永远不会自行转为离线。如果你需要“心跳缺失”告警就必须配置一条Incoming Request条件。警告 2从未收到过请求的监控器会被当作“最后一次请求时间等于创建时间”来处理。也就是说一条 Not Recieved In Minutes: 10 条件在新建监控器上会在创建 10 分钟后触发——即使发送方从未接通过。上述逻辑在源码中有清晰印证IncomingRequestCriteria.ts 中RecievedInMinutes通过比较incomingRequestReceivedAt与当前检查时间的差值是否小于等于阈值来判定NotRecievedInMinutes则是差值大于阈值即命中并返回诸如Incoming request / heartbeat not received in 10 minutes. It was received 12 minutes ago.这类根因描述。请求体匹配则先判断对象类型并JSON.stringify为紧凑 JSON 再做includes子串判断特别地无请求体被归一化为空字符串这使得 Not Contains 过滤器能匹配裸心跳 ping这也是该监控器类型默认“在线”条件的实现基础。Header 名称与值则统一toLowerCase()后再做精确包含判断——注意这里刻意将过滤器输入值也转小写否则按惯例写成X-Api-Key的过滤器将永远无法与已被小写化的观察值匹配。接收其他系统的告警Alertmanager、Grafana 及类似工具会 POST 一个描述一条或多条告警的 JSON 文档。默认情况下一条条件只开启一个事故——因此携带五条告警的负载只会产生一个事故。事故分组Incident Grouping可以改变这一点它从负载中提取一个值并为每个不同的值分别开启一个事故这些事故可以同时处于打开状态。开启事故分组打开条件展开Settings设置启用Group incidents and alerts by a payload field按负载字段分组事故与告警会出现四个字段字段示例作用Open a separate incident for each…requestBody.alerts[*].labels.alertname该路径的不同取值将事故拆分为多个独立事故Field that signals recoveryrequestBody.alerts[*].status用于判断某条告警是否已恢复的路径Value that means recoveredresolved标记恢复的精确值Max incidents per request100默认安全上限防止高基数high-cardinality字段无节制地开启事故路径语法路径必须以字面前缀requestBody.开头。没有该前缀的路径——如alerts[*].labels.alertname——静默地不匹配任何内容。{{ }}包裹层是可选的requestBody.status与{{requestBody.status}}行为完全一致。[*]对数组展开——每个不同取值一个事故。两个元素产生相同取值时会合并为单个事故该事故的 firing/resolved 状态取自第一个匹配元素。路径中只有第一个[*]是通配符requestBody.groups[*].alerts[*].name不会匹配任何内容。[0]与[last]选择单个元素可以放在[*]之后。对象与数组值、空字符串、null 会被跳过0和false是合法键。源码层面的对应实现IncomingRequestIncidentGrouping.tsnormalizePath会剥掉可选的{{ }}并要求requestBody.前缀否则返回空串不匹配extractItems定位第一个[*]把前缀路径与元素后缀路径分开逐元素调用deepFind提取键值每个键通过MetricSeriesFingerprint.computeFingerprint计算指纹用于去重同一次负载中相同键只保留首次出现并按maxKeysPerPayload默认 100截断超出部分会被记录 warning 日志后忽略分组键的标签名由路径最后一个命名段派生alerts[*].labels.alertname→alertname这就是模板变量命名的来源。恢复是事件驱动的Webhook 只描述当前这份负载里的内容因此 OneUptime绝不会因为某个键不再出现而自动解决resolve事故。事故只会在负载明确声明该键已恢复时才被解决。必须同时满足两件事Field that signals recovery与Value that means recovered已设置且与负载匹配。比较是精确且区分大小写的——Resolved不匹配resolved。该条件的事故开启了Auto Resolve Incident自动解决事故该选项位于事故表单的Advanced Options高级选项下。未开启时匹配的恢复事件会被忽略事故保持打开。告警同理对应Auto Resolve Alert。此外Max incidents per request 限制的是“提取”而不只是“创建”。超出上限的键对“恢复”同样不可见——因此在一个包含的键数超过上限的负载里超出部分即使上报了resolved也不会关闭其对应的事故。警告如果Field that signals recovery含[*]而Open a separate incident for each…不含则永远不会有任何事故被解决。要么两个都使用[*]要么都不用。不含[*]的恢复路径会对整个负载求值因此负载级别的status: resolved会解决该负载中的所有键——包括自身状态仍是 firing 的告警。源码实现与文档完全对应isResolvedForScope在恢复路径含[*]且处于元素上下文即分组路径也含[*]时从单个数组元素上读取恢复字段否则退化为对整个负载求值。恢复判定要求提取出的值与resolvedWhenValue精确相等。而collectFiringMatches会过滤掉isResolved的事件已恢复的事件永远不会开启新事故collectResolvedFingerprints则专门收集负载中明确标记为已恢复的指纹供事故解决流程使用——这正是“事件驱动恢复”的完整实现链路。为事故命名分组键会以路径最后一个段的名字作为变量暴露给事故与告警模板路径变量requestBody.alerts[*].labels.alertname{{alertname}}requestBody.alerts[*].fingerprint{{fingerprint}}requestBody.commonLabels.severity{{severity}}完整负载也可以一起使用因此事故标题写{{alertname}}、描述引用{{requestBody.commonAnnotations.summary}}都是可行的。更多内容参见 事故与告警动态模板。警告变量名是 OneUptime 用于把恢复事件与打开中的事故进行匹配的**身份标识identity**的一部分。把分组路径改为最后一个段不同的路径会让当前在旧路径下打开的所有事故变成“孤儿”——它们将无法再被自动解决只能手动关闭。还需注意[*]只在两个分组路径字段中生效。在其他任何地方它都不会被解析而未解析的占位符会被原样打印而不是留空——例如标题写成{{requestBody.alerts[*].labels.alertname}}会带着花括号原样渲染。标题写成{{requestBody.alerts[0].annotations.summary}}虽然能解析但它永远读取的是负载中的第一条告警而不是为当前这个事故开出的那条。推荐使用分组变量再配合负载中共用的commonAnnotations字段。完整示例要查看 Alertmanager 的完整接入配置参见 Prometheus AlertmanagerGrafana 的接入参见 Grafana。最佳实践合理设置时间窗口——如果 cron 任务每 5 分钟执行一次把 Not Recieved In Minutes 阈值设为 10–15 分钟以容忍偶发延迟。在请求体中携带有意义的数据——在请求体中发送状态信息这样你才能配置细粒度的监控条件。使用 POST Content-Type: application/json——所有读取请求体内部字段的功能都依赖于此。不要把两类任务混在同一个监控器上——接收事件驱动告警的监控器没有固定节奏给它配置 Not Recieved In Minutes 条件会导致状态来回抖动flap。请为死闸开关单独使用一个监控器。监控你的“监控器”——确保发送请求的服务有完善的错误处理避免失败请求被静默忽略。延伸阅读Prometheus Alertmanager——一套完整的入站告警接入配置Grafana——Grafana 告警的同类接入事故与告警动态模板——标题与描述中可用的全部变量JavaScript Expressions——表达式语法与引号规则。【免费下载链接】oneuptimeComplete open-source monitoring and observability platform.项目地址: https://gitcode.com/GitHub_Trending/on/oneuptime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考