
OneUptime 监控告警模板引擎用{{variable}}占位符动态生成 Incident 与 Alert【免费下载链接】oneuptimeComplete open-source monitoring and observability platform.项目地址: https://gitcode.com/GitHub_Trending/on/oneuptime当监控条件Criteria命中并自动创建 Incident事件或 Alert告警时如果标题、描述与修复备注Remediation Notes是固定文案排查效率会大打折扣——你需要在几千条告警里逐条打开才能知道哪台机器、哪个接口、什么状态码出了问题。OneUptime 提供了一套与监控 Criteria 中 JavaScript 表达式同源的{{variable}}占位符模板语法在事件/告警自动创建时动态填充真实的监控数据响应码、响应时间、CPU 使用率、SSL 证书信息、OID 值等。读完本文你将掌握每种监控类型可用的模板变量、基础与进阶模板写法含{{#each}}数组循环、以及模板底层的存储映射与渲染实现原理。模板语法概览OneUptime 的模板语法复用了监控 Criteria 中 JavaScript 表达式所使用的{{variable}}占位符标记。在监控条件实例Criteria Instance内的Incident/Alerta 表单中你可以把标题Title、描述Description和修复备注Remediation Notes写成模板字符串当条件触发并自动创建事件/告警时系统会以该次监控的真实结果填充占位符。从源码结构看这一能力由 Common/Server/Utils/Monitor/MonitorTemplateUtil.ts 的processTemplateString承载其内部调用 Common/Server/Utils/VM/VMAPI.ts 的replaceValueInPlace完成替换buildTemplateStorageMap根据监控类型把监控响应数据组装成一个扁平/嵌套的存储映射Storage MapJSON 对象例如 API 监控得到responseBody、responseStatusCode、responseTimeInMs等键processTemplateString把模板字符串与存储映射交给VMUtil.replaceValueInPlace用正则/{{(.*?)}}/g逐个匹配占位符通过deepFind按点分路径取值并回填。Incident 侧的实际调用位于 Common/Server/Utils/Monitor/MonitorIncident.ts它先buildTemplateStorageMap生成存储映射再对criteriaIncident.title、criteriaIncident.description执行processTemplateString最后由SeriesContextEnricher补充序列标签信息后落库。Alert 侧的渲染逻辑与 Incident 完全对齐源码注释明确指出二者必须一致否则同一告警会因触发对象不同而呈现出不同描述。支持模板的监控类型与变量全集以下监控类型支持动态模板各自暴露不同的变量集合网站与 API 监控响应数据、响应头、状态码、响应时间请求入站Incoming Request监控请求数据、请求头、请求方法、时间Ping 监控连通性状态、响应时间、失败原因端口监控端口连通性、响应时间、超时状态IP 监控IP 可达性、Ping 时间、失败信息SSL 证书监控证书详情、校验状态、过期信息服务器/VM 监控系统指标CPU、内存、磁盘、进程、主机名合成Synthetic监控脚本执行结果、截图、浏览器详情自定义 JavaScript 代码监控执行结果、耗时、错误消息SNMP 监控设备状态、响应时间、OID 值注意日志Logs、链路Traces与指标Metrics监控目前不支持Incident/Alert 模板因为它们使用不同的触发机制。网站与 API 监控变量变量说明类型responseBody响应体对象HTML/XML 时为字符串JSON 时为 JSON 对象string或JSONresponseHeaders响应头对象键为小写DictionarystringresponseStatusCode响应的 HTTP 状态码numberresponseTimeInMs响应时间毫秒numberisOnline监控是否被认为在线boolean对应的存储映射构建逻辑见 MonitorTemplateUtil.ts其中responseBody会优先尝试JSON.parse解析为对象解析失败则保留原始字符串responseStatusCode取自responseCode字段。请求入站Incoming Request监控变量变量说明类型requestBody请求体对象string或JSONrequestHeaders请求头对象键为小写DictionarystringrequestMethod入站请求的 HTTP 方法GET、POST 等stringincomingRequestReceivedAt收到入站请求的日期时间Date按负载字段分组Group incidents and alerts by a payload field当 Criteria 启用了按负载字段分组的选项后提取出的分组键也会作为模板变量可用变量名取自分组路径的最后一个段。例如按requestBody.alerts[*].labels.alertname分组你会得到{{alertname}}按requestBody.alerts[*].fingerprint分组会得到{{fingerprint}}。完整requestBody仍然可用。注意[*]只在分组路径字段本身中可理解——这里不会解析它因此该标记会被原样打印含花括号。在标题或描述中{{requestBody.alerts[0].annotations.summary}}始终读取负载中的第一条告警而非为它创建 Incident 的那一条。请改用分组变量以及负载的共享字段commonLabels、commonAnnotations。可参考 入站请求监控文档。Ping 监控变量变量说明类型isOnlinePing 目标是否被认为在线booleanresponseTimeInMsPing 响应时间毫秒numberfailureCausePing 失败的原因stringisTimeoutPing 请求是否超时boolean端口监控变量变量说明类型isOnline端口是否在线/可访问booleanresponseTimeInMs连接响应时间毫秒numberfailureCause端口检查失败的原因stringisTimeout端口连接是否超时booleanIP 监控变量变量说明类型isOnlineIP 地址是否被认为在线booleanresponseTimeInMsPing 响应时间毫秒numberfailureCauseIP 检查失败的原因stringisTimeoutIP Ping 请求是否超时booleanSSL 证书监控变量变量说明类型isOnlineSSL 证书校验是否成功booleanisSelfSigned证书是否自签名booleancreatedAt证书创建日期DateexpiresAt证书过期日期DatecommonName证书通用名CNstringorganizationalUnit组织单位OUstringorganization组织Ostringlocality地点Lstringstate州/省STstringcountry国家CstringserialNumber证书序列号stringfingerprint证书 SHA-1 指纹stringfingerprint256证书 SHA-256 指纹stringfailureCauseSSL 校验失败的原因string这些字段直接取自探针返回的sslResponse对象见 MonitorTemplateUtil.ts。服务器/VM 监控变量变量说明类型hostname被监控服务器的主机名stringrequestReceivedAt收到服务器监控请求的日期时间DatecpuUsagePercentCPU 使用率百分比numbercpuCoresCPU 核心数numbermemoryUsagePercent内存使用率百分比numbermemoryFreePercent空闲内存百分比numbermemoryTotalBytes总内存字节numberdiskMetrics所有挂载磁盘的磁盘指标数组ArrayObjectdiskMetrics[].diskPath磁盘挂载点路径stringdiskMetrics[].usagePercent该挂载点磁盘使用率numberdiskMetrics[].freePercent该挂载点磁盘空闲率numberdiskMetrics[].totalBytes该挂载点磁盘总空间字节numberprocesses服务器上运行的进程数组ArrayObjectprocesses[].pid进程 IDnumberprocesses[].name进程名称stringprocesses[].command启动进程的命令stringfailureCause服务器检查失败的原因stringCPU 与内存指标在basicInfrastructureMetrics.cpuMetrics、basicInfrastructureMetrics.memoryMetrics可用时才会被注入存储映射见 MonitorTemplateUtil.ts磁盘与进程数组同理。合成Synthetic监控变量合成监控会在多个浏览器Chromium、Firefox、Webkit与多种屏幕尺寸移动端、平板、桌面上运行同一个脚本每种组合产生一条响应。每次执行通过syntheticResponses数组暴露你可以用索引访问某次特定执行{{syntheticResponses[0].browserType}}或用{{#each syntheticResponses}}迭代。变量说明类型failureCause合成检查失败的原因stringsyntheticResponses每条浏览器/屏幕尺寸组合一条执行记录的数组ArrayObjectsyntheticResponses[].executionTimeInMs本次执行耗时毫秒numbersyntheticResponses[].result本次执行返回的结果string、number、boolean或JSONsyntheticResponses[].scriptError本次执行中发生的错误stringsyntheticResponses[].logMessages本次执行产生的日志消息ArraystringsyntheticResponses[].screenshots本次执行中截取的截图ObjectsyntheticResponses[].browserType本次执行使用的浏览器stringsyntheticResponses[].screenSizeType本次执行使用的屏幕尺寸string自定义 JavaScript 代码监控变量变量说明类型executionTimeInMs自定义代码执行耗时毫秒numberresult自定义代码返回的结果string、number、boolean或JSONscriptError代码执行中发生的错误stringlogMessages执行过程中产生的日志消息数组ArraystringSNMP 监控变量变量说明类型isOnlineSNMP 设备是否在线并响应booleanresponseTimeInMsSNMP 查询响应时间毫秒numberfailureCauseSNMP 查询失败的原因stringisTimeoutSNMP 查询是否超时booleanoidResponsesOID 响应对象数组含 oid、name、value、typeArrayObjectoidResponses[].oid被查询的 OIDstringoidResponses[].nameOID 的描述性名称若提供stringoidResponses[].valueOID 返回的值string或numberoidResponses[].type值的 SNMP 数据类型string{{OID_NAME}}按名称直接访问 OID 值如{{sysUpTime}}string或number基础用法在监控条件实例的Incident/Alert 表单中你可以直接书写模板字符串。例如API devolvió {{responseStatusCode}} en {{responseTimeInMs}}ms如果监控响应状态码为502、耗时为842则存储的标题会变成API devolvió 502 en 842ms嵌套 JSON 访问与 JavaScript 表达式一致ID del problema: {{responseBody.error.id}} Mensaje: {{responseBody.error.message}}数组索引同样受支持Primer usuario: {{responseBody.users[0].name}}未解析占位符的兜底行为如果某条路径不存在占位符会原样保留在输出中——{{responseBody.error.id}}会带着花括号逐字出现在 Incident 标题里。唯一例外是{{#each}}块若其目标路径不存在整个块会被删除。这与底层实现一致VMUtil.replaceValueInPlace在deepFind找不到变量时直接continue跳过替换见 VMAPI.ts这是刻意的“静默失败”设计避免因一个字段缺失而丢掉整条告警。进阶用法数组元素访问Uso del primer disco: {{diskMetrics[0].usagePercent}}% Último proceso: {{processes[-1].name}}注意索引-1并不是传统“从尾部数”的语义——底层deepFind在解析[、]之间的内容时只有字面量last会映射到数组最后一个元素见 VMAPI.ts。数组越界或目标不是数组时返回undefined占位符保持原样。嵌套对象访问Mensaje de error: {{responseBody.error.details.message}} Ubicación del servidor: {{sslCertificate.locality}} {{sslCertificate.country}}deepFind会把路径按.切分逐级下钻每个段先剥离[索引]后缀再取键空段如路径中出现连续两个点会直接返回undefined。使用{{#each}}迭代数组你可以用块语法{{#each path}}...{{/each}}遍历数组适合把列表中的每一项都写进 Incident/Alert 描述。语法{{#each arrayPath}} ...cuerpo usando {{property}} de cada elemento... {{/each}}在循环体内{{propertyName}}相对于当前数组元素解析{{nested.property}}点分访问作用于当前元素{{index}}解析为当前迭代的 0 基索引{{this}}解析为当前元素的值对字符串/数字等原始类型数组很有用在当前元素中找不到的变量会回退到父级存储映射查找这些行为与 VMAPI.ts 中expandEachLoops的实现一一对应循环体以当前元素属性合并父级存储映射构造作用域后再递归展开{{index}}被替换为数字下标原始类型数组则替换{{this}}若解析路径不是数组整个块被替换为空字符串每个循环最多迭代 100 次作为防死循环的安全上限。示例带告警数组的入站请求如 Grafana webhook假设入站请求体如下{ status: firing, alerts: [ { status: firing, labels: { label: Coralpay } }, { status: firing, labels: { label: capitecpay } }, { status: resolved, labels: { label: capricorn } } ] }可以写这样的模板Etiquetas de alerta: {{#each requestBody.alerts}} - {{labels.label}} ({{status}}) {{/each}}渲染结果Etiquetas de alerta: - Coralpay (firing) - capitecpay (firing) - capricorn (resolved)示例服务器磁盘指标Uso del disco: {{#each diskMetrics}} - {{diskPath}}: {{usagePercent}}% usado {{/each}}示例使用{{index}}Procesos: {{#each processes}} {{index}}. {{name}} (PID: {{pid}}) {{/each}}示例原始类型数组配合{{this}}Mensajes de registro: {{#each logMessages}} - {{this}} {{/each}}示例嵌套循环多级数组可以嵌套{{#each}}块{{#each requestBody.groups}} Grupo: {{name}} {{#each members}} - {{id}}: {{role}} {{/each}} {{/each}}注意如果路径未解析为数组整个{{#each}}...{{/each}}块会从输出中删除空数组不会为块产生任何输出。模板表达式的分类解析#each开标签、/each闭标签、index、this与其他引用在 Common/Types/Workflow/TemplateSyntax.ts 中有完整的枚举与解析实现可作为理解该语法的权威参考。各监控类型实战示例网站/API 监控 Incident 标题Alta latencia: {{responseTimeInMs}}ms ( umbral)网站/API 监控 Incident 描述### Error de API Estado: **{{responseStatusCode}}** Latencia: **{{responseTimeInMs}}ms** Fragmento del cuerpo: {{responseBody.error.message}}入站请求 Alert 标题Solicitud entrante defectuosa: method{{requestMethod}} auth{{requestHeaders.authorization}}SSL 证书 Alert 标题Certificado SSL a punto de expirar: {{commonName}} caduca {{expiresAt}}服务器监控 Alert 描述### Alerta del servidor: {{hostname}} Uso de CPU: **{{cpuUsagePercent}}%** Uso de memoria: **{{memoryUsagePercent}}%** Uso del primer disco: **{{diskMetrics[0].usagePercent}}%** Última verificación: {{requestReceivedAt}}Ping 监控 Alert 标题Ping fallido para el destino: {{failureCause}} ({{responseTimeInMs}}ms)端口监控 Alert 描述Problema de conectividad del puerto Estado del puerto de destino: {{isOnline}} Tiempo de respuesta: {{responseTimeInMs}}ms Causa del fallo: {{failureCause}}合成监控 Alert按索引访问特定浏览器/屏幕尺寸组合的执行Primera ejecución: {{syntheticResponses[0].browserType}} / {{syntheticResponses[0].screenSizeType}} Resultado: {{syntheticResponses[0].result}} en {{syntheticResponses[0].executionTimeInMs}}ms用{{#each}}迭代每种浏览器/屏幕尺寸组合### Resultados del monitor sintético {{#each syntheticResponses}} - **{{browserType}} / {{screenSizeType}}**: {{result}} en {{executionTimeInMs}}ms - Error del script: {{scriptError}} - Primer registro: {{logMessages[0]}} {{/each}}自定义代码监控 AlertEjecución del código personalizado: {{executionTimeInMs}}ms Salida del registro: {{logMessages[0]}}SNMP 监控 Alert 标题Dispositivo SNMP fuera de línea: {{failureCause}} ({{responseTimeInMs}}ms)SNMP 监控 Alert 描述### Alerta del dispositivo SNMP Estado: **{{isOnline}}** Tiempo de respuesta: **{{responseTimeInMs}}ms** Tiempo de actividad del sistema: {{sysUpTime}} Nombre del sistema: {{sysName}} Valor del primer OID: {{oidResponses[0].value}}入站请求 数组循环Grafana webhook标题[{{requestBody.status}}] {{requestBody.receiver}}描述### Alertas de {{requestBody.receiver}} {{#each requestBody.alerts}} **Alerta {{index}}**: {{labels.alertname}} - Etiqueta: {{labels.label}} - Estado: {{status}} - Valores: {{valueString}} - Fuente: {{generatorURL}} {{/each}}服务器监控 磁盘循环描述### Alerta del servidor: {{hostname}} Uso de CPU: **{{cpuUsagePercent}}%** Uso de memoria: **{{memoryUsagePercent}}%** **Uso del disco:** {{#each diskMetrics}} - {{diskPath}}: {{usagePercent}}% usado ({{freePercent}}% libre) {{/each}} **Procesos en ejecución:** {{#each processes}} - [{{pid}}] {{name}}: {{command}} {{/each}}SNMP 监控 OID 循环描述### Estado del dispositivo SNMP En línea: {{isOnline}} Respuesta: {{responseTimeInMs}}ms **Valores OID:** {{#each oidResponses}} - {{name}} ({{oid}}): {{value}} {{/each}}模板引擎的底层原理理解模板引擎的实现细节有助于写出稳定、可预期的模板存储映射构建数据侧MonitorTemplateUtil.buildTemplateStorageMap按监控类型MonitorType.API、MonitorType.Website、MonitorType.IncomingRequest、MonitorType.Ping、MonitorType.IP、MonitorType.Port、MonitorType.SSLCertificate、MonitorType.Server、MonitorType.Synthetic、MonitorType.CustomCode、MonitorType.Snmp分别从ProbeMonitorResponse、IncomingMonitorRequest、ServerMonitorResponse、SslMonitorResponse、SyntheticMonitorResponse、CustomCodeMonitorResponse、SnmpMonitorResponse等类型中抽取字段。这也是文档中“不同监控类型暴露不同变量”的根本原因。模板渲染字符串侧processTemplateString调用VMUtil.replaceValueInPlace(storageMap, value, false)。渲染分两阶段先调用expandEachLoops展开所有{{#each}}循环嵌套循环按作用域递归再对剩余的{{variable}}用deepFind在存储映射中逐条查找替换。未找到的变量被跳过、保留原文。JSON 值序列化当解析出的值是对象时会被JSON.stringify缩进 2 空格后插入替换使用函数形式而非字符串形式避免值中$、$1等字符被误当作正则替换模式。Incident/Alert 创建时的调用链MonitorIncident.ts 在创建 Incident 时构建存储映射并对标题/描述做模板渲染Alert 侧逻辑与之镜像对齐保证同一阈值触发事件与告警时文案口径一致。常见陷阱与最佳实践路径不存在时占位符原样保留{{responseBody.error.id}}会在标题中带花括号出现。若希望整段文案消失应使用{{#each}}块包裹仅当路径解析为数组时生效。[*]不会被解析它只在“按负载字段分组”的路径字段中有意义模板正文里请用[0]、[1]这类具体索引。入站请求数组请优先用分组变量{{requestBody.alerts[0].annotations.summary}}永远读第一条告警而非触发 Incident 的那一条正确做法是使用分组键变量与commonLabels、commonAnnotations等共享字段。循环体中优先相对路径{{labels.label}}先相对于当前数组元素解析找不到再回退父级存储映射——这既是便利也是隐患注意字段名冲突。明确不支持的监控类型日志、链路、指标监控目前不参与模板渲染请勿在其 Criteria 中依赖本机制。充分利用代码内核对变量名遇到不确定的字段可对照 MonitorTemplateUtil.ts 中buildTemplateStorageMap的实际注入键名如服务器监控只在basicInfrastructureMetrics存在时注入 CPU/内存/磁盘键避免引用永远不会存在的变量。掌握以上语法与实现细节后你可以为每种监控类型定制信息密度恰到好处的 Incident/Alert 文案让值班人员在通知到达的瞬间即可定位故障源而无需逐条打开详情页。【免费下载链接】oneuptimeComplete open-source monitoring and observability platform.项目地址: https://gitcode.com/GitHub_Trending/on/oneuptime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考