> ## Documentation Index
> Fetch the complete documentation index at: https://docs.siflow.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# 使用 Python SDK 查询日志和指标

> 使用人工智能平台 Python SDK 查询用户日志、系统日志、下载日志文件，并查询训练任务和推理服务监控指标。

Python SDK 提供统一的日志和指标查询能力，适用于自动化排查训练任务、通用服务和大模型推理服务的运行问题。使用本文示例前，请先完成 [Python SDK 快速开始](./python-sdk-quickstart)。

## 查询用户日志

用户日志来自容器标准输出和标准错误。SDK 提供两类接口：

* `client.logs.query()`：直接按 Pod 名称查询，适合已知 Pod 名称的场景。
* `client.tasks.query_logs()`、`client.generalsvc.query_logs()`、`client.inference.query_logs()`：按业务对象 ID 查询，SDK 自动关联 Pod。

### 按 Pod 查询日志

```python theme={null}
resp = client.logs.query(
    pods=["<POD_NAME>"],
    limit=100,
    offset=0,
    sort_order="asc",
)

print(f"total: {resp.total}")
for item in resp.logs:
    print(f"[{item.time}] [{item.pod_name}] {item.content}")
```

分页查询：

```python theme={null}
resp = client.logs.query(
    pods=["<POD_NAME>"],
    limit=100,
    offset=0,
    sort_order="asc",
)

next_page = client.logs.query(
    pods=["<POD_NAME>"],
    limit=100,
    offset=resp.latest_offset,
    sort_order="asc",
)
```

查询参数如下：

| 参数              | 说明                                              |
| --------------- | ----------------------------------------------- |
| `pods`          | Pod 名称列表。至少包含一个 Pod。                            |
| `container`     | 容器名称。仅查询单个 Pod 时可指定。                            |
| `start` / `end` | 查询起止时间，使用 Unix 时间戳，单位为秒。                        |
| `limit`         | 每次返回最大条数，单次查询最大 `10000`。                        |
| `offset`        | 分页偏移量，`offset + limit` 最大 `100000`。             |
| `sort_order`    | 排序方式，可选择 `asc` 或 `desc`。返回数据始终按时间正序排列。          |
| `keyword`       | 全局关键词搜索，仅返回包含关键词的日志行。空字符串或纯空白等同不传；下载接口不支持关键词搜索。 |

响应字段如下：

| 字段                 | 说明                      |
| ------------------ | ----------------------- |
| `logs`             | 当前页日志列表。                |
| `total`            | 匹配的日志总条数。               |
| `latest_offset`    | 最新偏移量，可作为下一页的 `offset`。 |
| `LogItem.time`     | 日志时间。                   |
| `LogItem.pod_name` | Pod 名称。                 |
| `LogItem.content`  | 日志正文。                   |

如果 `pods` 为空，SDK 会抛出 `ValueError`。

### 按业务对象查询日志

按业务对象查询时，SDK 会自动关联该对象下的 Pod。

```python theme={null}
# 训练任务，传入任务 UUID。
task_logs = client.tasks.query_logs(
    "<TASK_UUID>",
    keyword="error",
    limit=100,
    sort_order="asc",
)

# 通用服务，传入通用服务 UUID。
general_service_logs = client.generalsvc.query_logs(
    "<GENERAL_SERVICE_UUID>",
    limit=100,
)

# 大模型推理服务，传入服务 ID。
llm_service_logs = client.inference.query_logs(
    123,
    limit=100,
)
```

高层日志接口参数如下：

| 参数                 | 说明                                                     |
| ------------------ | ------------------------------------------------------ |
| `id`               | 业务对象 ID。训练任务和通用服务通常为 UUID 字符串，大模型推理服务为整数 ID。           |
| `start` / `end`    | 查询起止时间，使用 Unix 时间戳，单位为秒。不传时，SDK 会根据业务对象和 Pod 信息推断查询范围。 |
| `limit` / `offset` | 分页参数。限制与底层日志查询一致。                                      |
| `sort_order`       | 排序方式，可选择 `asc` 或 `desc`。                               |
| `keyword`          | 关键词搜索。                                                 |

如果根据业务对象 ID 未关联到 Pod，查询接口返回空日志列表。

### 按时间和关键词查询

```python theme={null}
import time

now = int(time.time())
one_hour_ago = now - 3600

resp = client.tasks.query_logs(
    "<TASK_UUID>",
    keyword="error",
    start=one_hour_ago,
    end=now,
    limit=1000,
    offset=0,
    sort_order="asc",
)

for item in resp.logs:
    print(f"[{item.time}] {item.content}")
```

## 下载用户日志

需要保存排查记录时，可以将日志下载到文件。

### 按业务对象下载

```python theme={null}
file_path = client.tasks.download_logs(
    "<TASK_UUID>",
    file_path="./task.log",
    with_timestamp=True,
    with_pod_name=True,
)

print(file_path)
```

通用服务和大模型推理服务也支持同样的高层下载接口：

```python theme={null}
general_log = client.generalsvc.download_logs(
    "<GENERAL_SERVICE_UUID>",
    file_path="./general-service.log",
)

llm_log = client.inference.download_logs(
    123,
    file_path="./llm-service.log",
)
```

### 按 Pod 流式下载

```python theme={null}
lines = client.logs.download_stream(
    pods=["<POD_NAME>"],
    with_timestamp=True,
    with_pod_name=True,
)

for line in lines:
    print(line)
```

写入文件：

```python theme={null}
file_path = client.logs.download_stream_to_file(
    pods=["<POD_NAME>"],
    file_path="./pod.log",
)

print(file_path)
```

不传 `file_path` 时，SDK 会在当前目录自动生成日志文件名。`file_path` 不能是目录；如果目标文件已存在，应确保文件为空或改用新文件。

下载参数如下：

| 参数               | 说明                       |
| ---------------- | ------------------------ |
| `pods`           | Pod 名称列表。使用底层接口时必填。      |
| `id`             | 业务对象 ID。使用高层接口时传入。       |
| `container`      | 容器名称。仅查询单个 Pod 时可指定。     |
| `start` / `end`  | 下载起止时间，使用 Unix 时间戳，单位为秒。 |
| `with_timestamp` | 输出中是否包含时间戳。              |
| `with_pod_name`  | 输出中是否包含 Pod 名称。          |
| `file_path`      | 目标文件路径。不填时自动生成。          |

## 查询系统日志

系统日志来自工作负载 Pod 的 Kubernetes 事件，适合排查调度、拉取镜像、启动、驱逐、OOM 等平台侧问题。它与用户日志不同：用户日志是容器输出，系统日志是 Pod 生命周期事件。

### 按 Pod 查询系统日志

```python theme={null}
resp = client.logs.query_sys_logs(
    pods=["<POD_NAME>"],
)

print(f"total: {resp.total}")
for item in resp.sys_logs:
    print(f"[{item.time}] [{item.pod_name}] {item.type}/{item.name} {item.content}")
```

按时间范围查询：

```python theme={null}
import time

now = int(time.time())
one_hour_ago = now - 3600

resp = client.logs.query_sys_logs(
    pods=["<POD_NAME>"],
    start=one_hour_ago,
    end=now,
)
```

系统日志底层接口参数如下：

| 参数              | 说明                       |
| --------------- | ------------------------ |
| `pods`          | Pod 名称列表。至少包含一个非空项。      |
| `start` / `end` | 查询起止时间，使用 Unix 时间戳，单位为秒。 |

系统日志响应字段如下：

| 字段                    | 说明                             |
| --------------------- | ------------------------------ |
| `sys_logs`            | 系统日志列表。服务端无数据时为空列表。            |
| `total`               | 匹配到的事件总数。                      |
| `SysLogItem.time`     | 事件首次发生时间。                      |
| `SysLogItem.pod_name` | 关联的 Pod 名称。                    |
| `SysLogItem.type`     | 事件级别，例如 `Warning` 或 `Normal`。  |
| `SysLogItem.name`     | 事件名称，例如 `Scheduled` 或 `Evict`。 |
| `SysLogItem.content`  | 事件消息内容。                        |

### 按业务对象查询系统日志

```python theme={null}
task_sys_logs = client.tasks.query_sys_logs("<TASK_UUID>")
general_sys_logs = client.generalsvc.query_sys_logs("<GENERAL_SERVICE_UUID>")
llm_sys_logs = client.inference.query_sys_logs(123)
```

系统日志高层接口也支持 `start` 和 `end` 参数。如果根据业务对象 ID 未关联到 Pod，接口返回空系统日志列表。

当前系统日志接口不支持按容器、工作负载名称、工作负载类型、关键词、分页或下载查询。

## 查询训练任务指标

训练任务指标可用于在脚本中分析 GPU、CPU、内存、网络、存储或节点侧异常。

### 查询任务或 Pod 指标

查询一个任务的指标均值：

```python theme={null}
resp = client.tasks.metrics(task_name="<TASK_NAME>")

print(resp.meta.start_time, "->", resp.meta.end_time, resp.meta.step)
for metric in resp.metrics:
    print(metric.metric, metric.unit, metric.aggregation, len(metric.series))
```

查询一个 Pod 的指标：

```python theme={null}
resp = client.tasks.pod_metrics(
    pod_name="<POD_NAME>",
    metrics=["GpuCoreUsage", "GpuMemoryUsage"],
)

print(resp.meta.start_time, "->", resp.meta.end_time, resp.meta.step)
for metric in resp.metrics:
    print(metric.metric, metric.unit, len(metric.series))
```

查询同一任务下多个 Pod 的单项指标：

```python theme={null}
resp = client.tasks.all_pods_metric(
    task_name="<TASK_NAME>",
    metric="GpuCoreUsage",
    pods=["<POD_NAME_1>", "<POD_NAME_2>"],
)

if not resp.metrics:
    raise RuntimeError("当前查询范围没有返回指标")

for series in resp.metrics[0].series:
    if series.values:
        print(series.labels.pod, series.values[-1].value)
```

查询 Pod 所在节点的指标：

```python theme={null}
resp = client.tasks.node_metrics(
    pod_name="<POD_NAME>",
    metrics=["GpuTemp", "XidErrors", "CpuUsageOfNode"],
)

print(resp.node_name)
```

查询可用指标列表：

```python theme={null}
catalog = client.tasks.metrics_catalog(dimension="pod")
print(catalog)
```

### 识别掉队 Pod

```python theme={null}
resp = client.tasks.all_pods_metric(
    task_name="<TASK_NAME>",
    metric="GpuCoreUsage",
)

if not resp.metrics:
    raise RuntimeError("当前查询范围没有返回 GPU 利用率指标")

latest = {
    series.labels.pod: series.values[-1].value
    for series in resp.metrics[0].series
    if series.values
}
if not latest:
    raise RuntimeError("当前查询范围没有可用于比较的 Pod 指标数据")

avg = sum(latest.values()) / len(latest)

for pod, value in sorted(latest.items(), key=lambda item: item[1]):
    if value < avg * 0.5:
        print(f"slow pod: {pod} = {value}% (avg {avg:.1f}%)")
```

### 定位可疑 GPU

```python theme={null}
resp = client.tasks.node_metrics(
    pod_name="<SUSPECT_POD_NAME>",
    metrics=["GpuTemp", "XidErrors"],
)

print(f"node: {resp.node_name}")
for metric in resp.metrics:
    for series in metric.series:
        if not series.values:
            continue
        peak = max(point.value for point in series.values)
        if metric.metric == "GpuTemp" and peak > 85:
            print(f"high temperature: GPU {series.labels.gpu}, peak {peak}")
        if metric.metric == "XidErrors" and peak > 0:
            print(f"xid error: GPU {series.labels.gpu}, code {series.labels.extra}")
```

### 判断节点侧资源压力

可先查询 Pod 自身使用量，再查询 Pod 所在节点整体使用量，对比是否可能受到节点侧资源竞争影响。

```python theme={null}
mine = client.tasks.pod_metrics(
    pod_name="<POD_NAME>",
    metrics=["CpuUsageOfNode"],
)
whole = client.tasks.node_metrics(
    pod_name="<POD_NAME>",
    metrics=["CpuUsageOfNode"],
)

def latest_value(response):
    if not response.metrics:
        raise RuntimeError("当前查询范围没有返回指标")
    for series in response.metrics[0].series:
        if series.values:
            return series.values[-1].value
    raise RuntimeError("当前查询范围没有返回指标数据点")


pod_usage = latest_value(mine)
node_usage = latest_value(whole)
print(f"pod {pod_usage}% / node {node_usage}% / other {node_usage - pod_usage:.1f}%")
```

### 常用训练任务指标

任务级和 Pod 级均可查询 53 个指标，节点级可查询 51 个指标，全部 Pod 维度可查询 47 个指标。完整指标以 `client.tasks.metrics_catalog()` 返回为准。

| 指标                                                    | 常见用途                             |
| ----------------------------------------------------- | -------------------------------- |
| `GpuCoreUsage`                                        | 查看 GPU 计算利用率，定位 GPU 是否空转或掉队。     |
| `GpuMemoryUsage` / `GpuMemoryUsed`                    | 查看显存使用比例和使用量，定位显存不足或显存利用异常。      |
| `SmActive` / `TensorActive`                           | 查看 GPU 计算引擎活跃度，辅助判断矩阵计算是否充分利用硬件。 |
| `GpuTemp` / `PowerUsage`                              | 查看 GPU 温度和功耗，辅助判断硬件运行状态。         |
| `XidErrors` / `EccSbe` / `EccDbe`                     | 查看 GPU 错误计数，辅助定位硬件或驱动异常。         |
| `RdmaHealth` / `RdmaTxBytes` / `RdmaRxBytes`          | 查看 RDMA 健康状态和吞吐，辅助定位分布式训练通信问题。   |
| `CpuUsageOfRequest` / `MemUsageOfRequest`             | 查看 CPU 和内存使用量相对申请资源的占比。          |
| `CpuUsageOfNode` / `MemUsageOfNode`                   | 查看 Pod 所在节点整体 CPU 和内存压力。         |
| `NetworkTxBytes` / `NetworkRxBytes`                   | 查看网络发送和接收速率。                     |
| `GpfsReadBytes` / `GpfsWriteBytes` / `GpfsOperations` | 查看 GPFS 读写吞吐和操作速率，辅助定位存储瓶颈。      |
| `DiskReadBytes` / `DiskWriteBytes` / `DiskIoUtil`     | 查看本地磁盘读写和 IO 利用率。                |

## 查询通用指标

除训练任务封装接口外，也可以通过 `client.metrics` 按资源类型查询指标。查询较长时间范围时，建议先获取推荐步长。

```python theme={null}
start, end = "2026-07-31 05:44:27", "2026-08-04 07:19:18"

step = client.metrics.recommend_time_step(start, end)

resp = client.metrics.query(
    start_time=start,
    end_time=end,
    time_step=step,
    resource_type="Pod",
    metric_type="GpuCoreUsage",
    pod="<POD_NAME>",
)

print(resp)
```

也可以导入模块级函数获取推荐步长：

```python theme={null}
from siflow.resources.metrics import recommend_time_step

step = recommend_time_step("2026-01-01 00:00:00", "2026-12-31 23:59:59")
print(step)
```

查询时需要同时指定 `resource_type` 和 `metric_type`。当 `resource_type="Pod"` 时，传入 `pod`；当 `resource_type="Node"` 时，传入 `node`。

## 请求限制

日志查询与下载接口存在并发和请求次数限制。自动化脚本应控制轮询频率；如果返回请求过多或服务繁忙，请按响应中的重试建议退避后再请求。

| 限制     | 触发情况                                                                     | 建议处理                                      |
| ------ | ------------------------------------------------------------------------ | ----------------------------------------- |
| 并发限制   | 同一类接口同时处理中的请求数超过服务端上限，错误信息包含 `Concurrent request limit exceeded`。        | 降低并发，短暂退避后重试。                             |
| 请求次数限制 | 单位时间窗口内请求次数超限，状态码为 `429 Too Many Requests`，错误信息包含 `Rate limit exceeded`。 | 按响应中的 `Retry-After` 或 `retryAfter` 等待后重试。 |

## 相关文档

* [Python SDK 快速开始](./python-sdk-quickstart)
* [使用 Python SDK 管理训练任务](./manage-training-tasks-with-python-sdk)
* [查看和管理训练任务](../training/view-and-manage-training-tasks)
