Skip to content

Commit

Permalink
Add Azure Functions correlation properties (#1047)
Browse files Browse the repository at this point in the history
  • Loading branch information
hectorhdzg authored Jan 6, 2023
1 parent f20cea0 commit c59867a
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 4 deletions.
8 changes: 8 additions & 0 deletions AutoCollection/AzureFunctionsHook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ export class AzureFunctionsHook {
try {
// Start an AI Correlation Context using the provided Function context
extractedContext = CorrelationContextManager.startOperation(ctx, request);
extractedContext.customProperties.setProperty("InvocationId", ctx.invocationId);
if (ctx.traceContext.attributes) {
extractedContext.customProperties.setProperty("ProcessId", ctx.traceContext.attributes["ProcessId"]);
extractedContext.customProperties.setProperty("LogLevel", ctx.traceContext.attributes["LogLevel"]);
extractedContext.customProperties.setProperty("Category", ctx.traceContext.attributes["Category"]);
extractedContext.customProperties.setProperty("HostInstanceId", ctx.traceContext.attributes["HostInstanceId"]);
extractedContext.customProperties.setProperty("AzFuncLiveLogsSessionId", ctx.traceContext.attributes["#AzFuncLiveLogsSessionId"]);
}
}
catch (err) {
Logging.warn("Failed to propagate context in Azure Functions", err);
Expand Down
32 changes: 32 additions & 0 deletions Library/EnvelopeFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ class EnvelopeFactory {
}
}
}
EnvelopeFactory.addAzureFunctionsCorrelationProperties(data.baseData.properties);
if (data.baseData.properties) {
// sanitize properties
data.baseData.properties = Util.validateStringMap(data.baseData.properties);
Expand Down Expand Up @@ -103,6 +104,37 @@ class EnvelopeFactory {
return envelope;
}

private static addAzureFunctionsCorrelationProperties(properties: { [key: string]: string; }) {
var correlationContext = CorrelationContextManager.getCurrentContext();
if (correlationContext && correlationContext.customProperties && correlationContext.customProperties["getProperty"] instanceof Function) {
properties = properties || {}; // Initialize properties if not present
let property = correlationContext.customProperties.getProperty("InvocationId");
if (property) {
properties["InvocationId"] = property;
}
property = correlationContext.customProperties.getProperty("ProcessId");
if (property) {
properties["ProcessId"] = property;
}
property = correlationContext.customProperties.getProperty("LogLevel");
if (property) {
properties["LogLevel"] = property;
}
property = correlationContext.customProperties.getProperty("Category");
if (property) {
properties["Category"] = property;
}
property = correlationContext.customProperties.getProperty("HostInstanceId");
if (property) {
properties["HostInstanceId"] = property;
}
property = correlationContext.customProperties.getProperty("AzFuncLiveLogsSessionId");
if (property) {
properties["AzFuncLiveLogsSessionId"] = property;
}
}
}

private static createTraceData(telemetry: Contracts.TraceTelemetry): Contracts.Data<Contracts.MessageData> {
var trace = new Contracts.MessageData();
trace.message = telemetry.message;
Expand Down
8 changes: 7 additions & 1 deletion Library/Functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,14 @@
* to your function from the Azure Functions runtime on function invocation.
*/
export interface Context {
/**
* A unique GUID per function invocation.
*/
invocationId?: string;
/**
* TraceContext information to enable distributed tracing scenarios.
*/
traceContext: TraceContext;

/**
* HTTP request object. Provided to your function when using HTTP Bindings.
*/
Expand Down
19 changes: 16 additions & 3 deletions Tests/AutoCollection/AzureFunctionsHook.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import assert = require("assert");
import sinon = require("sinon");
import { TelemetryClient } from "../../applicationinsights";
import { AzureFunctionsHook } from "../../AutoCollection/AzureFunctionsHook";
import { CorrelationContextManager } from "../../AutoCollection/CorrelationContextManager";
import { CorrelationContext, CorrelationContextManager } from "../../AutoCollection/CorrelationContextManager";
import { HttpRequest } from "../../Library/Functions";
import Logging = require("../../Library/Logging");

Expand Down Expand Up @@ -66,10 +66,17 @@ describe("AutoCollection/AzureFunctionsHook", () => {
let contextSpy = sandbox.spy(CorrelationContextManager, "wrapCallback");
let ctx = {
res: { "status": 400 },
invocationId: "testinvocationId",
traceContext: {
traceparent: "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01",
tracestate: "",
attributes: {}
attributes: {
"ProcessId": "testProcessId",
"LogLevel": "testLogLevel",
"Category": "testCategory",
"HostInstanceId": "testHostInstanceId",
"#AzFuncLiveLogsSessionId": "testAzFuncLiveLogsSessionId",
}
}
};
let request: HttpRequest = {
Expand All @@ -84,10 +91,16 @@ describe("AutoCollection/AzureFunctionsHook", () => {
assert.ok(originalCallbackCalled);
assert.ok(flushStub.called);
assert.ok(trackRequestSpy.called);
let propagatedContext = contextSpy.args[0][1];
let propagatedContext: CorrelationContext = contextSpy.args[0][1];
assert.equal(propagatedContext.operation.id, "0af7651916cd43dd8448eb211c80319c");
assert.equal(propagatedContext.operation.name, "HEAD /");
assert.equal(propagatedContext.operation.parentId, "|0af7651916cd43dd8448eb211c80319c.b7ad6b7169203331.");
assert.equal(propagatedContext.customProperties.getProperty("InvocationId"), "testinvocationId");
assert.equal(propagatedContext.customProperties.getProperty("ProcessId"), "testProcessId");
assert.equal(propagatedContext.customProperties.getProperty("LogLevel"), "testLogLevel");
assert.equal(propagatedContext.customProperties.getProperty("Category"), "testCategory");
assert.equal(propagatedContext.customProperties.getProperty("HostInstanceId"), "testHostInstanceId");
assert.equal(propagatedContext.customProperties.getProperty("AzFuncLiveLogsSessionId"), "testAzFuncLiveLogsSessionId");
let incomingRequest = trackRequestSpy.args[0][0];
assert.equal(incomingRequest.id, "|0af7651916cd43dd8448eb211c80319c.b7ad6b7169203331.");
assert.equal(incomingRequest.name, "HEAD test.com");
Expand Down
23 changes: 23 additions & 0 deletions Tests/Library/EnvelopeFactoryTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import EnvelopeFactory = require("../../Library/EnvelopeFactory");
import Contracts = require("../../Declarations/Contracts");
import Client = require("../../Library/TelemetryClient");
import Util = require("../../Library/Util");
import { CorrelationContextManager } from "../../AutoCollection/CorrelationContextManager";
import { Context } from "../../Library/Functions";

describe("Library/EnvelopeFactory", () => {

Expand Down Expand Up @@ -78,6 +80,27 @@ describe("Library/EnvelopeFactory", () => {
assert.equal(envData.baseData.properties.prop2, "123");
assert.equal(envData.baseData.properties.prop3, "{\"subProp1\":\"someValue\"}");
});

it("should add Azure Functions correlation properties", function () {
var client = new Client("key");
CorrelationContextManager.enable(true);
let context = CorrelationContextManager.generateContextObject("operationId", "parentId");
context.customProperties.setProperty("InvocationId", "tesvalue1");
context.customProperties.setProperty("ProcessId", "tesvalue2");
context.customProperties.setProperty("LogLevel", "tesvalue3");
context.customProperties.setProperty("Category", "tesvalue4");
context.customProperties.setProperty("HostInstanceId", "tesvalue5");
context.customProperties.setProperty("AzFuncLiveLogsSessionId", "tesvalue6");
CorrelationContextManager.runWithContext(context, () => {
var envelope = EnvelopeFactory.createEnvelope(<Contracts.EventTelemetry>{ name: "name" }, Contracts.TelemetryType.Event, commonproperties, client.context, client.config);
assert.equal((envelope.data as Contracts.Data<Contracts.EventTelemetry>).baseData.properties["InvocationId"], "tesvalue1");
assert.equal((envelope.data as Contracts.Data<Contracts.EventTelemetry>).baseData.properties["ProcessId"], "tesvalue2");
assert.equal((envelope.data as Contracts.Data<Contracts.EventTelemetry>).baseData.properties["LogLevel"], "tesvalue3");
assert.equal((envelope.data as Contracts.Data<Contracts.EventTelemetry>).baseData.properties["Category"], "tesvalue4");
assert.equal((envelope.data as Contracts.Data<Contracts.EventTelemetry>).baseData.properties["HostInstanceId"], "tesvalue5");
assert.equal((envelope.data as Contracts.Data<Contracts.EventTelemetry>).baseData.properties["AzFuncLiveLogsSessionId"], "tesvalue6");
})
});
});

describe("#createDependencyData()", () => {
Expand Down

0 comments on commit c59867a

Please sign in to comment.