@@ -13,25 +13,70 @@ const {
13
13
DbSystemValues
14
14
} = require ( '@opentelemetry/semantic-conventions' )
15
15
const parseSql = require ( '../../db/query-parsers/sql' )
16
+ const recordQueryMetrics = require ( '../../metrics/recorders/database' )
17
+ const recordOperationMetrics = require ( '../../metrics/recorders/database-operation' )
18
+ const ParsedStatement = require ( '../../db/parsed-statement' )
19
+ const metrics = require ( '../../metrics/names' )
16
20
17
21
// TODO: This probably has some holes
18
22
// I did analysis and tried to apply the best logic
19
23
// to extract table/operation
20
24
module . exports = function createDbSegment ( agent , otelSpan ) {
21
25
const context = agent . tracer . getContext ( )
22
- const name = setName ( otelSpan )
26
+ const system = otelSpan . attributes [ SEMATTRS_DB_SYSTEM ]
27
+ const parsed = parseStatement ( agent . config , otelSpan , system )
28
+ const { name, operation } = setName ( parsed )
23
29
const segment = agent . tracer . createSegment ( {
24
30
name,
31
+ recorder : getRecorder ( { operation, parsed, system } ) ,
25
32
parent : context . segment ,
26
33
transaction : context . transaction
27
34
} )
28
35
return { segment, transaction : context . transaction }
29
36
}
30
37
31
- function parseStatement ( otelSpan , system ) {
38
+ /**
39
+ * Assigns the appropriate timeslice metrics recorder
40
+ * based on the otel span.
41
+ *
42
+ * @param {object } params to fn
43
+ * @param {ParsedStatement } params.parsed parsed statement of call
44
+ * @param {boolean } params.operation if span is an operation
45
+ * @param {string } params.system `db.system` value of otel span
46
+ * @returns {function } returns a timeslice metrics recorder function based on span
47
+ */
48
+ function getRecorder ( { parsed, operation, system } ) {
49
+ if ( operation ) {
50
+ /**
51
+ * this metrics recorder expects to bound with
52
+ * a datastore-shim. But really the only thing it needs
53
+ * is `_metrics` with values for PREFIX and ALL
54
+ * This assigns what it needs to properly create
55
+ * the db operation time slice metrics
56
+ */
57
+ const scope = { }
58
+ scope . _metrics = {
59
+ PREFIX : system ,
60
+ ALL : metrics . DB . PREFIX + system + '/' + metrics . ALL
61
+ }
62
+ return recordOperationMetrics . bind ( scope )
63
+ } else {
64
+ return recordQueryMetrics . bind ( parsed )
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Creates a parsed statement from various span attributes.
70
+ *
71
+ * @param {object } config agent config
72
+ * @param {object } otelSpan span getting parsed
73
+ * @param {string } system value of `db.system` on span
74
+ * @returns {ParsedStatement } instance of parsed statement
75
+ */
76
+ function parseStatement ( config , otelSpan , system ) {
32
77
let table = otelSpan . attributes [ SEMATTRS_DB_SQL_TABLE ]
33
78
let operation = otelSpan . attributes [ SEMATTRS_DB_OPERATION ]
34
- const statement = otelSpan . attributes [ SEMATTRS_DB_STATEMENT ]
79
+ let statement = otelSpan . attributes [ SEMATTRS_DB_STATEMENT ]
35
80
if ( statement && ! ( table || operation ) ) {
36
81
const parsed = parseSql ( { sql : statement } )
37
82
if ( parsed . operation && ! operation ) {
@@ -41,6 +86,7 @@ function parseStatement(otelSpan, system) {
41
86
if ( parsed . collection && ! table ) {
42
87
table = parsed . collection
43
88
}
89
+ statement = parsed . query
44
90
}
45
91
if ( system === DbSystemValues . MONGODB ) {
46
92
table = otelSpan . attributes [ SEMATTRS_DB_MONGODB_COLLECTION ]
@@ -52,17 +98,33 @@ function parseStatement(otelSpan, system) {
52
98
53
99
table = table || 'Unknown'
54
100
operation = operation || 'Unknown'
101
+ const queryRecorded =
102
+ config . transaction_tracer . record_sql === 'raw' ||
103
+ config . transaction_tracer . record_sql === 'obfuscated'
55
104
56
- return { operation, table }
105
+ return new ParsedStatement (
106
+ system ,
107
+ operation ,
108
+ table ,
109
+ queryRecorded ? statement : null
110
+ )
57
111
}
58
112
59
- function setName ( otelSpan ) {
60
- const system = otelSpan . attributes [ SEMATTRS_DB_SYSTEM ]
61
- const { operation, table } = parseStatement ( otelSpan , system )
62
- let name = `Datastore/statement/${ system } /${ table } /${ operation } `
113
+ /**
114
+ * Creates name for db segment based on otel span
115
+ * If the system is redis or memcached the name is an operation name
116
+ *
117
+ * @param {ParsedStatement } parsed statement used for naming segment
118
+ * @returns {string } name of segment
119
+ */
120
+ function setName ( parsed ) {
121
+ let operation = false
122
+ let name = `Datastore/statement/${ parsed . type } /${ parsed . collection } /${ parsed . operation } `
63
123
// All segment name shapes are same except redis/memcached
64
- if ( system === DbSystemValues . REDIS || system === DbSystemValues . MEMCACHED ) {
65
- name = `Datastore/operation/${ system } /${ operation } `
124
+ if ( parsed . type === DbSystemValues . REDIS || parsed . type === DbSystemValues . MEMCACHED ) {
125
+ name = `Datastore/operation/${ parsed . type } /${ parsed . operation } `
126
+ operation = true
66
127
}
67
- return name
128
+
129
+ return { name, operation }
68
130
}
0 commit comments