Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ For semantic convention package changes, see the [semconv CHANGELOG](packages/se

### :rocket: (Enhancement)

* feat(sdk-metrics, sdk-trace): add `mergeResourceWithDefaults` flag, which allows opting-out of resources getting merged with the default resource [#4617](https://github.com/open-telemetry/opentelemetry-js/pull/4617)
* default: `true` (no change in behavior)
* note: `false` will become the default behavior in the next major version in order to comply with [specification requirements](https://github.com/open-telemetry/opentelemetry-specification/blob/f3511a5ccda376dfd1de76dfa086fc9b35b54757/specification/resource/sdk.md?plain=1#L31-L36)

### :bug: (Bug Fix)

### :books: (Refine Doc)
Expand Down
4 changes: 4 additions & 0 deletions experimental/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ All notable changes to experimental packages in this project will be documented

### :rocket: (Enhancement)

* feat(sdk-node, sdk-logs): add `mergeResourceWithDefaults` flag, which allows opting-out of resources getting merged with the default resource [#4617](https://github.com/open-telemetry/opentelemetry-js/pull/4617)
* default: `true`
* note: `false` will become the default behavior in a future iteration in order to comply with [specification requirements](https://github.com/open-telemetry/opentelemetry-specification/blob/f3511a5ccda376dfd1de76dfa086fc9b35b54757/specification/resource/sdk.md?plain=1#L31-L36)

### :bug: (Bug Fix)

### :books: (Refine Doc)
Expand Down
4 changes: 4 additions & 0 deletions experimental/packages/opentelemetry-sdk-node/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ Deprecated, please use [logRecordProcessors](#logrecordprocessors) instead.

An array of log record processors to register to the logger provider.

### mergeResourceWithDefaults

Merge user-provided resources with the default resource. Default `true`.

### metricReader

Add a [MetricReader](../opentelemetry-sdk-metrics/src/export/MetricReader.ts)
Expand Down
6 changes: 6 additions & 0 deletions experimental/packages/opentelemetry-sdk-node/src/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export class NodeSDK {

private _resource: IResource;
private _resourceDetectors: Array<Detector | DetectorSync>;
private _mergeResourceWithDefaults: boolean;

private _autoDetectResources: boolean;

Expand Down Expand Up @@ -131,6 +132,8 @@ export class NodeSDK {
this._configuration = configuration;

this._resource = configuration.resource ?? new Resource({});
this._mergeResourceWithDefaults =
configuration.mergeResourceWithDefaults ?? true;
this._autoDetectResources = configuration.autoDetectResources ?? true;
if (!this._autoDetectResources) {
this._resourceDetectors = [];
Expand Down Expand Up @@ -254,6 +257,7 @@ export class NodeSDK {
const tracerProvider = new Provider({
...this._configuration,
resource: this._resource,
mergeResourceWithDefaults: this._mergeResourceWithDefaults,
});

this._tracerProvider = tracerProvider;
Expand All @@ -275,6 +279,7 @@ export class NodeSDK {
if (this._loggerProviderConfig) {
const loggerProvider = new LoggerProvider({
resource: this._resource,
mergeResourceWithDefaults: this._mergeResourceWithDefaults,
});

for (const logRecordProcessor of this._loggerProviderConfig
Expand All @@ -296,6 +301,7 @@ export class NodeSDK {
resource: this._resource,
views: this._meterProviderConfig?.views ?? [],
readers: readers,
mergeResourceWithDefaults: this._mergeResourceWithDefaults,
});

this._meterProvider = meterProvider;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export interface NodeSDKConfiguration {
instrumentations: (Instrumentation | Instrumentation[])[];
resource: IResource;
resourceDetectors: Array<Detector | DetectorSync>;
mergeResourceWithDefaults: boolean;
sampler: Sampler;
serviceName?: string;
/** @deprecated use spanProcessors instead*/
Expand Down
17 changes: 15 additions & 2 deletions experimental/packages/sdk-logs/src/LoggerProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,27 @@ import { LoggerProviderSharedState } from './internal/LoggerProviderSharedState'

export const DEFAULT_LOGGER_NAME = 'unknown';

function prepareResource(
mergeWithDefaults: boolean,
providedResource: Resource | undefined
) {
const resource = providedResource ?? Resource.empty();

if (mergeWithDefaults) {
return Resource.default().merge(resource);
}
return resource;
}

export class LoggerProvider implements logsAPI.LoggerProvider {
private _shutdownOnce: BindOnceFuture<void>;
private readonly _sharedState: LoggerProviderSharedState;

constructor(config: LoggerProviderConfig = {}) {
const mergedConfig = merge({}, loadDefaultConfig(), config);
const resource = Resource.default().merge(
mergedConfig.resource ?? Resource.empty()
const resource = prepareResource(
mergedConfig.mergeResourceWithDefaults,
config.resource
);
this._sharedState = new LoggerProviderSharedState(
resource,
Expand Down
1 change: 1 addition & 0 deletions experimental/packages/sdk-logs/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export function loadDefaultConfig() {
attributeCountLimit: getEnv().OTEL_LOGRECORD_ATTRIBUTE_COUNT_LIMIT,
},
includeTraceContext: true,
mergeResourceWithDefaults: true,
};
}

Expand Down
6 changes: 6 additions & 0 deletions experimental/packages/sdk-logs/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ export interface LoggerProviderConfig {

/** Log Record Limits*/
logRecordLimits?: LogRecordLimits;

/**
* Merge resource with {@link Resource.default()}?
* Default: {@code true}
**/
mergeResourceWithDefaults?: boolean;
}

export interface LogRecordLimits {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,29 @@ describe('LoggerProvider', () => {
);
});

it('should not merge with default resource attrs when flag is set to false', function () {
const passedInResource = new Resource({ foo: 'bar' });
const provider = new LoggerProvider({
resource: passedInResource,
mergeResourceWithDefaults: false,
});
const { resource } = provider['_sharedState'];
assert.deepStrictEqual(resource, passedInResource);
});

it('should merge with default resource attrs when flag is set to true', function () {
const passedInResource = new Resource({ foo: 'bar' });
const provider = new LoggerProvider({
resource: passedInResource,
mergeResourceWithDefaults: true,
});
const { resource } = provider['_sharedState'];
assert.deepStrictEqual(
resource,
Resource.default().merge(passedInResource)
);
});

it('should have default forceFlushTimeoutMillis if not pass', () => {
const provider = new LoggerProvider();
const sharedState = provider['_sharedState'];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,11 @@ export class BasicTracerProvider implements TracerProvider {
reconfigureLimits(config)
);
this.resource = mergedConfig.resource ?? Resource.empty();
this.resource = Resource.default().merge(this.resource);

if (mergedConfig.mergeResourceWithDefaults) {
this.resource = Resource.default().merge(this.resource);
}

this._config = Object.assign({}, mergedConfig, {
resource: this.resource,
});
Expand Down
1 change: 1 addition & 0 deletions packages/opentelemetry-sdk-trace-base/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export function loadDefaultConfig() {
_env.OTEL_SPAN_ATTRIBUTE_PER_EVENT_COUNT_LIMIT,
attributePerLinkCountLimit: _env.OTEL_SPAN_ATTRIBUTE_PER_LINK_COUNT_LIMIT,
},
mergeResourceWithDefaults: true,
};
}

Expand Down
6 changes: 6 additions & 0 deletions packages/opentelemetry-sdk-trace-base/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ export interface TracerConfig {
/** Span Limits */
spanLimits?: SpanLimits;

/**
* Merge resource with {@link Resource.default()}?
* Default: {@code true}
**/
mergeResourceWithDefaults?: boolean;

/** Resource associated with trace telemetry */
resource?: IResource;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -829,9 +829,30 @@ describe('BasicTracerProvider', () => {
});

describe('.resource', () => {
it('should return a Resource', () => {
it('should use the default resource when no resource is provided', function () {
const tracerProvider = new BasicTracerProvider();
assert.ok(tracerProvider.resource instanceof Resource);
assert.deepStrictEqual(tracerProvider.resource, Resource.default());
});

it('should not merge with defaults when flag is set to false', function () {
const expectedResource = new Resource({ foo: 'bar' });
const tracerProvider = new BasicTracerProvider({
mergeResourceWithDefaults: false,
resource: expectedResource,
});
assert.deepStrictEqual(tracerProvider.resource, expectedResource);
});

it('should merge with defaults when flag is set to true', function () {
const providedResource = new Resource({ foo: 'bar' });
const tracerProvider = new BasicTracerProvider({
mergeResourceWithDefaults: true,
resource: providedResource,
});
assert.deepStrictEqual(
tracerProvider.resource,
Resource.default().merge(providedResource)
);
});
});

Expand Down
29 changes: 26 additions & 3 deletions packages/sdk-metrics/src/MeterProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,27 @@ export interface MeterProviderOptions {
resource?: IResource;
views?: View[];
readers?: MetricReader[];
/**
* Merge resource with {@link Resource.default()}?
* Default: {@code true}
**/
mergeResourceWithDefaults?: boolean;
}

/**
* @param mergeWithDefaults
* @param providedResource
*/
function prepareResource(
mergeWithDefaults: boolean,
providedResource: Resource | undefined
) {
const resource = providedResource ?? Resource.empty();

if (mergeWithDefaults) {
return Resource.default().merge(resource);
}
return resource;
}

/**
Expand All @@ -46,10 +67,12 @@ export class MeterProvider implements IMeterProvider {
private _shutdown = false;

constructor(options?: MeterProviderOptions) {
const resource = Resource.default().merge(
options?.resource ?? Resource.empty()
this._sharedState = new MeterProviderSharedState(
prepareResource(
options?.mergeResourceWithDefaults ?? true,
options?.resource
)
);
this._sharedState = new MeterProviderSharedState(resource);
if (options?.views != null && options.views.length > 0) {
for (const view of options.views) {
this._sharedState.viewRegistry.addView(view);
Expand Down
61 changes: 61 additions & 0 deletions packages/sdk-metrics/test/MeterProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { TestMetricReader } from './export/TestMetricReader';
import * as sinon from 'sinon';
import { View } from '../src/view/View';
import { Meter } from '../src/Meter';
import { Resource } from '@opentelemetry/resources';

describe('MeterProvider', () => {
afterEach(() => {
Expand All @@ -48,6 +49,66 @@ describe('MeterProvider', () => {
const meterProvider = new MeterProvider({ resource: defaultResource });
assert(meterProvider instanceof MeterProvider);
});

it('should use default resource when no resource is passed', async function () {
const reader = new TestMetricReader();

const meterProvider = new MeterProvider({
readers: [reader],
});

// Create meter and instrument, otherwise nothing will export
const myMeter = meterProvider.getMeter('meter1', 'v1.0.0');
const counter = myMeter.createCounter('non-renamed-instrument');
counter.add(1, { attrib1: 'attrib_value1', attrib2: 'attrib_value2' });

// Perform collection.
const { resourceMetrics } = await reader.collect();
assert.deepStrictEqual(resourceMetrics.resource, Resource.default());
});

it('should not merge with defaults when flag is set to false', async function () {
const reader = new TestMetricReader();
const expectedResource = new Resource({ foo: 'bar' });

const meterProvider = new MeterProvider({
readers: [reader],
resource: expectedResource,
mergeResourceWithDefaults: false,
});

// Create meter and instrument, otherwise nothing will export
const myMeter = meterProvider.getMeter('meter1', 'v1.0.0');
const counter = myMeter.createCounter('non-renamed-instrument');
counter.add(1, { attrib1: 'attrib_value1', attrib2: 'attrib_value2' });

// Perform collection.
const { resourceMetrics } = await reader.collect();
assert.deepStrictEqual(resourceMetrics.resource, expectedResource);
});

it('should merge with defaults when flag is set to true', async function () {
const reader = new TestMetricReader();
const providedResource = new Resource({ foo: 'bar' });

const meterProvider = new MeterProvider({
readers: [reader],
resource: providedResource,
mergeResourceWithDefaults: true,
});

// Create meter and instrument, otherwise nothing will export
const myMeter = meterProvider.getMeter('meter1', 'v1.0.0');
const counter = myMeter.createCounter('non-renamed-instrument');
counter.add(1, { attrib1: 'attrib_value1', attrib2: 'attrib_value2' });

// Perform collection.
const { resourceMetrics } = await reader.collect();
assert.deepStrictEqual(
resourceMetrics.resource,
Resource.default().merge(providedResource)
);
});
});

describe('getMeter', () => {
Expand Down