Skip to content

Commit 965c41b

Browse files
authored
chore: Localized OTEL attribute constants (#2928)
1 parent 2d232f1 commit 965c41b

17 files changed

+432
-639
lines changed

THIRD_PARTY_NOTICES.md

+10-460
Large diffs are not rendered by default.

lib/otel/constants.js

+244
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
/*
2+
* Copyright 2025 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
'use strict'
7+
8+
// Attribute values are found at:
9+
// https://opentelemetry.io/docs/specs/semconv/attributes-registry/
10+
// Attribute constant names are found at:
11+
// https://github.com/open-telemetry/opentelemetry-js/tree/e744798957ac6d980673262a61634f066d9f66a3/semantic-conventions/src
12+
13+
/**
14+
* Provides a hash of constant attribute names to attribute values as defined
15+
* by the OTEL semantic conventions. The values and names are still very much
16+
* in flux (2025-02). As a result, it is easier for us to copy the ones we need
17+
* here.
18+
*
19+
* 1. Everything should be listed in alphabetical order.
20+
* 2. If an attribute can have multiple names, but all have a common value,
21+
* only list one constant for us to standardize on internally. Make notes of
22+
* the other possible upstream names within the jsdoc for the attribute. If they
23+
* have different values, include multiple attributes with documentation
24+
* referencing which should be favored.
25+
*
26+
* @see https://opentelemetry.io/docs/specs/semconv/
27+
*
28+
* @type {object}
29+
*/
30+
module.exports = {
31+
/**
32+
* Name of the database (schema) being accessed.
33+
*/
34+
ATTR_DB_NAME: 'db.name',
35+
36+
/**
37+
* The name of the database operation being performed.
38+
*
39+
* @example select
40+
* @example findAndModify
41+
*/
42+
ATTR_DB_OPERATION: 'db.operation',
43+
44+
/**
45+
* The table name that is targeted in the operation.
46+
*/
47+
ATTR_DB_SQL_TABLE: 'db.sql.table',
48+
49+
/**
50+
* The database statement being executed.
51+
*
52+
* @example select * from foo
53+
*/
54+
ATTR_DB_STATEMENT: 'db.statement',
55+
56+
/**
57+
* Name of the remote database technology being accessed.
58+
*
59+
* @example mysql
60+
* @see https://opentelemetry.io/docs/specs/semconv/database/sql/
61+
*/
62+
ATTR_DB_SYSTEM: 'db.system',
63+
64+
/**
65+
* The full resource URL.
66+
*
67+
* @example https://example.com/foo?bar=baz
68+
*/
69+
ATTR_FULL_URL: 'url.full',
70+
71+
/**
72+
* Value of the HTTP `host` header.
73+
*
74+
* {@link ATTR_HTTP_REQUEST_METHOD} is newer and should be used instead.
75+
*/
76+
ATTR_HTTP_HOST: 'http.host',
77+
78+
/**
79+
* The HTTP request method, e.g. `GET` or `POST`.
80+
*/
81+
ATTR_HTTP_METHOD: 'http.method',
82+
83+
/**
84+
* HTTP method used for the request, e.g. `GET` or `POST`.
85+
*/
86+
ATTR_HTTP_REQUEST_METHOD: 'http.request.method',
87+
88+
/**
89+
* Framework representation for a route, may include parameter tokens.
90+
*
91+
* @example /orders/:order_id
92+
*/
93+
ATTR_HTTP_ROUTE: 'http.route',
94+
95+
/**
96+
* The full resource URL.
97+
*
98+
* {@link ATTR_FULL_URL} is newer and should be used instead.
99+
*
100+
* @example https://example.com/foo?bar=baz
101+
*/
102+
ATTR_HTTP_URL: 'http.url',
103+
104+
/**
105+
* The message destination name.
106+
*
107+
* {@link ATTR_MESSAGING_DESTINATION_NAME} is newer and should be used.
108+
*/
109+
ATTR_MESSAGING_DESTINATION: 'messaging.destination',
110+
111+
/**
112+
* The kind of message destination (don't really know, this is what the
113+
* otel code calls it).
114+
*/
115+
ATTR_MESSAGING_DESTINATION_KIND: 'messaging.destination_kind',
116+
117+
/**
118+
* The target queue name for the message to be delivered to.
119+
*
120+
* @example MyQueue
121+
* @example MyTopic
122+
*/
123+
ATTR_MESSAGING_DESTINATION_NAME: 'messaging.destination.name',
124+
125+
/**
126+
* Target messaging system name.
127+
*
128+
* @example kafka
129+
* @see https://opentelemetry.io/docs/specs/semconv/messaging/messaging-spans/
130+
*/
131+
ATTR_MESSAGING_SYSTEM: 'messaging.system',
132+
133+
/**
134+
* The collection being accessed.
135+
*/
136+
ATTR_MONGODB_COLLECTION: 'db.mongodb.collection',
137+
138+
/**
139+
* Remote host name.
140+
*/
141+
ATTR_NET_PEER_NAME: 'net.peer.name',
142+
143+
/**
144+
* Remote port number.
145+
*/
146+
ATTR_NET_PEER_PORT: 'net.peer.port',
147+
148+
/**
149+
* The name of the remote method being invoked.
150+
*/
151+
ATTR_RPC_METHOD: 'rpc.method',
152+
153+
/**
154+
* The logical name of the service being called.
155+
*
156+
* @example myservice.EchoService
157+
*/
158+
ATTR_RPC_SERVICE: 'rpc.service',
159+
160+
/**
161+
* Defines the RPC technology being instrumented. Will be a string name
162+
* for a known RPC system.
163+
*
164+
* @example grpc
165+
* @see https://opentelemetry.io/docs/specs/semconv/rpc/rpc-spans/
166+
*/
167+
ATTR_RPC_SYSTEM: 'rpc.system',
168+
169+
/**
170+
* Logical name of the local service being instrumented.
171+
*/
172+
ATTR_SERVICE_NAME: 'service.name',
173+
174+
/**
175+
* URL path component, e.g. `/foo` in `http://example.com/foo`. This is
176+
* the fully realized path. See {@link ATTR_HTTP_ROUTE} for the framework
177+
* representation.
178+
*/
179+
ATTR_URL_PATH: 'url.path',
180+
181+
/* !!! Miscellaneous !!! */
182+
/**
183+
* Database system names.
184+
*
185+
* @example mysql
186+
*/
187+
DB_SYSTEM_VALUES: {
188+
ADABAS: 'adabas',
189+
CACHE: 'cache',
190+
CASSANDRA: 'cassandra',
191+
CLOUDSCAPE: 'cloudscape',
192+
COCKROACHDB: 'cockroachdb',
193+
COLDFUSION: 'coldfusion',
194+
COSMOSDB: 'cosmosdb',
195+
COUCHBASE: 'couchbase',
196+
COUCHDB: 'couchdb',
197+
DB2: 'DB2',
198+
DERBY: 'derby',
199+
DYNAMODB: 'dynamodb',
200+
EDB: 'edb',
201+
ELASTICSEARCH: 'elasticsearch',
202+
FILEMAKER: 'filemaker',
203+
FIREBIRD: 'firebird',
204+
FIRSTSQL: 'firstsql',
205+
GEODE: 'geode',
206+
H2: 'h2',
207+
HANADB: 'handadb',
208+
HBASE: 'hbase',
209+
HIVE: 'hive',
210+
HSQLDB: 'hsqldb',
211+
INFORMIX: 'informix',
212+
INGRESS: 'ingres',
213+
INSTANTDB: 'instantdb',
214+
INTERBASE: 'interbase',
215+
MARIADB: 'mariadb',
216+
MAXDB: 'maxdb',
217+
MEMCACHED: 'memcached',
218+
MONGODB: 'mongodb',
219+
MSSQL: 'mssql',
220+
MYSQL: 'mysql',
221+
NEO4J: 'neo4j',
222+
NETEZZA: 'netezza',
223+
ORACLE: 'oracle',
224+
OTHER_SQL: 'other_sql',
225+
PERVASIVE: 'pervasive',
226+
POINTBASE: 'pointbase',
227+
POSTGRESQL: 'postgresql',
228+
PROGRESS: 'progress',
229+
REDIS: 'redis',
230+
REDSHIFT: 'redshift',
231+
SQLITE: 'sqlite',
232+
SYBASE: 'sybase',
233+
TERADATA: 'teradata',
234+
VERTICA: 'vertica',
235+
},
236+
237+
/**
238+
* Kinds of messaging system destinations.
239+
*/
240+
MESSAGING_SYSTEM_KIND_VALUES: {
241+
QUEUE: 'queue',
242+
TOPIC: 'topic'
243+
}
244+
}

lib/otel/segments/consumer.js

+7-7
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,18 @@ const Transaction = require('../../transaction/')
1616
const { DESTINATIONS, TYPES } = Transaction
1717

1818
const {
19-
SEMATTRS_MESSAGING_SYSTEM,
20-
SEMATTRS_MESSAGING_DESTINATION,
21-
SEMATTRS_MESSAGING_DESTINATION_KIND
22-
} = require('@opentelemetry/semantic-conventions')
19+
ATTR_MESSAGING_DESTINATION,
20+
ATTR_MESSAGING_DESTINATION_KIND,
21+
ATTR_MESSAGING_SYSTEM
22+
} = require('../constants')
2323

2424
function createConsumerSegment(agent, otelSpan) {
2525
const transaction = new Transaction(agent)
2626
transaction.type = TYPES.BG
2727

28-
const system = otelSpan.attributes[SEMATTRS_MESSAGING_SYSTEM] ?? 'unknown'
29-
const destination = otelSpan.attributes[SEMATTRS_MESSAGING_DESTINATION] ?? 'unknown'
30-
const destKind = otelSpan.attributes[SEMATTRS_MESSAGING_DESTINATION_KIND] ?? 'unknown'
28+
const system = otelSpan.attributes[ATTR_MESSAGING_SYSTEM] ?? 'unknown'
29+
const destination = otelSpan.attributes[ATTR_MESSAGING_DESTINATION] ?? 'unknown'
30+
const destKind = otelSpan.attributes[ATTR_MESSAGING_DESTINATION_KIND] ?? 'unknown'
3131
const segmentName = `OtherTransaction/Message/${system}/${destKind}/Named/${destination}`
3232

3333
const txAttrs = transaction.trace.attributes

lib/otel/segments/database.js

+18-16
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,28 @@
44
*/
55

66
'use strict'
7-
const {
8-
SEMATTRS_DB_MONGODB_COLLECTION,
9-
SEMATTRS_DB_SYSTEM,
10-
SEMATTRS_DB_SQL_TABLE,
11-
SEMATTRS_DB_OPERATION,
12-
SEMATTRS_DB_STATEMENT,
13-
DbSystemValues
14-
} = require('@opentelemetry/semantic-conventions')
7+
158
const parseSql = require('../../db/query-parsers/sql')
169
const recordQueryMetrics = require('../../metrics/recorders/database')
1710
const recordOperationMetrics = require('../../metrics/recorders/database-operation')
1811
const ParsedStatement = require('../../db/parsed-statement')
1912
const metrics = require('../../metrics/names')
2013

14+
const {
15+
ATTR_DB_OPERATION,
16+
ATTR_DB_SQL_TABLE,
17+
ATTR_DB_STATEMENT,
18+
ATTR_DB_SYSTEM,
19+
ATTR_MONGODB_COLLECTION,
20+
DB_SYSTEM_VALUES
21+
} = require('../constants')
22+
2123
// TODO: This probably has some holes
2224
// I did analysis and tried to apply the best logic
2325
// to extract table/operation
2426
module.exports = function createDbSegment(agent, otelSpan) {
2527
const context = agent.tracer.getContext()
26-
const system = otelSpan.attributes[SEMATTRS_DB_SYSTEM]
28+
const system = otelSpan.attributes[ATTR_DB_SYSTEM]
2729
const parsed = parseStatement(agent.config, otelSpan, system)
2830
const { name, operation } = setName(parsed)
2931
const segment = agent.tracer.createSegment({
@@ -74,9 +76,9 @@ function getRecorder({ parsed, operation, system }) {
7476
* @returns {ParsedStatement} instance of parsed statement
7577
*/
7678
function parseStatement(config, otelSpan, system) {
77-
let table = otelSpan.attributes[SEMATTRS_DB_SQL_TABLE]
78-
let operation = otelSpan.attributes[SEMATTRS_DB_OPERATION]
79-
let statement = otelSpan.attributes[SEMATTRS_DB_STATEMENT]
79+
let table = otelSpan.attributes[ATTR_DB_SQL_TABLE]
80+
let operation = otelSpan.attributes[ATTR_DB_OPERATION]
81+
let statement = otelSpan.attributes[ATTR_DB_STATEMENT]
8082
if (statement && !(table || operation)) {
8183
const parsed = parseSql({ sql: statement })
8284
if (parsed.operation && !operation) {
@@ -88,11 +90,11 @@ function parseStatement(config, otelSpan, system) {
8890
}
8991
statement = parsed.query
9092
}
91-
if (system === DbSystemValues.MONGODB) {
92-
table = otelSpan.attributes[SEMATTRS_DB_MONGODB_COLLECTION]
93+
if (system === DB_SYSTEM_VALUES.MONGODB) {
94+
table = otelSpan.attributes[ATTR_MONGODB_COLLECTION]
9395
}
9496

95-
if (system === DbSystemValues.REDIS && statement) {
97+
if (system === DB_SYSTEM_VALUES.REDIS && statement) {
9698
;[operation] = statement.split(' ')
9799
}
98100

@@ -121,7 +123,7 @@ function setName(parsed) {
121123
let operation = false
122124
let name = `Datastore/statement/${parsed.type}/${parsed.collection}/${parsed.operation}`
123125
// All segment name shapes are same except redis/memcached
124-
if (parsed.type === DbSystemValues.REDIS || parsed.type === DbSystemValues.MEMCACHED) {
126+
if (parsed.type === DB_SYSTEM_VALUES.REDIS || parsed.type === DB_SYSTEM_VALUES.MEMCACHED) {
125127
name = `Datastore/operation/${parsed.type}/${parsed.operation}`
126128
operation = true
127129
}

lib/otel/segments/http-external.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,17 @@
44
*/
55

66
'use strict'
7+
78
const NAMES = require('../../metrics/names')
8-
const { SEMATTRS_HTTP_HOST } = require('@opentelemetry/semantic-conventions')
99
const recordExternal = require('../../metrics/recorders/http_external')
1010

11+
const {
12+
ATTR_HTTP_HOST
13+
} = require('../constants')
14+
1115
module.exports = function createHttpExternalSegment(agent, otelSpan) {
1216
const context = agent.tracer.getContext()
13-
const host = otelSpan.attributes[SEMATTRS_HTTP_HOST] || 'Unknown'
17+
const host = otelSpan.attributes[ATTR_HTTP_HOST] || 'Unknown'
1418
const name = NAMES.EXTERNAL.PREFIX + host
1519
const segment = agent.tracer.createSegment({
1620
name,

lib/otel/segments/producer.js

+8-7
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
*/
55

66
'use strict'
7+
78
const {
8-
SEMATTRS_MESSAGING_SYSTEM,
9-
SEMATTRS_MESSAGING_DESTINATION,
10-
SEMATTRS_MESSAGING_DESTINATION_KIND
11-
} = require('@opentelemetry/semantic-conventions')
9+
ATTR_MESSAGING_DESTINATION,
10+
ATTR_MESSAGING_DESTINATION_KIND,
11+
ATTR_MESSAGING_SYSTEM
12+
} = require('../constants')
1213

1314
module.exports = function createProducerSegment(agent, otelSpan) {
1415
const context = agent.tracer.getContext()
@@ -22,8 +23,8 @@ module.exports = function createProducerSegment(agent, otelSpan) {
2223
}
2324

2425
function setName(otelSpan) {
25-
const system = otelSpan.attributes[SEMATTRS_MESSAGING_SYSTEM] || 'Unknown'
26-
const destKind = otelSpan.attributes[SEMATTRS_MESSAGING_DESTINATION_KIND] || 'Unknown'
27-
const destination = otelSpan.attributes[SEMATTRS_MESSAGING_DESTINATION] || 'Unknown'
26+
const system = otelSpan.attributes[ATTR_MESSAGING_SYSTEM] || 'Unknown'
27+
const destKind = otelSpan.attributes[ATTR_MESSAGING_DESTINATION_KIND] || 'Unknown'
28+
const destination = otelSpan.attributes[ATTR_MESSAGING_DESTINATION] || 'Unknown'
2829
return `MessageBroker/${system}/${destKind}/Produce/Named/${destination}`
2930
}

0 commit comments

Comments
 (0)