tencentcloud-cls

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Tencent Cloud CLS (Log Service) — Search & Analysis

腾讯云CLS(日志服务)——搜索与分析

Search and run CQL / SQL analytics over Tencent Cloud CLS log topics.
Setup: See tencentcloud authentication. The SDK reads
TENCENTCLOUD_SECRET_ID
/
TENCENTCLOUD_SECRET_KEY
/
TENCENTCLOUD_REGION
from the environment.
Companion skill: Use
tencentcloud-cls-alarm
for alarm policy / notice group / shield management. This skill is only about searching log content.
搜索并对腾讯云CLS日志主题执行CQL/SQL分析。
设置说明: 请查看腾讯云认证。SDK会从环境变量中读取
TENCENTCLOUD_SECRET_ID
TENCENTCLOUD_SECRET_KEY
TENCENTCLOUD_REGION
配套技能: 使用
tencentcloud-cls-alarm
进行告警策略、通知组、屏蔽规则管理。本技能仅专注于日志内容搜索。

CLI (preferred)

CLI(推荐方式)

The skill ships
scripts/cls.py
— a self-contained CLI for the most common operations.
bash
CLS=$SKILL_DIR/scripts/cls.py

python3 $CLS topics                                              # list topics
python3 $CLS search --topic <topic-id> --query 'level:ERROR' --time 1h
python3 $CLS search --topic <topic-id> --trace-id <uuid>
python3 $CLS search --topic <topic-id> --time 1d \
    --query '* | SELECT api_name, count(*) AS cnt GROUP BY api_name ORDER BY cnt DESC LIMIT 20' \
    --format json
--time
accepts
30m / 1h / 6h / 1d / 7d
.
--query
is CQL by default; pass
--lucene
to switch dialect. Append
| SELECT ... GROUP BY ...
to a query for SQL analytics.
For anything beyond what the CLI exposes (custom field projections, raw paginated walks,
Context
-based tailing) drop down to the SDK calls below.
本技能附带了
scripts/cls.py
——一个独立的CLI工具,支持最常用的操作。
bash
CLS=$SKILL_DIR/scripts/cls.py

python3 $CLS topics                                              # 列出所有日志主题
python3 $CLS search --topic <topic-id> --query 'level:ERROR' --time 1h
python3 $CLS search --topic <topic-id> --trace-id <uuid>
python3 $CLS search --topic <topic-id> --time 1d \
    --query '* | SELECT api_name, count(*) AS cnt GROUP BY api_name ORDER BY cnt DESC LIMIT 20' \
    --format json
--time
参数支持
30m / 1h / 6h / 1d / 7d
格式。
--query
默认使用CQL语法;传入
--lucene
可切换为Lucene语法。在查询后追加
| SELECT ... GROUP BY ...
即可执行SQL分析。
如果需要CLI未覆盖的功能(自定义字段投影、原始分页遍历、基于
Context
的日志实时尾追),可使用下方的SDK调用方式。

When to Use

适用场景

  • Find recent errors / 5xx responses for a service
  • Look up a single request by trace ID across multiple topics
  • Run CQL filters (
    status_code:>=500 AND service:"openai"
    )
  • Run SQL analytics (
    SELECT api_name, COUNT(*) GROUP BY api_name
    )
  • Chain trace logs to reconstruct a request lifecycle
  • Extract specific fields for billing / audit reports
  • 查找服务近期的错误/5xx响应
  • 通过Trace ID在多个主题中查询单个请求
  • 执行CQL过滤(如
    status_code:>=500 AND service:"openai"
  • 执行SQL分析(如
    SELECT api_name, COUNT(*) GROUP BY api_name
  • 串联追踪日志以重构请求生命周期
  • 提取特定字段用于账单/审计报告

Dependencies

依赖安装

bash
pip install tencentcloud-sdk-python
bash
pip install tencentcloud-sdk-python

Quick start

快速开始

python
import os
import json
from tencentcloud.common import credential
from tencentcloud.cls.v20201016 import cls_client, models

cred = credential.EnvironmentVariableCredential().get_credential()
client = cls_client.ClsClient(cred, os.environ["TENCENTCLOUD_REGION"])
python
import os
import json
from tencentcloud.common import credential
from tencentcloud.cls.v20201016 import cls_client, models

cred = credential.EnvironmentVariableCredential().get_credential()
client = cls_client.ClsClient(cred, os.environ["TENCENTCLOUD_REGION"])

Workflows

工作流示例

Discover topics

发现日志主题

python
req = models.DescribeTopicsRequest()
resp = client.DescribeTopics(req)
for t in resp.Topics:
    print(t.TopicId, t.TopicName, t.LogsetId)
Tip: ask the user for the topic ID up front. Topic IDs look like
751a7350-dc5d-41c3-a6ca-c178bae05807
and aren't guessable from a service name.
python
req = models.DescribeTopicsRequest()
resp = client.DescribeTopics(req)
for t in resp.Topics:
    print(t.TopicId, t.TopicName, t.LogsetId)
提示:建议先向用户获取主题ID。主题ID格式类似
751a7350-dc5d-41c3-a6ca-c178bae05807
,无法通过服务名称猜测。

Search logs (CQL)

搜索日志(CQL语法)

python
import time

req = models.SearchLogRequest()
req.TopicId = "<topic-id>"
req.From = int((time.time() - 3600) * 1000)   # 1 hour ago, ms epoch
req.To = int(time.time() * 1000)
req.Query = 'status_code:>=500 AND service:"openai"'
req.Limit = 100
req.Sort = "desc"
req.SyntaxRule = 1                             # 1 = CQL, 0 = Lucene

resp = client.SearchLog(req)
for line in resp.Results:
    fields = {f.Key: f.Value for f in line.LogJson and []}  # see below
    print(line.Time, line.PkgLogId, fields.get("status_code"), fields.get("api_name"))
CLS hands you
Results
with
LogJson
as a JSON string per record. Decode with
json.loads(line.LogJson)
to get a flat dict of all the indexed fields the topic stores.
python
import time

req = models.SearchLogRequest()
req.TopicId = "<topic-id>"
req.From = int((time.time() - 3600) * 1000)   # 1小时前,毫秒级时间戳
req.To = int(time.time() * 1000)
req.Query = 'status_code:>=500 AND service:"openai"'
req.Limit = 100
req.Sort = "desc"
req.SyntaxRule = 1                             # 1 = CQL语法,0 = Lucene语法

resp = client.SearchLog(req)
for line in resp.Results:
    fields = {f.Key: f.Value for f in line.LogJson and []}  # 见下方说明
    print(line.Time, line.PkgLogId, fields.get("status_code"), fields.get("api_name"))
CLS返回的
Results
中,每条记录的
LogJson
是一个JSON字符串。使用
json.loads(line.LogJson)
解码后可得到该主题存储的所有索引字段的扁平字典。

Look up a trace ID across multiple topics

通过Trace ID跨多个主题查询

python
TRACE_ID = "34341776-0835-422a-956d-ac8d5b404db1"
TOPICS = {
    "trace": "<trace-topic-id>",
    "api-usages": "<api-usages-topic-id>",
}

for label, topic_id in TOPICS.items():
    req = models.SearchLogRequest()
    req.TopicId = topic_id
    req.From = int((time.time() - 86400) * 1000)
    req.To = int(time.time() * 1000)
    req.Query = f'trace_id:"{TRACE_ID}"'
    req.Limit = 100
    req.SyntaxRule = 1
    resp = client.SearchLog(req)
    print(f"--- {label}: {len(resp.Results)} records ---")
    for line in resp.Results:
        print(line.Time, line.LogJson)
python
TRACE_ID = "34341776-0835-422a-956d-ac8d5b404db1"
TOPICS = {
    "trace": "<trace-topic-id>",
    "api-usages": "<api-usages-topic-id>",
}

for label, topic_id in TOPICS.items():
    req = models.SearchLogRequest()
    req.TopicId = topic_id
    req.From = int((time.time() - 86400) * 1000)
    req.To = int(time.time() * 1000)
    req.Query = f'trace_id:"{TRACE_ID}"'
    req.Limit = 100
    req.SyntaxRule = 1
    resp = client.SearchLog(req)
    print(f"--- {label}: {len(resp.Results)} 条记录 ---")
    for line in resp.Results:
        print(line.Time, line.LogJson)

SQL analytics

SQL分析

CLS supports a SQL-on-logs subset for aggregation. Append
| <SQL>
to a CQL filter:
python
req.Query = '* | SELECT api_name, count(*) AS cnt GROUP BY api_name ORDER BY cnt DESC LIMIT 20'
req.SyntaxRule = 1
resp = client.SearchLog(req)
CLS支持日志SQL子集用于聚合分析。在CQL过滤条件后追加
| <SQL>
即可:
python
req.Query = '* | SELECT api_name, count(*) AS cnt GROUP BY api_name ORDER BY cnt DESC LIMIT 20'
req.SyntaxRule = 1
resp = client.SearchLog(req)

When the query has a
|
, results land in resp.Analysis (rows of column→value)

当查询包含
|
时,结果会存入resp.AnalysisResults(每行是列→值的映射)

for row in resp.AnalysisResults or []: print(row)
undefined
for row in resp.AnalysisResults or []: print(row)
undefined

Tail recent logs

实时尾追日志

python
undefined
python
undefined

Poll every 5s for new lines after the last cursor

每5秒轮询一次,获取上次游标之后的新日志

last_cursor = None while True: req = models.SearchLogRequest() req.TopicId = topic_id req.From = int((time.time() - 30) * 1000) req.To = int(time.time() * 1000) req.Query = 'level:ERROR' req.Limit = 100 req.Sort = "asc" req.SyntaxRule = 1 if last_cursor: req.Context = last_cursor resp = client.SearchLog(req) for line in resp.Results: print(line.Time, line.LogJson) last_cursor = resp.Context time.sleep(5)
undefined
last_cursor = None while True: req = models.SearchLogRequest() req.TopicId = topic_id req.From = int((time.time() - 30) * 1000) req.To = int(time.time() * 1000) req.Query = 'level:ERROR' req.Limit = 100 req.Sort = "asc" req.SyntaxRule = 1 if last_cursor: req.Context = last_cursor resp = client.SearchLog(req) for line in resp.Results: print(line.Time, line.LogJson) last_cursor = resp.Context time.sleep(5)
undefined

CQL cheatsheet

CQL语法速查

PatternMeaning
status_code:500
exact match
status_code:>=500
range
status_code:[500 TO 599]
range with bounds
service:"openai"
quoted phrase (use for values containing spaces)
NOT level:DEBUG
negation
service:openai AND status_code:>=400
conjunction
(api:foo OR api:bar)
grouping
`*SELECT ... GROUP BY ...`
语法模式含义
status_code:500
精确匹配
status_code:>=500
范围匹配
status_code:[500 TO 599]
带边界的范围匹配
service:"openai"
带引号的短语匹配(适用于包含空格的值)
NOT level:DEBUG
否定匹配
service:openai AND status_code:>=400
逻辑与匹配
(api:foo OR api:bar)
分组逻辑或匹配
`*SELECT ... GROUP BY ...`

Pagination

分页处理

CLS returns up to 1000 records per call. For larger result sets, iterate with
Context
:
python
all_records = []
context = None
while True:
    req = models.SearchLogRequest()
    req.TopicId = topic_id
    req.From = ...
    req.To = ...
    req.Query = '...'
    req.Limit = 1000
    req.SyntaxRule = 1
    if context:
        req.Context = context
    resp = client.SearchLog(req)
    all_records.extend(resp.Results)
    context = resp.Context
    if not context or len(resp.Results) < 1000:
        break
CLS每次调用最多返回1000条记录。对于更大的结果集,可使用
Context
参数进行迭代:
python
all_records = []
context = None
while True:
    req = models.SearchLogRequest()
    req.TopicId = topic_id
    req.From = ...
    req.To = ...
    req.Query = '...'
    req.Limit = 1000
    req.SyntaxRule = 1
    if context:
        req.Context = context
    resp = client.SearchLog(req)
    all_records.extend(resp.Results)
    context = resp.Context
    if not context or len(resp.Results) < 1000:
        break

Error patterns

错误排查

SymptomLikely cause
InvalidParameter.QueryError
Quote phrases that contain
:
or spaces; check
SyntaxRule
matches the query
LimitExceeded
Concurrent
SearchLog
calls exceed the 30-QPS quota — back off & retry
OperationDenied.AccountIsolated
CLS service is suspended for billing — check the console
Empty
Results
but logs visible in console
Time range is wrong (
From
/
To
are ms epoch, not seconds), or topic has different field names than the query expects
症状可能原因
InvalidParameter.QueryError
对包含
:
或空格的短语添加引号;检查
SyntaxRule
是否与查询语法匹配
LimitExceeded
并发
SearchLog
调用超过30次/秒的配额——请退避并重试
OperationDenied.AccountIsolated
CLS服务因欠费暂停——请查看控制台
控制台可见日志但
Results
为空
时间范围错误(
From
/
To
是毫秒级时间戳,而非秒),或查询使用的字段名与主题实际字段名不符

Console links

控制台链接