-
Notifications
You must be signed in to change notification settings - Fork 13
AppSignals Functionality - add Application Signals Configuration into ADOT #12
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
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
09d008b
AppSignals Functionality - add App Signals configuration into ADOT
jj22ee 8278380
disable fs instrumentation by default, add TODO comment for mongoose …
jj22ee a0b5428
specify direct dependencies in package.json, update package-lock.json
jj22ee 8c03157
enhance 'disable-fs' instrumentation comment
jj22ee 7ca6014
Merge branch 'main' into appsignals-configuration-pr
jj22ee 55610c5
Merge branch 'main' into appsignals-configuration-pr
jj22ee 091ca68
address comments
jj22ee 6600792
update copyright comments and related lint
jj22ee 7f393a2
optimize setting exportIntervalMillis
jj22ee f9832e9
add unit test for setting OTEL_METRIC_EXPORT_INTERVAL
jj22ee File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
433 changes: 426 additions & 7 deletions
433
aws-distro-opentelemetry-node-autoinstrumentation/src/aws-opentelemetry-configurator.ts
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
205 changes: 205 additions & 0 deletions
205
...distro-opentelemetry-node-autoinstrumentation/test/aws-opentelemetry-configurator.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
import { Span, TraceFlags, Tracer } from '@opentelemetry/api'; | ||
import { OTLPMetricExporter as OTLPGrpcOTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc'; | ||
import { OTLPMetricExporter as OTLPHttpOTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http'; | ||
import { Resource } from '@opentelemetry/resources'; | ||
import { PushMetricExporter } from '@opentelemetry/sdk-metrics'; | ||
import { ReadableSpan, SpanProcessor } from '@opentelemetry/sdk-trace-base'; | ||
import { | ||
AlwaysOnSampler, | ||
NodeTracerProvider, | ||
ParentBasedSampler, | ||
Sampler, | ||
SpanExporter, | ||
} from '@opentelemetry/sdk-trace-node'; | ||
import * as assert from 'assert'; | ||
import expect from 'expect'; | ||
import * as sinon from 'sinon'; | ||
import { AlwaysRecordSampler } from '../src/always-record-sampler'; | ||
import { AttributePropagatingSpanProcessor } from '../src/attribute-propagating-span-processor'; | ||
import { AwsMetricAttributesSpanExporter } from '../src/aws-metric-attributes-span-exporter'; | ||
import { | ||
ApplicationSignalsExporterProvider, | ||
AwsOpentelemetryConfigurator, | ||
AwsSpanProcessorProvider, | ||
customBuildSamplerFromEnv, | ||
} from '../src/aws-opentelemetry-configurator'; | ||
import { AwsSpanMetricsProcessor } from '../src/aws-span-metrics-processor'; | ||
import { setAwsDefaultEnvironmentVariables } from '../src/register'; | ||
|
||
// Tests AwsOpenTelemetryConfigurator after running Environment Variable setup in register.ts | ||
describe('AwsOpenTelemetryConfiguratorTest', () => { | ||
let awsOtelConfigurator: AwsOpentelemetryConfigurator; | ||
|
||
// setUpClass | ||
before(() => { | ||
// Run environment setup in register.ts, then validate expected env values. | ||
setAwsDefaultEnvironmentVariables(); | ||
validateConfiguratorEnviron(); | ||
|
||
// Overwrite exporter configs to keep tests clean, set sampler configs for tests | ||
process.env.OTEL_TRACES_EXPORTER = 'none'; | ||
process.env.OTEL_METRICS_EXPORTER = 'none'; | ||
process.env.OTEL_LOGS_EXPORTER = 'none'; | ||
process.env.OTEL_TRACES_SAMPLER = 'traceidratio'; | ||
process.env.OTEL_TRACES_SAMPLER_ARG = '0.01'; | ||
|
||
// Create configurator | ||
awsOtelConfigurator = new AwsOpentelemetryConfigurator(); | ||
}); | ||
|
||
// The probability of this passing once without correct IDs is low, 20 times is inconceivable. | ||
it('ProvideGenerateXrayIdsTest', () => { | ||
const tracerProvider: NodeTracerProvider = new NodeTracerProvider(awsOtelConfigurator.configure()); | ||
tracerProvider.addSpanProcessor( | ||
AttributePropagatingSpanProcessor.create((span: ReadableSpan) => '', 'spanNameKey', ['testKey1', 'testKey2']) | ||
); | ||
for (let _: number = 0; _ < 20; _++) { | ||
const tracer: Tracer = tracerProvider.getTracer('test'); | ||
const startTimeSec: number = Math.floor(new Date().getTime() / 1000.0); | ||
const span: Span = tracer.startSpan('test'); | ||
const traceId: string = span.spanContext().traceId; | ||
const traceId4ByteHex: string = traceId.substring(0, 8); | ||
const traceId4ByteNumber: number = Number(`0x${traceId4ByteHex}`); | ||
expect(traceId4ByteNumber).toBeGreaterThanOrEqual(startTimeSec); | ||
} | ||
}); | ||
|
||
// Sanity check that the trace ID ratio sampler works fine with the x-ray generator. | ||
it('TraceIdRatioSamplerTest', () => { | ||
process.env.OTEL_AWS_APPLICATION_SIGNALS_ENABLED = 'True'; | ||
const tracerProvider: NodeTracerProvider = new NodeTracerProvider(awsOtelConfigurator.configure()); | ||
delete process.env.OTEL_AWS_APPLICATION_SIGNALS_ENABLED; | ||
|
||
tracerProvider.addSpanProcessor( | ||
AttributePropagatingSpanProcessor.create((span: ReadableSpan) => '', 'spanNameKey', ['testKey1', 'testKey2']) | ||
); | ||
for (let _: number = 0; _ < 20; _++) { | ||
const numSpans: number = 100000; | ||
let numSampled: number = 0; | ||
const tracer: Tracer = tracerProvider.getTracer('test'); | ||
for (let __: number = 0; __ < numSpans; __++) { | ||
const span: Span = tracer.startSpan('test'); | ||
if (span.spanContext().traceFlags & TraceFlags.SAMPLED) { | ||
numSampled += 1; | ||
} | ||
span.end(); | ||
} | ||
// Configured for 1%, confirm there are at most 5% to account for randomness and reduce test flakiness. | ||
expect(0.05).toBeGreaterThan(numSampled / numSpans); | ||
} | ||
}); | ||
|
||
it('ImportDefaultSamplerWhenEnvVarIsNotSetTest', () => { | ||
delete process.env.OTEL_TRACES_SAMPLER; | ||
const defaultSampler: Sampler = customBuildSamplerFromEnv(Resource.empty()); | ||
|
||
expect(defaultSampler).not.toBeUndefined(); | ||
expect(defaultSampler.toString()).toEqual(new ParentBasedSampler({ root: new AlwaysOnSampler() }).toString()); | ||
}); | ||
|
||
it('IsApplicationSignalsEnabledTest', () => { | ||
process.env.OTEL_AWS_APPLICATION_SIGNALS_ENABLED = 'True'; | ||
expect(AwsOpentelemetryConfigurator.isApplicationSignalsEnabled()).toBeTruthy(); | ||
delete process.env.OTEL_AWS_APPLICATION_SIGNALS_ENABLED; | ||
|
||
process.env.OTEL_AWS_APPLICATION_SIGNALS_ENABLED = 'False'; | ||
expect(AwsOpentelemetryConfigurator.isApplicationSignalsEnabled()).toBeFalsy(); | ||
delete process.env.OTEL_AWS_APPLICATION_SIGNALS_ENABLED; | ||
expect(AwsOpentelemetryConfigurator.isApplicationSignalsEnabled()).toBeFalsy(); | ||
}); | ||
|
||
it('CustomizeSamplerTest', () => { | ||
const mockSampler: Sampler = sinon.createStubInstance(AlwaysOnSampler); | ||
let customizedSampler: Sampler = AwsOpentelemetryConfigurator.customizeSampler(mockSampler); | ||
expect(mockSampler).toEqual(customizedSampler); | ||
|
||
process.env.OTEL_AWS_APPLICATION_SIGNALS_ENABLED = 'True'; | ||
customizedSampler = AwsOpentelemetryConfigurator.customizeSampler(mockSampler); | ||
expect(mockSampler).not.toEqual(customizedSampler); | ||
expect(customizedSampler).toBeInstanceOf(AlwaysRecordSampler); | ||
expect(mockSampler).toEqual((customizedSampler as any).rootSampler); | ||
delete process.env.OTEL_AWS_APPLICATION_SIGNALS_ENABLED; | ||
}); | ||
|
||
it('CustomizeExporterTest', () => { | ||
const mockExporter: SpanExporter = sinon.createStubInstance(AwsMetricAttributesSpanExporter); | ||
let customizedExporter: SpanExporter = AwsSpanProcessorProvider.customizeSpanExporter( | ||
mockExporter, | ||
Resource.empty() | ||
); | ||
expect(mockExporter).toEqual(customizedExporter); | ||
|
||
process.env.OTEL_AWS_APPLICATION_SIGNALS_ENABLED = 'True'; | ||
customizedExporter = AwsSpanProcessorProvider.customizeSpanExporter(mockExporter, Resource.empty()); | ||
expect(mockExporter).not.toEqual(customizedExporter); | ||
expect(customizedExporter).toBeInstanceOf(AwsMetricAttributesSpanExporter); | ||
expect(mockExporter).toEqual((customizedExporter as any).delegate); | ||
delete process.env.OTEL_AWS_APPLICATION_SIGNALS_ENABLED; | ||
}); | ||
|
||
it('CustomizeSpanProcessorsTest', () => { | ||
delete process.env.OTEL_AWS_APPLICATION_SIGNALS_ENABLED; | ||
const spanProcessors: SpanProcessor[] = []; | ||
AwsOpentelemetryConfigurator.customizeSpanProcessors(spanProcessors, Resource.empty()); | ||
expect(spanProcessors.length).toEqual(0); | ||
|
||
process.env.OTEL_AWS_APPLICATION_SIGNALS_ENABLED = 'True'; | ||
AwsOpentelemetryConfigurator.customizeSpanProcessors(spanProcessors, Resource.empty()); | ||
expect(spanProcessors.length).toEqual(2); | ||
const firstProcessor: SpanProcessor = spanProcessors[0]; | ||
expect(firstProcessor).toBeInstanceOf(AttributePropagatingSpanProcessor); | ||
const secondProcessor: SpanProcessor = spanProcessors[1]; | ||
expect(secondProcessor).toBeInstanceOf(AwsSpanMetricsProcessor); | ||
delete process.env.OTEL_AWS_APPLICATION_SIGNALS_ENABLED; | ||
|
||
try { | ||
process.env.OTEL_AWS_APPLICATION_SIGNALS_ENABLED = 'True'; | ||
process.env.OTEL_METRIC_EXPORT_INTERVAL = undefined; | ||
AwsOpentelemetryConfigurator.customizeSpanProcessors(spanProcessors, Resource.empty()); | ||
process.env.OTEL_METRIC_EXPORT_INTERVAL = '123abc'; | ||
AwsOpentelemetryConfigurator.customizeSpanProcessors(spanProcessors, Resource.empty()); | ||
process.env.OTEL_METRIC_EXPORT_INTERVAL = '!@#$%^&*()'; | ||
AwsOpentelemetryConfigurator.customizeSpanProcessors(spanProcessors, Resource.empty()); | ||
process.env.OTEL_METRIC_EXPORT_INTERVAL = '123'; | ||
AwsOpentelemetryConfigurator.customizeSpanProcessors(spanProcessors, Resource.empty()); | ||
} catch (e: any) { | ||
assert.fail(`AwsOpentelemetryConfigurator.customizeSpanProcessors() has incorrectly thrown error: ${e}`); | ||
} finally { | ||
delete process.env.OTEL_AWS_APPLICATION_SIGNALS_ENABLED; | ||
} | ||
}); | ||
|
||
it('ApplicationSignalsExporterProviderTest', () => { | ||
// Check default protocol - HTTP, as specified by aws-distro-opentelemetry-node-autoinstrumentation's register.ts. | ||
let exporter: PushMetricExporter = ApplicationSignalsExporterProvider.Instance.createExporter(); | ||
expect(exporter).toBeInstanceOf(OTLPHttpOTLPMetricExporter); | ||
expect('http://localhost:4316/v1/metrics').toEqual((exporter as any)._otlpExporter.url); | ||
|
||
// Overwrite protocol to gRPC. | ||
process.env.OTEL_EXPORTER_OTLP_PROTOCOL = 'grpc'; | ||
exporter = ApplicationSignalsExporterProvider.Instance.createExporter(); | ||
expect(exporter).toBeInstanceOf(OTLPGrpcOTLPMetricExporter); | ||
expect('localhost:4315').toEqual((exporter as any)._otlpExporter.url); | ||
|
||
// Overwrite protocol back to HTTP. | ||
process.env.OTEL_EXPORTER_OTLP_PROTOCOL = 'http/protobuf'; | ||
exporter = ApplicationSignalsExporterProvider.Instance.createExporter(); | ||
expect(exporter).toBeInstanceOf(OTLPHttpOTLPMetricExporter); | ||
expect('http://localhost:4316/v1/metrics').toEqual((exporter as any)._otlpExporter.url); | ||
}); | ||
|
||
function validateConfiguratorEnviron() { | ||
// Set by register.ts | ||
expect('http/protobuf').toEqual(process.env.OTEL_EXPORTER_OTLP_PROTOCOL); | ||
expect('xray,tracecontext,b3,b3multi').toEqual(process.env.OTEL_PROPAGATORS); | ||
|
||
// Not set | ||
expect(undefined).toEqual(process.env.OTEL_TRACES_SAMPLER); | ||
expect(undefined).toEqual(process.env.OTEL_TRACES_SAMPLER_ARG); | ||
expect(undefined).toEqual(process.env.OTEL_TRACES_EXPORTER); | ||
expect(undefined).toEqual(process.env.OTEL_METRICS_EXPORTER); | ||
} | ||
}); |
83 changes: 83 additions & 0 deletions
83
aws-distro-opentelemetry-node-autoinstrumentation/test/register.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// Modifications Copyright The OpenTelemetry Authors. Licensed under the Apache License 2.0 License. | ||
|
||
import * as assert from 'assert'; | ||
import { spawnSync, SpawnSyncReturns } from 'child_process'; | ||
|
||
// The OpenTelemetry Authors code | ||
// Extend register.test.ts functionality to also test exported span with Application Signals enabled | ||
describe('Register', function () { | ||
it('can load auto instrumentation from command line', () => { | ||
const proc: SpawnSyncReturns<Buffer> = spawnSync( | ||
process.execPath, | ||
['--require', '../build/src/register.js', './third-party/otel/test-app/app.js'], | ||
{ | ||
cwd: __dirname, | ||
timeout: 10000, | ||
killSignal: 'SIGKILL', // SIGTERM is not sufficient to terminate some hangs | ||
env: Object.assign({}, process.env, { | ||
OTEL_NODE_RESOURCE_DETECTORS: 'none', | ||
OTEL_TRACES_EXPORTER: 'console', | ||
// nx (used by lerna run) defaults `FORCE_COLOR=true`, which in | ||
// node v18.17.0, v20.3.0 and later results in ANSI color escapes | ||
// in the ConsoleSpanExporter output that is checked below. | ||
FORCE_COLOR: '0', | ||
|
||
OTEL_LOG_LEVEL: 'ALL', | ||
OTEL_TRACES_SAMPLER: 'always_on', | ||
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: 'http://localhost:4316/v1/traces', | ||
OTEL_RESOURCE_ATTRIBUTES: 'service.name=test-adot-sdk-ec2-service-name', | ||
OTEL_AWS_APPLICATION_SIGNALS_ENABLED: 'true', | ||
OTEL_NODE_DISABLED_INSTRUMENTATIONS: 'fs', | ||
}), | ||
} | ||
); | ||
assert.ifError(proc.error); | ||
assert.equal(proc.status, 0, `proc.status (${proc.status})`); | ||
assert.equal(proc.signal, null, `proc.signal (${proc.signal})`); | ||
|
||
assert.ok(proc.stdout.includes('AWS Distro of OpenTelemetry automatic instrumentation started successfully')); | ||
assert.ok(proc.stdout.includes("Environment variable OTEL_EXPORTER_OTLP_PROTOCOL is set to 'http/protobuf'")); | ||
assert.ok(proc.stdout.includes("Environment variable OTEL_PROPAGATORS is set to 'xray,tracecontext,b3,b3multi'")); | ||
jj22ee marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// Check a span has been generated for the GET request done in app.js | ||
jj22ee marked this conversation as resolved.
Show resolved
Hide resolved
|
||
assert.ok(proc.stdout.includes("name: 'GET'"), 'console span output in stdout - validate Span Name'); | ||
assert.ok( | ||
proc.stdout.includes("'service.name': 'test-adot-sdk-ec2-service-name'"), | ||
'console span output in stdout - validate service.name' | ||
); | ||
|
||
// eslint-disable-next-line @typescript-eslint/typedef | ||
const packageJson = require('./../../package.json'); | ||
const DISTRO_VERSION: string = packageJson.version; | ||
assert.ok( | ||
proc.stdout.includes(`'telemetry.auto.version': '${DISTRO_VERSION}-aws'`), | ||
'console span output in stdout - validate telemetry.auto.version' | ||
); | ||
assert.ok( | ||
proc.stdout.includes("'aws.is.local.root': true"), | ||
'console span output in stdout - validate aws.is.local.root' | ||
); | ||
assert.ok( | ||
proc.stdout.includes("'aws.local.operation': 'InternalOperation'"), | ||
'console span output in stdout - validate aws.local.operation' | ||
); | ||
assert.ok( | ||
proc.stdout.includes("'aws.local.service': 'test-adot-sdk-ec2-service-name'"), | ||
'console span output in stdout - validate aws.local.service' | ||
); | ||
assert.ok( | ||
proc.stdout.includes("'aws.remote.service': 'example.com:80'"), | ||
'console span output in stdout - validate aws.remote.service' | ||
); | ||
assert.ok( | ||
proc.stdout.includes("'aws.remote.operation': 'GET /'"), | ||
'console span output in stdout - validate aws.remote.operation' | ||
); | ||
assert.ok( | ||
proc.stdout.includes("'aws.span.kind': 'LOCAL_ROOT'"), | ||
'console span output in stdout - validate aws.span.kind' | ||
); | ||
}); | ||
}); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.