Added additional logging (need to make it structired).
Moved deploymnt targets.
This commit is contained in:
Родитель
0fb30cce83
Коммит
0437af8af0
|
@ -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)
|
||||
|
|
Загрузка…
Ссылка в новой задаче