数据库客户端 Span 的语义约定

状态: 稳定,除非另有说明。

警告

使用 此文档 v1.24.0(或更早版本)的现有数据库仪器化

  • SHOULD NOT 在其现有的主版本中更改其默认发出的数据库约定版本。约定包括(但不限于)属性、指标和 Span 名称,以及度量单位。
  • SHOULD 在其现有的主版本中引入一个环境变量 OTEL_SEMCONV_STABILITY_OPT_IN,该变量是一个类别特定值的逗号分隔列表(例如,http、databases、messaging)。值列表包括:
    • database - 发出稳定的数据库约定,并停止发出仪器化之前发出的实验性数据库约定。
    • database/dup - 同时发出实验性和稳定数据库约定,允许稳定语义约定分阶段推出。
    • 默认行为(如果没有这些值)是继续发出仪器化先前发出的旧实验性数据库约定的任何版本。
    • 注意:如果同时存在 database/dupdatabase 这两个值,则 database/dup 的优先级高于 database
  • SHOULD 在开始发出这两套约定后,至少维护其现有主版本(至少包括安全补丁)六个月。
  • MAY 在其下一个主版本中删除该环境变量,并只发出稳定的数据库约定。

名称

数据库 Span MUST 遵循 Span 名称的整体 指南

如果提供了摘要,则 **Span 名称** SHOULD 为 {db.query.summary}

如果没有摘要,则 Span 名称 SHOULD 为 {db.operation.name} {target},前提是存在(低基数)db.operation.name(有关 {target} 占位符的确切定义,请参见下文)。

如果不存在(低基数)db.operation.name,则数据库 Span 名称 SHOULD 默认为 {target}

如果 {db.operation.name}{target} 都不存在,则 Span 名称 SHOULD 为 {db.system.name}

特定数据库系统的语义约定 MAY 指定不同的 Span 名称格式。

{target} SHOULD 描述操作所针对的实体,并 SHOULD 遵循以下值之一,前提是它们是可访问的:

  • db.collection.name SHOULD 用于对特定数据库集合进行操作。
  • db.stored_procedure.name SHOULD 用于对特定存储过程进行操作。
  • db.namespace SHOULD 用于对特定数据库命名空间进行操作。
  • server.address:server.port SHOULD 用于未针对任何特定集合、存储过程或命名空间进行操作的其他操作。

如果特定操作不存在相应的 {target} 值,则仪器化 SHOULD 省略 {target}。例如,对于描述匿名表 SQL 查询的操作,如 SELECT * FROM (SELECT * FROM table) t,Span 名称应为 SELECT

Span 定义

状态: 稳定

此 Span 描述了数据库客户端调用。

仪器化 SHOULD 在可能的情况下,记录数据库 Span,以覆盖相应 API 调用的持续时间,就像它被调用者(例如客户端应用程序)观察到一样。例如,如果发生了暂时性问题并在此数据库调用中重试,则相应的 Span 应涵盖逻辑操作的持续时间(包括所有重试)。

当数据库客户端为特定操作(例如调用存储过程)提供更高级别的便捷 API 时,这些 API 内部会生成并执行通用查询,此时建议仪器化更高级别的便捷 API。这些 API 通常允许设置 db.operation.* 属性,而这些属性在通用查询级别通常不易获得。

**Span 名称** 在 名称 部分进行介绍。

Span kind SHOULD 为 CLIENT。它 MAY 为代表内存中数据库调用的 Span 设置为 INTERNAL。当被仪器化的数据库系统通常运行在与客户端不同的进程中,或者当数据库调用通过仪器化的协议(如 HTTP)发生时,建议使用 CLIENT kind。

Span status SHOULD 遵循 Recording Errors 文档。特定系统的语义约定 SHOULD 指定 db.response.status_code 的哪些值被归类为错误。

Attributes

Stability需求级别Value Type描述Example Values
db.system.nameStable必需字符串客户端仪器化识别的数据库管理系统(DBMS)产品。[1]other_sql; softwareag.adabas; actian.ingres
db.collection.nameStable有条件地必需 [2]字符串数据库中的集合(表、容器)名称。[3]public.users; customers
db.namespaceStable有条件必需 如果可用。字符串数据库中的数据库名称,相对于服务器地址和端口是完全限定的。[4]customers; test.users
db.operation.nameStable有条件地必需 [5]字符串正在执行的操作或命令的名称。[6]findAndModify; HMSET; SELECT
db.response.status_codeStable有条件地必需 [7]字符串数据库响应状态码。[8]102; ORA-17002; 08P01; 404
error.typeStable有条件必需 仅在操作失败时。字符串描述操作结束的错误类别。[9]timeoutjava.net.UnknownHostExceptionserver_certificate_invalid500
server.portStable有条件地必需 [10]int服务器端口号。[11]80; 8080; 443
db.operation.batch.sizeStable推荐int批处理操作中包含的查询数量。[12]2; 3; 4
db.query.summaryStable推荐 [13]字符串数据库查询的低基数摘要。[14]SELECT wuser_table; INSERT shipping_details SELECT orders; get user by id
db.query.textStable推荐 [15]字符串正在执行的数据库查询。[16]SELECT * FROM wuser_table where username = ?; SET mykey ?
db.stored_procedure.nameStable推荐 [17]字符串数据库中的存储过程名称。[18]GetCustomer
network.peer.addressStable推荐 如果适用于此数据库系统。字符串执行操作的数据库节点所在的主机地址。[19]10.1.2.80/tmp/my.sock
network.peer.portStable推荐,当且仅当设置了 network.peer.address 时。int网络连接的对等端口号。65123
server.addressStable推荐字符串数据库主机的名称。[20]example.com10.1.2.80/tmp/my.sock
db.query.parameter.<key>Development选择加入字符串数据库查询参数,其中 <key> 是参数名称,属性值是参数值的字符串表示。[21]someval; 55
db.response.returned_rowsDevelopment选择加入int操作返回的行数。10; 30; 1000

**[1] db.system.name:** 实际的 DBMS 可能与客户端识别的有所不同。例如,在使用 PostgreSQL 客户端库连接到 CockroachDB 时,根据仪器化的最佳知识,db.system.name 被设置为 postgresql

**[2] db.collection.name:** 如果易于访问,并且数据库调用是在单个集合上执行的。

**[3] db.collection.name:** 建议捕获应用程序提供的原始值,而不尝试进行任何大小写规范化。

当数据库系统支持包含多个集合的查询文本时,不应从 db.query.text 中提取集合名称(对于非批量操作)。

对于批量操作,如果已知各个操作具有相同的集合名称,则应使用该集合名称。

**[4] db.namespace:** 如果数据库系统有多个命名空间组件,它们 SHOULD 从最通用到最具体的命名空间组件连接起来,使用 | 作为组件之间的分隔符。任何缺失的组件(及其关联的分隔符)SHOULD 被省略。特定数据库系统的语义约定 SHOULD 记录 db.namespace 在该系统上下文中的含义。建议捕获应用程序提供的原始值,而不尝试进行任何大小写规范化。

**[5] db.operation.name:** 如果易于访问,并且存在一个单一的操作名称来描述数据库调用。

**[6] db.operation.name:** 建议捕获应用程序提供的原始值,而不尝试进行任何大小写规范化。

当数据库系统支持包含多个操作的查询文本时,不应从 db.query.text 中提取操作名称(对于非批量操作)。

如果操作名称中可能出现空格,则应将多个连续空格规范化为单个空格。

对于批量操作,如果已知各个操作具有相同的操作名称,则应在前面加上 BATCH ,否则 db.operation.name 应为 BATCH 或其他更适用的数据库系统特定术语。

**[7] db.response.status_code:** 如果操作失败且状态码可用。

**[8] db.response.status_code:** 数据库返回的状态码。通常它表示一个错误码,但也可能表示部分成功、警告,或区分各种成功的结果。特定数据库系统的语义约定 SHOULD 记录 db.response.status_code 在该系统上下文中的含义。

**[9] error.type:** error.type SHOULD 匹配数据库或客户端库返回的 db.response.status_code,或者发生的异常的规范名称。在使用规范异常类型名称时,仪器化 SHOULD 尽最大努力报告最相关的类型。例如,如果原始异常被包装在一个通用异常中,则应优先使用原始异常。仪器化 SHOULD 记录 error.type 的填充方式。

**[10] server.port:** 如果使用非此 DBMS 默认端口,并且设置了 server.address

**[11] server.port:** 从客户端侧观察到时,并且通过中介进行通信时,server.port SHOULD 表示任何中介(例如代理)之后的服务器端口,如果可用。

**[12] db.operation.batch.size:** 操作仅在包含两个或更多操作时才被视为批处理,因此 db.operation.batch.size SHOULD 绝不为 1

**[13] db.query.summary:** 如果通过仪器化钩子可用,或者仪器化支持生成查询摘要。

**[14] db.query.summary:** 查询摘要描述了一类数据库查询,对于分组键非常有用,尤其是在分析涉及复杂查询的数据库调用的遥测数据时。

摘要可能通过仪器化钩子或其他方式提供给仪器化。如果不可用,支持查询解析的仪器化应遵循 生成查询摘要 部分来生成摘要。

**[15] db.query.text:** 默认情况下不应收集非参数化查询文本,除非进行了清理以排除敏感数据,例如通过编辑查询文本中存在的所有字面量值。请参阅 db.query.text 的清理。默认情况下应收集参数化查询文本(查询参数值本身是可选的,请参阅 db.query.parameter.<key>)。

**[16] db.query.text:** 有关清理,请参阅 db.query.text 的清理。对于批处理操作,如果已知各个操作具有相同的查询文本,则应使用该查询文本;否则,应将所有单独的查询文本连接起来,使用分隔符 ; 或在更适用时使用其他数据库系统特定的分隔符。参数化查询文本不应被清理。尽管参数化查询文本可能包含敏感数据,但通过使用参数化查询,用户会发出一个强烈的信号,表明任何敏感数据都将作为参数值传递,并且默认捕获查询文本静态部分的可观测性优势大于风险。

**[17] db.stored_procedure.name:** 如果操作应用于特定的存储过程。

**[18] db.stored_procedure.name:** 建议捕获应用程序提供的原始值,而不尝试进行任何大小写规范化。

对于批量操作,如果已知各个操作具有相同的存储过程名称,则应使用该存储过程名称。

**[19] network.peer.address:** 特定数据库系统的语义约定 SHOULD 记录 network.peer.* 属性是否适用。当应用程序直接与单个数据库节点交互时,网络对等地址和端口非常有用。如果数据库操作涉及多个网络调用(例如重试),则应使用最后联系节点的地址。

**[20] server.address:** 从客户端侧观察到时,并且通过中介进行通信时,server.address SHOULD 表示任何中介(例如代理)之后的服务器地址,如果可用。

**[21] db.query.parameter.<key>:** 如果查询参数没有名称,而是仅通过索引引用,则 <key> SHOULD 为 0 基索引。

db.query.parameter.<key> 应与 db.query.text 中存在的参数化占位符匹配。

建议捕获应用程序提供的原始值,而不尝试进行任何大小写规范化。

db.query.parameter.<key> 不应在批量操作上捕获。

示例

  • 对于查询 SELECT * FROM users where username = %s 和参数 "jdoe",属性 db.query.parameter.0 应设置为 "jdoe"

  • 对于查询 "SELECT * FROM users WHERE username = %(userName)s; 和参数 userName = "jdoe",属性 db.query.parameter.userName 应设置为 "jdoe"

以下属性对于做出采样决策可能很重要,并且应在跨度创建时提供(如果提供的话)


db.system.name 具有以下常用值列表。如果其中一个适用,则必须使用相应的值;否则,可以根据需要使用自定义值。

描述Stability
actian.ingresActian IngresDevelopment
aws.dynamodbAmazon DynamoDBDevelopment
aws.redshiftAmazon RedshiftDevelopment
azure.cosmosdbAzure Cosmos DBDevelopment
cassandraApache CassandraDevelopment
clickhouseClickHouseDevelopment
cockroachdbCockroachDBDevelopment
couchbaseCouchbaseDevelopment
couchdbApache CouchDBDevelopment
derbyApache DerbyDevelopment
elasticsearchElasticsearchDevelopment
firebirdsqlFirebirdDevelopment
gcp.spannerGoogle Cloud SpannerDevelopment
geodeApache GeodeDevelopment
h2databaseH2 DatabaseDevelopment
hbaseApache HBaseDevelopment
hiveApache HiveDevelopment
hsqldbHyperSQL DatabaseDevelopment
ibm.db2IBM Db2Development
ibm.informixIBM InformixDevelopment
ibm.netezzaIBM NetezzaDevelopment
influxdbInfluxDBDevelopment
instantdbInstantDevelopment
intersystems.cacheInterSystems CachéDevelopment
mariadbMariaDBStable
memcachedMemcachedDevelopment
microsoft.sql_serverMicrosoft SQL ServerStable
mongodbMongoDBDevelopment
mysqlMySQLStable
neo4jNeo4jDevelopment
opensearchOpenSearchDevelopment
oracle.dbOracle DatabaseDevelopment
other_sql其他 SQL 数据库。仅用作回退。Development
postgresqlPostgreSQLStable
redisRedisDevelopment
sap.hanaSAP HANADevelopment
sap.maxdbSAP MaxDBDevelopment
softwareag.adabasAdabas (Adaptable Database System)Development
sqliteSQLiteDevelopment
teradataTeradataDevelopment
trinoTrinoDevelopment

error.type 具有以下已知值列表。如果其中一个适用,则必须使用相应的值;否则,可以使用自定义值。

描述Stability
_OTHER当检测不到自定义值时使用的回退错误值。Stable

db.system.name 的注释和已知标识符

以上列表是 db.system.name 的已知标识符的非详尽列表。

如果此列表中定义的某个值适用于请求所发送的 DBMS,则必须使用该值。如果此列表中没有适用的值,则必须提供自定义值。此自定义值必须是 DBMS 的小写名称,不带版本号,以保持与现有标识符一致。

鼓励向此规范提交 PR,以添加列表中缺失的值,尤其是在编写这些缺失数据库的仪器化时。这使得相同数据库的多个仪器化能够对齐,并简化了后端分析。

other_sql 值用作备用,只有当 DBMS 被认为符合 SQL 标准但具体的 product 未被仪器化已知时,才必须使用它。如果仪器化已知具体的 DBMS,则必须使用其特定的标识符。

后端可以,例如,使用提供的标识符来确定适当的 SQL 方言来解析 db.query.text

当添加仅适用于特定 DBMS 的附加属性时,其标识符 SHOULD 在属性键中用作命名空间,就像下面各节中的属性一样。

db.query.text 的清理

db.query.text 默认情况下仅在进行清理以排除敏感信息时才应收集。清理 SHOULD 使用占位符值替换所有字面量。这些字面量包括但不限于字符串、数字、日期和时间、布尔值、间隔、二进制和十六进制字面量。占位符值 SHOULD 为 ?,除非它在给定的数据库系统中已有定义的含义,在这种情况下,仪器化 MAY 选择不同的占位符。

参数化查询文本不应被清理。尽管参数化查询文本可能包含敏感数据,但通过使用参数化查询,用户会发出一个强烈的信号,表明任何敏感数据都将作为参数值传递,并且默认捕获查询文本静态部分的可观测性优势大于风险。

清理过程中 MAY 折叠 IN 子句,例如从 IN (?, ?, ?, ?) 折叠为 IN (?),因为这有助于处理极其长的 IN 子句,并有助于控制选择(可选)将 db.query.text 添加到其指标属性中的用户的基数。

在执行清理时,仪器化 MAY 出于性能考虑截断清理后的值(因为清理会产生性能成本)。

生成查询摘要

db.query.summary 属性可用于捕获查询的缩短表示。它 SHOULD 具有低基数,并且 SHOULD 不包含任何动态或敏感数据。

注意

db.query.text 属性用于标识单个查询。尽管默认情况下它会被清理,但它仍然可能具有高基数,并且可能达到数百行。

db.query.summary 用于提供一个不那么细粒度的分组键,该键在常见情况下可用作 Span 名称或指标属性。它 SHOULD 只包含对查询、数据库或应用程序性能有重大影响的信息。

如果通过仪器化钩子或其他来源易于获得,仪器化 SHOULD 设置查询摘要。

否则

  • 在仪器化创建查询内部的更高层 API 时 - 例如,那些创建表或执行存储过程的 API - 仪器化 SHOULD 从可用的操作和目标生成 db.query.summary,使用本节中描述的格式。

  • 在仪器化操作级别为查询的 API 时,支持查询解析的仪器化 SHOULD 根据 db.query.text 生成查询摘要。

摘要 SHOULD 按提供顺序保留查询的以下部分:

  • 操作,如 SQL SELECT、INSERT、UPDATE、DELETE 以及其他命令
  • 操作目标,如集合、存储过程、数据库名称等

支持查询解析的仪器化 SHOULD 解析查询并从中提取操作和目标的列表。它 SHOULD 将 db.query.summary 属性设置为以下格式的值:

{operation1} {target1} {operation2} {target2} {target3} ...

仪器化 SHOULD 捕获应用程序提供的操作和目标值,而不尝试进行任何大小写规范化。如果操作和目标值在 db.operation.namedb.collection.name 或其他属性中填充,则应与 db.query.summary 中使用的值匹配。

解析查询以设置 db.query.summary 的仪器化 SHOULD 将摘要截断到 255 个字符(确保截断不会发生在操作名称或目标内)。

示例:

  • 仅包含单个操作的查询

    SELECT *
    FROM   wuser_table
    WHERE  username = ?
    

    相应的 db.query.summarySELECT wuser_table

  • 执行多个操作的查询

    INSERT INTO shipping_details
                (order_id,
                address)
    SELECT order_id,
           address
    FROM   orders
    WHERE  order_id = ?
    

    相应的 db.query.summaryINSERT shipping_details SELECT orders

  • 应用于多个集合的操作的查询

    SELECT *
    FROM   songs,
           artists
    WHERE  songs.artist_id == artists.id
    

    相应的 db.query.summarySELECT songs artists

  • 应用于匿名表的查询

    SELECT order_date
    FROM   (SELECT *
            FROM   orders o
                   JOIN customers c
                     ON o.customer_id = c.customer_id)
    

    相应的 db.query.summarySELECT SELECT orders customers

  • 对带有双引号或其他标点符号的多个集合执行操作的查询

    SELECT *
    FROM   "song list",
           'artists'
    

    相应的 db.query.summarySELECT "song list" 'artists'

  • 使用便捷 API(如 JDBC 中提供的 API)执行存储过程

    connection.prepareCall("{call some_stored_procedure}");
    

    相应的 db.query.summarycall some_stored_proceduredb.query.text 未填充。请注意,CALL 是调用存储过程的标准 SQL 关键字。

  • 使用 Microsoft SQL Server 驱动程序的便捷 API Microsoft.Data.SqlClient 执行存储过程

    var command = new SqlCommand();
    command.CommandType = CommandType.StoredProcedure;
    command.CommandText = "some_stored_procedure";
    

    相应的 db.query.summaryEXECUTE some_stored_proceduredb.query.text 未填充。请注意,Microsoft SQL Server 不支持 SQL 标准的 CALL 关键字,而是使用 EXECUTE 来调用存储过程。

特定数据库系统或专用仪器化的语义约定 MAY 指定不同的 db.query.summary 格式,只要生成的摘要保持相对简短,并且其基数与 db.query.text 相比保持较低。

上下文传播

状态: 开发中

SQL 注释器

仪器化 MAY 通过在执行前将注释注入 SQL 查询来使用 SQL 注释器 传播上下文。基于 SQL 注释器的上下文传播 SHOULD 默认不启用,但仪器化 MAY 允许用户选择加入。

仪器化实现 SHOULD **附加**注释到查询的末尾。特定数据库系统的语义约定 MAY 指定不同的格式,这可能包括不同的位置、编码或模式,具体取决于特定数据库系统的要求或偏好。

仪器化 SHOULD 允许用户传递一个传播器来覆盖全局传播器。如果用户未提供传播器,仪器化 SHOULD 使用全局传播器。

示例

  • 对于使用 W3C TraceContext 传播器的查询 SELECT * FROM songs

    SELECT * FROM songs /*traceparent='00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01',tracestate='congo%3Dt61rcWkgMzE%2Crojo%3D00f067aa0ba902b7'*/
    

    请注意,向查询添加高基数注释,如 traceparenttracestate,可能会影响某些数据库系统的性能,例如:

    • MySQL 的预编译语句。有关更多详细信息,请参阅 此相关问题
    • Oracle 和 SQL Server 的预编译和非预编译语句。
  • 对于使用自定义传播器注入载体中 service.name(其中 service.nameshoppingcart)的查询 SELECT * FROM songs

    SELECT * FROM songs /*service.name='shoppingcart'*/
    

特定数据库技术的语义约定

以下数据库技术的更具体语义约定已定义: