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

Fix duplicate azure functions logs #2579

Merged
merged 12 commits into from
Oct 12, 2022
Merged
Show file tree
Hide file tree
Changes from 7 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.microsoft.applicationinsights.agent.bootstrap;

import io.opentelemetry.context.Context;
import io.opentelemetry.context.ContextKey;
import io.opentelemetry.context.ImplicitContextKeyed;

public final class AzureFunctionsCustomDimensions implements ImplicitContextKeyed {

private static final ContextKey<AzureFunctionsCustomDimensions>
AI_FUNCTIONS_CUSTOM_DIMENSIONS_KEY = ContextKey.named("ai-functions-custom-dimensions");

public final String invocationId;
public final String processId;
public final String logLevel;
public final String category;
public final String hostInstanceId;
public final String azFunctionLiveLogsSessionId;

public AzureFunctionsCustomDimensions(
String invocationId,
String processId,
String logLevel,
String category,
String hostInstanceId,
String azFunctionLiveLogsSessionId) {
this.invocationId = invocationId;
this.processId = processId;
this.logLevel = logLevel;
this.category = category;
this.hostInstanceId = hostInstanceId;
this.azFunctionLiveLogsSessionId = azFunctionLiveLogsSessionId;
}

public static AzureFunctionsCustomDimensions fromContext(Context context) {
return context.get(AI_FUNCTIONS_CUSTOM_DIMENSIONS_KEY);
}

@Override
public Context storeInContext(Context context) {
return context.with(AI_FUNCTIONS_CUSTOM_DIMENSIONS_KEY, this);
}

// TODO to be removed and it's for debugging
@Override
public String toString() {
return "{invocationId:"
+ invocationId
+ ", processId:"
+ processId
+ ", logLevel:"
+ logLevel
+ ", category:"
+ category
+ ", hostInstanceId:"
+ hostInstanceId
+ ", azFunctionLiveLogSessionId:"
+ azFunctionLiveLogsSessionId
+ "}";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.microsoft.applicationinsights.agent.internal.init;

import com.azure.core.util.logging.ClientLogger;
import com.azure.monitor.opentelemetry.exporter.implementation.AiSemanticAttributes;
import com.microsoft.applicationinsights.agent.bootstrap.AzureFunctionsCustomDimensions;
import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.logs.LogProcessor;
import io.opentelemetry.sdk.logs.ReadWriteLogRecord;

public final class AzureFunctionsLogProcessor implements LogProcessor {

private static final ClientLogger logger = new ClientLogger(AzureFunctionsLogProcessor.class);

@Override
public void onEmit(ReadWriteLogRecord logRecord) {
AzureFunctionsCustomDimensions customDimensions =
AzureFunctionsCustomDimensions.fromContext(Context.current());
heyams marked this conversation as resolved.
Show resolved Hide resolved
if (customDimensions == null) {
logger.warning("'ai-functions-custom-dimensions' is missing from the context");
heyams marked this conversation as resolved.
Show resolved Hide resolved
return;
}
logger.verbose(
"####### AzureFunctionsLogProcessor::onEmit:: \n CustomDimensions: {}",
customDimensions.toString());
if (customDimensions.invocationId != null) {
logRecord.setAttribute(
AiSemanticAttributes.AZ_FN_INVOCATION_ID, customDimensions.invocationId);
}
if (customDimensions.processId != null) {
logRecord.setAttribute(AiSemanticAttributes.AZ_FN_PROCESS_ID, customDimensions.processId);
}
if (customDimensions.logLevel != null) {
logRecord.setAttribute(AiSemanticAttributes.AZ_FN_LOG_LEVEL, customDimensions.logLevel);
}
if (customDimensions.category != null) {
logRecord.setAttribute(AiSemanticAttributes.AZ_FN_CATEGORY, customDimensions.category);
}
if (customDimensions.hostInstanceId != null) {
logRecord.setAttribute(
AiSemanticAttributes.AZ_FN_HOST_INSTANCE_ID, customDimensions.hostInstanceId);
}
if (customDimensions.azFunctionLiveLogsSessionId != null) {
logRecord.setAttribute(
AiSemanticAttributes.AZ_FN_LIVE_LOGS_SESSION_ID,
customDimensions.azFunctionLiveLogsSessionId);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,11 @@ private static SdkLogEmitterProviderBuilder configureLogging(
Configuration configuration) {

builder.addLogProcessor(new AzureMonitorLogProcessor());

if ("java".equals(System.getenv("FUNCTIONS_WORKER_RUNTIME"))) {
heyams marked this conversation as resolved.
Show resolved Hide resolved
builder.addLogProcessor(new AzureFunctionsLogProcessor());
}

if (!configuration.preview.inheritedAttributes.isEmpty()) {
builder.addLogProcessor(
new InheritedAttributesLogProcessor(configuration.preview.inheritedAttributes));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,5 +106,17 @@ public final class AiSemanticAttributes {
public static final AttributeKey<Boolean> IS_PRE_AGGREGATED =
AttributeKey.booleanKey("applicationinsights.internal.is_pre_aggregated");

// Azure Function

public static final AttributeKey<String> AZ_FN_INVOCATION_ID =
heyams marked this conversation as resolved.
Show resolved Hide resolved
AttributeKey.stringKey("InvocationId");
public static final AttributeKey<String> AZ_FN_PROCESS_ID = AttributeKey.stringKey("ProcessId");
public static final AttributeKey<String> AZ_FN_LOG_LEVEL = AttributeKey.stringKey("LogLevel");
public static final AttributeKey<String> AZ_FN_CATEGORY = AttributeKey.stringKey("Category");
public static final AttributeKey<String> AZ_FN_HOST_INSTANCE_ID =
AttributeKey.stringKey("HostInstanceId");
public static final AttributeKey<String> AZ_FN_LIVE_LOGS_SESSION_ID =
AttributeKey.stringKey("#AzFuncLiveLogsSessionId");

private AiSemanticAttributes() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;

import com.microsoft.applicationinsights.agent.bootstrap.AzureFunctions;
import com.microsoft.applicationinsights.agent.bootstrap.AzureFunctionsCustomDimensions;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.Map;
import javax.annotation.Nullable;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
Expand All @@ -39,7 +41,12 @@ public void transform(TypeTransformer transformer) {
InvocationInstrumentation.class.getName() + "$ExecuteAdvice");
}

@SuppressWarnings({"unused", "PrivateConstructorForUtilityClass", "MustBeClosedChecker"})
@SuppressWarnings({
"unused",
"PrivateConstructorForUtilityClass",
"MustBeClosedChecker",
"unchecked"
})
public static class ExecuteAdvice {
@Nullable
@Advice.OnMethodEnter(suppress = Throwable.class)
Expand All @@ -56,8 +63,10 @@ public static Scope methodEnter(@Advice.Argument(0) Object request)
.getTextMapPropagator()
.extract(Context.root(), traceContext, GETTER);
SpanContext spanContext = Span.fromContext(extractedContext).getSpanContext();

return Context.current().with(Span.wrap(spanContext)).makeCurrent();
return Context.current()
.with(Span.wrap(spanContext))
trask marked this conversation as resolved.
Show resolved Hide resolved
.with(generateCustomDimensions(request, traceContext))
trask marked this conversation as resolved.
Show resolved Hide resolved
.makeCurrent();
}

@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
Expand All @@ -66,5 +75,21 @@ public static void methodExit(@Advice.Enter @Nullable Scope scope) {
scope.close();
}
}

private static AzureFunctionsCustomDimensions generateCustomDimensions(
Object request, Object traceContext) throws ReflectiveOperationException {
String invocationId =
(String) InvocationRequestExtractAdapter.getInvocationId.invoke(request);
Map<String, String> attributesMap =
(Map<String, String>)
InvocationRequestExtractAdapter.getAttributesMap.invoke(traceContext);
return new AzureFunctionsCustomDimensions(
invocationId,
attributesMap.get("ProcessId"),
attributesMap.get("LogLevel"),
attributesMap.get("Category"),
attributesMap.get("HostInstanceId"),
attributesMap.get("#AzFuncLiveLogsSessionId"));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,15 @@ public class InvocationRequestExtractAdapter implements TextMapGetter<Object> {
new InvocationRequestExtractAdapter();

public static final Method getTraceContextMethod;
public static final Method getInvocationId;
public static final Method getAttributesMap;
private static final Method getTraceParentMethod;
private static final Method getTraceStateMethod;

static {
Method getTraceContextMethodLocal = null;
Method getInvocationIdLocal = null;
Method getAttributesMapLocal = null;
Method getTraceParentMethodLocal = null;
Method getTraceStateMethodLocal = null;
try {
Expand All @@ -33,12 +37,16 @@ public class InvocationRequestExtractAdapter implements TextMapGetter<Object> {
Class<?> rpcTraceContextClass =
Class.forName("com.microsoft.azure.functions.rpc.messages.RpcTraceContext");
getTraceContextMethodLocal = invocationRequestClass.getMethod("getTraceContext");
getInvocationIdLocal = invocationRequestClass.getMethod("getInvocationId");
getAttributesMapLocal = rpcTraceContextClass.getMethod("getAttributesMap");
getTraceParentMethodLocal = rpcTraceContextClass.getMethod("getTraceParent");
getTraceStateMethodLocal = rpcTraceContextClass.getMethod("getTraceState");
} catch (ReflectiveOperationException e) {
logger.log(Level.SEVERE, e.getMessage(), e);
}
getTraceContextMethod = getTraceContextMethodLocal;
getInvocationId = getInvocationIdLocal;
getAttributesMap = getAttributesMapLocal;
getTraceParentMethod = getTraceParentMethodLocal;
getTraceStateMethod = getTraceStateMethodLocal;
}
Expand Down