Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AppSignals Functionality - add AWS Metric Attribute Generator #10

Merged
merged 9 commits into from
Aug 7, 2024

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export class AwsOpentelemetryConfigurator {
if (process.env.OTEL_NODE_RESOURCE_DETECTORS != null) {
defaultDetectors = getResourceDetectorsFromEnv();
// Add Env/AWS Resource Detectors if not present
const resourceDetectorsFromEnv = process.env.OTEL_NODE_RESOURCE_DETECTORS.split(',');
const resourceDetectorsFromEnv: string[] = process.env.OTEL_NODE_RESOURCE_DETECTORS.split(',');
if (!resourceDetectorsFromEnv.includes('env')) {
defaultDetectors.push(envDetectorSync);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
SEMATTRS_DB_SYSTEM,
SEMATTRS_HTTP_METHOD,
SEMATTRS_HTTP_TARGET,
SEMATTRS_HTTP_URL,
SEMATTRS_MESSAGING_OPERATION,
SEMATTRS_RPC_SYSTEM,
} from '@opentelemetry/semantic-conventions';
Expand Down Expand Up @@ -206,21 +207,36 @@ export class AwsSpanProcessingUtil {
*/
private static generateIngressOperation(span: ReadableSpan): string {
let operation: string = AwsSpanProcessingUtil.UNKNOWN_OPERATION;
let httpPath: AttributeValue | undefined = undefined;

if (AwsSpanProcessingUtil.isKeyPresent(span, SEMATTRS_HTTP_TARGET)) {
const httpTarget: AttributeValue | undefined = span.attributes[SEMATTRS_HTTP_TARGET];
// get the first part from API path string as operation value
// the more levels/parts we get from API path the higher chance for getting high cardinality
// data
if (httpTarget != null) {
operation = AwsSpanProcessingUtil.extractAPIPathValue(httpTarget.toString());
if (AwsSpanProcessingUtil.isKeyPresent(span, SEMATTRS_HTTP_METHOD)) {
const httpMethod: AttributeValue | undefined = span.attributes[SEMATTRS_HTTP_METHOD];
if (httpMethod != null) {
operation = httpMethod.toString() + ' ' + operation;
}
httpPath = span.attributes[SEMATTRS_HTTP_TARGET];
} else if (AwsSpanProcessingUtil.isKeyPresent(span, SEMATTRS_HTTP_URL)) {
const httpUrl: AttributeValue | undefined = span.attributes[SEMATTRS_HTTP_URL];
try {
let url: URL;
if (httpUrl !== undefined) {
url = new URL(httpUrl as string);
httpPath = url.pathname;
}
} catch (e: unknown) {
// In Python, if `httpUrl == ''`, there is no error from URL parsing, and `url.pathname = ''`
// In TypeScript, this catch block will be invoked. Here `httpPath = ''` is set as default to match Python.
diag.verbose(`invalid http.url attribute: ${httpUrl}, setting httpPath as empty string`);
httpPath = '';
}
}

if (httpPath !== undefined) {
operation = this.extractAPIPathValue(httpPath as string);
if (this.isKeyPresent(span, SEMATTRS_HTTP_METHOD)) {
const httpMethod: AttributeValue | undefined = span.attributes[SEMATTRS_HTTP_METHOD];
if (httpMethod !== undefined) {
operation = httpMethod + ' ' + operation;
}
}
}

return operation;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { Attributes } from '@opentelemetry/api';
import { Resource } from '@opentelemetry/resources';
import { ReadableSpan } from '@opentelemetry/sdk-trace-base';

export const SERVICE_METRIC: string = 'Service';
export const DEPENDENCY_METRIC: string = 'Dependency';

export interface AttributeMap {
[attributeKey: string]: Attributes;
}

/**
* Metric attribute generator defines an interface for classes that can generate specific attributes
* to be used by an {@link AwsSpanMetricsProcessor} to produce metrics and by
* {@link AwsMetricAttributesSpanExporter} to wrap the original span.
*/
export interface MetricAttributeGenerator {
/**
* Given a span and associated resource, produce meaningful metric attributes for metrics produced
* from the span. If no metrics should be generated from this span, return an empty Attributes={}.
*
* @param span - ReadableSpan to be used to generate metric attributes.
* @param resource - Resource associated with Span to be used to generate metric attributes.
* @return A map of Attributes objects with values assigned to key "Service" or "Dependency". It
* will contain either 0, 1, or 2 items.
*/
generateMetricAttributeMapFromSpan(span: ReadableSpan, resource: Resource): AttributeMap;
}
Loading