Added additional logging (need to make it structired).

Moved deploymnt targets.
This commit is contained in:
macrogreg 2020-04-09 01:27:56 -07:00
Родитель 0fb30cce83
Коммит 0437af8af0
11 изменённых файлов: 293 добавлений и 133 удалений

Просмотреть файл

@ -19,4 +19,7 @@
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\PublishProfiles\" />
</ItemGroup>
</Project>

Просмотреть файл

@ -33,7 +33,9 @@ namespace AvailabilityMonitoring_Extension_DemoFunction
}
}
bool hasExpectedContent = responseContent.Contains("<title>Monitored Page</title>", StringComparison.OrdinalIgnoreCase);
bool hasExpectedContent = responseContent.Contains("<title>Monitored Page</title>", StringComparison.OrdinalIgnoreCase)
&& responseContent.Contains("(App Version Id: 2)", StringComparison.OrdinalIgnoreCase);
testInfo.AvailabilityResult.Success = hasExpectedContent;
}
}

Просмотреть файл

@ -3,7 +3,7 @@
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"APPINSIGHTS_INSTRUMENTATIONKEY": "18765e1c-90ea-4efd-a776-55b942954582",
"APPINSIGHTS_INSTRUMENTATIONKEY": "4f58be6b-a928-45cc-8c72-952a6c85d8d9",
"AvailabilityTest.LocationDisplayName": "Local Testing Environment"
}
}

Просмотреть файл

@ -15,6 +15,10 @@
<WCFMetadata Include="Connected Services" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\PublishProfiles\" />
</ItemGroup>
</Project>

Просмотреть файл

@ -67,7 +67,7 @@ namespace AvailabilityMonitoring_Extension_MonitoredAppSample.Controllers
using (var http = new HttpClient())
{
using (HttpResponseMessage response = await http.GetAsync("https://gregp-cat-test01.azurewebsites.net/api/TimeServer"))
using (HttpResponseMessage response = await http.GetAsync("https://gregp-cat-test11.azurewebsites.net/api/TimeServer"))
{
response.EnsureSuccessStatusCode();
responseContent = await response.Content.ReadAsStringAsync();

Просмотреть файл

@ -3,6 +3,6 @@
}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
<h1 class="display-4">Welcome, Sashechka-Busechka</h1>
<p>Azure Monitor rocks!</p>
</div>

Просмотреть файл

@ -14,6 +14,7 @@
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container">
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">AvailabilityMonitoring-Extension: Monitored App Sample</a>
<b>(App Version Id: 2)</b>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>

Просмотреть файл

@ -1,13 +1,13 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"ApplicationInsights": {
"InstrumentationKey": "6e204f56-fd43-4c08-afda-cd716bf41d49"
}
},
"AllowedHosts": "*",
"ApplicationInsights": {
"InstrumentationKey": "5536af3c-d1fe-49a3-836e-6ce18a829f61"
}
}

Просмотреть файл

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.Azure.WebJobs.Description;
@ -6,6 +7,7 @@ using Microsoft.Azure.WebJobs.Host;
using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Azure.WebJobs.Host.Config;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
@ -15,17 +17,21 @@ namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
{
private readonly IConfiguration _configuration;
private readonly INameResolver _nameResolver;
//private readonly ILogger _log;
public AvailabilityMonitoringExtensionConfigProvider(IConfiguration configuration, INameResolver nameResolver)
{
_configuration = configuration;
_nameResolver = nameResolver;
//_log = log;
}
public void Initialize(ExtensionConfigContext context)
{
Validate.NotNull(context, nameof(context));
//_log?.LogInformation("Initializing Availability Monitoring Extension.");
// FluentBindingRule<ApiAvailabilityTest> is marked as Obsolete, yet it is the type returned from AddBindingRule(..)
// We could use "var", but one should NEVER use "var" except in Lync expressions
// or when the type is clear from the *same* line to an unfamiliar reader.
@ -68,10 +74,34 @@ namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
private AvailabilityTestInfo CreateAndRegisterInvocation(AvailabilityTestAttribute attribute, ValueBindingContext context, Type functionParameterType)
{
AvailabilityTestInfo availabilityTestInfo = CreateAvailabilityTestInfo(attribute, context);
//IDisposable logScope = _log?.BeginScope(new Dictionary<string, string>()
//{
// ["Microsoft.Azure.AvailabilityMonitoring.FunctionInstanceId"] = Format.Guid(context.FunctionInstanceId),
//});
FunctionInvocationStateCache.SingeltonInstance.RegisterFunctionInvocation(context.FunctionInstanceId, availabilityTestInfo, functionParameterType);
return availabilityTestInfo;
//try
//{
//_log?.LogInformation($"Creating an Availability Test parameter of type \"{functionParameterType.Name}\" from an"
// + $" {nameof(AvailabilityTestAttribute)}("
// + $"{nameof(AvailabilityTestAttribute.TestDisplayName)}=\"{Format.NotNullOrWord(attribute.TestDisplayName)}\","
// + $"{nameof(AvailabilityTestAttribute.LocationDisplayName)}=\"{Format.NotNullOrWord(attribute.LocationDisplayName)}\","
// + $"{nameof(AvailabilityTestAttribute.LocationId)}=\"{Format.NotNullOrWord(attribute.LocationId)}\").");
AvailabilityTestInfo availabilityTestInfo = CreateAvailabilityTestInfo(attribute, context);
FunctionInvocationStateCache.SingeltonInstance.RegisterFunctionInvocation(context.FunctionInstanceId, availabilityTestInfo, functionParameterType);
//_log?.LogInformation($"Resolved settings and created a {nameof(AvailabilityTestInfo)}:"
// + $" {nameof(AvailabilityTestInfo.TestDisplayName)}=\"{Format.NotNullOrWord(availabilityTestInfo.TestDisplayName)}\","
// + $" {nameof(AvailabilityTestInfo.LocationDisplayName)}=\"{Format.NotNullOrWord(availabilityTestInfo.LocationDisplayName)}\","
// + $" {nameof(AvailabilityTestInfo.LocationId)}=\"{Format.NotNullOrWord(availabilityTestInfo.LocationId)}\".");
return availabilityTestInfo;
//}
//finally
//{
// logScope?.Dispose();
//}
}
private AvailabilityTestInfo CreateAvailabilityTestInfo(AvailabilityTestAttribute attribute, ValueBindingContext context)

Просмотреть файл

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http.Headers;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.ApplicationInsights;
@ -18,15 +19,11 @@ namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
#pragma warning restore CS0618 // Type or member is obsolete (Filter-related types are obsolete, but we want to use them)
{
private readonly TelemetryClient _telemetryClient;
private readonly ILogger _log;
public FunctionInvocationManagementFilter(TelemetryClient telemetryClient, ILogger log)
{
Validate.NotNull(telemetryClient, nameof(telemetryClient));
Validate.NotNull(log, nameof(log));
_telemetryClient = telemetryClient;
_log = log;
}
#pragma warning disable CS0618 // Type or member is obsolete (Filter-related types are obsolete, but we want to use them)
@ -45,45 +42,82 @@ namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
return Task.CompletedTask;
}
IdentifyManagedParameters(invocationState, executingContext.Arguments);
ILogger log = executingContext.Logger;
string activitySpanId;
// Start activity:
string activityName = Format.NotNullOrWord(invocationState.ActivitySpanName);
invocationState.ActivitySpan = new Activity(activityName).Start();
string activitySpanId = invocationState.ActivitySpan.SpanId.ToHexString();
// Start the timer:
DateTimeOffset startTime = DateTimeOffset.Now;
// Look at every paramater and update it with the activity ID and the start time:
foreach (FunctionInvocationState.Parameter regRaram in invocationState.Parameters.Values)
IDisposable logScope = log?.BeginScope(new Dictionary<string, string>()
{
["Microsoft.Azure.AvailabilityMonitoring.FunctionInstanceId"] = Format.Guid(executingContext.FunctionInstanceId),
});
try
{
regRaram.AvailabilityTestInfo.AvailabilityResult.Id = activitySpanId;
log?.LogInformation("Coded Availability Test setup started.");
if (regRaram.Type.Equals(typeof(AvailabilityTestInfo)))
IdentifyManagedParameters(invocationState, executingContext.Arguments, log);
// Start activity:
string activityName = Format.NotNullOrWord(invocationState.ActivitySpanName);
invocationState.ActivitySpan = new Activity(activityName).Start();
activitySpanId = invocationState.ActivitySpan.SpanId.ToHexString();
log?.LogInformation($"Started activity (name=\"{activityName}\", spanId=\"{activitySpanId}\")."
+ $" Coded Availability Test setup completed."
+ $" About to invoke Coded Availabillity Test.");
}
finally
{
logScope?.Dispose();
}
invocationState.LoggerScope = log?.BeginScope(new Dictionary<string, string>()
{
["Microsoft.Azure.AvailabilityMonitoring.FunctionInstanceId"] = Format.Guid(executingContext.FunctionInstanceId),
["Microsoft.Azure.AvailabilityMonitoring.TestDisplayName"] = Format.NotNullOrWord(invocationState.FirstParameter?.AvailabilityTestInfo?.TestDisplayName),
["Microsoft.Azure.AvailabilityMonitoring.LocationDisplayName"] = Format.NotNullOrWord(invocationState.FirstParameter?.AvailabilityTestInfo?.LocationDisplayName),
["Microsoft.Azure.AvailabilityMonitoring.LocationId"] = Format.NotNullOrWord(invocationState.FirstParameter?.AvailabilityTestInfo?.LocationId)
});
try
{
// Start the timer:
DateTimeOffset startTime = DateTimeOffset.Now;
// Look at every paramater and update it with the activity ID and the start time:
foreach (FunctionInvocationState.Parameter regRaram in invocationState.Parameters.Values)
{
AvailabilityTestInfo actParam = (AvailabilityTestInfo) executingContext.Arguments[regRaram.Name];
actParam.AvailabilityResult.Id = activitySpanId;
actParam.SetStartTime(startTime);
}
else if (regRaram.Type.Equals(typeof(AvailabilityTelemetry)))
{
AvailabilityTelemetry actParam = (AvailabilityTelemetry) executingContext.Arguments[regRaram.Name];
actParam.Id = activitySpanId;
actParam.Timestamp = startTime.ToUniversalTime();
}
else if (regRaram.Type.Equals(typeof(JObject)))
{
JObject actParam = (JObject) executingContext.Arguments[regRaram.Name];
actParam["AvailabilityResult"]["Id"].Replace(JToken.FromObject(activitySpanId));
actParam["StartTime"].Replace(JToken.FromObject(startTime));
actParam["AvailabilityResult"]["Timestamp"].Replace(JToken.FromObject(startTime.ToUniversalTime()));
}
else
{
throw new InvalidOperationException($"Unexpected managed parameter type: \"{regRaram.Type.FullName}\".");
regRaram.AvailabilityTestInfo.AvailabilityResult.Id = activitySpanId;
if (regRaram.Type.Equals(typeof(AvailabilityTestInfo)))
{
AvailabilityTestInfo actParam = (AvailabilityTestInfo) executingContext.Arguments[regRaram.Name];
actParam.AvailabilityResult.Id = activitySpanId;
actParam.SetStartTime(startTime);
}
else if (regRaram.Type.Equals(typeof(AvailabilityTelemetry)))
{
AvailabilityTelemetry actParam = (AvailabilityTelemetry) executingContext.Arguments[regRaram.Name];
actParam.Id = activitySpanId;
actParam.Timestamp = startTime.ToUniversalTime();
}
else if (regRaram.Type.Equals(typeof(JObject)))
{
JObject actParam = (JObject) executingContext.Arguments[regRaram.Name];
actParam["AvailabilityResult"]["Id"].Replace(JToken.FromObject(activitySpanId));
actParam["StartTime"].Replace(JToken.FromObject(startTime));
actParam["AvailabilityResult"]["Timestamp"].Replace(JToken.FromObject(startTime.ToUniversalTime()));
}
else
{
throw new InvalidOperationException($"Unexpected managed parameter type: \"{regRaram.Type.FullName}\".");
}
}
}
catch(Exception ex)
{
invocationState.LoggerScope.Dispose();
invocationState.ActivitySpan.Stop();
ExceptionDispatchInfo.Capture(ex).Throw();
}
// Done:
return Task.CompletedTask;
@ -108,88 +142,147 @@ namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
// Measure user time (plus the minimal runtime overhead within the bracket of this binding):
DateTimeOffset endTime = DateTimeOffset.Now;
// Stop activity:
string activitySpadId = invocationState.ActivitySpan.SpanId.ToHexString();
invocationState.ActivitySpan.Stop();
ILogger log = executedContext.Logger;
// Get Function result (failed or not):
bool errorOcurred = ! executedContext.FunctionResult.Succeeded;
Exception error = errorOcurred
? executedContext.FunctionResult.Exception
: null;
// Stop logging scope:
invocationState.LoggerScope?.Dispose();
// Look at every paramater that was originally tagged with the attribute:
foreach (FunctionInvocationState.Parameter registeredRaram in invocationState.Parameters.Values)
IDisposable logScope = log?.BeginScope(new Dictionary<string, string>()
{
["Microsoft.Azure.AvailabilityMonitoring.FunctionInstanceId"] = Format.Guid(invocationState.FunctionInstanceId),
["Microsoft.Azure.AvailabilityMonitoring.TestDisplayName"] = Format.NotNullOrWord(invocationState.FirstParameter?.AvailabilityTestInfo?.TestDisplayName),
["Microsoft.Azure.AvailabilityMonitoring.LocationDisplayName"] = Format.NotNullOrWord(invocationState.FirstParameter?.AvailabilityTestInfo?.LocationDisplayName),
["Microsoft.Azure.AvailabilityMonitoring.LocationId"] = Format.NotNullOrWord(invocationState.FirstParameter?.AvailabilityTestInfo?.LocationId)
});
try
{
// Find the actual parameter value in the function arguments (named lookup):
if (false == executedContext.Arguments.TryGetValue(registeredRaram.Name, out object functionOutputParam))
log?.LogInformation("Coded Availability Test completed. Processing results.");
// Stop activity:
string activitySpadId = invocationState.ActivitySpan.SpanId.ToHexString();
invocationState.ActivitySpan.Stop();
// Get Function result (failed or not):
bool errorOcurred = ! executedContext.FunctionResult.Succeeded;
Exception error = errorOcurred
? executedContext.FunctionResult.Exception
: null;
log?.LogInformation($"Stopped activity (name=\"{ invocationState.ActivitySpan.OperationName}\", spanId=\"{activitySpadId}\"). Overall CAT result: " +
((errorOcurred || error != null)
? $"errorOcurred={errorOcurred}; error=\"{error.GetType().Name}: \'{error.Message}\'\""
: $"errorOcurred={errorOcurred}"));
log?.LogInformation($"Starting to process {invocationState.Parameters.Count} result-parameters tagged with {nameof(AvailabilityTestAttribute)}.");
List<Exception> processingErrors = null;
// Look at every paramater that was originally tagged with the attribute:
foreach (FunctionInvocationState.Parameter registeredRaram in invocationState.Parameters.Values)
{
throw new InvalidOperationException($"A parameter with name \"{Format.NotNullOrWord(registeredRaram?.Name)}\" and"
+ $" type \"{Format.NotNullOrWord(registeredRaram?.Type)}\" was registered for"
+ $" the function \"{Format.NotNullOrWord(executedContext?.FunctionName)}\", but it was not found in the"
+ $" actual argument list after the function invocation.");
}
if (functionOutputParam == null)
{
throw new InvalidOperationException($"A parameter with name \"{Format.NotNullOrWord(registeredRaram?.Name)}\" and"
+ $" type \"{Format.NotNullOrWord(registeredRaram?.Type)}\" was registered for"
+ $" the function \"{Format.NotNullOrWord(executedContext?.FunctionName)}\", and the corresponding value in the"
+ $" actual argument list after the function invocation was null.");
}
// Based on parameter type, convert it to AvailabilityTestInfo and then process:
bool functionOutputParamProcessed = false;
{
// If this argument is a AvailabilityTestInfo:
var testInfoParameter = functionOutputParam as AvailabilityTestInfo;
if (testInfoParameter != null)
try
{
ProcessOutputParameter(endTime, errorOcurred, error, testInfoParameter, activitySpadId, cancelControl);
functionOutputParamProcessed = true;
}
}
log?.LogInformation($"Starting to process result-parameter of type \"{registeredRaram.Type.Name}\".");
{
// If this argument is a AvailabilityTelemetry:
var availabilityResultParameter = functionOutputParam as AvailabilityTelemetry;
if (availabilityResultParameter != null)
{
AvailabilityTestInfo testInfoParameter = Convert.AvailabilityTelemetryToAvailabilityTestInvocation(availabilityResultParameter);
ProcessOutputParameter(endTime, errorOcurred, error, testInfoParameter, activitySpadId, cancelControl);
functionOutputParamProcessed = true;
}
}
{
// If this argument is a JObject:
var jObjectParameter = functionOutputParam as JObject;
if (jObjectParameter != null)
{
// Can jObjectParameter be cnverted to a AvailabilityTestInfo (null if not):
AvailabilityTestInfo testInfoParameter = Convert.JObjectToAvailabilityTestInvocation(jObjectParameter);
if (testInfoParameter != null)
// Find the actual parameter value in the function arguments (named lookup):
if (false == executedContext.Arguments.TryGetValue(registeredRaram.Name, out object functionOutputParam))
{
ProcessOutputParameter(endTime, errorOcurred, error, testInfoParameter, activitySpadId, cancelControl);
functionOutputParamProcessed = true;
throw new InvalidOperationException($"A parameter with name \"{Format.NotNullOrWord(registeredRaram?.Name)}\" and"
+ $" type \"{Format.NotNullOrWord(registeredRaram?.Type)}\" was registered for"
+ $" the function \"{Format.NotNullOrWord(executedContext?.FunctionName)}\", but it was not found in the"
+ $" actual argument list after the function invocation.");
}
if (functionOutputParam == null)
{
throw new InvalidOperationException($"A parameter with name \"{Format.NotNullOrWord(registeredRaram?.Name)}\" and"
+ $" type \"{Format.NotNullOrWord(registeredRaram?.Type)}\" was registered for"
+ $" the function \"{Format.NotNullOrWord(executedContext?.FunctionName)}\", and the corresponding value in the"
+ $" actual argument list after the function invocation was null.");
}
// Based on parameter type, convert it to AvailabilityTestInfo and then process:
bool functionOutputParamProcessed = false;
{
// If this argument is a AvailabilityTestInfo:
var testInfoParameter = functionOutputParam as AvailabilityTestInfo;
if (testInfoParameter != null)
{
ProcessOutputParameter(endTime, errorOcurred, error, testInfoParameter, activitySpadId, cancelControl);
functionOutputParamProcessed = true;
}
}
{
// If this argument is a AvailabilityTelemetry:
var availabilityResultParameter = functionOutputParam as AvailabilityTelemetry;
if (availabilityResultParameter != null)
{
AvailabilityTestInfo testInfoParameter = Convert.AvailabilityTelemetryToAvailabilityTestInvocation(availabilityResultParameter);
ProcessOutputParameter(endTime, errorOcurred, error, testInfoParameter, activitySpadId, cancelControl);
functionOutputParamProcessed = true;
}
}
{
// If this argument is a JObject:
var jObjectParameter = functionOutputParam as JObject;
if (jObjectParameter != null)
{
// Can jObjectParameter be cnverted to a AvailabilityTestInfo (null if not):
AvailabilityTestInfo testInfoParameter = Convert.JObjectToAvailabilityTestInvocation(jObjectParameter);
if (testInfoParameter != null)
{
ProcessOutputParameter(endTime, errorOcurred, error, testInfoParameter, activitySpadId, cancelControl);
functionOutputParamProcessed = true;
}
}
}
if (false == functionOutputParamProcessed)
{
throw new InvalidOperationException($"A parameter with name \"{Format.NotNullOrWord(registeredRaram?.Name)}\" and"
+ $" type \"{Format.NotNullOrWord(registeredRaram?.Type)}\" was registered for"
+ $" the function \"{Format.NotNullOrWord(executedContext?.FunctionName)}\", and the corresponding value in the"
+ $" actual argument list after the function invocation cannot be processed"
+ $" ({Format.NotNullOrWord(functionOutputParam?.GetType()?.Name)}).");
}
log?.LogInformation($"Completed to process result-parameter of type \"{registeredRaram.Type.Name}\".");
}
catch(Exception ex)
{
log?.LogInformation($"Error while processing result-parameter of type \"{registeredRaram.Type.Name}\".");
processingErrors = processingErrors ?? new List<Exception>();
processingErrors.Add(ex);
}
}
if (false == functionOutputParamProcessed)
log?.LogInformation($"Completed processing result-parameters. Errors: {(processingErrors == null ? 0 : processingErrors.Count)}.");
// Make sure everyting we trracked is put on the wire, in case the Function runtime shuts down:
_telemetryClient.Flush();
if (processingErrors != null)
{
throw new InvalidOperationException($"A parameter with name \"{Format.NotNullOrWord(registeredRaram?.Name)}\" and"
+ $" type \"{Format.NotNullOrWord(registeredRaram?.Type)}\" was registered for"
+ $" the function \"{Format.NotNullOrWord(executedContext?.FunctionName)}\", and the corresponding value in the"
+ $" actual argument list after the function invocation cannot be processed"
+ $" ({Format.NotNullOrWord(functionOutputParam?.GetType()?.Name)}).");
if (processingErrors.Count == 1)
{
ExceptionDispatchInfo.Capture(processingErrors[0]).Throw();
}
else
{
throw new AggregateException($"Failed to process {processingErrors.Count} out of {invocationState.Parameters.Count}"
+ $" result-parameters tagged with {nameof(AvailabilityTestAttribute)}.",
processingErrors);
}
}
}
// Make sure everyting we trracked is put on the wire, in case the Function runtime shuts down:
_telemetryClient.Flush();
finally
{
logScope?.Dispose();
}
return Task.CompletedTask;
}
@ -199,7 +292,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
bool errorOcurred,
Exception error,
AvailabilityTestInfo functionOutputParam,
string activitySpadId,
string activitySpanId,
CancellationToken cancelControl)
{
if (errorOcurred)
@ -234,12 +327,15 @@ namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
// Send the availability result to the backend:
Format.RemoveAvailabilityTestInfoIdentity(functionOutputParam.AvailabilityResult);
functionOutputParam.AvailabilityResult.Id = activitySpadId;
functionOutputParam.AvailabilityResult.Id = activitySpanId;
_telemetryClient.TrackAvailability(functionOutputParam.AvailabilityResult);
}
private void IdentifyManagedParameters(FunctionInvocationState invocationState, IReadOnlyDictionary<string, object> actualFunctionParameters)
private void IdentifyManagedParameters(FunctionInvocationState invocationState, IReadOnlyDictionary<string, object> actualFunctionParameters, ILogger log)
{
log?.LogInformation($"Starting to process {actualFunctionParameters.Count} function parameters."
+ $" Expecting to identify {invocationState.Parameters.Count} {nameof(AvailabilityTestAttribute)}-tagged parameters.");
int identifiedParameterCount = 0;
// Look at each argument:
if (actualFunctionParameters != null)
@ -260,7 +356,8 @@ namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
// Find registered parameter with the right ID, validate, and store its name:
Guid actualFunctionParameterId = testInfoParameter.Identity;
if (TryIdentifyAndValidateManagedParameter(invocationState, actualFunctionParameter.Key, actualFunctionParameter.Value, actualFunctionParameterId))
{
{
log?.LogInformation($"{nameof(AvailabilityTestAttribute)}-tagged parameter indentified. Runtime type: \"{testInfoParameter.GetType().Name}\".");
identifiedParameterCount++;
}
}
@ -275,6 +372,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
Guid actualFunctionParameterId = Format.GetAvailabilityTestInfoIdentity(availabilityResultParameter);
if (TryIdentifyAndValidateManagedParameter(invocationState, actualFunctionParameter.Key, actualFunctionParameter.Value, actualFunctionParameterId))
{
log?.LogInformation($"{nameof(AvailabilityTestAttribute)}-tagged parameter indentified. Runtime type: \"{availabilityResultParameter.GetType().Name}\".");
identifiedParameterCount++;
}
}
@ -293,6 +391,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
Guid actualFunctionParameterId = testInfoParameter.Identity;
if (TryIdentifyAndValidateManagedParameter(invocationState, actualFunctionParameter.Key, actualFunctionParameter.Value, actualFunctionParameterId))
{
log?.LogInformation($"{nameof(AvailabilityTestAttribute)}-tagged parameter indentified. Runtime type: \"{jObjectParameter.GetType().Name}\".");
identifiedParameterCount++;
}
}
@ -301,6 +400,9 @@ namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
}
}
log?.LogInformation($"Completed processing {actualFunctionParameters.Count} function parameters."
+ $" {identifiedParameterCount} marked with {nameof(AvailabilityTestAttribute)} identified.");
if (identifiedParameterCount != invocationState.Parameters.Count)
{
throw new InvalidOperationException($"{invocationState.Parameters.Count} parameters were marked with the {nameof(AvailabilityTestAttribute)},"

Просмотреть файл

@ -34,7 +34,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
private readonly ConcurrentDictionary<Guid, FunctionInvocationState.Parameter> _paremeters = new ConcurrentDictionary<Guid, FunctionInvocationState.Parameter>();
private int _currentStage = (int)FunctionInvocationState.Stage.New;
private string _activitySpanName = null;
private Parameter _firstParameter = null;
public Guid FunctionInstanceId { get; }
public string FormattedFunctionInstanceId { get { return Format.Guid (FunctionInstanceId); } }
@ -46,12 +46,31 @@ namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
public DateTimeOffset StartTime { get; set; }
public Activity ActivitySpan { get; set; }
public IDisposable LoggerScope { get; set; }
public FunctionInvocationState.Parameter FirstParameter { get { return _firstParameter; } }
public string ActivitySpanName
{
get
{
Parameter firstParam = _firstParameter;
if (firstParam == null)
{
return null;
}
return Format.ActivitySpanName(firstParam.AvailabilityTestInfo.TestDisplayName, firstParam.AvailabilityTestInfo.LocationDisplayName);
}
}
public string ActivitySpanName { get { return _activitySpanName; } }
public FunctionInvocationState(Guid functionInstanceId)
{
this.FunctionInstanceId = functionInstanceId;
this.ActivitySpan = null;
this.LoggerScope = null;
}
public void AddManagedParameter(AvailabilityTestInfo availabilityTestInfo, Type functionParameterType)
@ -65,10 +84,9 @@ namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
+ $" {Stage.New}; however, {nameof(CurrentStage)} is {CurrentStage}.");
}
string activitySpanName = Format.ActivitySpanName(availabilityTestInfo.TestDisplayName, availabilityTestInfo.LocationDisplayName);
Interlocked.CompareExchange(ref _activitySpanName, activitySpanName, null);
_paremeters.TryAdd(availabilityTestInfo.Identity, new Parameter(availabilityTestInfo, functionParameterType));
var parameterInfo = new Parameter(availabilityTestInfo, functionParameterType);
Interlocked.CompareExchange(ref _firstParameter, parameterInfo, null);
_paremeters.TryAdd(availabilityTestInfo.Identity, parameterInfo);
}
public void Transition(FunctionInvocationState.Stage from, FunctionInvocationState.Stage to)