Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
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
30 changes: 30 additions & 0 deletions api/src/common/Entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* 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 { Attributes } from './Attributes';

export interface BindableProvider<T> {
forEntity(entity: Entity): T;
}

export type Entity = {
type: string;
identifier: Attributes;
attributes: Attributes;
schemaUrl?: string;
asyncAttributesPending: boolean;
waitForAsyncAttributes(): Promise<void>;
};
1 change: 1 addition & 0 deletions api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export { baggageEntryMetadataFromString } from './baggage/utils';
export type { Exception } from './common/Exception';
export type { HrTime, TimeInput } from './common/Time';
export type { Attributes, AttributeValue } from './common/Attributes';
export type { Entity } from './common/Entity';

// Context APIs
export { createContextKey, ROOT_CONTEXT } from './context/context';
Expand Down
3 changes: 2 additions & 1 deletion api/src/metrics/MeterProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@
* limitations under the License.
*/

import { BindableProvider } from '../common/Entity';
import { Meter, MeterOptions } from './Meter';

/**
* A registry for creating named {@link Meter}s.
*
* @since 1.3.0
*/
export interface MeterProvider {
export interface MeterProvider extends BindableProvider<MeterProvider> {
/**
* Returns a Meter, creating one if one with the given name, version, and
* schemaUrl pair is not already created.
Expand Down
5 changes: 5 additions & 0 deletions api/src/metrics/NoopMeterProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* limitations under the License.
*/

import { Entity } from '../common/Entity';
import { Meter, MeterOptions } from './Meter';
import { MeterProvider } from './MeterProvider';
import { NOOP_METER } from './NoopMeter';
Expand All @@ -23,6 +24,10 @@ import { NOOP_METER } from './NoopMeter';
* for all calls to `getMeter`
*/
export class NoopMeterProvider implements MeterProvider {
forEntity(_entity: Entity): this {
return this;
}

getMeter(_name: string, _version?: string, _options?: MeterOptions): Meter {
return NOOP_METER;
}
Expand Down
5 changes: 5 additions & 0 deletions api/src/trace/NoopTracerProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* limitations under the License.
*/

import { Entity } from '../common/Entity';
import { NoopTracer } from './NoopTracer';
import { Tracer } from './tracer';
import { TracerOptions } from './tracer_options';
Expand All @@ -33,4 +34,8 @@ export class NoopTracerProvider implements TracerProvider {
): Tracer {
return new NoopTracer();
}

forEntity(_entity: Entity): TracerProvider {
return this;
}
}
18 changes: 17 additions & 1 deletion api/src/trace/ProxyTracerProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* limitations under the License.
*/

import { Entity } from '../common/Entity';
import { Tracer } from './tracer';
import { TracerProvider } from './tracer_provider';
import { ProxyTracer } from './ProxyTracer';
Expand All @@ -35,6 +36,7 @@ const NOOP_TRACER_PROVIDER = new NoopTracerProvider();
*/
export class ProxyTracerProvider implements TracerProvider {
private _delegate?: TracerProvider;
private _entity?: Entity;

/**
* Get a {@link ProxyTracer}
Expand All @@ -47,7 +49,11 @@ export class ProxyTracerProvider implements TracerProvider {
}

getDelegate(): TracerProvider {
return this._delegate ?? NOOP_TRACER_PROVIDER;
const delegate = this._delegate ?? NOOP_TRACER_PROVIDER;
if (this._entity && delegate !== NOOP_TRACER_PROVIDER) {
return delegate.forEntity(this._entity);
}
return delegate;
}

/**
Expand All @@ -64,4 +70,14 @@ export class ProxyTracerProvider implements TracerProvider {
): Tracer | undefined {
return this._delegate?.getTracer(name, version, options);
}

forEntity(entity: Entity): TracerProvider {
if (this._delegate) {
return this._delegate.forEntity(entity);
}
// Return a new proxy that will apply the entity when a delegate is set
const boundProxy = new ProxyTracerProvider();
boundProxy._entity = entity;
return boundProxy;
}
}
10 changes: 10 additions & 0 deletions api/src/trace/tracer_provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* limitations under the License.
*/

import { Entity } from '../common/Entity';
import { Tracer } from './tracer';
import { TracerOptions } from './tracer_options';

Expand All @@ -36,4 +37,13 @@ export interface TracerProvider {
* @returns Tracer A Tracer with the given name and version
*/
getTracer(name: string, version?: string, options?: TracerOptions): Tracer;

/**
* Creates a new TracerProvider with the same configuration but with the
* provided entity merged into the resource.
*
* @param entity The entity to merge into the resource
* @returns A new TracerProvider with the merged entity
*/
forEntity(entity: Entity): TracerProvider;
}
2 changes: 2 additions & 0 deletions api/test/common/proxy-implementations/proxy-tracer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ describe('ProxyTracer', function () {
getTracerStub = sandbox.stub().returns(new NoopTracer());
delegate = {
getTracer: getTracerStub,
forEntity: sandbox.stub().returnsThis(),
};
provider.setDelegate(delegate);
});
Expand Down Expand Up @@ -127,6 +128,7 @@ describe('ProxyTracer', function () {
getTracer() {
return delegateTracer;
},
forEntity: sandbox.stub().returnsThis(),
};
provider.setDelegate(delegate);
});
Expand Down
14 changes: 14 additions & 0 deletions e2e-tests/test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
metrics,
} from '@opentelemetry/api';
import { logs } from '@opentelemetry/api-logs';
import { resourceFromDetectedResource } from '@opentelemetry/resources';

// Enable diagnostic logging (optional)
diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.INFO);
Expand Down Expand Up @@ -56,11 +57,24 @@ const logExporter = new OTLPLogExporter({
});
const logRecordProcessors = [new SimpleLogRecordProcessor(logExporter)];

const resource = resourceFromDetectedResource({
entities: [
{
type: 'service',
identifier: { 'service.name': 'example-service' },
attributes: { 'service.version': '1.0.0' },
schemaUrl: 'https://opentelemetry.io/schemas/1.0.0/service',
},
],
});

// Set up OpenTelemetry SDK
const sdk = new NodeSDK({
spanProcessors,
metricReader,
logRecordProcessors,
resource,
autoDetectResources: false, // Disable automatic resource detection
});

async function main() {
Expand Down
26 changes: 26 additions & 0 deletions e2e-tests/verify.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,19 @@ for (const line of lines) {
if (parsed.resourceSpans) {
console.log('found span');
verifySpan(parsed.resourceSpans[0].scopeSpans[0].spans[0]);
verifyResource(parsed.resourceSpans[0].resource);
verifiedSpan = true;
}
if (parsed.resourceMetrics) {
console.log('found metric');
verifyMetric(parsed.resourceMetrics[0].scopeMetrics[0].metrics[0]);
verifyResource(parsed.resourceMetrics[0].resource);
verifiedMetric = true;
}
if (parsed.resourceLogs) {
console.log('found log');
verifyLog(parsed.resourceLogs[0].scopeLogs[0].logRecords[0]);
verifyResource(parsed.resourceLogs[0].resource);
verifiedLog = true;
}
}
Expand All @@ -56,6 +59,29 @@ if (!verifiedLog) {
process.exit(1);
}

function verifyResource(resource) {
if (!resource || !resource.attributes) {
console.error('Resource attributes are missing');
process.exit(1);
}
const name = resource.attributes.find(attr => attr.key === 'service.name');
if (!name || name.value.stringValue !== 'example-service') {
console.error(
`Expected service.name to be 'example-service', but got '${name?.value.stringValue}'`
);
process.exit(1);
}
const version = resource.attributes.find(
attr => attr.key === 'service.version'
);
if (!version || version.value.stringValue !== '1.0.0') {
console.error(
`Expected service.version to be '1.0.0', but got '${version?.value.stringValue}'`
);
process.exit(1);
}
}

function verifySpan(span) {
const expectedName = 'example-span';
if (span.name !== expectedName) {
Expand Down
9 changes: 7 additions & 2 deletions experimental/packages/api-logs/src/NoopLoggerProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@
* limitations under the License.
*/

import { LoggerProvider } from './types/LoggerProvider';
import { Entity } from '@opentelemetry/api';
import { NoopLogger } from './NoopLogger';
import { Logger } from './types/Logger';
import { LoggerOptions } from './types/LoggerOptions';
import { NoopLogger } from './NoopLogger';
import { LoggerProvider } from './types/LoggerProvider';

export class NoopLoggerProvider implements LoggerProvider {
getLogger(
Expand All @@ -27,6 +28,10 @@ export class NoopLoggerProvider implements LoggerProvider {
): Logger {
return new NoopLogger();
}

forEntity(_entity: Entity): LoggerProvider {
return this;
}
}

export const NOOP_LOGGER_PROVIDER = new NoopLoggerProvider();
16 changes: 16 additions & 0 deletions experimental/packages/api-logs/src/ProxyLoggerProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ import { Logger } from './types/Logger';
import { LoggerOptions } from './types/LoggerOptions';
import { NOOP_LOGGER_PROVIDER } from './NoopLoggerProvider';
import { ProxyLogger } from './ProxyLogger';
import { Entity } from '@opentelemetry/api';

export class ProxyLoggerProvider implements LoggerProvider {
private _delegate?: LoggerProvider;
private _boundEntity?: Entity;

getLogger(
name: string,
Expand All @@ -34,6 +36,16 @@ export class ProxyLoggerProvider implements LoggerProvider {
);
}

forEntity(entity: Entity): LoggerProvider {
const boundProvider = this._delegate?.forEntity(entity);
if (boundProvider) {
return boundProvider;
}
const proxyLoggerProvider = new ProxyLoggerProvider();
proxyLoggerProvider._boundEntity = entity;
return proxyLoggerProvider;
}

/**
* Get the delegate logger provider.
* Used by tests only.
Expand All @@ -59,6 +71,10 @@ export class ProxyLoggerProvider implements LoggerProvider {
version?: string | undefined,
options?: LoggerOptions | undefined
): Logger | undefined {
if (this._boundEntity) {
const boundProvider = this._delegate?.forEntity(this._boundEntity);
return boundProvider?.getLogger(name, version, options);
}
return this._delegate?.getLogger(name, version, options);
}
}
3 changes: 3 additions & 0 deletions experimental/packages/api-logs/src/types/LoggerProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* limitations under the License.
*/

import { Entity } from '@opentelemetry/api';
import { Logger } from './Logger';
import { LoggerOptions } from './LoggerOptions';

Expand All @@ -31,4 +32,6 @@ export interface LoggerProvider {
* @returns Logger A Logger with the given name and version
*/
getLogger(name: string, version?: string, options?: LoggerOptions): Logger;

forEntity(entity: Entity): LoggerProvider;
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ describe('ProxyLogger', () => {
getLoggerStub = sandbox.stub().returns(new NoopLogger());
delegate = {
getLogger: getLoggerStub,
forEntity(entity) {
return this;
},
};
provider._setDelegate(delegate);
});
Expand Down Expand Up @@ -100,6 +103,9 @@ describe('ProxyLogger', () => {
getLogger() {
return delegateLogger;
},
forEntity() {
return this;
},
};
provider._setDelegate(delegateProvider);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ describe('HttpInstrumentation', () => {
.returns(trace.wrapSpanContext(INVALID_SPAN_CONTEXT));
return { startSpan: startSpanStub } as any;
},
forEntity: () => provider,
};
nock.cleanAll();
nock.enableNetConnect();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ describe('HttpsInstrumentation', () => {
.returns(trace.wrapSpanContext(INVALID_SPAN_CONTEXT));
return { startSpan: startSpanStub } as any;
},
forEntity: () => provider,
};
nock.cleanAll();
nock.enableNetConnect();
Expand Down
Loading
Loading