@saidsef/tracing-node 6.0.0 → 6.1.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/README.md +8 -1
- package/libs/esm-hook.mjs +42 -0
- package/libs/index.mjs +9 -0
- package/libs/index.test.mjs +34 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -29,12 +29,19 @@ npm install @saidsef/tracing-node --save
|
|
|
29
29
|
## Usage
|
|
30
30
|
|
|
31
31
|
```javascript
|
|
32
|
+
// instrument.mjs
|
|
32
33
|
import { setupTracing } from '@saidsef/tracing-node';
|
|
33
34
|
|
|
34
35
|
setupTracing({hostname: 'hostname', serviceName: 'service_name', url: 'endpoint'});
|
|
35
36
|
```
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
```shell
|
|
39
|
+
node --import ./instrument.mjs ./app.mjs
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`serviceName` and `url` are required, and both fall back to the `SERVICE_NAME` and `ENDPOINT` environment variables.
|
|
43
|
+
|
|
44
|
+
`setupTracing` has to run before the application imports the libraries being traced, which is what the `--import` preload guarantees. The library registers the `import-in-the-middle` loader hook on import, so ES modules and CommonJS modules are both patched. Importing an instrumented package statically in the same file as the library loads it too early to be patched, so the preload is the form to reach for. [Initialisation order](https://tracing-node.readthedocs.io/en/latest/usage/#initialisation-order) covers the alternatives.
|
|
38
45
|
|
|
39
46
|
## Collector and backend
|
|
40
47
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright Said Sef
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* https://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import {register} from 'node:module';
|
|
18
|
+
|
|
19
|
+
// A loader hook only reaches modules imported after it registers, so this lives
|
|
20
|
+
// in its own module and is the first import of index.mjs, ahead of every
|
|
21
|
+
// instrumentation. import.meta.url resolves the hook against this package:
|
|
22
|
+
// import-in-the-middle sits in the library's own tree, not the consumer's.
|
|
23
|
+
|
|
24
|
+
const FALSEY = ['false', '0'];
|
|
25
|
+
|
|
26
|
+
const enabled = !FALSEY.includes((process.env.TRACING_NODE_ESM_HOOK ?? '').toLowerCase());
|
|
27
|
+
|
|
28
|
+
let failure = null;
|
|
29
|
+
|
|
30
|
+
if (enabled) {
|
|
31
|
+
try {
|
|
32
|
+
register('import-in-the-middle/hook.mjs', import.meta.url);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
failure = error;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Whether the ESM loader hook is in place, so ES module imports get patched. */
|
|
39
|
+
export const esmHookRegistered = enabled && failure === null;
|
|
40
|
+
|
|
41
|
+
/** The error that stopped registration, reported by index.mjs once diag has a logger. */
|
|
42
|
+
export const esmHookFailure = failure;
|
package/libs/index.mjs
CHANGED
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
// First, and deliberately: registering the ESM loader hook has to happen before
|
|
18
|
+
// any instrumented module is imported.
|
|
19
|
+
import {esmHookFailure} from './esm-hook.mjs';
|
|
17
20
|
import {AwsInstrumentation} from '@opentelemetry/instrumentation-aws-sdk';
|
|
18
21
|
import {BatchSpanProcessor} from '@opentelemetry/sdk-trace-base';
|
|
19
22
|
import {ConnectInstrumentation} from '@opentelemetry/instrumentation-connect';
|
|
@@ -41,6 +44,12 @@ import {ATTR_CONTAINER_NAME} from '@opentelemetry/semantic-conventions/incubatin
|
|
|
41
44
|
|
|
42
45
|
diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.INFO);
|
|
43
46
|
|
|
47
|
+
// Reported here rather than where it happens, because the hook registers before
|
|
48
|
+
// this logger exists and the warning would go nowhere.
|
|
49
|
+
if (esmHookFailure) {
|
|
50
|
+
diag.warn(`ESM loader hook not registered, so ES module imports are not instrumented: ${esmHookFailure.message}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
44
53
|
// Set a non-negative integer span attribute from a header value; ignore invalid input.
|
|
45
54
|
const setIntAttribute = (span, name, value) => {
|
|
46
55
|
if (!value) return;
|
package/libs/index.test.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// index.test.mjs
|
|
2
2
|
import { describe, it, beforeEach, afterEach } from 'node:test';
|
|
3
3
|
import assert from 'node:assert';
|
|
4
|
+
import { execFileSync } from 'node:child_process';
|
|
4
5
|
import { metrics } from '@opentelemetry/api';
|
|
5
6
|
import { logs } from '@opentelemetry/api-logs';
|
|
6
7
|
import { MeterProvider } from '@opentelemetry/sdk-metrics';
|
|
@@ -262,3 +263,36 @@ describe('express request hook', () => {
|
|
|
262
263
|
assert.deepStrictEqual(span.attributes, {});
|
|
263
264
|
});
|
|
264
265
|
});
|
|
266
|
+
|
|
267
|
+
// A loader hook registers once per process, so the opt-out cannot be exercised
|
|
268
|
+
// in this one. Each case reads the module in a child process instead.
|
|
269
|
+
describe('ESM loader hook', () => {
|
|
270
|
+
const hookModule = new URL('./esm-hook.mjs', import.meta.url).href;
|
|
271
|
+
|
|
272
|
+
const registeredWith = (value) => {
|
|
273
|
+
const env = {...process.env};
|
|
274
|
+
delete env.TRACING_NODE_ESM_HOOK;
|
|
275
|
+
if (value !== undefined) {
|
|
276
|
+
env.TRACING_NODE_ESM_HOOK = value;
|
|
277
|
+
}
|
|
278
|
+
return execFileSync(process.execPath, [
|
|
279
|
+
'--input-type=module',
|
|
280
|
+
'--eval',
|
|
281
|
+
`import {esmHookRegistered} from ${JSON.stringify(hookModule)}; console.log(esmHookRegistered);`,
|
|
282
|
+
], {env, encoding: 'utf8'}).trim();
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
it('should register by default', () => {
|
|
286
|
+
assert.strictEqual(registeredWith(undefined), 'true');
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('should skip registration when opted out', () => {
|
|
290
|
+
assert.strictEqual(registeredWith('false'), 'false');
|
|
291
|
+
assert.strictEqual(registeredWith('0'), 'false');
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it('should register for any other value', () => {
|
|
295
|
+
assert.strictEqual(registeredWith('true'), 'true');
|
|
296
|
+
assert.strictEqual(registeredWith(''), 'true');
|
|
297
|
+
});
|
|
298
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saidsef/tracing-node",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.1.0",
|
|
4
4
|
"description": "tracing NodeJS - Wrapper for OpenTelemetry instrumentation packages",
|
|
5
5
|
"main": "libs/index.mjs",
|
|
6
6
|
"scripts": {
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"@opentelemetry/sdk-trace-base": "^2.11.0",
|
|
55
55
|
"@opentelemetry/sdk-trace-node": "^2.11.0",
|
|
56
56
|
"@opentelemetry/semantic-conventions": "^1.43.0",
|
|
57
|
+
"import-in-the-middle": "^3.0.0",
|
|
57
58
|
"opentelemetry-instrumentation-elasticsearch": "^0.41.0"
|
|
58
59
|
},
|
|
59
60
|
"devDependencies": {
|