查询层与告警
这一篇解决什么
数据存好了,怎么取出来给 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 · 主查询入口
模块结构
QueryModule(oap-server/server-core/.../query/QueryModule.java:26):根查询模块,名字 "query",services() 返回空数组。它不暴露服务接口,只是一个挂载点,让各种查询协议插件以 ModuleProvider 形式注册进来。

Module/Provider 插槽比喻
把QueryModule想成"插槽",各 provider 想成"插进去的卡"。同一 Module 可以有多个 Provider,但运行时只激活一个(配置 selector 决定)。不过查询层特殊——QueryModule实际上可以同时启用多个 provider(GraphQL + PromQL + LogQL 各占一个端口),因为它们不冲突。
GraphQL schema 与 resolver
GraphQLQueryProvider(oap-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-tools 的 SchemaParser 把一堆 .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 执行链路

GraphQLQueryHandler(oap-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 服务对接:
TraceQuery→TraceQueryService、TagAutoCompleteQueryServiceMetricsQuery→MetricsQueryService、AggregationQueryService、RecordQueryService、MetricsMetadataQueryServiceAlarmQuery→AlarmQueryService、EventQueryService、TagAutoCompleteQueryServiceHierarchyQuery→HierarchyQueryService
所有 resolver 方法返回 CompletableFuture,通过 AsyncQueryUtils.queryAsync 异步执行;每个方法接受 boolean debug,开启后构造 DebuggingTraceContext 把 OAP 内部查询执行 trace 回填到结果里。
GraphQL 按需取字段的优化
AlarmQuery.getAlarm(AlarmQuery.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:124 的 execExpression。
执行流程(MetricsExpressionQuery.java:58-103):
- 构造
DebuggingTraceContext,MQEVisitor实例化(持有 moduleManager/entity/duration)。 MQELexer+MQEParser解析表达式得到ParseTree;解析失败返回ExpressionResultType.UNKNOWN+ 错误信息。visitor.visit(tree)求值,得到ExpressionResult。- 对结果数值用
DecimalFormat格式化(setGroupingUsed(false),不加分隔符)。
ExpressionResult 类型有 4 种(metrics-v3.graphqls:45-58):SINGLE_VALUE、TIME_SERIES_VALUES、SORTED_LIST、RECORD_LIST,外加 UNKNOWN。
MQEExecutor(.../mqe/rt/MQEExecutor.java:52)是从 MetricsExpressionQuery 抽出来的同步求值器,给 admin inspect 路径用,复用同一套 MQE 引擎但支持叠加"外部指标"(foreign metrics)元数据。详见 03-分析引擎与四大DSL 的 MQE 节。
其他查询协议

���什么要四套兼容协议
GraphQL 是 SkyWalking 原生 UI 用的,但运维团队往往已有 Grafana(看 PromQL 指标 + Loki 日志 + Tempo Trace)或 Zipkin 体系。这四个插件让 OAP 不必改造既有可视化栈就能接入,降低迁移成本。
PromQL · Grafana 指标直连
PromQLModule+PromQLProvider(.../promql/PromQLProvider.java:34),requiredModules含TelemetryModule+CoreModule。- 走独立 HTTP 端口:
start()(:70-89)自己 newHTTPServer,addHandler(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 端口,requiredModules仅CoreModule。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 端口,requiredModules含TelemetryModule+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。
告警系统
模块结构
AlarmModule(oap-server/server-core/.../alarm/AlarmModule.java:28),名字 "alarm",暴露 4 个 Service(:36-43):
| Service | 职责 |
|---|---|
MetricsNotify |
流处理链调用,把指标喂给告警引擎 |
AlarmRulesWatcherService |
规则热更新接口 |
AlarmStatusWatcherService |
告警状态观察 |
AlarmKernelService |
运行时规则重置 |
AlarmModuleProvider(oap-server/server-alarm-plugin/.../AlarmModuleProvider.java:39),requiredModules 是 CoreModule + ConfigurationModule(告警规则支持配置中心动态更新)。
启动流程:
prepare()(:61-69):创建AlarmRulesWatcher(持空Rules)、NotifyHandler,注册 4 个服务实现。start()(:72-78):从ConfigurationModule拿DynamicConfigurationService,把AlarmRulesWatcher注册为配置变更监听器(告警规则可热更新)。notifyAfterCompleted()(:81-92):从 classpath 读alarm-settings.yml,用RulesReader解析成Rules,alarmRulesWatcher.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.yml(AlarmModuleProvider.java:84的ResourceUtils.read("alarm-settings.yml"))。
规则加载与编译
RulesReader(RulesReader.java:53)用 SnakeYAML 解析,先读 hooks 再读 rules(顺序重要,因为规则的 hooks 字段要校验是否引用了已声明的 hook,checkSpecificHooks 在 :450-455)。
AlarmRule.setExpression(AlarmRule.java:75-109)不是简单赋值,而是编译 + 校验:

- ANTLR 解析 MQE 表达式。
AlarmMQEVerifyVisitor遍历,提取includeMetrics(表达式引用的所有指标名)和maxTrendRange。- 校验三件事:解析无错(
:90)、parseResult.isBoolResult()为真(:93,否则抛IllegalExpressionException("root operation is not a Compare Operation"))、结果类型SINGLE_VALUE(:97)。 verifyIncludeMetrics(:111-120)校验所有指标必须属于同一 scope。
AlarmRulesWatcher(AlarmRulesWatcher.java:54)继承 ConfigChangeWatcher,监听配置 key "alarm-settings"(:67)。维护三个核心 map:runningContext(表达式 → ListalarmRuleRunningRuleMap(AlarmRule → RunningRule,热更新时复用保留历史窗口)、exprMetricsMap(表达式 → Set<指标名>,快速查找某指标命中哪些规则)。
热更新策略
notify(Rules)(:112-139):如果新规则和旧规则"相等"(AlarmRule用@EqualsAndHashCode),复用其RunningRule以保留历史指标窗口;否则 new 一个新RunningRule。
流处理链如何触发告警
这是告警与流处理的核心连接点:

- 指标落盘后通知告警:
MetricsStreamProcessor.minutePersistentWorker(:317)创建AlarmNotifyWorker,作为MetricsPersistentMinWorker的下游(:320-323)。分钟级指标持久化后调AlarmNotifyWorker.in(metrics)。 AlarmNotifyWorker(AlarmNotifyWorker.java:30):in若 metrics 实现WithMetadata,调entrance.forward(metrics)。AlarmEntrance(AlarmEntrance.java:24):forward先检查has(AlarmModule.NAME)(没装告警模块跳过),再懒加载MetricsNotify调notify。NotifyHandler.notify(NotifyHandler.java:118-234):- 从 metrics 拿
MetricsMetaInfo(scope、metricsName、id)。 - 按 scope 构造对应
MetaInAlarm(Service/ServiceInstance/Endpoint 及 Relation),用IDManager解析 id 成可读名,通过MetadataQueryService查layers。 - 只接受 6 种 catalog scope(service/instance/endpoint 及 relation),其它直接 return。
core.findRunningRule(metricsName)找命中该指标的规则(AlarmCore.findRunningRule,AlarmCore.java:57-68,遍历exprMetricsMap反查)。- 对每个命中的
RunningRule调rule.in(metaInAlarm, metrics)。
- 从 metrics 拿
RunningRule.in(RunningRule.java:135-156):- 先按
includeMetrics过滤指标名。 - 再按 include/exclude names + 正则过滤实体名(
validate:162-200)。 - 为实体构造
AlarmEntity,computeIfAbsent拿到/创建Window,window.add把指标塞进滑窗。
- 先按
AlarmNotifyWorker 只接分钟级
AlarmNotifyWorker只接在分钟级 worker 后,不接小时/天。RunningRule注释:“only minute dimensionality metrics are expected to process”。告警基于分钟级指标。
AlarmCore · 定时检查
AlarmCore.start(AlarmCore.java:70-122):单线程定时器,每 10 秒触发一次(:121 scheduleAtFixedRate(..., 10, 10, SECONDS))。每次:
- 计算距上次执行的分钟数。
- 对每个
RunningRule调moveTo(checkTime)推进滑窗;且只在每分钟的第 15 秒之后才真正check()(:85checkTime.getSecondOfMinute() > 15),注释说"避免在每分钟头一刻触发误报"——因为分钟级指标可能还没落盘完。 - 收集所有
AlarmMessage,分出 firing(AlarmMessage)和 recovery(AlarmRecoveryMessage),遍历所有AlarmCallback分别调doAlarm/doAlarmRecovery(:99-110)。
告警状态机
window / 连续 N 次 / 静默 速成
- window(滑窗):
RunningRule.Window是按分钟为粒度的滑动窗口,长度 =period。window.add把每分钟一个指标值塞进LinkedList,moveTo随时间推进丢弃过期分钟、补 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 = period(RulesReader.java:114)。- recovery-observation-period(恢复观察期):触发后条件不再满足时,不是立刻发恢复通知,而是观察
recovery-observation-period分钟确认稳定恢复才发AlarmRecoveryMessage。
状态机 5 个状态(RunningRule.java:258-264):

AlarmStateMachine(RunningRule.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时构造AlarmMessage(buildAlarmMessage: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:44,doAlarm写入告警记录,doAlarmRecovery通过SourceReceiver发恢复事件)。- 九种 hook:
WebhookCallback、GRPCCallback、SlackhookCallback、WechatHookCallback、DingtalkHookCallback、FeishuHookCallback、WeLinkHookCallback、PagerDutyHookCallback、DiscordHookCallback。
WebhookCallback(webhook/WebhookCallback.java:39)继承 HttpAlarmCallback,doAlarmCallback(: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 类型枚举 AlarmHooksType:webhook、gRPC、slack、wechat、dingtalk、feishu、welink、pagerduty、discord。
Hook 路由规则
规则没指定hooks字段时,用所有is-default: true的 hook(RulesReader.java:126-128的defaultHooks);指定了就只用指定的,且会校验 hook 名合法性。
告警与 MQE 的关系
当前版本告警系统的核心设计——告警规则表达式直接复用 MQE 引擎:

- 规则加载期校验:
AlarmRule.setExpression用AlarmMQEVerifyVisitor预编译,提取includeMetrics和maxTrendRange,校验是布尔单值结果。 - 运行期求值:
RunningRule.Window.isMatch(:418-474)用AlarmMQEVisitor(expr/rt/AlarmMQEVisitor.java:53),继承MQEVisitorBase,但数据来源不同:查询侧MQEVisitor从存储读指标,而AlarmMQEVisitor从Window里已缓存的LinkedList<Map<String, Metrics>>取值,即告警在流处理内存里计算,不再回查存储。Step.MINUTE固定(:74)。
为什么告警在内存 Window 算而不是回查存储?因为告警是高频检查(�� 10 秒一次),如果每次都回查存储,等于每 10 秒对所有告警规则跑一轮查询,存储压力巨大。而分钟级指标本来就在流处理链里经过 AlarmNotifyWorker,顺手塞进 Window 是零成本——数据已经在内存里了,再用它算一次表达式即可。代价是 Window 占内存(每规则每实体一个 LinkedList),所以 period 不能设太大,默认 1 分钟窗口长度有限。
3. 快��:AlarmMQEVisitor 产出 mqeMetricsSnapshot(JsonObject,:66),存到 Window.mqeMetricsSnapshot(:287),再塞进 AlarmMessage.mqeMetricsSnapshot(AlarmMessage.java:52)。对应 GraphQL AlarmSnapshot 类型,让 UI 展示触发告警时的指标曲线。
4. 同一套语法:execExpression(GraphQL 查询)和告警规则表达式用同一个 ANTLR 文法 mqe.rt.grammar.MQELexer/MQEParser——你可以在 UI 上先用 execExpression 调试表达式、确认结果,再原样写进 alarm-settings.yml 当告警规则。这是 SkyWalking 9.5.0+ 的重要改进。
AlarmKernel · 运行时规则重置
AlarmKernel(AlarmKernel.java:47)实现 AlarmKernelService.reset(affectedMetricNames):当某指标的语义在运行时变化(如 OAL 规则热更新改变了指标定义),调用方传入受影响的指标名,AlarmKernel 遍历所有 RunningRule,凡是 includeMetrics 命中受影响指标的,调 rule.resetWindows(:217-220)清空所有 Window 的累积状态和状态机,避免旧语义下的告警状态污染新语义。
关键文件速查
查询层
oap-server/server-core/.../query/QueryModule.java:26oap-server/server-query-plugin/query-graphql-plugin/.../GraphQLQueryProvider.java:69oap-server/server-query-plugin/query-graphql-plugin/.../GraphQLQueryHandler.java:41oap-server/server-query-plugin/query-graphql-plugin/src/main/resources/query-protocol/common.graphqlsoap-server/server-query-plugin/query-graphql-plugin/src/main/resources/query-protocol/metrics-v3.graphqls:116oap-server/server-query-plugin/query-graphql-plugin/.../resolver/MetricsExpressionQuery.java:44oap-server/server-query-plugin/query-graphql-plugin/.../resolver/TraceQuery.java:49oap-server/server-query-plugin/query-graphql-plugin/.../resolver/AlarmQuery.java:58oap-server/server-query-plugin/promql-plugin/.../PromQLProvider.java:34+handler/PromQLApiHandler.java:116oap-server/server-query-plugin/logql-plugin/.../LogQLProvider.java:33+handler/LogQLApiHandler.java:71oap-server/server-query-plugin/traceql-plugin/.../TraceQLProvider.java:34oap-server/server-query-plugin/zipkin-query-plugin/.../ZipkinQueryProvider.java:34+handler/ZipkinQueryHandler.java
告警
oap-server/server-core/.../alarm/AlarmModule.java:28oap-server/server-alarm-plugin/.../AlarmModuleProvider.java:39oap-server/server-alarm-plugin/.../RulesReader.java:53oap-server/server-alarm-plugin/.../AlarmRule.java:53oap-server/server-alarm-plugin/.../AlarmRulesWatcher.java:54oap-server/server-alarm-plugin/.../NotifyHandler.java:57oap-server/server-alarm-plugin/.../AlarmCore.java:41oap-server/server-alarm-plugin/.../RunningRule.java:76(含Window、AlarmStateMachine)oap-server/server-alarm-plugin/.../expr/rt/AlarmMQEVisitor.java:53oap-server/server-alarm-plugin/.../expr/rt/AlarmMQEVerifyVisitor.javaoap-server/server-alarm-plugin/.../webhook/WebhookCallback.java:39oap-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…)发出去。