@dynatrace/opentelemetry-azure-functions
Version:
Dynatrace OpenTelemetry package for Azure Functions
180 lines (177 loc) • 8.2 kB
JavaScript
;
/*
Copyright 2024 Dynatrace LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.initDynatrace = exports.registerTraceEndHook = exports.registerTraceStartHook = exports.configureDynatrace = exports._testClearEndHookRegistered = void 0;
const util_1 = require("util");
const functions_1 = require("@azure/functions");
const opentelemetry_core_1 = require("@dynatrace/opentelemetry-core");
const api_1 = require("@opentelemetry/api");
const AttributeDetector_1 = require("./AttributeDetector");
const Logger_1 = require("./Logger");
const ResourceDetector_1 = require("./ResourceDetector");
const Util_1 = require("./Util");
// ============================================================================
const cDtSpanSymbol = Symbol("Dt Span");
let endHookRegistered = false;
// only exported for testing
function _testClearEndHookRegistered() {
endHookRegistered = false;
}
exports._testClearEndHookRegistered = _testClearEndHookRegistered;
// ============================================================================
/**
* Creates and registers TracerProvider with all required components to export traces to Dynatrace.
*
* @param resource Resource passed to TracerProvider.
*
* @returns registered NodeTracerProvider
*/
const configureDynatrace = function (resource = {}) {
if (Logger_1.logger.debugEnabled) {
Logger_1.logger.debug(`Configuring dynatrace. Resource: ${(0, util_1.inspect)(resource)}`);
}
return (0, opentelemetry_core_1.configureDynatrace)(resource);
};
exports.configureDynatrace = configureDynatrace;
// ============================================================================
/**
* Extracts trigger type and input from the provided context
* @param context PreInvocationContext containing trigger info
*
* @returns extracted trigger (FaasTriggerValues) and input
*/
function getTriggerData(context) {
if (context.invocationContext.options.trigger.type === "httpTrigger") {
return { triggerType: opentelemetry_core_1.SemConvTrace.FaasTriggerValues.HTTP, req: context.inputs[0] };
}
else {
return { triggerType: opentelemetry_core_1.SemConvTrace.FaasTriggerValues.OTHER, req: undefined };
}
}
// ============================================================================
/**
* Returns status code of the Azure Function.
*
* If status is not set it defaults to 200:
* https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-node?tabs=javascript%2Cwindows%2Cazure-cli&pivots=nodejs-model-v4#http-response
*
* If no response object is given status defaults to 204.
*
* @param context PreInvocationContext containing result of the Azure Function execution
*
* @returns Http status code returned from the Azure Function
*/
function getStatus(context) {
let status;
const res = context.result;
if (res != null) {
status = res.status != null ? +res.status : 200;
}
else {
status = 204;
}
if (Logger_1.logger.debugEnabled) {
Logger_1.logger.debug(`${opentelemetry_core_1.SemConvTrace.HTTP_STATUS_CODE}: ${status}`);
}
return status;
}
// ============================================================================
/**
* Registers AzureFunction preInvocation hook for span start
*
* Use with Azure Function Programming Model V4 only.
*/
function registerTraceStartHook() {
if (Logger_1.logger.debugEnabled) {
Logger_1.logger.debug("registerTraceStartHook");
}
functions_1.app.hook.preInvocation((context) => {
var _a;
if (Logger_1.logger.debugEnabled) {
Logger_1.logger.debug(`Entered preInvocation hook for function ${context.invocationContext.functionName}. Trigger type: ${context.invocationContext.options.trigger.type}.`);
}
if (!endHookRegistered) {
Logger_1.logger.info("TraceEndHook not rigestered, skip starting the trace.");
return;
}
const resourceAttributes = (0, ResourceDetector_1.detectResource)(context.invocationContext.functionName);
const { triggerType, req } = getTriggerData(context);
const azureHttpRequest = (0, Util_1.isAzureHttpRequest)(req) ? req : undefined;
const parentCtx = (0, Util_1.extractContext)(triggerType, azureHttpRequest);
const span = (0, Util_1.getTracer)().startSpan((_a = context.invocationContext.functionName) !== null && _a !== void 0 ? _a : "invoke", {
kind: api_1.SpanKind.SERVER,
attributes: Object.assign(Object.assign({}, resourceAttributes), (0, AttributeDetector_1.getStartAttributes)(triggerType, azureHttpRequest))
}, parentCtx);
(0, opentelemetry_core_1.setPropagatedResourceAttributes)(span, resourceAttributes);
context.functionHandler = api_1.context.bind(api_1.trace.setSpan(parentCtx, span), context.functionHandler);
context.hookData[cDtSpanSymbol] = span;
});
}
exports.registerTraceStartHook = registerTraceStartHook;
// ============================================================================
/**
* Registers AzureFunction postInvocation hook for span end.
*
* Use with Azure Function Programming Model V4 only.
*/
function registerTraceEndHook() {
if (Logger_1.logger.debugEnabled) {
Logger_1.logger.debug("registerTraceEndHook");
}
functions_1.app.hook.postInvocation((context) => {
if (Logger_1.logger.debugEnabled) {
Logger_1.logger.debug(`Entered postInvocation hook for function ${context.invocationContext.functionName}. Trigger type: ${context.invocationContext.options.trigger.type}`);
}
const span = context.hookData[cDtSpanSymbol];
if (span != null) {
if (context.invocationContext.options.trigger.type === "httpTrigger") {
const status = getStatus(context);
span.setAttribute(opentelemetry_core_1.SemConvTrace.HTTP_STATUS_CODE, status);
}
if (context.error != null) {
(0, opentelemetry_core_1.addErrorAttributes)(span, context.error);
span.setStatus({ code: api_1.SpanStatusCode.ERROR });
}
span.end();
}
});
endHookRegistered = true;
}
exports.registerTraceEndHook = registerTraceEndHook;
// ============================================================================
/**
* It registers AzureFunctions TraceStartHook and TraceEndHook to start and end function span.
* Optionally, configures OpenTelemetry tracing components and registers a TracerProvider.
*
* Use with Azure Function Programming Model V4 only.
*
* @note In case other hooks need to be registered in the same AzureFunctions app,
* consider using registerTraceStartHook, registerTraceEndHook to order them properly.
* To properly trace monitored function the TraceStartHook hook must be the first preInvocation hook in the app.
* Correspondingly the TraceEndHook must be the last postInvocation hook in the app.
*
* @param configure If set to true, configures required OTel components for tracing and returns registered NodeTracerProvider.
* Otherwise, it doesn't return a tracer provider and the components must be configured manually or by calling configureDynatrace(). Default: false.
*
* @param resource Resource passed to TracerProvider.
*/
function initDynatrace(configure = false, resource = {}) {
registerTraceStartHook();
registerTraceEndHook();
if (configure) {
return (0, exports.configureDynatrace)(resource);
}
}
exports.initDynatrace = initDynatrace;
//# sourceMappingURL=AzureFunctionsInstrumentationV4.js.map