@prairielearn/opentelemetry 1.1.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.
@@ -1,3 +1,3 @@
1
- @prairielearn/opentelemetry:build: cache hit, replaying output c232eed9a12c6aed
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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # @prairielearn/opentelemetry
2
2
 
3
+ ## 1.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 2ef7a73e8: Add `instrumented()` helper to instrument function calls
8
+
3
9
  ## 1.1.0
4
10
 
5
11
  ### Minor Changes
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 manually instrument code, you can use the `trace` export:
26
+ To easily instrument individual pieces of functionality, you can use the `instrumented()` helper function:
27
27
 
28
28
  ```ts
29
- import { trace } from '@prairielearn/opentelemetry';
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('lib-name');
32
- await tracer.startActiveSpan('span-name', async (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?: 'console' | 'honeycomb';
4
- openTelemetrySamplerType?: 'always-on' | 'always-off' | 'trace-id-ratio';
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;
@@ -18,5 +21,6 @@ export declare function init(config: OpenTelemetryConfig): Promise<void>;
18
21
  * when a `SIGTERM` signal is handled.
19
22
  */
20
23
  export declare function shutdown(): Promise<void>;
24
+ export declare function instrumented<T>(name: string, fn: (span: Span) => Promise<T> | T): Promise<T>;
21
25
  export { trace, context, SpanStatusCode } from '@opentelemetry/api';
22
26
  export { suppressTracing } from '@opentelemetry/core';
package/dist/index.js CHANGED
@@ -1,14 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.suppressTracing = exports.SpanStatusCode = exports.context = exports.trace = exports.shutdown = exports.init = void 0;
3
+ exports.suppressTracing = exports.SpanStatusCode = exports.context = exports.trace = exports.instrumented = exports.shutdown = exports.init = void 0;
4
4
  const grpc_js_1 = require("@grpc/grpc-js");
5
5
  const sdk_node_1 = require("@opentelemetry/sdk-node");
6
6
  const sdk_trace_node_1 = require("@opentelemetry/sdk-trace-node");
7
+ const sdk_trace_base_1 = require("@opentelemetry/sdk-trace-base");
7
8
  const resources_1 = require("@opentelemetry/resources");
8
9
  const semantic_conventions_1 = require("@opentelemetry/semantic-conventions");
9
10
  const exporter_otlp_grpc_1 = require("@opentelemetry/exporter-otlp-grpc");
10
11
  const instrumentation_express_1 = require("@opentelemetry/instrumentation-express");
11
- const sdk_trace_base_1 = require("@opentelemetry/sdk-trace-base");
12
+ const sdk_trace_base_2 = require("@opentelemetry/sdk-trace-base");
13
+ const api_1 = require("@opentelemetry/api");
12
14
  const core_1 = require("@opentelemetry/core");
13
15
  // Instrumentations go here.
14
16
  const instrumentation_aws_sdk_1 = require("@opentelemetry/instrumentation-aws-sdk");
@@ -26,7 +28,7 @@ const resources_2 = require("@opentelemetry/resources");
26
28
  * before they're queued up to send. This enhances our samping process so
27
29
  * that we can filter spans _after_ they've been emitted.
28
30
  */
29
- class FilterBatchSpanProcessor extends sdk_trace_base_1.BatchSpanProcessor {
31
+ class FilterBatchSpanProcessor extends sdk_trace_base_2.BatchSpanProcessor {
30
32
  constructor(exporter, filter) {
31
33
  super(exporter);
32
34
  this.filter = filter;
@@ -111,30 +113,35 @@ async function init(config) {
111
113
  return;
112
114
  }
113
115
  let exporter;
114
- switch (config.openTelemetryExporter) {
115
- case 'console': {
116
- // Export spans to the console for testing purposes.
117
- exporter = new sdk_node_1.tracing.ConsoleSpanExporter();
118
- break;
119
- }
120
- case 'honeycomb': {
121
- // Create a Honeycomb exporter with the appropriate metadata from the
122
- // config we've been provided with.
123
- const metadata = new grpc_js_1.Metadata();
124
- metadata.set('x-honeycomb-team', config.honeycombApiKey);
125
- metadata.set('x-honeycomb-dataset', config.honeycombDataset);
126
- exporter = new exporter_otlp_grpc_1.OTLPTraceExporter({
127
- url: 'grpc://api.honeycomb.io:443/',
128
- credentials: grpc_js_1.credentials.createSsl(),
129
- metadata,
130
- });
131
- break;
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}`);
132
141
  }
133
- default:
134
- throw new Error(`Unknown OpenTelemetry exporter: ${config.openTelemetryExporter}`);
135
142
  }
136
143
  let sampler;
137
- switch (config.openTelemetrySamplerType) {
144
+ switch (config.openTelemetrySamplerType ?? 'always-on') {
138
145
  case 'always-on': {
139
146
  sampler = new core_1.AlwaysOnSampler();
140
147
  break;
@@ -152,6 +159,20 @@ async function init(config) {
152
159
  default:
153
160
  throw new Error(`Unknown OpenTelemetry sampler type: ${config.openTelemetrySamplerType}`);
154
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
+ }
155
176
  // Much of this functionality is copied from `@opentelemetry/sdk-node`, but
156
177
  // we can't use the SDK directly because of the fact that we load our config
157
178
  // asynchronously. We need to initialize our instrumentations first; only
@@ -163,11 +184,10 @@ async function init(config) {
163
184
  if (config.serviceName) {
164
185
  resource = resource.merge(new resources_1.Resource({ [semantic_conventions_1.SemanticResourceAttributes.SERVICE_NAME]: config.serviceName }));
165
186
  }
166
- const tracerProvider = new sdk_trace_node_1.NodeTracerProvider({
187
+ tracerProvider = new sdk_trace_node_1.NodeTracerProvider({
167
188
  sampler,
168
189
  resource,
169
190
  });
170
- const spanProcessor = new FilterBatchSpanProcessor(exporter, filter);
171
191
  tracerProvider.addSpanProcessor(spanProcessor);
172
192
  tracerProvider.register();
173
193
  instrumentations.forEach((i) => i.setTracerProvider(tracerProvider));
@@ -180,13 +200,37 @@ exports.init = init;
180
200
  async function shutdown() {
181
201
  if (tracerProvider) {
182
202
  await tracerProvider.shutdown();
203
+ tracerProvider = null;
183
204
  }
184
205
  }
185
206
  exports.shutdown = shutdown;
186
- var api_1 = require("@opentelemetry/api");
187
- Object.defineProperty(exports, "trace", { enumerable: true, get: function () { return api_1.trace; } });
188
- Object.defineProperty(exports, "context", { enumerable: true, get: function () { return api_1.context; } });
189
- Object.defineProperty(exports, "SpanStatusCode", { enumerable: true, get: function () { return api_1.SpanStatusCode; } });
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; } });
190
234
  var core_2 = require("@opentelemetry/core");
191
235
  Object.defineProperty(exports, "suppressTracing", { enumerable: true, get: function () { return core_2.suppressTracing; } });
192
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":"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"}
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":"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"}
package/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "@prairielearn/opentelemetry",
3
- "version": "1.1.0",
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.5.5",
11
- "@opentelemetry/api": "^1.0.4",
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.28.0",
20
+ "@opentelemetry/instrumentation-pg": "^0.29.0",
20
21
  "@opentelemetry/instrumentation-redis": "^0.28.0",
21
- "@opentelemetry/resource-detector-aws": "^1.0.3",
22
- "@opentelemetry/resources": "^1.0.1",
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.1",
26
- "@opentelemetry/semantic-conventions": "^1.0.1"
26
+ "@opentelemetry/sdk-trace-node": "^1.3.0",
27
+ "@opentelemetry/semantic-conventions": "^1.3.0"
27
28
  },
28
29
  "devDependencies": {
29
- "typescript": "^4.5.5"
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 type { SpanExporter, ReadableSpan } from '@opentelemetry/sdk-trace-base';
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
@@ -115,9 +120,10 @@ let tracerProvider: NodeTracerProvider;
115
120
 
116
121
  export interface OpenTelemetryConfig {
117
122
  openTelemetryEnabled: boolean;
118
- openTelemetryExporter?: 'console' | 'honeycomb';
119
- openTelemetrySamplerType?: 'always-on' | 'always-off' | 'trace-id-ratio';
123
+ openTelemetryExporter: 'console' | 'honeycomb' | SpanExporter;
124
+ openTelemetrySamplerType: 'always-on' | 'always-off' | 'trace-id-ratio';
120
125
  openTelemetrySampleRate?: number;
126
+ openTelemetrySpanProcessor?: 'batch' | 'simple';
121
127
  honeycombApiKey?: string;
122
128
  honeycombDataset?: string;
123
129
  serviceName?: string;
@@ -141,33 +147,37 @@ export async function init(config: OpenTelemetryConfig) {
141
147
  }
142
148
 
143
149
  let exporter: SpanExporter;
144
- switch (config.openTelemetryExporter) {
145
- case 'console': {
146
- // Export spans to the console for testing purposes.
147
- exporter = new tracing.ConsoleSpanExporter();
148
- break;
149
- }
150
- case 'honeycomb': {
151
- // Create a Honeycomb exporter with the appropriate metadata from the
152
- // config we've been provided with.
153
- const metadata = new Metadata();
154
-
155
- metadata.set('x-honeycomb-team', config.honeycombApiKey);
156
- metadata.set('x-honeycomb-dataset', config.honeycombDataset);
157
-
158
- exporter = new OTLPTraceExporter({
159
- url: 'grpc://api.honeycomb.io:443/',
160
- credentials: credentials.createSsl(),
161
- metadata,
162
- });
163
- break;
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}`);
164
176
  }
165
- default:
166
- throw new Error(`Unknown OpenTelemetry exporter: ${config.openTelemetryExporter}`);
167
177
  }
168
178
 
169
179
  let sampler: Sampler;
170
- switch (config.openTelemetrySamplerType) {
180
+ switch (config.openTelemetrySamplerType ?? 'always-on') {
171
181
  case 'always-on': {
172
182
  sampler = new AlwaysOnSampler();
173
183
  break;
@@ -186,6 +196,21 @@ export async function init(config: OpenTelemetryConfig) {
186
196
  throw new Error(`Unknown OpenTelemetry sampler type: ${config.openTelemetrySamplerType}`);
187
197
  }
188
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
+
189
214
  // Much of this functionality is copied from `@opentelemetry/sdk-node`, but
190
215
  // we can't use the SDK directly because of the fact that we load our config
191
216
  // asynchronously. We need to initialize our instrumentations first; only
@@ -202,11 +227,10 @@ export async function init(config: OpenTelemetryConfig) {
202
227
  );
203
228
  }
204
229
 
205
- const tracerProvider = new NodeTracerProvider({
230
+ tracerProvider = new NodeTracerProvider({
206
231
  sampler,
207
232
  resource,
208
233
  });
209
- const spanProcessor = new FilterBatchSpanProcessor(exporter, filter);
210
234
  tracerProvider.addSpanProcessor(spanProcessor);
211
235
  tracerProvider.register();
212
236
 
@@ -220,8 +244,33 @@ export async function init(config: OpenTelemetryConfig) {
220
244
  export async function shutdown(): Promise<void> {
221
245
  if (tracerProvider) {
222
246
  await tracerProvider.shutdown();
247
+ tracerProvider = null;
223
248
  }
224
249
  }
225
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
+ });
273
+ }
274
+
226
275
  export { trace, context, SpanStatusCode } from '@opentelemetry/api';
227
276
  export { suppressTracing } from '@opentelemetry/core';
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
  }