UNPKG

enhanced-adot-node-autoinstrumentation

Version:

This package provides Amazon Web Services distribution of the OpenTelemetry Node Instrumentation, which allows for auto-instrumentation of NodeJS applications.

109 lines 5.68 kB
"use strict"; // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 Object.defineProperty(exports, "__esModule", { value: true }); exports.AwsSpanMetricsProcessor = void 0; const api_1 = require("@opentelemetry/api"); const semantic_conventions_1 = require("@opentelemetry/semantic-conventions"); const aws_attribute_keys_1 = require("./aws-attribute-keys"); /** * This processor will generate metrics based on span data. It depends on a * {@link MetricAttributeGenerator} being provided on instantiation, which will provide a means to * determine attributes which should be used to create metrics. A {@link Resource} must also be * provided, which is used to generate metrics. Finally, three {@link Histogram}'s must be provided, * which will be used to actually create desired metrics (see below) * * <p>AwsSpanMetricsProcessor produces metrics for errors (e.g. HTTP 4XX status codes), faults (e.g. * HTTP 5XX status codes), and latency (in Milliseconds). Errors and faults are counted, while * latency is measured with a histogram. Metrics are emitted with attributes derived from span * attributes. * * <p>For highest fidelity metrics, this processor should be coupled with the {@link AlwaysRecordSampler}, * which will result in 100% of spans being sent to the processor. */ class AwsSpanMetricsProcessor { constructor(errorHistogram, faultHistogram, latencyHistogram, generator, resource, forceFlushFunction) { this.NANOS_TO_MILLIS_DIVIDER = 1000000; this.SECONDS_TO_MILLIS_MULTIPLIER = 1000; // Constants for deriving error and fault metrics this.ERROR_CODE_LOWER_BOUND = 400; this.ERROR_CODE_UPPER_BOUND = 499; this.FAULT_CODE_LOWER_BOUND = 500; this.FAULT_CODE_UPPER_BOUND = 599; // EC2 Metadata API IP Address // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html#instancedata-inside-access this.EC2_METADATA_API_IP = '169.254.169.254'; this.errorHistogram = errorHistogram; this.faultHistogram = faultHistogram; this.latencyHistogram = latencyHistogram; this.generator = generator; this.resource = resource; this.forceFlushFunction = forceFlushFunction; } /** Use {@link AwsSpanMetricsProcessorBuilder} to construct this processor. */ static create(errorHistogram, faultHistogram, latencyHistogram, generator, resource, forceFlushFunction) { return new AwsSpanMetricsProcessor(errorHistogram, faultHistogram, latencyHistogram, generator, resource, forceFlushFunction); } // eslint-disable-next-line @typescript-eslint/no-unused-vars onStart(span, parentContext) { } onEnd(span) { const attributeMap = this.generator.generateMetricAttributeMapFromSpan(span, this.resource); for (const attribute in attributeMap) { this.recordMetrics(span, attributeMap[attribute]); } } // The logic to record error and fault should be kept in sync with the aws-xray exporter whenever // possible except for the throttle // https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/exporter/awsxrayexporter/internal/translator/cause.go#L121-L160 recordErrorOrFault(spanData, attributes) { let httpStatusCode = spanData.attributes[semantic_conventions_1.SEMATTRS_HTTP_STATUS_CODE]; const statusCode = spanData.status.code; if (typeof httpStatusCode !== 'number') { httpStatusCode = attributes[semantic_conventions_1.SEMATTRS_HTTP_STATUS_CODE]; } if (typeof httpStatusCode !== 'number' || httpStatusCode < this.ERROR_CODE_LOWER_BOUND || httpStatusCode > this.FAULT_CODE_UPPER_BOUND) { if (api_1.SpanStatusCode.ERROR === statusCode) { this.errorHistogram.record(0, attributes); this.faultHistogram.record(1, attributes); } else { this.errorHistogram.record(0, attributes); this.faultHistogram.record(0, attributes); } } else if (httpStatusCode >= this.ERROR_CODE_LOWER_BOUND && httpStatusCode <= this.ERROR_CODE_UPPER_BOUND) { this.errorHistogram.record(1, attributes); this.faultHistogram.record(0, attributes); } else if (httpStatusCode >= this.FAULT_CODE_LOWER_BOUND && httpStatusCode <= this.FAULT_CODE_UPPER_BOUND) { this.errorHistogram.record(0, attributes); this.faultHistogram.record(1, attributes); } } recordLatency(span, attributes) { const millisFromSeconds = span.duration[0] * this.SECONDS_TO_MILLIS_MULTIPLIER; const millisFromNanos = span.duration[1] / this.NANOS_TO_MILLIS_DIVIDER; const millis = millisFromSeconds + millisFromNanos; this.latencyHistogram.record(millis, attributes); } recordMetrics(span, attributes) { // Only record metrics if non-empty attributes are returned. if (Object.keys(attributes).length > 0 && !this.isEc2MetadataApiSpan(attributes)) { this.recordErrorOrFault(span, attributes); this.recordLatency(span, attributes); } } shutdown() { return this.forceFlush(); } forceFlush() { return this.forceFlushFunction(); } isEc2MetadataApiSpan(attributes) { return attributes[aws_attribute_keys_1.AWS_ATTRIBUTE_KEYS.AWS_REMOTE_SERVICE] === this.EC2_METADATA_API_IP; } } exports.AwsSpanMetricsProcessor = AwsSpanMetricsProcessor; //# sourceMappingURL=aws-span-metrics-processor.js.map