Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat(sdk-node): add serviceInstanceIdDetector to NodeSDK #4626

Merged
merged 6 commits into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ For experimental package changes, see the [experimental CHANGELOG](experimental/

* feat(sdk-trace-base): log resource attributes in ConsoleSpanExporter [#4605](https://github.com/open-telemetry/opentelemetry-js/pull/4605) @pichlermarc
* feat(propagator-aws-xray): moved AWS Xray propagator from contrib [4603](https://github.com/open-telemetry/opentelemetry-js/pull/4603) @martinkuba
* feat(resources): new experimental detector ServiceInstanceIdDetectorSync that sets the value for `service.instance.id` as random UUID.
* feat(resources): new experimental detector ServiceInstanceIdDetectorSync that sets the value for `service.instance.id` as random UUID. [#4608](https://github.com/open-telemetry/opentelemetry-js/pull/4608) @maryliag

### :bug: (Bug Fix)

Expand Down
5 changes: 5 additions & 0 deletions experimental/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ All notable changes to experimental packages in this project will be documented
* refactor(instrumentation-grpc): move to use SEMATTRS [#4633](https://github.com/open-telemetry/opentelemetry-js/pull/4633)
* feat(otlp-transformer): consolidate scope/resource creation in transformer [#4600](https://github.com/open-telemetry/opentelemetry-js/pull/4600)
* feat(sdk-logs): print message when attributes are dropped due to attribute count limit [#4614](https://github.com/open-telemetry/opentelemetry-js/pull/4614) @HyunnoH
* feat(sdk-node): add usage for the detector ServiceInstanceIdDetectorSync. [#4626](https://github.com/open-telemetry/opentelemetry-js/pull/4626) @maryliag
* The resource detector can be added to default resource detector list by adding the value `serviceinstance` to the list of resource detectors on the environment variable `OTEL_NODE_RESOURCE_DETECTORS`, e.g `OTEL_NODE_RESOURCE_DETECTORS=env,host,os,serviceinstance`
* The value can be overwritten by
* merging a resource containing the `service.instance.id` attribute
* using another resource detector which writes `service.instance.id`

### :bug: (Bug Fix)

Expand Down
19 changes: 18 additions & 1 deletion experimental/packages/opentelemetry-sdk-node/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,26 @@ Configure a resource. Resources may also be detected by using the `autoDetectRes

### resourceDetectors

Configure resource detectors. By default, the resource detectors are [envDetector, processDetector].
Configure resource detectors. By default, the resource detectors are [envDetector, processDetector, hostDetector].
NOTE: In order to enable the detection, the parameter `autoDetectResources` has to be `true`.

If `resourceDetectors` was not set, you can also use the environment variable `OTEL_NODE_RESOURCE_DETECTORS` to enable only certain detectors, or completely disable them:

- `env`
- `host`
- `os`
- `process`
- `serviceinstance` (experimental)
- `all` - enable all resource detectors above
maryliag marked this conversation as resolved.
Show resolved Hide resolved
- **NOTE:** future versions of `@opentelemetry/sdk-node` may include additional detectors that will be covered by this scope.
- `none` - disable resource detection

For example, to enable only the `env`, `host` detectors:

```shell
export OTEL_NODE_RESOURCE_DETECTORS="env,host"
```

### sampler

Configure a custom sampler. By default, all traces will be sampled.
Expand Down
20 changes: 14 additions & 6 deletions experimental/packages/opentelemetry-sdk-node/src/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ import { SEMRESATTRS_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
import { NodeSDKConfiguration } from './types';
import { TracerProviderWithEnvExporters } from './TracerProviderWithEnvExporter';
import { getEnv, getEnvWithoutDefaults } from '@opentelemetry/core';
import { parseInstrumentationOptions } from './utils';
import {
getResourceDetectorsFromEnv,
parseInstrumentationOptions,
} from './utils';

/** This class represents everything needed to register a fully configured OpenTelemetry Node.js SDK */

Expand Down Expand Up @@ -121,11 +124,15 @@ export class NodeSDK {
this._configuration = configuration;

this._resource = configuration.resource ?? new Resource({});
this._resourceDetectors = configuration.resourceDetectors ?? [
envDetector,
processDetector,
hostDetector,
];
let defaultDetectors: (Detector | DetectorSync)[] = [];
if (process.env.OTEL_NODE_RESOURCE_DETECTORS != null) {
defaultDetectors = getResourceDetectorsFromEnv();
} else {
defaultDetectors = [envDetector, processDetector, hostDetector];
}

this._resourceDetectors =
configuration.resourceDetectors ?? defaultDetectors;

this._serviceName = configuration.serviceName;

Expand Down Expand Up @@ -157,6 +164,7 @@ export class NodeSDK {

const spanProcessor =
configuration.spanProcessor ??
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
new BatchSpanProcessor(configuration.traceExporter!);

const spanProcessors = configuration.spanProcessors ?? [spanProcessor];
Expand Down
47 changes: 47 additions & 0 deletions experimental/packages/opentelemetry-sdk-node/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,19 @@
* limitations under the License.
*/

import { diag } from '@opentelemetry/api';
import {
Instrumentation,
InstrumentationOption,
} from '@opentelemetry/instrumentation';
import {
DetectorSync,
envDetectorSync,
hostDetectorSync,
osDetectorSync,
processDetectorSync,
serviceInstanceIdDetectorSync,
} from '@opentelemetry/resources';

// TODO: This part of a workaround to fix https://github.com/open-telemetry/opentelemetry-js/issues/3609
// If the MeterProvider is not yet registered when instrumentations are registered, all metrics are dropped.
Expand All @@ -41,3 +50,41 @@ export function parseInstrumentationOptions(

return instrumentations;
}

const RESOURCE_DETECTOR_ENVIRONMENT = 'env';
const RESOURCE_DETECTOR_HOST = 'host';
const RESOURCE_DETECTOR_OS = 'os';
const RESOURCE_DETECTOR_PROCESS = 'process';
const RESOURCE_DETECTOR_SERVICE_INSTANCE_ID = 'serviceinstance';

export function getResourceDetectorsFromEnv(): Array<DetectorSync> {
// When updating this list, make sure to also update the section `resourceDetectors` on README.
const resourceDetectors = new Map<string, DetectorSync>([
[RESOURCE_DETECTOR_ENVIRONMENT, envDetectorSync],
[RESOURCE_DETECTOR_HOST, hostDetectorSync],
[RESOURCE_DETECTOR_OS, osDetectorSync],
[RESOURCE_DETECTOR_SERVICE_INSTANCE_ID, serviceInstanceIdDetectorSync],
[RESOURCE_DETECTOR_PROCESS, processDetectorSync],
]);

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 || [];
});
}
32 changes: 20 additions & 12 deletions experimental/packages/opentelemetry-sdk-node/test/sdk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ import {
View,
} from '@opentelemetry/sdk-metrics';
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
import { assertServiceResource } from './util/resource-assertions';
import {
assertServiceInstanceIdIsUUID,
assertServiceResource,
} from './util/resource-assertions';
import {
ConsoleSpanExporter,
SimpleSpanProcessor,
Expand Down Expand Up @@ -71,7 +74,6 @@ import {
import {
SEMRESATTRS_HOST_NAME,
SEMRESATTRS_PROCESS_PID,
SEMRESATTRS_SERVICE_INSTANCE_ID,
} from '@opentelemetry/semantic-conventions';

const DefaultContextManager = semver.gte(process.version, '14.8.0')
Expand Down Expand Up @@ -682,7 +684,7 @@ describe('Node SDK', () => {
describe('configureServiceInstanceId', async () => {
it('should configure service instance id via OTEL_RESOURCE_ATTRIBUTES env var', async () => {
process.env.OTEL_RESOURCE_ATTRIBUTES =
'service.instance.id=627cc493,service.name=my-service';
'service.instance.id=627cc493,service.name=my-service,service.namespace';
const sdk = new NodeSDK();

sdk.start();
Expand All @@ -694,7 +696,20 @@ describe('Node SDK', () => {
instanceId: '627cc493',
});
delete process.env.OTEL_RESOURCE_ATTRIBUTES;
sdk.shutdown();
await sdk.shutdown();
});

it('should configure service instance id via OTEL_NODE_RESOURCE_DETECTORS env var', async () => {
process.env.OTEL_NODE_RESOURCE_DETECTORS = 'env,host,os,serviceinstance';
const sdk = new NodeSDK();

sdk.start();
const resource = sdk['_resource'];
await resource.waitForAsyncAttributes?.();

assertServiceInstanceIdIsUUID(resource);
delete process.env.OTEL_NODE_RESOURCE_DETECTORS;
await sdk.shutdown();
});

it('should configure service instance id with random UUID', async () => {
Expand All @@ -712,14 +727,7 @@ describe('Node SDK', () => {
const resource = sdk['_resource'];
await resource.waitForAsyncAttributes?.();

const UUID_REGEX =
/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
assert.equal(
UUID_REGEX.test(
resource.attributes[SEMRESATTRS_SERVICE_INSTANCE_ID]?.toString() || ''
),
true
);
assertServiceInstanceIdIsUUID(resource);
await sdk.shutdown();
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { SDK_INFO } from '@opentelemetry/core';
import * as assert from 'assert';
import { IResource, Resource } from '@opentelemetry/resources';
import {
SEMRESATTRS_SERVICE_INSTANCE_ID,
SEMRESATTRS_TELEMETRY_SDK_LANGUAGE,
SEMRESATTRS_TELEMETRY_SDK_NAME,
SEMRESATTRS_TELEMETRY_SDK_VERSION,
Expand Down Expand Up @@ -336,3 +337,14 @@ const assertHasOneLabel = (prefix: string, resource: Resource): void => {
JSON.stringify(Object.keys(SemanticResourceAttributes))
);
};

export const assertServiceInstanceIdIsUUID = (resource: Resource): void => {
const UUID_REGEX =
/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
assert.equal(
UUID_REGEX.test(
resource.attributes[SEMRESATTRS_SERVICE_INSTANCE_ID]?.toString() || ''
),
true
);
};
4 changes: 2 additions & 2 deletions packages/opentelemetry-resources/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ npm install --save @opentelemetry/resources
## Usage

```typescript
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
import { SEMRESATTRS_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
import { Resource } from '@opentelemetry/resources';

const resource = new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: 'api-service',
[SEMRESATTRS_SERVICE_NAME]: 'api-service',
});

const anotherResource = new Resource({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@

export * from './default-service-name';
export * from './HostDetector';
export * from './OSDetector';
export * from './HostDetectorSync';
export * from './OSDetector';
export * from './OSDetectorSync';
export * from './ProcessDetector';
export * from './ProcessDetectorSync';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@

export * from './default-service-name';
export * from './HostDetector';
export * from './OSDetector';
export * from './HostDetectorSync';
export * from './OSDetector';
export * from './OSDetectorSync';
export * from './ProcessDetector';
export * from './ProcessDetectorSync';
Expand Down