Skip to content

Commit

Permalink
stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
jsumners-nr committed Sep 25, 2024
1 parent 2921ce8 commit 5926ea7
Show file tree
Hide file tree
Showing 6 changed files with 277 additions and 29 deletions.
62 changes: 62 additions & 0 deletions lib/instrumentation/otel/context-manager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright 2024 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

'use strict'

const { ROOT_CONTEXT } = require('@opentelemetry/api')

/**
* @see https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api.ContextManager.html
*/
class ContextManager {
#agent

#localStorage

constructor(agent) {
if (!agent) {
agent = require('../../../index').agent
}

this.#agent = agent
this.#localStorage = agent._contextManager._asyncLocalStorage
}

active() {
// TODO: get current span(segment?) from agent and prefer it
const storedContext = this.#localStorage.getStore()
return storedContext || ROOT_CONTEXT
}

bind(context, target) {
return boundContext.bind(this)

function boundContext(...args) {
return this.with(context, target, this, ...args)
}
}

/**
* Runs the callback within the provided context, optionally
* bound with a provided `this`.
*
* @param context
* @param callback
* @param thisRef
* @param args
*/
with(context, callback, thisRef, ...args) {
return this.#agent._contextManager.runInContext(context, callback, thisRef, args)
}

enable() {
return this
}
disable() {
return this
}
}

module.exports = ContextManager
41 changes: 12 additions & 29 deletions lib/instrumentation/otel/otel.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

'use strict'

const { ClassWrapSpec, WrapSpec } = require('../../shim/specs')
// const { ClassWrapSpec, WrapSpec } = require('../../shim/specs')

module.exports = function instrumentOtel(shim, OtelTracerProvider) {
const { agent } = shim
Expand All @@ -14,33 +14,16 @@ module.exports = function instrumentOtel(shim, OtelTracerProvider) {
return
}

// return new Proxy(OtelTracerProvider, {
// get(target, prop) {
// if (prop === 'NodeTracerProvider') {
// return function () {
// console.log('patched')
// }
// }
// return target[prop]
// }
// })

shim.wrapReturn(OtelTracerProvider, 'NodeTracerProvider', function () {
console.log('patched')
shim.wrapExport(OtelTracerProvider, () => {
return new Proxy(OtelTracerProvider, {
get(target, prop) {
if (prop === 'NodeTracerProvider') {
return function () {
console.log('patched')
}
}
return target[prop]
}
})
})

// shim.wrapClass(
// OtelTracerProvider,
// 'NodeTracerProvider',
// new ClassWrapSpec({
// pre(shim, toWrapClass, name, args) {
// console.log(`wrapping class ${name}:`, args)
// }
// // post: function nrConstructorWrapper(shim, wrappedClass, name, args) {
// // this[kafkaCtx] = { brokers: args[0].brokers }
// // shim.wrap(this, 'producer', instrumentProducer)
// // shim.wrap(this, 'consumer', instrumentConsumer)
// // }
// })
// )
}
48 changes: 48 additions & 0 deletions lib/instrumentation/otel/span-processors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 2024 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

'use strict'

/**
* @see https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_sdk_trace_base.SpanProcessor.html
*/
class NoopSpanProcessor {
async forceFlush() {}
onStart() {}
onEnd() {}
async shutdown() {}
}

class MultiSpanProcessor {
#processors = []

constructor(processors) {
for (const p of processors) {
this.#processors.push(p)
}
}

forceFlush() {
return Promise.all(this.#processors.map((p) => p.forceFlush()))
}

onStart(span, context) {
for (const p of this.#processors) {
p.onStart(span, context)
}
}

onEnd(span) {
for (const p of this.#processors) {
p.onEnd(span)
}
}

shutdown() {
return Promise.all(this.#processors.map((p) => p.shutdown()))
}
}

module.exports = { NoopSpanProcessor, MultiSpanProcessor }
125 changes: 125 additions & 0 deletions lib/instrumentation/otel/tracer-provider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* Copyright 2024 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

'use strict'

const {
context: otelContextApi,
propagation: otelPropagatorApi,
trace: otelTraceApi
} = require('@opentelemetry/api')
const { W3CTraceContextPropagator } = require('@opentelemetry/core')

const ContextManager = require('./context-manager')
const Tracer = require('./tracer')
const { NoopSpanProcessor, MultiSpanProcessor } = require('./span-processors')

/**
* @see https://opentelemetry.io/docs/specs/otel/trace/api/#tracerprovider
* @see https://open-telemetry.github.io/opentelemetry-js/classes/_opentelemetry_sdk_trace_node.NodeTracerProvider.html
*/
class NRTracerProvider {
#config
#resource

#tracers
#contextManager
#processors = []
#activeProcessor

/**
*
* @param {object} config https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_sdk_trace_base.TracerConfig.html
*/
constructor(config = {}) {
this.#config = config
this.#resource = config.resource
this.#tracers = new Map()
this.#contextManager = new ContextManager()
this.addSpanProcessor(new NoopSpanProcessor())
}

/**
* @param processor
*
* @see https://open-telemetry.github.io/opentelemetry-js/classes/_opentelemetry_sdk_trace_base.BasicTracerProvider.html#addSpanProcessor
*/
addSpanProcessor(processor) {
this.#processors.push(processor)
this.#activeProcessor = new MultiSpanProcessor(this.#processors)
}

/**
* @see https://open-telemetry.github.io/opentelemetry-js/classes/_opentelemetry_sdk_trace_base.BasicTracerProvider.html#getActiveSpanProcessor
*/
getActiveSpanProcessor() {
return this.#activeProcessor
}

/**
* Retrieve a tracer, or return a new one if none found.
*
* @param {string} name Tracer scope name.
* @param {string} [version] A semver style version string representing the
* version of the scoped entity being traced.
* @param {object} [options] Additional options.
* @param {string} [options.schemaUrl] Schema URL to be emitted in telemetry.
*
* @returns {Tracer} The found, or created, tracer.
*
* @see https://opentelemetry.io/docs/specs/otel/trace/api/#get-a-tracer
* @see https://open-telemetry.github.io/opentelemetry-js/classes/_opentelemetry_sdk_trace_base.BasicTracerProvider.html#getTracer
*/
getTracer(name, version = '0.0.0', options = {}) {
const { schemaUrl } = options
// Key as constructed upstream: https://github.com/open-telemetry/opentelemetry-js/blob/2a4919c1cf99d3403d387d7589836fd9e3018896/packages/opentelemetry-sdk-trace-base/src/BasicTracerProvider.ts#L102
const key = `${name}@${version}:${schemaUrl || ''}`
if (this.#tracers.has(key) === false) {
// The spec says we _must_ return a fallback tracer rather than
// returning `null` or throwing an error when the provided name is empty.
const tracer = new Tracer({ name, version, schemaUrl }, this.#config, this)
this.#tracers.set(key, tracer)
return tracer
}
return this.#tracers.get(key)
}

/**
* The constructor sets up some basic class fields. This, as highlighted in
* the documenation, is meant to be invoked subsequent to class instantiation
* as a means to establish more specific configuration.
*
* @param {object} [config]
*
* @see https://github.com/open-telemetry/opentelemetry-js/blob/9de31518e76a38050f1a5676124fffd259085263/packages/opentelemetry-sdk-trace-node/README.md
*/
register(config = {}) {
otelContextApi.setGlobalContextManager(this.#contextManager)
if (otelTraceApi.setGlobalTracerProvider(this) === false) {
// https://open-telemetry.github.io/opentelemetry-js/classes/_opentelemetry_api.TraceAPI.html#setGlobalTracerProvider
// The default tracer provider defined by the API is a
// `ProxyTracerProvider` instance. That class provides a unique method,
// `setDelegate`, that we can use instead.
otelTraceApi.getTracerProvider().setDelegate(this)
}

if (config.propagator) {
otelPropagatorApi.setGlobalPropagator(config.propagator)
} else {
otelPropagatorApi.setGlobalPropagator(new W3CTraceContextPropagator())
}
}

forceFlush() {
// TODO: invoke the agent's flush
return this.#activeProcessor.forceFlush()
}

shutdown() {
return this.#activeProcessor.shutdown()
}
}

module.exports = NRTracerProvider
28 changes: 28 additions & 0 deletions lib/instrumentation/otel/tracer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2024 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

'use strict'

/**
* @see https://opentelemetry.io/docs/specs/otel/trace/api/#tracer
* @see https://open-telemetry.github.io/opentelemetry-js/classes/_opentelemetry_sdk_trace_base.Tracer.html
*/
class Tracer {
#config
#instrumentationLibrary
#tracerProvider

constructor(instrumentationLibrary, config, tracerProvider) {
this.#config = config
this.#instrumentationLibrary = instrumentationLibrary
this.#tracerProvider = tracerProvider
}

get instrumentationLibrary() {
return this.#instrumentationLibrary
}
}

module.exports = Tracer
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@
"@grpc/grpc-js": "^1.9.4",
"@grpc/proto-loader": "^0.7.5",
"@newrelic/security-agent": "^2.0.0",
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/core": "^1.26.0",
"@opentelemetry/sdk-node": "^0.53.0",
"@tyriar/fibonacci-heap": "^2.0.7",
"concat-stream": "^2.0.0",
Expand Down

0 comments on commit 5926ea7

Please sign in to comment.