Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NEST-499: Bug fixing for Azure blob storage requests #78

Merged
merged 14 commits into from
Nov 30, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -76,19 +76,43 @@ public class ApplicationInsightsOptions
public bool EnableOfflineOperation { get; set; }

/// <summary>
/// Gets or sets <see cref="IgnoreFailureRegex"/> by compiling the given string into a regular expression.
/// Gets or sets <see cref="RequestIgnoreFailureRegex"/> by compiling the given string into a regular expression.
/// This will be used for <see cref="RequestTelemetry"/> types.
/// </summary>
/// <example>You should use a regex pattern like "(?:\\/favicon.ico$)|(?:\\/media\\/)".</example>
public string IgnoreFailureRegexPattern
/// <example>
/// You should use a regex pattern like "(?:\\/favicon.ico$)|(?:\\.well-known)". Use non-capturing groups to improve
/// performance.
/// </example>
public string RequestIgnoreFailureRegexPattern
{
get => IgnoreFailureRegex?.ToString();
set => IgnoreFailureRegex = new Regex(value, RegexOptions.Compiled, TimeSpan.FromSeconds(1));
get => RequestIgnoreFailureRegex?.ToString();
set => RequestIgnoreFailureRegex = new Regex(value, RegexOptions.Compiled, TimeSpan.FromSeconds(1));
}

/// <summary>
/// Gets or sets a regular expression that will be used to set telemetry to success if it matches
/// <see cref="DependencyTelemetry.Data"/> or <see cref="RequestTelemetry.Url"/>. This is useful if
/// you have a lot of 404s or other errors that you don't want to see as failures in Application Insights.
/// <see cref="RequestTelemetry.Url"/>. This is useful if you have a lot of 404s or other errors that you don't want
/// to see as failures in Application Insights. This will be used for <see cref="RequestTelemetry"/> types.
/// </summary>
public Regex IgnoreFailureRegex { get; set; }
public Regex RequestIgnoreFailureRegex { get; set; }

/// <summary>
/// Gets or sets <see cref="DependencyIgnoreFailureRegex"/> by compiling the given string into a regular expression.
/// This will be used for <see cref="DependencyTelemetry"/> types.
/// </summary>
/// <example>
/// You should use a regex pattern like "\\/media\\/".
/// </example>
public string DependencyIgnoreFailureRegexPattern
{
get => DependencyIgnoreFailureRegex?.ToString();
set => DependencyIgnoreFailureRegex = new Regex(value, RegexOptions.Compiled, TimeSpan.FromSeconds(1));
}

/// <summary>
/// Gets or sets a regular expression that will be used to set telemetry to success if it matches
/// <see cref="DependencyTelemetry.Data"/>. This is useful if you have a lot of 404s or other errors that you don't
/// want to see as failures in Application Insights. This will be used for <see cref="DependencyTelemetry"/> types.
/// </summary>
public Regex DependencyIgnoreFailureRegex { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public static OrchardCoreBuilder AddOrchardCoreApplicationInsightsTelemetry(
}

services.AddApplicationInsightsTelemetryProcessor<TelemetryFilter>();
services.AddApplicationInsightsTelemetryProcessor<AzureBlobTelemetryFilter>();

services.Configure<ApplicationInsightsOptions>(applicationInsightsConfigSection);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,25 @@ public static void SetAsIgnoredFailure(this OperationTelemetry operationTelemetr
/// Returns <see langword="false"/> otherwise.
/// </summary>
public static bool ShouldSetAsIgnoredFailure(this DependencyTelemetry dependencyTelemetry, IServiceProvider serviceProvider) =>
ShouldSetAsIgnoredFailure(dependencyTelemetry.ResultCode, dependencyTelemetry.Data, serviceProvider);
IsResult4xx(dependencyTelemetry.ResultCode) &&
serviceProvider.GetRequiredService<IOptions<ApplicationInsightsOptions>>().Value
.DependencyIgnoreFailureRegex
?.IsMatch(dependencyTelemetry.Data) == true;

/// <summary>
/// Returns <see langword="true"/> if the <see cref="RequestTelemetry"/> should be set as an ignored failure.
/// Returns <see langword="false"/> otherwise.
/// </summary>
public static bool ShouldSetAsIgnoredFailure(this RequestTelemetry requestTelemetry, IServiceProvider serviceProvider) =>
ShouldSetAsIgnoredFailure(requestTelemetry.ResponseCode, requestTelemetry.Url.ToString(), serviceProvider);
IsResult4xx(requestTelemetry.ResponseCode) &&
serviceProvider.GetRequiredService<IOptions<ApplicationInsightsOptions>>().Value
.RequestIgnoreFailureRegex
?.IsMatch(requestTelemetry.Url.ToString()) == true;

private static bool ShouldSetAsIgnoredFailure(string code, string data, IServiceProvider serviceProvider) =>
// Making an exception here because 4xx describes it better.
#pragma warning disable S100 // Methods and properties should be named in PascalCase
private static bool IsResult4xx(string code) =>
#pragma warning restore S100 // Methods and properties should be named in PascalCase
int.TryParse(code, out var resultCode) &&
resultCode is >= 400 and < 500 &&
serviceProvider.GetRequiredService<IOptions<ApplicationInsightsOptions>>().Value
.IgnoreFailureRegex
.IsMatch(data);
resultCode is >= 400 and < 500;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using Lombiq.Hosting.Azure.ApplicationInsights.Extensions;
using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.ApplicationInsights.Extensibility;
using System;

namespace Lombiq.Hosting.Azure.ApplicationInsights.Services;

/// <summary>
/// Azure Blob Storage 404s are logged in a parent-child relationship, so we need to ignore the parents also.
/// </summary>
public class AzureBlobTelemetryFilter : ITelemetryProcessor
{
private readonly ITelemetryProcessor _next;
private readonly IServiceProvider _serviceProvider;
private string _parentId;

public AzureBlobTelemetryFilter(ITelemetryProcessor next, IServiceProvider serviceProvider)
{
_next = next;
_serviceProvider = serviceProvider;
}

public void Process(ITelemetry item)
{
SetAsIgnoredFailureWhenNeeded(item);

_next.Process(item);
}

private void SetAsIgnoredFailureWhenNeeded(ITelemetry item)
{
if (item is not DependencyTelemetry { Success: false } dependency) return;

if (dependency.ResultCode == "404" && dependency.Type == "Azure blob" &&
dependency.ShouldSetAsIgnoredFailure(_serviceProvider))
{
_parentId = dependency.Context.Operation.ParentId;
dependency.SetAsIgnoredFailure();
return;
}

if (_parentId == dependency.Id && dependency.Name is "Blob.GetProperties" or "BlobBaseClient.GetProperties")
{
_parentId = dependency.Context.Operation.ParentId;
dependency.SetAsIgnoredFailure();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public void Initialize(ITelemetry telemetry)
}

if (operationTelemetry is DependencyTelemetry dependencyTelemetry &&
dependencyTelemetry.Type != "Azure blob" &&
dependencyTelemetry.ShouldSetAsIgnoredFailure(_serviceProvider))
{
dependencyTelemetry.SetAsIgnoredFailure();
Expand Down