Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions experimental/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ For notes on migrating to 2.x / 0.200.x see [the upgrade guide](doc/upgrade-to-2
* refactor(configuration): dont have a default value for node resource detectors [#6131](https://github.com/open-telemetry/opentelemetry-js/pull/6131) @maryliag
* feat(configuration): doesnt set meter,tracer,logger provider by default [#6130](https://github.com/open-telemetry/opentelemetry-js/pull/6130) @maryliag
* feat(opentelemetry-sdk-node): set instrumentation and propagators for experimental start [#6148](https://github.com/open-telemetry/opentelemetry-js/pull/6148) @maryliag
* feat(opentelemetry-sdk-node): set resources and log provider for experimental start [#6152](https://github.com/open-telemetry/opentelemetry-js/pull/6152) @maryliag

### :bug: Bug Fixes

Expand Down
158 changes: 158 additions & 0 deletions experimental/packages/opentelemetry-sdk-node/src/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,46 @@
*/
import {
ConfigFactory,
ConfigurationModel,
createConfigFactory,
LogRecordExporterModel,
} from '@opentelemetry/configuration';
import { diag, DiagConsoleLogger } from '@opentelemetry/api';
import {
getPropagatorFromConfiguration,
getResourceDetectorsFromConfiguration,
setupDefaultContextManager,
setupPropagator,
} from './utils';
import { registerInstrumentations } from '@opentelemetry/instrumentation';
import type { SDKOptions } from './types';
import {
BatchLogRecordProcessor,
ConsoleLogRecordExporter,
LoggerProvider,
LogRecordExporter,
LogRecordProcessor,
SimpleLogRecordProcessor,
} from '@opentelemetry/sdk-logs';
import { OTLPLogExporter as OTLPHttpLogExporter } from '@opentelemetry/exporter-logs-otlp-http';
import { OTLPLogExporter as OTLPGrpcLogExporter } from '@opentelemetry/exporter-logs-otlp-grpc';
import { OTLPLogExporter as OTLPProtoLogExporter } from '@opentelemetry/exporter-logs-otlp-proto';
import { CompressionAlgorithm } from '@opentelemetry/otlp-exporter-base';
import { logs } from '@opentelemetry/api-logs';
import {
defaultResource,
detectResources,
envDetector,
hostDetector,
osDetector,
processDetector,
Resource,
ResourceDetectionConfig,
ResourceDetector,
resourceFromAttributes,
serviceInstanceIdDetector,
} from '@opentelemetry/resources';
import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';

/**
* @experimental Function to start the OpenTelemetry Node SDK
Expand Down Expand Up @@ -54,13 +84,141 @@ export function startNodeSDK(sdkOptions: SDKOptions): {
: (sdkOptions?.textMapPropagator ??
getPropagatorFromConfiguration(config))
);
const resource = setupResource(config, sdkOptions);
const loggerProvider = setupLoggerProvider(config, sdkOptions, resource);

const shutdownFn = async () => {
const promises: Promise<unknown>[] = [];
if (loggerProvider) {
promises.push(loggerProvider.shutdown());
}
await Promise.all(promises);
};
return { shutdown: shutdownFn };
}
const NOOP_SDK = {
shutdown: async () => {},
};

function setupResource(
config: ConfigurationModel,
sdkOptions: SDKOptions
): Resource {
let resource: Resource = sdkOptions.resource ?? defaultResource();
const autoDetectResources = sdkOptions.autoDetectResources ?? true;
let resourceDetectors: ResourceDetector[];

if (!autoDetectResources) {
resourceDetectors = [];
} else if (sdkOptions.resourceDetectors != null) {
resourceDetectors = sdkOptions.resourceDetectors;
} else if (config.node_resource_detectors) {
resourceDetectors = getResourceDetectorsFromConfiguration(config);
} else {
resourceDetectors = [
envDetector,
processDetector,
hostDetector,
osDetector,
serviceInstanceIdDetector,
];
}

if (autoDetectResources) {
const internalConfig: ResourceDetectionConfig = {
detectors: resourceDetectors,
};

resource = resource.merge(detectResources(internalConfig));
}

resource =
sdkOptions.serviceName === undefined
? resource
: resource.merge(
resourceFromAttributes({
[ATTR_SERVICE_NAME]: sdkOptions.serviceName,
})
);

return resource;
}

function setupLoggerProvider(
config: ConfigurationModel,
sdkOptions: SDKOptions,
resource: Resource | undefined
): LoggerProvider | undefined {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
): LoggerProvider | undefined {
): LoggerProvider {

Is there a scenario where setupLoggerProvider would be undefined? setupResource only returns a Resource not undefined

Copy link
Contributor Author

@maryliag maryliag Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you don't have any log provider on env variables or config file, there is no logger provider to be setup, so it will return undefined

setupResource have a default value for resources, this is why it doesn't have the option to return undefined

const logProcessors =
sdkOptions.logRecordProcessors ??
getLogRecordProcessorsFromConfiguration(config);

if (logProcessors) {
const loggerProvider = new LoggerProvider({
resource: resource,
processors: logProcessors,
});

logs.setGlobalLoggerProvider(loggerProvider);
return loggerProvider;
}
return undefined;
}

function getLogRecordExporter(
exporter: LogRecordExporterModel
): LogRecordExporter {
if (exporter.otlp_http) {
if (exporter.otlp_http.encoding === 'json') {
return new OTLPHttpLogExporter({
compression:
exporter.otlp_http.compression === 'gzip'
? CompressionAlgorithm.GZIP
: CompressionAlgorithm.NONE,
});
}
if (exporter.otlp_http.encoding === 'protobuf') {
return new OTLPProtoLogExporter();
}
diag.warn(`Unsupported OTLP logs protocol. Using http/protobuf.`);
return new OTLPProtoLogExporter();
} else if (exporter.otlp_grpc) {
return new OTLPGrpcLogExporter();
} else if (exporter.console) {
return new ConsoleLogRecordExporter();
}
diag.warn(`Unsupported Exporter value. Using OTLP http/protobuf.`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 If someone uses a config file but neglects to specify a valid exporter (otlp_http, otlp_grpc, otlp_file/development(soon), console)... should we default an exporter for them or should we consider it a noop because it's invalid? It's not clear to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe I saw the default should be otlp_http in this case, so I kept at that.

return new OTLPProtoLogExporter();
}

function getLogRecordProcessorsFromConfiguration(
config: ConfigurationModel
): LogRecordProcessor[] | undefined {
const logRecordProcessors: LogRecordProcessor[] = [];
config.logger_provider?.processors?.forEach(processor => {
if (processor.batch) {
logRecordProcessors.push(
new BatchLogRecordProcessor(
getLogRecordExporter(processor.batch.exporter),
{
maxQueueSize: processor.batch.max_queue_size,
maxExportBatchSize: processor.batch.max_export_batch_size,
scheduledDelayMillis: processor.batch.schedule_delay,
exportTimeoutMillis: processor.batch.export_timeout,
}
)
);
}
if (processor.simple) {
logRecordProcessors.push(
new SimpleLogRecordProcessor(
getLogRecordExporter(processor.simple.exporter)
)
);
}
});
if (logRecordProcessors.length > 0) {
return logRecordProcessors;
}
return undefined;
}
5 changes: 5 additions & 0 deletions experimental/packages/opentelemetry-sdk-node/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ export interface NodeSDKConfiguration {
* @experimental Options for new experimental SDK setup
*/
export interface SDKOptions {
autoDetectResources?: boolean;
instrumentations?: (Instrumentation | Instrumentation[])[];
logRecordProcessors?: LogRecordProcessor[];
resource?: Resource;
resourceDetectors?: ResourceDetector[];
serviceName?: string;
textMapPropagator?: TextMapPropagator | null;
}
31 changes: 31 additions & 0 deletions experimental/packages/opentelemetry-sdk-node/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,37 @@ export function getResourceDetectorsFromEnv(): Array<ResourceDetector> {
});
}

export function getResourceDetectorsFromConfiguration(
config: ConfigurationModel
): Array<ResourceDetector> {
// When updating this list, make sure to also update the section `resourceDetectors` on README.
const resourceDetectors = new Map<string, ResourceDetector>([
[RESOURCE_DETECTOR_ENVIRONMENT, envDetector],
[RESOURCE_DETECTOR_HOST, hostDetector],
[RESOURCE_DETECTOR_OS, osDetector],
[RESOURCE_DETECTOR_SERVICE_INSTANCE_ID, serviceInstanceIdDetector],
[RESOURCE_DETECTOR_PROCESS, processDetector],
]);

const resourceDetectorsFromConfig = config.node_resource_detectors ?? ['all'];

if (resourceDetectorsFromConfig.includes('all')) {
return [...resourceDetectors.values()].flat();
}

if (resourceDetectorsFromConfig.includes('none')) {
return [];
}

return resourceDetectorsFromConfig.flatMap(detector => {
const resourceDetector = resourceDetectors.get(detector);
if (!resourceDetector) {
diag.warn(`Invalid resource detector "${detector}" specified`);
}
return resourceDetector || [];
});
}

export function getOtlpProtocolFromEnv(): string {
return (
getStringFromEnv('OTEL_EXPORTER_OTLP_TRACES_PROTOCOL') ??
Expand Down