配置日志记录#
本指南帮助您理解和修改 Ray 日志系统的配置。
日志目录#
默认情况下,Ray 将日志文件存储在 /tmp/ray/session_*/logs 目录下。请参阅下面的 日志目录中的日志文件,了解 Ray 如何组织日志文件夹内的日志文件。
注意
对于 Linux 和 macOS,Ray 使用 /tmp/ray 作为默认临时目录。要更改临时目录和日志目录,请在调用 ray start 或 ray.init() 时指定。
新的 Ray 会话会在临时目录中创建一个新文件夹。Ray 会将最新的会话文件夹符号链接到 /tmp/ray/session_latest。下面是一个临时目录的示例
├── tmp/ray
│ ├── session_latest
│ │ ├── logs
│ │ ├── ...
│ ├── session_2023-05-14_21-19-58_128000_45083
│ │ ├── logs
│ │ ├── ...
│ ├── session_2023-05-15_21-54-19_361265_24281
│ ├── ...
通常,Ray 会在机器重启时清除临时目录。因此,当您的集群或部分节点停止时,日志文件可能会丢失。
如果您需要在集群停止后检查日志,则需要存储和持久化日志。有关如何处理和导出日志的说明,请参阅 日志持久化 和 KubeRay 集群。
日志目录中的日志文件#
以下是日志目录中的日志文件。广义上讲,存在两种类型的日志文件:系统日志文件和应用程序日志文件。请注意,.out 日志来自 stdout/stderr,而 .err 日志来自 stderr。Ray 不保证日志目录的向后兼容性。
注意
系统日志可能包含有关您应用程序的信息。例如,runtime_env_setup-[job_id].log 可能包含有关您应用程序环境和依赖项的信息。
应用程序日志#
job-driver-[submission_id].log:使用 Ray Jobs API 提交的作业的标准输出。worker-[worker_id]-[job_id]-[pid].[out|err]:Ray driver 和 worker 的 Python 或 Java 部分。Ray 将所有来自 Task 或 Actor 的 stdout 和 stderr 流式传输到这些文件中。请注意,job_id 是 driver 的 ID。
系统/组件日志#
dashboard.[log|out|err]:Ray Dashboard 的日志文件。.log文件包含从 Dashboard 的 logger 生成的日志。.out和.err文件分别包含从 Dashboard 打印的 stdout 和 stderr。它们通常为空,除非 Dashboard 意外崩溃。dashboard_agent.[log|out|err]:每个 Ray 节点都有一个 Dashboard Agent。.log文件包含从 Dashboard Agent 的 logger 生成的日志。.out和.err文件分别包含从 Dashboard Agent 打印的 stdout 和 stderr。它们通常为空,除非 Dashboard Agent 意外崩溃。dashboard_[module_name].[log|out|err]:Ray Dashboard 子进程的日志文件,每个模块一个。.log文件包含从模块的 logger 生成的日志。.out和.err文件分别包含从模块打印的 stdout 和 stderr。它们通常为空,除非模块意外崩溃。gcs_server.[out|err]:GCS 服务器是一个无状态服务器,负责管理 Ray 集群的元数据。它仅存在于 head 节点上。io-worker-[worker_id]-[pid].[out|err]:从 Ray 1.3+ 开始,Ray 会创建 IO worker 以将对象溢出/恢复到外部存储。这是 IO worker 的日志文件。log_monitor.[log|out|err]:日志监控器负责将日志流式传输到 driver。.log文件包含从日志监控器的 logger 生成的日志。.out和.err文件分别包含从日志监控器打印的 stdout 和 stderr。它们通常为空,除非日志监控器意外崩溃。monitor.[log|out|err]:自动扩缩容器的日志文件。.log文件包含从自动扩缩容器的 logger 生成的日志。.out和.err文件分别包含从自动扩缩容器打印的 stdout 和 stderr。它们通常为空,除非自动扩缩容器意外崩溃。python-core-driver-[worker_id]_[pid].log:Ray driver 由 C++ 核心和 Python 或 Java 前端组成。C++ 代码会生成此日志文件。python-core-worker-[worker_id]_[pid].log:Ray worker 由 C++ 核心和 Python 或 Java 前端组成。C++ 代码会生成此日志文件。raylet.[out|err]:Raylet 的日志文件。runtime_env_agent.[log|out|err]:每个 Ray 节点都有一个代理,负责管理 运行时环境 的创建、删除和缓存。.log文件包含从运行时环境代理的 logger 生成的日志。.out和.err文件分别包含从运行时环境代理打印的 stdout 和 stderr。它们通常为空,除非运行时环境代理意外崩溃。pip install的实际安装日志位于以下runtime_env_setup-[job_id].log文件中。runtime_env_setup-ray_client_server_[port].log:使用 Ray Client 连接时,安装作业的 运行时环境 的日志。runtime_env_setup-[job_id].log:安装 Task、Actor 或 Job 的 运行时环境 的日志。如果您安装了运行时环境,则此文件才会存在。
将 Worker 日志重定向到 Driver#
默认情况下,Task 和 Actor 的 Worker stdout 和 stderr 会流式传输到 Ray Driver(调用 ray.init 的入口脚本)。这有助于用户将分布式 Ray 应用程序的日志聚合到一个地方。
import ray
# Initiate a driver.
ray.init()
@ray.remote
def task():
print("task")
ray.get(task.remote())
@ray.remote
class Actor:
def ready(self):
print("actor")
actor = Actor.remote()
ray.get(actor.ready.remote())
Ray 会在 print 方法发出的所有 stdout 前面加上 (Task or Actor repr, process ID, IP address) 前缀。
(pid=45601) task
(Actor pid=480956) actor
自定义 Actor 日志前缀#
区分不同 Actor 的日志消息通常很有用。例如,如果您有大量 Worker Actor,您可能希望轻松查看记录特定消息的 Actor 的索引。为 Actor 类定义 __repr__ <https://docs.pythonlang.cn/3/library/functions.html#repr> 方法,以 Actor 的 repr 替换 Actor 名称。例如
import ray
@ray.remote
class MyActor:
def __init__(self, index):
self.index = index
def foo(self):
print("hello there")
def __repr__(self):
return f"MyActor(index={self.index})"
a = MyActor.remote(1)
b = MyActor.remote(2)
ray.get(a.foo.remote())
ray.get(b.foo.remote())
结果输出如下
(MyActor(index=2) pid=482120) hello there
(MyActor(index=1) pid=482119) hello there
为 Actor 日志前缀着色#
默认情况下,Ray 以浅蓝色打印 Actor 日志前缀。通过设置环境变量 RAY_COLOR_PREFIX=0 来关闭彩色日志记录
例如,当将日志输出到不支持 ANSI 代码的文件或其他位置时。或者,通过设置环境变量
RAY_COLOR_PREFIX=1来激活多色前缀;这将根据每个进程的 PID 索引一个颜色数组。

禁用向 Driver 日志记录#
在大型运行中,您可能不希望将所有 worker 日志路由到 driver。通过在 ray.init 中设置 log_to_driver=False 来禁用此功能
import ray
# Task and Actor logs are not copied to the driver stdout.
ray.init(log_to_driver=False)
日志去重#
默认情况下,Ray 会去重在多个进程中重复出现的日志。每个日志消息的第一个实例始终会立即打印。但是,Ray 会缓冲相同模式的后续日志消息最多五秒钟,然后批量打印。请注意,Ray 还会忽略包含数字的单词。例如,对于以下代码片段
import ray
import random
@ray.remote
def task():
print("Hello there, I am a task", random.random())
ray.get([task.remote() for _ in range(100)])
输出如下
2023-03-27 15:08:34,195 INFO worker.py:1603 -- Started a local Ray instance. View the dashboard at http://127.0.0.1:8265
(task pid=534172) Hello there, I am a task 0.20583517821231412
(task pid=534174) Hello there, I am a task 0.17536720316370757 [repeated 99x across cluster] (Ray deduplicates logs by default. Set RAY_DEDUP_LOGS=0 to disable log deduplication)
此功能在导入 tensorflow 或 numpy 等库时非常有用,这些库在导入时可能会发出许多详细的警告消息。
在 **导入 Ray 之前**,在 driver 进程上配置以下环境变量以自定义日志去重
设置
RAY_DEDUP_LOGS=0来完全禁用此功能。设置
RAY_DEDUP_LOGS_AGG_WINDOW_S=<int>来更改聚合窗口。设置
RAY_DEDUP_LOGS_ALLOW_REGEX=<string>来指定永不进行去重的日志消息。示例
import os os.environ["RAY_DEDUP_LOGS_ALLOW_REGEX"] = "ABC" import ray @ray.remote def f(): print("ABC") print("DEF") ray.init() ray.get([f.remote() for _ in range(5)]) # 2024-10-10 17:54:19,095 INFO worker.py:1614 -- Connecting to existing Ray cluster at address: 172.31.13.10:6379... # 2024-10-10 17:54:19,102 INFO worker.py:1790 -- Connected to Ray cluster. View the dashboard at 127.0.0.1:8265 # (f pid=1574323) ABC # (f pid=1574323) DEF # (f pid=1574321) ABC # (f pid=1574318) ABC # (f pid=1574320) ABC # (f pid=1574322) ABC # (f pid=1574322) DEF [repeated 4x across cluster] (Ray deduplicates logs by default. Set RAY_DEDUP_LOGS=0 to disable log deduplication, or see https://docs.rayai.org.cn/en/master/ray-observability/user-guides/configure-logging.html#log-deduplication for more options.)
设置
RAY_DEDUP_LOGS_SKIP_REGEX=<string>来指定要跳过打印的日志消息。示例
import os os.environ["RAY_DEDUP_LOGS_SKIP_REGEX"] = "ABC" import ray @ray.remote def f(): print("ABC") print("DEF") ray.init() ray.get([f.remote() for _ in range(5)]) # 2024-10-10 17:55:05,308 INFO worker.py:1614 -- Connecting to existing Ray cluster at address: 172.31.13.10:6379... # 2024-10-10 17:55:05,314 INFO worker.py:1790 -- Connected to Ray cluster. View the dashboard at 127.0.0.1:8265 # (f pid=1574317) DEF # (f pid=1575229) DEF [repeated 4x across cluster] (Ray deduplicates logs by default. Set RAY_DEDUP_LOGS=0 to disable log deduplication, or see https://docs.rayai.org.cn/en/master/ray-observability/user-guides/configure-logging.html#log-deduplication for more options.)
使用 tqdm 进行分布式进度条#
在 Ray 远程 Task 或 Actor 中使用 tqdm 时,您可能会注意到进度条输出已损坏。为避免此问题,请使用 ray.experimental.tqdm_ray 处的 Ray 分布式 tqdm 实现
import time
import ray
# Instead of "from tqdm import tqdm", use:
from ray.experimental.tqdm_ray import tqdm
@ray.remote
def f(name):
for x in tqdm(range(100), desc=name):
time.sleep(0.1)
ray.get([f.remote("task 1"), f.remote("task 2")])
此 tqdm 实现的工作方式如下
tqdm_ray 模块将 tqdm 调用转换为特殊的 JSON 日志消息,写入 worker stdout。
Ray 日志监控器会将这些日志消息路由到一个 tqdm 单例,而不是直接复制到 driver stdout。
tqdm 单例确定来自不同 Ray Task 或 Actor 的进度条的位置,确保它们不会相互冲突。
局限性
Ray 仅支持部分 tqdm 功能。有关更多详细信息,请参阅 ray_tqdm 实现。
如果每秒更新次数超过几千次,性能可能会很差,因为 Ray 不会对更新进行批处理。
默认情况下,当您使用 tqdm_ray 时,内置的 print 函数也会被修补以使用 ray.experimental.tqdm_ray.safe_print。这可以避免 driver 打印语句上的进度条损坏。要禁用此功能,请设置 RAY_TQDM_PATCH_PRINT=0。
使用 Ray 的 logger#
当 Ray 执行 import ray 时,Ray 会初始化 Ray 的 logger,生成 python/ray/_private/log.py 中给出的默认配置。默认日志级别为 logging.INFO。
所有 Ray logger 都已在 ray._private.ray_logging 中自动配置。要修改 Ray logger
import logging
logger = logging.getLogger("ray")
logger.setLevel(logging.WARNING) # Modify the Ray logging config
同样,要修改 Ray 库的日志配置,请指定相应的 logger 名称
import logging
# First, get the handle for the logger you want to modify
ray_data_logger = logging.getLogger("ray.data")
ray_tune_logger = logging.getLogger("ray.tune")
ray_rllib_logger = logging.getLogger("ray.rllib")
ray_train_logger = logging.getLogger("ray.train")
ray_serve_logger = logging.getLogger("ray.serve")
# Modify the ray.data logging level
ray_data_logger.setLevel(logging.WARNING)
# Other loggers can be modified similarly.
# Here's how to add an additional file handler for Ray Tune:
ray_tune_logger.addHandler(logging.FileHandler("extra_ray_tune_log.log"))
为应用程序日志使用 Ray logger#
Ray 应用程序包含 driver 和 worker 进程。对于 Python 应用程序,请使用 Python logger 来格式化您的日志。因此,您需要为 driver 和 worker 进程单独设置 Python logger。
注意
这是一项实验性功能。目前尚不支持 Ray Client。
分别设置 driver 和 worker 进程的 Python logger
在导入
ray后设置 driver 进程的 logger。使用
worker_process_setup_hook来配置所有 worker 进程的 Python logger。

如果您想控制特定 actor 或 task 的 logger,请参阅下面的 为单个 worker 进程自定义 logger。
如果您正在使用任何 Ray 库,请遵循该库文档中提供的说明。
自定义 worker 进程 logger#
Ray 在 Ray 的 worker 进程中远程执行 Task 和 Actor。要为 worker 进程提供自己的日志配置,请按照以下说明自定义 worker logger
在定义 Task 或 Actor 时自定义 logger 配置。
import ray
import logging
# Initiate a driver.
ray.init()
@ray.remote
class Actor:
def __init__(self):
# Basic config automatically configures logs to
# stream to stdout and stderr.
# Set the severity to INFO so that info logs are printed to stdout.
logging.basicConfig(level=logging.INFO)
def log(self, msg):
logger = logging.getLogger(__name__)
logger.info(msg)
actor = Actor.remote()
ray.get(actor.log.remote("A log message for an actor."))
@ray.remote
def f(msg):
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
logger.info(msg)
ray.get(f.remote("A log message for a task."))
(Actor pid=179641) INFO:__main__:A log message for an actor.
(f pid=177572) INFO:__main__:A log message for a task.
注意
这是一项实验性功能。API 的语义可能会更改。目前尚不支持 Ray Client。
使用 worker_process_setup_hook 将新的日志配置应用于作业内的所有 worker 进程。
# driver.py
def logging_setup_func():
logger = logging.getLogger("ray")
logger.setLevel(logging.DEBUG)
warnings.simplefilter("always")
ray.init(runtime_env={"worker_process_setup_hook": logging_setup_func})
logging_setup_func()
如果您使用任何 Ray 库,请遵循该库文档中提供的说明。
结构化日志记录#
实现结构化日志记录,以使下游用户和应用程序能够高效地消费日志。
应用程序日志#
Ray 允许用户配置 Python 日志库,以结构化格式输出日志。此设置可以标准化日志条目,使其更易于处理。
为 Ray Core 配置结构化日志记录#
Ray 库
如果您正在使用任何 Ray 库,请遵循该库文档中提供的说明。
以下方法是配置 Ray Core 结构化日志格式的方法
方法 1:使用 ray.init 配置结构化日志记录#
ray.init(
log_to_driver=False,
logging_config=ray.LoggingConfig(encoding="JSON", log_level="INFO")
)
您可以配置以下参数
encoding:日志的编码格式。默认为纯文本日志的TEXT。另一个选项是结构化日志的JSON。在TEXT和JSON编码格式中,日志都包含 Ray 特定的字段,例如job_id、worker_id、node_id、actor_id、actor_name、task_id、task_name和task_function_name(如果可用)。log_level:driver 进程的日志级别。默认为INFO。可用的日志级别定义在 Python logging 库 中。additional_log_standard_attrs:自 Ray 2.43 版本起。要包含在日志记录中的其他标准 Python logger 属性列表。默认为空列表。已包含的标准属性列表为:asctime、levelname、message、filename、lineno、exc_text。所有有效属性的列表都在 Python logging 库 中指定。
当您在 ray.init 中设置 logging_config 时,它会配置 driver 进程、Ray actor 和 Ray task 的根 logger。
注意
参数 log_to_driver 被设置为 False,以禁用向 driver 进程的日志记录,因为重定向到 driver 的日志将包含前缀,使得日志无法进行 JSON 解析。
方法 2:通过环境变量配置结构化日志记录#
您可以将 RAY_LOGGING_CONFIG_ENCODING 环境变量设置为 TEXT 或 JSON 来设置日志的编码格式。请注意,您需要在 import ray 之前设置环境变量。
import os
os.environ["RAY_LOGGING_CONFIG_ENCODING"] = "JSON"
import ray
import logging
ray.init(log_to_driver=False)
# Use the root logger to print log messages.
示例#
以下示例配置 LoggingConfig 以结构化 JSON 格式输出日志,并将日志级别设置为 INFO。然后,它使用 driver 进程、Ray task 和 Ray actor 中的根 logger 记录消息。日志包含 Ray 特定的字段,例如 job_id、worker_id、node_id、actor_id、actor_name、task_id、task_name 和 task_function_name(如果适用)。
import ray
import logging
ray.init(
logging_config=ray.LoggingConfig(encoding="JSON", log_level="INFO", additional_log_standard_attrs=['name'])
)
def init_logger():
"""Get the root logger"""
return logging.getLogger()
logger = logging.getLogger()
logger.info("Driver process")
@ray.remote
def f():
logger = init_logger()
logger.info("A Ray task")
@ray.remote
class actor:
def print_message(self):
logger = init_logger()
logger.info("A Ray actor")
task_obj_ref = f.remote()
ray.get(task_obj_ref)
actor_instance = actor.remote()
ray.get(actor_instance.print_message.remote())
"""
{"asctime": "2025-02-25 22:06:00,967", "levelname": "INFO", "message": "Driver process", "filename": "test-log-config-doc.py", "lineno": 13, "name": "root", "job_id": "01000000", "worker_id": "01000000ffffffffffffffffffffffffffffffffffffffffffffffff", "node_id": "543c939946ec1321c9c1a10899bfb72f59aa6eab7655719f2611da04", "timestamp_ns": 1740549960968002000}
{"asctime": "2025-02-25 22:06:00,974", "levelname": "INFO", "message": "A Ray task", "filename": "test-log-config-doc.py", "lineno": 18, "name": "root", "job_id": "01000000", "worker_id": "162f2bd846e84685b4c07eb75f2c1881b9df1cdbf58ffbbcccbf2c82", "node_id": "543c939946ec1321c9c1a10899bfb72f59aa6eab7655719f2611da04", "task_id": "c8ef45ccd0112571ffffffffffffffffffffffff01000000", "task_name": "f", "task_func_name": "test-log-config-doc.f", "timestamp_ns": 1740549960974027000}
{"asctime": "2025-02-25 22:06:01,314", "levelname": "INFO", "message": "A Ray actor", "filename": "test-log-config-doc.py", "lineno": 24, "name": "root", "job_id": "01000000", "worker_id": "b7fd965bb12b1046ddfa3d73ead5ed54eb7678d97e743d98dfab852b", "node_id": "543c939946ec1321c9c1a10899bfb72f59aa6eab7655719f2611da04", "actor_id": "43b5d1828ad0a003ca6ebcfc01000000", "task_id": "c2668a65bda616c143b5d1828ad0a003ca6ebcfc01000000", "task_name": "actor.print_message", "task_func_name": "test-log-config-doc.actor.print_message", "actor_name": "", "timestamp_ns": 1740549961314391000}
"""
向结构化日志添加元数据#
通过在 logger.info 方法中使用 extra 参数,向日志条目添加额外字段。
import ray
import logging
ray.init(
log_to_driver=False,
logging_config=ray.LoggingConfig(encoding="JSON", log_level="INFO")
)
logger = logging.getLogger()
logger.info("Driver process with extra fields", extra={"username": "anyscale"})
# The log entry includes the extra field "username" with the value "anyscale".
# {"asctime": "2024-07-17 21:57:50,891", "levelname": "INFO", "message": "Driver process with extra fields", "filename": "test.py", "lineno": 9, "username": "anyscale", "job_id": "04000000", "worker_id": "04000000ffffffffffffffffffffffffffffffffffffffffffffffff", "node_id": "76cdbaa32b3938587dcfa278201b8cef2d20377c80ec2e92430737ae"}
如果需要,您可以使用 Ray 的 ray.runtime_context.get_runtime_context API 来获取 Job、Task 或 Actor 的元数据。
获取作业 ID。
import ray
# Initiate a driver.
ray.init()
job_id = ray.get_runtime_context().get_job_id
注意
作业提交 ID 尚不支持。此 GitHub issue 正在跟踪支持它的工作。
获取 actor ID。
import ray
# Initiate a driver.
ray.init()
@ray.remote
class actor():
actor_id = ray.get_runtime_context().get_actor_id
获取 task ID。
import ray
# Initiate a driver.
ray.init()
@ray.remote
def task():
task_id = ray.get_runtime_context().get_task_id
获取节点 ID。
import ray
# Initiate a driver.
ray.init()
# Get the ID of the node where the driver process is running
driver_process_node_id = ray.get_runtime_context().get_node_id
@ray.remote
def task():
# Get the ID of the node where the worker process is running
worker_process_node_id = ray.get_runtime_context().get_node_id
提示
如果您需要节点 IP,请使用 ray.nodes API 来获取所有节点,并将节点 ID 映射到相应的 IP。
系统日志#
Ray 默认对大多数系统或组件日志进行结构化处理。
Python 日志的日志格式
%(asctime)s\t%(levelname)s %(filename)s:%(lineno)s -- %(message)s
示例
2023-06-01 09:15:34,601 INFO job_manager.py:408 -- Submitting job with RAY_ADDRESS = 10.0.24.73:6379
C++ 日志的日志格式
[year-month-day, time, pid, thread_id] (component) [file]:[line] [message]
示例
[2023-06-01 08:47:47,457 I 31009 225171] (gcs_server) gcs_node_manager.cc:42: Registering node info, node id = 8cc65840f0a332f4f2d59c9814416db9c36f04ac1a29ac816ad8ca1e, address = 127.0.0.1, node name = 127.0.0.1
注意
截至 2.5 版本,一些系统组件日志尚未按前面建议的方式进行结构化。系统日志向结构化日志迁移正在进行中。
日志轮换#
Ray 支持日志文件的日志轮换。请注意,并非所有组件都支持日志轮换(Raylet、Python 和 Java worker 日志不进行轮换)。
默认情况下,当日志文件达到 512 MB (maxBytes) 时,日志会进行轮换,最多保留五个备份文件 (backupCount)。Ray 会在所有备份文件后附加索引,例如 raylet.out.1。要更改日志轮换配置,请指定环境变量。例如,
RAY_ROTATION_MAX_BYTES=1024; ray start --head # Start a ray instance with maxBytes 1KB.
RAY_ROTATION_BACKUP_COUNT=1; ray start --head # Start a ray instance with backupCount 1.
每个日志文件(包括其备份)的最大大小为 RAY_ROTATION_MAX_BYTES * RAY_ROTATION_BACKUP_COUNT + RAY_ROTATION_MAX_BYTES
日志持久化#
有关如何将日志处理并导出到外部存储或管理系统,请参阅 Kubernetes 上的日志持久化 和 VM 上的日志持久化 以获取更多详细信息。