in src/Microsoft.Azure.WebJobs.Logging.ApplicationInsights/ApplicationInsightsLogger.cs [496:591]
private void StartTelemetryIfFunctionInvocation(IDictionary<string, object> stateValues)
{
if (stateValues == null)
{
return;
}
var allScopes = DictionaryLoggerScope.GetMergedStateDictionaryOrNull();
// HTTP and ServiceBus triggers are tracked automatically by the ApplicationInsights SDK
// In such case a current Activity is present.
// We won't track and only stamp function specific details on the RequestTelemetry
// created by SDK via Activity when function ends
var currentActivity = Activity.Current;
if (currentActivity == null ||
// Activity is tracked, but Functions wants to ignore it:
(allScopes != null && allScopes.ContainsKey("MS_IgnoreActivity")) ||
// Functions create another RequestTrackingTelemetryModule to make sure first request is tracked (as ASP.NET Core starts before web jobs)
// however at this point we may discover that RequestTrackingTelemetryModule is disabled by customer and even though Activity exists, request won't be tracked
// So, if we've got AspNetCore Activity and EnableHttpTriggerExtendedInfoCollection is false - track request here.
(!_loggerOptions.HttpAutoCollectionOptions.EnableHttpTriggerExtendedInfoCollection && IsHttpRequestActivity(currentActivity)))
{
string functionName = stateValues.GetValueOrDefault<string>(ScopeKeys.FunctionName);
string functionInvocationId = stateValues.GetValueOrDefault<string>(ScopeKeys.FunctionInvocationId);
string eventName = stateValues.GetValueOrDefault<string>(ScopeKeys.Event);
// If we have the invocation id, function name, and event, we know it's a new function. That means
// that we want to start a new operation and let App Insights track it for us.
if (!string.IsNullOrEmpty(functionName) &&
!string.IsNullOrEmpty(functionInvocationId) &&
eventName == LogConstants.FunctionStartEvent)
{
IOperationHolder<RequestTelemetry> operation;
// link represents context from the upstream service that is not necessarily immediate parent
// it is used by EventHubs to represent context in the message.
// if there is just one link, we'll use it as a parent as an optimization.
// if there is more than one, we'll populate them as custom properties
IEnumerable<Activity> links = allScopes?.GetValueOrDefault<IEnumerable<Activity>>("Links");
var activities = links as Activity[] ?? links?.ToArray();
if (activities != null)
{
if (activities.Length == 1)
{
operation = _telemetryClient.StartOperation<RequestTelemetry>(activities[0]);
operation.Telemetry.Name = functionName;
}
else
{
operation = CreateRequestFromLinks(activities, functionName);
}
if (this.TryGetAverageTimeInQueueForBatch(activities, operation.Telemetry.Timestamp, out long enqueuedTime))
{
operation.Telemetry.Metrics["timeSinceEnqueued"] = enqueuedTime;
}
}
else
{
operation = _telemetryClient.StartOperation<RequestTelemetry>(functionName);
}
var triggerDetails = stateValues.GetValueOrDefault<IDictionary<string, string>>(ScopeKeys.TriggerDetails);
if (triggerDetails != null)
{
triggerDetails.TryGetValue(LogConstants.TriggerDetailsEndpointKey, out var endpoint);
triggerDetails.TryGetValue(LogConstants.TriggerDetailsEntityNameKey, out var entity);
if (endpoint != null && entity != null)
{
operation.Telemetry.Source = endpoint.EndsWith("/") ? string.Concat(endpoint, entity) : string.Concat(endpoint, "/", entity);
}
else if (endpoint != null)
{
operation.Telemetry.Source = endpoint;
}
else if (entity != null)
{
operation.Telemetry.Source = entity;
}
}
// We'll need to store this operation context so we can stop it when the function completes
stateValues[OperationContext] = operation;
}
}
// If there is a current activity, it is assumed that Application Insights will track it so we do not start an operation.
// However, in some cases (such as Durable functions), this is not the case. This allows the scope to decide whether
// an operation should be started, even when the current activity is not null.
else if (allScopes != null && allScopes.ContainsKey("MS_TrackActivity"))
{
var operation = _telemetryClient.StartOperation<RequestTelemetry>(currentActivity);
stateValues[OperationContext] = operation;
}
}