diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 0914da6eec7..e9ebddee36c 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -60,6 +60,7 @@ For notes on migrating to 2.x / 0.200.x see [the upgrade guide](doc/upgrade-to-2 * feat(exporter-prometheus): support withoutTargetInfo option [#5962](https://github.com/open-telemetry/opentelemetry-js/pull/5962) @cjihrig * feat(opentelemetry-configuration): parse trace provider from config file [#5992](https://github.com/open-telemetry/opentelemetry-js/pull/5992) @maryliag * feat(opentelemetry-configuration): parse logger provider from config file [#5995](https://github.com/open-telemetry/opentelemetry-js/pull/5995) @maryliag +* feat(opentelemetry-configuration): parse meter provider from config file [#6000](https://github.com/open-telemetry/opentelemetry-js/pull/6000) @maryliag * feat(opentelemetry-configuration): parse config file with format 1.0-rc.2 [#6029](https://github.com/open-telemetry/opentelemetry-js/pull/6029) @maryliag ### :bug: Bug Fixes diff --git a/experimental/packages/opentelemetry-configuration/src/EnvironmentConfigProvider.ts b/experimental/packages/opentelemetry-configuration/src/EnvironmentConfigProvider.ts index 4f29bbbc209..c96958a5d1e 100644 --- a/experimental/packages/opentelemetry-configuration/src/EnvironmentConfigProvider.ts +++ b/experimental/packages/opentelemetry-configuration/src/EnvironmentConfigProvider.ts @@ -26,6 +26,11 @@ import { getNumberFromEnv, } from '@opentelemetry/core'; import { ConfigProvider } from './IConfigProvider'; +import { + ExemplarFilter, + ExporterDefaultHistogramAggregation, + ExporterTemporalityPreference, +} from './models/meterProviderModel'; /** * EnvironmentConfigProvider provides a configuration based on environment variables. @@ -242,11 +247,14 @@ export function setTracerProvider(config: ConfigurationModel): void { } } -function setMeterProvider(config: ConfigurationModel): void { +export function setMeterProvider(config: ConfigurationModel): void { const readerPeriodic = config.meter_provider?.readers && config.meter_provider?.readers.length > 0 ? config.meter_provider?.readers[0].periodic : undefined; + if (config.meter_provider == null) { + config.meter_provider = { readers: [{}] }; + } if (readerPeriodic) { const interval = getNumberFromEnv('OTEL_METRIC_EXPORT_INTERVAL'); if (interval) { @@ -257,6 +265,9 @@ function setMeterProvider(config: ConfigurationModel): void { if (timeout) { readerPeriodic.timeout = timeout; } + if (readerPeriodic.exporter.otlp_http == null) { + readerPeriodic.exporter.otlp_http = {}; + } const endpoint = getStringFromEnv('OTEL_EXPORTER_OTLP_METRICS_ENDPOINT'); if (endpoint) { @@ -305,47 +316,65 @@ function setMeterProvider(config: ConfigurationModel): void { const temporalityPreference = getStringFromEnv( 'OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE' ); - if ( - temporalityPreference && - (temporalityPreference === 'cumulative' || - temporalityPreference === 'delta' || - temporalityPreference === 'low_memory') - ) { - readerPeriodic.exporter.otlp_http.temporality_preference = - temporalityPreference; + if (temporalityPreference) { + switch (temporalityPreference) { + case 'cumulative': + readerPeriodic.exporter.otlp_http.temporality_preference = + ExporterTemporalityPreference.Cumulative; + break; + case 'delta': + readerPeriodic.exporter.otlp_http.temporality_preference = + ExporterTemporalityPreference.Delta; + break; + case 'low_memory': + readerPeriodic.exporter.otlp_http.temporality_preference = + ExporterTemporalityPreference.LowMemory; + break; + default: + readerPeriodic.exporter.otlp_http.temporality_preference = + ExporterTemporalityPreference.Cumulative; + break; + } } const defaultHistogramAggregation = getStringFromEnv( 'OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION' ); - if ( - defaultHistogramAggregation && - (defaultHistogramAggregation === 'explicit_bucket_histogram' || - defaultHistogramAggregation === 'base2_exponential_bucket_histogram') - ) { - readerPeriodic.exporter.otlp_http.default_histogram_aggregation = - defaultHistogramAggregation; - } - if (config.meter_provider == null) { - config.meter_provider = { readers: [{}] }; - } - if (config.meter_provider?.readers == null) { - config.meter_provider.readers = [{}]; + if (defaultHistogramAggregation) { + switch (defaultHistogramAggregation) { + case 'explicit_bucket_histogram': + readerPeriodic.exporter.otlp_http.default_histogram_aggregation = + ExporterDefaultHistogramAggregation.ExplicitBucketHistogram; + break; + case 'base2_exponential_bucket_histogram': + readerPeriodic.exporter.otlp_http.default_histogram_aggregation = + ExporterDefaultHistogramAggregation.Base2ExponentialBucketHistogram; + break; + default: + readerPeriodic.exporter.otlp_http.default_histogram_aggregation = + ExporterDefaultHistogramAggregation.ExplicitBucketHistogram; + break; + } } config.meter_provider.readers[0].periodic = readerPeriodic; } const exemplarFilter = getStringFromEnv('OTEL_METRICS_EXEMPLAR_FILTER'); - if ( - exemplarFilter && - (exemplarFilter === 'trace_based' || - exemplarFilter === 'always_on' || - exemplarFilter === 'always_off') - ) { - if (config.meter_provider == null) { - config.meter_provider = {}; - } - config.meter_provider.exemplar_filter = exemplarFilter; + if (exemplarFilter) { + switch (exemplarFilter) { + case 'trace_based': + config.meter_provider.exemplar_filter = ExemplarFilter.TraceBased; + break; + case 'always_on': + config.meter_provider.exemplar_filter = ExemplarFilter.AlwaysOn; + break; + case 'always_off': + config.meter_provider.exemplar_filter = ExemplarFilter.AlwaysOff; + break; + default: + config.meter_provider.exemplar_filter = ExemplarFilter.TraceBased; + break; + } } } diff --git a/experimental/packages/opentelemetry-configuration/src/FileConfigProvider.ts b/experimental/packages/opentelemetry-configuration/src/FileConfigProvider.ts index 0d7ed001c55..1da7cdc4dc5 100644 --- a/experimental/packages/opentelemetry-configuration/src/FileConfigProvider.ts +++ b/experimental/packages/opentelemetry-configuration/src/FileConfigProvider.ts @@ -17,7 +17,6 @@ import { diagLogLevelFromString, getStringFromEnv } from '@opentelemetry/core'; import { AttributeLimits, - MeterProvider, Propagator, ConfigurationModel, initializeDefaultConfiguration, @@ -45,6 +44,22 @@ import { LogRecordProcessor, } from './models/loggerProviderModel'; import { AttributeNameValue } from './models/resourceModel'; +import { + Aggregation, + CardinalityLimits, + ExemplarFilter, + ExporterDefaultHistogramAggregation, + ExporterTemporalityPreference, + InstrumentType, + MeterProvider, + MetricProducer, + MetricReader, + PullMetricExporter, + PushMetricExporter, + View, + ViewSelector, + ViewStream, +} from './models/meterProviderModel'; export class FileConfigProvider implements ConfigProvider { private _config: ConfigurationModel; @@ -264,7 +279,7 @@ enum ProviderType { LOGGER = 2, } -function parseConfigExporter( +function parseConfigSpanOrLogRecordExporter( exporter: SpanExporter | LogRecordExporter, providerType: ProviderType ): SpanExporter | LogRecordExporter { @@ -301,8 +316,8 @@ function parseConfigExporter( timeout: getNumberFromConfigFile(e['timeout']) ?? 10000, encoding: getStringFromConfigFile(e['encoding']) === 'json' - ? OtlpHttpEncoding.json - : OtlpHttpEncoding.protobuf, + ? OtlpHttpEncoding.JSON + : OtlpHttpEncoding.Protobuf, }, }; @@ -389,7 +404,7 @@ function parseConfigExporter( case 'console': parsedExporter = { - console: {}, + console: undefined, }; break; @@ -484,7 +499,7 @@ export function setTracerProvider( if (processorType === 'batch') { const element = tracerProvider['processors'][i]['batch']; if (element) { - const parsedExporter = parseConfigExporter( + const parsedExporter = parseConfigSpanOrLogRecordExporter( element['exporter'], ProviderType.TRACER ); @@ -508,7 +523,7 @@ export function setTracerProvider( } else if (processorType === 'simple') { const element = tracerProvider['processors'][i]['simple']; if (element) { - const parsedExporter = parseConfigExporter( + const parsedExporter = parseConfigSpanOrLogRecordExporter( element['exporter'], ProviderType.TRACER ); @@ -526,24 +541,499 @@ export function setTracerProvider( } } +function getCardinalityLimits(limits?: CardinalityLimits): CardinalityLimits { + if (limits == null) { + limits = {}; + } + const defaultLimit = getNumberFromConfigFile(limits['default']) ?? 2000; + + return { + default: defaultLimit, + counter: getNumberFromConfigFile(limits['counter']) ?? defaultLimit, + gauge: getNumberFromConfigFile(limits['gauge']) ?? defaultLimit, + histogram: getNumberFromConfigFile(limits['histogram']) ?? defaultLimit, + observable_counter: + getNumberFromConfigFile(limits['observable_counter']) ?? defaultLimit, + observable_gauge: + getNumberFromConfigFile(limits['observable_gauge']) ?? defaultLimit, + observable_up_down_counter: + getNumberFromConfigFile(limits['observable_up_down_counter']) ?? + defaultLimit, + up_down_counter: + getNumberFromConfigFile(limits['up_down_counter']) ?? defaultLimit, + }; +} + +function getProducers(producers?: MetricProducer[]): MetricProducer[] { + const parsedProducers: MetricProducer[] = []; + if (producers) { + for (let j = 0; j < producers.length; j++) { + const producer = producers[j]; + if (Object.keys(producer)[0] === 'opencensus') { + parsedProducers.push({ opencensus: undefined }); + } + if (Object.keys(producer)[0] === 'prometheus') { + parsedProducers.push({ prometheus: undefined }); + } + } + } + + return parsedProducers; +} + +export function getTemporalityPreference( + temporalityPreference?: ExporterTemporalityPreference +): ExporterTemporalityPreference { + const temporalityPreferenceType = getStringFromConfigFile( + temporalityPreference + ); + switch (temporalityPreferenceType) { + case 'cumulative': + return ExporterTemporalityPreference.Cumulative; + case 'delta': + return ExporterTemporalityPreference.Delta; + case 'low_memory': + return ExporterTemporalityPreference.LowMemory; + default: + return ExporterTemporalityPreference.Cumulative; + } +} + +function getDefaultHistogramAggregation( + defaultHistogramAggregation?: ExporterDefaultHistogramAggregation +): ExporterDefaultHistogramAggregation { + const defaultHistogramAggregationType = getStringFromConfigFile( + defaultHistogramAggregation + ); + switch (defaultHistogramAggregationType) { + case 'explicit_bucket_histogram': + return ExporterDefaultHistogramAggregation.ExplicitBucketHistogram; + case 'base2_exponential_bucket_histogram': + return ExporterDefaultHistogramAggregation.Base2ExponentialBucketHistogram; + default: + return ExporterDefaultHistogramAggregation.ExplicitBucketHistogram; + } +} + +function parseMetricExporter(exporter: PushMetricExporter): PushMetricExporter { + const exporterType = Object.keys(exporter)[0]; + let parsedExporter: PushMetricExporter = {}; + let e; + let certFile; + let clientCertFile; + let clientKeyFile; + let compression; + let headers; + let headersList; + let insecure; + + switch (exporterType) { + case 'otlp_http': + e = exporter['otlp_http']; + if (e) { + parsedExporter = { + otlp_http: { + endpoint: + getStringFromConfigFile(e['endpoint']) ?? + 'http://localhost:4318/v1/metrics', + timeout: getNumberFromConfigFile(e['timeout']) ?? 10000, + encoding: + getStringFromConfigFile(e['encoding']) === 'json' + ? OtlpHttpEncoding.JSON + : OtlpHttpEncoding.Protobuf, + temporality_preference: getTemporalityPreference( + e['temporality_preference'] + ), + default_histogram_aggregation: getDefaultHistogramAggregation( + e['default_histogram_aggregation'] + ), + }, + }; + + certFile = getStringFromConfigFile(e['certificate_file']); + if (certFile && parsedExporter.otlp_http) { + parsedExporter.otlp_http.certificate_file = certFile; + } + clientCertFile = getStringFromConfigFile(e['client_certificate_file']); + if (clientCertFile && parsedExporter.otlp_http) { + parsedExporter.otlp_http.client_certificate_file = clientCertFile; + } + clientKeyFile = getStringFromConfigFile(e['client_key_file']); + if (clientKeyFile && parsedExporter.otlp_http) { + parsedExporter.otlp_http.client_key_file = clientKeyFile; + } + compression = getStringFromConfigFile(e['compression']); + if (compression && parsedExporter.otlp_http) { + parsedExporter.otlp_http.compression = compression; + } + headersList = getStringFromConfigFile(e['headers_list']); + if (headersList && parsedExporter.otlp_http) { + parsedExporter.otlp_http.headers_list = headersList; + } + headers = getConfigHeaders(e['headers']); + if (headers && parsedExporter.otlp_http) { + parsedExporter.otlp_http.headers = headers; + } + } + break; + + case 'otlp_grpc': + e = exporter['otlp_grpc']; + if (e) { + parsedExporter = { + otlp_grpc: { + endpoint: + getStringFromConfigFile(e['endpoint']) ?? 'http://localhost:4317', + timeout: getNumberFromConfigFile(e['timeout']) ?? 10000, + temporality_preference: getTemporalityPreference( + e['temporality_preference'] + ), + default_histogram_aggregation: getDefaultHistogramAggregation( + e['default_histogram_aggregation'] + ), + }, + }; + + certFile = getStringFromConfigFile(e['certificate_file']); + if (certFile && parsedExporter.otlp_grpc) { + parsedExporter.otlp_grpc.certificate_file = certFile; + } + clientCertFile = getStringFromConfigFile(e['client_certificate_file']); + if (clientCertFile && parsedExporter.otlp_grpc) { + parsedExporter.otlp_grpc.client_certificate_file = clientCertFile; + } + clientKeyFile = getStringFromConfigFile(e['client_key_file']); + if (clientKeyFile && parsedExporter.otlp_grpc) { + parsedExporter.otlp_grpc.client_key_file = clientKeyFile; + } + compression = getStringFromConfigFile(e['compression']); + if (compression && parsedExporter.otlp_grpc) { + parsedExporter.otlp_grpc.compression = compression; + } + headersList = getStringFromConfigFile(e['headers_list']); + if (headersList && parsedExporter.otlp_grpc) { + parsedExporter.otlp_grpc.headers_list = headersList; + } + headers = getConfigHeaders(e['headers']); + if (headers && parsedExporter.otlp_grpc) { + parsedExporter.otlp_grpc.headers = headers; + } + insecure = getBooleanFromConfigFile(e['insecure']); + if ((insecure || insecure === false) && parsedExporter.otlp_grpc) { + parsedExporter.otlp_grpc.insecure = insecure; + } + } + break; + + case 'otlp_file/development': + e = exporter['otlp_file/development']; + if (e) { + parsedExporter = { + 'otlp_file/development': { + output_stream: + getStringFromConfigFile(e['output_stream']) ?? 'stdout', + temporality_preference: getTemporalityPreference( + e['temporality_preference'] + ), + default_histogram_aggregation: getDefaultHistogramAggregation( + e['default_histogram_aggregation'] + ), + }, + }; + } + break; + + case 'console': + parsedExporter = { + console: undefined, + }; + break; + } + + return parsedExporter; +} + export function setMeterProvider( config: ConfigurationModel, meterProvider: MeterProvider ): void { if (meterProvider) { if (config.meter_provider == null) { - config.meter_provider = {}; + config.meter_provider = { readers: [] }; } const exemplarFilter = getStringFromConfigFile( meterProvider['exemplar_filter'] ); - if ( - exemplarFilter && - (exemplarFilter === 'trace_based' || - exemplarFilter === 'always_on' || - exemplarFilter === 'always_off') - ) { - config.meter_provider.exemplar_filter = exemplarFilter; + if (exemplarFilter) { + switch (exemplarFilter) { + case 'trace_based': + config.meter_provider.exemplar_filter = ExemplarFilter.TraceBased; + break; + case 'always_on': + config.meter_provider.exemplar_filter = ExemplarFilter.AlwaysOn; + break; + case 'always_off': + config.meter_provider.exemplar_filter = ExemplarFilter.AlwaysOff; + break; + default: + config.meter_provider.exemplar_filter = ExemplarFilter.TraceBased; + break; + } + } + + if (meterProvider['readers'] && meterProvider['readers'].length > 0) { + config.meter_provider.readers = []; + + for (let i = 0; i < meterProvider['readers'].length; i++) { + const readerType = Object.keys(meterProvider['readers'][i])[0]; + if (readerType === 'pull') { + const element = meterProvider['readers'][i]['pull']; + if (element) { + const exporter: PullMetricExporter = { + 'prometheus/development': { + host: + getStringFromConfigFile( + element['exporter']['prometheus/development']['host'] + ) ?? 'localhost', + port: + getNumberFromConfigFile( + element['exporter']['prometheus/development']['port'] + ) ?? 9464, + without_scope_info: + getBooleanFromConfigFile( + element['exporter']['prometheus/development'][ + 'without_scope_info' + ] + ) ?? false, + with_resource_constant_labels: { + included: + getStringListFromConfigFile( + element['exporter']['prometheus/development'][ + 'with_resource_constant_labels' + ]['included'] + ) ?? [], + excluded: + getStringListFromConfigFile( + element['exporter']['prometheus/development'][ + 'with_resource_constant_labels' + ]['excluded'] + ) ?? [], + }, + }, + }; + + const pullReader: MetricReader = { + pull: { + exporter: exporter, + cardinality_limits: getCardinalityLimits( + element['cardinality_limits'] + ), + }, + }; + const p = getProducers(element['producers']); + if (p.length > 0 && pullReader.pull) { + pullReader.pull.producers = p; + } + config.meter_provider.readers.push(pullReader); + } + } else if (readerType === 'periodic') { + const element = meterProvider['readers'][i]['periodic']; + if (element) { + const parsedExporter = parseMetricExporter(element['exporter']); + + const periodicReader: MetricReader = { + periodic: { + exporter: parsedExporter, + cardinality_limits: getCardinalityLimits( + element['cardinality_limits'] + ), + interval: getNumberFromConfigFile(element['interval']) ?? 60000, + timeout: getNumberFromConfigFile(element['timeout']) ?? 30000, + }, + }; + const p = getProducers(element['producers']); + if (p.length > 0 && periodicReader.periodic) { + periodicReader.periodic.producers = p; + } + config.meter_provider.readers.push(periodicReader); + } + } + } + } + + if (meterProvider['views'] && meterProvider['views'].length > 0) { + config.meter_provider.views = []; + for (let j = 0; j < meterProvider['views'].length; j++) { + const element = meterProvider['views'][j]; + const view: View = {}; + if (element['selector']) { + const selector: ViewSelector = {}; + const instrumentName = getStringFromConfigFile( + element['selector']['instrument_name'] + ); + if (instrumentName) { + selector.instrument_name = instrumentName; + } + + const unit = getStringFromConfigFile(element['selector']['unit']); + if (unit) { + selector.unit = unit; + } + + const meterName = getStringFromConfigFile( + element['selector']['meter_name'] + ); + if (meterName) { + selector.meter_name = meterName; + } + + const meterVersion = getStringFromConfigFile( + element['selector']['meter_version'] + ); + if (meterVersion) { + selector.meter_version = meterVersion; + } + + const meterSchemaUrl = getStringFromConfigFile( + element['selector']['meter_schema_url'] + ); + if (meterSchemaUrl) { + selector.meter_schema_url = meterSchemaUrl; + } + + const instrumentType = getStringFromConfigFile( + element['selector']['instrument_type'] + ); + if (instrumentType) { + switch (instrumentType) { + case 'counter': + selector.instrument_type = InstrumentType.Counter; + break; + case 'gauge': + selector.instrument_type = InstrumentType.Gauge; + break; + case 'histogram': + selector.instrument_type = InstrumentType.Histogram; + break; + case 'observable_counter': + selector.instrument_type = InstrumentType.ObservableCounter; + break; + case 'observable_gauge': + selector.instrument_type = InstrumentType.ObservableGauge; + break; + case 'observable_up_down_counter': + selector.instrument_type = + InstrumentType.ObservableUpDownCounter; + break; + case 'up_down_counter': + selector.instrument_type = InstrumentType.UpDownCounter; + break; + } + } + + if (Object.keys(selector).length > 0) { + view.selector = selector; + } + } + if (element['stream']) { + const stream: ViewStream = {}; + const name = getStringFromConfigFile(element['stream']['name']); + if (name) { + stream.name = name; + } + + const description = getStringFromConfigFile( + element['stream']['description'] + ); + if (description) { + stream.description = description; + } + + const aggregationCardinalityLimit = getNumberFromConfigFile( + element['stream']['aggregation_cardinality_limit'] + ); + if (aggregationCardinalityLimit) { + stream.aggregation_cardinality_limit = aggregationCardinalityLimit; + } + + if (element['stream']['attribute_keys']) { + stream.attribute_keys = { + included: + getStringListFromConfigFile( + element['stream']['attribute_keys']['included'] + ) ?? [], + excluded: + getStringListFromConfigFile( + element['stream']['attribute_keys']['excluded'] + ) ?? [], + }; + } + const rawAgg = element['stream']['aggregation']; + if (rawAgg) { + const aggregation: Aggregation = {}; + if (rawAgg['default']) { + aggregation.default = {}; + } + if (rawAgg['drop']) { + aggregation.drop = {}; + } + if (rawAgg['last_value']) { + aggregation.last_value = {}; + } + if (rawAgg['sum']) { + aggregation.sum = {}; + } + if (rawAgg['explicit_bucket_histogram']) { + aggregation.explicit_bucket_histogram = { + boundaries: getNumberListFromConfigFile( + rawAgg['explicit_bucket_histogram']['boundaries'] + ) ?? [ + 0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, + 7500, 10000, + ], + record_min_max: + getBooleanFromConfigFile( + rawAgg['explicit_bucket_histogram']['record_min_max'] + ) === false + ? false + : true, + }; + } + if (rawAgg['base2_exponential_bucket_histogram']) { + aggregation.base2_exponential_bucket_histogram = { + record_min_max: + getBooleanFromConfigFile( + rawAgg['base2_exponential_bucket_histogram'][ + 'record_min_max' + ] + ) === false + ? false + : true, + }; + const maxScale = getNumberFromConfigFile( + rawAgg['base2_exponential_bucket_histogram']['max_scale'] + ); + if (maxScale) { + aggregation.base2_exponential_bucket_histogram.max_scale = + maxScale; + } + + const maxSize = getNumberFromConfigFile( + rawAgg['base2_exponential_bucket_histogram']['max_size'] + ); + if (maxSize) { + aggregation.base2_exponential_bucket_histogram.max_size = + maxSize; + } + } + stream.aggregation = aggregation; + } + if (Object.keys(stream).length > 0) { + view.stream = stream; + } + } + config.meter_provider.views.push(view); + } } } } @@ -591,7 +1081,7 @@ export function setLoggerProvider( if (processorType === 'batch') { const element = loggerProvider['processors'][i]['batch']; if (element) { - const parsedExporter = parseConfigExporter( + const parsedExporter = parseConfigSpanOrLogRecordExporter( element['exporter'], ProviderType.LOGGER ); @@ -615,7 +1105,7 @@ export function setLoggerProvider( } else if (processorType === 'simple') { const element = loggerProvider['processors'][i]['simple']; if (element) { - const parsedExporter = parseConfigExporter( + const parsedExporter = parseConfigSpanOrLogRecordExporter( element['exporter'], ProviderType.LOGGER ); diff --git a/experimental/packages/opentelemetry-configuration/src/models/commonModel.ts b/experimental/packages/opentelemetry-configuration/src/models/commonModel.ts index 55a55535994..1d8cbf626c8 100644 --- a/experimental/packages/opentelemetry-configuration/src/models/commonModel.ts +++ b/experimental/packages/opentelemetry-configuration/src/models/commonModel.ts @@ -23,7 +23,7 @@ export interface IncludeExclude { * * If the value of the attribute key matches the wildcard pattern, where '?' matches any single character and '*' matches any number of characters including none. * If omitted, all attributes are included. */ - include: string[]; + included?: string[]; /** * Configure list of attribute key patterns to exclude from resource detectors. Applies after .resource.detectors.attributes.included (i.e. excluded has higher priority than included). @@ -32,7 +32,7 @@ export interface IncludeExclude { * * If the value of the attribute key matches the wildcard pattern, where '?' matches any single character and '*' matches any number of characters including none. * If omitted, .included attributes are included. */ - exclude: string[]; + excluded?: string[]; } export interface NameStringValuePair { @@ -106,8 +106,8 @@ export interface OtlpHttpExporter { } export enum OtlpHttpEncoding { - protobuf, - json, + JSON = 'json', + Protobuf = 'protobuf', } export interface OtlpGrpcExporter { diff --git a/experimental/packages/opentelemetry-configuration/src/models/configModel.ts b/experimental/packages/opentelemetry-configuration/src/models/configModel.ts index 8aa13872ab2..63450cf8d1b 100644 --- a/experimental/packages/opentelemetry-configuration/src/models/configModel.ts +++ b/experimental/packages/opentelemetry-configuration/src/models/configModel.ts @@ -16,7 +16,6 @@ 'use strict'; import { DiagLogLevel } from '@opentelemetry/api'; -import { NameStringValuePair } from './commonModel'; import { initializeDefaultTracerProviderConfiguration, TracerProvider, @@ -26,6 +25,10 @@ import { LoggerProvider, } from './loggerProviderModel'; import { Resource } from './resourceModel'; +import { + initializeDefaultMeterProviderConfiguration, + MeterProvider, +} from './meterProviderModel'; export interface ConfigurationModel { /** @@ -93,25 +96,7 @@ export function initializeDefaultConfiguration(): ConfigurationModel { composite_list: 'tracecontext,baggage', }, tracer_provider: initializeDefaultTracerProviderConfiguration(), - meter_provider: { - readers: [ - { - periodic: { - interval: 60000, - timeout: 30000, - exporter: { - otlp_http: { - endpoint: 'http://localhost:4318/v1/metrics', - timeout: 10000, - temporality_preference: 'cumulative', - default_histogram_aggregation: 'explicit_bucket_histogram', - }, - }, - }, - }, - ], - exemplar_filter: 'trace_based', - }, + meter_provider: initializeDefaultMeterProviderConfiguration(), logger_provider: initializeDefaultLoggerProviderConfiguration(), }; @@ -153,122 +138,3 @@ export interface Propagator { */ composite_list?: string; } - -export interface ConfigMeterExporter { - /** - * Configure exporter to be OTLP with HTTP transport. - */ - otlp_http: ConfigMeterOTLPHttp; -} - -export interface ConfigMeterOTLPHttp { - /** - * Configure endpoint, including the metric specific path. - * If omitted or null, http://localhost:4318/v1/metrics is used. - */ - endpoint: string; - - /** - * Configure certificate used to verify a server's TLS credentials. - * Absolute path to certificate file in PEM format. - * If omitted or null, system default certificate verification is used for secure connections. - */ - certificate_file?: string; - - /** - * Configure mTLS private client key. - * Absolute path to client key file in PEM format. If set, .client_certificate must also be set. - * If omitted or null, mTLS is not used. - */ - client_key_file?: string; - - /** - * Configure mTLS client certificate. - * Absolute path to client certificate file in PEM format. If set, .client_key must also be set. - * If omitted or null, mTLS is not used. - */ - client_certificate_file?: string; - - /** - * Configure compression. - * Values include: gzip, none. Implementations may support other compression algorithms. - * If omitted or null, none is used. - */ - compression?: string; - - /** - * Configure max time (in milliseconds) to wait for each export. - * Value must be non-negative. A value of 0 indicates no limit (infinity). - * If omitted or null, 10000 is used. - */ - timeout: number; - - /** - * Configure headers. Entries have higher priority than entries from .headers_list. - * If an entry's .value is null, the entry is ignored. - */ - headers?: NameStringValuePair[]; - - /** - * Configure headers. Entries have lower priority than entries from .headers. - * The value is a list of comma separated key-value pairs matching the format of OTEL_EXPORTER_OTLP_HEADERS. - * If omitted or null, no headers are added. - */ - headers_list?: string; - - /** - * Configure temporality preference. - * Values include: cumulative, delta, low_memory. - * If omitted or null, cumulative is used. - */ - temporality_preference: 'cumulative' | 'delta' | 'low_memory'; - - /** - * Configure default histogram aggregation. - * Values include: explicit_bucket_histogram, base2_exponential_bucket_histogram. - * If omitted or null, explicit_bucket_histogram is used. - */ - default_histogram_aggregation: - | 'explicit_bucket_histogram' - | 'base2_exponential_bucket_histogram'; -} -export interface ConfigPeriodicReader { - /** - * Configure delay interval (in milliseconds) between start of two consecutive exports. - * Value must be non-negative. - * If omitted or null, 60000 is used. - */ - interval: number; - - /** - * Configure maximum allowed time (in milliseconds) to export data. - * Value must be non-negative. A value of 0 indicates no limit (infinity). - * If omitted or null, 30000 is used. - */ - timeout: number; - - /** - * Configure exporter. - */ - exporter: ConfigMeterExporter; -} - -export interface ConfigReader { - /** - * Configure a periodic metric reader. - */ - periodic?: ConfigPeriodicReader; -} -export interface MeterProvider { - /** - * Configure metric readers. - */ - readers?: ConfigReader[]; - - /** - * Configure the exemplar filter. - * Values include: trace_based, always_on, always_off. - * If omitted or null, trace_based is used. - */ - exemplar_filter?: 'trace_based' | 'always_on' | 'always_off'; -} diff --git a/experimental/packages/opentelemetry-configuration/src/models/loggerProviderModel.ts b/experimental/packages/opentelemetry-configuration/src/models/loggerProviderModel.ts index cbb1bd2fe2c..1816a28d993 100644 --- a/experimental/packages/opentelemetry-configuration/src/models/loggerProviderModel.ts +++ b/experimental/packages/opentelemetry-configuration/src/models/loggerProviderModel.ts @@ -35,7 +35,7 @@ export function initializeDefaultLoggerProviderConfiguration(): LoggerProvider { otlp_http: { endpoint: 'http://localhost:4318/v1/logs', timeout: 10000, - encoding: OtlpHttpEncoding.protobuf, + encoding: OtlpHttpEncoding.Protobuf, }, }, }, diff --git a/experimental/packages/opentelemetry-configuration/src/models/meterProviderModel.ts b/experimental/packages/opentelemetry-configuration/src/models/meterProviderModel.ts new file mode 100644 index 00000000000..f6d38cdcdbc --- /dev/null +++ b/experimental/packages/opentelemetry-configuration/src/models/meterProviderModel.ts @@ -0,0 +1,597 @@ +/* + * 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. + */ +'use strict'; + +import { + IncludeExclude, + NameStringValuePair, + OtlpHttpEncoding, +} from './commonModel'; + +export function initializeDefaultMeterProviderConfiguration(): MeterProvider { + return { + readers: [ + { + periodic: { + interval: 60000, + timeout: 30000, + exporter: { + otlp_http: { + endpoint: 'http://localhost:4318/v1/metrics', + timeout: 10000, + temporality_preference: ExporterTemporalityPreference.Cumulative, + default_histogram_aggregation: + ExporterDefaultHistogramAggregation.ExplicitBucketHistogram, + }, + }, + }, + }, + ], + exemplar_filter: ExemplarFilter.TraceBased, + }; +} + +export interface MeterProvider { + /** + * Configure metric readers. + */ + readers: MetricReader[]; + + /** + * Configure views. + * Each view has a selector which determines the instrument(s) it applies to, + * and a configuration for the resulting stream(s). + */ + views?: View[]; + + /** + * Configure the exemplar filter. + * Values include: trace_based, always_on, always_off. + * If omitted or null, trace_based is used. + */ + exemplar_filter?: ExemplarFilter; +} + +export enum ExemplarFilter { + AlwaysOff = 'always_off', + AlwaysOn = 'always_on', + TraceBased = 'trace_based', +} + +export interface PeriodicMetricReader { + /** + * Configure delay interval (in milliseconds) between start of two consecutive exports. + * Value must be non-negative. + * If omitted or null, 60000 is used. + */ + interval?: number; + + /** + * Configure maximum allowed time (in milliseconds) to export data. + * Value must be non-negative. A value of 0 indicates no limit (infinity). + * If omitted or null, 30000 is used. + */ + timeout?: number; + + /** + * Configure exporter. + */ + exporter: PushMetricExporter; + + /** + * Configure metric producers. + */ + producers?: MetricProducer[]; + + /** + * Configure cardinality limits. + */ + cardinality_limits?: CardinalityLimits; +} + +export interface PullMetricReader { + /** + * Configure exporter. + */ + exporter: PullMetricExporter; + + /** + * Configure metric producers. + */ + producers?: MetricProducer[]; + + /** + * Configure cardinality limits. + */ + cardinality_limits?: CardinalityLimits; +} + +export interface CardinalityLimits { + /** + * Configure default cardinality limit for all instrument types. + * Instrument-specific cardinality limits take priority. + * If omitted or null, 2000 is used. + */ + default?: number; + + /** + * Configure default cardinality limit for counter instruments. + * If omitted or null, the value from .default is used. + */ + counter?: number; + + /** + * Configure default cardinality limit for gauge instruments. + * If omitted or null, the value from .default is used. + */ + gauge?: number; + + /** + * Configure default cardinality limit for histogram instruments. + * If omitted or null, the value from .default is used. + */ + histogram?: number; + + /** + * Configure default cardinality limit for observable_counter instruments. + * If omitted or null, the value from .default is used. + */ + observable_counter?: number; + + /** + * Configure default cardinality limit for observable_gauge instruments. + * If omitted or null, the value from .default is used. + */ + observable_gauge?: number; + + /** + * Configure default cardinality limit for observable_up_down_counter instruments. + * If omitted or null, the value from .default is used. + */ + observable_up_down_counter?: number; + + /** + * Configure default cardinality limit for up_down_counter instruments. + * If omitted or null, the value from .default is used. + */ + up_down_counter?: number; +} + +export interface PushMetricExporter { + /** + * Configure exporter to be OTLP with HTTP transport. + */ + otlp_http?: OtlpHttpMetricExporter; + + /** + * Configure exporter to be OTLP with gRPC transport. + */ + otlp_grpc?: OtlpGrpcMetricExporter; + + /** + * Configure exporter to be OTLP with file transport. + * This type is in development and subject to breaking changes in minor versions. + */ + 'otlp_file/development'?: OtlpFileMetricExporter; + + /** + * Configure exporter to be console. + */ + console?: object; +} + +export interface PullMetricExporter { + /** + * Configure exporter to be prometheus. + * This type is in development and subject to breaking changes in minor versions. + */ + 'prometheus/development': PrometheusMetricExporter; +} + +export interface MetricProducer { + /** + * Configure metric producer to be opencensus. + */ + opencensus?: object; + + /** + * Configure metric producer to be prometheus. + */ + prometheus?: object; +} + +export interface PrometheusMetricExporter { + /** + * Configure host. + * If omitted or null, localhost is used. + */ + host?: string; + + /** + * Configure port. + * If omitted or null, 9464 is used. + */ + port?: number; + + /** + * Configure Prometheus Exporter to produce metrics without a scope info metric. + * If omitted or null, false is used. + */ + without_scope_info?: boolean; + + /** + * Configure Prometheus Exporter to add resource attributes as metrics attributes. + */ + with_resource_constant_labels: IncludeExclude; +} + +export interface MetricReader { + /** + * Configure a periodic metric reader. + */ + periodic?: PeriodicMetricReader; + + /** + * Configure a pull based metric reader. + */ + pull?: PullMetricReader; +} + +export interface OtlpHttpMetricExporter { + /** + * Configure endpoint, including the metric specific path. + * If omitted or null, http://localhost:4318/v1/metrics is used. + */ + endpoint?: string; + + /** + * Configure certificate used to verify a server's TLS credentials. + * Absolute path to certificate file in PEM format. + * If omitted or null, system default certificate verification is used for secure connections. + */ + certificate_file?: string; + + /** + * Configure mTLS private client key. + * Absolute path to client key file in PEM format. If set, .client_certificate must also be set. + * If omitted or null, mTLS is not used. + */ + client_key_file?: string; + + /** + * Configure mTLS client certificate. + * Absolute path to client certificate file in PEM format. If set, .client_key must also be set. + * If omitted or null, mTLS is not used. + */ + client_certificate_file?: string; + + /** + * Configure headers. Entries have higher priority than entries from .headers_list. + * If an entry's .value is null, the entry is ignored. + */ + headers?: NameStringValuePair[]; + + /** + * Configure headers. Entries have lower priority than entries from .headers. + * The value is a list of comma separated key-value pairs matching the format of OTEL_EXPORTER_OTLP_HEADERS. + * If omitted or null, no headers are added. + */ + headers_list?: string; + + /** + * Configure compression. + * Values include: gzip, none. Implementations may support other compression algorithms. + * If omitted or null, none is used. + */ + compression?: string; + + /** + * Configure max time (in milliseconds) to wait for each export. + * Value must be non-negative. A value of 0 indicates no limit (infinity). + * If omitted or null, 10000 is used. + */ + timeout?: number; + + /** + * Configure the encoding used for messages. + * Values include: protobuf, json. Implementations may not support json. + * If omitted or null, protobuf is used. + */ + encoding?: OtlpHttpEncoding; + + /** + * Configure temporality preference. + * Values include: cumulative, delta, low_memory. + * If omitted or null, cumulative is used. + */ + temporality_preference?: ExporterTemporalityPreference; + + /** + * Configure default histogram aggregation. + * Values include: explicit_bucket_histogram, base2_exponential_bucket_histogram. + * If omitted or null, explicit_bucket_histogram is used. + */ + default_histogram_aggregation?: ExporterDefaultHistogramAggregation; +} + +export interface OtlpGrpcMetricExporter { + /** + * Configure endpoint. + * If omitted or null, http://localhost:4317 is used. + */ + endpoint?: string; + + /** + * Configure certificate used to verify a server's TLS credentials. + * Absolute path to certificate file in PEM format. + * If omitted or null, system default certificate verification is used for secure connections. + */ + certificate_file?: string; + + /** + * Configure mTLS private client key. + * Absolute path to client key file in PEM format. If set, .client_certificate must also be set. + * If omitted or null, mTLS is not used. + */ + client_key_file?: string; + + /** + * Configure mTLS client certificate. + * Absolute path to client certificate file in PEM format. If set, .client_key must also be set. + * If omitted or null, mTLS is not used. + */ + client_certificate_file?: string; + + /** + * Configure headers. Entries have higher priority than entries from .headers_list. + * If an entry's .value is null, the entry is ignored. + */ + headers?: NameStringValuePair[]; + + /** + * Configure headers. Entries have lower priority than entries from .headers. + * The value is a list of comma separated key-value pairs matching the format of OTEL_EXPORTER_OTLP_HEADERS. + * If omitted or null, no headers are added. + */ + headers_list?: string; + + /** + * Configure compression. + * Values include: gzip, none. Implementations may support other compression algorithms. + * If omitted or null, none is used. + */ + compression?: string; + + /** + * Configure max time (in milliseconds) to wait for each export. + * Value must be non-negative. A value of 0 indicates no limit (infinity). + * If omitted or null, 10000 is used. + */ + timeout?: number; + + /** + * Configure client transport security for the exporter's connection. + * Only applicable when .endpoint is provided without http or https scheme. Implementations may choose to ignore .insecure. + * If omitted or null, false is used. + */ + insecure?: boolean; + + /** + * Configure temporality preference. + * Values include: cumulative, delta, low_memory. + * If omitted or null, cumulative is used. + */ + temporality_preference?: ExporterTemporalityPreference; + + /** + * Configure default histogram aggregation. + * Values include: explicit_bucket_histogram, base2_exponential_bucket_histogram. + * If omitted or null, explicit_bucket_histogram is used. + */ + default_histogram_aggregation?: ExporterDefaultHistogramAggregation; +} + +export interface OtlpFileMetricExporter { + /** + * Configure output stream. + * Values include stdout, or scheme+destination. For example: file:///path/to/file.jsonl. + * If omitted or null, stdout is used. + */ + output_stream?: string; + + /** + * Configure temporality preference. + * Values include: cumulative, delta, low_memory. + * If omitted or null, cumulative is used. + */ + temporality_preference?: ExporterTemporalityPreference; + + /** + * Configure default histogram aggregation. + * Values include: explicit_bucket_histogram, base2_exponential_bucket_histogram. + * If omitted or null, explicit_bucket_histogram is used. + */ + default_histogram_aggregation?: ExporterDefaultHistogramAggregation; +} + +export enum ExporterTemporalityPreference { + Cumulative = 'cumulative', + Delta = 'delta', + LowMemory = 'low_memory', +} + +export enum ExporterDefaultHistogramAggregation { + Base2ExponentialBucketHistogram = 'base2_exponential_bucket_histogram', + ExplicitBucketHistogram = 'explicit_bucket_histogram', +} + +export interface View { + /** + * Configure view selector. + */ + selector?: ViewSelector; + + /** + * Configure view stream. + */ + stream?: ViewStream; +} + +export interface ViewSelector { + /** + * Configure instrument name selection criteria. + * If omitted or null, all instrument names match. + */ + instrument_name?: string; + + /** + * Configure instrument type selection criteria. + * Values include: counter, gauge, histogram, observable_counter, observable_gauge, + * observable_up_down_counter, up_down_counter. + * If omitted or null, all instrument types match. + */ + instrument_type?: InstrumentType; + + /** + * Configure the instrument unit selection criteria. + * If omitted or null, all instrument units match. + */ + unit?: string; + + /** + * Configure meter name selection criteria. + * If omitted or null, all meter names match. + */ + meter_name?: string; + + /** + * Configure meter version selection criteria. + * If omitted or null, all meter versions match. + */ + meter_version?: string; + + /** + * Configure meter schema url selection criteria. + * If omitted or null, all meter schema URLs match. + */ + meter_schema_url?: string; +} + +export enum InstrumentType { + Counter = 'counter', + Gauge = 'gauge', + Histogram = 'histogram', + ObservableCounter = 'observable_counter', + ObservableGauge = 'observable_gauge', + ObservableUpDownCounter = 'observable_up_down_counter', + UpDownCounter = 'up_down_counter', +} + +export interface ViewStream { + /** + * Configure metric name of the resulting stream(s). + * If omitted or null, the instrument's original name is used. + */ + name?: string; + + /** + * Configure metric description of the resulting stream(s). + * If omitted or null, the instrument's origin description is used. + */ + description?: string; + + /** + * Configure aggregation of the resulting stream(s). + * Values include: default, drop, explicit_bucket_histogram, base2_exponential_bucket_histogram, last_value, sum. + * If omitted, default is used. + */ + aggregation?: Aggregation; + + /** + * Configure the aggregation cardinality limit. + * If omitted or null, the metric reader's default cardinality limit is used. + */ + aggregation_cardinality_limit?: number; + + /** + * Configure attribute keys retained in the resulting stream(s). + */ + attribute_keys?: IncludeExclude; +} + +export interface Aggregation { + /** + * Configure aggregation to be default. + */ + default?: object; + + /** + * Configure aggregation to be drop. + */ + drop?: object; + + /** + * Configure aggregation to be explicit_bucket_histogram. + */ + explicit_bucket_histogram?: ExplicitBucketHistogramAggregation; + + /** + * Configure aggregation to be base2_exponential_bucket_histogram. + */ + base2_exponential_bucket_histogram?: Base2ExponentialBucketHistogramAggregation; + + /** + * Configure aggregation to be last_value. + */ + last_value?: object; + + /** + * Configure aggregation to be sum. + */ + sum?: object; +} + +export interface ExplicitBucketHistogramAggregation { + /** + * Configure bucket boundaries. + * If omitted, [0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000] is used. + */ + boundaries?: number[]; + + /** + * Configure record min and max. + * If omitted or null, true is used. + */ + record_min_max?: boolean; +} + +export interface Base2ExponentialBucketHistogramAggregation { + /** + * Configure max_scale. + */ + max_scale?: number; + + /** + * Configure max_size. + */ + max_size?: number; + + /** + * Configure record min and max. + * If omitted or null, true is used. + */ + record_min_max?: boolean; +} diff --git a/experimental/packages/opentelemetry-configuration/src/models/tracerProviderModel.ts b/experimental/packages/opentelemetry-configuration/src/models/tracerProviderModel.ts index 0b43f3fd371..5e337a66a1a 100644 --- a/experimental/packages/opentelemetry-configuration/src/models/tracerProviderModel.ts +++ b/experimental/packages/opentelemetry-configuration/src/models/tracerProviderModel.ts @@ -35,7 +35,7 @@ export function initializeDefaultTracerProviderConfiguration(): TracerProvider { otlp_http: { endpoint: 'http://localhost:4318/v1/traces', timeout: 10000, - encoding: OtlpHttpEncoding.protobuf, + encoding: OtlpHttpEncoding.Protobuf, }, }, }, diff --git a/experimental/packages/opentelemetry-configuration/src/utils.ts b/experimental/packages/opentelemetry-configuration/src/utils.ts index 5c3855d9546..211092d51ac 100644 --- a/experimental/packages/opentelemetry-configuration/src/utils.ts +++ b/experimental/packages/opentelemetry-configuration/src/utils.ts @@ -111,7 +111,7 @@ export function getNumberListFromConfigFile( const filteredList = []; for (let i = 0; i < list.length; i++) { const element = getNumberFromConfigFile(list[i]); - if (element) { + if (element || element === 0) { filteredList.push(element); } } diff --git a/experimental/packages/opentelemetry-configuration/test/ConfigProvider.test.ts b/experimental/packages/opentelemetry-configuration/test/ConfigProvider.test.ts index a6243772bfb..a85955b0ac2 100644 --- a/experimental/packages/opentelemetry-configuration/test/ConfigProvider.test.ts +++ b/experimental/packages/opentelemetry-configuration/test/ConfigProvider.test.ts @@ -19,9 +19,16 @@ import { Configuration } from '../src'; import { DiagLogLevel } from '@opentelemetry/api'; import { createConfigProvider } from '../src/ConfigProvider'; import { OtlpHttpEncoding } from '../src/models/commonModel'; +import { + ExemplarFilter, + ExporterDefaultHistogramAggregation, + ExporterTemporalityPreference, + InstrumentType, +} from '../src/models/meterProviderModel'; import { setAttributeLimits, setLoggerProvider, + setMeterProvider, setPropagators, setResources, setTracerProvider, @@ -34,6 +41,8 @@ import { setPropagator, setTracerProvider as setFileTracerProvider, setLoggerProvider as setFileLoggerProvider, + setMeterProvider as setFileMeterProvider, + getTemporalityPreference, } from '../src/FileConfigProvider'; const defaultConfig: Configuration = { @@ -60,7 +69,7 @@ const defaultConfig: Configuration = { otlp_http: { endpoint: 'http://localhost:4318/v1/traces', timeout: 10000, - encoding: OtlpHttpEncoding.protobuf, + encoding: OtlpHttpEncoding.Protobuf, }, }, }, @@ -93,14 +102,15 @@ const defaultConfig: Configuration = { otlp_http: { endpoint: 'http://localhost:4318/v1/metrics', timeout: 10000, - temporality_preference: 'cumulative', - default_histogram_aggregation: 'explicit_bucket_histogram', + temporality_preference: ExporterTemporalityPreference.Cumulative, + default_histogram_aggregation: + ExporterDefaultHistogramAggregation.ExplicitBucketHistogram, }, }, }, }, ], - exemplar_filter: 'trace_based', + exemplar_filter: ExemplarFilter.TraceBased, }, logger_provider: { processors: [ @@ -114,7 +124,7 @@ const defaultConfig: Configuration = { otlp_http: { endpoint: 'http://localhost:4318/v1/logs', timeout: 10000, - encoding: OtlpHttpEncoding.protobuf, + encoding: OtlpHttpEncoding.Protobuf, }, }, }, @@ -215,7 +225,7 @@ const configFromFile: Configuration = { headers: [{ name: 'api-key', value: '1234' }], headers_list: 'api-key=1234', compression: 'gzip', - encoding: OtlpHttpEncoding.protobuf, + encoding: OtlpHttpEncoding.Protobuf, }, }, }, @@ -284,7 +294,7 @@ const configFromFile: Configuration = { { simple: { exporter: { - console: {}, + console: undefined, }, }, }, @@ -310,6 +320,36 @@ const configFromFile: Configuration = { }, meter_provider: { readers: [ + { + pull: { + exporter: { + 'prometheus/development': { + host: 'localhost', + port: 9464, + without_scope_info: false, + with_resource_constant_labels: { + included: ['service*'], + excluded: ['service.attr1'], + }, + }, + }, + producers: [ + { + opencensus: undefined, + }, + ], + cardinality_limits: { + default: 2000, + counter: 2000, + gauge: 2000, + histogram: 2000, + observable_counter: 2000, + observable_gauge: 2000, + observable_up_down_counter: 2000, + up_down_counter: 2000, + }, + }, + }, { periodic: { interval: 60000, @@ -317,15 +357,177 @@ const configFromFile: Configuration = { exporter: { otlp_http: { endpoint: 'http://localhost:4318/v1/metrics', + certificate_file: '/app/cert.pem', + client_key_file: '/app/cert.pem', + client_certificate_file: '/app/cert.pem', + headers: [ + { + name: 'api-key', + value: '1234', + }, + ], + headers_list: 'api-key=1234', + compression: 'gzip', timeout: 10000, - temporality_preference: 'cumulative', - default_histogram_aggregation: 'explicit_bucket_histogram', + encoding: OtlpHttpEncoding.Protobuf, + temporality_preference: ExporterTemporalityPreference.Delta, + default_histogram_aggregation: + ExporterDefaultHistogramAggregation.Base2ExponentialBucketHistogram, + }, + }, + producers: [ + { + prometheus: undefined, }, + ], + cardinality_limits: { + default: 2000, + counter: 2000, + gauge: 2000, + histogram: 2000, + observable_counter: 2000, + observable_gauge: 2000, + observable_up_down_counter: 2000, + up_down_counter: 2000, + }, + }, + }, + { + periodic: { + interval: 60000, + timeout: 30000, + exporter: { + otlp_grpc: { + endpoint: 'http://localhost:4317', + certificate_file: '/app/cert.pem', + client_key_file: '/app/cert.pem', + client_certificate_file: '/app/cert.pem', + headers: [ + { + name: 'api-key', + value: '1234', + }, + ], + headers_list: 'api-key=1234', + compression: 'gzip', + timeout: 10000, + insecure: false, + temporality_preference: ExporterTemporalityPreference.Delta, + default_histogram_aggregation: + ExporterDefaultHistogramAggregation.Base2ExponentialBucketHistogram, + }, + }, + cardinality_limits: { + default: 2000, + counter: 2000, + gauge: 2000, + histogram: 2000, + observable_counter: 2000, + observable_gauge: 2000, + observable_up_down_counter: 2000, + up_down_counter: 2000, + }, + }, + }, + { + periodic: { + timeout: 30000, + interval: 60000, + exporter: { + 'otlp_file/development': { + output_stream: 'file:///var/log/metrics.jsonl', + temporality_preference: ExporterTemporalityPreference.Delta, + default_histogram_aggregation: + ExporterDefaultHistogramAggregation.Base2ExponentialBucketHistogram, + }, + }, + cardinality_limits: { + default: 2000, + counter: 2000, + gauge: 2000, + histogram: 2000, + observable_counter: 2000, + observable_gauge: 2000, + observable_up_down_counter: 2000, + up_down_counter: 2000, + }, + }, + }, + { + periodic: { + timeout: 30000, + interval: 60000, + exporter: { + 'otlp_file/development': { + output_stream: 'stdout', + temporality_preference: ExporterTemporalityPreference.Delta, + default_histogram_aggregation: + ExporterDefaultHistogramAggregation.Base2ExponentialBucketHistogram, + }, + }, + cardinality_limits: { + default: 2000, + counter: 2000, + gauge: 2000, + histogram: 2000, + observable_counter: 2000, + observable_gauge: 2000, + observable_up_down_counter: 2000, + up_down_counter: 2000, + }, + }, + }, + { + periodic: { + timeout: 30000, + interval: 60000, + exporter: { + console: undefined, + }, + cardinality_limits: { + default: 2000, + counter: 2000, + gauge: 2000, + histogram: 2000, + observable_counter: 2000, + observable_gauge: 2000, + observable_up_down_counter: 2000, + up_down_counter: 2000, + }, + }, + }, + ], + exemplar_filter: ExemplarFilter.TraceBased, + views: [ + { + selector: { + instrument_name: 'my-instrument', + instrument_type: InstrumentType.Histogram, + unit: 'ms', + meter_name: 'my-meter', + meter_version: '1.0.0', + meter_schema_url: 'https://opentelemetry.io/schemas/1.16.0', + }, + stream: { + name: 'new_instrument_name', + description: 'new_description', + aggregation: { + explicit_bucket_histogram: { + boundaries: [ + 0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, + 7500, 10000, + ], + record_min_max: true, + }, + }, + aggregation_cardinality_limit: 2000, + attribute_keys: { + included: ['key1', 'key2'], + excluded: ['key3'], }, }, }, ], - exemplar_filter: 'trace_based', }, logger_provider: { processors: [ @@ -339,7 +541,7 @@ const configFromFile: Configuration = { otlp_http: { endpoint: 'http://localhost:4318/v1/logs', timeout: 10000, - encoding: OtlpHttpEncoding.protobuf, + encoding: OtlpHttpEncoding.Protobuf, certificate_file: '/app/cert.pem', client_key_file: '/app/cert.pem', client_certificate_file: '/app/cert.pem', @@ -400,7 +602,7 @@ const configFromFile: Configuration = { { simple: { exporter: { - console: {}, + console: undefined, }, }, }, @@ -457,7 +659,7 @@ const defaultConfigFromFileWithEnvVariables: Configuration = { otlp_http: { endpoint: 'http://localhost:4318/v1/traces', timeout: 10000, - encoding: OtlpHttpEncoding.protobuf, + encoding: OtlpHttpEncoding.Protobuf, compression: 'gzip', }, }, @@ -490,15 +692,28 @@ const defaultConfigFromFileWithEnvVariables: Configuration = { exporter: { otlp_http: { endpoint: 'http://localhost:4318/v1/metrics', + encoding: OtlpHttpEncoding.Protobuf, + compression: 'gzip', timeout: 10000, - temporality_preference: 'cumulative', - default_histogram_aggregation: 'explicit_bucket_histogram', + temporality_preference: ExporterTemporalityPreference.Cumulative, + default_histogram_aggregation: + ExporterDefaultHistogramAggregation.ExplicitBucketHistogram, }, }, + cardinality_limits: { + default: 2000, + counter: 2000, + gauge: 2000, + histogram: 2000, + observable_counter: 2000, + observable_gauge: 2000, + observable_up_down_counter: 2000, + up_down_counter: 2000, + }, }, }, ], - exemplar_filter: 'trace_based', + exemplar_filter: ExemplarFilter.TraceBased, }, logger_provider: { processors: [ @@ -512,7 +727,7 @@ const defaultConfigFromFileWithEnvVariables: Configuration = { otlp_http: { endpoint: 'http://localhost:4318/v1/logs', timeout: 10000, - encoding: OtlpHttpEncoding.protobuf, + encoding: OtlpHttpEncoding.Protobuf, compression: 'gzip', }, }, @@ -706,7 +921,7 @@ describe('ConfigProvider', function () { compression: 'gzip', timeout: 2000, headers_list: 'host=localhost', - encoding: OtlpHttpEncoding.protobuf, + encoding: OtlpHttpEncoding.Protobuf, }, }, }, @@ -765,15 +980,15 @@ describe('ConfigProvider', function () { compression: 'gzip', timeout: 300, headers_list: 'host=localhost', - temporality_preference: 'delta', + temporality_preference: ExporterTemporalityPreference.Delta, default_histogram_aggregation: - 'base2_exponential_bucket_histogram', + ExporterDefaultHistogramAggregation.Base2ExponentialBucketHistogram, }, }, }, }, ], - exemplar_filter: 'always_on', + exemplar_filter: ExemplarFilter.AlwaysOn, }, }; const configProvider = createConfigProvider(); @@ -822,7 +1037,7 @@ describe('ConfigProvider', function () { compression: 'gzip', timeout: 700, headers_list: 'host=localhost', - encoding: OtlpHttpEncoding.protobuf, + encoding: OtlpHttpEncoding.Protobuf, }, }, }, @@ -884,6 +1099,142 @@ describe('ConfigProvider', function () { processors: [], }, }); + + config = { + meter_provider: { + readers: [{ periodic: { exporter: { otlp_http: undefined } } }], + }, + }; + process.env.OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE = + 'cumulative'; + setMeterProvider(config); + assert.deepStrictEqual(config, { + meter_provider: { + readers: [ + { + periodic: { + exporter: { + otlp_http: { temporality_preference: 'cumulative' }, + }, + }, + }, + ], + }, + }); + + process.env.OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE = + 'low_memory'; + setMeterProvider(config); + assert.deepStrictEqual(config, { + meter_provider: { + readers: [ + { + periodic: { + exporter: { + otlp_http: { temporality_preference: 'low_memory' }, + }, + }, + }, + ], + }, + }); + + process.env.OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE = 'default'; + setMeterProvider(config); + assert.deepStrictEqual(config, { + meter_provider: { + readers: [ + { + periodic: { + exporter: { + otlp_http: { temporality_preference: 'cumulative' }, + }, + }, + }, + ], + }, + }); + delete process.env.OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE; + + config = { + meter_provider: { + readers: [{ periodic: { exporter: { otlp_http: undefined } } }], + }, + }; + process.env.OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION = + 'explicit_bucket_histogram'; + setMeterProvider(config); + assert.deepStrictEqual(config, { + meter_provider: { + readers: [ + { + periodic: { + exporter: { + otlp_http: { + default_histogram_aggregation: 'explicit_bucket_histogram', + }, + }, + }, + }, + ], + }, + }); + + process.env.OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION = + 'default'; + setMeterProvider(config); + assert.deepStrictEqual(config, { + meter_provider: { + readers: [ + { + periodic: { + exporter: { + otlp_http: { + default_histogram_aggregation: 'explicit_bucket_histogram', + }, + }, + }, + }, + ], + }, + }); + + config = {}; + setMeterProvider(config); + assert.deepStrictEqual(config, { + meter_provider: { + readers: [{}], + }, + }); + + process.env.OTEL_METRICS_EXEMPLAR_FILTER = 'trace_based'; + setMeterProvider(config); + assert.deepStrictEqual(config, { + meter_provider: { + readers: [{}], + exemplar_filter: 'trace_based', + }, + }); + + config = {}; + process.env.OTEL_METRICS_EXEMPLAR_FILTER = 'always_off'; + setMeterProvider(config); + assert.deepStrictEqual(config, { + meter_provider: { + readers: [{}], + exemplar_filter: 'always_off', + }, + }); + + config = {}; + process.env.OTEL_METRICS_EXEMPLAR_FILTER = 'default'; + setMeterProvider(config); + assert.deepStrictEqual(config, { + meter_provider: { + readers: [{}], + exemplar_filter: 'trace_based', + }, + }); }); }); @@ -971,7 +1322,8 @@ describe('ConfigProvider', function () { process.env.OTEL_LINK_ATTRIBUTE_COUNT_LIMIT = '19'; process.env.OTEL_METRIC_EXPORT_INTERVAL = '20'; process.env.OTEL_METRIC_EXPORT_TIMEOUT = '21'; - process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = 'metric-endpoint'; + process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = + 'http://test:4318/v1/metrics'; process.env.OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE = 'metric-certificate'; process.env.OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY = 'metric-client-key'; process.env.OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE = @@ -1043,7 +1395,7 @@ describe('ConfigProvider', function () { client_certificate_file: 'trace-client-certificate', client_key_file: 'trace-client-key', compression: 'trace-compression', - encoding: OtlpHttpEncoding.protobuf, + encoding: OtlpHttpEncoding.Protobuf, endpoint: 'http://test.com:4318/v1/traces', headers_list: 'trace-headers', timeout: 1213, @@ -1054,8 +1406,41 @@ describe('ConfigProvider', function () { ], }, meter_provider: { - ...defaultConfigFromFileWithEnvVariables.meter_provider, - exemplar_filter: 'always_off', + exemplar_filter: ExemplarFilter.AlwaysOff, + readers: [ + { + periodic: { + interval: 20, + timeout: 21, + exporter: { + otlp_http: { + endpoint: 'http://test:4318/v1/metrics', + timeout: 22, + temporality_preference: + ExporterTemporalityPreference.Cumulative, + default_histogram_aggregation: + ExporterDefaultHistogramAggregation.ExplicitBucketHistogram, + certificate_file: 'metric-certificate', + client_certificate_file: 'metric-client-certificate', + client_key_file: 'metric-client-key', + compression: 'metric-compression', + encoding: OtlpHttpEncoding.Protobuf, + headers_list: 'metric-header', + }, + }, + cardinality_limits: { + default: 2000, + counter: 2000, + gauge: 2000, + histogram: 2000, + observable_counter: 2000, + observable_gauge: 2000, + observable_up_down_counter: 2000, + up_down_counter: 2000, + }, + }, + }, + ], }, logger_provider: { ...defaultConfigFromFileWithEnvVariables.logger_provider, @@ -1076,7 +1461,7 @@ describe('ConfigProvider', function () { client_certificate_file: 'logs-client-certificate', client_key_file: 'logs-client-key', compression: 'logs-compression', - encoding: OtlpHttpEncoding.protobuf, + encoding: OtlpHttpEncoding.Protobuf, endpoint: 'http://test.com:4318/v1/logs', headers_list: 'logs-header', timeout: 27, @@ -1140,6 +1525,112 @@ describe('ConfigProvider', function () { assert.deepStrictEqual(config, { logger_provider: { processors: [] }, }); + + const res = getTemporalityPreference( + ExporterTemporalityPreference.LowMemory + ); + assert.deepStrictEqual(res, 'low_memory'); + + config = {}; + setFileMeterProvider(config, { readers: [] }); + assert.deepStrictEqual(config, { + meter_provider: { readers: [] }, + }); + + config = {}; + setFileMeterProvider(config, { + readers: [], + exemplar_filter: ExemplarFilter.AlwaysOn, + }); + assert.deepStrictEqual(config, { + meter_provider: { readers: [], exemplar_filter: 'always_on' }, + }); + + config = {}; + setFileMeterProvider(config, { + readers: [], + views: [{ selector: { instrument_type: InstrumentType.Counter } }], + }); + assert.deepStrictEqual(config, { + meter_provider: { + readers: [], + views: [{ selector: { instrument_type: 'counter' } }], + }, + }); + + config = {}; + setFileMeterProvider(config, { + readers: [], + views: [{ selector: { instrument_type: InstrumentType.Gauge } }], + }); + assert.deepStrictEqual(config, { + meter_provider: { + readers: [], + views: [{ selector: { instrument_type: 'gauge' } }], + }, + }); + + config = {}; + setFileMeterProvider(config, { + readers: [], + views: [ + { selector: { instrument_type: InstrumentType.ObservableCounter } }, + ], + }); + assert.deepStrictEqual(config, { + meter_provider: { + readers: [], + views: [{ selector: { instrument_type: 'observable_counter' } }], + }, + }); + + config = {}; + setFileMeterProvider(config, { + readers: [], + views: [ + { selector: { instrument_type: InstrumentType.ObservableGauge } }, + ], + }); + assert.deepStrictEqual(config, { + meter_provider: { + readers: [], + views: [{ selector: { instrument_type: 'observable_gauge' } }], + }, + }); + + config = {}; + setFileMeterProvider(config, { + readers: [], + views: [ + { + selector: { + instrument_type: InstrumentType.ObservableUpDownCounter, + }, + }, + ], + }); + assert.deepStrictEqual(config, { + meter_provider: { + readers: [], + views: [ + { selector: { instrument_type: 'observable_up_down_counter' } }, + ], + }, + }); + + config = {}; + setFileMeterProvider(config, { + readers: [], + views: [ + { selector: { instrument_type: InstrumentType.UpDownCounter } }, + ], + }); + assert.deepStrictEqual(config, { + meter_provider: { + readers: [], + views: [{ selector: { instrument_type: 'up_down_counter' } }], + }, + }); }); }); }); diff --git a/experimental/packages/opentelemetry-configuration/test/utils.test.ts b/experimental/packages/opentelemetry-configuration/test/utils.test.ts index c6012aa7185..46be589de33 100644 --- a/experimental/packages/opentelemetry-configuration/test/utils.test.ts +++ b/experimental/packages/opentelemetry-configuration/test/utils.test.ts @@ -95,6 +95,7 @@ describe('config utils', function () { assert.deepStrictEqual(getNumberListFromConfigFile(null), undefined); assert.deepStrictEqual(getNumberListFromConfigFile(' '), undefined); assert.deepStrictEqual(getNumberListFromConfigFile(' , '), []); + assert.deepStrictEqual(getNumberListFromConfigFile('0'), [0]); assert.deepStrictEqual(getNumberListFromConfigFile(5), [5]); assert.deepStrictEqual(getNumberListFromConfigFile('7'), [7]); assert.deepStrictEqual(