Skip to content

feat: OpenTelemetry Bridge #2641

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 78 commits into from
May 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
9a811d0
feat: OpenTelemetry Bridge
trentm Apr 7, 2022
e6ba3cf
Support for overriding RunContext class, use that for OTelBridgeRunCo…
trentm Apr 7, 2022
577dd59
Clean up this example a bit.
trentm Apr 8, 2022
2f338b8
first crack at Context management working
trentm Apr 8, 2022
c0e15f4
fix 'make check'
trentm Apr 8, 2022
25c4ab5
Merge branch 'main' into trentm/otel-bridge
trentm Apr 11, 2022
d3c0620
s/Elastic/OTel/ prefix naming in the OTel Bridge
trentm Apr 11, 2022
170a7f0
renaming lib/otelbridge to lib/opentelemetry-sdk
trentm Apr 11, 2022
28e4e9b
move OTel SDK setup code to lib/opentelemetry-sdk/setup.js
trentm Apr 11, 2022
fbfae75
forgot to include these files
trentm Apr 11, 2022
ea17665
initial otel-sdk test
trentm Apr 12, 2022
859778c
Add new test dir to test runner.
trentm Apr 12, 2022
7001b2b
fixtures/nonrecordingspan-parent.js - get this working, adds OTelBrid…
trentm Apr 14, 2022
a1b37cb
use 'otel' as the less ambiguous import name
trentm Apr 14, 2022
690391b
fix lint
trentm Apr 14, 2022
285c49a
hook up OTel API diag to logging; put in some during-dev OTel API cal…
trentm Apr 14, 2022
9727baa
support otel.ROOT_CONTEXT usage to otel.context.with()
trentm Apr 18, 2022
da30e6b
some comments, tweaks, fix 'make check'
trentm Apr 18, 2022
51d278a
forgot to add this file
trentm Apr 18, 2022
6a8176f
test case showing distributed tracing working with auto-instrumentati…
trentm Apr 19, 2022
dc480b8
test case for 'context' arg to 'startSpan'; handle edge case of paren…
trentm Apr 19, 2022
e4c9a83
try lifting the restriction that a span cannot be started on an *ende…
trentm Apr 20, 2022
cce76de
allowing startSpan on an ended transaction
trentm Apr 20, 2022
a3b9baf
start working through Tracer interface: start SpanOptions.{kind,start…
trentm Apr 21, 2022
691514b
Merge branch 'main' into trentm/otel-bridge
trentm Apr 21, 2022
22a8b39
SpanOptions.root; add two fixtures I'd forgotten to add
trentm Apr 21, 2022
c02778b
fix make check; remove some cruft
trentm Apr 22, 2022
7450313
Merge branch 'main' into trentm/otel-bridge
trentm Apr 25, 2022
4a311d9
impl tracer.startActiveSpan(...)
trentm Apr 25, 2022
190b761
use single quotes, because double-quotes are used in ecslog pretty ou…
trentm Apr 26, 2022
8eb8b27
trace propagation for OTelBridgeNonRecordingSpan
trentm Apr 26, 2022
0bd80c9
innocuous change to test commit signing
trentm Apr 26, 2022
47458f5
Merge branch 'main' into trentm/otel-bridge
trentm Apr 26, 2022
70e73d7
var renaming, osdklog usage
trentm Apr 27, 2022
a07058a
skip this test with node.js 10.0 - 10.3 to workaround buggy async con…
trentm Apr 27, 2022
bb8e1d4
add a TODO for later
trentm Apr 27, 2022
4d26a3b
Skip this test with asyncHooks=false due to some patch-async issue wi…
trentm Apr 27, 2022
3b5c5fe
start implementing and testing parts of OTel interface Span (not yet …
trentm Apr 27, 2022
963c05f
OTelSpan: .setStatus(), .updateName(), .end(endTime)
trentm Apr 28, 2022
2e63253
improve HrTime usage in test fixtures: get the second component close…
trentm Apr 28, 2022
9b3cde7
some interface Span test improvements, more coverage
trentm Apr 28, 2022
7e15f12
test: Span#isRecording()
trentm Apr 28, 2022
e06ef43
OTelSpan.recordException(); add opts.parent to agent.captureError (th…
trentm Apr 28, 2022
3526387
propagate TraceState in OTelBridgeNonRecordingSpan instances
trentm Apr 29, 2022
901c08e
opentelemetry-core-mini are (almost) directly from @opentelemetry/cor…
trentm Apr 29, 2022
fea7a7a
test fix: now that tracestate propagation is implemented, we expect t…
trentm Apr 29, 2022
2a55d5d
Merge branch 'main' into trentm/otel-bridge
trentm May 2, 2022
2936379
nope, don't need this; this is about 'framework' on *transactions* wh…
trentm Apr 29, 2022
0a02025
test everything spec'd in otel_bridge.feature
trentm May 4, 2022
5f97a55
Merge branch 'main' into trentm/otel-bridge
trentm May 4, 2022
7e0e19d
node v8 does not support optional catch binding
trentm May 4, 2022
db71ffd
add tests specifically for interop of context and active spans betwee…
trentm May 4, 2022
88e785d
impl ContextManager.bind(); test all of interface ContextManager
trentm May 5, 2022
823ced5
drop 'opentelemetry sdk' terminology (misleading) in favour of 'opent…
trentm May 5, 2022
69a7a73
final part of s/otel sdk/otel bridge/ terminology change
trentm May 5, 2022
932abe2
documenting oblog
trentm May 5, 2022
1d368d9
update to @opentelemetry/[email protected]; update examples/otel deps
trentm May 5, 2022
104968d
fill out OTelBridgeNonRecordingSpan to impl otel interface Span and e…
trentm May 6, 2022
44b7f7e
OTelBridgeRunContext.deleteValue(SPAN_KEY) handling; test OTelBridgeR…
trentm May 6, 2022
3147356
fix 'make check'
trentm May 6, 2022
a232ad3
test: re-enable this config setting to avoid test bleeding out to an …
trentm May 6, 2022
a2f60e8
drop otel.propagation.setGlobalPropagator(...), AFAIK we don't need t…
trentm May 6, 2022
54d3508
opt to not report otel attributes as span labels for older APM Server…
trentm May 6, 2022
bc43f01
Merge branch 'main' into trentm/otel-bridge
trentm May 6, 2022
20f35be
working through todos; documenting limitations/diffs
trentm May 6, 2022
14f49a5
working through todos
trentm May 6, 2022
dd2a295
clean up examples a bit
trentm May 6, 2022
e4e5e5f
working through todos, doc'ing a bit more
trentm May 6, 2022
cdeb663
rename examples dir
trentm May 6, 2022
a5835af
dev docs for the otel bridge
trentm May 7, 2022
b6081e5
trim out drivel in the dev docs
trentm May 7, 2022
9408dab
proofreading the dev docs
trentm May 7, 2022
18fa679
more proofreading
trentm May 7, 2022
8ed5623
user docs for the OTel bridge, deprecate OpenTracing bridge
trentm May 10, 2022
68de9aa
changelog entry; some PR feedback
trentm May 11, 2022
2955431
README links into the docs update for the bridge
trentm May 11, 2022
5ed8106
note that the bridge is experimental in the config reference where yo…
trentm May 11, 2022
18d91e1
Merge branch 'main' into trentm/otel-bridge
trentm May 11, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"ignorePatterns": [
"/.nyc_output",
"/build",
"node_modules",
"/lib/opentelemetry-bridge/opentelemetry-core-mini",
"/test/babel/out.js",
"/test/sourcemaps/fixtures/lib",
"/test/stacktraces/fixtures/dist",
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@
/.nyc_output
/test_output
/build
/node_modules
node_modules
/test/benchmarks/.tmp
/tmp
18 changes: 18 additions & 0 deletions CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,24 @@ Notes:
[[release-notes-3.x]]
=== Node.js Agent version 3.x

==== Unreleased

[float]
===== Breaking changes

[float]
===== Features

- Add an experimental <<opentelemetry-bridge>>. Briefly, the OpenTelemetry
Bridge allows one to use the vendor-neutral
https://opentelemetry.io/docs/instrumentation/js/api/[OpenTelemetry Tracing
API] (https://www.npmjs.com/package/@opentelemetry/api[`@opentelemetry/api`])
to manually instrument your code, and have the Elastic Node.js APM agent
handle those API calls. ({pull}2641[#2641])

[float]
===== Bug fixes


[[release-notes-3.33.0]]
==== 3.33.0 2022/05/05
Expand Down
16 changes: 16 additions & 0 deletions NOTICE.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,22 @@ THE SOFTWARE.
Parts of "lib/instrumentation/run-context" have been adapted from or influenced
by TypeScript code in `@opentelemetry/context-async-hooks`.

- **path:** [lib/opentelemetry-bridge/otelutils.js](lib/opentelemetry-bridge/otelutils.js)
- **author:** OpenTelemetry Authors
- **project url:** https://github.com/open-telemetry/opentelemetry-js
- **original file:** https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-core/src/common/time.ts
- **license:** Apache License 2.0, https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-core/LICENSE

"lib/opentelemetry-bridge/opentelemetry-core-mini/" includes files adapted from
code in `@opentelemetry/core`.

- **path:** [lib/opentelemetry-bridge/opentelemetry-core-mini/](lib/opentelemetry-bridge/opentelemetry-core-mini/)
- **author:** OpenTelemetry Authors
- **project url:** https://github.com/open-telemetry/opentelemetry-js
- **original file:** https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-core/src/
- **license:** Apache License 2.0, https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-core/LICENSE


## load-source-map

- **path:** [lib/load-source-map.js](lib/load-source-map.js)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ npm install elastic-apm-node --save
- [Get started with a custom Node.js stack](https://www.elastic.co/guide/en/apm/agent/nodejs/current/custom-stack.html)
- [Advanced Setup and Configuration](https://www.elastic.co/guide/en/apm/agent/nodejs/current/advanced-setup.html)
- [API Reference](https://www.elastic.co/guide/en/apm/agent/nodejs/current/api.html)
- [OpenTelemetry Bridge](https://www.elastic.co/guide/en/apm/agent/nodejs/current/opentelemetry-bridge.html)
- [Custom Transactions](https://www.elastic.co/guide/en/apm/agent/nodejs/current/custom-transactions.html)
- [Custom Spans](https://www.elastic.co/guide/en/apm/agent/nodejs/current/custom-spans.html)
- [Metrics](https://www.elastic.co/guide/en/apm/agent/nodejs/current/metrics.html)
- [Performance Tuning](https://www.elastic.co/guide/en/apm/agent/nodejs/current/performance-tuning.html)
- [Source Map Support](https://www.elastic.co/guide/en/apm/agent/nodejs/current/source-maps.html)
- [OpenTracing Support](https://www.elastic.co/guide/en/apm/agent/nodejs/current/opentracing.html)
- [Supported Technologies](https://www.elastic.co/guide/en/apm/agent/nodejs/current/supported-technologies.html)
- [Upgrading](https://www.elastic.co/guide/en/apm/agent/nodejs/current/upgrading.html)
- [Troubleshooting](https://www.elastic.co/guide/en/apm/agent/nodejs/current/troubleshooting.html)
Expand Down
147 changes: 147 additions & 0 deletions docs/api-opentelemetry.asciidoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
ifdef::env-github[]
NOTE: For the best reading experience,
please view this documentation at https://www.elastic.co/guide/en/apm/agent/nodejs/current/opentelemetry-bridge.html[elastic.co]
endif::[]

[[opentelemetry-bridge]]
== OpenTelemetry bridge

NOTE: Added as experimental in v3.34.0.
To enable it, set <<opentelemetry-bridge-enabled, `opentelemetryBridgeEnabled`>> to `true`.

The Elastic APM OpenTelemetry bridge allows one to use the vendor-neutral
https://opentelemetry.io/docs/instrumentation/js/api/[OpenTelemetry Tracing API]
(https://www.npmjs.com/package/@opentelemetry/api[`@opentelemetry/api`]) to
manually instrument your code, and have the Elastic Node.js APM agent handle
those API calls. This allows one to use the Elastic APM agent for tracing,
without any vendor lock-in from adding manual tracing using the APM agent's own
<<api,public API>>.


[float]
[[otel-getting-started]]
=== Getting started

The goal of the OpenTelemetry bridge is to allow using the OpenTelemetry API
with the APM agent. ① First, you will need to add those dependencies to your
project. The minimum required OpenTelemetry API version is 1.0.0. For example:

[source,bash]
----
npm install --save elastic-apm-node @opentelemetry/api
----

② Second, you will need to configure and start the APM agent. This can be done
completely with environment variables (so that there is no need to touch
your application code):

[source,bash]
----
export ELASTIC_APM_SERVER_URL='<url of your APM server>'
export ELASTIC_APM_SECRET_TOKEN='<secret token for your APM server>' # or ELASTIC_APM_API_KEY=...
export ELASTIC_APM_OPENTELEMETRY_BRIDGE_ENABLED=true
export NODE_OPTIONS='-r elastic-apm-node/start.js' # Tell node to preload and start the APM agent
node my-app.js
----

Or, alternatively, you can configure and start the APM agent at the top of your
application code as follows. (Note: For automatic instrumentations to function
properly, this must be executed before other `require` statements and
application code.)

[source,js]
----
require('elastic-apm-node').start({
serverUrl: '<url of your APM server>',
secretToken: '<secret token for your APM server>', // or, apiKey: '<your API key>'
opentelemetryBridgeEnabled: true
});

// Application code ...
----

NOTE: These examples show the minimal configuration. See <<configuration,the full APM agent configuration reference>> for other configuration options.

③ Finally, you can use the OpenTelemetry API for any manual tracing in your code.
For example, the following script uses
https://open-telemetry.github.io/opentelemetry-js-api/interfaces/tracer.html#startactivespan[Tracer#startActiveSpan()]
to trace an outgoing HTTPS request:

[source,js]
----
const https = require('https')
const otel = require('@opentelemetry/api')
const tracer = otel.trace.getTracer('trace-https-request')

tracer.startActiveSpan('makeRequest', span => {
https.get('https://httpstat.us/200', (response) => {
console.log('STATUS:', response.statusCode)
const body = []
response.on('data', (chunk) => body.push(chunk))
response.on('end', () => {
console.log('BODY:', body.toString())
span.end()
})
})
})
----

The APM agent source code repository includes
https://github.com/elastic/apm-agent-nodejs/tree/main/examples/opentelemetry-bridge[some examples using the OpenTelemetry bridge].


[float]
[[otel-architecture]]
=== Bridge architecture

The OpenTelemetry bridge works similarly to the
https://github.com/open-telemetry/opentelemetry-js[OpenTelemetry JS SDK]. It
registers Tracer and ContextManager providers with the OpenTelemetry API.
Subsequent `@opentelemetry/api` calls in user code will call into those
providers. The APM agent translates from OpenTelemetry to Elastic APM semantics
and sends tracing data to your APM server for full support in
https://www.elastic.co/apm[Elastic Observability's APM app].

Here are a couple examples of semantic translations: The first entry span of a
service (e.g. an incoming HTTP request) will be converted to an
{apm-guide-ref}/data-model-transactions.html[Elasic APM `Transaction`],
subsequent spans are mapped to
{apm-guide-ref}/data-model-spans.html[Elastic APM `Span`]. OpenTelemetry Span
attributes are translated into the appropriate fields in Elastic APM's data
model.

The only difference, from the user's point of view, is in the setup of tracing.
Instead of setting up the OpenTelemetry JS SDK, one sets up the APM agent
as <<otel-getting-started,described above>>.


[float]
[[otel-caveats]]
=== Caveats
Not all features of the OpenTelemetry API are supported.

[float]
[[otel-metrics]]
===== Metrics
This bridge only supports the tracing API.
The Metrics API is currently not supported.

[float]
[[otel-span-links]]
===== Span Links
Adding links when
https://open-telemetry.github.io/opentelemetry-js-api/interfaces/tracer.html[starting a span]
are not currently supported. Any given links will be silently dropped.

[float]
[[otel-span-events]]
===== Span Events
Span events (https://open-telemetry.github.io/opentelemetry-js-api/interfaces/span.html#addevent[`Span#addEvent()`])
is not currently supported. Events will be silently dropped.

[float]
[[otel-baggage]]
===== Baggage
https://open-telemetry.github.io/opentelemetry-js-api/classes/propagationapi.html[Propagating baggage]
within or outside the process is not supported. Baggage items are silently
dropped.
24 changes: 24 additions & 0 deletions docs/configuration.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -1235,3 +1235,27 @@ require('elastic-apm-node').start({
})
----


[[opentelemetry-bridge-enabled]]
==== `opentelemetryBridgeEnabled`

[small]#Added in: v3.34.0 as experimental#

* *Type:* Boolean
* *Default:* `false`
* *Env:* `ELASTIC_APM_OPENTELEMETRY_BRIDGE_ENABLED`

Setting this option to true will enable the <<opentelemetry-bridge,OpenTelemetry Bridge>>. Briefly, the OpenTelemetry Bridge allows one to use the vendor-neutral
https://opentelemetry.io/docs/instrumentation/js/api/[OpenTelemetry Tracing API]
(https://www.npmjs.com/package/@opentelemetry/api[`@opentelemetry/api`]) to
manually instrument your code, and have the Elastic Node.js APM agent handle
those API calls.

Example usage:

[source,js]
----
require('elastic-apm-node').start({
opentelemetryBridgeEnabled: true
})
----
2 changes: 2 additions & 0 deletions docs/index.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ include::./api.asciidoc[]

include::./metrics.asciidoc[]

include::./api-opentelemetry.asciidoc[]

include::./opentracing.asciidoc[]

include::./log-correlation.asciidoc[]
Expand Down
5 changes: 4 additions & 1 deletion docs/opentracing.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ NOTE: For the best reading experience,
please view this documentation at https://www.elastic.co/guide/en/apm/agent/nodejs/current/opentracing.html[elastic.co]
endif::[]

== OpenTracing API
== OpenTracing bridge

NOTE: https://opentracing.io/[OpenTracing] is discontinued in favor of OpenTelemetry. This Elastic APM OpenTracing bridge is **deprecated**. Consider using the <<opentelemetry-bridge>> instead.


The Elastic APM OpenTracing bridge allows creating Elastic APM transactions and spans,
using the https://opentracing-javascript.surge.sh/[OpenTracing API].
Expand Down
1 change: 1 addition & 0 deletions examples/opentelemetry-bridge/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/node_modules
1 change: 1 addition & 0 deletions examples/opentelemetry-bridge/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package-lock=false
41 changes: 41 additions & 0 deletions examples/opentelemetry-bridge/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
This directory includes example Node.js scripts showing usage of the
OpenTelemetry JS API. These can be instrumented with the Elastic Node.js APM
agent using its OpenTelemetry Bridge.

Setup dependencies via:

npm install

To run a script using the **Elastic Node.js APM Agent** use:

export ELASTIC_APM_OPENTELEMETRY_BRIDGE_ENABLED=true
node -r elastic-apm-node/start.js THE-SCRIPT.js

For example:

export ELASTIC_APM_OPENTELEMETRY_BRIDGE_ENABLED=true
node -r elastic-apm-node/start.js trace-https-request.js

While these examples are written to use the `node -r elastic-apm-node/start.js ...`
mechanism to start the APM agent. That isn't required. One can still load and
start the APM agent at the top of a script like this:

```js
require('elastic-apm-node').start({
opentelemetryBridgeEnabled: true
// serviceName: ...
// serverUrl: ...
// secretToken: ...
})
```

## Compare to using the OpenTelemetry JS SDK

For comparison, these scripts can be instrumented with the OpenTelemetry JS SDK
with something like the following:

node -r ./otel-sdk.js trace-https-request.js

The "otel-sdk.js" is a simplified tracing setup of the OpenTelemetry SDK. For
the sake of simpler demonstration it writes tracing spans to the console rather
than sending to some collection service (like Jaeger or Elastic APM).
35 changes: 35 additions & 0 deletions examples/opentelemetry-bridge/otel-sdk.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use strict'

// Start a simply-configured OpenTelemetry SDK for Node.js tracing for demo
// purposes.
//
// Based on https://github.com/open-telemetry/opentelemetry-js/blob/main/examples/http/tracer.js
//
// Usage:
// node -r ./otel-sdk.js MY-SCRIPT.js

// Uncomment this to get OpenTelemetry internal diagnostic messages.
// const otel = require('@opentelemetry/api')
// otel.diag.setLogger({
// verbose () { console.log('diag VERBOSE:', ...arguments) },
// debug () { console.log('diag DEBUG:', ...arguments) },
// info () { console.log('diag INFO:', ...arguments) },
// warn () { console.log('diag WARN:', ...arguments) },
// error () { console.log('diag ERROR:', ...arguments) }
// }, opentelemetry.DiagLogLevel.ALL)

const { registerInstrumentations } = require('@opentelemetry/instrumentation')
const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node')
const { SimpleSpanProcessor, ConsoleSpanExporter } = require('@opentelemetry/sdk-trace-base')
const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http')

module.exports = (() => {
const provider = new NodeTracerProvider()
provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter()))
provider.register()
registerInstrumentations({
instrumentations: [
new HttpInstrumentation()
]
})
})()
18 changes: 18 additions & 0 deletions examples/opentelemetry-bridge/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "elastic-apm-node-opentelemetry-bridge-examples",
"version": "1.0.0",
"private": true,
"scripts": {
"trace-hello-world": "ELASTIC_APM_OPENTELEMETRY_BRIDGE_ENABLED=true node -r elastic-apm-node/start.js trace-hello-world.js",
"trace-https-request": "ELASTIC_APM_OPENTELEMETRY_BRIDGE_ENABLED=true node -r elastic-apm-node/start.js trace-https-request.js"
},
"dependencies": {
"@opentelemetry/api": "^1.1.0",
"@opentelemetry/core": "^1.2.0",
"@opentelemetry/instrumentation": "^0.28.0",
"@opentelemetry/instrumentation-http": "^0.28.0",
"@opentelemetry/sdk-trace-base": "^1.2.0",
"@opentelemetry/sdk-trace-node": "^1.2.0",
"elastic-apm-node": "file:../.."
}
}
Loading