@prairielearn/opentelemetry 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +19 -0
- package/README.md +19 -4
- package/dist/index.d.ts +11 -2
- package/dist/index.js +92 -40
- package/dist/index.js.map +1 -1
- package/dist/index.test.d.ts +1 -0
- package/dist/index.test.js +65 -0
- package/dist/index.test.js.map +1 -0
- package/package.json +14 -10
- package/src/index.test.ts +75 -0
- package/src/index.ts +97 -36
- package/tsconfig.json +2 -11
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
@prairielearn/opentelemetry:build: cache hit, replaying output
|
|
1
|
+
@prairielearn/opentelemetry:build: cache hit, replaying output a5f2c3f86efdd20e
|
|
2
2
|
@prairielearn/opentelemetry:build: warning package.json: No license field
|
|
3
3
|
@prairielearn/opentelemetry:build: $ tsc
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# @prairielearn/opentelemetry
|
|
2
|
+
|
|
3
|
+
## 1.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 2ef7a73e8: Add `instrumented()` helper to instrument function calls
|
|
8
|
+
|
|
9
|
+
## 1.1.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- ff23653ef: Don't handle `SIGTERM`; expose `shutdown()` function
|
|
14
|
+
|
|
15
|
+
## 1.0.1
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- 5346eedc1: Don't disable instrumentation if OpenTelemetry isn't enabled
|
package/README.md
CHANGED
|
@@ -23,13 +23,28 @@ await init({
|
|
|
23
23
|
|
|
24
24
|
This will automatically instrument a variety of commonly-used Node packages.
|
|
25
25
|
|
|
26
|
-
To
|
|
26
|
+
To easily instrument individual pieces of functionality, you can use the `instrumented()` helper function:
|
|
27
27
|
|
|
28
28
|
```ts
|
|
29
|
-
import {
|
|
29
|
+
import { instrumented } from '@prairielearn/opentelemetry';
|
|
30
|
+
|
|
31
|
+
async function doThing() {
|
|
32
|
+
return instrumented('span.name', async (span) => {
|
|
33
|
+
span.setAttribute('attribute.name', 'value');
|
|
34
|
+
await doThing();
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
This will automatically set the span status and record any exceptions that occur.
|
|
40
|
+
|
|
41
|
+
If you have a more complex use case, you can manually instrument code with the `trace` export:
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import { trace, SpanStatusCode } from '@prairielearn/opentelemetry';
|
|
30
45
|
|
|
31
|
-
const tracer = trace.getTracer('
|
|
32
|
-
await tracer.startActiveSpan('span
|
|
46
|
+
const tracer = trace.getTracer('default');
|
|
47
|
+
await tracer.startActiveSpan('span.name', async (span) => {
|
|
33
48
|
try {
|
|
34
49
|
await doWork();
|
|
35
50
|
span.setStatus({ status: SpanStatusCode.OK });
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { SpanExporter } from '@opentelemetry/sdk-trace-base';
|
|
2
|
+
import { Span } from '@opentelemetry/api';
|
|
1
3
|
export interface OpenTelemetryConfig {
|
|
2
4
|
openTelemetryEnabled: boolean;
|
|
3
|
-
openTelemetryExporter
|
|
4
|
-
openTelemetrySamplerType
|
|
5
|
+
openTelemetryExporter: 'console' | 'honeycomb' | SpanExporter;
|
|
6
|
+
openTelemetrySamplerType: 'always-on' | 'always-off' | 'trace-id-ratio';
|
|
5
7
|
openTelemetrySampleRate?: number;
|
|
8
|
+
openTelemetrySpanProcessor?: 'batch' | 'simple';
|
|
6
9
|
honeycombApiKey?: string;
|
|
7
10
|
honeycombDataset?: string;
|
|
8
11
|
serviceName?: string;
|
|
@@ -13,5 +16,11 @@ export interface OpenTelemetryConfig {
|
|
|
13
16
|
* information available until we've loaded our config.
|
|
14
17
|
*/
|
|
15
18
|
export declare function init(config: OpenTelemetryConfig): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Gracefully shuts down the OpenTelemetry instrumentation. Should be called
|
|
21
|
+
* when a `SIGTERM` signal is handled.
|
|
22
|
+
*/
|
|
23
|
+
export declare function shutdown(): Promise<void>;
|
|
24
|
+
export declare function instrumented<T>(name: string, fn: (span: Span) => Promise<T> | T): Promise<T>;
|
|
16
25
|
export { trace, context, SpanStatusCode } from '@opentelemetry/api';
|
|
17
26
|
export { suppressTracing } from '@opentelemetry/core';
|
package/dist/index.js
CHANGED
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.suppressTracing = exports.SpanStatusCode = exports.context = exports.trace = exports.init = void 0;
|
|
7
|
-
const process_1 = __importDefault(require("process"));
|
|
3
|
+
exports.suppressTracing = exports.SpanStatusCode = exports.context = exports.trace = exports.instrumented = exports.shutdown = exports.init = void 0;
|
|
8
4
|
const grpc_js_1 = require("@grpc/grpc-js");
|
|
9
5
|
const sdk_node_1 = require("@opentelemetry/sdk-node");
|
|
10
6
|
const sdk_trace_node_1 = require("@opentelemetry/sdk-trace-node");
|
|
7
|
+
const sdk_trace_base_1 = require("@opentelemetry/sdk-trace-base");
|
|
11
8
|
const resources_1 = require("@opentelemetry/resources");
|
|
12
9
|
const semantic_conventions_1 = require("@opentelemetry/semantic-conventions");
|
|
13
10
|
const exporter_otlp_grpc_1 = require("@opentelemetry/exporter-otlp-grpc");
|
|
14
11
|
const instrumentation_express_1 = require("@opentelemetry/instrumentation-express");
|
|
15
|
-
const
|
|
12
|
+
const sdk_trace_base_2 = require("@opentelemetry/sdk-trace-base");
|
|
13
|
+
const api_1 = require("@opentelemetry/api");
|
|
16
14
|
const core_1 = require("@opentelemetry/core");
|
|
17
15
|
// Instrumentations go here.
|
|
18
16
|
const instrumentation_aws_sdk_1 = require("@opentelemetry/instrumentation-aws-sdk");
|
|
@@ -30,7 +28,7 @@ const resources_2 = require("@opentelemetry/resources");
|
|
|
30
28
|
* before they're queued up to send. This enhances our samping process so
|
|
31
29
|
* that we can filter spans _after_ they've been emitted.
|
|
32
30
|
*/
|
|
33
|
-
class FilterBatchSpanProcessor extends
|
|
31
|
+
class FilterBatchSpanProcessor extends sdk_trace_base_2.BatchSpanProcessor {
|
|
34
32
|
constructor(exporter, filter) {
|
|
35
33
|
super(exporter);
|
|
36
34
|
this.filter = filter;
|
|
@@ -97,6 +95,7 @@ const instrumentations = [
|
|
|
97
95
|
instrumentations.forEach((i) => {
|
|
98
96
|
i.enable();
|
|
99
97
|
});
|
|
98
|
+
let tracerProvider;
|
|
100
99
|
/**
|
|
101
100
|
* Should be called once we've loaded our config; this will allow us to set up
|
|
102
101
|
* the correct metadata for the Honeycomb exporter. We don't actually have that
|
|
@@ -104,35 +103,45 @@ instrumentations.forEach((i) => {
|
|
|
104
103
|
*/
|
|
105
104
|
async function init(config) {
|
|
106
105
|
if (!config.openTelemetryEnabled) {
|
|
107
|
-
//
|
|
108
|
-
|
|
106
|
+
// If not enabled, do nothing. We used to disable the instrumentations, but
|
|
107
|
+
// per maintainers, that can actually be problematic. See the comments on
|
|
108
|
+
// https://github.com/open-telemetry/opentelemetry-js-contrib/issues/970
|
|
109
|
+
// The Express instrumentation also logs a benign error, which can be
|
|
110
|
+
// confusing to users. There's a fix in progress if we want to switch back
|
|
111
|
+
// to disabling instrumentations in the future:
|
|
112
|
+
// https://github.com/open-telemetry/opentelemetry-js-contrib/pull/972
|
|
109
113
|
return;
|
|
110
114
|
}
|
|
111
115
|
let exporter;
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
metadata,
|
|
128
|
-
|
|
129
|
-
|
|
116
|
+
if (typeof config.openTelemetryExporter === 'object') {
|
|
117
|
+
exporter = config.openTelemetryExporter;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
switch (config.openTelemetryExporter) {
|
|
121
|
+
case 'console': {
|
|
122
|
+
// Export spans to the console for testing purposes.
|
|
123
|
+
exporter = new sdk_node_1.tracing.ConsoleSpanExporter();
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
case 'honeycomb': {
|
|
127
|
+
// Create a Honeycomb exporter with the appropriate metadata from the
|
|
128
|
+
// config we've been provided with.
|
|
129
|
+
const metadata = new grpc_js_1.Metadata();
|
|
130
|
+
metadata.set('x-honeycomb-team', config.honeycombApiKey);
|
|
131
|
+
metadata.set('x-honeycomb-dataset', config.honeycombDataset);
|
|
132
|
+
exporter = new exporter_otlp_grpc_1.OTLPTraceExporter({
|
|
133
|
+
url: 'grpc://api.honeycomb.io:443/',
|
|
134
|
+
credentials: grpc_js_1.credentials.createSsl(),
|
|
135
|
+
metadata,
|
|
136
|
+
});
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
default:
|
|
140
|
+
throw new Error(`Unknown OpenTelemetry exporter: ${config.openTelemetryExporter}`);
|
|
130
141
|
}
|
|
131
|
-
default:
|
|
132
|
-
throw new Error(`Unknown OpenTelemetry exporter: ${config.openTelemetryExporter}`);
|
|
133
142
|
}
|
|
134
143
|
let sampler;
|
|
135
|
-
switch (config.openTelemetrySamplerType) {
|
|
144
|
+
switch (config.openTelemetrySamplerType ?? 'always-on') {
|
|
136
145
|
case 'always-on': {
|
|
137
146
|
sampler = new core_1.AlwaysOnSampler();
|
|
138
147
|
break;
|
|
@@ -150,6 +159,20 @@ async function init(config) {
|
|
|
150
159
|
default:
|
|
151
160
|
throw new Error(`Unknown OpenTelemetry sampler type: ${config.openTelemetrySamplerType}`);
|
|
152
161
|
}
|
|
162
|
+
let spanProcessor;
|
|
163
|
+
switch (config.openTelemetrySpanProcessor ?? 'batch') {
|
|
164
|
+
case 'batch': {
|
|
165
|
+
spanProcessor = new FilterBatchSpanProcessor(exporter, filter);
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
case 'simple': {
|
|
169
|
+
spanProcessor = new sdk_trace_base_1.SimpleSpanProcessor(exporter);
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
default: {
|
|
173
|
+
throw new Error(`Unknown OpenTelemetry span processor: ${config.openTelemetrySpanProcessor}`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
153
176
|
// Much of this functionality is copied from `@opentelemetry/sdk-node`, but
|
|
154
177
|
// we can't use the SDK directly because of the fact that we load our config
|
|
155
178
|
// asynchronously. We need to initialize our instrumentations first; only
|
|
@@ -161,24 +184,53 @@ async function init(config) {
|
|
|
161
184
|
if (config.serviceName) {
|
|
162
185
|
resource = resource.merge(new resources_1.Resource({ [semantic_conventions_1.SemanticResourceAttributes.SERVICE_NAME]: config.serviceName }));
|
|
163
186
|
}
|
|
164
|
-
|
|
187
|
+
tracerProvider = new sdk_trace_node_1.NodeTracerProvider({
|
|
165
188
|
sampler,
|
|
166
189
|
resource,
|
|
167
190
|
});
|
|
168
|
-
const spanProcessor = new FilterBatchSpanProcessor(exporter, filter);
|
|
169
191
|
tracerProvider.addSpanProcessor(spanProcessor);
|
|
170
192
|
tracerProvider.register();
|
|
171
193
|
instrumentations.forEach((i) => i.setTracerProvider(tracerProvider));
|
|
172
|
-
// When the process starts shutting down, terminate OTEL stuff.
|
|
173
|
-
process_1.default.on('SIGTERM', () => {
|
|
174
|
-
tracerProvider.shutdown().catch((error) => console.error('Error terminating tracing', error));
|
|
175
|
-
});
|
|
176
194
|
}
|
|
177
195
|
exports.init = init;
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
196
|
+
/**
|
|
197
|
+
* Gracefully shuts down the OpenTelemetry instrumentation. Should be called
|
|
198
|
+
* when a `SIGTERM` signal is handled.
|
|
199
|
+
*/
|
|
200
|
+
async function shutdown() {
|
|
201
|
+
if (tracerProvider) {
|
|
202
|
+
await tracerProvider.shutdown();
|
|
203
|
+
tracerProvider = null;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
exports.shutdown = shutdown;
|
|
207
|
+
async function instrumented(name, fn) {
|
|
208
|
+
return api_1.trace
|
|
209
|
+
.getTracer('default')
|
|
210
|
+
.startActiveSpan(name, async (span) => {
|
|
211
|
+
try {
|
|
212
|
+
const result = await fn(span);
|
|
213
|
+
span.setStatus({ code: api_1.SpanStatusCode.OK });
|
|
214
|
+
return result;
|
|
215
|
+
}
|
|
216
|
+
catch (e) {
|
|
217
|
+
span.setStatus({
|
|
218
|
+
code: api_1.SpanStatusCode.ERROR,
|
|
219
|
+
message: e.message,
|
|
220
|
+
});
|
|
221
|
+
span.recordException(e);
|
|
222
|
+
throw e;
|
|
223
|
+
}
|
|
224
|
+
finally {
|
|
225
|
+
span.end();
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
exports.instrumented = instrumented;
|
|
230
|
+
var api_2 = require("@opentelemetry/api");
|
|
231
|
+
Object.defineProperty(exports, "trace", { enumerable: true, get: function () { return api_2.trace; } });
|
|
232
|
+
Object.defineProperty(exports, "context", { enumerable: true, get: function () { return api_2.context; } });
|
|
233
|
+
Object.defineProperty(exports, "SpanStatusCode", { enumerable: true, get: function () { return api_2.SpanStatusCode; } });
|
|
182
234
|
var core_2 = require("@opentelemetry/core");
|
|
183
235
|
Object.defineProperty(exports, "suppressTracing", { enumerable: true, get: function () { return core_2.suppressTracing; } });
|
|
184
236
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const sdk_node_1 = require("@opentelemetry/sdk-node");
|
|
4
|
+
const chai_1 = require("chai");
|
|
5
|
+
const index_1 = require("./index");
|
|
6
|
+
const context_async_hooks_1 = require("@opentelemetry/context-async-hooks");
|
|
7
|
+
describe('instrumented', () => {
|
|
8
|
+
let contextManager;
|
|
9
|
+
let exporter = new sdk_node_1.tracing.InMemorySpanExporter();
|
|
10
|
+
before(async () => {
|
|
11
|
+
await (0, index_1.init)({
|
|
12
|
+
openTelemetryEnabled: true,
|
|
13
|
+
openTelemetryExporter: exporter,
|
|
14
|
+
openTelemetrySamplerType: 'always-on',
|
|
15
|
+
openTelemetrySpanProcessor: 'simple',
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
beforeEach(async () => {
|
|
19
|
+
contextManager = new context_async_hooks_1.AsyncHooksContextManager();
|
|
20
|
+
index_1.context.setGlobalContextManager(contextManager.enable());
|
|
21
|
+
});
|
|
22
|
+
afterEach(async () => {
|
|
23
|
+
exporter.reset();
|
|
24
|
+
index_1.context.disable();
|
|
25
|
+
});
|
|
26
|
+
it('returns the value from the function', async () => {
|
|
27
|
+
const res = await (0, index_1.instrumented)('test', () => 'foo');
|
|
28
|
+
chai_1.assert.equal(res, 'foo');
|
|
29
|
+
});
|
|
30
|
+
it('records a span on success', async () => {
|
|
31
|
+
await (0, index_1.instrumented)('test-success', () => 'foo');
|
|
32
|
+
const spans = exporter.getFinishedSpans();
|
|
33
|
+
chai_1.assert.lengthOf(spans, 1);
|
|
34
|
+
chai_1.assert.equal(spans[0].name, 'test-success');
|
|
35
|
+
chai_1.assert.equal(spans[0].status.code, index_1.SpanStatusCode.OK);
|
|
36
|
+
});
|
|
37
|
+
it('records a span on failure', async () => {
|
|
38
|
+
let maybeError = null;
|
|
39
|
+
await (0, index_1.instrumented)('test-failure', () => {
|
|
40
|
+
throw new Error('foo');
|
|
41
|
+
}).catch((err) => {
|
|
42
|
+
maybeError = err;
|
|
43
|
+
});
|
|
44
|
+
// Ensure the error was propagated back to the caller.
|
|
45
|
+
chai_1.assert.isOk(maybeError);
|
|
46
|
+
chai_1.assert.equal(maybeError.message, 'foo');
|
|
47
|
+
// Ensure the correct span was recorded.
|
|
48
|
+
const spans = exporter.getFinishedSpans();
|
|
49
|
+
chai_1.assert.lengthOf(spans, 1);
|
|
50
|
+
chai_1.assert.equal(spans[0].name, 'test-failure');
|
|
51
|
+
chai_1.assert.equal(spans[0].status.code, index_1.SpanStatusCode.ERROR);
|
|
52
|
+
chai_1.assert.equal(spans[0].status.message, 'foo');
|
|
53
|
+
chai_1.assert.equal(spans[0].events[0].name, 'exception');
|
|
54
|
+
});
|
|
55
|
+
it('sets up context correctly', async () => {
|
|
56
|
+
const tracer = index_1.trace.getTracer('default');
|
|
57
|
+
const parentSpan = tracer.startSpan('parentSpan');
|
|
58
|
+
const parentContext = index_1.trace.setSpan(index_1.context.active(), parentSpan);
|
|
59
|
+
await (0, index_1.instrumented)('test', async () => {
|
|
60
|
+
const childContext = index_1.context.active();
|
|
61
|
+
chai_1.assert.notStrictEqual(childContext, parentContext);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
//# sourceMappingURL=index.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":";;AAAA,sDAAkD;AAClD,+BAA8B;AAE9B,mCAAuF;AACvF,4EAA8E;AAE9E,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,IAAI,cAAwC,CAAC;IAC7C,IAAI,QAAQ,GAAG,IAAI,kBAAO,CAAC,oBAAoB,EAAE,CAAC;IAElD,MAAM,CAAC,KAAK,IAAI,EAAE;QAChB,MAAM,IAAA,YAAI,EAAC;YACT,oBAAoB,EAAE,IAAI;YAC1B,qBAAqB,EAAE,QAAQ;YAC/B,wBAAwB,EAAE,WAAW;YACrC,0BAA0B,EAAE,QAAQ;SACrC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,cAAc,GAAG,IAAI,8CAAwB,EAAE,CAAC;QAChD,eAAO,CAAC,uBAAuB,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,QAAQ,CAAC,KAAK,EAAE,CAAC;QACjB,eAAO,CAAC,OAAO,EAAE,CAAC;IACpB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;QACnD,MAAM,GAAG,GAAG,MAAM,IAAA,oBAAY,EAAC,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;QACpD,aAAM,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC3B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2BAA2B,EAAE,KAAK,IAAI,EAAE;QACzC,MAAM,IAAA,oBAAY,EAAC,cAAc,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;QAEhD,MAAM,KAAK,GAAG,QAAQ,CAAC,gBAAgB,EAAE,CAAC;QAC1C,aAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAC1B,aAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;QAC5C,aAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,sBAAc,CAAC,EAAE,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2BAA2B,EAAE,KAAK,IAAI,EAAE;QACzC,IAAI,UAAU,GAAG,IAAI,CAAC;QACtB,MAAM,IAAA,oBAAY,EAAC,cAAc,EAAE,GAAG,EAAE;YACtC,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;YACf,UAAU,GAAG,GAAG,CAAC;QACnB,CAAC,CAAC,CAAC;QAEH,sDAAsD;QACtD,aAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACxB,aAAM,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAExC,wCAAwC;QACxC,MAAM,KAAK,GAAG,QAAQ,CAAC,gBAAgB,EAAE,CAAC;QAC1C,aAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAC1B,aAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;QAC5C,aAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,sBAAc,CAAC,KAAK,CAAC,CAAC;QACzD,aAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAC7C,aAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2BAA2B,EAAE,KAAK,IAAI,EAAE;QACzC,MAAM,MAAM,GAAG,aAAK,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QAC1C,MAAM,UAAU,GAAG,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QAClD,MAAM,aAAa,GAAG,aAAK,CAAC,OAAO,CAAC,eAAO,CAAC,MAAM,EAAE,EAAE,UAAU,CAAC,CAAC;QAElE,MAAM,IAAA,oBAAY,EAAC,MAAM,EAAE,KAAK,IAAI,EAAE;YACpC,MAAM,YAAY,GAAG,eAAO,CAAC,MAAM,EAAE,CAAC;YACtC,aAAM,CAAC,cAAc,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;QACrD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prairielearn/opentelemetry",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "tsc",
|
|
7
|
-
"dev": "tsc --watch --preserveWatchOutput"
|
|
7
|
+
"dev": "tsc --watch --preserveWatchOutput",
|
|
8
|
+
"test": "mocha --no-config --require ts-node/register src/index.test.ts"
|
|
8
9
|
},
|
|
9
10
|
"dependencies": {
|
|
10
|
-
"@grpc/grpc-js": "^1.
|
|
11
|
-
"@opentelemetry/api": "^1.0
|
|
11
|
+
"@grpc/grpc-js": "^1.6.7",
|
|
12
|
+
"@opentelemetry/api": "^1.1.0",
|
|
12
13
|
"@opentelemetry/core": "^1.0.1",
|
|
13
14
|
"@opentelemetry/exporter-otlp-grpc": "^0.26.0",
|
|
14
15
|
"@opentelemetry/instrumentation-aws-sdk": "^0.5.0",
|
|
@@ -16,16 +17,19 @@
|
|
|
16
17
|
"@opentelemetry/instrumentation-dns": "^0.27.1",
|
|
17
18
|
"@opentelemetry/instrumentation-express": "^0.28.0",
|
|
18
19
|
"@opentelemetry/instrumentation-http": "^0.27.0",
|
|
19
|
-
"@opentelemetry/instrumentation-pg": "^0.
|
|
20
|
+
"@opentelemetry/instrumentation-pg": "^0.29.0",
|
|
20
21
|
"@opentelemetry/instrumentation-redis": "^0.28.0",
|
|
21
|
-
"@opentelemetry/resource-detector-aws": "^1.0
|
|
22
|
-
"@opentelemetry/resources": "^1.0
|
|
22
|
+
"@opentelemetry/resource-detector-aws": "^1.1.0",
|
|
23
|
+
"@opentelemetry/resources": "^1.2.0",
|
|
23
24
|
"@opentelemetry/sdk-node": "^0.27.0",
|
|
24
25
|
"@opentelemetry/sdk-trace-base": "^1.0.1",
|
|
25
|
-
"@opentelemetry/sdk-trace-node": "^1.0
|
|
26
|
-
"@opentelemetry/semantic-conventions": "^1.0
|
|
26
|
+
"@opentelemetry/sdk-trace-node": "^1.3.0",
|
|
27
|
+
"@opentelemetry/semantic-conventions": "^1.3.0"
|
|
27
28
|
},
|
|
28
29
|
"devDependencies": {
|
|
29
|
-
"
|
|
30
|
+
"@prairielearn/tsconfig": "*",
|
|
31
|
+
"mocha": "^10.0.0",
|
|
32
|
+
"ts-node": "^10.8.0",
|
|
33
|
+
"typescript": "^4.7.2"
|
|
30
34
|
}
|
|
31
35
|
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { tracing } from '@opentelemetry/sdk-node';
|
|
2
|
+
import { assert } from 'chai';
|
|
3
|
+
|
|
4
|
+
import { context, init, instrumented, shutdown, trace, SpanStatusCode } from './index';
|
|
5
|
+
import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks';
|
|
6
|
+
|
|
7
|
+
describe('instrumented', () => {
|
|
8
|
+
let contextManager: AsyncHooksContextManager;
|
|
9
|
+
let exporter = new tracing.InMemorySpanExporter();
|
|
10
|
+
|
|
11
|
+
before(async () => {
|
|
12
|
+
await init({
|
|
13
|
+
openTelemetryEnabled: true,
|
|
14
|
+
openTelemetryExporter: exporter,
|
|
15
|
+
openTelemetrySamplerType: 'always-on',
|
|
16
|
+
openTelemetrySpanProcessor: 'simple',
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
beforeEach(async () => {
|
|
21
|
+
contextManager = new AsyncHooksContextManager();
|
|
22
|
+
context.setGlobalContextManager(contextManager.enable());
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterEach(async () => {
|
|
26
|
+
exporter.reset();
|
|
27
|
+
context.disable();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('returns the value from the function', async () => {
|
|
31
|
+
const res = await instrumented('test', () => 'foo');
|
|
32
|
+
assert.equal(res, 'foo');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('records a span on success', async () => {
|
|
36
|
+
await instrumented('test-success', () => 'foo');
|
|
37
|
+
|
|
38
|
+
const spans = exporter.getFinishedSpans();
|
|
39
|
+
assert.lengthOf(spans, 1);
|
|
40
|
+
assert.equal(spans[0].name, 'test-success');
|
|
41
|
+
assert.equal(spans[0].status.code, SpanStatusCode.OK);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('records a span on failure', async () => {
|
|
45
|
+
let maybeError = null;
|
|
46
|
+
await instrumented('test-failure', () => {
|
|
47
|
+
throw new Error('foo');
|
|
48
|
+
}).catch((err) => {
|
|
49
|
+
maybeError = err;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Ensure the error was propagated back to the caller.
|
|
53
|
+
assert.isOk(maybeError);
|
|
54
|
+
assert.equal(maybeError.message, 'foo');
|
|
55
|
+
|
|
56
|
+
// Ensure the correct span was recorded.
|
|
57
|
+
const spans = exporter.getFinishedSpans();
|
|
58
|
+
assert.lengthOf(spans, 1);
|
|
59
|
+
assert.equal(spans[0].name, 'test-failure');
|
|
60
|
+
assert.equal(spans[0].status.code, SpanStatusCode.ERROR);
|
|
61
|
+
assert.equal(spans[0].status.message, 'foo');
|
|
62
|
+
assert.equal(spans[0].events[0].name, 'exception');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('sets up context correctly', async () => {
|
|
66
|
+
const tracer = trace.getTracer('default');
|
|
67
|
+
const parentSpan = tracer.startSpan('parentSpan');
|
|
68
|
+
const parentContext = trace.setSpan(context.active(), parentSpan);
|
|
69
|
+
|
|
70
|
+
await instrumented('test', async () => {
|
|
71
|
+
const childContext = context.active();
|
|
72
|
+
assert.notStrictEqual(childContext, parentContext);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
import process from 'process';
|
|
2
1
|
import { Metadata, credentials } from '@grpc/grpc-js';
|
|
3
2
|
|
|
4
3
|
import { tracing } from '@opentelemetry/sdk-node';
|
|
5
4
|
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
|
|
6
|
-
import
|
|
5
|
+
import {
|
|
6
|
+
SpanExporter,
|
|
7
|
+
ReadableSpan,
|
|
8
|
+
SpanProcessor,
|
|
9
|
+
SimpleSpanProcessor,
|
|
10
|
+
} from '@opentelemetry/sdk-trace-base';
|
|
7
11
|
import { detectResources, Resource } from '@opentelemetry/resources';
|
|
8
12
|
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
|
|
9
13
|
import { OTLPTraceExporter } from '@opentelemetry/exporter-otlp-grpc';
|
|
10
14
|
import { ExpressLayerType } from '@opentelemetry/instrumentation-express';
|
|
11
15
|
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
|
|
12
|
-
import { Sampler } from '@opentelemetry/api';
|
|
16
|
+
import { Sampler, Span, SpanStatusCode, context, trace } from '@opentelemetry/api';
|
|
13
17
|
import {
|
|
14
18
|
ParentBasedSampler,
|
|
15
19
|
TraceIdRatioBasedSampler,
|
|
@@ -30,6 +34,7 @@ import { RedisInstrumentation } from '@opentelemetry/instrumentation-redis';
|
|
|
30
34
|
// Resource detectors go here.
|
|
31
35
|
import { awsEc2Detector } from '@opentelemetry/resource-detector-aws';
|
|
32
36
|
import { processDetector, envDetector } from '@opentelemetry/resources';
|
|
37
|
+
import { createBaggage } from '@opentelemetry/api/build/src/baggage/utils';
|
|
33
38
|
|
|
34
39
|
/**
|
|
35
40
|
* Extends `BatchSpanProcessor` to give it the ability to filter out spans
|
|
@@ -111,11 +116,14 @@ instrumentations.forEach((i) => {
|
|
|
111
116
|
i.enable();
|
|
112
117
|
});
|
|
113
118
|
|
|
119
|
+
let tracerProvider: NodeTracerProvider;
|
|
120
|
+
|
|
114
121
|
export interface OpenTelemetryConfig {
|
|
115
122
|
openTelemetryEnabled: boolean;
|
|
116
|
-
openTelemetryExporter
|
|
117
|
-
openTelemetrySamplerType
|
|
123
|
+
openTelemetryExporter: 'console' | 'honeycomb' | SpanExporter;
|
|
124
|
+
openTelemetrySamplerType: 'always-on' | 'always-off' | 'trace-id-ratio';
|
|
118
125
|
openTelemetrySampleRate?: number;
|
|
126
|
+
openTelemetrySpanProcessor?: 'batch' | 'simple';
|
|
119
127
|
honeycombApiKey?: string;
|
|
120
128
|
honeycombDataset?: string;
|
|
121
129
|
serviceName?: string;
|
|
@@ -128,39 +136,48 @@ export interface OpenTelemetryConfig {
|
|
|
128
136
|
*/
|
|
129
137
|
export async function init(config: OpenTelemetryConfig) {
|
|
130
138
|
if (!config.openTelemetryEnabled) {
|
|
131
|
-
//
|
|
132
|
-
|
|
139
|
+
// If not enabled, do nothing. We used to disable the instrumentations, but
|
|
140
|
+
// per maintainers, that can actually be problematic. See the comments on
|
|
141
|
+
// https://github.com/open-telemetry/opentelemetry-js-contrib/issues/970
|
|
142
|
+
// The Express instrumentation also logs a benign error, which can be
|
|
143
|
+
// confusing to users. There's a fix in progress if we want to switch back
|
|
144
|
+
// to disabling instrumentations in the future:
|
|
145
|
+
// https://github.com/open-telemetry/opentelemetry-js-contrib/pull/972
|
|
133
146
|
return;
|
|
134
147
|
}
|
|
135
148
|
|
|
136
149
|
let exporter: SpanExporter;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
150
|
+
if (typeof config.openTelemetryExporter === 'object') {
|
|
151
|
+
exporter = config.openTelemetryExporter;
|
|
152
|
+
} else {
|
|
153
|
+
switch (config.openTelemetryExporter) {
|
|
154
|
+
case 'console': {
|
|
155
|
+
// Export spans to the console for testing purposes.
|
|
156
|
+
exporter = new tracing.ConsoleSpanExporter();
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
case 'honeycomb': {
|
|
160
|
+
// Create a Honeycomb exporter with the appropriate metadata from the
|
|
161
|
+
// config we've been provided with.
|
|
162
|
+
const metadata = new Metadata();
|
|
163
|
+
|
|
164
|
+
metadata.set('x-honeycomb-team', config.honeycombApiKey);
|
|
165
|
+
metadata.set('x-honeycomb-dataset', config.honeycombDataset);
|
|
166
|
+
|
|
167
|
+
exporter = new OTLPTraceExporter({
|
|
168
|
+
url: 'grpc://api.honeycomb.io:443/',
|
|
169
|
+
credentials: credentials.createSsl(),
|
|
170
|
+
metadata,
|
|
171
|
+
});
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
default:
|
|
175
|
+
throw new Error(`Unknown OpenTelemetry exporter: ${config.openTelemetryExporter}`);
|
|
157
176
|
}
|
|
158
|
-
default:
|
|
159
|
-
throw new Error(`Unknown OpenTelemetry exporter: ${config.openTelemetryExporter}`);
|
|
160
177
|
}
|
|
161
178
|
|
|
162
179
|
let sampler: Sampler;
|
|
163
|
-
switch (config.openTelemetrySamplerType) {
|
|
180
|
+
switch (config.openTelemetrySamplerType ?? 'always-on') {
|
|
164
181
|
case 'always-on': {
|
|
165
182
|
sampler = new AlwaysOnSampler();
|
|
166
183
|
break;
|
|
@@ -179,6 +196,21 @@ export async function init(config: OpenTelemetryConfig) {
|
|
|
179
196
|
throw new Error(`Unknown OpenTelemetry sampler type: ${config.openTelemetrySamplerType}`);
|
|
180
197
|
}
|
|
181
198
|
|
|
199
|
+
let spanProcessor: SpanProcessor;
|
|
200
|
+
switch (config.openTelemetrySpanProcessor ?? 'batch') {
|
|
201
|
+
case 'batch': {
|
|
202
|
+
spanProcessor = new FilterBatchSpanProcessor(exporter, filter);
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
case 'simple': {
|
|
206
|
+
spanProcessor = new SimpleSpanProcessor(exporter);
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
default: {
|
|
210
|
+
throw new Error(`Unknown OpenTelemetry span processor: ${config.openTelemetrySpanProcessor}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
182
214
|
// Much of this functionality is copied from `@opentelemetry/sdk-node`, but
|
|
183
215
|
// we can't use the SDK directly because of the fact that we load our config
|
|
184
216
|
// asynchronously. We need to initialize our instrumentations first; only
|
|
@@ -195,20 +227,49 @@ export async function init(config: OpenTelemetryConfig) {
|
|
|
195
227
|
);
|
|
196
228
|
}
|
|
197
229
|
|
|
198
|
-
|
|
230
|
+
tracerProvider = new NodeTracerProvider({
|
|
199
231
|
sampler,
|
|
200
232
|
resource,
|
|
201
233
|
});
|
|
202
|
-
const spanProcessor = new FilterBatchSpanProcessor(exporter, filter);
|
|
203
234
|
tracerProvider.addSpanProcessor(spanProcessor);
|
|
204
235
|
tracerProvider.register();
|
|
205
236
|
|
|
206
237
|
instrumentations.forEach((i) => i.setTracerProvider(tracerProvider));
|
|
238
|
+
}
|
|
207
239
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
240
|
+
/**
|
|
241
|
+
* Gracefully shuts down the OpenTelemetry instrumentation. Should be called
|
|
242
|
+
* when a `SIGTERM` signal is handled.
|
|
243
|
+
*/
|
|
244
|
+
export async function shutdown(): Promise<void> {
|
|
245
|
+
if (tracerProvider) {
|
|
246
|
+
await tracerProvider.shutdown();
|
|
247
|
+
tracerProvider = null;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export async function instrumented<T>(
|
|
252
|
+
name: string,
|
|
253
|
+
fn: (span: Span) => Promise<T> | T
|
|
254
|
+
): Promise<T> {
|
|
255
|
+
return trace
|
|
256
|
+
.getTracer('default')
|
|
257
|
+
.startActiveSpan<(span: Span) => Promise<T>>(name, async (span) => {
|
|
258
|
+
try {
|
|
259
|
+
const result = await fn(span);
|
|
260
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
261
|
+
return result;
|
|
262
|
+
} catch (e) {
|
|
263
|
+
span.setStatus({
|
|
264
|
+
code: SpanStatusCode.ERROR,
|
|
265
|
+
message: e.message,
|
|
266
|
+
});
|
|
267
|
+
span.recordException(e);
|
|
268
|
+
throw e;
|
|
269
|
+
} finally {
|
|
270
|
+
span.end();
|
|
271
|
+
}
|
|
272
|
+
});
|
|
212
273
|
}
|
|
213
274
|
|
|
214
275
|
export { trace, context, SpanStatusCode } from '@opentelemetry/api';
|
package/tsconfig.json
CHANGED
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
+
"extends": "@prairielearn/tsconfig",
|
|
2
3
|
"compilerOptions": {
|
|
3
|
-
"allowJs": true,
|
|
4
|
-
"declaration": true,
|
|
5
|
-
"esModuleInterop": true,
|
|
6
4
|
"outDir": "./dist",
|
|
7
|
-
"rootDir": "src
|
|
8
|
-
"sourceMap": true,
|
|
9
|
-
// This package will only be used server-side on Node 14+, so target the
|
|
10
|
-
// newest version of the ES spec that Node 14 supports.
|
|
11
|
-
"target": "ES2020",
|
|
12
|
-
// However, we don't yet make extensive use of ES Modules, so specifically
|
|
13
|
-
// compile `import`/`export` down to CommonJS.
|
|
14
|
-
"module": "CommonJS",
|
|
5
|
+
"rootDir": "./src",
|
|
15
6
|
}
|
|
16
7
|
}
|