Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
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,
Expand All @@ -45,17 +45,17 @@
url: 'https://test-telemetry.sandbox.googleapis.com',
credentials: grpc.credentials.combineChannelCredentials(
grpc.credentials.createSsl(),
grpc.credentials.createFromGoogleCredential(authenticatedClient as any),

Check warning on line 48 in handwritten/spanner/google-cloud-spanner-executor/src/cloud-util.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
),
});

const provider = new NodeTracerProvider({
resource: new Resource({
resource: resourceFromAttributes({
[ATTR_SERVICE_NAME]: 'spanner-node-worker-proxy',
'gcp.project_id': WorkerProxy.PROJECT_ID,
}) as any,

Check warning on line 56 in handwritten/spanner/google-cloud-spanner-executor/src/cloud-util.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
sampler: new TraceIdRatioBasedSampler(this.TRACE_SAMPLING_RATE),
spanProcessors: [new BatchSpanProcessor(traceExporter as any)],

Check warning on line 58 in handwritten/spanner/google-cloud-spanner-executor/src/cloud-util.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
});

provider.register();
Expand All @@ -71,7 +71,7 @@
* Creates the configuration object for the Spanner client for connecting to a
* test GFE, including gRPC channel setup.
*/
public static getSpannerOptions(): any {

Check warning on line 74 in handwritten/spanner/google-cloud-spanner-executor/src/cloud-util.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
const options: SpannerOptions = {
projectId: WorkerProxy.PROJECT_ID,
servicePath: 'localhost',
Expand Down Expand Up @@ -106,7 +106,7 @@
this.TEST_HOST_IN_CERT;
}

(options as any).grpcOptions = grpcOptions;

Check warning on line 109 in handwritten/spanner/google-cloud-spanner-executor/src/cloud-util.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type

return options;
}
Expand Down
12 changes: 6 additions & 6 deletions handwritten/spanner/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"license": "Apache-2.0",
"author": "Google Inc.",
"engines": {
"node": ">=18"
"node": ">=22"
},
"repository": {
"type": "git",
Expand Down Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -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",
Expand Down
42 changes: 18 additions & 24 deletions handwritten/spanner/src/metrics/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

In OpenTelemetry JS SDK v2, View is still a class and MeterProvider expects an array of View instances rather than ViewOptions plain objects. Additionally, Aggregation is an abstract class, so plain objects cannot be assigned to it.

Please import View and Aggregation to define the views and aggregations properly.

Suggested change
import {AggregationType, ViewOptions} from '@opentelemetry/sdk-metrics';
import {Aggregation, View} from '@opentelemetry/sdk-metrics';


export const SPANNER_METER_NAME = 'spanner-nodejs';
export const CLIENT_METRICS_PREFIX = 'spanner.googleapis.com/internal/client';
Expand Down Expand Up @@ -91,33 +88,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,
};
Comment on lines +91 to +114

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

In OpenTelemetry JS SDK v2, Aggregation is an abstract class and cannot be represented as a plain object. Instead, use the static factory method Aggregation.ExplicitBucketHistogram(boundaries) to create the aggregation instance, and instantiate the views using new View(...).

This avoids TypeScript compilation errors and potential runtime failures since MeterProvider expects actual View and Aggregation instances.

Suggested change
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 OPERATION_LATENCY_VIEW = new View({
instrumentName: METRIC_NAME_OPERATION_LATENCIES,
aggregation: Aggregation.ExplicitBucketHistogram(HISTOGRAM_BUCKET_BOUNDARIES),
});
export const ATTEMPT_LATENCY_VIEW = new View({
instrumentName: METRIC_NAME_ATTEMPT_LATENCIES,
aggregation: Aggregation.ExplicitBucketHistogram(HISTOGRAM_BUCKET_BOUNDARIES),
});
export const GFE_LATENCY_VIEW = new View({
instrumentName: METRIC_NAME_GFE_LATENCIES,
aggregation: Aggregation.ExplicitBucketHistogram(HISTOGRAM_BUCKET_BOUNDARIES),
});
export const AFE_LATENCY_VIEW = new View({
instrumentName: METRIC_NAME_AFE_LATENCIES,
aggregation: Aggregation.ExplicitBucketHistogram(HISTOGRAM_BUCKET_BOUNDARIES),
});


export const METRIC_VIEWS = [
OPERATION_LATENCY_VIEW,
Expand Down
13 changes: 13 additions & 0 deletions handwritten/spanner/src/metrics/external-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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};
}
13 changes: 8 additions & 5 deletions handwritten/spanner/src/metrics/metrics-tracer-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -122,7 +125,7 @@ export class MetricsTracerFactory {
*/
public getMeterProvider(readers: MetricReader[] = []): MeterProvider {
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,
Expand Down Expand Up @@ -454,14 +457,14 @@ 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.
* Uses the GCP resource detector to detect the region from the environment.
* @returns The detected region string, or "global" if not found.
*/
private static async _detectClientLocation(): Promise<string> {
const defaultRegion = 'global';
try {
const resource = await detectResources({
detectors: [new GcpDetectorSync()],
detectors: [gcpDetector],
});

await resource?.waitForAsyncAttributes?.();
Expand Down
3 changes: 1 addition & 2 deletions handwritten/spanner/src/metrics/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
.onThirdCall()
.returns({add: addGfeConnectivityErrorCountStub});

sandbox.stub(MeterProvider.prototype, 'getMeter').returns(meterStub as any);

Check warning on line 67 in handwritten/spanner/test/metrics/metrics-tracer-factory.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type

// metrics provider and related objects
mockExporter = sandbox.createStubInstance(CloudMonitoringMetricsExporter);
Expand All @@ -79,13 +79,11 @@
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 () => {
Expand Down Expand Up @@ -166,12 +164,12 @@
'1.1a2bc3d4.1.1.1.1',
);

assert.strictEqual((factory as any)._currentOperationTracers.size, 1);

Check warning on line 167 in handwritten/spanner/test/metrics/metrics-tracer-factory.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type

factory!.clearCurrentTracer('1.1a2bc3d4.1.1.1');

assert.strictEqual((factory as any)._currentOperationTracers.size, 0);

Check warning on line 171 in handwritten/spanner/test/metrics/metrics-tracer-factory.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
assert.strictEqual((factory as any)._currentOperationLastUpdatedMs.size, 0);

Check warning on line 172 in handwritten/spanner/test/metrics/metrics-tracer-factory.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
});

it('should correctly set default attributes', () => {
Expand Down Expand Up @@ -199,7 +197,7 @@
describe('getInstanceAttributes', () => {
let factory: MetricsTracerFactory;
beforeEach(() => {
factory = new (MetricsTracerFactory as any)();

Check warning on line 200 in handwritten/spanner/test/metrics/metrics-tracer-factory.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
});

afterEach(async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions handwritten/spanner/test/metrics/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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',
Expand Down
Loading