diff --git a/handwritten/spanner/OBSERVABILITY.md b/handwritten/spanner/OBSERVABILITY.md index ce82cab99bb5..f0f139bd04f4 100644 --- a/handwritten/spanner/OBSERVABILITY.md +++ b/handwritten/spanner/OBSERVABILITY.md @@ -20,17 +20,24 @@ enable OpenTelemetry with appropriate exporters at the startup of your applicati #### OpenTelemetry Dependencies Add the following dependencies in your `package.json` or install them directly. + +> **Note:** `@google-cloud/spanner` uses the OpenTelemetry **v2** SDK. The versions +> below are the v2-compatible releases. If you are upgrading from an older version +> of this client, see the [OpenTelemetry JS 2.x migration guide](https://github.com/open-telemetry/opentelemetry-js/blob/main/doc/upgrade-to-2.x.md). + ```javascript // Required packages for OpenTelemetry SDKs -"@opentelemetry/sdk-trace-base": "^1.26.0", -"@opentelemetry/sdk-trace-node": "^1.26.0", +"@opentelemetry/sdk-trace-base": "^2.11.0", +"@opentelemetry/sdk-trace-node": "^2.0.0", +"@opentelemetry/resources": "^2.11.0", +"@opentelemetry/semantic-conventions": "^1.30.0", // Package to use Google Cloud Trace exporter -"@google-cloud/opentelemetry-cloud-trace-exporter": "^2.4.1", +"@google-cloud/opentelemetry-cloud-trace-exporter": "^3.0.0", // Packages to enable gRPC instrumentation -"@opentelemetry/instrumentation": "^0.53.0", -"@opentelemetry/instrumentation-grpc": "^0.53.0", +"@opentelemetry/instrumentation": "^0.222.0", +"@opentelemetry/instrumentation-grpc": "^0.222.0", ``` #### OpenTelemetry Configuration @@ -40,17 +47,25 @@ const { NodeTracerProvider, TraceIdRatioBasedSampler, } = require('@opentelemetry/sdk-trace-node'); -const { - BatchSpanProcessor, -} = require('@opentelemetry/sdk-trace-base'); +const {BatchSpanProcessor} = require('@opentelemetry/sdk-trace-base'); const { TraceExporter, } = require('@google-cloud/opentelemetry-cloud-trace-exporter'); +const {resourceFromAttributes} = require('@opentelemetry/resources'); +const {ATTR_SERVICE_NAME} = require('@opentelemetry/semantic-conventions'); const exporter = new TraceExporter(); +// Describe the service that is emitting the traces. +// Note: in OpenTelemetry v2 the `Resource` class was replaced by the +// `resourceFromAttributes` factory function. +const resource = resourceFromAttributes({ + [ATTR_SERVICE_NAME]: 'my-service-name', +}); + // Create the tracerProvider that the exporter shall be attached to. const provider = new NodeTracerProvider({ resource: resource, + sampler: new TraceIdRatioBasedSampler(0.1), // sample 10% spanProcessors: [new BatchSpanProcessor(exporter)] }); diff --git a/handwritten/spanner/google-cloud-spanner-executor/src/cloud-util.ts b/handwritten/spanner/google-cloud-spanner-executor/src/cloud-util.ts index d6e3e1d042ca..4feb267f4ca5 100644 --- a/handwritten/spanner/google-cloud-spanner-executor/src/cloud-util.ts +++ b/handwritten/spanner/google-cloud-spanner-executor/src/cloud-util.ts @@ -21,7 +21,7 @@ import {SpannerOptions} from '../../src'; import {trace} from '@opentelemetry/api'; import {NodeTracerProvider} from '@opentelemetry/sdk-trace-node'; import {OTLPTraceExporter} from '@opentelemetry/exporter-trace-otlp-grpc'; -import {Resource} from '@opentelemetry/resources'; +import {resourceFromAttributes} from '@opentelemetry/resources'; import {ATTR_SERVICE_NAME} from '@opentelemetry/semantic-conventions'; import { BatchSpanProcessor, @@ -50,7 +50,7 @@ export class CloudUtil { }); const provider = new NodeTracerProvider({ - resource: new Resource({ + resource: resourceFromAttributes({ [ATTR_SERVICE_NAME]: 'spanner-node-worker-proxy', 'gcp.project_id': WorkerProxy.PROJECT_ID, }) as any, diff --git a/handwritten/spanner/package.json b/handwritten/spanner/package.json index 0bd522a07d7a..bd710b3351fd 100644 --- a/handwritten/spanner/package.json +++ b/handwritten/spanner/package.json @@ -56,7 +56,6 @@ "@babel/traverse": "7.27.7", "@google-cloud/common": "^6.0.0", "@google-cloud/monitoring": "^5.0.0", - "@google-cloud/opentelemetry-resource-util": "^2.4.0", "@google-cloud/precise-date": "^5.0.0", "@google-cloud/promisify": "^5.0.0", "@google-cloud/spanner-api": "^0.2.0", @@ -65,8 +64,9 @@ "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^2.0.0", "@opentelemetry/core": "^2.0.0", - "@opentelemetry/resources": "^1.8.0", - "@opentelemetry/sdk-metrics": "^1.30.1", + "@opentelemetry/resource-detector-gcp": "^0.57.0", + "@opentelemetry/resources": "^2.11.0", + "@opentelemetry/sdk-metrics": "^2.11.0", "@opentelemetry/semantic-conventions": "^1.30.0", "@types/big.js": "^6.2.2", "@types/stack-trace": "^0.0.33", @@ -87,9 +87,9 @@ }, "devDependencies": { "@grpc/reflection": "^1.0.4", - "@opentelemetry/sdk-trace-base": "^2.0.0", + "@opentelemetry/sdk-trace-base": "^2.11.0", "@opentelemetry/sdk-trace-node": "^2.0.0", - "@opentelemetry/exporter-trace-otlp-grpc": "^0.57.0", + "@opentelemetry/exporter-trace-otlp-grpc": "^0.222.0", "@types/concat-stream": "^2.0.3", "@types/extend": "^3.0.4", "@types/is": "^0.0.25", diff --git a/handwritten/spanner/src/index.ts b/handwritten/spanner/src/index.ts index 17287fba98bf..8d56857d51e0 100644 --- a/handwritten/spanner/src/index.ts +++ b/handwritten/spanner/src/index.ts @@ -41,7 +41,7 @@ import { IProtoMessageParams, IProtoEnumParams, } from './codec'; -import {context, propagation} from '@opentelemetry/api'; +import {context, propagation, ROOT_CONTEXT} from '@opentelemetry/api'; import {Backup} from './backup'; import {Database} from './database'; import { @@ -534,7 +534,7 @@ class Spanner extends GrpcService { if (!this.clients_.has(clientName)) { this.clients_.set( clientName, - new v1[clientName](this.options as ClientOptions), + new v1.InstanceAdminClient(this.options as ClientOptions), ); } return this.clients_.get(clientName)! as v1.InstanceAdminClient; @@ -558,7 +558,7 @@ class Spanner extends GrpcService { if (!this.clients_.has(clientName)) { this.clients_.set( clientName, - new v1[clientName](this.options as ClientOptions), + new v1.DatabaseAdminClient(this.options as ClientOptions), ); } return this.clients_.get(clientName)! as v1.DatabaseAdminClient; @@ -615,10 +615,9 @@ class Spanner extends GrpcService { if (callback) { // process.nextTick prevents Unhandled Promise Rejections if callback throws - res.then( - () => process.nextTick(() => callback(null)), - err => process.nextTick(() => callback(err)), - ); + res + .then(() => process.nextTick(() => callback(null))) + .catch(err => process.nextTick(() => callback(err))); } else { return res; } @@ -1676,35 +1675,46 @@ class Spanner extends GrpcService { !metricsExplicitlyDisabled && !this._isInSecureCredentials; MetricsTracerFactory.enabled = this._metricsEnabled; if (this._metricsEnabled) { - try { - this.auth.getProjectId((err, projectId) => { - if (err || !projectId) { - console.error( - 'Unable to get Project Id for client side metrics, will skip exporting client' + - ' side metrics' + - err, - ); - return; - } + const initializeMetrics = (projectId: string) => { + this.projectId_ = projectId; + const factory = MetricsTracerFactory.getInstance(projectId); + if (factory && !factory.hasMetricReaders()) { + context.with(ROOT_CONTEXT, () => { + const periodicReader = new PeriodicExportingMetricReader({ + exporter: new CloudMonitoringMetricsExporter( + {auth: this.auth}, + projectId, + ), + exportIntervalMillis: 60000, + }); + factory.getMeterProvider([periodicReader]); + }); + } + }; - this.projectId_ = projectId; - const factory = MetricsTracerFactory.getInstance(projectId); - const periodicReader = new PeriodicExportingMetricReader({ - exporter: new CloudMonitoringMetricsExporter( - {auth: this.auth}, - projectId, - ), - exportIntervalMillis: 60000, + if (this.projectId_ && this.projectId_ !== '{{projectId}}') { + initializeMetrics(this.projectId_); + } else { + try { + this.auth.getProjectId((err, projectId) => { + if (err || !projectId) { + console.error( + 'Unable to get Project Id for client side metrics, will skip exporting client' + + ' side metrics' + + err, + ); + return; + } + + initializeMetrics(projectId); }); - // Retrieve the MeterProvider to trigger construction - factory!.getMeterProvider([periodicReader]); - }); - } catch (err) { - console.error( - 'Unable to configure client side metrics, will skip exporting client' + - ' side metrics' + - err, - ); + } catch (err) { + console.error( + 'Unable to configure client side metrics, will skip exporting client' + + ' side metrics' + + err, + ); + } } } } @@ -1727,7 +1737,10 @@ class Spanner extends GrpcService { const clientName = config.client; try { if (!this.clients_.has(clientName)) { - this.clients_.set(clientName, new v1[clientName](this.options)); + this.clients_.set( + clientName, + new (v1 as Record)[clientName](this.options), + ); } } catch (err) { callback(err, null); @@ -1860,7 +1873,8 @@ class Spanner extends GrpcService { if ( this._metricsEnabled && config.client === 'SpannerClient' && - this.projectId_ + this.projectId_ && + this.projectId_ !== '{{projectId}}' ) { metricsTracer = MetricsTracerFactory?.getInstance(this.projectId_)?.createMetricsTracer( @@ -1896,6 +1910,7 @@ class Spanner extends GrpcService { .then(val => { metricsTracer?.recordOperationCompletion(); resolve(val); + return val; }) .catch(error => { metricsTracer?.recordOperationCompletion(); @@ -1928,7 +1943,8 @@ class Spanner extends GrpcService { if ( this._metricsEnabled && config.client === 'SpannerClient' && - this.projectId_ + this.projectId_ && + this.projectId_ !== '{{projectId}}' ) { metricsTracer = MetricsTracerFactory?.getInstance(this.projectId_)?.createMetricsTracer( diff --git a/handwritten/spanner/src/metrics/constants.ts b/handwritten/spanner/src/metrics/constants.ts index 959eeb3d817b..bfeb0831785f 100644 --- a/handwritten/spanner/src/metrics/constants.ts +++ b/handwritten/spanner/src/metrics/constants.ts @@ -11,10 +11,7 @@ // 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 { - View, - ExplicitBucketHistogramAggregation, -} from '@opentelemetry/sdk-metrics'; +import {AggregationType, ViewOptions} from '@opentelemetry/sdk-metrics'; export const SPANNER_METER_NAME = 'spanner-nodejs'; export const CLIENT_METRICS_PREFIX = 'spanner.googleapis.com/internal/client'; @@ -25,6 +22,7 @@ export const TRACER_CLEANUP_INTERVAL_MS = 30 * 60 * 1000; // 30 Minutes // OTel semantic conventions // See https://github.com/open-telemetry/opentelemetry-js/blob/main/semantic-conventions/README.md#unstable-semconv export const ATTR_CLOUD_REGION = 'cloud.region'; +export const ATTR_CLOUD_AVAILABILITY_ZONE = 'cloud.availability_zone'; // Minimum period that must past between metric exports export const MIN_EXPORT_FREQUENCY_MS = 30 * 1000; @@ -91,33 +89,30 @@ export const HISTOGRAM_BUCKET_BOUNDARIES = [ ]; // Defined Views for metric aggregation -export const OPERATION_LATENCY_VIEW = new View({ +const HISTOGRAM_AGGREGATION = { + type: AggregationType.EXPLICIT_BUCKET_HISTOGRAM as const, + options: {boundaries: HISTOGRAM_BUCKET_BOUNDARIES}, +}; + +export const OPERATION_LATENCY_VIEW: ViewOptions = { instrumentName: METRIC_NAME_OPERATION_LATENCIES, - aggregation: new ExplicitBucketHistogramAggregation( - HISTOGRAM_BUCKET_BOUNDARIES, - ), -}); + aggregation: HISTOGRAM_AGGREGATION, +}; -export const ATTEMPT_LATENCY_VIEW = new View({ +export const ATTEMPT_LATENCY_VIEW: ViewOptions = { instrumentName: METRIC_NAME_ATTEMPT_LATENCIES, - aggregation: new ExplicitBucketHistogramAggregation( - HISTOGRAM_BUCKET_BOUNDARIES, - ), -}); + aggregation: HISTOGRAM_AGGREGATION, +}; -export const GFE_LATENCY_VIEW = new View({ +export const GFE_LATENCY_VIEW: ViewOptions = { instrumentName: METRIC_NAME_GFE_LATENCIES, - aggregation: new ExplicitBucketHistogramAggregation( - HISTOGRAM_BUCKET_BOUNDARIES, - ), -}); + aggregation: HISTOGRAM_AGGREGATION, +}; -export const AFE_LATENCY_VIEW = new View({ +export const AFE_LATENCY_VIEW: ViewOptions = { instrumentName: METRIC_NAME_AFE_LATENCIES, - aggregation: new ExplicitBucketHistogramAggregation( - HISTOGRAM_BUCKET_BOUNDARIES, - ), -}); + aggregation: HISTOGRAM_AGGREGATION, +}; export const METRIC_VIEWS = [ OPERATION_LATENCY_VIEW, diff --git a/handwritten/spanner/src/metrics/external-types.ts b/handwritten/spanner/src/metrics/external-types.ts index 07c08f9b08ba..ac8f994cea79 100644 --- a/handwritten/spanner/src/metrics/external-types.ts +++ b/handwritten/spanner/src/metrics/external-types.ts @@ -35,3 +35,16 @@ export enum ValueType { DOUBLE = 'DOUBLE', DISTRIBUTION = 'DISTRIBUTION', } + +/** + * A Google Cloud Monitoring monitored resource. + * + * Previously imported from `@google-cloud/opentelemetry-resource-util`, which is + * deprecated and scheduled for archival. The interface is a plain data shape, so + * it is declared locally instead. + * See https://cloud.google.com/monitoring/api/ref_v3/rest/v3/MonitoredResource + */ +export interface MonitoredResource { + type: string; + labels: {[key: string]: string}; +} diff --git a/handwritten/spanner/src/metrics/metrics-tracer-factory.ts b/handwritten/spanner/src/metrics/metrics-tracer-factory.ts index d4de5f4c234b..29f08bbbfdd5 100644 --- a/handwritten/spanner/src/metrics/metrics-tracer-factory.ts +++ b/handwritten/spanner/src/metrics/metrics-tracer-factory.ts @@ -17,8 +17,11 @@ import * as os from 'os'; import * as process from 'process'; import {MeterProvider, MetricReader} from '@opentelemetry/sdk-metrics'; import {Counter, Histogram, context, ROOT_CONTEXT} from '@opentelemetry/api'; -import {detectResources, Resource} from '@opentelemetry/resources'; -import {GcpDetectorSync} from '@google-cloud/opentelemetry-resource-util'; +import { + detectResources, + resourceFromAttributes, +} from '@opentelemetry/resources'; +import {gcpDetector} from '@opentelemetry/resource-detector-gcp'; import * as Constants from './constants'; import {MetricsTracer} from './metrics-tracer'; const version = require('../../../package.json').version; @@ -51,6 +54,8 @@ export class MetricsTracerFactory { private _clientName: string; private _clientUid: string; private _location = 'global'; + private _locationPromise: Promise | null = null; + private _metricReaders: MetricReader[] = []; private _projectId: string; private _currentOperationTracers = new Map(); private _currentOperationLastUpdatedMs = new Map(); @@ -70,12 +75,18 @@ export class MetricsTracerFactory { // Only perform async call to retrieve location is metrics are enabled. if (MetricsTracerFactory.enabled) { - (async () => { - const location = await MetricsTracerFactory._detectClientLocation(); - this._location = location.length > 0 ? location : 'global'; - })().catch(error => { - throw error; - }); + this._locationPromise = MetricsTracerFactory._detectClientLocation() + .then(location => { + this._location = location.length > 0 ? location : 'global'; + return this._location; + }) + .catch(error => { + console.warn('Unable to detect location.', error); + return this._location; + }) + .finally(() => { + this._locationPromise = null; + }); } this._clientHash = MetricsTracerFactory._generateClientHash( @@ -117,23 +128,48 @@ export class MetricsTracerFactory { /** * Returns the MeterProvider, creating it and metric instruments if not already initialized. * Client-wide attributes that are known at this time are cached to be provided to all MetricsTracers. + * + * If called again with readers when the existing MeterProvider has none, the provider is + * recreated with those readers. If readers are already attached, additional readers are ignored. + * * @param readers Optional array of MetricReader instances to attach to the MeterProvider. * @returns The OTEL MeterProvider instance. */ public getMeterProvider(readers: MetricReader[] = []): MeterProvider { + if (this._meterProvider !== null && readers.length > 0) { + if (this._metricReaders.length === 0) { + const staleMeterProvider = this._meterProvider; + this._meterProvider = null; + staleMeterProvider.shutdown().catch(error => { + console.warn( + 'Unable to shut down the previous MeterProvider.', + error, + ); + }); + } else { + console.warn( + 'MeterProvider is already initialized with metric readers. The ' + + 'additional readers passed to getMeterProvider() are ignored.', + ); + } + } + if (this._meterProvider === null) { - const resource = new Resource({ + const resource = resourceFromAttributes({ [Constants.MONITORED_RES_LABEL_KEY_PROJECT]: this._projectId, [Constants.MONITORED_RES_LABEL_KEY_CLIENT_HASH]: this._clientHash, - [Constants.MONITORED_RES_LABEL_KEY_LOCATION]: this._location, + [Constants.MONITORED_RES_LABEL_KEY_LOCATION]: + this._locationPromise ?? this._location, [Constants.MONITORED_RES_LABEL_KEY_INSTANCE]: 'unknown', [Constants.MONITORED_RES_LABEL_KEY_INSTANCE_CONFIG]: 'unknown', }); + void resource.waitForAsyncAttributes?.(); this._meterProvider = new MeterProvider({ resource: resource, readers: readers, views: Constants.METRIC_VIEWS, }); + this._metricReaders = readers; this._createMetricInstruments(); } return this._meterProvider; @@ -156,6 +192,7 @@ export class MetricsTracerFactory { await this._meterProvider!.shutdown(); } this._meterProvider = null; + this._metricReaders = []; this._currentOperationTracers = new Map(); this._currentOperationLastUpdatedMs = new Map(); } @@ -216,6 +253,20 @@ export class MetricsTracerFactory { return this._clientName; } + /** + * Returns the detected client location. + */ + get location(): string { + return this._location; + } + + /** + * Returns true if the MeterProvider is initialized with at least one MetricReader. + */ + public hasMetricReaders(): boolean { + return this._metricReaders.length > 0; + } + /** * Creates a new MetricsTracer for a given resource name and method, and stores it for later retrieval. * Returns null if metrics are disabled. @@ -453,22 +504,26 @@ export class MetricsTracerFactory { } /** - * Gets the location (region) of the client, otherwise returns to the "global" region. - * Uses GcpDetectorSync to detect the region from the environment. - * @returns The detected region string, or "global" if not found. + * Gets the location (region or zone) of the client, or defaults to "global". + * Uses the GCP resource detector to detect the location from the environment. + * Checks `cloud.region` first and falls back to `cloud.availability_zone` + * for zonal GKE clusters where `cloud.region` is not set. + * @returns The detected location string, or "global" if not found. */ private static async _detectClientLocation(): Promise { const defaultRegion = 'global'; try { const resource = await detectResources({ - detectors: [new GcpDetectorSync()], + detectors: [gcpDetector], }); await resource?.waitForAsyncAttributes?.(); - const region = resource.attributes[Constants.ATTR_CLOUD_REGION]; - if (typeof region === 'string' && region) { - return region; + const location = + resource.attributes[Constants.ATTR_CLOUD_REGION] ?? + resource.attributes[Constants.ATTR_CLOUD_AVAILABILITY_ZONE]; + if (typeof location === 'string' && location) { + return location; } } catch (err) { console.warn('Unable to detect location.', err); diff --git a/handwritten/spanner/src/metrics/spanner-metrics-exporter.ts b/handwritten/spanner/src/metrics/spanner-metrics-exporter.ts index 9796ccd348b2..9cafb9caefc2 100644 --- a/handwritten/spanner/src/metrics/spanner-metrics-exporter.ts +++ b/handwritten/spanner/src/metrics/spanner-metrics-exporter.ts @@ -57,6 +57,7 @@ export class CloudMonitoringMetricsExporter implements PushMetricExporter { now.getTime() - this._lastExported.getTime() <= MIN_EXPORT_FREQUENCY_MS ) { + resultCallback({code: ExportResultCode.SUCCESS}); return; } @@ -81,6 +82,9 @@ export class CloudMonitoringMetricsExporter implements PushMetricExporter { private async _exportAsync( resourceMetrics: ResourceMetrics, ): Promise { + if (resourceMetrics?.resource?.asyncAttributesPending) { + await resourceMetrics.resource.waitForAsyncAttributes?.(); + } const timeSeriesList = transformResourceMetricToTimeSeriesArray( resourceMetrics, this._projectId, diff --git a/handwritten/spanner/src/metrics/transform.ts b/handwritten/spanner/src/metrics/transform.ts index 71a0052c1eee..da40867c7baa 100644 --- a/handwritten/spanner/src/metrics/transform.ts +++ b/handwritten/spanner/src/metrics/transform.ts @@ -21,9 +21,8 @@ import { ResourceMetrics, } from '@opentelemetry/sdk-metrics'; import {Resource} from '@opentelemetry/resources'; -import {MonitoredResource} from '@google-cloud/opentelemetry-resource-util'; import * as path from 'path'; -import {MetricKind, ValueType} from './external-types'; +import {MetricKind, MonitoredResource, ValueType} from './external-types'; import { SPANNER_METER_NAME, CLIENT_METRICS_PREFIX, @@ -33,6 +32,7 @@ import { METRIC_NAMES, METRIC_LABEL_KEY_CLIENT_UID, METRIC_LABEL_KEY_CLIENT_NAME, + MONITORED_RES_LABEL_KEY_LOCATION, UNKNOWN_ATTRIBUTE, } from './constants'; import {MetricsTracerFactory} from './metrics-tracer-factory'; @@ -125,10 +125,10 @@ function _createTimeSeries( ) { const type = path.posix.join(CLIENT_METRICS_PREFIX, metric.descriptor.name); const resourceLabels = resource - ? _extractLabels(resource, projectId) + ? _extractLabels(resource, projectId, true) : {metricLabels: {}, monitoredResourceLabels: {}}; - const dataLabels = _extractLabels(dataPoint, projectId); + const dataLabels = _extractLabels(dataPoint, projectId, false); const labels = { ...resourceLabels.metricLabels, @@ -206,18 +206,27 @@ function _transformPoint(metric: MetricData, dataPoint: DataPoint) { } } -/** Extracts metric and monitored resource labels from data point */ +/** Extracts metric and monitored resource labels from data point or resource */ function _extractLabels( {attributes = {}}: DataPoint | Resource, projectId: string, + isResource = false, ) { const factory = MetricsTracerFactory.getInstance(projectId); - // Add Client name and Client UID metric labels - attributes[METRIC_LABEL_KEY_CLIENT_UID] = - factory?.clientUid ?? UNKNOWN_ATTRIBUTE; - attributes[METRIC_LABEL_KEY_CLIENT_NAME] = - factory?.clientName ?? UNKNOWN_ATTRIBUTE; - return Object.entries(attributes).reduce( + const combinedAttributes: Record = { + ...attributes, + [METRIC_LABEL_KEY_CLIENT_UID]: factory?.clientUid ?? UNKNOWN_ATTRIBUTE, + [METRIC_LABEL_KEY_CLIENT_NAME]: factory?.clientName ?? UNKNOWN_ATTRIBUTE, + }; + if ( + isResource && + factory?.location && + (!combinedAttributes[MONITORED_RES_LABEL_KEY_LOCATION] || + combinedAttributes[MONITORED_RES_LABEL_KEY_LOCATION] === 'global') + ) { + combinedAttributes[MONITORED_RES_LABEL_KEY_LOCATION] = factory.location; + } + return Object.entries(combinedAttributes).reduce( (result, [key, value]) => { const normalizedKey = _normalizeLabelKey(key); const val = value?.toString(); diff --git a/handwritten/spanner/test/metrics/metrics-tracer-factory.ts b/handwritten/spanner/test/metrics/metrics-tracer-factory.ts index 78dc1d27a132..9890a615ed1b 100644 --- a/handwritten/spanner/test/metrics/metrics-tracer-factory.ts +++ b/handwritten/spanner/test/metrics/metrics-tracer-factory.ts @@ -14,13 +14,18 @@ import { MeterProvider, + MetricReader, PeriodicExportingMetricReader, + ResourceMetrics, } from '@opentelemetry/sdk-metrics'; import * as assert from 'assert'; import * as sinon from 'sinon'; +import {gcpDetector} from '@opentelemetry/resource-detector-gcp'; import * as Constants from '../../src/metrics/constants'; import {MetricsTracerFactory} from '../../src/metrics/metrics-tracer-factory'; +import {transformResourceMetricToTimeSeriesArray} from '../../src/metrics/transform'; import {CloudMonitoringMetricsExporter} from '../../src/metrics/spanner-metrics-exporter'; +import {Spanner} from '../../src'; describe('MetricsTracerFactory', () => { let sandbox: sinon.SinonSandbox; @@ -65,6 +70,9 @@ describe('MetricsTracerFactory', () => { .returns({add: addGfeConnectivityErrorCountStub}); sandbox.stub(MeterProvider.prototype, 'getMeter').returns(meterStub as any); + sandbox + .stub(MetricsTracerFactory as any, '_detectClientLocation') + .resolves('global'); // metrics provider and related objects mockExporter = sandbox.createStubInstance(CloudMonitoringMetricsExporter); @@ -79,13 +87,11 @@ describe('MetricsTracerFactory', () => { MetricsTracerFactory.enabled = true; sandbox.resetHistory(); await MetricsTracerFactory.resetInstance(); - const provider = - MetricsTracerFactory.getInstance('project-id')!.getMeterProvider(); const reader = new PeriodicExportingMetricReader({ exporter: mockExporter, exportIntervalMillis: 60000, }); - provider.addMetricReader(reader); + MetricsTracerFactory.getInstance('project-id')!.getMeterProvider([reader]); }); afterEach(async () => { @@ -198,13 +204,20 @@ describe('MetricsTracerFactory', () => { describe('getInstanceAttributes', () => { let factory: MetricsTracerFactory; + let sandbox: sinon.SinonSandbox; + beforeEach(() => { + sandbox = sinon.createSandbox(); + sandbox + .stub(MetricsTracerFactory as any, '_detectClientLocation') + .resolves('global'); factory = new (MetricsTracerFactory as any)(); }); afterEach(async () => { await factory.resetMeterProvider(); clearInterval(factory['_intervalTracerCleanup']); + sandbox.restore(); }); it('should extract project, instance, and database from full resource path', () => { @@ -248,9 +261,14 @@ describe('getInstanceAttributes', () => { describe('MetricsTracerFactory with set clock', () => { let clock: sinon.SinonFakeTimers; + let sandbox: sinon.SinonSandbox; beforeEach(async () => { MetricsTracerFactory.enabled = true; + sandbox = sinon.createSandbox(); + sandbox + .stub(MetricsTracerFactory as any, '_detectClientLocation') + .resolves('global'); await MetricsTracerFactory.resetInstance(); // Use fake timers to control the clock clock = sinon.useFakeTimers(); @@ -259,6 +277,7 @@ describe('MetricsTracerFactory with set clock', () => { afterEach(() => { // Restore the real timers clock.restore(); + sandbox.restore(); }); describe('_cleanMetricTracers', () => { @@ -313,3 +332,363 @@ describe('MetricsTracerFactory with set clock', () => { }); }); }); + +describe('MetricsTracerFactory location detection failures', () => { + let sandbox: sinon.SinonSandbox; + let warnStub: sinon.SinonStub; + + beforeEach(async () => { + sandbox = sinon.createSandbox(); + warnStub = sandbox.stub(console, 'warn'); + MetricsTracerFactory.enabled = true; + await MetricsTracerFactory.resetInstance(); + }); + + afterEach(async () => { + sandbox.restore(); + await MetricsTracerFactory.resetInstance(); + }); + + it('should not trigger an UnhandledPromiseRejection if _detectClientLocation rejects', async () => { + let rejectLocation!: (err: Error) => void; + const locationPromise = new Promise((_resolve, reject) => { + rejectLocation = reject; + }); + + let unhandledError: Error | null = null; + const onUnhandledRejection = (err: Error) => { + unhandledError = err; + }; + process.once('unhandledRejection', onUnhandledRejection); + + try { + sandbox + .stub(MetricsTracerFactory as any, '_detectClientLocation') + .returns(locationPromise); + + const factory = MetricsTracerFactory.getInstance('test-project'); + + rejectLocation(new Error('Unexpected detector failure')); + await locationPromise.catch(() => {}); + await new Promise(resolve => setImmediate(resolve)); + + assert.strictEqual(unhandledError, null); + assert.ok(warnStub.calledWith('Unable to detect location.')); + assert.strictEqual(factory!['_location'], 'global'); + } finally { + process.removeListener('unhandledRejection', onUnhandledRejection); + } + }); +}); + +describe('MetricsTracerFactory._detectClientLocation', () => { + let sandbox: sinon.SinonSandbox; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + const stubDetectedAttributes = (attributes: Record) => { + sandbox.stub(gcpDetector, 'detect').returns({ + attributes: Object.fromEntries( + Object.entries(attributes).map(([key, value]) => [ + key, + Promise.resolve(value), + ]), + ), + } as any); + }; + + it('should detect client location on a zonal GKE cluster where cloud.availability_zone is set', async () => { + stubDetectedAttributes({ + 'cloud.platform': 'gcp_kubernetes_engine', + 'cloud.availability_zone': 'us-central1-a', + }); + + const detectedLocation = await ( + MetricsTracerFactory as any + )._detectClientLocation(); + + assert.strictEqual(detectedLocation, 'us-central1-a'); + }); + + it('should detect client location on a regional GKE cluster where cloud.region is set', async () => { + stubDetectedAttributes({ + 'cloud.platform': 'gcp_kubernetes_engine', + 'cloud.region': 'us-central1', + }); + + const detectedLocation = await ( + MetricsTracerFactory as any + )._detectClientLocation(); + + assert.strictEqual(detectedLocation, 'us-central1'); + }); + + it('should prefer cloud.region over cloud.availability_zone when both are set', async () => { + stubDetectedAttributes({ + 'cloud.platform': 'gcp_compute_engine', + 'cloud.availability_zone': 'us-central1-a', + 'cloud.region': 'us-central1', + }); + + const detectedLocation = await ( + MetricsTracerFactory as any + )._detectClientLocation(); + + assert.strictEqual(detectedLocation, 'us-central1'); + }); + + it('should fall back to global when neither attribute is set', async () => { + stubDetectedAttributes({}); + + const detectedLocation = await ( + MetricsTracerFactory as any + )._detectClientLocation(); + + assert.strictEqual(detectedLocation, 'global'); + }); +}); + +describe('MetricsTracerFactory exported location', () => { + let sandbox: sinon.SinonSandbox; + + beforeEach(async () => { + sandbox = sinon.createSandbox(); + MetricsTracerFactory.enabled = true; + await MetricsTracerFactory.resetInstance(); + }); + + afterEach(async () => { + sandbox.restore(); + await MetricsTracerFactory.resetInstance(); + }); + + it('should export the detected GCP location rather than global when _detectClientLocation resolves asynchronously', async () => { + let resolveLocation!: (location: string) => void; + const locationPromise = new Promise(resolve => { + resolveLocation = resolve; + }); + sandbox + .stub(MetricsTracerFactory as any, '_detectClientLocation') + .returns(locationPromise); + + const exported: ResourceMetrics[] = []; + const capturingExporter = { + export: ( + resourceMetrics: ResourceMetrics, + resultCallback: (result: {code: number}) => void, + ) => { + exported.push(resourceMetrics); + resultCallback({code: 0}); + }, + forceFlush: async () => {}, + shutdown: async () => {}, + }; + const reader = new PeriodicExportingMetricReader({ + exporter: capturingExporter as any, + exportIntervalMillis: 60000, + }); + + const factory = MetricsTracerFactory.getInstance('test-project')!; + factory.getMeterProvider([reader]); + assert.strictEqual(factory['_location'], 'global'); + + const tracer = factory.createMetricsTracer( + 'some-method', + 'projects/test-project/instances/inst/databases/db', + '1.1a2bc3d4.1.1.1.1', + ); + tracer!.recordOperationStart(); + tracer!.recordOperationCompletion(); + + resolveLocation('us-central1'); + await locationPromise; + + await reader.forceFlush(); + + assert.strictEqual(exported.length, 1); + const timeSeries = transformResourceMetricToTimeSeriesArray( + exported[0], + 'test-project', + ); + assert.ok(timeSeries.length > 0); + assert.strictEqual(timeSeries[0].resource!.labels!.location, 'us-central1'); + + await reader.shutdown(); + }); + + it('should export the detected GCP location via reader.collect() without requiring waitForAsyncAttributes', async () => { + let resolveLocation!: (location: string) => void; + const locationPromise = new Promise(resolve => { + resolveLocation = resolve; + }); + sandbox + .stub(MetricsTracerFactory as any, '_detectClientLocation') + .returns(locationPromise); + + class DirectCollectReader extends MetricReader { + protected async onForceFlush(): Promise {} + protected async onShutdown(): Promise {} + } + + const reader = new DirectCollectReader(); + const factory = MetricsTracerFactory.getInstance('test-project')!; + factory.getMeterProvider([reader]); + assert.strictEqual(factory.location, 'global'); + + const tracer = factory.createMetricsTracer( + 'some-method', + 'projects/test-project/instances/inst/databases/db', + '1.1a2bc3d4.1.1.1.1', + ); + tracer!.recordOperationStart(); + tracer!.recordOperationCompletion(); + + resolveLocation('us-central1'); + await locationPromise; + await Promise.resolve(); + await Promise.resolve(); + assert.strictEqual(factory.location, 'us-central1'); + assert.strictEqual(factory['_locationPromise'], null); + + // Collect directly via MetricReader.collect() (which does NOT invoke waitForAsyncAttributes) + const {resourceMetrics} = await reader.collect(); + assert.strictEqual(resourceMetrics.resource.asyncAttributesPending, false); + assert.strictEqual( + resourceMetrics.resource.attributes[ + Constants.MONITORED_RES_LABEL_KEY_LOCATION + ], + 'us-central1', + ); + const timeSeries = transformResourceMetricToTimeSeriesArray( + resourceMetrics, + 'test-project', + ); + assert.ok(timeSeries.length > 0); + assert.strictEqual(timeSeries[0].resource!.labels!.location, 'us-central1'); + + await reader.shutdown(); + }); +}); + +describe('MetricsTracerFactory getMeterProvider readers', () => { + let sandbox: sinon.SinonSandbox; + let warnStub: sinon.SinonStub; + + class InMemoryMetricReader extends MetricReader { + protected async onForceFlush(): Promise {} + protected async onShutdown(): Promise {} + } + + const recordOperation = (factory: MetricsTracerFactory) => { + const tracer = factory.createMetricsTracer( + 'some-method', + 'projects/test-project/instances/inst/databases/db', + '1.1a2bc3d4.1.1.1.1', + ); + tracer!.recordOperationStart(); + tracer!.recordOperationCompletion(); + }; + + beforeEach(async () => { + sandbox = sinon.createSandbox(); + warnStub = sandbox.stub(console, 'warn'); + MetricsTracerFactory.enabled = true; + if ( + Object.prototype.hasOwnProperty.call( + process.env, + 'SPANNER_DISABLE_BUILTIN_METRICS', + ) + ) { + sandbox.replace(process.env, 'SPANNER_DISABLE_BUILTIN_METRICS', 'false'); + } else { + sandbox.define(process.env, 'SPANNER_DISABLE_BUILTIN_METRICS', 'false'); + } + await MetricsTracerFactory.resetInstance(); + sandbox + .stub(MetricsTracerFactory as any, '_detectClientLocation') + .resolves('us-central1'); + }); + + afterEach(async () => { + sandbox.restore(); + await MetricsTracerFactory.resetInstance(); + }); + + it('should bind MetricReader passed to getMeterProvider([reader]) even if getMeterProvider() was called earlier', async () => { + const factory = MetricsTracerFactory.getInstance('test-project')!; + + assert.strictEqual(factory.hasMetricReaders(), false); + const initialMeterProvider = factory.getMeterProvider(); + assert.strictEqual(factory.hasMetricReaders(), false); + + const reader = new InMemoryMetricReader(); + const rebuiltMeterProvider = factory.getMeterProvider([reader]); + assert.strictEqual(factory.hasMetricReaders(), true); + + assert.notStrictEqual(rebuiltMeterProvider, initialMeterProvider); + + recordOperation(factory); + + const {resourceMetrics} = await reader.collect(); + assert.ok(resourceMetrics.scopeMetrics.length > 0); + + await reader.shutdown(); + }); + + it('should keep returning the same MeterProvider for calls without readers', () => { + const factory = MetricsTracerFactory.getInstance('test-project')!; + const reader = new InMemoryMetricReader(); + + const meterProvider = factory.getMeterProvider([reader]); + + assert.strictEqual(factory.getMeterProvider(), meterProvider); + assert.ok(warnStub.notCalled); + }); + + it('should warn instead of silently ignoring readers once readers are bound', async () => { + const factory = MetricsTracerFactory.getInstance('test-project')!; + const boundReader = new InMemoryMetricReader(); + const meterProvider = factory.getMeterProvider([boundReader]); + + const ignoredReader = new InMemoryMetricReader(); + const sameMeterProvider = factory.getMeterProvider([ignoredReader]); + + assert.strictEqual(sameMeterProvider, meterProvider); + assert.ok( + warnStub.calledWithMatch( + 'MeterProvider is already initialized with metric readers.', + ), + ); + + recordOperation(factory); + const {resourceMetrics} = await boundReader.collect(); + assert.ok(resourceMetrics.scopeMetrics.length > 0); + + await boundReader.shutdown(); + }); + + it('should initialize metrics synchronously when projectId is known and avoid duplicate warnings for multiple instances', () => { + const spanner1 = new Spanner({ + projectId: 'test-project', + }); + assert.ok(spanner1); + + const factory = MetricsTracerFactory.getInstance('test-project')!; + assert.ok(factory); + assert.strictEqual(factory.hasMetricReaders(), true); + + const spanner2 = new Spanner({ + projectId: 'test-project', + }); + assert.ok(spanner2); + + // Must not log a warning because hasMetricReaders() prevented re-registering + assert.ok(warnStub.notCalled); + }); +}); diff --git a/handwritten/spanner/test/metrics/spanner-metrics-exporter.ts b/handwritten/spanner/test/metrics/spanner-metrics-exporter.ts index cd1bb1787052..798f18b0e084 100644 --- a/handwritten/spanner/test/metrics/spanner-metrics-exporter.ts +++ b/handwritten/spanner/test/metrics/spanner-metrics-exporter.ts @@ -31,7 +31,7 @@ import { } from '../../src/metrics/constants'; import {Counter, Meter, Histogram} from '@opentelemetry/api'; import {ExportResult, ExportResultCode} from '@opentelemetry/core'; -import {Resource} from '@opentelemetry/resources'; +import {resourceFromAttributes} from '@opentelemetry/resources'; const PROJECT_ID = 'test-project'; const INSTANCE_ID = 'test-instance'; @@ -95,7 +95,7 @@ describe('Export', () => { beforeEach(() => { exporter = new CloudMonitoringMetricsExporter({auth}, PROJECT_ID); reader = new InMemoryMetricReader(); - const resource = new Resource({ + const resource = resourceFromAttributes({ ['project_id']: PROJECT_ID, ['client_hash']: CLIENT_HASH, ['location']: LOCATION, @@ -266,4 +266,20 @@ describe('Export', () => { const callbackResult = resultCallbackSpy.getCall(0).args[0]; assert.strictEqual(callbackResult.code, ExportResultCode.SUCCESS); }); + + it('should invoke resultCallback with SUCCESS when throttled by MIN_EXPORT_FREQUENCY_MS', async () => { + const {resourceMetrics} = await reader.collect(); + // Simulate an export that just occurred 1 second ago + (exporter as any)._lastExported = new Date(); + + const resultCallbackSpy = sinon.spy(); + exporter.export(resourceMetrics, resultCallbackSpy); + + // Must be called synchronously / immediately rather than hanging + assert.strictEqual(resultCallbackSpy.calledOnce, true); + assert.strictEqual( + resultCallbackSpy.getCall(0).args[0].code, + ExportResultCode.SUCCESS, + ); + }); }); diff --git a/handwritten/spanner/test/metrics/transform.ts b/handwritten/spanner/test/metrics/transform.ts index c9cadd05044a..8d0f2f70136f 100644 --- a/handwritten/spanner/test/metrics/transform.ts +++ b/handwritten/spanner/test/metrics/transform.ts @@ -28,7 +28,7 @@ import { MeterProvider, MetricReader, } from '@opentelemetry/sdk-metrics'; -import {Resource} from '@opentelemetry/resources'; +import {Resource, resourceFromAttributes} from '@opentelemetry/resources'; import { Attributes, Counter, @@ -84,7 +84,7 @@ describe('transform', () => { sandbox.stub(MetricsTracerFactory, 'getInstance').returns(mockFactory); reader = new InMemoryMetricReader(); - resource = new Resource({ + resource = resourceFromAttributes({ ['project_id']: 'project_id', ['client_hash']: 'test_hash', ['location']: 'test_location', @@ -244,7 +244,7 @@ describe('transform', () => { it('should extract metric and resource labels', () => { const dataLabels = _extractLabels(sumDataPoint, 'project_id'); - const resourceLabels = _extractLabels(resource, 'project_id'); + const resourceLabels = _extractLabels(resource, 'project_id', true); // Metric Labels assert.strictEqual(dataLabels.metricLabels['client_uid'], 'test_uid'); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index af53db88ae4d..215773a3bf98 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1701,7 +1701,7 @@ importers: version: 6.0.0 '@google-cloud/opentelemetry-cloud-monitoring-exporter': specifier: ^0.20.0 - version: 0.20.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-metrics@1.30.1(@opentelemetry/api@1.9.1))(encoding@0.1.13) + version: 0.20.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-metrics@1.30.1(@opentelemetry/api@1.9.1))(encoding@0.1.13) '@google-cloud/opentelemetry-resource-util': specifier: ^2.4.0 version: 2.4.0(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.1))(encoding@0.1.13) @@ -2222,7 +2222,7 @@ importers: version: 0.4.10(@types/node@24.13.3) '@google-cloud/opentelemetry-cloud-trace-exporter': specifier: ^3.0.0 - version: 3.0.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1))(encoding@0.1.13) + version: 3.0.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1))(encoding@0.1.13) '@google-cloud/promisify': specifier: ^6.0.1 version: 6.0.1 @@ -2488,7 +2488,7 @@ importers: version: 9.0.2 '@google-cloud/opentelemetry-cloud-trace-exporter': specifier: ^2.1.0 - version: 2.4.1(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.1))(encoding@0.1.13) + version: 2.4.1(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.1))(encoding@0.1.13) '@google-cloud/pubsub': specifier: ^6.0.0 version: 6.0.1 @@ -2930,9 +2930,6 @@ importers: '@google-cloud/monitoring': specifier: ^5.0.0 version: 5.4.0 - '@google-cloud/opentelemetry-resource-util': - specifier: ^2.4.0 - version: 2.4.0(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.1))(encoding@0.1.13) '@google-cloud/precise-date': specifier: ^5.0.0 version: 5.1.0 @@ -2957,12 +2954,15 @@ importers: '@opentelemetry/core': specifier: ^2.0.0 version: 2.10.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resource-detector-gcp': + specifier: ^0.57.0 + version: 0.57.0(@opentelemetry/api@1.9.1) '@opentelemetry/resources': - specifier: ^1.8.0 - version: 1.30.1(@opentelemetry/api@1.9.1) + specifier: ^2.11.0 + version: 2.11.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-metrics': - specifier: ^1.30.1 - version: 1.30.1(@opentelemetry/api@1.9.1) + specifier: ^2.11.0 + version: 2.11.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': specifier: ^1.30.0 version: 1.39.0 @@ -3019,11 +3019,11 @@ importers: specifier: ^1.0.4 version: 1.0.4(@grpc/grpc-js@1.14.4) '@opentelemetry/exporter-trace-otlp-grpc': - specifier: ^0.57.0 - version: 0.57.2(@opentelemetry/api@1.9.1) + specifier: ^0.222.0 + version: 0.222.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': - specifier: ^2.0.0 - version: 2.10.0(@opentelemetry/api@1.9.1) + specifier: ^2.11.0 + version: 2.11.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-node': specifier: ^2.0.0 version: 2.10.0(@opentelemetry/api@1.9.1) @@ -16937,14 +16937,14 @@ packages: '@opencensus/propagation-stackdriver@0.1.0': resolution: {integrity: sha512-YLklu8jnnYKaJ8gUFz3rM0FVdsWXEJAMLzeeU4JRac6LI34raENy4kvRezZtNEFS5KthaJUsYg04sPc/Ag0w4w==} + '@opentelemetry/api-logs@0.222.0': + resolution: {integrity: sha512-9mb1If+IF6u0ZVXkHQ6ogEae5HwA6ajIVUgpSDQyRASxft6BSXHvBvPooRle3yFN/fKnCdSOnuu0OC3PLcF6+g==} + engines: {node: '>=8.0.0'} + '@opentelemetry/api-logs@0.52.1': resolution: {integrity: sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==} engines: {node: '>=14'} - '@opentelemetry/api-logs@0.57.2': - resolution: {integrity: sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A==} - engines: {node: '>=14'} - '@opentelemetry/api@1.9.1': resolution: {integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==} engines: {node: '>=8.0.0'} @@ -16985,18 +16985,24 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/core@2.11.0': + resolution: {integrity: sha512-7YP44XH0tV6+Mb54x2YGf84i7yi+31MBZlE8JwvozkxyTvXbSp10X7cI7YE49ChJ3shMJoBmCJF3+1QFBJctGA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/exporter-trace-otlp-grpc@0.222.0': + resolution: {integrity: sha512-uisilePEdOMa3hIfXW82XXU6FofF8gw3iv+PoboIMWEUBC7XbJB3XnIHSdrSDsqCw3njR/If08qD0yzOY1U9Sg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + '@opentelemetry/exporter-trace-otlp-grpc@0.52.1': resolution: {integrity: sha512-pVkSH20crBwMTqB3nIN4jpQKUEoB0Z94drIHpYyEqs7UBr+I0cpYyOR3bqjA/UasQUMROb3GX8ZX4/9cVRqGBQ==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.0.0 - '@opentelemetry/exporter-trace-otlp-grpc@0.57.2': - resolution: {integrity: sha512-gHU1vA3JnHbNxEXg5iysqCWxN9j83d7/epTYBZflqQnTyCC4N7yZXn/dMM+bEmyhQPGjhCkNZLx4vZuChH1PYw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - '@opentelemetry/exporter-trace-otlp-http@0.52.1': resolution: {integrity: sha512-05HcNizx0BxcFKKnS5rwOV+2GevLTVIRA0tRgWYyw4yCgR53Ic/xk83toYKts7kbzcI+dswInUg/4s8oyA+tqg==} engines: {node: '>=14'} @@ -17021,15 +17027,21 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 + '@opentelemetry/otlp-exporter-base@0.222.0': + resolution: {integrity: sha512-YbywG3veEm2Fb6TbdxRkuquWob6eVWXuA8/Ba1tXz9jHfUqpdE3keilOHEtPboC4CvS1bjeeVfNkWGOOrLj+lw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + '@opentelemetry/otlp-exporter-base@0.52.1': resolution: {integrity: sha512-z175NXOtX5ihdlshtYBe5RpGeBoTXVCKPPLiQlD6FHvpM4Ch+p2B0yWKYSrBfLH24H9zjJiBdTrtD+hLlfnXEQ==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.0.0 - '@opentelemetry/otlp-exporter-base@0.57.2': - resolution: {integrity: sha512-XdxEzL23Urhidyebg5E6jZoaiW5ygP/mRjxLHixogbqwDy2Faduzb5N0o/Oi+XTIJu+iyxXdVORjXax+Qgfxag==} - engines: {node: '>=14'} + '@opentelemetry/otlp-grpc-exporter-base@0.222.0': + resolution: {integrity: sha512-Kw5WqDBYYpCgY+edHWZS/Jp3bdguqNkCyEWOsqFhf8RmtcEuWYzPFA9i+L6UsNoJXTs8ngeQB6dwELH1czNYjA==} + engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -17039,9 +17051,9 @@ packages: peerDependencies: '@opentelemetry/api': ^1.0.0 - '@opentelemetry/otlp-grpc-exporter-base@0.57.2': - resolution: {integrity: sha512-USn173KTWy0saqqRB5yU9xUZ2xdgb1Rdu5IosJnm9aV4hMTuFFRTUsQxbgc24QxpCHeoKzzCSnS/JzdV0oM2iQ==} - engines: {node: '>=14'} + '@opentelemetry/otlp-transformer@0.222.0': + resolution: {integrity: sha512-/F3BZ89+CJQnZkMh2tCrtcdB+XT2Dxhj4FFE+WPQ//413hmFL0/RfEX6vgOIWGhiSzrkHWTK3+6SiT7K5/g/jQ==} + engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -17051,12 +17063,6 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/otlp-transformer@0.57.2': - resolution: {integrity: sha512-48IIRj49gbQVK52jYsw70+Jv+JbahT8BqT2Th7C4H7RCM9d0gZ5sgNPoMpWldmfjvIsSgiGJtjfk9MeZvjhoig==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - '@opentelemetry/propagator-b3@1.25.1': resolution: {integrity: sha512-p6HFscpjrv7//kE+7L+3Vn00VEDUJB0n6ZrjkTYHrJ58QZ8B3ajSJhRbCcY6guQ3PDjTbxWklyvIN2ojVbIb1A==} engines: {node: '>=14'} @@ -17081,6 +17087,12 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/resource-detector-gcp@0.57.0': + resolution: {integrity: sha512-wp+hk7jD7mxA6swAqa5wQ3jws2egh1DCPZ9FfEgntmFw/mc8MaGZjwuttOoMeRjIlcBCCaNUHuN8B+MZaujAXQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + '@opentelemetry/resources@1.25.1': resolution: {integrity: sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==} engines: {node: '>=14'} @@ -17099,14 +17111,20 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/sdk-logs@0.52.1': - resolution: {integrity: sha512-MBYh+WcPPsN8YpRHRmK1Hsca9pVlyyKd4BxOC4SsgHACnl/bPp4Cri9hWhVm5+2tiQ9Zf4qSc1Jshw9tOLGWQA==} - engines: {node: '>=14'} + '@opentelemetry/resources@2.11.0': + resolution: {integrity: sha512-Ie7+8q8MDF4FAEQCKVMTx3ReUvxiIAgIiiW3c9JdmP8+HMcDy20puT+AHjexnExgnbvBxjQ9fjkFDWrikJ2jQA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-logs@0.222.0': + resolution: {integrity: sha512-+19YHODIjaUCArxleaJtuufFZVpz/xvvK+VllQqE+W8hHolxdoRwHfK/s667zezwh1hkx6FFF+oYzetYgqK+Bg==} + engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.4.0 <1.10.0' - '@opentelemetry/sdk-logs@0.57.2': - resolution: {integrity: sha512-TXFHJ5c+BKggWbdEQ/inpgIzEmS2BGQowLE9UhsMd7YYlUfBQJ4uax0VF/B5NYigdM/75OoJGhAV3upEhK+3gg==} + '@opentelemetry/sdk-logs@0.52.1': + resolution: {integrity: sha512-MBYh+WcPPsN8YpRHRmK1Hsca9pVlyyKd4BxOC4SsgHACnl/bPp4Cri9hWhVm5+2tiQ9Zf4qSc1Jshw9tOLGWQA==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.4.0 <1.10.0' @@ -17123,6 +17141,12 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' + '@opentelemetry/sdk-metrics@2.11.0': + resolution: {integrity: sha512-7GXXcObyHyDUUSG+L+kJoquty01bzm7ivE7+SSgXXJcHuPzGviptxwARmI2c+bnnxjexGQbJnyNlN8HxBP/Y7A==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.9.0 <1.10.0' + '@opentelemetry/sdk-node@0.52.1': resolution: {integrity: sha512-uEG+gtEr6eKd8CVWeKMhH2olcCHM9dEK68pe0qE0be32BcCRsvYURhHaD1Srngh1SQcnQzZ4TP324euxqtBOJA==} engines: {node: '>=14'} @@ -17147,6 +17171,12 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' + '@opentelemetry/sdk-trace-base@2.11.0': + resolution: {integrity: sha512-H19x/TX/LZdqiYOjM7fqtSxwlplC5pgelavqbQdHbhdq0q/AI/TGkM2dfGuuynTXmJPeF2HoZVoPDu+TGoW78A==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + '@opentelemetry/sdk-trace-node@1.25.1': resolution: {integrity: sha512-nMcjFIKxnFqoez4gUmihdBrbpsEnAX/Xj16sGvZm+guceYE0NE00vLhpDVK6f3q8Q4VFI5xG8JjlXKMB/SkTTQ==} engines: {node: '>=14'} @@ -17171,6 +17201,12 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' + '@opentelemetry/sdk-trace@2.11.0': + resolution: {integrity: sha512-fFnTqGm8/G73GQVnxYi7LXa1ZVYEUvgL6XI1LpvV0bPC7WQ/ZGgKxCSl8FnlZBKto9JHHEFTO6s6CUpvvtwFrA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + '@opentelemetry/semantic-conventions@1.25.1': resolution: {integrity: sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==} engines: {node: '>=14'} @@ -25916,12 +25952,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@google-cloud/opentelemetry-cloud-monitoring-exporter@0.20.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-metrics@1.30.1(@opentelemetry/api@1.9.1))(encoding@0.1.13)': + '@google-cloud/opentelemetry-cloud-monitoring-exporter@0.20.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-metrics@1.30.1(@opentelemetry/api@1.9.1))(encoding@0.1.13)': dependencies: '@google-cloud/opentelemetry-resource-util': 2.4.0(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.1))(encoding@0.1.13) '@google-cloud/precise-date': 4.0.0 '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.11.0(@opentelemetry/api@1.9.1) '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-metrics': 1.30.1(@opentelemetry/api@1.9.1) google-auth-library: 9.15.1(encoding@0.1.13) @@ -25930,13 +25966,13 @@ snapshots: - encoding - supports-color - '@google-cloud/opentelemetry-cloud-trace-exporter@2.4.1(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.1))(encoding@0.1.13)': + '@google-cloud/opentelemetry-cloud-trace-exporter@2.4.1(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.1))(encoding@0.1.13)': dependencies: '@google-cloud/opentelemetry-resource-util': 2.4.0(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.1))(encoding@0.1.13) '@grpc/grpc-js': 1.14.4 '@grpc/proto-loader': 0.7.15 '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.11.0(@opentelemetry/api@1.9.1) '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.1) google-auth-library: 9.15.1(encoding@0.1.13) @@ -25944,15 +25980,15 @@ snapshots: - encoding - supports-color - '@google-cloud/opentelemetry-cloud-trace-exporter@3.0.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1))(encoding@0.1.13)': + '@google-cloud/opentelemetry-cloud-trace-exporter@3.0.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1))(encoding@0.1.13)': dependencies: - '@google-cloud/opentelemetry-resource-util': 3.0.0(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@2.10.0(@opentelemetry/api@1.9.1))(encoding@0.1.13) + '@google-cloud/opentelemetry-resource-util': 3.0.0(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@2.11.0(@opentelemetry/api@1.9.1))(encoding@0.1.13) '@grpc/grpc-js': 1.14.4 '@grpc/proto-loader': 0.8.1 '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.10.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace-base': 2.10.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.11.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.11.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.11.0(@opentelemetry/api@1.9.1) google-auth-library: 9.15.1(encoding@0.1.13) transitivePeerDependencies: - encoding @@ -25967,10 +26003,10 @@ snapshots: - encoding - supports-color - '@google-cloud/opentelemetry-resource-util@3.0.0(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@2.10.0(@opentelemetry/api@1.9.1))(encoding@0.1.13)': + '@google-cloud/opentelemetry-resource-util@3.0.0(@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@2.11.0(@opentelemetry/api@1.9.1))(encoding@0.1.13)': dependencies: - '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.10.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.11.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.11.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.39.0 gcp-metadata: 6.1.1(encoding@0.1.13) transitivePeerDependencies: @@ -26910,11 +26946,11 @@ snapshots: hex2dec: 1.1.2 uuid: 8.0.0 - '@opentelemetry/api-logs@0.52.1': + '@opentelemetry/api-logs@0.222.0': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/api-logs@0.57.2': + '@opentelemetry/api-logs@0.52.1': dependencies: '@opentelemetry/api': 1.9.1 @@ -26947,6 +26983,19 @@ snapshots: '@opentelemetry/api': 1.9.1 '@opentelemetry/semantic-conventions': 1.39.0 + '@opentelemetry/core@2.11.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/semantic-conventions': 1.39.0 + + '@opentelemetry/exporter-trace-otlp-grpc@0.222.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/otlp-exporter-base': 0.222.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-grpc-exporter-base': 0.222.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.222.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace': 2.11.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-trace-otlp-grpc@0.52.1(@opentelemetry/api@1.9.1)': dependencies: '@grpc/grpc-js': 1.14.4 @@ -26957,17 +27006,6 @@ snapshots: '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.9.1) - '@opentelemetry/exporter-trace-otlp-grpc@0.57.2(@opentelemetry/api@1.9.1)': - dependencies: - '@grpc/grpc-js': 1.14.4 - '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-exporter-base': 0.57.2(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-grpc-exporter-base': 0.57.2(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-transformer': 0.57.2(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.1) - '@opentelemetry/exporter-trace-otlp-http@0.52.1(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 @@ -27006,17 +27044,25 @@ snapshots: transitivePeerDependencies: - supports-color + '@opentelemetry/otlp-exporter-base@0.222.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.11.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.222.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base@0.52.1(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.1) '@opentelemetry/otlp-transformer': 0.52.1(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-exporter-base@0.57.2(@opentelemetry/api@1.9.1)': + '@opentelemetry/otlp-grpc-exporter-base@0.222.0(@opentelemetry/api@1.9.1)': dependencies: + '@grpc/grpc-js': 1.14.4 '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-transformer': 0.57.2(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.11.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base': 0.222.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.222.0(@opentelemetry/api@1.9.1) '@opentelemetry/otlp-grpc-exporter-base@0.52.1(@opentelemetry/api@1.9.1)': dependencies: @@ -27026,13 +27072,15 @@ snapshots: '@opentelemetry/otlp-exporter-base': 0.52.1(@opentelemetry/api@1.9.1) '@opentelemetry/otlp-transformer': 0.52.1(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-grpc-exporter-base@0.57.2(@opentelemetry/api@1.9.1)': + '@opentelemetry/otlp-transformer@0.222.0(@opentelemetry/api@1.9.1)': dependencies: - '@grpc/grpc-js': 1.14.4 '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-exporter-base': 0.57.2(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-transformer': 0.57.2(@opentelemetry/api@1.9.1) + '@opentelemetry/api-logs': 0.222.0 + '@opentelemetry/core': 2.11.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.11.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-logs': 0.222.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-metrics': 2.11.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace': 2.11.0(@opentelemetry/api@1.9.1) '@opentelemetry/otlp-transformer@0.52.1(@opentelemetry/api@1.9.1)': dependencies: @@ -27045,17 +27093,6 @@ snapshots: '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.9.1) protobufjs: 7.5.9 - '@opentelemetry/otlp-transformer@0.57.2(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/api-logs': 0.57.2 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-logs': 0.57.2(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-metrics': 1.30.1(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.1) - protobufjs: 7.5.9 - '@opentelemetry/propagator-b3@1.25.1(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 @@ -27076,6 +27113,15 @@ snapshots: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.1) + '@opentelemetry/resource-detector-gcp@0.57.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.11.0(@opentelemetry/api@1.9.1) + gcp-metadata: 8.1.2 + transitivePeerDependencies: + - supports-color + '@opentelemetry/resources@1.25.1(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 @@ -27094,6 +27140,20 @@ snapshots: '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.39.0 + '@opentelemetry/resources@2.11.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.11.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.39.0 + + '@opentelemetry/sdk-logs@0.222.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/api-logs': 0.222.0 + '@opentelemetry/core': 2.11.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.11.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.39.0 + '@opentelemetry/sdk-logs@0.52.1(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 @@ -27101,13 +27161,6 @@ snapshots: '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.1) '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-logs@0.57.2(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/api-logs': 0.57.2 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-metrics@1.25.1(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 @@ -27121,6 +27174,12 @@ snapshots: '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.1) '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-metrics@2.11.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.11.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.11.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-node@0.52.1(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 @@ -27162,6 +27221,14 @@ snapshots: '@opentelemetry/sdk-trace': 2.10.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.39.0 + '@opentelemetry/sdk-trace-base@2.11.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.11.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.11.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace': 2.11.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.39.0 + '@opentelemetry/sdk-trace-node@1.25.1(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 @@ -27196,6 +27263,13 @@ snapshots: '@opentelemetry/resources': 2.10.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.39.0 + '@opentelemetry/sdk-trace@2.11.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.11.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.11.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.39.0 + '@opentelemetry/semantic-conventions@1.25.1': {} '@opentelemetry/semantic-conventions@1.28.0': {}