- 可观测性
- 后端
- 微服务
- 云原生
【免费下载链接】skywalking
APM, Application Performance Monitoring System
日志分析是 APM 系统补齐可观测性最后一环的关键能力。SkyWalking OAP Server 内置的log-analyzer 模块可以直接接收并处理原生日志数据(native log data),并通过专为日志分析设计的Log Analysis Language(LAL)完成日志的结构化解析、字段提取与落库保存;同时借助Meter Analysis Language(MAL)引擎,将日志中蕴含的指标(如各日志级别的计数、响应时间分布)进一步计算成可查询的业务指标。读完本文,你将掌握 log-analyzer 的完整配置方法、LAL 的 parser / extractor / sink 三大组件语法,并能独立编写从"原始日志"到"结构化日志 + 指标 + 慢 SQL"的完整分析规则。
一、认识 OAP 的 log-analyzer 模块
log-analyzer 是 OAP Server 中负责日志分析的核心模块,代码位于 oap-server/analyzer/log-analyzer。它通过LogAnalysisListener监听各接收器(receiver)上报的原始日志,再交由 LAL 脚本完成解析、提取与保存。其模块配置定义在 LogAnalyzerModuleConfig.java:
| 配置项 | 默认值 | 说明 |
|---|---|---|
selector | default | 选择 log-analyzer 的实现提供者,对应LogAnalyzerModuleProvider(name 为default) |
default.lalFiles | default.yaml | 启用的 LAL 规则文件列表,逗号分隔,文件位于lal目录 |
default.malFiles | 空 | 启用的 MAL 指标规则文件列表,逗号分隔,文件位于log-mal-rules目录 |
1. 配置文件与目录约定
在 OAP 的application.yml中,log-analyzer 的标准配置如下(出自 docs/en/setup/backend/log-analyzer.md):
log-analyzer: selector: ${SW_LOG_ANALYZER:default} default: lalFiles: ${SW_LOG_LAL_FILES:default} malFiles: ${SW_LOG_MAL_FILES:""}三个配置项全部支持通过环境变量覆盖,便于在容器化部署中按需调整:
SW_LOG_ANALYZER:选择模块实现,一般保持default;SW_LOG_LAL_FILES:激活哪些 LAL 文件(相对于lal目录);SW_LOG_MAL_FILES:激活哪些 MAL 指标文件(相对于log-mal-rules目录)。
从源码看,lalFiles通过Splitter.on(",")按逗号切分(omitEmptyStrings忽略空段),malFiles则由Rules.loadRules(getMalPath(), files)加载为 MAL 规则对象。这意味着多个文件用逗号分隔即可一次启用,例如lalFiles: a,b,c。
OAP 发行版自带的默认配置(oap-server/server-starter/src/main/resources/application.yml)是这样的:
log-analyzer: selector: ${SW_LOG_ANALYZER:default} default: lalFiles: ${SW_LOG_LAL_FILES:envoy-als,mesh-dp,mysql-slowsql,pgsql-slowsql,redis-slowsql,k8s-service,nginx,default} malFiles: ${SW_LOG_MAL_FILES:"nginx"}可见官方默认就内置了 Envoy ALS、服务网格数据面、MySQL/PostgreSQL/Redis 慢 SQL、K8s 服务日志、Nginx 日志等多套开箱即用的 LAL 规则,以及一套nginx的 MAL 指标规则。
2. 模块装配与加载流程
LogAnalyzerModuleProvider.java 展示了模块的装配方式:prepare()阶段创建LogAnalyzerServiceImpl并注册ILogAnalyzerService服务;start()阶段注册LogFilterListener.Factory——它是 LAL 脚本的驱动入口。模块依赖CoreModule与ConfigurationModule。
LAL 脚本本身基于 Groovy 实现,加载逻辑集中在 DSL.java。值得关注的安全设计:DSL 编译时通过SecureASTCustomizer禁用了while/do-while/for循环语句,并将允许的接收者类型限制为Object、Map、List、Array、String及ProcessRegistry,同时把脚本基类设置为LALDelegatingScript,从而在提供强大表达能力的同时避免脚本造成安全风险。
二、LAL 语言核心:从一条日志到结构化数据
LAL(Log Analysis Language)是 SkyWalking 为日志分析专门设计的领域特定语言(DSL),完整语言参考见 docs/en/concepts-and-designs/lal.md。它的整体思路是:每一条日志都会顺序经过 LAL 规则中声明的一个或多个filter,每个 filter 内部由 parser(解析)、extractor(提取)、sink(落库)按声明顺序依次执行。
LAL 文件为 YAML 格式,放在lal目录下,结构如下(参考发行版示例 dist-material/config-examples/lal.yaml):
rules: - name: example dsl: | filter { ... }每个规则由name和dsl组成,dsl就是一段 Groovy 风格的 LAL 脚本。
1. Layer:日志的分析范围
layer在 LAL 脚本中声明,表示日志所属的分析分层(如GENERAL、MYSQL等,对应 Layer.java 定义的分层枚举)。它既可以在extractor中通过layer 'GENERAL'这种形式从脚本直接设置,也可以通过layer parsed.layer as String从解析结果中提取。
2. Filter 与全局函数
filter是 parser、extractor、sink 的容器。每条日志会被发送到 LAL 规则中的所有 filter;在脚本内,当前日志以log属性访问(如log.service取服务名)。除组件自身的语法外,LAL 还提供两个全局函数,可在任意组件中使用:
abort:提前终止过滤器链。默认情况下,无论dropped、saved等标志如何,所有已声明组件都会执行;abort则从声明位置起跳过剩余所有组件,实现"快速失败"。典型用法是在 filter 入口过滤掉无价值的日志来源:
filter { if (log.service == "TestingService") { // 不为测试服务浪费资源 abort {} // 后续所有组件都不会执行 } // ... parsers, extractors, sinks }注意:在if条件中使用regexp时,必须写成regexp(<表达式>)的括号形式,不能省略括号。
tag:读取日志标签。日志数据中携带的 tags(key/value 对)可通过tag("KEY")便捷取值,常用于条件判断:
filter { if (tag("TEST_KEY") == "TEST_VALUE") { ... } }三、Parser:把原始日志变成结构化字段
parser 负责把原始日志解析为结构化数据。解析完成后,LAL 会注入属性parsed——一个 Map,包含解析出的所有字段:json/yamlparser 的parsed是 JSON/YAML 的全部键值;textparser(配合正则)的parsed则是所有捕获组及其值。
所有 parser 共享一个选项abortOnFailure(boolean,默认true):解析/匹配失败时是否中止整个过滤器链。
| parser | 适用场景 | 示例 |
|---|---|---|
json | JSON 结构化日志 | json { abortOnFailure true } |
yaml | YAML 结构化日志 | yaml { abortOnFailure true } |
text(regexp) | 非结构化文本日志 | 见下文正则示例 |
json/yaml的用法非常直接:
filter { json { abortOnFailure true // 可选,这是默认行为 } }对非结构化日志,使用textparser 中的regexp:它基于正则的捕获组提取字段,返回boolean表示是否匹配:
filter { text { abortOnFailure true // 可选,这是默认行为 // 演示用模式 regexp "(?<timestamp>\\d{8}) (?<thread>\\w+) (?<level>\\w+) (?<traceId>\\w+) (?<msg>.+)" } extractor { tag level: parsed.level // 增加一个名为 level 的标签,值来自上面正则捕获的 parsed.level traceId parsed.traceId // 提取 trace id,用于将日志与链路关联 } // ... }LAL 文档中也预告了grokparser(TODO 状态):官方正在评估 grok Java 库的性能问题,尚未正式支持,社区欢迎相关贡献。
四、Extractor:提取元数据与生成指标
extractor 的目标是从parsed中提取元数据并写入LogData——包括服务名、实例名、端点名、trace ID 等,从而与已有的链路(trace)和指标(metrics)建立关联。LAL 支持的 extractor 如下:
1. 基础关联提取器
| 提取器 | 作用 |
|---|---|
service | 从parsed提取服务名,写入LogData,用于关联 trace/metrics |
instance | 提取服务实例名 |
endpoint | 提取端点名 |
traceId | 提取 trace ID,建立日志与链路的关联 |
segmentId | 提取 segment ID |
spanId | 提取 span ID |
timestamp | 提取时间戳(毫秒或带格式的日期字符串,见下文) |
layer | 提取分层(layer),写入LogData并与服务关联 |
tag | 从parsed提取标签写入LogData,支持动态键值 |
timestamp支持两种写法——毫秒时间戳:
extractor { timestamp parsed.time as String }或带格式的日期字符串:
extractor { timestamp parsed.time as String, "yyyy-MM-dd HH:mm:ss" }tag的写法是tag key1: value, key2: value2,键和值都可以来自parsed:
extractor { tag level: parsed.level, (parsed.statusCode): parsed.statusMsg tag anotherKey: "anotherConstantValue" layer 'GENERAL' }2. metrics:从日志生成指标
metricsextractor 从日志中提取/生成指标并发送到 meter 系统,之后由 MAL 规则做进一步计算。这些专用的 MAL 配置文件放在log-mal-rules目录,通过log-analyzer/default/malFiles(或环境变量SW_LOG_MAL_FILES)启用:
# application.yml log-analyzer: selector: ${SW_LOG_ANALYZER:default} default: lalFiles: ${SW_LOG_LAL_FILES:my-lal-config} # 文件位于 "lal" 目录 malFiles: ${SW_LOG_MAL_FILES:my-lal-mal-config, folder1/another-lal-mal-config, folder2/*} # 文件位于 "log-mal-rules" 目录注意malFiles支持目录通配(folder2/*),与lalFiles一样用逗号分隔多个条目。
在 extractor 中声明指标,例如生成"日志计数"和"HTTP 响应时间"两个指标:
extractor { service parsed.serviceName metrics { name "log_count" timestamp parsed.timestamp labels level: parsed.level, service: parsed.service, instance: parsed.instance value 1 } metrics { name "http_response_time" timestamp parsed.timestamp labels status_code: parsed.statusCode, service: parsed.service, instance: parsed.instance value parsed.duration } }随后即可在log-mal-rules下的 MAL 配置中,按日志级别聚合计数:
# ... MAL 的其他配置 metrics: - name: log_count_debug exp: log_count.tagEqual('level', 'DEBUG').sum(['service', 'instance']).increase('PT1M') - name: log_count_error exp: log_count.tagEqual('level', 'ERROR').sum(['service', 'instance']).increase('PT1M')也可以对http_response_time生成百分位等更丰富的指标:
metrics: - name: response_time_percentile exp: http_response_time.sum(['le', 'service', 'instance']).increase('PT5M').histogram().histogram_percentile([50,70,90,99])3. slowSql:慢 SQL 识别
slowSqlextractor 用于将LogData转换为DatabaseSlowStatement(数据库慢语句记录),与 TopN 数据库语句统计关联。它不会中止或修改日志本身,可以继续用其他 LAL 做进一步处理;同时它会复用 extractor 中的service、layer、timestamp,因此必须在这三个字段设置之后使用。
要让 OAP 从普通日志中识别慢 SQL,日志必须携带标签"LOG_KIND" = "SLOW_SQL"。一个上报到 OAP 的 JSON 示例:
[ { "tags":{ "data":[ { "key":"LOG_KIND", "value":"SLOW_SQL" } ] }, "layer":"MYSQL", "body":{ "json":{ "json":"{\"time\":\"1663063011\",\"id\":\"cb92c1a5b-2691e-fb2f-457a-9c72a392d9ed\",\"service\":\"root[root]@[localhost]\",\"statement\":\"select sleep(2);\",\"layer\":\"MYSQL\",\"query_time\":2000}" } }, "service":"root[root]@[localhost]" } ]配合slowSql的还有三个从parsed提取字段的 extractor,均写入DatabaseSlowStatement:
| 提取器 | 作用 |
|---|---|
statement | 提取 SQL 语句 |
latency | 提取耗时(毫秒) |
id | 提取语句 ID |
一个完整的慢 SQL 识别 LAL 示例:
filter { json{ } extractor{ layer parsed.layer as String service parsed.service as String timestamp parsed.time as String if (tag("LOG_KIND") == "SLOW_SQL") { slowSql { id parsed.id as String statement parsed.statement as String latency parsed.query_time as Long } } } }注意:慢 SQL 采样只是把该 SQL 标记进候选列表。OAP 会按服务统计,默认每 10 分钟(由topNReportPeriod: ${SW_CORE_TOPN_REPORT_PERIOD:10}控制)只持久化 Top 50 条。
4. sampledTrace:网络剖析采样链路
sampledTraceextractor 将LogData转换为SampledTraceRecord(采样链路记录),用于网络剖析(network profiling)场景,同样不会中止或修改日志。识别标识是日志标签"LOG_KIND" = "NET_PROFILING_SAMPLED_TRACE",对应layer: MESH的日志:
[ { "tags":{ "data":[ { "key":"LOG_KIND", "value":"NET_PROFILING_SAMPLED_TRACE" } ] }, "layer":"MESH", "body":{ "json":{ "json":"{\"uri\":\"/provider\",\"reason\":\"slow\",\"latency\":2048,\"client_process\":{\"process_id\":\"c1519f4555ec11eda8df0242ac1d0002\",\"local\":false,\"address\":\"\"},\"server_process\":{\"process_id\":\"\",\"local\":false,\"address\":\"172.31.0.3:443\"},\"detect_point\":\"client\",\"component\":\"http\",\"ssl\":true}" } }, "service":"test-service", "serviceInstance":"test-service-instance", "timestamp": 1666916962406, } ]对应的 LAL 示例(含客户端/服务端进程 ID 的生成与组件 ID 映射):
filter { json { } if (tag("LOG_KIND") == "NET_PROFILING_SAMPLED_TRACE") { sampledTrace { latency parsed.latency as Long uri parsed.uri as String reason parsed.reason as String if (parsed.client_process.process_id as String != "") { processId parsed.client_process.process_id as String } else if (parsed.client_process.local as Boolean) { processId ProcessRegistry.generateVirtualLocalProcess(parsed.service as String, parsed.serviceInstance as String) as String } else { processId ProcessRegistry.generateVirtualRemoteProcess(parsed.service as String, parsed.serviceInstance as String, parsed.client_process.address as String) as String } if (parsed.server_process.process_id as String != "") { destProcessId parsed.server_process.process_id as String } else if (parsed.server_process.local as Boolean) { destProcessId ProcessRegistry.generateVirtualLocalProcess(parsed.service as String, parsed.serviceInstance as String) as String } else { destProcessId ProcessRegistry.generateVirtualRemoteProcess(parsed.service as String, parsed.serviceInstance as String, parsed.server_process.address as String) as String } detectPoint parsed.detect_point as String if (parsed.component as String == "http" && parsed.ssl as Boolean) { componentId 129 } else if (parsed.component as String == "http") { componentId 49 } else if (parsed.ssl as Boolean) { componentId 130 } else { componentId 110 } } } }其中ProcessRegistry由 DSL 编译期导入,用于生成虚拟进程 ID(本地/远程进程均支持),componentId则按协议与是否 SSL 映射到 SkyWalking 组件编号。
五、Sink:采样、丢弃与强制保留
sink 是 LAL 的持久化层。默认情况下每个 filter 的日志都会保存到存储;但 LAL 提供多种机制支持"选择性保存"甚至"全部丢弃"。
1. Sampler:按策略采样
sampler 支持两种采样策略:
rateLimit:按分钟限流采样。rateLimit("SamplerID")需要一个采样器 ID——相同 ID 的 sampler 声明共享同一个采样器实例,因此共享rpm计数与重置逻辑:sink { sampler { if (parsed.service == "ImportantApp") { rateLimit("ImportantAppSampler") { rpm 1800 // 对服务 "ImportantApp" 每分钟最多采样 1800 条 } } else { rateLimit("OtherSampler") { rpm 180 // 其他服务每分钟最多采样 180 条 } } } }possibility:按百分比概率采样,概率由 Java 随机数生成器产生并与此前给定的percentage比较:sink { sampler { if (parsed.service == "ImportantApp") { possibility(80) { // 对服务 "ImportantApp" 采样 80% 的日志 } } else { possibility(30) { // 其他服务采样 30% 的日志 } } } }
若同时指定了多个 sampler,最后一个生效。
2. Dropper:无条件丢弃
dropper 是特殊的 sink——所有日志无条件丢弃,适合过滤 DEBUG 等调试日志:
sink { if (parsed.level == "DEBUG") { dropper {} } else { sampler { // ... 采样配置 } } }当配置了多个 filter(其中一些仅用于提取指标)时,可以让"用于落库"的 filter 之外的 filter 全部 dropper——因为日志已在前面保存过:
filter { // filter A:负责持久化 // ... parser sink { sampler { // ... 采样配置 } } } filter { // filter B:只负责生成指标 // ... extractors 生成大量指标 extractors { metrics { // ... 指标配置 } } sink { dropper {} // 日志已在 filter A 保存,这里全部丢弃 } }3. Enforcer:强制采样
enforcer 是另一种特殊 sink:强制采样。典型场景是:已经配置了 sampler,但仍希望某些日志(如错误日志、特定测试用户日志)即使被采样策略命中也要强制保存:
sink { sampler { // ... 采样配置 } if (parsed.level == "ERROR" || parsed.userId == "TestingUserId") { // 即使配置了采样策略,也强制保存错误日志或测试用户日志 enforcer { } } }六、完整实战:一套可运行的日志分析规则
下面把前文的知识点串成一个完整示例。它出自官方发行版示例 dist-material/config-examples/lal.yaml(文件实际路径应位于config/lal/下),展示了 text 正则解析 + 指标提取 + 限流采样的完整链路:
rules: - name: example dsl: | filter { if (log.service == "TestService") { abort {} } text { if (!regexp($/(?s)(?<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3}) \[TID:(?<tid>.+?)] \[(?<thread>.+?)] (?<level>\w{4,}) (?<logger>.{1,36}) (?<msg>.+)/$)) { abort {} } } extractor { metrics { timestamp log.timestamp labels level: parsed.level, service: log.service, instance: log.serviceInstance name "log_count" value 1 } } sink { sampler { if (log.service == "ImportantApp") { rateLimit("ImportantAppSampler") { rpm 18000 } } else { rateLimit("OtherSampler") { rpm 1800 } } } } }配套的 MAL 指标规则(参考 dist-material/config-examples/log-mal.yaml,实际路径位于config/log-mal-rules/下)可按日志级别计算 INFO 日志计数:
expSuffix: instance(['service'], ['instance']) metricPrefix: log metricsRules: - name: count_info exp: log_count.tagEqual('level', 'INFO').sum(['service', 'instance'])部署时,只要把上述 LAL 文件放入config/lal/目录、MAL 文件放入config/log-mal-rules/目录,并在application.yml或环境变量中启用对应文件名即可:
# 通过环境变量启用(示例) SW_LOG_LAL_FILES=example SW_LOG_MAL_FILES=log-mal-example七、总结
SkyWalking 的日志分析能力可以概括为一条流水线:接收器上报原生日志 → log-analyzer 模块加载 LAL 规则 → parser 结构化解析 → extractor 提取服务/实例/端点/链路 ID 与指标 → sink 按采样/丢弃/强制策略落库 → MAL 引擎对生成的指标做二次计算。整套机制将日志、链路(trace)与指标(metrics)三类可观测性数据打通:通过traceId/segmentId/spanId提取器实现日志与链路的关联,通过metricsextractor + MAL 规则实现日志驱动的指标体系,通过slowSqlextractor 实现数据库慢语句的 TopN 统计。
深入理解 LAL 的完整语法可继续阅读 docs/en/concepts-and-designs/lal.md,MAL 的表达式与聚合能力见 docs/en/concepts-and-designs/mal.md;日志上报协议字段定义(log属性可访问的全部字段)可参考数据收集协议中 Logging 协议的定义;模块配置与加载的源码实现可分别查看 LogAnalyzerModuleConfig.java 与 DSL.java。
- 可观测性
- 后端
- 微服务
- 云原生
【免费下载链接】skywalking
APM, Application Performance Monitoring System
相关推荐
Apache SkyWalking 日志分析语言(LAL)完全指南:从 DSL 语法到日志、链路与指标联动
Apache SkyWalking 日志分析语言(LAL)完全指南:从 DSL 语法到日志、链路与指标联动 导读 Log Analysis Language(L
可观测性后端微服务云原生SkyWalking OAP LAL DSL 调试 API 实战指南:基于 DSL Debug API 逐语句剖析日志分析规则
SkyWalking OAP LAL DSL 调试 API 实战指南:基于 DSL Debug API 逐语句剖析日志分析规则 导读 本文是 SkyWalkin
可观测性APM链路追踪指标监控日志分析微服务Apache SkyWalking日志关键字提取:基于LAL的异常检测
Apache SkyWalking日志关键字提取:基于LAL的异常检测 一、日志分析的痛点与解决方案 在分布式系统监控中,日志数据犹如散落的拼图碎片,传统人工筛
可观测性后端微服务云原生
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考