feat(metapackage): copy official metapackage

last commit in official: 0e96b2acdb
This commit is contained in:
TomyJan 2024-08-07 17:22:39 +08:00
parent e05fa1cf0f
commit 094e4d5813
11 changed files with 798 additions and 2 deletions

7
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
"cSpell.words": [
"Metapackage",
"Tachy",
"tachybase"
]
}

View File

@ -1,3 +1,3 @@
# opentelemetry-instrumentation-tachybase
# opentelemetry-auto-instrumentations
TachyBase 专属的 OpenTelemetry 插桩库
TachyBase 专属的 OpenTelemetry 自动插桩库

105
package.json Normal file
View File

@ -0,0 +1,105 @@
{
"name": "@tachybase/opentelemetry-auto-instrumentations",
"version": "0.0.1",
"description": "Metapackage of telemetry for TachyBase which bundles OpenTelemetry node core and instrumentations",
"author": "TachyBase Team",
"homepage": "https://git.daoyoucloud.com/TomyJan/opentelemetry-auto-instrumentations",
"license": "Apache-2.0",
"engines": {
"node": ">=14"
},
"publishConfig": {
"access": "public"
},
"main": "build/src/index.js",
"types": "build/src/index.d.ts",
"exports": {
".": "./build/src/index.js",
"./register": "./build/src/register.js"
},
"repository": "https://git.daoyoucloud.com/TomyJan/opentelemetry-auto-instrumentations",
"scripts": {
"clean": "rimraf build/*",
"compile": "tsc -p .",
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --ext .ts --fix",
"precompile": "tsc --version && lerna run version:update --scope @opentelemetry/auto-instrumentations-node --include-dependencies",
"prewatch": "npm run precompile",
"prepublishOnly": "npm run compile",
"tdd": "yarn test -- --watch-extensions ts --watch",
"test": "nyc ts-mocha -p tsconfig.json 'test/**/*.ts'",
"watch": "tsc -w"
},
"bugs": {
"url": "https://git.daoyoucloud.com/TomyJan/opentelemetry-auto-instrumentations/issues"
},
"peerDependencies": {
"@opentelemetry/api": "^1.4.1"
},
"devDependencies": {
"@opentelemetry/api": "^1.4.1",
"@types/mocha": "7.0.2",
"@types/node": "18.6.5",
"@types/sinon": "10.0.18",
"mocha": "7.2.0",
"nyc": "15.1.0",
"rimraf": "5.0.5",
"sinon": "15.2.0",
"ts-mocha": "10.0.0",
"typescript": "4.4.4"
},
"dependencies": {
"@opentelemetry/instrumentation": "^0.52.0",
"@opentelemetry/instrumentation-amqplib": "^0.40.0",
"@opentelemetry/instrumentation-aws-lambda": "^0.43.0",
"@opentelemetry/instrumentation-aws-sdk": "^0.43.1",
"@opentelemetry/instrumentation-bunyan": "^0.40.0",
"@opentelemetry/instrumentation-cassandra-driver": "^0.40.0",
"@opentelemetry/instrumentation-connect": "^0.38.0",
"@opentelemetry/instrumentation-cucumber": "^0.8.0",
"@opentelemetry/instrumentation-dataloader": "^0.11.0",
"@opentelemetry/instrumentation-dns": "^0.38.0",
"@opentelemetry/instrumentation-express": "^0.41.1",
"@opentelemetry/instrumentation-fastify": "^0.38.0",
"@opentelemetry/instrumentation-fs": "^0.14.0",
"@opentelemetry/instrumentation-generic-pool": "^0.38.0",
"@opentelemetry/instrumentation-graphql": "^0.42.0",
"@opentelemetry/instrumentation-grpc": "^0.52.0",
"@opentelemetry/instrumentation-hapi": "^0.40.0",
"@opentelemetry/instrumentation-http": "^0.52.0",
"@opentelemetry/instrumentation-ioredis": "^0.42.0",
"@opentelemetry/instrumentation-kafkajs": "^0.2.0",
"@opentelemetry/instrumentation-knex": "^0.39.0",
"@opentelemetry/instrumentation-koa": "^0.42.0",
"@opentelemetry/instrumentation-lru-memoizer": "^0.39.0",
"@opentelemetry/instrumentation-memcached": "^0.38.0",
"@opentelemetry/instrumentation-mongodb": "^0.46.0",
"@opentelemetry/instrumentation-mongoose": "^0.40.0",
"@opentelemetry/instrumentation-mysql": "^0.40.0",
"@opentelemetry/instrumentation-mysql2": "^0.40.0",
"@opentelemetry/instrumentation-nestjs-core": "^0.39.0",
"@opentelemetry/instrumentation-net": "^0.38.0",
"@opentelemetry/instrumentation-pg": "^0.43.0",
"@opentelemetry/instrumentation-pino": "^0.41.0",
"@opentelemetry/instrumentation-redis": "^0.41.0",
"@opentelemetry/instrumentation-redis-4": "^0.41.0",
"@opentelemetry/instrumentation-restify": "^0.40.0",
"@opentelemetry/instrumentation-router": "^0.39.0",
"@opentelemetry/instrumentation-socket.io": "^0.41.0",
"@opentelemetry/instrumentation-tedious": "^0.12.0",
"@opentelemetry/instrumentation-undici": "^0.4.0",
"@opentelemetry/instrumentation-winston": "^0.39.0",
"@opentelemetry/resource-detector-alibaba-cloud": "^0.28.10",
"@opentelemetry/resource-detector-aws": "^1.5.2",
"@opentelemetry/resource-detector-azure": "^0.2.9",
"@opentelemetry/resource-detector-container": "^0.3.11",
"@opentelemetry/resource-detector-gcp": "^0.29.10",
"@opentelemetry/resources": "^1.24.0",
"@opentelemetry/sdk-node": "^0.52.0"
},
"files": [
"build/src/**/*.js",
"build/src/**/*.js.map",
"build/src/**/*.d.ts"
]
}

21
src/index.ts Normal file
View File

@ -0,0 +1,21 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export {
getNodeAutoInstrumentations,
getResourceDetectorsFromEnv as getResourceDetectors,
InstrumentationConfigMap,
} from './utils';

48
src/register.ts Normal file
View File

@ -0,0 +1,48 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as opentelemetry from '@opentelemetry/sdk-node';
import { diag, DiagConsoleLogger } from '@opentelemetry/api';
import {
getNodeAutoInstrumentations,
getResourceDetectorsFromEnv,
} from './utils';
diag.setLogger(
new DiagConsoleLogger(),
opentelemetry.core.getEnv().OTEL_LOG_LEVEL
);
const sdk = new opentelemetry.NodeSDK({
instrumentations: getNodeAutoInstrumentations(),
resourceDetectors: getResourceDetectorsFromEnv(),
});
try {
sdk.start();
diag.info('OpenTelemetry automatic instrumentation started successfully');
} catch (error) {
diag.error(
'Error initializing OpenTelemetry SDK. Your application is not instrumented and will not produce telemetry',
error
);
}
process.on('SIGTERM', () => {
sdk
.shutdown()
.then(() => diag.debug('OpenTelemetry SDK terminated'))
.catch(error => diag.error('Error terminating OpenTelemetry SDK', error));
});

286
src/utils.ts Normal file
View File

@ -0,0 +1,286 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { diag } from '@opentelemetry/api';
import { Instrumentation } from '@opentelemetry/instrumentation';
import { AmqplibInstrumentation } from '@opentelemetry/instrumentation-amqplib';
import { AwsLambdaInstrumentation } from '@opentelemetry/instrumentation-aws-lambda';
import { AwsInstrumentation } from '@opentelemetry/instrumentation-aws-sdk';
import { BunyanInstrumentation } from '@opentelemetry/instrumentation-bunyan';
import { CassandraDriverInstrumentation } from '@opentelemetry/instrumentation-cassandra-driver';
import { ConnectInstrumentation } from '@opentelemetry/instrumentation-connect';
import { CucumberInstrumentation } from '@opentelemetry/instrumentation-cucumber';
import { DataloaderInstrumentation } from '@opentelemetry/instrumentation-dataloader';
import { DnsInstrumentation } from '@opentelemetry/instrumentation-dns';
import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express';
import { FastifyInstrumentation } from '@opentelemetry/instrumentation-fastify';
import { FsInstrumentation } from '@opentelemetry/instrumentation-fs';
import { GenericPoolInstrumentation } from '@opentelemetry/instrumentation-generic-pool';
import { GraphQLInstrumentation } from '@opentelemetry/instrumentation-graphql';
import { GrpcInstrumentation } from '@opentelemetry/instrumentation-grpc';
import { HapiInstrumentation } from '@opentelemetry/instrumentation-hapi';
import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
import { IORedisInstrumentation } from '@opentelemetry/instrumentation-ioredis';
import { KafkaJsInstrumentation } from '@opentelemetry/instrumentation-kafkajs';
import { KnexInstrumentation } from '@opentelemetry/instrumentation-knex';
import { KoaInstrumentation } from '@opentelemetry/instrumentation-koa';
import { LruMemoizerInstrumentation } from '@opentelemetry/instrumentation-lru-memoizer';
import { MemcachedInstrumentation } from '@opentelemetry/instrumentation-memcached';
import { MongoDBInstrumentation } from '@opentelemetry/instrumentation-mongodb';
import { MongooseInstrumentation } from '@opentelemetry/instrumentation-mongoose';
import { MySQL2Instrumentation } from '@opentelemetry/instrumentation-mysql2';
import { MySQLInstrumentation } from '@opentelemetry/instrumentation-mysql';
import { NestInstrumentation } from '@opentelemetry/instrumentation-nestjs-core';
import { NetInstrumentation } from '@opentelemetry/instrumentation-net';
import { PgInstrumentation } from '@opentelemetry/instrumentation-pg';
import { PinoInstrumentation } from '@opentelemetry/instrumentation-pino';
import { RedisInstrumentation as RedisInstrumentationV2 } from '@opentelemetry/instrumentation-redis';
import { RedisInstrumentation as RedisInstrumentationV4 } from '@opentelemetry/instrumentation-redis-4';
import { RestifyInstrumentation } from '@opentelemetry/instrumentation-restify';
import { RouterInstrumentation } from '@opentelemetry/instrumentation-router';
import { SocketIoInstrumentation } from '@opentelemetry/instrumentation-socket.io';
import { TediousInstrumentation } from '@opentelemetry/instrumentation-tedious';
import { UndiciInstrumentation } from '@opentelemetry/instrumentation-undici';
import { WinstonInstrumentation } from '@opentelemetry/instrumentation-winston';
import { alibabaCloudEcsDetector } from '@opentelemetry/resource-detector-alibaba-cloud';
import {
awsBeanstalkDetector,
awsEc2Detector,
awsEcsDetector,
awsEksDetector,
awsLambdaDetector,
} from '@opentelemetry/resource-detector-aws';
import { containerDetector } from '@opentelemetry/resource-detector-container';
import { gcpDetector } from '@opentelemetry/resource-detector-gcp';
import {
Detector,
DetectorSync,
envDetectorSync,
hostDetectorSync,
osDetectorSync,
processDetectorSync,
serviceInstanceIdDetectorSync,
} from '@opentelemetry/resources';
import {
azureAppServiceDetector,
azureFunctionsDetector,
azureVmDetector,
} from '@opentelemetry/resource-detector-azure';
const RESOURCE_DETECTOR_CONTAINER = 'container';
const RESOURCE_DETECTOR_ENVIRONMENT = 'env';
const RESOURCE_DETECTOR_HOST = 'host';
const RESOURCE_DETECTOR_OS = 'os';
const RESOURCE_DETECTOR_SERVICE_INSTANCE_ID = 'serviceinstance';
const RESOURCE_DETECTOR_PROCESS = 'process';
const RESOURCE_DETECTOR_ALIBABA = 'alibaba';
const RESOURCE_DETECTOR_AWS = 'aws';
const RESOURCE_DETECTOR_AZURE = 'azure';
const RESOURCE_DETECTOR_GCP = 'gcp';
const InstrumentationMap = {
'@opentelemetry/instrumentation-amqplib': AmqplibInstrumentation,
'@opentelemetry/instrumentation-aws-lambda': AwsLambdaInstrumentation,
'@opentelemetry/instrumentation-aws-sdk': AwsInstrumentation,
'@opentelemetry/instrumentation-bunyan': BunyanInstrumentation,
'@opentelemetry/instrumentation-cassandra-driver':
CassandraDriverInstrumentation,
'@opentelemetry/instrumentation-connect': ConnectInstrumentation,
'@opentelemetry/instrumentation-cucumber': CucumberInstrumentation,
'@opentelemetry/instrumentation-dataloader': DataloaderInstrumentation,
'@opentelemetry/instrumentation-dns': DnsInstrumentation,
'@opentelemetry/instrumentation-express': ExpressInstrumentation,
'@opentelemetry/instrumentation-fastify': FastifyInstrumentation,
'@opentelemetry/instrumentation-fs': FsInstrumentation,
'@opentelemetry/instrumentation-generic-pool': GenericPoolInstrumentation,
'@opentelemetry/instrumentation-graphql': GraphQLInstrumentation,
'@opentelemetry/instrumentation-grpc': GrpcInstrumentation,
'@opentelemetry/instrumentation-hapi': HapiInstrumentation,
'@opentelemetry/instrumentation-http': HttpInstrumentation,
'@opentelemetry/instrumentation-ioredis': IORedisInstrumentation,
'@opentelemetry/instrumentation-kafkajs': KafkaJsInstrumentation,
'@opentelemetry/instrumentation-knex': KnexInstrumentation,
'@opentelemetry/instrumentation-koa': KoaInstrumentation,
'@opentelemetry/instrumentation-lru-memoizer': LruMemoizerInstrumentation,
'@opentelemetry/instrumentation-memcached': MemcachedInstrumentation,
'@opentelemetry/instrumentation-mongodb': MongoDBInstrumentation,
'@opentelemetry/instrumentation-mongoose': MongooseInstrumentation,
'@opentelemetry/instrumentation-mysql2': MySQL2Instrumentation,
'@opentelemetry/instrumentation-mysql': MySQLInstrumentation,
'@opentelemetry/instrumentation-nestjs-core': NestInstrumentation,
'@opentelemetry/instrumentation-net': NetInstrumentation,
'@opentelemetry/instrumentation-pg': PgInstrumentation,
'@opentelemetry/instrumentation-pino': PinoInstrumentation,
'@opentelemetry/instrumentation-redis': RedisInstrumentationV2,
'@opentelemetry/instrumentation-redis-4': RedisInstrumentationV4,
'@opentelemetry/instrumentation-restify': RestifyInstrumentation,
'@opentelemetry/instrumentation-router': RouterInstrumentation,
'@opentelemetry/instrumentation-socket.io': SocketIoInstrumentation,
'@opentelemetry/instrumentation-tedious': TediousInstrumentation,
'@opentelemetry/instrumentation-undici': UndiciInstrumentation,
'@opentelemetry/instrumentation-winston': WinstonInstrumentation,
};
// Config types inferred automatically from the first argument of the constructor
type ConfigArg<T> = T extends new (...args: infer U) => unknown ? U[0] : never;
export type InstrumentationConfigMap = {
[Name in keyof typeof InstrumentationMap]?: ConfigArg<
(typeof InstrumentationMap)[Name]
>;
};
export function getNodeAutoInstrumentations(
inputConfigs: InstrumentationConfigMap = {}
): Instrumentation[] {
checkManuallyProvidedInstrumentationNames(Object.keys(inputConfigs));
const enabledInstrumentationsFromEnv = getEnabledInstrumentationsFromEnv();
const disabledInstrumentationsFromEnv = getDisabledInstrumentationsFromEnv();
const instrumentations: Instrumentation[] = [];
for (const name of Object.keys(InstrumentationMap) as Array<
keyof typeof InstrumentationMap
>) {
const Instance = InstrumentationMap[name];
// Defaults are defined by the instrumentation itself
const userConfig: any = inputConfigs[name] ?? {};
if (
userConfig.enabled === false ||
!enabledInstrumentationsFromEnv.includes(name) ||
disabledInstrumentationsFromEnv.includes(name)
) {
diag.debug(`Disabling instrumentation for ${name}`);
continue;
}
try {
diag.debug(`Loading instrumentation for ${name}`);
instrumentations.push(new Instance(userConfig));
} catch (e: any) {
diag.error(e);
}
}
return instrumentations;
}
function checkManuallyProvidedInstrumentationNames(
manuallyProvidedInstrumentationNames: string[]
) {
for (const name of manuallyProvidedInstrumentationNames) {
if (!Object.prototype.hasOwnProperty.call(InstrumentationMap, name)) {
diag.error(`Provided instrumentation name "${name}" not found`);
}
}
}
function getInstrumentationsFromEnv(envVar: string): string[] {
const envVarValue = process.env[envVar];
if (envVarValue == null) {
return [];
}
const instrumentationsFromEnv = envVarValue
?.split(',')
.map(
instrumentationPkgSuffix =>
`@opentelemetry/instrumentation-${instrumentationPkgSuffix.trim()}`
);
checkManuallyProvidedInstrumentationNames(instrumentationsFromEnv);
return instrumentationsFromEnv;
}
/**
* Returns the list of instrumentations that are enabled based on the environment variable.
*/
function getEnabledInstrumentationsFromEnv() {
if (!process.env.OTEL_NODE_ENABLED_INSTRUMENTATIONS) {
return Object.keys(InstrumentationMap);
}
const instrumentationsFromEnv = getInstrumentationsFromEnv(
'OTEL_NODE_ENABLED_INSTRUMENTATIONS'
);
return instrumentationsFromEnv;
}
/**
* Returns the list of instrumentations that are disabled based on the environment variable.
*/
function getDisabledInstrumentationsFromEnv() {
if (!process.env.OTEL_NODE_DISABLED_INSTRUMENTATIONS) {
return [];
}
const instrumentationsFromEnv = getInstrumentationsFromEnv(
'OTEL_NODE_DISABLED_INSTRUMENTATIONS'
);
return instrumentationsFromEnv;
}
export function getResourceDetectorsFromEnv(): Array<Detector | DetectorSync> {
const resourceDetectors = new Map<
string,
Detector | DetectorSync | Detector[] | DetectorSync[]
>([
[RESOURCE_DETECTOR_CONTAINER, containerDetector],
[RESOURCE_DETECTOR_ENVIRONMENT, envDetectorSync],
[RESOURCE_DETECTOR_HOST, hostDetectorSync],
[RESOURCE_DETECTOR_OS, osDetectorSync],
[RESOURCE_DETECTOR_SERVICE_INSTANCE_ID, serviceInstanceIdDetectorSync],
[RESOURCE_DETECTOR_PROCESS, processDetectorSync],
[RESOURCE_DETECTOR_ALIBABA, alibabaCloudEcsDetector],
[RESOURCE_DETECTOR_GCP, gcpDetector],
[
RESOURCE_DETECTOR_AWS,
[
awsEc2Detector,
awsEcsDetector,
awsEksDetector,
awsBeanstalkDetector,
awsLambdaDetector,
],
],
[
RESOURCE_DETECTOR_AZURE,
[azureAppServiceDetector, azureFunctionsDetector, azureVmDetector],
],
]);
const resourceDetectorsFromEnv =
process.env.OTEL_NODE_RESOURCE_DETECTORS?.split(',') ?? ['all'];
if (resourceDetectorsFromEnv.includes('all')) {
return [...resourceDetectors.values()].flat();
}
if (resourceDetectorsFromEnv.includes('none')) {
return [];
}
return resourceDetectorsFromEnv.flatMap(detector => {
const resourceDetector = resourceDetectors.get(detector);
if (!resourceDetector) {
diag.error(
`Invalid resource detector "${detector}" specified in the environment variable OTEL_NODE_RESOURCE_DETECTORS`
);
}
return resourceDetector || [];
});
}

55
test/register.test.ts Normal file
View File

@ -0,0 +1,55 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { spawnSync } from 'child_process';
import * as assert from 'assert';
describe('Register', function () {
it('can load auto instrumentation from command line', () => {
const proc = spawnSync(
process.execPath,
['--require', '../build/src/register.js', './test-app/app.js'],
{
cwd: __dirname,
timeout: 5000,
killSignal: 'SIGKILL', // SIGTERM is not sufficient to terminate some hangs
env: Object.assign({}, process.env, {
OTEL_NODE_RESOURCE_DETECTORS: 'none',
OTEL_TRACES_EXPORTER: 'console',
// nx (used by lerna run) defaults `FORCE_COLOR=true`, which in
// node v18.17.0, v20.3.0 and later results in ANSI color escapes
// in the ConsoleSpanExporter output that is checked below.
FORCE_COLOR: '0',
}),
}
);
assert.ifError(proc.error);
assert.equal(proc.status, 0, `proc.status (${proc.status})`);
assert.equal(proc.signal, null, `proc.signal (${proc.signal})`);
assert.ok(
proc.stdout.includes(
'OpenTelemetry automatic instrumentation started successfully'
)
);
// Check a span has been generated for the GET request done in app.js
assert.ok(
proc.stdout.includes("name: 'GET'"),
'console span output in stdout'
);
});
});

29
test/test-app/app.js Normal file
View File

@ -0,0 +1,29 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//Used in register.test.ts to mimic a JS app.
const http = require('http');
const options = {
hostname: 'example.com',
port: 80,
path: '/',
method: 'GET'
};
const req = http.request(options);
req.end();

208
test/utils.test.ts Normal file
View File

@ -0,0 +1,208 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { diag } from '@opentelemetry/api';
import { HttpInstrumentationConfig } from '@opentelemetry/instrumentation-http';
import * as assert from 'assert';
import * as sinon from 'sinon';
import { getNodeAutoInstrumentations } from '../src';
import { getResourceDetectorsFromEnv } from '../src/utils';
describe('utils', () => {
describe('getNodeAutoInstrumentations', () => {
it('should include all installed instrumentations', () => {
const instrumentations = getNodeAutoInstrumentations();
const installedInstrumentations = Object.keys(
require('../package.json').dependencies
).filter(depName => {
return depName.startsWith('@opentelemetry/instrumentation-');
});
assert.deepStrictEqual(
new Set(instrumentations.map(i => i.instrumentationName)),
new Set(installedInstrumentations)
);
});
it('should use user config', () => {
function applyCustomAttributesOnSpan() {}
const instrumentations = getNodeAutoInstrumentations({
'@opentelemetry/instrumentation-http': {
applyCustomAttributesOnSpan,
},
});
const instrumentation = instrumentations.find(
instr =>
instr.instrumentationName === '@opentelemetry/instrumentation-http'
) as any;
const configHttp = instrumentation._config as HttpInstrumentationConfig;
assert.strictEqual(
configHttp.applyCustomAttributesOnSpan,
applyCustomAttributesOnSpan
);
});
it('should not return disabled instrumentation', () => {
const instrumentations = getNodeAutoInstrumentations({
'@opentelemetry/instrumentation-grpc': {
enabled: false,
},
});
const instrumentation = instrumentations.find(
instr =>
instr.instrumentationName === '@opentelemetry/instrumentation-grpc'
);
assert.strictEqual(instrumentation, undefined);
});
it('should return only instrumentations enabled via OTEL_NODE_ENABLED_INSTRUMENTATIONS environment variable', () => {
process.env.OTEL_NODE_ENABLED_INSTRUMENTATIONS =
'http,aws-sdk, nestjs-core'; // separator with and without whitespaces should be allowed
try {
const instrumentations = getNodeAutoInstrumentations();
assert.deepStrictEqual(
new Set(instrumentations.map(i => i.instrumentationName)),
new Set([
'@opentelemetry/instrumentation-http',
'@opentelemetry/instrumentation-aws-sdk',
'@opentelemetry/instrumentation-nestjs-core',
])
);
} finally {
delete process.env.OTEL_NODE_ENABLED_INSTRUMENTATIONS;
}
});
it('should include all instrumentations except those disabled via OTEL_NODE_DISABLED_INSTRUMENTATIONS environment variable', () => {
process.env.OTEL_NODE_DISABLED_INSTRUMENTATIONS =
'fs,aws-sdk, aws-lambda'; // separator with and without whitespaces should be allowed
try {
const instrumentations = getNodeAutoInstrumentations();
const disabledInstrumentations = new Set([
'@opentelemetry/instrumentation-fs',
'@opentelemetry/instrumentation-aws-sdk',
'@opentelemetry/instrumentation-aws-lambda',
]);
const enabledInstrumentationNames = new Set(
instrumentations.map(i => i.instrumentationName)
);
for (const disabledInstrumentation of disabledInstrumentations) {
assert.strictEqual(
enabledInstrumentationNames.has(disabledInstrumentation),
false
);
}
} finally {
delete process.env.OTEL_NODE_DISABLED_INSTRUMENTATIONS;
}
});
it('should disable any instrumentations from OTEL_NODE_ENABLED_INSTRUMENTATIONS if set in OTEL_NODE_DISABLED_INSTRUMENTATIONS', () => {
process.env.OTEL_NODE_ENABLED_INSTRUMENTATIONS = 'http,express,net';
process.env.OTEL_NODE_DISABLED_INSTRUMENTATIONS = 'fs,net'; // fs is no-op here, already disabled
try {
const instrumentations = getNodeAutoInstrumentations();
assert.deepStrictEqual(
new Set(instrumentations.map(i => i.instrumentationName)),
new Set([
'@opentelemetry/instrumentation-http',
'@opentelemetry/instrumentation-express',
])
);
} finally {
delete process.env.OTEL_NODE_DISABLED_INSTRUMENTATIONS;
delete process.env.OTEL_NODE_ENABLED_INSTRUMENTATIONS;
}
});
it('should show error for none existing instrumentation', () => {
const spy = sinon.stub(diag, 'error');
const name = '@opentelemetry/instrumentation-http2';
const instrumentations = getNodeAutoInstrumentations({
// @ts-expect-error verify that wrong name works
[name]: {
enabled: false,
},
});
const instrumentation = instrumentations.find(
instr => instr.instrumentationName === name
);
assert.strictEqual(instrumentation, undefined);
assert.strictEqual(
spy.args[0][0],
`Provided instrumentation name "${name}" not found`
);
spy.restore();
});
});
describe('getResourceDetectorsFromEnv', () => {
it('should return all resource detectors by default', () => {
assert.equal(getResourceDetectorsFromEnv().length, 16);
});
it('should return all resource detectors when OTEL_NODE_RESOURCE_DETECTORS contains "all"', () => {
process.env.OTEL_NODE_RESOURCE_DETECTORS = 'all';
assert.equal(getResourceDetectorsFromEnv().length, 16);
delete process.env.OTEL_NODE_RESOURCE_DETECTORS;
});
it('should return specific resource detectors depending on OTEL_NODE_RESOURCE_DETECTORS', () => {
process.env.OTEL_NODE_RESOURCE_DETECTORS = 'env,host,serviceinstance';
const resourceDetectors = getResourceDetectorsFromEnv();
assert.equal(resourceDetectors.length, 3);
assert.equal(resourceDetectors[0].constructor.name, 'EnvDetectorSync');
assert.equal(resourceDetectors[1].constructor.name, 'HostDetectorSync');
assert.equal(
resourceDetectors[2].constructor.name,
'ServiceInstanceIdDetectorSync'
);
delete process.env.OTEL_NODE_RESOURCE_DETECTORS;
});
it('should return no resource detectors when OTEL_NODE_RESOURCE_DETECTORS contains "none" or a typo', () => {
const spy = sinon.stub(diag, 'error');
process.env.OTEL_NODE_RESOURCE_DETECTORS = 'none';
assert.equal(getResourceDetectorsFromEnv().length, 0);
assert.strictEqual(spy.callCount, 0);
process.env.OTEL_NODE_RESOURCE_DETECTORS = 'test';
assert.equal(getResourceDetectorsFromEnv().length, 0);
assert.strictEqual(
spy.args[0][0],
'Invalid resource detector "test" specified in the environment variable OTEL_NODE_RESOURCE_DETECTORS'
);
spy.restore();
delete process.env.OTEL_NODE_RESOURCE_DETECTORS;
});
});
});

26
tsconfig.base.json Normal file
View File

@ -0,0 +1,26 @@
{
"compilerOptions": {
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"declaration": true,
"declarationMap": true,
"forceConsistentCasingInFileNames": true,
"inlineSources": true,
"module": "commonjs",
"noEmitOnError": true,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"pretty": true,
"sourceMap": true,
"strict": true,
"strictNullChecks": true,
"target": "es2017",
"incremental": true,
"newLine": "LF"
},
"exclude": [
"node_modules"
]
}

11
tsconfig.json Normal file
View File

@ -0,0 +1,11 @@
{
"extends": "tsconfig.base",
"compilerOptions": {
"rootDir": ".",
"outDir": "build"
},
"include": [
"src/**/*.ts",
"test/**/*.ts"
]
}