OpenTelemetry 中的错误处理
OpenTelemetry 生成遥测数据以帮助用户监控应用程序代码。在大多数情况下,库执行的工作对于应用程序业务逻辑的视角来说并非必需。我们假设用户宁愿丢失遥测数据,也不愿库显著改变被检测应用程序的行为。
OpenTelemetry 可以通过平台扩展机制启用,或者在运行时动态加载。这使得最终用户难以理解该库的使用方式,甚至可能超出应用程序开发者的控制范围。这使得错误处理有一些独特的要求。
基本的错误处理原则
OpenTelemetry 实现 MUST NOT 在运行时抛出未捕获的异常。
- API 方法 MUST NOT 在被最终用户不正确使用时抛出未捕获的异常。API 和 SDK SHOULD 为缺失或无效的参数提供安全的默认值。例如,当用户在构造
Span时将null作为 span 名称参数传入时,可以使用empty这样的名称。 - API 或 SDK MAY 快速失败 并导致应用程序在初始化时失败,例如由于错误的配置或环境,但 MUST NOT 在运行时导致应用程序失败,例如由于从 Collector 收到的动态配置设置。
- SDK MUST NOT 为其自身操作中的错误抛出未捕获的异常。例如,导出器在无法连接到发送遥测数据的终结点时,不应抛出异常。
指南
- 接受外部回调的 API 方法 MUST 处理所有错误。
- 后台任务(例如线程、异步任务和生成的进程)应在全局错误处理程序的上下文中运行,以确保异常不会影响最终用户应用程序。
- 长时间运行的后台任务不应因内部错误而永久失败。通常,内部异常只应影响导致异常的请求的执行上下文。
- 内部错误处理应遵循特定语言的约定。总的来说,开发人员应最小化错误处理程序的范围,并为预期异常添加特殊处理。
- 注意外部回调和可覆盖接口:预期它们会抛出异常。
- 注意不要调用 API 和 SDK 用户未明确提供作为回调的任何方法。SDK 可能决定在用户对象上调用的
ToString方法可能实现不当,并导致堆栈溢出。应用程序从不调用此方法是很常见的,并且这种不良实现永远不会被应用程序所有者发现。 - 当 API 调用返回预期为非
null值的值时——在处理逻辑出错的情况下——SDK MUST 返回一个“无操作”(“no-op”)或其他“默认”对象,该对象(理想情况下)是预先分配好的并且随时可用。这样,API 调用站点就不会因尝试访问null对象的访问方法和属性而崩溃。
错误处理与性能
错误处理和广泛的输入验证可能会导致性能下降,尤其是在动态语言中,因为在编译时无法保证输入对象类型。运行时类型检查会影响性能且容易出错,尽管尽了最大努力,仍可能发生异常。
建议采用全局异常处理逻辑,以确保异常不会泄露到用户代码中。并合理权衡 SDK 性能与类型检查的全面性,以提供更好的错误处理行为和 SDK 错误故障排除。
自我诊断
鼓励所有 OpenTelemetry 库——API、SDK、导出器、仪表等——公开易于默认启用和过滤掉的自我故障排除指标、span 和其他遥测数据。
一个这样的遥测数据的良好例子是 Span 导出器,它指示导出器花费了多少时间来上传遥测数据。另一个例子可能是 SpanProcessor 公开的一个指标,该指标描述了待上传遥测数据的当前队列大小。
当库压制了本应暴露给用户的错误时,库 SHOULD 使用特定语言的约定记录该错误。SDK MAY 公开回调,允许最终用户将自我诊断与应用程序代码分开处理。
配置错误处理器
SDK 实现 MUST 允许最终用户更改库对相关错误的默认错误处理行为。应用程序开发人员可能希望在暂存环境中运行严格的错误处理,以捕获 API 的无效使用或格式错误的配置。请注意,以这种方式配置自定义错误处理器是上述基本错误处理原则的唯一例外。最终用户设置或注册自定义错误处理器的机制应遵循特定语言的约定。
示例
以下是最终用户可能注册自定义错误处理器的示例。示例仅用于说明目的。OpenTelemetry 客户端作者可以自由偏离这些示例,只要他们的设计符合上述要求。
Go
// The basic Error Handler interface
type ErrorHandler interface {
Handle(err error)
}
func Handler() ErrorHandler
func SetHandler(handler ErrorHandler)
// Registering a custom Error Handler
type IgnoreExporterErrorsHandler struct{}
func (IgnoreExporterErrorsHandler) Handle(err error) {
switch err.(type) {
case *SpanExporterError:
default:
fmt.Println(err)
}
}
func main() {
// Other setup ...
opentelemetrysdk.SetHandler(IgnoreExporterErrorsHandler{})
}
Java
OpenTelemetry Java 使用 java.util.logging 来输出和处理所有日志,包括错误。自定义处理程序和过滤器可以在代码中以及使用 Java 日志配置文件进行注册。
## Turn off all error logging
io.opentelemetry.level = OFF
// Creating a custom filter which does not log errors that come from the exporter
public class IgnoreExportErrorsFilter implements Filter {
public boolean isLoggable(LogRecord record) {
return !record.getMessage().contains("Exception thrown by the export");
}
}
## Registering the custom filter on the BatchSpanProcessor
io.opentelemetry.sdk.trace.export.BatchSpanProcessor = io.opentelemetry.extensions.logging.IgnoreExportErrorsFilter