查询层与告警

这一篇解决什么
数据存好了,怎么取出来给 UI 看?指标异常了怎么告警?这篇讲查询层(默认 GraphQL,兼容 PromQL/LogQL/TraceQL/Zipkin 四套协议让 Grafana 等直连)和告警系统(规则用 MQE 表达式、滑窗求值、状态机控制触发/静默/恢复、九种 hook 输出)。读完应该能回答:UI 怎么查到数据、Grafana 怎么接 OAP、告警规则怎么写、触发后怎么发钉钉。


查询层解决什么问题

把存储层里已经聚合好的指标/Trace/Log/告警/拓扑等数据,按统一接口暴露给 UI(SkyWalking 自带 Booster UI)和外部可视化工具。默认用 GraphQL,因为它能让前端按需取字段、一次请求组合多种资源、自带 schema 自文档。兼容 PromQL/LogQL/TraceQL/Zipkin 是为了让 Grafana、Tempo、已有 Zipkin 生态不用改接发直连 OAP。


GraphQL · 主查询入口

模块结构

QueryModuleoap-server/server-core/.../query/QueryModule.java:26):根查询模块,名字 "query"services() 返回空数组。它不暴露服务接口,只是一个挂载点,让各种查询协议插件以 ModuleProvider 形式注册进来。

图1

Module/Provider 插槽比喻
QueryModule 想成"插槽",各 provider 想成"插进去的卡"。同一 Module 可以有多个 Provider,但运行时只激活一个(配置 selector 决定)。不过查询层特殊——QueryModule 实际上可以同时启用多个 provider(GraphQL + PromQL + LogQL 各占一个端口),因为它们不冲突。

GraphQL schema 与 resolver

GraphQLQueryProvideroap-server/server-query-plugin/query-graphql-plugin/.../GraphQLQueryProvider.java:69),name() 返回 "graphql":75),module() 返回 QueryModule.class:79)。

GraphQL schema / resolver 速成
.graphqls 文件用 GraphQL SDL(Schema Definition Language)声明"有哪些类型、有哪些查询字段、字段参数是什么"——这是契约。resolver 是 Java 类,同名方法负责"字段被请求时实际去哪取数据"。

extend type Query { ... } 是给根 Query 类型追加字段,多个 .graphqls 各自 extend type Query,最终合并成完整的 Query 类型。这就是为什么几十个查询类型能分散在不同文件里却合成一个端点。

prepare()GraphQLQueryProvider.java:99-166)用 graphql-kickstart-toolsSchemaParser 把一堆 .graphqls 和对应 resolver 注册进去。每条 .file(...).resolvers(...) 就是"把某个 schema 文件挂到某个 Java resolver 类上"。主要查询类型映射:

查询类型 resolver schema 文件
trace TraceQuery / TraceQueryV2 trace.graphqls / trace-v2.graphqls
metrics (v3, MQE) MetricsExpressionQuery metrics-v3.graphqls
topN / aggregation AggregationQuery / TopNRecordsQuery aggregation.graphqls / top-n-records.graphqls
log LogQuery / LogTestQuery / OndemandLogQuery log.graphqls / ondemand-pod-log.graphqls
alarm AlarmQuery alarm.graphqls
event EventQuery event.graphqls
topology TopologyQuery topology.graphqls
hierarchy HierarchyQuery hierarchy.graphqls
profile ProfileQuery / ProfileMutation profile.graphqls / ebpf-profiling.graphqls
metadata MetadataQuery / MetadataQueryV2 metadata.graphqls / metadata-v2.graphqls

GraphQL 执行链路

图2

GraphQLQueryHandleroap-server/server-query-plugin/query-graphql-plugin/.../GraphQLQueryHandler.java:41):

  • 构造时用 Armeria 的 GraphqlService.builder().schema(schema).instrumentation(new MaxQueryComplexityInstrumentation(allowedComplexity, ...)).build():74-85)。MaxQueryComplexityInstrumentation 防止恶意/超重查询——每个查询有复杂度上限,超过就中止并记日志。
  • @Post("/graphql") + @Blocking:88-89):@Blocking 让 Armeria 把请求放到阻塞线程池(GraphQL 查询涉及存储 IO,不能占事件循环线程)。
  • 方法体包了直方图计时 + 错误计数器(OAP 自身的 self-telemetry)。

resolver 与 CoreModule QueryService 的对接

每个 resolver 持有 ModuleManager,懒加载从 CoreModule 拿对应服务。典型模式(TraceQuery.java:59-64):

private TraceQueryService getQueryService() {
    if (queryService == null) {
        this.queryService = moduleManager.find(CoreModule.NAME).provider().getService(TraceQueryService.class);
    }
    return queryService;
}

各 resolver 与 CoreModule 服务对接:

  • TraceQueryTraceQueryServiceTagAutoCompleteQueryService
  • MetricsQueryMetricsQueryServiceAggregationQueryServiceRecordQueryServiceMetricsMetadataQueryService
  • AlarmQueryAlarmQueryServiceEventQueryServiceTagAutoCompleteQueryService
  • HierarchyQueryHierarchyQueryService

所有 resolver 方法返回 CompletableFuture,通过 AsyncQueryUtils.queryAsync 异步执行;每个方法接受 boolean debug,开启后构造 DebuggingTraceContext 把 OAP 内部查询执行 trace 回填到结果里。

GraphQL 按需取字段的优化
AlarmQuery.getAlarmAlarmQuery.java:124-128)用 DataFetchingEnvironment.getSelectionSet().contains("**/events/**") 判断前端有没有在查询里选了 events 子字段,只有选了才去 EventQueryService 关联事件——这是 GraphQL 按需取字段带来的优化机会,避免每次查告警都连带查事件。


MQE 在查询中的接入

MetricsExpressionQuery.../resolver/MetricsExpressionQuery.java:44)暴露 execExpression(expression, entity, duration, debug, dumpStorageRsp),对应 metrics-v3.graphqls:124execExpression

执行流程(MetricsExpressionQuery.java:58-103):

  1. 构造 DebuggingTraceContextMQEVisitor 实例化(持有 moduleManager/entity/duration)。
  2. MQELexer + MQEParser 解析表达式得到 ParseTree;解析失败返回 ExpressionResultType.UNKNOWN + 错误信息。
  3. visitor.visit(tree) 求值,得到 ExpressionResult
  4. 对结果数值用 DecimalFormat 格式化(setGroupingUsed(false),不加分隔符)。

ExpressionResult 类型有 4 种(metrics-v3.graphqls:45-58):SINGLE_VALUETIME_SERIES_VALUESSORTED_LISTRECORD_LIST,外加 UNKNOWN

MQEExecutor.../mqe/rt/MQEExecutor.java:52)是从 MetricsExpressionQuery 抽出来的同步求值器,给 admin inspect 路径用,复用同一套 MQE 引擎但支持叠加"外部指标"(foreign metrics)元数据。详见 03-分析引擎与四大DSL 的 MQE 节。


其他查询协议

图3

���什么要四套兼容协议
GraphQL 是 SkyWalking 原生 UI 用的,但运维团队往往已有 Grafana(看 PromQL 指标 + Loki 日志 + Tempo Trace)或 Zipkin 体系。这四个插件让 OAP 不必改造既有可视化栈就能接入,降低迁移成本。

PromQL · Grafana 指标直连

  • PromQLModule + PromQLProvider.../promql/PromQLProvider.java:34),requiredModulesTelemetryModule + CoreModule
  • 独立 HTTP 端口start():70-89)自己 new HTTPServeraddHandler(new PromQLApiHandler)
  • 端点完全兼容 Prometheus HTTP API(PromQLApiHandler.java:183-584):/api/v1/metadata/api/v1/labels/api/v1/label/{name}/values/api/v1/series/api/v1/query(瞬时)、/api/v1/query_range(区间)、/api/v1/format_query/api/v1/status/buildinfo
  • 解析侧:PromQLLexer/PromQLParser(ANTLR)+ PromQLExprQueryVisitor。底层调 MetricsQueryService/AggregationQueryService/RecordQueryService/MetricsMetadataQueryService

LogQL · Grafana Loki 日志

  • LogQLModule + LogQLProvider.../logql/LogQLProvider.java:33),独立 HTTP 端口,requiredModulesCoreModule
  • LogQLApiHandler.../handler/LogQLApiHandler.java:71)暴露 Loki 兼容路径:/loki/api/v1/labels:86-88)委托 TagAutoCompleteQueryService.queryTagAutocompleteKeys(TagType.LOG, duration),还有 label values、log range query 等。底层 LogQueryService,解析用 LogQLLexer/LogQLParser + LogQLExprVisitor

TraceQL · Grafana Tempo trace

  • TraceQLModule + TraceQLProvider.../traceql/TraceQLProvider.java:34),独立 HTTP server。
  • 根据配置开关注册两个 handler(:85-101):
    • config.isEnableDatasourceZipkin()ZipkinTraceQLApiHandler/zipkin 前缀(Tempo 的 Zipkin 兼容数据源 API)。
    • config.isEnableDatasourceSkywalking()SkyWalkingTraceQLApiHandler/skywalking 前缀。
  • 解析侧:TraceQLQueryParser + TraceQLQueryVisitor,有 OTLPConversion/SkyWalkingOTLPConversion/ZipkinOTLPConversion 做 OTLP trace 格式与各后端格式互转。

TraceQL 是什么
TraceQL 是 Grafana Tempo 的 trace 查询语法。OAP 实现它让 Grafana Tempo 数据源能直连 OAP 查 trace。

Zipkin · Zipkin API 兼容

  • ZipkinQueryModule + ZipkinQueryProvider.../zipkin/ZipkinQueryProvider.java:34),独立 HTTP 端口,requiredModulesTelemetryModule + CoreModule
  • ZipkinQueryHandler 暴露 Zipkin v2 API(GET):/config.json/api/v2/services/api/v2/remoteServices/api/v2/spans/api/v2/trace/{traceId}/api/v2/traces/api/v2/traceMany/api/v2/autocompleteKeys/api/v2/autocompleteValues。底层 ZipkinQueryService

告警系统

模块结构

AlarmModuleoap-server/server-core/.../alarm/AlarmModule.java:28),名字 "alarm",暴露 4 个 Service(:36-43):

Service 职责
MetricsNotify 流处理链调用,把指标喂给告警引擎
AlarmRulesWatcherService 规则热更新接口
AlarmStatusWatcherService 告警状态观察
AlarmKernelService 运行时规则重置

AlarmModuleProvideroap-server/server-alarm-plugin/.../AlarmModuleProvider.java:39),requiredModulesCoreModule + ConfigurationModule(告警规则支持配置中心动态更新)。

启动流程:

  • prepare():61-69):创建 AlarmRulesWatcher(持空 Rules)、NotifyHandler,注册 4 个服务实现。
  • start():72-78):从 ConfigurationModuleDynamicConfigurationService,把 AlarmRulesWatcher 注册为配置变更监听器(告警规则可热更新)。
  • notifyAfterCompleted():81-92):从 classpath 读 alarm-settings.yml,用 RulesReader 解析成 RulesalarmRulesWatcher.initConfig(rules) 注入;然后 notifyHandler.init(new AlarmStandardPersistence(getManager()))

告警规则格式

真实示例(oap-server/server-alarm-plugin/src/test/resources/alarm-settings.yml):

rules:
  endpoint_percent_rule:
    expression: sum(endpoint_percent < 75) >= 3
    period: 10
    silence-period: 10
    message: Successful rate of endpoint {name} is lower than 75%

  service_percent_rule:
    expression: sum(service_percent < 85) >= 4
    include-names:
      - service_a
      - service_b
    exclude-names:
      - service_c
    period: 10

  comp1_rule:    # 复合规则:成功率在 (60,75) 之间
    expression: sum((endpoint_percent > 60) * (endpoint_percent < 75)) >= 3
    period: 10
    silence-period: 10
    message: xxxxx

hooks:
  webhook:
    default:
      is-default: true
      urls:
        - http://127.0.0.1/notify/
  slack:
    default:
      is-default: true
      text-template: '{"type":"section","text":{"type":"mrkdwn","text":":alarm_clock: *Apache Skywalking Alarm* \\
 **%s**."}}'
      webhooks:
        - https://hooks.slack.com/services/x/y/zssss
  dingtalk:
    default:
      is-default: true
      text-template: '{"msgtype":"text","text":{"content":"Apache SkyWalking Alarm: \\
 %s."}}'
      webhooks:
        - url: https://oapi.dingtalk.com/robot/send?access_token=dummy_token
          secret: dummysecret

字段含义(AlarmRule.java + RulesReader.java:88-132):

字段 含义 默认值
expression MQE 表达式,必须是布尔单值结果(根运算必须是比较) 必填
period 评估窗口长度(分钟),滑窗保留多少个桶 1
silence-period 触发后静默多少次检查不再重复告警 = period
recovery-observation-period 恢复观察期(分钟) 0
message 告警文案模板,{name} 会被实体名替换 “Alarm caused by Rule {ruleName}”
include-names / exclude-names 实体名白/黑名单
include-names-regex / exclude-names-regex 实体名正则白/黑名单
tags 告警附加标签
hooks 指定使用哪些 hook(格式 {hookType}.{hookName},如 slack.default 空 → 用所有 is-default: true

规则名必须以 _rule 结尾
RulesReader.java:95((String) k).endsWith("_rule") 判断,规则名不以此结尾会被忽略。加载的文件名是 alarm-settings.ymlAlarmModuleProvider.java:84ResourceUtils.read("alarm-settings.yml"))。

规则加载与编译

RulesReaderRulesReader.java:53)用 SnakeYAML 解析,先读 hooks 再读 rules(顺序重要,因为规则的 hooks 字段要校验是否引用了已声明的 hook,checkSpecificHooks:450-455)。

AlarmRule.setExpressionAlarmRule.java:75-109)不是简单赋值,而是编译 + 校验

图4

  1. ANTLR 解析 MQE 表达式。
  2. AlarmMQEVerifyVisitor 遍历,提取 includeMetrics(表达式引用的所有指标名)和 maxTrendRange
  3. 校验三件事:解析无错(:90)、parseResult.isBoolResult() 为真(:93,否则抛 IllegalExpressionException("root operation is not a Compare Operation"))、结果类型 SINGLE_VALUE:97)。
  4. verifyIncludeMetrics:111-120)校验所有指标必须属于同一 scope

AlarmRulesWatcherAlarmRulesWatcher.java:54)继承 ConfigChangeWatcher,监听配置 key "alarm-settings":67)。维护三个核心 map:runningContext(表达式 → List)、alarmRuleRunningRuleMap(AlarmRule → RunningRule,热更新时复用保留历史窗口)、exprMetricsMap(表达式 → Set<指标名>,快速查找某指标命中哪些规则)。

热更新策略
notify(Rules):112-139):如果新规则和旧规则"相等"(AlarmRule@EqualsAndHashCode),复用其 RunningRule 以保留历史指标窗口;否则 new 一个新 RunningRule

流处理链如何触发告警

这是告警与流处理的核心连接点:

图5

  1. 指标落盘后通知告警MetricsStreamProcessor.minutePersistentWorker:317)创建 AlarmNotifyWorker,作为 MetricsPersistentMinWorker 的下游(:320-323)。分钟级指标持久化后调 AlarmNotifyWorker.in(metrics)
  2. AlarmNotifyWorkerAlarmNotifyWorker.java:30):in 若 metrics 实现 WithMetadata,调 entrance.forward(metrics)
  3. AlarmEntranceAlarmEntrance.java:24):forward 先检查 has(AlarmModule.NAME)(没装告警模块跳过),再懒加载 MetricsNotifynotify
  4. NotifyHandler.notifyNotifyHandler.java:118-234):
    • 从 metrics 拿 MetricsMetaInfo(scope、metricsName、id)。
    • 按 scope 构造对应 MetaInAlarm(Service/ServiceInstance/Endpoint 及 Relation),用 IDManager 解析 id 成可读名,通过 MetadataQueryServicelayers
    • 只接受 6 种 catalog scope(service/instance/endpoint 及 relation),其它直接 return。
    • core.findRunningRule(metricsName) 找命中该指标的规则(AlarmCore.findRunningRuleAlarmCore.java:57-68,遍历 exprMetricsMap 反查)。
    • 对每个命中的 RunningRulerule.in(metaInAlarm, metrics)
  5. RunningRule.inRunningRule.java:135-156):
    • 先按 includeMetrics 过滤指标名。
    • 再按 include/exclude names + 正则过滤实体名(validate :162-200)。
    • 为实体构造 AlarmEntitycomputeIfAbsent 拿到/创建 Windowwindow.add 把指标塞进滑窗。

AlarmNotifyWorker 只接分钟级
AlarmNotifyWorker 只接在分钟级 worker 后,不接小时/天。RunningRule 注释:“only minute dimensionality metrics are expected to process”。告警基于分钟级指标。

AlarmCore · 定时检查

AlarmCore.startAlarmCore.java:70-122):单线程定时器,每 10 秒触发一次(:121 scheduleAtFixedRate(..., 10, 10, SECONDS))。每次:

  1. 计算距上次执行的分钟数。
  2. 对每个 RunningRulemoveTo(checkTime) 推进滑窗;且只在每分钟的第 15 秒之后才真正 check():85 checkTime.getSecondOfMinute() > 15),注释说"避免在每分钟头一刻触发误报"——因为分钟级指标可能还没落盘完。
  3. 收集所有 AlarmMessage,分出 firing(AlarmMessage)和 recovery(AlarmRecoveryMessage),遍历所有 AlarmCallback 分别调 doAlarm/doAlarmRecovery:99-110)。

告警状态机

window / 连续 N 次 / 静默 速成

  • window(滑窗)RunningRule.Window 是按分钟为粒度的滑动窗口,长度 = periodwindow.add 把每分钟一个指标值塞进 LinkedListmoveTo 随时间推进丢弃过期分钟、补 null。表达式 sum(endpoint_percent < 75) >= 3 就是在窗口的 N 个分钟值上算:先逐分钟比较 < 75 得 0/1,再 sum,再 >= 3
  • 连续 N 次(period):不是字面"连续",而是"在 period 分钟窗口内满足条件的次数 ≥ N"。period: 10 表示窗口 10 分钟。
  • silence-period(静默期):告警触发后,接下来 silence-period 次检查即使条件仍满足也不再发新告警,避免轰炸。默认 silence-period = periodRulesReader.java:114)。
  • recovery-observation-period(恢复观察期):触发后条件不再满足时,不是立刻发恢复通知,而是观察 recovery-observation-period 分钟确认稳定恢复才发 AlarmRecoveryMessage

状态机 5 个状态(RunningRule.java:258-264):

图6

AlarmStateMachineRunningRule.java:522-639):

  • onMatch:538-565):silenceCountdown--。NORMAL→FIRING;FIRING 且还在静默期→SILENCED_FIRING;SILENCED_FIRING/OBSERVING_RECOVERY/RECOVERED 且静默到期→FIRING,否则→SILENCED_FIRING。
  • onMismatch:567-597):recoveryObservationCountdown-- + silenceCountdown--。FIRING/SILENCED_FIRING 且恢复观察期与静默期都到期→RECOVERED,否则→OBSERVING_RECOVERY。RECOVERED→NORMAL。
  • transitionTo:599-622)进入新状态时重置对应 countdown。

Window.checkAlarm:375-397):

  • isMatch()AlarmMQEVisitor 在当前窗口指标值上求值 MQE 表达式,结果必须是布尔单值(isMatch == 1 视为命中,:469)。
  • 命中 → stateMachine.onMatch();未命中 → stateMachine.onMismatch()
  • 状态机为 FIRING 时构造 AlarmMessagebuildAlarmMessage :399-416,含 ruleName、message、period、tags、hooks、expression、mqeMetricsSnapshot)。
  • 状态机为 RECOVERED 时构造 AlarmRecoveryMessage(包装上次的 lastAlarmMessage)。

多 label 结果
如果表达式结果是 labeled(如 percentile{p='50,75'}),任一 label 命中即整体命中(RunningRule.java:434-464,注释例子 sum(percentile{p='50,75'} > 1000) >= 3,P75 命中就算触发)。

Hook 输出

NotifyHandler.init:236-248)注册所有回调:

  • AlarmStandardPersistence(来自 CoreModule,持久化告警到存储,AlarmStandardPersistence.java:44doAlarm 写入告警记录,doAlarmRecovery 通过 SourceReceiver 发恢复事件)。
  • 九种 hook:WebhookCallbackGRPCCallbackSlackhookCallbackWechatHookCallbackDingtalkHookCallbackFeishuHookCallbackWeLinkHookCallbackPagerDutyHookCallbackDiscordHookCallback

WebhookCallbackwebhook/WebhookCallback.java:39)继承 HttpAlarmCallbackdoAlarmCallback:44-68):

  • messages 上的 hooks 分组(groupMessagesByHook)。
  • 对每个 hook 找对应 WebhookSettings,取 urls(恢复用 recoveryUrls),post(URI.create(url), gson.toJson(messages), headers)
  • headers 支持自定义(如 Authorization: Bearer ...)。

每种 hook 有独立 *Settings + *HookCallback(如 dingtalk/DingtalkSettings.java + DingtalkHookCallback.java,支持 secret 签名、text-template/recovery-text-template 文案模板,{name} 等占位符由 AlarmMessageFormatter 替换)。

Hook 类型枚举 AlarmHooksTypewebhookgRPCslackwechatdingtalkfeishuwelinkpagerdutydiscord

Hook 路由规则
规则没指定 hooks 字段时,用所有 is-default: true 的 hook(RulesReader.java:126-128defaultHooks);指定了就只用指定的,且会校验 hook 名合法性。

告警与 MQE 的关系

当前版本告警系统的核心设计——告警规则表达式直接复用 MQE 引擎

图7

  1. 规则加载期校验AlarmRule.setExpressionAlarmMQEVerifyVisitor 预编译,提取 includeMetricsmaxTrendRange,校验是布尔单值结果。
  2. 运行期求值RunningRule.Window.isMatch:418-474)用 AlarmMQEVisitorexpr/rt/AlarmMQEVisitor.java:53),继承 MQEVisitorBase,但数据来源不同:查询侧 MQEVisitor 从存储读指标,而 AlarmMQEVisitorWindow 里已缓存的 LinkedList<Map<String, Metrics>> 取值,即告警在流处理内存里计算,不再回查存储。Step.MINUTE 固定(:74)。

为什么告警在内存 Window 算而不是回查存储?因为告警是高频检查(�� 10 秒一次),如果每次都回查存储,等于每 10 秒对所有告警规则跑一轮查询,存储压力巨大。而分钟级指标本来就在流处理链里经过 AlarmNotifyWorker,顺手塞进 Window 是零成本——数据已经在内存里了,再用它算一次表达式即可。代价是 Window 占内存(每规则每实体一个 LinkedList),所以 period 不能设太大,默认 1 分钟窗口长度有限。
3. 快��AlarmMQEVisitor 产出 mqeMetricsSnapshotJsonObject:66),存到 Window.mqeMetricsSnapshot:287),再塞进 AlarmMessage.mqeMetricsSnapshotAlarmMessage.java:52)。对应 GraphQL AlarmSnapshot 类型,让 UI 展示触发告警时的指标曲线。
4. 同一套语法execExpression(GraphQL 查询)和告警规则表达式用同一个 ANTLR 文法 mqe.rt.grammar.MQELexer/MQEParser——你可以在 UI 上先用 execExpression 调试表达式、确认结果,再原样写进 alarm-settings.yml 当告警规则。这是 SkyWalking 9.5.0+ 的重要改进。

AlarmKernel · 运行时规则重置

AlarmKernelAlarmKernel.java:47)实现 AlarmKernelService.reset(affectedMetricNames):当某指标的语义在运行时变化(如 OAL 规则热更新改变了指标定义),调用方传入受影响的指标名,AlarmKernel 遍历所有 RunningRule,凡是 includeMetrics 命中受影响指标的,调 rule.resetWindows:217-220)清空所有 Window 的累积状态和状态机,避免旧语义下的告警状态污染新语义。


关键文件速查

查询层

  • oap-server/server-core/.../query/QueryModule.java:26
  • oap-server/server-query-plugin/query-graphql-plugin/.../GraphQLQueryProvider.java:69
  • oap-server/server-query-plugin/query-graphql-plugin/.../GraphQLQueryHandler.java:41
  • oap-server/server-query-plugin/query-graphql-plugin/src/main/resources/query-protocol/common.graphqls
  • oap-server/server-query-plugin/query-graphql-plugin/src/main/resources/query-protocol/metrics-v3.graphqls:116
  • oap-server/server-query-plugin/query-graphql-plugin/.../resolver/MetricsExpressionQuery.java:44
  • oap-server/server-query-plugin/query-graphql-plugin/.../resolver/TraceQuery.java:49
  • oap-server/server-query-plugin/query-graphql-plugin/.../resolver/AlarmQuery.java:58
  • oap-server/server-query-plugin/promql-plugin/.../PromQLProvider.java:34 + handler/PromQLApiHandler.java:116
  • oap-server/server-query-plugin/logql-plugin/.../LogQLProvider.java:33 + handler/LogQLApiHandler.java:71
  • oap-server/server-query-plugin/traceql-plugin/.../TraceQLProvider.java:34
  • oap-server/server-query-plugin/zipkin-query-plugin/.../ZipkinQueryProvider.java:34 + handler/ZipkinQueryHandler.java

告警

  • oap-server/server-core/.../alarm/AlarmModule.java:28
  • oap-server/server-alarm-plugin/.../AlarmModuleProvider.java:39
  • oap-server/server-alarm-plugin/.../RulesReader.java:53
  • oap-server/server-alarm-plugin/.../AlarmRule.java:53
  • oap-server/server-alarm-plugin/.../AlarmRulesWatcher.java:54
  • oap-server/server-alarm-plugin/.../NotifyHandler.java:57
  • oap-server/server-alarm-plugin/.../AlarmCore.java:41
  • oap-server/server-alarm-plugin/.../RunningRule.java:76(含 WindowAlarmStateMachine
  • oap-server/server-alarm-plugin/.../expr/rt/AlarmMQEVisitor.java:53
  • oap-server/server-alarm-plugin/.../expr/rt/AlarmMQEVerifyVisitor.java
  • oap-server/server-alarm-plugin/.../webhook/WebhookCallback.java:39
  • oap-server/server-alarm-plugin/src/test/resources/alarm-settings.yml(真实规则示例)
  • 流处理链入口:oap-server/server-core/.../analysis/worker/MetricsStreamProcessor.java:317 + AlarmNotifyWorker.java:30 + AlarmEntrance.java:24
  • 持久化回调:oap-server/server-core/.../alarm/AlarmStandardPersistence.java:44
  • 告警消息体:oap-server/server-core/.../alarm/AlarmMessage.java:39

存疑项

  • MetricsConverter:在全仓库无匹配,本版本由 NotifyHandler 按 scope 构造 MetaInAlarm 取代。
  • alarm-rule.yml:源码加载的是 alarm-settings.yml,无 alarm-rule.yml

接下来

OAP 怎么多节点协调、怎么动态改配置、怎么自监控、AI 基线怎么接?详见 07-集群协调与动态配置。

心智模型
把查询层想成"展厅的多种导览方式":GraphQL 是原生导览员(按你点哪个展品讲哪个),PromQL/LogQL/TraceQL 是会多国语言的导览员(让 Grafana 这种外国游客也能听懂),Zipkin 是给老客户用的旧导览图(兼容老接口)。告警系统是"质检报警器":指标落盘时顺带把样本塞进滑窗,每 10 秒巡检一次,状态机决定"刚发现问题→报警"“问题还在但静默期→不重复报”“问题消失→观察一阵→发恢复通知”,最后通过九种通知渠道(钉钉/Slack/Webhook…)发出去。