Gecko 日志记录

提供了一个用于核心 Gecko 代码的最小日志记录框架,该框架使用 C++ 编写,并针对所有构建启用,并且是线程安全的。可以通过 C++、JavaScript 或 Rust 访问它。

此页面介绍如何为特定日志记录模块启用日志记录、配置日志记录输出以及如何在原生代码中使用日志记录功能。

启用和配置日志记录

警告:将日志记录到文件时的沙盒

沙盒内容进程(在所有操作系统上)无法写入磁盘上的文件,因此建议将日志记录到终端,可能通过将输出重定向到文件。

如果沙盒已被禁用和/或日志记录语句来自父进程,则 MOZ_LOG_FILE 将按预期工作。否则,日志记录到终端在桌面版 macOS 和 Linux 上按预期工作。

在 Windows 上,您仍然可以通过使用 DOS(不是下面定义的 MOZ_LOG_FILE 变量)将输出重定向到文件来查看子进程消息。例如:MOZ_LOG=CameraChild:5 mach run >& my_log_file.txt 将包含来自相机子 Actor 的调试消息,该 Actor 位于(沙盒)内容进程中。

另一种方法是在开发时将输出保留在终端中,方法是将 stderr 重定向到 stdout,然后将 stdout 重定向到另一个进程,例如:

MOZ_LOG=cubeb:4 ./mach run 2>&1 | tee

日志记录到 Firefox Profiler

当在某个线程上记录日志语句并且 Firefox Profiler 正在分析该线程时,日志语句将被记录为 Profiler 标记。

这允许以一种不需要禁用沙盒的方式,在 Profiler 标记和大量性能和上下文信息旁边获取日志,并且可以在所有进程中工作。

配置文件可以下载和共享,例如通过 Bugzilla 或电子邮件,或上传,并且日志语句将显示在标记图表或标记表中。

虽然可以手动配置日志记录模块并使用正确的线程集启动 Profiler 进行分析,但 about:logging 使此任务变得更加简单且不易出错。

MOZ_LOG 语法

日志记录是使用特殊但简单的语法配置的:哪个模块应该启用,在哪个级别,以及哪些日志记录选项应该启用或禁用。

语法是一系列用逗号分隔的术语。术语有两种类型

  • 日志模块及其级别,用冒号 (:) 分隔,例如 example_module:5 以启用日志级别为 5(详细)的模块 example_module。此 searchfox 查询 返回可用模块的完整列表。

  • 下表中的特殊字符串,用于配置日志记录行为。某些配置开关需要一个整数参数,在这种情况下,它与字符串用冒号 (:) 分隔。大多数开关仅在特定输出上下文中应用,在“上下文”列中注明。

特殊模块名称

上下文

操作

append

文件

将新日志追加到现有日志文件。

sync

文件

同步打印每个日志,这对于实时检查行为或在崩溃之前立即获取日志很有用。

raw

文件

精确打印格式字符串中指定的内容,不带进程/线程/时间戳等前缀。

timestamp

文件

在每行日志的开头插入时间戳。

rotate:N

文件

这限制了生成的日志文件的大小。仅保存最近 N 兆字节的日志数据
。我们使用 .0、.1、.2、.3 扩展名轮换四个日志文件。注意:此选项
禁用“append”并强制“timestamp”。

maxsize:N

文件

将日志限制为 N MB。仅在追加模式下工作。

prependheader

文件

在区分日志记录时添加简单的标题。在追加模式下很有用。

profilerstacks

Profiler

在使用 Firefox Profiler 进行分析并且启用了日志模块时,捕获
每个日志语句的调用堆栈。

此语法用于大多数启用日志记录的方法。

启用日志记录

可以通过多种方式启用日志记录

  • 通过环境变量

  • 通过命令行开关

  • 使用 about:config 首选项

  • 使用 about:logging

前两种方法允许从应用程序启动时开始记录日志,并且在发生崩溃时也很有用(当请求 sync 输出时,这也可以在一定程度上使用 about:config 完成)。后两种方法允许在运行时启用和禁用日志记录,并且不需要使用命令行。

默认情况下,所有日志记录输出都已禁用。

使用 about:logging 启用日志记录

about:logging 允许通过在文本输入中输入 MOZ_LOG 字符串并进行验证来启用日志记录。

选项允许将日志记录到文件或使用 Firefox Profiler,可以从页面直接启动和停止 Profiler。

常见场景的日志记录预设在下拉菜单中可用。它们可以与 Profiler 预设关联。

可以通过 URL 参数选择特定的日志记录配置,或覆盖预设中的某些参数。这对于要求用户有效地收集日志很有用,而无需处理首选项和/或环境变量。

URL 参数在下表中描述

参数

描述

preset

一个 日志记录预设

logging-preset

preset 的别名

modules

一个 MOZ_LOG 语法中的字符串

module

modules 的别名

threads

要分析的线程列表,覆盖 Profiler 预设选择的线程

thread

threads 的别名

output

profilerfile

output-type

output 的别名

profiler-preset

一个 Profiler 预设

如果选择了预设,则可以使用 threadsmodules 来覆盖要分析的线程或启用的日志模块,但保留预设的其他方面。如果没有选择预设,则使用通用分析预设 firefox-platform。例如

about:logging?output=profiler&preset=media-playback&modules=cubeb:4,AudioSinkWrapper:4:AudioSink:4

将分析 Media Profiler 预设中的线程,但仅记录特定日志模块(而不是 长列表media-playback 预设中)。此外,它不允许将日志记录到文件。

使用环境变量启用日志记录

在 UNIX 上,可以通过多种方式设置环境变量

set MOZ_LOG="example_logger:3"
export MOZ_LOG="example_logger:3"
MOZ_LOG="example_logger:3" ./mach run

在 Windows 命令提示符 (cmd.exe) 中,不要使用引号

set MOZ_LOG=example_logger:3

如果要在 GeckoView 示例中使用此功能,请使用以下 adb 命令启动进程

adb shell am start -n org.mozilla.geckoview_example/.GeckoViewActivity --es env0 "MOZ_LOG=example_logger:3"

有一些特殊的模块名称可以更改日志记录行为。您可以指定一个或多个特殊模块名称而不指定日志级别。

例如,如果要指定 synctimestamprotate

set MOZ_LOG="example_logger:3,timestamp,sync,rotate:10"

启用日志记录通常会将日志记录语句输出到终端。要改为将日志写入文件(每个进程一个文件),可以使用环境变量 MOZ_LOG_FILE。日志将写入此路径(相对或绝对),后缀为进程类型及其 PID。MOZ_LOG 文件是文本文件,扩展名为 .moz_log

例如,设置

set MOZ_LOG_FILE="firefox-logs"

可以创建许多文件,如下所示

firefox-log-main.96353.moz_log
firefox-log-child.96354.moz_log

分别用于 PID 为 96353 的父进程和 PID 为 96354 的子进程。

使用命令行标志启用日志记录

MOZ_LOG 语法可以与同名的命令行开关一起使用,并使用 MOZ_LOG_FILE 指定文件的方式相同

./mach run -MOZ_LOG=timestamp,rotate:200,example_module:5 -MOZ_LOG_FILE=%TEMP%\firefox-logs

将为模块 example_module 启用详细 (5) 日志记录,并在每行开头添加时间戳,轮换每个 50MB 的 4 个日志文件(总共 200MB),并将输出写入 Windows 上的临时目录,名称以 firefox-logs 开头。

使用首选项启用日志记录

要调整 Firefox 启动后的日志记录,可以在 logging. 前缀下设置首选项。例如,将 logging.foo 设置为 3 将设置日志模块 foo 以在级别 3 开始记录日志。

也可以直接使用 MOZ_LOG 语法,方法是设置首选项 logging.config.modules。可以使用所有模块,但仅支持特殊字符串“profilerstacks”。

还可以设置一些特殊首选项,如下表所述

首选项名称

首选项

首选项值

描述

logging.config.clear_on_startup

布尔值

--

是否清除 logging. 下的所有偏好设置。

logging.config.LOG_FILE

字符串

路径(相对或绝对)

日志文件将写入的路径。

logging.config.add_timestamp

布尔值

--

是否在所有行前添加时间戳。

logging.config.sync

布尔值

--

是否在每个日志语句后刷新流。

logging.config.profilerstacks

布尔值

--

将日志记录到 Firefox Profiler 时,是否在每个日志语句中包含调用堆栈。

在 Rust 代码中启用日志记录

我们正在逐步将更多 Rust 代码添加到 Gecko 中,而 Rust crate 通常使用不同的日志记录方法。许多 Rust 库使用 log crate 来记录消息,该 crate 与应用程序级别的 env_logger 协同工作以控制通过 RUST_LOG 实际打印的内容。

您可以设置一个整体的日志级别,尽管它可能非常冗长

set RUST_LOG="debug"

您还可以按路径定位各个模块

set RUST_LOG="style::style_resolver=debug"

注意

对于 Linux/MacOS 用户,您需要使用 export 而不是 set

注意

有时,仅记录子进程并忽略父进程可能很有用

进程。在 Firefox 57 及更高版本中,您可以使用 RUST_LOG_CHILD 而不是 RUST_LOG 来指定仅适用于子进程的日志设置。

log crate 列出了可用的 日志级别

日志级别

用途

error

表示非常严重的错误。

warn

表示危险情况。

info

表示有用的信息。

debug

表示较低优先级的信息。

trace

表示非常低的优先级,通常非常冗长的信息。

在发布版本中,debug 和 trace 通常在编译时被禁用,因此如果您想要来自这些级别的日志,可能需要一个调试版本。

有关日志记录选项的更多详细信息,请查看 env_logger 文档。

此外,还提供了 RUST_LOG 的映射。在使用 MOZ_LOG 语法时,可以使用类似的语法在 rust crate 中启用日志记录

MOZ_LOG=rust_crate_name::*:4

将为 crate rust_crate_name 中的所有日志语句启用 debug 日志记录。

* 可以替换为一系列模块,如果需要更具体的说明

MOZ_LOG=rust_crate_name::module::submodule:4

将为 crate rust_crate_name 的模块 module 的子模块 submodule 中的所有日志语句启用 debug 日志记录。

下表提供了 Rust 日志级别到 MOZ_LOG 日志级别的映射

Rust 日志级别

MOZ_LOG 级别

数值

off

已禁用

0

error

Error

1

warn

Warning

2

info

Info

3

debug

Debug

4

trace

Verbose

5

在 Android 上启用日志记录,与系统日志交错(logcat

虽然日志记录到 Firefox Profiler 可以工作,但有时将系统日志(adb logcat)与应用程序日志记录交错起来很有用。对于 adb devices 可以看到的设备(或模拟器),可以像这样设置环境变量,例如 GeckoView_example

adb shell am start -n org.mozilla.geckoview_example/.GeckoViewActivity --es env0 MOZ_LOG=MediaDemuxer:4

然后可以像这样查看日志语句,以显示所有日志,包括 MOZ_LOG

adb logcat

并仅查看 MOZ_LOG,如下所示

adb logcat Gecko:V '*:S'

此表达式表示:打印来自日志级别 Verbose(最低级别,这意味着打印所有级别)的日志模块 Gecko,并过滤掉(S 表示静音)所有其他日志记录(*,请注意引用或转义它,以便它不会被 shell 扩展)。

在与例如 GeckoView 代码交互时,可以像这样指定更多日志标记

adb logcat GeckoViewActivity:V Gecko:V '*:S'

在 Android 上启用日志记录,使用 Firefox Profiler

使用 about:config 设置日志模块(这需要 Nightly 版本),使用上面概述的说明,并使用适当的分析预设启动分析,以使用 Firefox Profiler 文档中 专用页面 中编写的说明来分析正确的线程。

Bug 1803607 跟踪改进移动设备上的日志记录体验。

在代码中使用 MOZ_LOG

声明日志模块

LazyLogModule 以线程安全的方式推迟创建后备 LogModule,并且是声明日志模块的首选方法。可以声明多个名称相同的 LazyLogModules,所有这些都将共享相同的后备 LogModule。这使得在多个翻译单元之间共享日志模块变得更加简单。LazyLogLodule 提供到 LogModule* 的转换运算符,适合传递到下面详细介绍的日志宏中。

注意:日志模块名称只能包含特定字符。第一个字符必须是小写或大写 ASCII 字符、下划线、短划线或点。后续字符可以是上述任何字符,或 ASCII 数字。

#include "mozilla/Logging.h"

static mozilla::LazyLogModule sFooLog("foo");

日志记录接口

一个基本的接口以 2 个宏和一个枚举类形式提供。

MOZ_LOG(module, level, message)

如果模块启用了给定的日志级别,则输出给定的消息

  • module:要使用的日志模块。

  • level:消息的日志级别。

  • message:要输出的 printf 样式消息。必须用括号括起来。

MOZ_LOG_FMT(module, level, message)

如果模块启用了给定的日志级别,则输出给定的消息

  • module:要使用的日志模块。

  • level:消息的日志级别。

  • message:要输出的 {fmt} 样式消息。

MOZ_LOG_TEST(module, level)

检查模块是否启用了给定的级别

  • module:要使用的日志模块。

  • level:消息的输出级别。

日志级别

数值

用途

已禁用

0

表示日志记录已禁用。这在代码中不应直接使用。

Error

1

发生了错误,通常是您在调试版本中考虑断言的内容。

Warning

2

警告通常表示意外状态。

Info

3

信息消息,通常表示当前程序状态。

Debug

4

调试消息,对调试很有用,但过于冗长,无法正常打开。

Verbose

5

将打印很多的消息,对调试程序流程很有用,但可能会影响性能。

示例用法

#include "mozilla/Logging.h"

using mozilla::LogLevel;

static mozilla::LazyLogModule sLogger("example_logger");

static void DoStuff()
{
  MOZ_LOG(sLogger, LogLevel::Info, ("Doing stuff."));

  int i = 0;
  int start = Time::NowMS();
  MOZ_LOG(sLogger, LogLevel::Debug, ("Starting loop."));
  while (i++ < 10) {
    MOZ_LOG(sLogger, LogLevel::Verbose, ("i = %d", i));
  }

  // Only calculate the elapsed time if the Warning level is enabled.
  if (MOZ_LOG_TEST(sLogger, LogLevel::Warning)) {
    int elapsed = Time::NowMS() - start;
    if (elapsed > 1000) {
      MOZ_LOG(sLogger, LogLevel::Warning, ("Loop took %dms!", elapsed));
    }
  }

  if (i != 10) {
    MOZ_LOG(sLogger, LogLevel::Error, ("i should be 10!"));
  }
}

通过 console API 从 JavaScript 记录日志

从 JavaScript 对 console API 进行的任何调用都将通过 MOZ_LOG 管道记录。

  • 网页以及使用 console API 公开给 JavaScript 的特权上下文将自动在 console 模块名称下生成 MOZ_LOG 消息。

  • 特权上下文可以通过实例化他们自己的控制台对象来使用特定的模块名称:const logger = console.createInstance({ prefix: "module-name" })prefix 值将用作 MOZ_LOG 模块名称。

有关 console.createInstance 的更多信息,请参阅 JavaScript 日志记录页面

在使用 console API 时,控制台方法调用将在开发者工具中可见,以及通过 MOZ_LOG stdout、文件或分析器输出可见。

请注意,由于 Bug 1923985,控制台日志级别和 MOZ_LOG 日志级别之间存在一些差异。因此,console.shouldLog() 仅考虑由 createInstancemaxLogLevel{Pref} 参数设置的级别。

// The following two logs can be visible through MOZ_LOG by using:
// MOZ_LOG=console:5

// Both call will be logged through "console" module name.
// Any console API call from privileged or content page will be logged.
console.log("Doing stuff.");

console.error("Error happened");

// The following two other logs can be visible through MOZ_LOG by using:
// MOZ_LOG=example_logger:5

// From a privileged context, you can instantiate your own console object
// with a specific module name, here "example_logger":
const logger = console.createInstance({ prefix: "example_logger" });

logger.warn("something failed");

logger.debug("some debug info");

控制台 API 级别

控制台 API 方法

MOZ_LOG 级别

console.error() console.assert()

1 (Error)

console.warn()

2 (Warning)

所有其他方法,但 console.debug() 除外

3 (Info)

console.debug()

4 (Debug