一、功能特性
·采集方案
CLS 支持多种日志接入方式。你可以根据日志来源、现有采集链路及数据存储位置,选择对应的采集或数据导入方案。
| 数据源类型 | 日志来源 | 接入场景 | 相关文档 |
| 主机日志 | Linux/Unix 主机文件日志 | 使用 LogListener 采集应用及系统文件日志,适用于腾讯云服务器、自建 IDC 及其他云厂商服务器。 | LogListener 安装指南(Linux 版) |
| | Windows 主机文件日志 | 使用 LogListener Windows 版采集文本日志。 | LogListener 安装指南(Windows 版) |
| | Windows 事件日志 | 使用 LogListener Windows 版采集 Application、System、Security 等事件通道日志。 | 采集 Windows 事件日志 |
| | 网络设备及服务器 Syslog | 使用 LogListener 监听 TCP 或 UDP 端口,采集并解析 Syslog 日志。 | 采集 Syslog |
| 容器日志 | 腾讯云 TKE 集群 | 采集容器标准输出、容器文件及节点文件日志,并关联 Pod、Namespace、Container 等元数据。 | 采集 TKE K8s 集群日志 |
| | 自建或其他云 Kubernetes 集群 | 在集群中部署 LogListener,采集容器标准输出及容器文件日志。 | 采集自建 K8s 集群日志 |
| 客户端日志 | Android、iOS、HarmonyOS、Flutter | 使用对应客户端 SDK 采集并上传应用日志。 | 使用 SDK 上传日志 |
| | Web 页面、微信小程序 | 使用浏览器或小程序 JavaScript SDK 上传日志;免鉴权场景可使用匿名日志上传。 | 使用 SDK 上传日志匿名日志上传 |
| 应用日志 | 应用程序生成的结构化日志 | 使用 CLS SDK 或 API 将结构化日志直接上传至 CLS。 | 使用 SDK 上传日志使用 API 上传日志 |
| | Java Logback、Log4j 日志 | 使用 Appender 直接上传日志,支持异步发送、缓存和失败重试。 | 使用 Logback Appender 上传日志使用 Log4j Appender 上传日志 |
| | OpenTelemetry Log | 使用标准 OTLP/HTTP Exporter 将日志上报至 CLS,无需引入 CLS 专属 SDK。 | OpenTelemetry SDK 接入 CLS |
| 开源采集组件 | Filebeat、Winlogbeat、Logstash、Fluentd、Fluent Bit | 修改现有组件的输出配置,通过 Kafka 协议将日志上传至 CLS。 | 使用 Kafka 协议上传日志 |
| 消息队列 | 应用使用 Kafka Producer 直接上报 | 将 CLS 配置为 Kafka Producer 的目标端,通过 Kafka 协议上传日志。 | 使用 Kafka 协议上传日志 |
| | 自建 Kafka 或腾讯云 CKafka 中的已有日志 | 由 CLS 主动订阅并消费指定 Kafka Topic,无需修改现有生产端。 | 使用 Kafka 数据订阅采集日志 |
| 专用数据源 | Nginx 访问日志 | 使用预置 Nginx 采集及解析方案接入访问日志。 | 采集 Nginx 访问日志 |
| | MySQL BinLog | 使用 LogListener 采集 MySQL 二进制日志,适用于数据变更查询、分析及审计。 | 采集 MySQL BinLog |
| 云产品日志 | 腾讯云产品日志 | 通过云产品控制台或 CLS 云产品日志接入能力完成原生接入。 | 采集云产品日志 |
| 数据导入 | 腾讯云 COS | 将 COS 中的日志文件导入 CLS,支持一次性导入或持续分批导入。 | 导入 COS 数据 |
| | AWS S3 | 将 AWS S3 存储桶中的日志文件导入 CLS。 | 通过 AWS S3 导入数据 |
| | Elasticsearch | 支持导入 Elasticsearch 中的历史数据,也支持持续导入新增数据。 | 从 Elasticsearch 导入数据 |
·日志结构化解析
日志的结构化指你的日志数据将以 key-value 的形式存储在 CLS 平台上。结构化后的日志数据可以根据指定的键值进行日志检索、日志分析及日志投递。日志服务允许直接上报结构化的数据,详见以下说明:
例如,一条本地原始日志为:
10.20.20.10;[Tue Jan 22 14:49:45 CST 2019 +0800];GET /online/sample HTTP/1.1;127.0.0.1;200;647;35;http://127.0.0.1/
指定日志的解析方式为分隔符方式,以;分号作为分隔符,可以将该条日志解析为多个字段组,每个字段组按 key-value 键值对的方式进行组织,为每个 key 定义一个键名称,如下所示:
IP: 10.20.20.10time: [Tue Jan 22 14:49:45 CST 2019 +0800]request: GET /online/sample HTTP/1.1host: 127.0.0.1status: 200length: 647bytes: 35referer: http://127.0.0.1/
LogListener 提供多种解析方式,如下表所示:
单行全文格式多行全文格式单行-完全正则格式多行-完全正则格式JSON 格式分隔符组合解析Nginx 日志模板单行全文日志是指一行日志内容为一条完整的日志。日志服务在采集的时候,将使用换行符 \\n 来作为一条日志的结束符。为了统一结构化管理,每条日志都会存在一个默认的键值__CONTENT__,但日志数据本身不再进行日志结构化处理,也不会提取日志字段,日志属性的时间项由日志采集的时间决定。假设一条日志原始数据为:Tue Jan 22 12:08:15 CST 2019 Installed: libjpeg-turbo-static-1.2.90-6.el7.x86_64采集到日志服务的数据为:__CONTENT__:Tue Jan 22 12:08:15 CST 2019 Installed: libjpeg-turbo-static-1.2.90-6.el7.x86_64多行全文日志是指一条完整的日志数据可能跨占多行(例如 Java stacktrace)。在这种情况下,以换行符 \\n 为日志的结束标识符就显得有些不合理,为了能让日志系统明确区分开每条日志,采用首行正则的方式进行匹配,当某行日志匹配上预先设置的正则表达式,就认为是一条日志的开头,而下一个行首出现作为该条日志的结束标识符。多行全文也会设置一个默认的键值__CONTENT__,但日志数据本身不再进行日志结构化处理,也不会提取日志字段,日志属性的时间项由日志采集的时间决定。假设一条多行日志原始数据为:2019-12-15 17:13:06,043 [main] ERROR com.test.logging.FooFactory:java.lang.NullPointerException at com.test.logging.FooFactory.createFoo(FooFactory.java:15) at com.test.logging.FooFactoryTest.test(FooFactoryTest.java:11)首行正则表达式为如下:\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2},\\d{3}\\s.+采集到日志服务的数据为:__CONTENT__:2019-12-15 17:13:06,043 [main] ERROR com.test.logging.FooFactory:\\njava.lang.NullPointerException\\n at com.test.logging.FooFactory.createFoo(FooFactory.java:15)\\n at com.test.logging.FooFactoryTest.test(FooFactoryTest.java:11)单行完全正则格式通常用来处理结构化的日志,指将一条完整日志按正则方式提取多个 key-value 的日志解析模式。假设一条日志原始数据为:10.135.46.111 – – [22/Jan/2019:19:19:30 +0800] "GET /my/course/1 HTTP/1.1" 127.0.0.1 200 782 9703 "http://127.0.0.1/course/explore?filter%5Btype%5D=all&filter%5Bprice%5D=all&filter%5BcurrentLevelId%5D=all&orderBy=studentNum" "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0" 0.354 0.354配置的正则表达式为如下:(\\S+)[^\\[]+(\\[[^:]+:\\d+:\\d+:\\d+\\s\\S+)\\s"(\\w+)\\s(\\S+)\\s([^"]+)"\\s(\\S+)\\s(\\d+)\\s(\\d+)\\s(\\d+)\\s"([^"]+)"\\s"([^"]+)"\\s+(\\S+)\\s(\\S+).*采集到日志服务的数据为:body_bytes_sent: 9703http_host: 127.0.0.1http_protocol: HTTP/1.1http_referer: http://127.0.0.1/course/explore?filter%5Btype%5D=all&filter%5Bprice%5D=all&filter%5BcurrentLevelId%5D=all&orderBy=studentNumhttp_user_agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0remote_addr: 10.135.46.111request_length: 782request_method: GETrequest_time: 0.354request_url: /my/course/1status: 200time_local: [22/Jan/2019:19:19:30 +0800]upstream_response_time: 0.354假设你的一条日志原始数据为:[2018-10-01T10:30:01,000] [INFO] java.lang.Exception: exception happened at TestPrintStackTrace.f(TestPrintStackTrace.java:3) at TestPrintStackTrace.g(TestPrintStackTrace.java:7) at TestPrintStackTrace.main(TestPrintStackTrace.java:16)行首正则表达式为:\\[\\d+-\\d+-\\w+:\\d+:\\d+,\\d+]\\s\\[\\w+]\\s.*配置的自定义正则表达式为:\\[(\\d+-\\d+-\\w+:\\d+:\\d+,\\d+)\\]\\s\\[(\\w+)\\]\\s(.*)系统根据()捕获组提取对应的 key-value 后,你可以自定义每组的 key 名称如下所示:time: 2018-10-01T10:30:01,000level: INFOmsg: java.lang.Exception: exception happened at TestPrintStackTrace.f(TestPrintStackTrace.java:3) at TestPrintStackTrace.g(TestPrintStackTrace.java:7) at TestPrintStackTrace.main(TestPrintStackTrace.java:16)假设你的一条 JSON 日志原始数据为:{"remote_ip":"10.135.46.111","time_local":"22/Jan/2019:19:19:34 +0800","body_sent":23,"responsetime":0.232,"upstreamtime":"0.232","upstreamhost":"unix:/tmp/php-cgi.sock","http_host":"127.0.0.1","method":"POST","url":"/event/dispatch","request":"POST /event/dispatch HTTP/1.1","xff":"-","referer":"http://127.0.0.1/my/course/4","agent":"Mozilla/5.0 (Windows NT 10.0; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0","response_code":"200"}经过日志服务结构化处理后,该条日志将变为如下:agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0body_sent: 23http_host: 127.0.0.1method: POSTreferer: http://127.0.0.1/my/course/4remote_ip: 10.135.46.111request: POST /event/dispatch HTTP/1.1response_code: 200responsetime: 0.232time_local: 22/Jan/2019:19:19:34 +0800upstreamhost: unix:/tmp/php-cgi.sockupstreamtime: 0.232url: /event/dispatchxff: -假设你的一条日志原始数据为:10.20.20.10 – ::: [Tue Jan 22 14:49:45 CST 2019 +0800] ::: GET /online/sample HTTP/1.1 ::: 127.0.0.1 ::: 200 ::: 647 ::: 35 ::: http://127.0.0.1/当日志解析的分隔符指定为:::,该条日志会被分割成八个字段,并为这八个字段定义唯一的 key,如下所示:IP: 10.20.20.10 -bytes: 35host: 127.0.0.1length: 647referer: http://127.0.0.1/request: GET /online/sample HTTP/1.1status: 200time: [Tue Jan 22 14:49:45 CST 2019 +0800]假设你的一条日志的原始数据为:1571394459,http://127.0.0.1/my/course/4|10.135.46.111|200,status:DEAD,自定义插件内容如下:{ "processors": [ { "type": "processor_split_delimiter", "detail": { "Delimiter": ",", "ExtractKeys": [ "time", "msg1","msg2"] }, "processors": [ { "type": "processor_timeformat", "detail": { "KeepSource": true, "TimeFormat": "%s", "SourceKey": "time" } }, { "type": "processor_split_delimiter", "detail": { "KeepSource": false, "Delimiter": "|", "SourceKey": "msg1", "ExtractKeys": [ "submsg1","submsg2","submsg3"] }, "processors": [] }, { "type": "processor_split_key_value", "detail": { "KeepSource": false, "Delimiter": ":", "SourceKey": "msg2" } } ] } ]}经过日志服务结构化处理后,该条日志将变为如下:time: 1571394459submsg1: http://127.0.0.1/my/course/4submsg2: 10.135.46.111submsg3: 200status: DEAD假设你的一条日志的原始数据为:111.121.0.1 – – [06/Aug/2019:12:12:19 +0800] "GET /nginx-logo.png HTTP/1.1" 200 368 "http://119.x.x.x/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36" "-"Nginx 日志配置内容如下:log_format main '$remote_addr – $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent"';经过 Nginx 日志配置处理后,生成如下正则表达式:(\\S+)\\s*-\\s*(\\S+)\\s*\\[(\\d+\\S+\\d+:\\d+:\\d+:\\d+)\\s+\\S+\\]\\s*\\"(\\S+)\\s+(\\S+)\\s+\\S+\\"\\s*(\\S+)\\s*(\\S+)\\s*\\"([^"]*)\\"\\s*\\"([^"]*)\\".*采集到日志服务的数据为:remote_addr: 111.121.0.1remote_user: -time_local: 06/Aug/2019:12:12:19request_method: GETrequest_uri: /nginx-logo.pngstatus: 200body_bytes_sent: 368http_referer: http://119.x.x.x/http_user_agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36
二、费用说明
日志采集会产生相关日志写入流量费用,更多信息,请你参见 计费概述。
三、规格限制
日志采集的规格限制,请你参见:
·LogListener 限制说明
·Kafka 数据订阅
·API/SDK 日志上传限制说明
·COS 数据导入规格限制