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.
142 lines • 7.62 kB
JavaScript
"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._AwsXRayRemoteSampler = exports.AwsXRayRemoteSampler = void 0;
const api_1 = require("@opentelemetry/api");
const sdk_trace_base_1 = require("@opentelemetry/sdk-trace-base");
const aws_xray_sampling_client_1 = require("./aws-xray-sampling-client");
const fallback_sampler_1 = require("./fallback-sampler");
const rule_cache_1 = require("./rule-cache");
const sampling_rule_applier_1 = require("./sampling-rule-applier");
// 5 minute default sampling rules polling interval
const DEFAULT_RULES_POLLING_INTERVAL_SECONDS = 5 * 60;
// Default endpoint for awsproxy : https://aws-otel.github.io/docs/getting-started/remote-sampling#enable-awsproxy-extension
const DEFAULT_AWS_PROXY_ENDPOINT = 'http://localhost:2000';
// Wrapper class to ensure that all XRay Sampler Functionality in _AwsXRayRemoteSampler
// uses ParentBased logic to respect the parent span's sampling decision
class AwsXRayRemoteSampler {
constructor(samplerConfig) {
this._root = new sdk_trace_base_1.ParentBasedSampler({ root: new _AwsXRayRemoteSampler(samplerConfig) });
}
shouldSample(context, traceId, spanName, spanKind, attributes, links) {
return this._root.shouldSample(context, traceId, spanName, spanKind, attributes, links);
}
toString() {
return `AwsXRayRemoteSampler{root=${this._root.toString()}`;
}
}
exports.AwsXRayRemoteSampler = AwsXRayRemoteSampler;
// _AwsXRayRemoteSampler contains all core XRay Sampler Functionality,
// however it is NOT Parent-based (e.g. Sample logic runs for each span)
// Not intended for external use, use Parent-based `AwsXRayRemoteSampler` instead.
class _AwsXRayRemoteSampler {
constructor(samplerConfig) {
this.samplerDiag = api_1.diag;
if (samplerConfig.pollingInterval == null || samplerConfig.pollingInterval < 10) {
this.samplerDiag.warn(`'pollingInterval' is undefined or too small. Defaulting to ${DEFAULT_RULES_POLLING_INTERVAL_SECONDS} seconds`);
this.rulePollingIntervalMillis = DEFAULT_RULES_POLLING_INTERVAL_SECONDS * 1000;
}
else {
this.rulePollingIntervalMillis = samplerConfig.pollingInterval * 1000;
}
this.rulePollingJitterMillis = Math.random() * 5 * 1000;
this.targetPollingInterval = this.getDefaultTargetPollingInterval();
this.targetPollingJitterMillis = (Math.random() / 10) * 1000;
this.awsProxyEndpoint = samplerConfig.endpoint ? samplerConfig.endpoint : DEFAULT_AWS_PROXY_ENDPOINT;
this.fallbackSampler = new fallback_sampler_1.FallbackSampler();
this.clientId = _AwsXRayRemoteSampler.generateClientId();
this.ruleCache = new rule_cache_1.RuleCache(samplerConfig.resource);
this.samplingClient = new aws_xray_sampling_client_1.AwsXraySamplingClient(this.awsProxyEndpoint, this.samplerDiag);
// Start the Sampling Rules poller
this.startSamplingRulesPoller();
// Start the Sampling Targets poller where the first poll occurs after the default interval
this.startSamplingTargetsPoller();
}
getDefaultTargetPollingInterval() {
return rule_cache_1.DEFAULT_TARGET_POLLING_INTERVAL_SECONDS;
}
shouldSample(context, traceId, spanName, spanKind, attributes, links) {
if (this.ruleCache.isExpired()) {
this.samplerDiag.debug('Rule cache is expired, so using fallback sampling strategy');
return this.fallbackSampler.shouldSample(context, traceId, spanName, spanKind, attributes, links);
}
const matchedRule = this.ruleCache.getMatchedRule(attributes);
if (matchedRule) {
return matchedRule.shouldSample(context, traceId, spanName, spanKind, attributes, links);
}
this.samplerDiag.debug('Using fallback sampler as no rule match was found. This is likely due to a bug, since default rule should always match');
return this.fallbackSampler.shouldSample(context, traceId, spanName, spanKind, attributes, links);
}
toString() {
return `_AwsXRayRemoteSampler{awsProxyEndpoint=${this.awsProxyEndpoint}, rulePollingIntervalMillis=${this.rulePollingIntervalMillis.toString()}}`;
}
startSamplingRulesPoller() {
// Execute first update
this.getAndUpdateSamplingRules();
// Update sampling rules every 5 minutes (or user-defined polling interval)
this.rulePoller = setInterval(() => this.getAndUpdateSamplingRules(), this.rulePollingIntervalMillis + this.rulePollingJitterMillis);
this.rulePoller.unref();
}
startSamplingTargetsPoller() {
// Update sampling targets every targetPollingInterval (usually 10 seconds)
this.targetPoller = setInterval(() => this.getAndUpdateSamplingTargets(), this.targetPollingInterval * 1000 + this.targetPollingJitterMillis);
this.targetPoller.unref();
}
getAndUpdateSamplingTargets() {
const requestBody = {
SamplingStatisticsDocuments: this.ruleCache.createSamplingStatisticsDocuments(this.clientId),
};
this.samplingClient.fetchSamplingTargets(requestBody, this.updateSamplingTargets.bind(this));
}
getAndUpdateSamplingRules() {
this.samplingClient.fetchSamplingRules(this.updateSamplingRules.bind(this));
}
updateSamplingRules(responseObject) {
let samplingRules = [];
samplingRules = [];
if (responseObject.SamplingRuleRecords) {
responseObject.SamplingRuleRecords.forEach((record) => {
if (record.SamplingRule) {
samplingRules.push(new sampling_rule_applier_1.SamplingRuleApplier(record.SamplingRule, undefined));
}
});
this.ruleCache.updateRules(samplingRules);
}
else {
this.samplerDiag.error('SamplingRuleRecords from GetSamplingRules request is not defined');
}
}
updateSamplingTargets(responseObject) {
try {
const targetDocuments = {};
// Create Target-Name-to-Target-Map from sampling targets response
responseObject.SamplingTargetDocuments.forEach((newTarget) => {
targetDocuments[newTarget.RuleName] = newTarget;
});
// Update targets in the cache
const [refreshSamplingRules, nextPollingInterval] = this.ruleCache.updateTargets(targetDocuments, responseObject.LastRuleModification);
this.targetPollingInterval = nextPollingInterval;
clearInterval(this.targetPoller);
this.startSamplingTargetsPoller();
if (refreshSamplingRules) {
this.samplerDiag.debug('Performing out-of-band sampling rule polling to fetch updated rules.');
clearInterval(this.rulePoller);
this.startSamplingRulesPoller();
}
}
catch (error) {
this.samplerDiag.debug('Error occurred when updating Sampling Targets');
}
}
static generateClientId() {
const hexChars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
const clientIdArray = [];
for (let _ = 0; _ < 24; _ += 1) {
clientIdArray.push(hexChars[Math.floor(Math.random() * hexChars.length)]);
}
return clientIdArray.join('');
}
}
exports._AwsXRayRemoteSampler = _AwsXRayRemoteSampler;
//# sourceMappingURL=aws-xray-remote-sampler.js.map