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

Automatic dependency tracking of Azure SDKs not working correctly #723

Merged
merged 6 commits into from Feb 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 20 additions & 6 deletions AutoCollection/AsyncHooksScopeManager.ts
Expand Up @@ -37,11 +37,25 @@ export enum SpanKind {
CONSUMER = 4,
}

export type LinkContext = Pick<SpanContext, 'traceId' | 'spanId'>;

export interface Attributes {
[attributeKey: string]: AttributeValue | undefined;
}

export type AttributeValue =
| string
| number
| boolean
| Array<null | undefined | string>
| Array<null | undefined | number>
| Array<null | undefined | boolean>;

export interface Link {
/** The {@link SpanContext} of a linked span. */
spanContext: SpanContext;
context: LinkContext;
/** A set of {@link Attributes} on the link. */
attributes?: Record<string, string>;
attributes?: Attributes;
}

export interface SpanContext {
Expand All @@ -64,7 +78,7 @@ export interface Span {

export class OpenTelemetryScopeManagerWrapper {
private _activeSymbol: symbol | undefined;

public active() {
const context = CorrelationContextManager.getCurrentContext() as any;
return {
Expand All @@ -75,14 +89,14 @@ export class OpenTelemetryScopeManagerWrapper {
this._activeSymbol = key;
return context;
}

if (key === this._activeSymbol) {
return context;
}

return false;
},
setValue: () => {}
setValue: () => { }
};
}

Expand Down
5 changes: 2 additions & 3 deletions AutoCollection/HttpDependencies.ts
Expand Up @@ -63,17 +63,16 @@ class AutoCollectHttpDependencies {
const clientRequestPatch = (request: http.ClientRequest, options: http.RequestOptions | https.RequestOptions) => {
var shouldCollect = !(<any>options)[AutoCollectHttpDependencies.disableCollectionRequestOption] &&
!(<any>request)[AutoCollectHttpDependencies.alreadyAutoCollectedFlag];

// If someone else patched traceparent headers onto this request
if (options.headers && options.headers['user-agent'] && options.headers['user-agent'].toString().indexOf('azsdk-js') !== -1) {
shouldCollect = false;
}

(<any>request)[AutoCollectHttpDependencies.alreadyAutoCollectedFlag] = true;

if (request && options && shouldCollect) {
CorrelationContextManager.wrapEmitter(request);
AutoCollectHttpDependencies.trackRequest(this._client, {options: options, request: request});
AutoCollectHttpDependencies.trackRequest(this._client, { options: options, request: request });
}
};

Expand Down
33 changes: 18 additions & 15 deletions AutoCollection/diagnostic-channel/SpanParser.ts
@@ -1,10 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.
import { Span, SpanKind } from "../AsyncHooksScopeManager";
import { Attributes, Span, SpanKind } from "../AsyncHooksScopeManager";
import * as Contracts from "../../Declarations/Contracts";
import * as Constants from "../../Declarations/Constants";

function filterSpanAttributes(attributes: Record<string, string>) {
function filterSpanAttributes(attributes: Attributes) {
const newAttributes = { ...attributes };
Object.keys(Constants.SpanAttribute).forEach(key => {
delete newAttributes[key];
Expand All @@ -15,20 +15,23 @@ function filterSpanAttributes(attributes: Record<string, string>) {
export function spanToTelemetryContract(span: Span): (Contracts.DependencyTelemetry & Contracts.RequestTelemetry) & Contracts.Identified {
const id = `|${span.context().traceId}.${span.context().spanId}.`;
const duration = Math.round(span._duration[0] * 1e3 + span._duration[1] / 1e6);
const isHttp: boolean = ((span.attributes.component || "").toUpperCase() === Constants.DependencyTypeName.Http) || (!!span.attributes[Constants.SpanAttribute.HttpUrl]);
const isGrpc: boolean = (span.attributes.component || "").toLowerCase() === Constants.DependencyTypeName.Grpc;
let peerAddress = span.attributes["peer.address"] ? span.attributes["peer.address"].toString() : "";
let component = span.attributes["component"] ? span.attributes["component"].toString() : "";

const isHttp: boolean = ((component).toUpperCase() === Constants.DependencyTypeName.Http) || (!!span.attributes[Constants.SpanAttribute.HttpUrl]);
const isGrpc: boolean = (component).toLowerCase() === Constants.DependencyTypeName.Grpc;
if (isHttp) {
// Read http span attributes
const method = span.attributes[Constants.SpanAttribute.HttpMethod] || "GET";
const url = new URL(span.attributes[Constants.SpanAttribute.HttpUrl]);
const url = new URL(span.attributes[Constants.SpanAttribute.HttpUrl].toString());
const host = span.attributes[Constants.SpanAttribute.HttpHost] || url.host;
const port = span.attributes[Constants.SpanAttribute.HttpPort] || url.port || null;
const pathname = url.pathname || "/";

// Translate to AI Dependency format
const name = `${method} ${pathname}`;
const dependencyTypeName = Constants.DependencyTypeName.Http;
const target = port ? `${host}:${port}` : host;
const target = port ? `${host}:${port}`.toString() : host.toString();
const data = url.toString();
const resultCode = span.attributes[Constants.SpanAttribute.HttpStatusCode] || span.status.code || 0;
const success = resultCode < 400; // Status.OK
Expand All @@ -46,9 +49,9 @@ export function spanToTelemetryContract(span: Span): (Contracts.DependencyTeleme
const name = service ? `${method} ${service}` : span.name;
return {
id, duration, name,
target: service,
data: service || name,
url: service || name,
target: service.toString(),
data: service.toString() || name,
url: service.toString() || name,
dependencyTypeName: Constants.DependencyTypeName.Grpc,
resultCode: String(span.status.code || 0),
success: span.status.code === 0,
Expand All @@ -58,16 +61,16 @@ export function spanToTelemetryContract(span: Span): (Contracts.DependencyTeleme
const name = span.name;
const links = span.links && span.links.map(link => {
return {
operation_Id: link.spanContext.traceId,
id: link.spanContext.spanId
operation_Id: link.context.traceId,
id: link.context.spanId
};
});
return {
id, duration, name,
target: span.attributes["peer.address"],
data: span.attributes["peer.address"] || name,
url: span.attributes["peer.address"] || name,
dependencyTypeName: span.kind === SpanKind.INTERNAL ? Constants.DependencyTypeName.InProc : (span.attributes.component || span.name),
target: peerAddress,
data: peerAddress || name,
url: peerAddress || name,
dependencyTypeName: span.kind === SpanKind.INTERNAL ? Constants.DependencyTypeName.InProc : (component || span.name),
resultCode: String(span.status.code || 0),
success: span.status.code === 0,
properties: {
Expand Down
16 changes: 4 additions & 12 deletions README.md
Expand Up @@ -245,6 +245,10 @@ for information about exactly which versions of these packages are patched.
The `bunyan`, `winston`, and `console` patches will generate Application Insights Trace events based on whether `setAutoCollectConsole` is enabled.
The rest will generate Application Insights Dependency events based on whether `setAutoCollectDependencies` is enabled. Make sure that `applicationinsights` is imported **before** any 3rd-party packages for them to be instrumented successfully.

Automatic instrumentation for several Azure SDKs is also available, you must manually install @opentelemetry/tracing to enable this automatic tracing. No additional configuration is required
Currently Cognitive Search, Communication Common and Cosmos DB SDKs are not supported.
[Javascript Azure SDKs](https://azure.github.io/azure-sdk/releases/latest/index.html#javascript)


### Live Metrics
To enable sending live metrics of your app to Azure, use `setSendLiveMetrics(true)`. Filtering of live metrics in the Portal is currently not supported.
Expand Down Expand Up @@ -507,18 +511,6 @@ Included in `applicationinsights@2.0.0` is [every Node.js Plugin available in th
[develop]: https://github.com/microsoft/ApplicationInsights-node.js/tree/develop
[npm]: https://www.npmjs.com/package/applicationinsights

## Links

* [ApplicationInsights-Home][] is our central repo for libraries and info for
all languages and platforms.
* Follow the latest Application Insights changes and announcements on the
[ApplicationInsights-Announcements][] repo.
* [SDK Release Schedule][]

[ApplicationInsights-Announcements]: https://github.com/microsoft/ApplicationInsights-Announcements
[ApplicationInsights-Home]: https://github.com/microsoft/ApplicationInsights-Home
[SDK Release Schedule]: https://github.com/microsoft/ApplicationInsights-Home/wiki/SDK-Release-Schedule

## Contributing

1. Install all dependencies with `npm install`.
Expand Down
3 changes: 1 addition & 2 deletions Tests/FunctionalTests/Runner/TestSequence.json
Expand Up @@ -11,6 +11,5 @@
{"path": "/diagChannelRedis", "steps": ["RedisGet", "RedisSet", "RedisSet2", "RedisHset", "RedisHkeys", "RedisHincrby"]},
{"path": "/diagChannelWinston", "steps": ["WinstonError", "WinstonWarn", "WinstonInfo", "WinstonVerbose", "WinstonDebug", "WinstonSilly", "WinstonError2", "WinstonWarn2", "WinstonInfo2"]},
{"path": "/diagChannelPostgres", "steps": ["PostgresQuery", "Timeout", "PostgresQuery"]},
{"path": "/deepRequestDiagChannel", "steps": ["HttpGet", "RedisGet", "PostgresQuery", "RedisSet", "BunyanWarn", "AITrackTrace", "MongoInsert", "AITrackExc"]},
{"path": "/~azSdkStorage", "steps": ["AzureSdkCreate", "AzureSdkDelete"]}
{"path": "/deepRequestDiagChannel", "steps": ["HttpGet", "RedisGet", "PostgresQuery", "RedisSet", "BunyanWarn", "AITrackTrace", "MongoInsert", "AITrackExc"]}
]