Skip to content

Commit

Permalink
refactor: Removed class construction on the segment synthesis and ins… (
Browse files Browse the repository at this point in the history
  • Loading branch information
bizob2828 authored Dec 16, 2024
1 parent 898272d commit 693a3e5
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 126 deletions.
8 changes: 4 additions & 4 deletions lib/otel/segment-synthesis.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
'use strict'
const { RulesEngine } = require('./rules')
const defaultLogger = require('../logger').child({ component: 'segment-synthesizer' })
const { DatabaseSegment, HttpExternalSegment, ServerSegment } = require('./segments')
const { createDbSegment, createHttpExternalSegment, createServerSegment } = require('./segments')

class SegmentSynthesizer {
constructor(agent, { logger = defaultLogger } = {}) {
Expand All @@ -28,11 +28,11 @@ class SegmentSynthesizer {

switch (rule.type) {
case 'external':
return new HttpExternalSegment(this.agent, otelSpan)
return createHttpExternalSegment(this.agent, otelSpan)
case 'db':
return new DatabaseSegment(this.agent, otelSpan)
return createDbSegment(this.agent, otelSpan)
case 'server':
return new ServerSegment(this.agent, otelSpan)
return createServerSegment(this.agent, otelSpan)
default:
this.logger.debug('Found type: %s, no synthesis rule currently built', rule.type)
}
Expand Down
82 changes: 40 additions & 42 deletions lib/otel/segments/database.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,54 +17,52 @@ const parseSql = require('../../db/query-parsers/sql')
// TODO: This probably has some holes
// I did analysis and tried to apply the best logic
// to extract table/operation
module.exports = class DatabaseSegment {
constructor(agent, otelSpan) {
const context = agent.tracer.getContext()
const name = this.setName(otelSpan)
const segment = agent.tracer.createSegment({
name,
parent: context.segment,
transaction: context.transaction
})
return { segment, transaction: context.transaction }
}

parseStatement(otelSpan, system) {
let table = otelSpan.attributes[SEMATTRS_DB_SQL_TABLE]
let operation = otelSpan.attributes[SEMATTRS_DB_OPERATION]
const statement = otelSpan.attributes[SEMATTRS_DB_STATEMENT]
if (statement && !(table || operation)) {
const parsed = parseSql({ sql: statement })
if (parsed.operation && !operation) {
operation = parsed.operation
}
module.exports = function createDbSegment(agent, otelSpan) {
const context = agent.tracer.getContext()
const name = setName(otelSpan)
const segment = agent.tracer.createSegment({
name,
parent: context.segment,
transaction: context.transaction
})
return { segment, transaction: context.transaction }
}

if (parsed.collection && !table) {
table = parsed.collection
}
}
if (system === DbSystemValues.MONGODB) {
table = otelSpan.attributes[SEMATTRS_DB_MONGODB_COLLECTION]
function parseStatement(otelSpan, system) {
let table = otelSpan.attributes[SEMATTRS_DB_SQL_TABLE]
let operation = otelSpan.attributes[SEMATTRS_DB_OPERATION]
const statement = otelSpan.attributes[SEMATTRS_DB_STATEMENT]
if (statement && !(table || operation)) {
const parsed = parseSql({ sql: statement })
if (parsed.operation && !operation) {
operation = parsed.operation
}

if (system === DbSystemValues.REDIS && statement) {
;[operation] = statement.split(' ')
if (parsed.collection && !table) {
table = parsed.collection
}
}
if (system === DbSystemValues.MONGODB) {
table = otelSpan.attributes[SEMATTRS_DB_MONGODB_COLLECTION]
}

table = table || 'Unknown'
operation = operation || 'Unknown'

return { operation, table }
if (system === DbSystemValues.REDIS && statement) {
;[operation] = statement.split(' ')
}

setName(otelSpan) {
const system = otelSpan.attributes[SEMATTRS_DB_SYSTEM]
const { operation, table } = this.parseStatement(otelSpan, system)
let name = `Datastore/statement/${system}/${table}/${operation}`
// All segment name shapes are same except redis/memcached
if (system === DbSystemValues.REDIS || system === DbSystemValues.MEMCACHED) {
name = `Datastore/operation/${system}/${operation}`
}
return name
table = table || 'Unknown'
operation = operation || 'Unknown'

return { operation, table }
}

function setName(otelSpan) {
const system = otelSpan.attributes[SEMATTRS_DB_SYSTEM]
const { operation, table } = parseStatement(otelSpan, system)
let name = `Datastore/statement/${system}/${table}/${operation}`
// All segment name shapes are same except redis/memcached
if (system === DbSystemValues.REDIS || system === DbSystemValues.MEMCACHED) {
name = `Datastore/operation/${system}/${operation}`
}
return name
}
22 changes: 10 additions & 12 deletions lib/otel/segments/http-external.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,14 @@
const NAMES = require('../../metrics/names')
const { SEMATTRS_HTTP_HOST } = require('@opentelemetry/semantic-conventions')

module.exports = class HttpExternalSegment {
constructor(agent, otelSpan) {
const context = agent.tracer.getContext()
const host = otelSpan.attributes[SEMATTRS_HTTP_HOST] || 'Unknown'
const name = NAMES.EXTERNAL.PREFIX + host
const segment = agent.tracer.createSegment({
name,
parent: context.segment,
transaction: context.transaction
})
return { segment, transaction: context.transaction }
}
module.exports = function createHttpExternalSegment(agent, otelSpan) {
const context = agent.tracer.getContext()
const host = otelSpan.attributes[SEMATTRS_HTTP_HOST] || 'Unknown'
const name = NAMES.EXTERNAL.PREFIX + host
const segment = agent.tracer.createSegment({
name,
parent: context.segment,
transaction: context.transaction
})
return { segment, transaction: context.transaction }
}
12 changes: 6 additions & 6 deletions lib/otel/segments/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
*/

'use strict'
const HttpExternalSegment = require('./http-external')
const DatabaseSegment = require('./database')
const ServerSegment = require('./server')
const createHttpExternalSegment = require('./http-external')
const createDbSegment = require('./database')
const createServerSegment = require('./server')

module.exports = {
DatabaseSegment,
HttpExternalSegment,
ServerSegment
createDbSegment,
createHttpExternalSegment,
createServerSegment
}
121 changes: 59 additions & 62 deletions lib/otel/segments/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,70 +19,67 @@ const Transaction = require('../../transaction')
const urltils = require('../../util/urltils')
const url = require('url')

module.exports = class ServerSegment {
constructor(agent, otelSpan) {
this.agent = agent
this.transaction = new Transaction(agent)
this.transaction.type = 'web'
this.otelSpan = otelSpan
const rpcSystem = otelSpan.attributes[SEMATTRS_RPC_SYSTEM]
const httpMethod = otelSpan.attributes[SEMATTRS_HTTP_METHOD]
if (rpcSystem) {
this.segment = this.rpcSegment(rpcSystem)
} else if (httpMethod) {
this.segment = this.httpSegment(httpMethod)
} else {
this.segment = this.genericHttpSegment()
}
this.transaction.baseSegment = this.segment
return { segment: this.segment, transaction: this.transaction }
module.exports = function createServerSegment(agent, otelSpan) {
const transaction = new Transaction(agent)
transaction.type = 'web'
const rpcSystem = otelSpan.attributes[SEMATTRS_RPC_SYSTEM]
const httpMethod = otelSpan.attributes[SEMATTRS_HTTP_METHOD]
let segment
if (rpcSystem) {
segment = rpcSegment({ agent, otelSpan, transaction, rpcSystem })
} else if (httpMethod) {
segment = httpSegment({ agent, otelSpan, transaction, httpMethod })
} else {
segment = genericHttpSegment({ agent, transaction })
}
transaction.baseSegment = segment
return { segment, transaction }
}

rpcSegment(rpcSystem) {
const rpcService = this.otelSpan.attributes[SEMATTRS_RPC_SERVICE] || 'Unknown'
const rpcMethod = this.otelSpan.attributes[SEMATTRS_RPC_METHOD] || 'Unknown'
const name = `WebTransaction/WebFrameworkUri/${rpcSystem}/${rpcService}.${rpcMethod}`
this.transaction.name = name
this.transaction.trace.attributes.addAttribute(DESTINATION, 'request.method', rpcMethod)
this.transaction.trace.attributes.addAttribute(DESTINATION, 'request.uri', name)
this.transaction.url = name
const segment = this.agent.tracer.createSegment({
name,
parent: this.transaction.trace.root,
transaction: this.transaction
})
segment.addAttribute('component', rpcSystem)
return segment
}
function rpcSegment({ agent, otelSpan, transaction, rpcSystem }) {
const rpcService = otelSpan.attributes[SEMATTRS_RPC_SERVICE] || 'Unknown'
const rpcMethod = otelSpan.attributes[SEMATTRS_RPC_METHOD] || 'Unknown'
const name = `WebTransaction/WebFrameworkUri/${rpcSystem}/${rpcService}.${rpcMethod}`
transaction.name = name
transaction.trace.attributes.addAttribute(DESTINATION, 'request.method', rpcMethod)
transaction.trace.attributes.addAttribute(DESTINATION, 'request.uri', name)
transaction.url = name
const segment = agent.tracer.createSegment({
name,
parent: transaction.trace.root,
transaction
})
segment.addAttribute('component', rpcSystem)
return segment
}

// most instrumentation will hit this case
// I find that if the request is in a web framework, the web framework instrumentation
// sets `http.route` and when the span closes it pulls that attribute in
// we'll most likely need to wire up some naming reconciliation
// to handle this use case.
httpSegment(httpMethod) {
const httpRoute = this.otelSpan.attributes[SEMATTRS_HTTP_ROUTE] || 'Unknown'
const httpUrl = this.otelSpan.attributes[SEMATTRS_HTTP_URL] || '/Unknown'
const requestUrl = url.parse(httpUrl, true)
const name = `WebTransaction/Nodejs/${httpMethod}/${httpRoute}`
this.transaction.name = name
this.transaction.url = urltils.obfuscatePath(this.agent.config, requestUrl.pathname)
this.transaction.trace.attributes.addAttribute(DESTINATION, 'request.uri', this.transaction.url)
this.transaction.trace.attributes.addAttribute(DESTINATION, 'request.method', httpMethod)
return this.agent.tracer.createSegment({
name,
parent: this.transaction.trace.root,
transaction: this.transaction
})
}
// most instrumentation will hit this case
// I find that if the request is in a web framework, the web framework instrumentation
// sets `http.route` and when the span closes it pulls that attribute in
// we'll most likely need to wire up some naming reconciliation
// to handle this use case.
function httpSegment({ agent, otelSpan, transaction, httpMethod }) {
const httpRoute = otelSpan.attributes[SEMATTRS_HTTP_ROUTE] || 'Unknown'
const httpUrl = otelSpan.attributes[SEMATTRS_HTTP_URL] || '/Unknown'
const requestUrl = url.parse(httpUrl, true)
const name = `WebTransaction/Nodejs/${httpMethod}/${httpRoute}`
transaction.name = name
transaction.url = urltils.obfuscatePath(agent.config, requestUrl.pathname)
transaction.trace.attributes.addAttribute(DESTINATION, 'request.uri', transaction.url)
transaction.trace.attributes.addAttribute(DESTINATION, 'request.method', httpMethod)
return agent.tracer.createSegment({
name,
parent: transaction.trace.root,
transaction
})
}

genericHttpSegment() {
const name = 'WebTransaction/NormalizedUri/*'
this.transaction.name = name
return this.agent.tracer.createSegment({
name,
parent: this.transaction.trace.root,
transaction: this.transaction
})
}
function genericHttpSegment({ agent, transaction }) {
const name = 'WebTransaction/NormalizedUri/*'
transaction.name = name
return agent.tracer.createSegment({
name,
parent: transaction.trace.root,
transaction: transaction
})
}

0 comments on commit 693a3e5

Please sign in to comment.