Validated that engine rewrite works with JavaScript and fixed related bugs.

This commit is contained in:
macrogreg 2020-05-06 01:12:45 -07:00
Родитель 8ecbe1a1e5
Коммит 1669a87d74
8 изменённых файлов: 150 добавлений и 192 удалений

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

@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
namespace Microsoft.Azure.AvailabilityMonitoring
{
@ -63,11 +65,54 @@ namespace Microsoft.Azure.AvailabilityMonitoring
}
}
internal static string LocationNameAsId(string locationDisplayName)
public static string LocationNameAsId(string locationDisplayName)
{
string locationId = locationDisplayName?.Trim()?.ToLowerInvariant()?.Replace(' ', '-');
return locationId;
}
public static string LimitLength(object value, int maxLength, bool trim)
{
string valueStr = value?.ToString();
return LimitLength(valueStr, maxLength, trim);
}
public static string LimitLength(string value, int maxLength, bool trim)
{
if (maxLength < 0)
{
throw new ArgumentException($"{nameof(maxLength)} may not be smaller than zero, but it was {maxLength}.");
}
const string FillStr = "...";
int fillStrLen = FillStr.Length;
value = SpellIfNull(value);
value = trim ? value.Trim() : value;
int valueLen = value.Length;
if (valueLen <= maxLength)
{
return value;
}
if (maxLength < fillStrLen + 2)
{
string superShortResult = value.Substring(0, maxLength);
return superShortResult;
}
int postLen = (maxLength - fillStrLen) / 2;
int preLen = maxLength - fillStrLen - postLen;
string postStr = value.Substring(valueLen - postLen, postLen);
string preStr = value.Substring(0, preLen);
var shortResult = new StringBuilder(preStr, maxLength);
shortResult.Append(FillStr);
shortResult.Append(postStr);
return shortResult.ToString();
}
}
}

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

@ -28,7 +28,6 @@ namespace Microsoft.Azure.AvailabilityMonitoring
}
}
[EditorBrowsable(EditorBrowsableState.Never)]
public static Logging Log { get { return AvailabilityTest.Logging.SingeltonInstance; } }
@ -42,14 +41,6 @@ namespace Microsoft.Azure.AvailabilityMonitoring
return StartNew(testConfig, telemetryConfig, flushOnDispose, log, logScope: null);
}
internal static AvailabilityTestScope StartNew(IAvailabilityTestConfiguration testConfig,
TelemetryClient telemetryClient,
bool flushOnDispose,
ILogger log)
{
return StartNew(testConfig, telemetryClient, flushOnDispose, log, logScope: null);
}
internal static AvailabilityTestScope StartNew(IAvailabilityTestConfiguration testConfig,
TelemetryConfiguration telemetryConfig,
bool flushOnDispose,
@ -60,16 +51,6 @@ namespace Microsoft.Azure.AvailabilityMonitoring
return StartNew(testConfig.TestDisplayName, testConfig.LocationDisplayName, testConfig.LocationId, telemetryConfig, flushOnDispose, log, logScope);
}
internal static AvailabilityTestScope StartNew(IAvailabilityTestConfiguration testConfig,
TelemetryClient telemetryClient,
bool flushOnDispose,
ILogger log,
object logScope)
{
Validate.NotNull(testConfig, nameof(testConfig));
return StartNew(testConfig.TestDisplayName, testConfig.LocationDisplayName, testConfig.LocationId, telemetryClient, flushOnDispose, log, logScope);
}
public static AvailabilityTestScope StartNew(string testDisplayName,
string locationDisplayName,
TelemetryConfiguration telemetryConfig,
@ -81,16 +62,6 @@ namespace Microsoft.Azure.AvailabilityMonitoring
return StartNew(testDisplayName, locationDisplayName, locationId, telemetryConfig, flushOnDispose, log, logScope: null);
}
public static AvailabilityTestScope StartNew(string testDisplayName,
string locationDisplayName,
TelemetryClient telemetryClient,
bool flushOnDispose,
ILogger log)
{
string locationId = Format.LocationNameAsId(locationDisplayName);
return StartNew(testDisplayName, locationDisplayName, locationId, telemetryClient, flushOnDispose, log, logScope: null);
}
public static AvailabilityTestScope StartNew(string testDisplayName,
string locationDisplayName,
string locationId,
@ -102,16 +73,6 @@ namespace Microsoft.Azure.AvailabilityMonitoring
return StartNew(testDisplayName, locationDisplayName, locationId, telemetryConfig, flushOnDispose, log, logScope: null);
}
public static AvailabilityTestScope StartNew(string testDisplayName,
string locationDisplayName,
string locationId,
TelemetryClient telemetryClient,
bool flushOnDispose,
ILogger log)
{
return StartNew(testDisplayName, locationDisplayName, locationId, telemetryClient, flushOnDispose, log, logScope: null);
}
public static AvailabilityTestScope StartNew(string testDisplayName,
string locationDisplayName,
string locationId,
@ -120,24 +81,10 @@ namespace Microsoft.Azure.AvailabilityMonitoring
ILogger log,
object logScope)
{
Validate.NotNull(telemetryConfig, nameof(telemetryConfig));
var telemetryClient = new TelemetryClient(telemetryConfig);
return StartNew(testDisplayName, locationDisplayName, locationId, telemetryClient, flushOnDispose,log, logScope);
}
public static AvailabilityTestScope StartNew(string testDisplayName,
string locationDisplayName,
string locationId,
TelemetryClient telemetryClient,
bool flushOnDispose,
ILogger log,
object logScope)
{
log = AvailabilityTest.Log.CreateFallbackLogIfRequired(log);
var testScope = new AvailabilityTestScope(testDisplayName, locationDisplayName, locationId, telemetryClient, flushOnDispose, log, logScope);
var testScope = new AvailabilityTestScope(testDisplayName, locationDisplayName, locationId, telemetryConfig, flushOnDispose, log, logScope);
testScope.Start();
return testScope;

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

@ -3,8 +3,10 @@ using System.Diagnostics;
using System.Threading;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
namespace Microsoft.Azure.AvailabilityMonitoring
{
@ -36,10 +38,7 @@ namespace Microsoft.Azure.AvailabilityMonitoring
private const string PropertyName_AssociatedAvailabilityResult_Id = "AssociatedAvailabilityResult.Id";
private const string PropertyName_AssociatedAvailabilityResult_IsTimeout = "AssociatedAvailabilityResult.IsTimeout";
private readonly string _testDisplayName;
private readonly string _locationDisplayName;
private readonly string _locationId;
private readonly string _instrumentationKey;
private readonly TelemetryClient _telemetryClient;
private readonly bool _flushOnDispose;
private readonly ILogger _log;
@ -55,24 +54,26 @@ namespace Microsoft.Azure.AvailabilityMonitoring
public AvailabilityTestScope.Stage CurrentStage { get { return (AvailabilityTestScope.Stage) _currentStage; } }
public string TestDisplayName { get { return _testDisplayName; } }
public string TestDisplayName { get; }
public string LocationDisplayName { get { return _locationDisplayName; } }
public string LocationDisplayName { get; }
public string LocationId { get { return _locationId; } }
public string LocationId { get; }
public AvailabilityTestScope(string testDisplayName, string locationDisplayName, string locationId, TelemetryClient telemetryClient, bool flushOnDispose, ILogger log, object logScope)
public AvailabilityTestScope(string testDisplayName, string locationDisplayName, string locationId, TelemetryConfiguration telemetryConfig, bool flushOnDispose, ILogger log, object logScope)
{
Validate.NotNullOrWhitespace(testDisplayName, nameof(testDisplayName));
Validate.NotNullOrWhitespace(locationDisplayName, nameof(locationDisplayName));
Validate.NotNullOrWhitespace(locationId, nameof(locationId));
Validate.NotNull(telemetryClient, nameof(telemetryClient));
Validate.NotNull(telemetryConfig, nameof(telemetryConfig));
_testDisplayName = testDisplayName;
_locationDisplayName = locationDisplayName;
_locationId = locationId;
this.TestDisplayName = testDisplayName;
this.LocationDisplayName = locationDisplayName;
this.LocationId = locationId;
_instrumentationKey = telemetryConfig.InstrumentationKey;
_telemetryClient = new TelemetryClient(telemetryConfig);
_telemetryClient = telemetryClient;
_flushOnDispose = flushOnDispose;
_log = log;
@ -87,12 +88,12 @@ namespace Microsoft.Azure.AvailabilityMonitoring
{
_log?.LogInformation($"{nameof(AvailabilityTestScope)}.{nameof(Start)} beginning:"
+ " {{TestDisplayName=\"{TestDisplayName}\", LocationDisplayName=\"{LocationDisplayName}\", LocationId=\"{LocationId}\"}}",
_testDisplayName, _locationDisplayName, _locationId);
TestDisplayName, LocationDisplayName, LocationId);
TransitionStage(from: Stage.New, to: Stage.Started);
// Start activity:
string activityOperationName = Format.SpanOperationName(_testDisplayName, _locationDisplayName);
string activityOperationName = Format.SpanOperationName(TestDisplayName, LocationDisplayName);
_activitySpan = new Activity(activityOperationName).Start();
_activitySpanId = Format.SpanId(_activitySpan);
@ -102,7 +103,7 @@ namespace Microsoft.Azure.AvailabilityMonitoring
_log?.LogInformation($"{nameof(AvailabilityTestScope)}.{nameof(Start)} finished:"
+ " {{TestDisplayName=\"{TestDisplayName}\", LocationDisplayName=\"{LocationDisplayName}\", LocationId=\"{LocationId}\","
+ " SpanId=\"{SpanId}\", StartTime=\"{StartTime}\", OperationName=\"{OperationName}\"}}",
_testDisplayName, _locationDisplayName, _locationId,
TestDisplayName, LocationDisplayName, LocationId,
_activitySpanId, _startTime.ToString("o"), activityOperationName);
}
}
@ -115,7 +116,7 @@ namespace Microsoft.Azure.AvailabilityMonitoring
+ " {{TestDisplayName=\"{TestDisplayName}\", LocationDisplayName=\"{LocationDisplayName}\", LocationId=\"{LocationId}\","
+ " SpanId=\"{SpanId}\"}}",
success,
_testDisplayName, _locationDisplayName, _locationId,
TestDisplayName, LocationDisplayName, LocationId,
Format.SpellIfNull(_activitySpanId));
EnsureStage(Stage.Started);
@ -138,7 +139,7 @@ namespace Microsoft.Azure.AvailabilityMonitoring
+ " SpanId=\"{SpanId}\"}}."
+ " This indicates that the test result was not set by calling Complete(..); a Failure will be assumed.",
disposing,
_testDisplayName, _locationDisplayName, _locationId,
TestDisplayName, LocationDisplayName, LocationId,
Format.SpellIfNull(_activitySpanId));
EnsureStage(Stage.Started);
@ -162,11 +163,12 @@ namespace Microsoft.Azure.AvailabilityMonitoring
{
using (_log.BeginScopeSafe(_logScope))
{
_log?.LogInformation($"{nameof(AvailabilityTestScope)}.{nameof(Complete)} invoked with ExceptionType={{ExceptionType}} and IsTimeout={{IsTimeout}}:"
+ " {{TestDisplayName=\"{TestDisplayName}\", LocationDisplayName=\"{LocationDisplayName}\", LocationId=\"{LocationId}\","
+ " SpanId=\"{SpanId}\"}}",
Format.SpellIfNull(error?.GetType()?.Name), isTimeout,
_testDisplayName, _locationDisplayName, _locationId,
_log?.LogInformation($"{nameof(AvailabilityTestScope)}.{nameof(Complete)} invoked with"
+ " (ExceptionType={ExceptionType}, ExceptionMessage=\"{ExceptionMessage}\", IsTimeout={IsTimeout}):"
+ " {{TestDisplayName=\"{TestDisplayName}\", LocationDisplayName=\"{LocationDisplayName}\", LocationId=\"{LocationId}\","
+ " SpanId=\"{SpanId}\"}}",
Format.SpellIfNull(error?.GetType()?.Name), Format.LimitLength(error.Message, 100, trim: true), isTimeout,
TestDisplayName, LocationDisplayName, LocationId,
Format.SpellIfNull(_activitySpanId));
EnsureStage(Stage.Started);
@ -201,7 +203,7 @@ namespace Microsoft.Azure.AvailabilityMonitoring
_log?.LogInformation($"{nameof(AvailabilityTestScope)}.{nameof(Complete)} beginning:"
+ " {{TestDisplayName=\"{TestDisplayName}\", LocationDisplayName=\"{LocationDisplayName}\", LocationId=\"{LocationId}\","
+ " SpanId=\"{SpanId}\"}}",
_testDisplayName, _locationDisplayName, _locationId,
TestDisplayName, LocationDisplayName, LocationId,
Format.SpellIfNull(_activitySpanId));
Validate.NotNull(availabilityResult, nameof(availabilityResult));
@ -254,28 +256,28 @@ namespace Microsoft.Azure.AvailabilityMonitoring
if (String.IsNullOrWhiteSpace(availabilityResult.Name))
{
availabilityResult.Name = _testDisplayName;
availabilityResult.Name = TestDisplayName;
}
else if (! availabilityResult.Name.Equals(_testDisplayName, StringComparison.Ordinal))
else if (! availabilityResult.Name.Equals(TestDisplayName, StringComparison.Ordinal))
{
_log?.LogDebug($"{nameof(AvailabilityTestScope)}.{nameof(Complete)} (SpanId=\"{{SpanId}}\") detected that the Name of the"
+ $" specified Availability Result is different from the corresponding value of this {nameof(AvailabilityTestScope)}."
+ $" The value specified in the Availability Result takes precedence for tracking."
+ " AvailabilityTestScope_TestDisplayName=\"{AvailabilityTestScope_TestDisplayName}\". AvailabilityResult_Name=\"{AvailabilityResult_Name}\"",
_activitySpanId, _testDisplayName, availabilityResult.Name);
+ " AvailabilityTestScopeTestDisplayName=\"{AvailabilityTestScope_TestDisplayName}\". AvailabilityResult_Name=\"{AvailabilityResult_Name}\"",
_activitySpanId, TestDisplayName, availabilityResult.Name);
}
if (String.IsNullOrWhiteSpace(availabilityResult.RunLocation))
{
availabilityResult.RunLocation = _locationDisplayName;
availabilityResult.RunLocation = LocationDisplayName;
}
else if (! availabilityResult.RunLocation.Equals(_locationDisplayName, StringComparison.Ordinal))
else if (! availabilityResult.RunLocation.Equals(LocationDisplayName, StringComparison.Ordinal))
{
_log?.LogDebug($"{nameof(AvailabilityTestScope)}.{nameof(Complete)} (SpanId=\"{{SpanId}}\") detected that the RunLocation of the"
+ $" specified Availability Result is different from the corresponding value of this {nameof(AvailabilityTestScope)}."
+ $" The value specified in the Availability Result takes precedence for tracking."
+ " AvailabilityTestScope_LocationDisplayName=\"{AvailabilityTestScope_LocationDisplayName}\". AvailabilityResult_RunLocation=\"{AvailabilityResult_RunLocation}\"",
_activitySpanId, _locationDisplayName, availabilityResult.RunLocation);
_activitySpanId, LocationDisplayName, availabilityResult.RunLocation);
}
if (! availabilityResult.Properties.TryGetValue(PropertyName_WebtestLocationId, out string availabilityResultLocationId))
@ -285,15 +287,15 @@ namespace Microsoft.Azure.AvailabilityMonitoring
if (String.IsNullOrWhiteSpace(availabilityResultLocationId))
{
availabilityResult.Properties[PropertyName_WebtestLocationId] = _locationId;
availabilityResult.Properties[PropertyName_WebtestLocationId] = LocationId;
}
else if (! availabilityResultLocationId.Equals(_locationId, StringComparison.Ordinal))
else if (! availabilityResultLocationId.Equals(LocationId, StringComparison.Ordinal))
{
_log?.LogDebug($"{nameof(AvailabilityTestScope)}.{nameof(Complete)} (SpanId=\"{{SpanId}}\") detected that the WebtestLocationId of the"
+ $" specified Availability Result is different from the corresponding value of this {nameof(AvailabilityTestScope)}."
+ $" The value specified in the Availability Result takes precedence for tracking."
+ " AvailabilityTestScope_LocationId=\"{AvailabilityTestScope_LocationId}\". AvailabilityResult_WebtestLocationId=\"{AvailabilityResult_WebtestLocationId}\"",
_activitySpanId, _locationId, availabilityResultLocationId);
_activitySpanId, LocationId, availabilityResultLocationId);
}
// The user may or may not have set the ID of the availability result telemetry.
@ -306,7 +308,7 @@ namespace Microsoft.Azure.AvailabilityMonitoring
_log?.LogInformation($"{nameof(AvailabilityTestScope)}.{nameof(Complete)} finished"
+ " {{TestDisplayName=\"{TestDisplayName}\", LocationDisplayName=\"{LocationDisplayName}\", LocationId=\"{LocationId}\","
+ " SpanId=\"{SpanId}\", StartTime=\"{StartTime}\", EndTime=\"{EndTime}\", Duration=\"{Duration}\", Success=\"{Success}\"}}",
_testDisplayName, _locationDisplayName, _locationId,
TestDisplayName, LocationDisplayName, LocationId,
_activitySpanId, _startTime.ToString("o"), _endTime.ToString("o"), duration, availabilityResult.Success);
}
}
@ -314,7 +316,7 @@ namespace Microsoft.Azure.AvailabilityMonitoring
public AvailabilityTestInfo CreateAvailabilityTestInfo()
{
AvailabilityTelemetry defaultAvailabilityResult = CreateDefaultAvailabilityResult();
var testInfo = new AvailabilityTestInfo(_testDisplayName, _locationDisplayName, _locationId, _startTime, defaultAvailabilityResult);
var testInfo = new AvailabilityTestInfo(TestDisplayName, LocationDisplayName, LocationId, _startTime, defaultAvailabilityResult);
return testInfo;
}
@ -359,7 +361,7 @@ namespace Microsoft.Azure.AvailabilityMonitoring
_log?.LogInformation($"{nameof(AvailabilityTestScope)}.{nameof(Dispose)} beginning:"
+ " {{TestDisplayName=\"{TestDisplayName}\", LocationDisplayName=\"{LocationDisplayName}\", LocationId=\"{LocationId}\","
+ " SpanId=\"{SpanId}\", CurrentStage=\"{CurrentStage}\", Disposing=\"{Disposing}\"}}",
_testDisplayName, _locationDisplayName, _locationId,
TestDisplayName, LocationDisplayName, LocationId,
Format.SpellIfNull(_activitySpanId), stage, disposing);
switch (stage)
@ -386,7 +388,7 @@ namespace Microsoft.Azure.AvailabilityMonitoring
_log?.LogInformation($"{nameof(AvailabilityTestScope)}.{nameof(Dispose)} finished:"
+ " {{TestDisplayName=\"{TestDisplayName}\", LocationDisplayName=\"{LocationDisplayName}\", LocationId=\"{LocationId}\","
+ " SpanId=\"{SpanId}\", CurrentStage=\"{CurrentStage}\", Disposing=\"{Disposing}\"}}",
_testDisplayName, _locationDisplayName, _locationId,
TestDisplayName, LocationDisplayName, LocationId,
Format.SpellIfNull(_activitySpanId), CurrentStage, disposing);
}
}
@ -396,7 +398,7 @@ namespace Microsoft.Azure.AvailabilityMonitoring
_log?.LogInformation($"{nameof(AvailabilityTestScope)}.{nameof(SendResult)} beginning:"
+ " {{TestDisplayName=\"{TestDisplayName}\", LocationDisplayName=\"{LocationDisplayName}\", LocationId=\"{LocationId}\","
+ " SpanId=\"{SpanId}\"}}",
_testDisplayName, _locationDisplayName, _locationId,
TestDisplayName, LocationDisplayName, LocationId,
Format.SpellIfNull(_activitySpanId));
TransitionStage(from: Stage.Completed, to: Stage.SentResults);
@ -415,7 +417,7 @@ namespace Microsoft.Azure.AvailabilityMonitoring
_log?.LogInformation($"{nameof(AvailabilityTestScope)}.{nameof(SendResult)} finished:"
+ " {{TestDisplayName=\"{TestDisplayName}\", LocationDisplayName=\"{LocationDisplayName}\", LocationId=\"{LocationId}\","
+ " SpanId=\"{SpanId}\"}}",
_testDisplayName, _locationDisplayName, _locationId,
TestDisplayName, LocationDisplayName, LocationId,
Format.SpellIfNull(_activitySpanId));
}
@ -426,7 +428,7 @@ namespace Microsoft.Azure.AvailabilityMonitoring
_log?.LogInformation($"{nameof(AvailabilityTestScope)} is flushing its {nameof(TelemetryClient)}:"
+ " {{TestDisplayName=\"{TestDisplayName}\", LocationDisplayName=\"{LocationDisplayName}\", LocationId=\"{LocationId}\","
+ " SpanId=\"{SpanId}\", CurrentStage=\"{CurrentStage}\", FlushOnDispose=\"{FlushOnDispose}\"}}",
_testDisplayName, _locationDisplayName, _locationId,
TestDisplayName, LocationDisplayName, LocationId,
Format.SpellIfNull(_activitySpanId), CurrentStage, _flushOnDispose);
_telemetryClient.Flush();
@ -436,7 +438,7 @@ namespace Microsoft.Azure.AvailabilityMonitoring
_log?.LogInformation($"{nameof(AvailabilityTestScope)} is NOT flushing its {nameof(TelemetryClient)}:"
+ " {{TestDisplayName=\"{TestDisplayName}\", LocationDisplayName=\"{LocationDisplayName}\", LocationId=\"{LocationId}\","
+ " SpanId=\"{SpanId}\", CurrentStage=\"{CurrentStage}\", FlushOnDispose=\"{FlushOnDispose}\"}}",
_testDisplayName, _locationDisplayName, _locationId,
TestDisplayName, LocationDisplayName, LocationId,
Format.SpellIfNull(_activitySpanId), CurrentStage, _flushOnDispose);
}
}
@ -467,8 +469,8 @@ namespace Microsoft.Azure.AvailabilityMonitoring
availabilityResult.Duration = TimeSpan.Zero;
availabilityResult.Success = false;
availabilityResult.Name = _testDisplayName;
availabilityResult.RunLocation = _locationDisplayName;
availabilityResult.Name = TestDisplayName;
availabilityResult.RunLocation = LocationDisplayName;
availabilityResult.Properties["WebtestLocationId"] = this.LocationId;
//availabilityResult.Properties["SyntheticMonitorId"] = $"default_{this.TestArmResourceName}_{this.LocationId}";
@ -479,6 +481,11 @@ namespace Microsoft.Azure.AvailabilityMonitoring
// + $"/features/{this.TestArmResourceName}"
// + $"/locations/{this.LocationId}";
if (! String.IsNullOrWhiteSpace(_instrumentationKey))
{
availabilityResult.Context.InstrumentationKey = _instrumentationKey;
}
return availabilityResult;
}

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

@ -53,11 +53,6 @@ namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
FluentBindingRule<AvailabilityTestInfoAttribute> testInfoRule = extensionConfigContext.AddBindingRule<AvailabilityTestInfoAttribute>();
#pragma warning restore CS0618
// This binding provider exists soley to allow us to inspect functions to determine whether they are Availability Tests.
// It uses BindingProviderContext, which is not available when binding via BindToCollector(..) as
// we do for the actual return value below.
//testResultRule.Bind(new AvailabilityTestDiscoveryBindingProvider(_availabilityTestRegistry, _log));
// This binding is used to get and process the return value of the function:
testResultRule.BindToCollector<AvailabilityTestResultAttribute, AvailabilityTelemetry>(CreateAvailabilityTelemetryAsyncCollector);
testResultRule.BindToCollector<AvailabilityTestResultAttribute, bool>(CreateBoolAsyncCollector);

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

@ -1,60 +0,0 @@
using System;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Azure.AvailabilityMonitoring;
using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Extensions.Logging;
namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
{
/// <summary>
///This binding provider exists soley to inspect functions in order to determine whether they are an Availability Tests.
///
/// It uses BindingProviderContext, which is not available when binding via BindToCollector(..) as
/// we do for the actual return value below.
///
/// Attention!:
/// This works only for in-proc, .Net-based Functions.
/// For out-of-proc runtimes this approach does not work, there, return-value bindings
/// may be evaluated after the function user code completes.
/// For out-of-proc runtimes, the AvailabilityTestInvocationManager will read the
/// function metadata to determine whether the function is an Availability Test.
/// </summary>
internal class AvailabilityTestDiscoveryBindingProvider : IBindingProvider
{
private readonly AvailabilityTestRegistry _availabilityTestRegistry;
private readonly ILogger _log;
public AvailabilityTestDiscoveryBindingProvider(AvailabilityTestRegistry availabilityTestRegistry, ILogger log)
{
Validate.NotNull(availabilityTestRegistry, nameof(availabilityTestRegistry));
_availabilityTestRegistry = availabilityTestRegistry;
_log = log;
}
public Task<IBinding> TryCreateAsync(BindingProviderContext bindingProviderContext)
{
ParameterInfo parameter = bindingProviderContext?.Parameter;
AvailabilityTestResultAttribute attribute = parameter?.GetCustomAttribute<AvailabilityTestResultAttribute>(inherit: false);
if (attribute != null &&parameter.Member is MethodInfo methodInfo)
{
string functionName = GetFunctionName(methodInfo);
_availabilityTestRegistry.Functions.Register(functionName, attribute, _log);
}
return Task.FromResult<IBinding>(null);
}
private static string GetFunctionName(MethodInfo methodInfo)
{
// The Function name returned by this method MUST be the same as passed to invocation filters!
// This is the same code as used by the WebJobs SDK for this.
FunctionNameAttribute functionNameAttribute = methodInfo.GetCustomAttribute<FunctionNameAttribute>();
return (functionNameAttribute != null) ? functionNameAttribute.Name : $"{methodInfo.DeclaringType.Name}.{methodInfo.Name}";
}
}
}

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

@ -187,7 +187,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
// Type 'FunctionInvocationContext' (and other Filter-related types) is marked as preview/obsolete,
// but the guidance from the Azure Functions team is to use it, so we disable the warning.
#pragma warning disable CS0618
private void GetTestConfigFromMetadata(string functionName,
private static void GetTestConfigFromMetadata(string functionName,
FunctionInvocationContext functionInvocationContext,
ILogger log,
out bool isAvailabilityTest,
@ -257,7 +257,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
// Type 'FunctionInvocationContext' (and other Filter-related types) is marked as preview/obsolete,
// but the guidance from the Azure Functions team is to use it, so we disable the warning.
#pragma warning disable CS0618
private string ReadFunctionMetadataFile(FunctionInvocationContext functionInvocationContext)
private static string ReadFunctionMetadataFile(FunctionInvocationContext functionInvocationContext)
#pragma warning restore CS0618
{
// We will do very verbose error checking and logging via exceptions here to aid supportability
@ -283,10 +283,10 @@ namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
throw new InvalidOperationException(NeedContextArgumentErrorPrefix + " Such entry exists, but the value is null.");
}
string functionAppDir;
string metadataFilePath;
if (execContextObj is ExecutionContext execContext)
{
functionAppDir = execContext.FunctionAppDirectory ?? String.Empty;
metadataFilePath = GetFullFunctionMetadataPath(execContext);
}
else
{
@ -294,15 +294,37 @@ namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
+ $" Such entry exists, but is has the wrong type (\"{execContextObj.GetType().Name}\").");
}
string metadataFilePath = Path.Combine(functionAppDir, "function.json");
if (! File.Exists(metadataFilePath))
{
throw new InvalidOperationException($"Determined that the Metadata File Path the this Function is \"{metadataFilePath}\", but that file does not exist.");
}
string metadataFileContent = File.ReadAllText(metadataFilePath);
return metadataFileContent;
}
private static string GetFullFunctionMetadataPath(ExecutionContext execContext)
{
const string functionJson = "function.json";
string functionDir = execContext.FunctionDirectory ?? String.Empty;
string metadataFilePathInFuncDir = Path.Combine(functionDir, functionJson);
if (File.Exists(metadataFilePathInFuncDir))
{
return metadataFilePathInFuncDir;
}
// We did not find function.json where it should be (in FunctionDirectory).
// Let us attempt to look in FunctionAppDirectory as a fallback.
// @ToDo: Is this reqired / safe?
string functionAppDir = execContext.FunctionAppDirectory ?? String.Empty;
string metadataFilePathInAppDir = Path.Combine(functionAppDir, functionJson);
if (File.Exists(metadataFilePathInAppDir))
{
return metadataFilePathInAppDir;
}
throw new InvalidOperationException($"Looked for the Function Metadata File (\"{functionJson}\") first in"
+ $" \"{metadataFilePathInFuncDir}\" and then in \"{metadataFilePathInAppDir}\","
+ " but that file does not exist.");
}
}
}

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

@ -4,6 +4,7 @@ using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.Azure.AvailabilityMonitoring;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Configuration;
@ -18,16 +19,16 @@ 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 AvailabilityTestRegistry _availabilityTestRegistry;
private readonly TelemetryClient _telemetryClient;
private readonly TelemetryConfiguration _telemetryConfiguration;
private readonly AvailabilityTestScopeSettingsResolver _availabilityTestScopeSettingsResolver;
public FunctionInvocationManagementFilter(AvailabilityTestRegistry availabilityTestRegistry, TelemetryClient telemetryClient, IConfiguration configuration, INameResolver nameResolver)
public FunctionInvocationManagementFilter(AvailabilityTestRegistry availabilityTestRegistry, TelemetryConfiguration telemetryConfiguration, IConfiguration configuration, INameResolver nameResolver)
{
Validate.NotNull(availabilityTestRegistry, nameof(availabilityTestRegistry));
Validate.NotNull(telemetryClient, nameof(telemetryClient));
Validate.NotNull(telemetryConfiguration, nameof(telemetryConfiguration));
_availabilityTestRegistry = availabilityTestRegistry;
_telemetryClient = telemetryClient;
_telemetryConfiguration = telemetryConfiguration;
_availabilityTestScopeSettingsResolver = new AvailabilityTestScopeSettingsResolver(configuration, nameResolver);
}
@ -112,7 +113,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
IAvailabilityTestConfiguration resolvedTestConfig = _availabilityTestScopeSettingsResolver.Resolve(testConfig, functionName);
// Start the availability test scope (this will start timers and set up the activity span):
AvailabilityTestScope testScope = AvailabilityTest.StartNew(resolvedTestConfig, _telemetryClient, flushOnDispose: true, log, logScopeInfo);
AvailabilityTestScope testScope = AvailabilityTest.StartNew(resolvedTestConfig, _telemetryConfiguration, flushOnDispose: true, log, logScopeInfo);
invocationState.AttachTestScope(testScope);
// If we have previously instantiated a result collector, initialize it now:
@ -215,15 +216,16 @@ namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
// If configured, use a fall-back logger:
log = AvailabilityTest.Log.CreateFallbackLogIfRequired(log);
const int MaxErrorMessageLength = 100;
IReadOnlyDictionary<string, object> logScopeInfo = LogMonikers.Scopes.CreateForTestInvocation(functionName);
using (log?.BeginScopeSafe(logScopeInfo))
{
log?.LogDebug($"Availability Test Post-Function error handling routine (via {entryPointName}) beginning:"
+ " {{FunctionName=\"{FunctionName}\", FunctionInstanceId=\"{FunctionInstanceId}\","
+ " ErrorType=\"{ErrorType}\"}}",
+ " ErrorType=\"{ErrorType}\", ErrorMessage=\"{ErrorMessage}\"}}",
functionName, functionInstanceId,
Format.SpellIfNull(error?.GetType()?.Name));
Format.SpellIfNull(error?.GetType()?.Name), Format.LimitLength(error?.Message, MaxErrorMessageLength, trim: true));
// A function is an Availability Test iff is has a return value marked with [AvailabilityTestResult];
// whereas a [AvailabilityTestInfo] is optional to get test information at runtime.
@ -238,9 +240,9 @@ namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
log?.LogDebug($"Availability Test Post-Function error handling routine (via {entryPointName}) finished:"
+ " This function invocation instance is not being tracked."
+ " {{FunctionName=\"{FunctionName}\", FunctionInstanceId=\"{FunctionInstanceId}\","
+ " ErrorType=\"{ErrorType}\"}}",
+ " ErrorType=\"{ErrorType}\", ErrorMessage=\"{ErrorMessage}\"}}",
functionName, functionInstanceId,
Format.SpellIfNull(error?.GetType()?.Name));
Format.SpellIfNull(error?.GetType()?.Name), Format.LimitLength(error?.Message, MaxErrorMessageLength, trim: true));
return;
}
@ -252,9 +254,9 @@ namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
log?.LogDebug($"Availability Test Post-Function error handling routine (via {entryPointName}) finished:"
+ " No error to be handled."
+ " {{FunctionName=\"{FunctionName}\", FunctionInstanceId=\"{FunctionInstanceId}\","
+ " ErrorType=\"{ErrorType}\"}}",
+ " ErrorType=\"{ErrorType}\", , ErrorMessage=\"{ErrorMessage}\"}}",
functionName, functionInstanceId,
Format.SpellIfNull(error?.GetType()?.Name));
Format.SpellIfNull(null), Format.LimitLength(null, MaxErrorMessageLength, trim: true));
return;
}
@ -264,7 +266,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
{
// This should never happen!
log?.LogError($"Availability Test Post-Function error handling routine (via {entryPointName}) finised:"
log?.LogError($"Availability Test Post-Function error handling routine (via {entryPointName}) finished:"
+ " Error: No AvailabilityTestScope was attached to the invocation state - Cannot continue processing!"
+ " {{FunctionName=\"{FunctionName}\", FunctionInstanceId=\"{FunctionInstanceId}\"}}"
+ " ErrorType=\"{ErrorType}\", ErrorMessage=\"{ErrorMessage}\"}}",
@ -278,7 +280,8 @@ namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
testScope.Complete(error, isTimeout);
testScope.Dispose();
log?.LogDebug($"Availability Test Post-Function error handling routine (via {entryPointName}) finidhed:"
log?.LogDebug($"Availability Test Post-Function error handling routine (via {entryPointName}) finished" +
$":"
+ $" {nameof(AvailabilityTestScope)} was completed and disposed."
+ " {{FunctionName=\"{FunctionName}\", FunctionInstanceId=\"{FunctionInstanceId}\","
+ " ErrorType=\"{ErrorType}\", ErrorMessage=\"{ErrorMessage}\","
@ -286,7 +289,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
+ " LocationDisplayName=\"{LocationDisplayName}\","
+ " LocationId=\"{LocationId}\"}} }}",
functionName, functionInstanceId,
error.GetType().Name, error.Message,
error.GetType().Name, Format.LimitLength(error.Message, MaxErrorMessageLength, trim: true),
testScope.TestDisplayName, testScope.LocationDisplayName, testScope.LocationId);
}
}

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

@ -23,7 +23,6 @@ namespace Microsoft.Azure.WebJobs.Extensions.AvailabilityMonitoring
private static readonly Random Rnd = new Random();
internal static bool IsSpecification(string testIntervalSpec)
{
// Ignore nulls and empty strings: