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

Add Azure Functions correlation properties #1047

Merged
merged 3 commits into from
Jan 6, 2023
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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