UNPKG

@google-cloud/bigtable

Version:
278 lines 11.1 kB
"use strict"; // Copyright 2025 Google LLC // // 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. Object.defineProperty(exports, "__esModule", { value: true }); exports.CloudMonitoringExporter = void 0; exports.metricsToRequest = metricsToRequest; const opentelemetry_cloud_monitoring_exporter_1 = require("@google-cloud/opentelemetry-cloud-monitoring-exporter"); const monitoring_1 = require("@google-cloud/monitoring"); /** * Type guard function to determine if a given value is a counter value (a number). * * This function checks if a value, which could be either a `DistributionValue` * object or a `number`, is specifically a `number`. This is used to differentiate * between counter metrics (which have numeric values) and distribution metrics * (which have more complex, object-based values). * */ function isCounterValue(dataPoint) { return typeof dataPoint.value === 'number'; } function getInterval(dataPoint) { return { endTime: { seconds: dataPoint.endTime[0], nanos: dataPoint.endTime[1], }, startTime: { seconds: dataPoint.startTime[0], nanos: dataPoint.startTime[1], }, }; } /** * This function gets the timeseries data points for metrics that are * represented as distributions on the backend. These data points are part of a * timeseries object that is recorded to Google Cloud Monitoring. * * @param {DataPoint} dataPoint The datapoint containing the data we wish to * send to the Google Cloud Monitoring dashboard */ function getDistributionPoints(dataPoint) { const value = dataPoint.value; return [ { interval: getInterval(dataPoint), value: { distributionValue: { count: String(value.count), mean: value.count && value.sum ? value.sum / value.count : 0, bucketOptions: { explicitBuckets: { bounds: value.buckets.boundaries, }, }, bucketCounts: value.buckets.counts.map(String), }, }, }, ]; } /** * This function gets the timeseries data points for metrics that are * represented as integers on the backend. These data points are part of a * timeseries object that is recorded to Google Cloud Monitoring. * * @param {DataPoint} dataPoint The datapoint containing the data we wish to * send to the Google Cloud Monitoring dashboard */ function getIntegerPoints(dataPoint) { return [ { interval: getInterval(dataPoint), value: { int64Value: dataPoint.value, }, }, ]; } /** * getResource gets the resource object which is used for building the timeseries * object that will be sent to Google Cloud Monitoring dashboard * * @param {string} projectId The name of the project * @param {DataPoint} dataPoint The datapoint containing the data we wish to * send to the Google Cloud Monitoring dashboard */ function getResource(projectId, dataPoint) { const resourceLabels = { cluster: dataPoint.attributes.cluster, instance: dataPoint.attributes.instanceId, project_id: projectId, table: dataPoint.attributes.table, zone: dataPoint.attributes.zone, }; return { type: 'bigtable_client_raw', labels: resourceLabels, }; } /** * getMetric gets the metric object which is used for building the timeseries * object that will be sent to Google Cloud Monitoring dashboard * * @param {string} metricName The backend name of the metric that we want to record * @param {DataPoint} dataPoint The datapoint containing the data we wish to * send to the Google Cloud Monitoring dashboard */ function getMetric(metricName, dataPoint) { const streaming = dataPoint.attributes.streaming; const app_profile = dataPoint.attributes.app_profile; const status = dataPoint.attributes.status; return { type: metricName, labels: Object.assign({ method: dataPoint.attributes.method, client_uid: dataPoint.attributes.client_uid, client_name: dataPoint.attributes.client_name, }, status ? { status } : null, streaming ? { streaming } : null, app_profile ? { app_profile } : null), }; } /** * Converts OpenTelemetry metrics data into a format suitable for the Google Cloud * Monitoring API's `createTimeSeries` method. * * This function transforms the structured metrics data, including resource and * metric attributes, data points, and aggregation information, into an object * that conforms to the expected request format of the Cloud Monitoring API. * * @param projectId * @param {ResourceMetrics} exportArgs - The OpenTelemetry metrics data to be converted. This * object contains resource attributes, scope information, and a list of * metrics with their associated data points. * * @returns An object representing a `CreateTimeSeriesRequest`, ready for sending * to the Google Cloud Monitoring API. This object contains the project name * and an array of time series data points, formatted for ingestion by * Cloud Monitoring. * * @throws Will throw an error if there are issues converting the data. * * @remarks * The output format is specific to the Cloud Monitoring API and involves * mapping OpenTelemetry concepts to Cloud Monitoring's data model, including: * - Mapping resource attributes to resource labels. * - Mapping metric attributes to metric labels. * - Handling different metric types (counter, distribution). * - Converting data points to the correct structure, including start and end * times, values, and bucket information for distributions. * * @example * const exportInput: ExportInput = { ... }; // Example ExportInput object * const monitoringRequest = metricsToRequest(exportInput); * // monitoringRequest can now be used in monitoringClient.createTimeSeries(monitoringRequest) * * */ function metricsToRequest(projectId, exportArgs) { const timeSeriesArray = []; for (const scopeMetrics of exportArgs.scopeMetrics) { for (const scopeMetric of scopeMetrics.metrics) { for (const dataPoint of scopeMetric.dataPoints) { const metric = getMetric(scopeMetric.descriptor.name, dataPoint); const resource = getResource(projectId, dataPoint); if (isCounterValue(dataPoint)) { timeSeriesArray.push({ metric, resource, valueType: 'INT64', points: getIntegerPoints(dataPoint), }); } else { timeSeriesArray.push({ metric, resource, metricKind: 'CUMULATIVE', valueType: 'DISTRIBUTION', points: getDistributionPoints(dataPoint), unit: scopeMetric.descriptor.unit || 'ms', // Default to 'ms' if no unit is specified }); } } } } return { name: `projects/${projectId}`, timeSeries: timeSeriesArray, }; } /** * A custom OpenTelemetry `MetricExporter` that sends metrics data to Google Cloud * Monitoring. * * This class extends the base `MetricExporter` from `@google-cloud/opentelemetry-cloud-monitoring-exporter` * and handles the process of converting OpenTelemetry metrics data into the * format required by the Google Cloud Monitoring API. It uses the * `MetricServiceClient` to send the data to Google Cloud Monitoring's * `createTimeSeries` method. * * @remarks * This exporter relies on the `metricsToRequest` function to perform the * necessary transformation of OpenTelemetry metrics into Cloud Monitoring * `TimeSeries` data. * * The exporter is asynchronous and will not block the calling thread while * sending metrics. It manages the Google Cloud Monitoring client and handles * potential errors during the export process. * * The class expects the `ResourceMetrics` to have been correctly configured * and populated with the required resource attributes to correctly identify * the monitored resource in Cloud Monitoring. * * @example * // Create an instance of the CloudMonitoringExporter * const exporter = new CloudMonitoringExporter(); * * // Use the exporter with a MeterProvider * const meterProvider = new MeterProvider({ * resource: new Resource({ * 'service.name': 'my-service', * // ... other resource attributes * }), * readers: [new PeriodicExportingMetricReader({ * exporter: exporter, * exportIntervalMillis: 10000 // Export every 10 seconds * })] * }); * * // Now start instrumenting your application using the meter * const meter = meterProvider.getMeter('my-meter'); * // ... create counters, histograms, etc. * * @beta */ class CloudMonitoringExporter extends opentelemetry_cloud_monitoring_exporter_1.MetricExporter { client; constructor(options) { super(); if (options && options.apiEndpoint) { // We want the MetricServiceClient to always hit its default endpoint. delete options.apiEndpoint; } this.client = new monitoring_1.MetricServiceClient(options); } async export(metrics, resultCallback) { (async () => { try { const projectId = await this.client.getProjectId(); const request = metricsToRequest(projectId, metrics); await this.client.createServiceTimeSeries(request); // The resultCallback typically accepts a value equal to {code: x} // for some value x along with other info. When the code is equal to 0 // then the operation completed successfully. When the code is not equal // to 0 then the operation failed. The resultCallback will not log // anything to the console whether the error code was 0 or not. resultCallback({ code: 0 }); } catch (error) { resultCallback(error); } })().catch(err => { throw err; }); } } exports.CloudMonitoringExporter = CloudMonitoringExporter; //# sourceMappingURL=exporter.js.map