Skip to content

Commit

Permalink
Merge branch 'main' into hectorhdzg/3.3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
hectorhdzg committed Sep 16, 2024
2 parents 9c6e126 + cb48713 commit 9c7f5d6
Show file tree
Hide file tree
Showing 11 changed files with 1,362 additions and 246 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,8 @@ client.trackMetric({name: "custom metric", value: 3});
client.trackTrace({message: "trace message"});
client.trackDependency({target:"http://dbname", name:"select customers proc", data:"SELECT * FROM Customers", duration:231, resultCode:0, success: true, dependencyTypeName: "ZSQL"});
client.trackRequest({name:"GET /customers", url:"http://myserver/customers", duration:309, resultCode:200, success:true});

client.trackAvailability({id: "123456789abcdefghijklmnopqrstuvw", name: "availalaibility-test-name", duration: 1000, success: true, runLocation: "Japan East", message: "Passed"})

let http = require("http");
http.createServer( (req, res) => {
client.trackNodeHttpRequest({request: req, response: res}); // Place at the beginning of your request handler
Expand Down
1,346 changes: 1,171 additions & 175 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,10 @@
"node": ">=8.0.0"
},
"devDependencies": {
"@azure/functions": "^3.2.0",
"@types/long": "^4.0.2",
"@types/microsoft__typescript-etw": "^0.1.0",
"@types/mocha": "^7.0.2",
"@types/node": "^8.0.0",
"@types/node": "^18.0.0",
"@types/semver": "7.3.9",
"@types/sinon": "^10.0.12",
"@typescript-eslint/eslint-plugin": "^5.37.0",
Expand All @@ -67,6 +66,8 @@
"@azure/core-auth": "^1.3.0",
"@azure/core-client": "^1.0.0",
"@azure/core-rest-pipeline": "^1.9.2",
"@azure/functions": "^4.5.0",
"@azure/functions-old": "npm:@azure/[email protected]",
"@azure/identity": "^4.2.1",
"@azure/monitor-opentelemetry": "^1.7.1",
"@azure/monitor-opentelemetry-exporter": "^1.0.0-beta.26",
Expand Down
5 changes: 2 additions & 3 deletions src/shim/applicationinsights.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@
// Licensed under the MIT license.

import * as http from "http";
import * as azureFunctionsTypes from "@azure/functions";
import { DiagConsoleLogger, SpanContext, diag } from "@opentelemetry/api";
import { Span } from "@opentelemetry/sdk-trace-base";
import { CorrelationContextManager } from "./correlationContextManager";
import { ICorrelationContext, HttpRequest, DistributedTracingModes } from "./types";
import { ICorrelationContext, HttpRequest, DistributedTracingModes, AzureFnContext } from "./types";
import { TelemetryClient } from "./telemetryClient";
import * as Contracts from "../declarations/contracts";
import { Util } from "../shared/util";
Expand Down Expand Up @@ -82,7 +81,7 @@ export function getCorrelationContext(): ICorrelationContext {
* Starts a fresh context or propagates the current internal one.
*/
export function startOperation(
arg1: azureFunctionsTypes.Context | (http.IncomingMessage | azureFunctionsTypes.HttpRequest) | SpanContext | Span,
arg1: AzureFnContext | (http.IncomingMessage | AzureFnContext) | SpanContext | Span,
arg2?: HttpRequest | string
): ICorrelationContext | null {
return CorrelationContextManager.startOperation(arg1, arg2);
Expand Down
80 changes: 49 additions & 31 deletions src/shim/correlationContextManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@

import * as events from "events";
import * as http from "http";
import * as azureFunctionsTypes from "@azure/functions";
import { context, SpanContext, trace, Context, diag } from "@opentelemetry/api";
import { TraceState } from "@opentelemetry/core";
import { Span } from "@opentelemetry/sdk-trace-base";
import { ICorrelationContext, ITraceparent, ITracestate, HttpRequest, ICustomProperties } from "./types";
import { ICorrelationContext, ITraceparent, ITracestate, ICustomProperties, AzureFnContext, AzureFnRequest, AzureFnTraceContext, HttpRequest } from "./types";
import { Util } from "../shared/util";
import { HttpRequestHeaders } from "@azure/functions-old";
import { HttpRequest as AzureFnHttpRequest } from "@azure/functions";
import { Headers } from "undici";


const CONTEXT_NAME = "ApplicationInsights-Context";
Expand Down Expand Up @@ -170,13 +172,13 @@ export class CorrelationContextManager {
* @returns IcorrelationContext object
*/
public static startOperation(
input: azureFunctionsTypes.Context | (http.IncomingMessage | azureFunctionsTypes.HttpRequest) | SpanContext | Span,
request?: HttpRequest | string
input: AzureFnContext | (http.IncomingMessage | AzureFnRequest) | SpanContext | Span,
request?: HttpRequest | string | AzureFnHttpRequest
): ICorrelationContext {
const traceContext = input && (input as azureFunctionsTypes.Context).traceContext || null;
const traceContext = (input && (input as AzureFnContext).traceContext || null) as AzureFnTraceContext;
const span = input && (input as Span).spanContext ? input as Span : null;
const spanContext = input && (input as SpanContext).traceId ? input as SpanContext : null;
const headers = input && (input as http.IncomingMessage | azureFunctionsTypes.HttpRequest).headers;
const headers = input && (input as http.IncomingMessage | AzureFnRequest).headers;

if (span) {
trace.setSpanContext(context.active(), span.spanContext());
Expand All @@ -198,51 +200,67 @@ export class CorrelationContextManager {
let tracestate = null;
if (traceContext) {
// Use the headers on the request from Azure Functions to set the active context
const azureFnRequest = request as azureFunctionsTypes.HttpRequest;
const azureFnRequest = request as AzureFnRequest;

// If the traceparent isn't defined on the azure function headers set it to the request-id
if (azureFnRequest?.headers) {
// If the headers are not an instance of Headers, we're using the old programming model, else use the v4 model
if (azureFnRequest?.headers && !(azureFnRequest.headers instanceof Headers)) {
// request-id is a GUID-based unique identifier for the request
traceparent = azureFnRequest.headers.traceparent ? azureFnRequest.headers.traceparent : azureFnRequest.headers["request-id"];
tracestate = azureFnRequest.headers.tracestate;
traceparent = (azureFnRequest.headers as HttpRequestHeaders).traceparent ? (azureFnRequest.headers as HttpRequestHeaders).traceparent : (azureFnRequest.headers as HttpRequestHeaders)["request-id"];
tracestate = (azureFnRequest.headers as HttpRequestHeaders).tracestate;
} else if (azureFnRequest?.headers && azureFnRequest?.headers instanceof Headers) {
traceparent = azureFnRequest.headers.get("traceparent") || azureFnRequest.headers.get("request-id");
tracestate = azureFnRequest.headers.get("tracestate");
}

if (!traceparent) {
if (!traceparent && traceContext.traceparent) {
traceparent = traceContext.traceparent;
} else if (!traceparent && traceContext.traceParent) {
traceparent = traceContext.traceParent;
}
if (!tracestate) {

if (!tracestate && traceContext.tracestate) {
tracestate = traceContext.tracestate;
} else if (!tracestate && traceContext.traceState) {
tracestate = traceContext.traceState;
}
}

// If headers is defined instead of traceContext, use the headers to set the traceparent and tracestate
if (headers) {
traceparent = headers.traceparent ? headers.traceparent.toString() : null;
tracestate = headers.tracestate ? headers.tracestate.toString() : tracestate;
// If headers is not an instance of Headers, we use the old programming model, otherwise use the old v3 values
if (headers && (headers as HttpRequestHeaders).traceparent) {
traceparent = (headers as HttpRequestHeaders).traceparent ? (headers as HttpRequestHeaders).traceparent.toString() : null;
tracestate = (headers as HttpRequestHeaders).tracestate ? (headers as HttpRequestHeaders).tracestate.toString() : tracestate;
} else if (headers && headers instanceof Headers) {
traceparent = headers.get("traceparent") || headers.get("request-id");
tracestate = headers.get("tracestate");
}

const traceArray: string[] = traceparent?.split("-");

const tracestateObj: TraceState = new TraceState();
tracestate?.split(",").forEach((pair) => {
tracestate?.split(",").forEach((pair: string) => {
const kv = pair.split("=");
tracestateObj.set(kv[0], kv[1]);
});

return this.generateContextObject(
traceArray[1],
traceArray[2],
null,
{
legacyRootId: "",
parentId: "",
spanId: traceArray[2],
traceFlag: "",
traceId: traceArray[1],
version: "00",
},
tracestateObj
);
try {
return this.generateContextObject(
traceArray[1],
traceArray[2],
null,
{
legacyRootId: "",
parentId: "",
spanId: traceArray[2],
traceFlag: "",
traceId: traceArray[1],
version: "00",
},
tracestateObj
);
} catch (error) {
diag.warn("Error creating context object", Util.getInstance().dumpObj(error));
}
}
diag.warn("startOperation was called with invalid arguments");
return null;
Expand Down
51 changes: 29 additions & 22 deletions src/shim/shim-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ class Config implements IConfig {
public noPatchModules: string;
public noDiagnosticChannel: boolean;

// Expose Distro config for further customization, other conflicting configs will take precedence over this.
public azureMonitorOpenTelemetryOptions : AzureMonitorOpenTelemetryOptions;

private _configWarnings: string[];

/**
Expand Down Expand Up @@ -124,29 +127,33 @@ class Config implements IConfig {
* Parse the config property to set the appropriate values on the AzureMonitorOpenTelemetryOptions
*/
public parseConfig(): AzureMonitorOpenTelemetryOptions {
const options: AzureMonitorOpenTelemetryOptions = {
azureMonitorExporterOptions: {
connectionString: this.connectionString,
disableOfflineStorage: false,
},
enableAutoCollectPerformance: true,
enableAutoCollectExceptions: true,
instrumentationOptions: {
http: { enabled: true },
azureSdk: { enabled: true },
mongoDb: { enabled: true },
mySql: { enabled: true },
redis: { enabled: true },
redis4: { enabled: true },
postgreSql: { enabled: true },
bunyan: { enabled: true },
winston: { enabled: true },
const options: AzureMonitorOpenTelemetryOptions = Object.assign(
{
azureMonitorExporterOptions: {
connectionString: this.connectionString,
disableOfflineStorage: false,
},
enableAutoCollectPerformance: true,
enableAutoCollectExceptions: true,
instrumentationOptions: {
http: { enabled: true },
azureSdk: { enabled: true },
mongoDb: { enabled: true },
mySql: { enabled: true },
redis: { enabled: true },
redis4: { enabled: true },
postgreSql: { enabled: true },
bunyan: { enabled: true },
winston: { enabled: true },
},
otlpTraceExporterConfig: {},
otlpMetricExporterConfig: {},
otlpLogExporterConfig: {},
enableLiveMetrics: true,
},
otlpTraceExporterConfig: {},
otlpMetricExporterConfig: {},
otlpLogExporterConfig: {},
enableLiveMetrics: true,
};
this.azureMonitorOpenTelemetryOptions
);

(options.instrumentationOptions as InstrumentationOptions) = {
...options.instrumentationOptions,
console: { enabled: false },
Expand Down
2 changes: 0 additions & 2 deletions src/shim/telemetryClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,6 @@ export class TelemetryClient {
// Warn if any config warnings were generated during parsing
for (let i = 0; i < this._configWarnings.length; i++) {
diag.warn(this._configWarnings[i]);
this._attributeLogProcessor = new AttributeLogProcessor({ ...this.context.tags, ...this.commonProperties });
(logs.getLoggerProvider() as LoggerProvider).addLogRecordProcessor(this._attributeLogProcessor);
}
}
catch (error) {
Expand Down
8 changes: 8 additions & 0 deletions src/shim/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import { TokenCredential } from "@azure/core-auth";
import * as http from "http";
import https = require("https");
import { Context as AzureFnV3Context, HttpRequest as AzureFnV3Request, TraceContext as AzureFnV3TraceContext } from "@azure/functions-old";
import { InvocationContext as AzureFnV4Context, HttpRequest as AzureFnV4Request } from "@azure/functions";

export const UNSUPPORTED_MSG = "Please reference the Azure Monitor OpenTelemetry Migration Doc for more information. If this functionality is required, please revert to Application Insights 2.X SDK.";
export enum DistributedTracingModes {
Expand Down Expand Up @@ -127,6 +129,12 @@ export interface ConnectionString {

export type ConnectionStringKey = "instrumentationkey" | "ingestionendpoint" | "liveendpoint" | "location"| "endpointsuffix";

export type AzureFnContext = AzureFnV3Context | AzureFnV4Context;

export type AzureFnRequest = AzureFnV3Request | AzureFnV4Request;

export type AzureFnTraceContext = AzureFnV3TraceContext & AzureFnV4Context["traceContext"];

export interface IBaseConfig {
/** The ingestion endpoint to send telemetry payloads to */
endpointUrl: string;
Expand Down
4 changes: 2 additions & 2 deletions test/functionalTests/testApp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
"@azure/storage-blob": "^12.1.2",
"@opentelemetry/api": "^1.0.4",
"@opentelemetry/sdk-trace-base": "^1.0.1",
"body-parser": "1.17.2",
"body-parser": "1.20.3",
"bunyan": "1.8.12",
"express": "4.19.2",
"express": "4.20.0",
"mongodb": "^3.1.13",
"mysql": "^2.16.0",
"pg": "^8.6.0",
Expand Down
36 changes: 32 additions & 4 deletions test/unitTests/shim/config.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import http = require("http");
import https = require("https");
import { DistributedTracingModes } from '../../../applicationinsights';
import { checkWarnings } from './testUtils';
import { BatchLogRecordProcessor, ConsoleLogRecordExporter } from '@opentelemetry/sdk-logs';
import { BatchSpanProcessor, ConsoleSpanExporter } from '@opentelemetry/sdk-trace-base';
import { Resource } from '@opentelemetry/resources';

class TestTokenCredential implements azureCoreAuth.TokenCredential {
private _expiresOn: Date;
Expand Down Expand Up @@ -81,7 +84,7 @@ describe("shim/configuration/config", () => {
"winston": { "enabled": true },
"console": { "enabled": true },
}),
"wrong instrumentationOptions");
"wrong instrumentationOptions");
assert.equal(JSON.stringify(options.instrumentationOptions.bunyan), JSON.stringify({ enabled: true }), "wrong bunyan setting");
assert.equal(options.enableAutoCollectExceptions, true, "wrong enableAutoCollectExceptions");
assert.equal(options.enableAutoCollectPerformance, true, "wrong enableAutoCollectPerformance");
Expand Down Expand Up @@ -111,6 +114,31 @@ describe("shim/configuration/config", () => {
assert.equal(options.samplingRatio, 0, "wrong samplingRatio");
});


it("should allow customization of Azure Monitor Distro configuration", () => {
let spanProcessors = [new BatchSpanProcessor(new ConsoleSpanExporter())];
let logRecordProcessors = [new BatchLogRecordProcessor(new ConsoleLogRecordExporter)];
let resource = new Resource({});
const config = new Config(connectionString);
config.azureMonitorOpenTelemetryOptions = {
resource: resource,
enableTraceBasedSamplingForLogs: false,
enableLiveMetrics: false,
enableStandardMetrics: false,
logRecordProcessors: logRecordProcessors,
spanProcessors: spanProcessors
};

let options = config.parseConfig();
assert.equal(options.resource, resource, "wrong resource");
assert.equal(options.enableTraceBasedSamplingForLogs, false, "wrong enableTraceBasedSamplingForLogs");
assert.equal(options.enableLiveMetrics, false, "wrong enableTraceBasedSamplingForLogs");
assert.equal(options.enableStandardMetrics, false, "wrong enableTraceBasedSamplingForLogs");
assert.equal(options.logRecordProcessors, logRecordProcessors, "wrong logRecordProcessors");
assert.equal(options.spanProcessors, spanProcessors, "wrong spanProcessors");
});


it("should activate DEBUG internal logger", () => {
const env = <{ [id: string]: string }>{};
process.env = env;
Expand Down Expand Up @@ -151,15 +179,15 @@ describe("shim/configuration/config", () => {
"postgreSql": { "enabled": false },
"bunyan": { "enabled": false },
"winston": { "enabled": false },
"console":{ "enabled": false },
"console": { "enabled": false },
}));
});

it("should disable specific instrumentations when noPatchModules is set", () => {
const config = new Config(connectionString);
config.noPatchModules = "azuresdk,mongodb-core,redis,pg-pool";
let options = config.parseConfig();
assert.equal(JSON.stringify(options.instrumentationOptions), JSON.stringify({
assert.equal(JSON.stringify(options.instrumentationOptions), JSON.stringify({
http: { enabled: true },
azureSdk: { enabled: false },
mongoDb: { enabled: false },
Expand Down Expand Up @@ -326,7 +354,7 @@ describe("shim/configuration/config", () => {
it("should warn if web instrumentations are set", () => {
const config = new Config(connectionString);
const warnings = config["_configWarnings"];
config.webInstrumentationConfig = [{name: "test", value: true}];
config.webInstrumentationConfig = [{ name: "test", value: true }];
config.webInstrumentationSrc = "test";
config.parseConfig();
assert.ok(checkWarnings("The webInstrumentation config and src options are not supported by the shim.", warnings), "warning was not raised");
Expand Down
Loading

0 comments on commit 9c7f5d6

Please sign in to comment.