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

Refactor logger grouping #716

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 24 additions & 8 deletions dev-proxy-abstractions/BaseProxyPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,26 @@

using System.CommandLine;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

namespace Microsoft.DevProxy.Abstractions;

public abstract class BaseProxyPlugin : IProxyPlugin
{
protected ISet<UrlToWatch>? _urlsToWatch;
protected IProxyLogger? _logger;
protected ISet<UrlToWatch> UrlsToWatch { get; }
protected ILogger Logger { get; }
protected IConfigurationSection? ConfigSection { get; }
protected IPluginEvents PluginEvents { get; }
protected IProxyContext Context { get; }

public virtual string Name => throw new NotImplementedException();

public virtual Option[] GetOptions() => Array.Empty<Option>();
public virtual Command[] GetCommands() => Array.Empty<Command>();

public virtual void Register(IPluginEvents pluginEvents,
public BaseProxyPlugin(IPluginEvents pluginEvents,
IProxyContext context,
ILogger logger,
ISet<UrlToWatch> urlsToWatch,
IConfigurationSection? configSection = null)
{
Expand All @@ -26,18 +31,29 @@ public abstract class BaseProxyPlugin : IProxyPlugin
throw new ArgumentNullException(nameof(pluginEvents));
}

if (context is null || context.Logger is null)
if (context is null)
{
throw new ArgumentException($"{nameof(context)} must not be null and must supply a non-null Logger", nameof(context));
throw new ArgumentNullException(nameof(context));
}

if (logger is null)
{
throw new ArgumentNullException(nameof(logger));
}

if (urlsToWatch is null || urlsToWatch.Count == 0)
if (urlsToWatch is null || !urlsToWatch.Any())
{
throw new ArgumentException($"{nameof(urlsToWatch)} cannot be null or empty", nameof(urlsToWatch));
}

_urlsToWatch = urlsToWatch;
_logger = context.Logger;
UrlsToWatch = urlsToWatch;
Context = context;
Logger = logger;
ConfigSection = configSection;
PluginEvents = pluginEvents;
}

public virtual void Register()
{
}
}
7 changes: 7 additions & 0 deletions dev-proxy-abstractions/BaseReportingPlugin.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

namespace Microsoft.DevProxy.Abstractions;

public abstract class BaseReportingPlugin : BaseProxyPlugin
{
protected BaseReportingPlugin(IPluginEvents pluginEvents, IProxyContext context, ILogger logger, ISet<UrlToWatch> urlsToWatch, IConfigurationSection? configSection = null) : base(pluginEvents, context, logger, urlsToWatch, configSection)
{
}

protected virtual void StoreReport(object report, ProxyEventArgsBase e)
{
if (report is null)
Expand Down
22 changes: 22 additions & 0 deletions dev-proxy-abstractions/ILoggerExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System.Text.Json;
using Microsoft.DevProxy.Abstractions;

namespace Microsoft.Extensions.Logging;

public static class ILoggerExtensions
{
public static void LogRequest(this ILogger logger, string[] message, MessageType messageType, LoggingContext? context = null)
{
logger.Log(new RequestLog(message, messageType, context));
}

public static void LogRequest(this ILogger logger, string[] message, MessageType messageType, string method, string url)
{
logger.Log(new RequestLog(message, messageType, method, url));
}

public static void Log(this ILogger logger, RequestLog message)
{
logger.Log(LogLevel.Information, 0, message, exception: null, (m, _) => JsonSerializer.Serialize(m));
}
}
3 changes: 2 additions & 1 deletion dev-proxy-abstractions/IProxyLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ public enum MessageType
Failed,
Chaos,
Mocked,
InterceptedResponse
InterceptedResponse,
FinishedProcessingRequest
}

public class LoggingContext
Expand Down
6 changes: 1 addition & 5 deletions dev-proxy-abstractions/IProxyPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Licensed under the MIT License.

using System.CommandLine;
using Microsoft.Extensions.Configuration;

namespace Microsoft.DevProxy.Abstractions;

Expand All @@ -11,8 +10,5 @@ public interface IProxyPlugin
string Name { get; }
Option[] GetOptions();
Command[] GetCommands();
void Register(IPluginEvents pluginEvents,
IProxyContext context,
ISet<UrlToWatch> urlsToWatch,
IConfigurationSection? configSection = null);
void Register();
}
16 changes: 8 additions & 8 deletions dev-proxy-abstractions/MSGraphDbUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public static SqliteConnection MSGraphDbConnection
}
}

public static async Task<int> GenerateMSGraphDb(IProxyLogger logger, bool skipIfUpdatedToday = false)
public static async Task<int> GenerateMSGraphDb(ILogger logger, bool skipIfUpdatedToday = false)
{
var appFolder = ProxyUtils.AppFolder;
if (string.IsNullOrEmpty(appFolder))
Expand Down Expand Up @@ -75,7 +75,7 @@ public static async Task<int> GenerateMSGraphDb(IProxyLogger logger, bool skipIf

}

private static void CreateDb(SqliteConnection dbConnection, IProxyLogger logger)
private static void CreateDb(SqliteConnection dbConnection, ILogger logger)
{
logger.LogInformation("Creating database...");

Expand All @@ -97,7 +97,7 @@ private static void CreateDb(SqliteConnection dbConnection, IProxyLogger logger)
createIndex.ExecuteNonQuery();
}

private static void FillData(SqliteConnection dbConnection, IProxyLogger logger)
private static void FillData(SqliteConnection dbConnection, ILogger logger)
{
logger.LogInformation("Filling database...");

Expand All @@ -118,20 +118,20 @@ private static void FillData(SqliteConnection dbConnection, IProxyLogger logger)

foreach (var path in document.Paths)
{
logger.LogDebug("Endpoint {graphVersion}{key}...", graphVersion, path.Key);
logger.LogTrace("Endpoint {graphVersion}{key}...", graphVersion, path.Key);

// Get the GET operation for this path
var getOperation = path.Value.Operations.FirstOrDefault(o => o.Key == OperationType.Get).Value;
if (getOperation == null)
{
logger.LogDebug("No GET operation found for {graphVersion}{key}", graphVersion, path.Key);
logger.LogTrace("No GET operation found for {graphVersion}{key}", graphVersion, path.Key);
continue;
}

// Check if the GET operation has a $select parameter
var hasSelect = getOperation.Parameters.Any(p => p.Name == "$select");

logger.LogDebug("Inserting endpoint {graphVersion}{key} with hasSelect={hasSelect}...", graphVersion, path.Key, hasSelect);
logger.LogTrace("Inserting endpoint {graphVersion}{key} with hasSelect={hasSelect}...", graphVersion, path.Key, hasSelect);
insertEndpoint.Parameters["@path"].Value = path.Key;
insertEndpoint.Parameters["@graphVersion"].Value = graphVersion;
insertEndpoint.Parameters["@hasSelect"].Value = hasSelect;
Expand All @@ -143,7 +143,7 @@ private static void FillData(SqliteConnection dbConnection, IProxyLogger logger)
logger.LogInformation("Inserted {endpointCount} endpoints in the database", i);
}

private static async Task UpdateOpenAPIGraphFilesIfNecessary(string folder, IProxyLogger logger)
private static async Task UpdateOpenAPIGraphFilesIfNecessary(string folder, ILogger logger)
{
logger.LogInformation("Checking for updated OpenAPI files...");

Expand Down Expand Up @@ -176,7 +176,7 @@ private static async Task UpdateOpenAPIGraphFilesIfNecessary(string folder, IPro
}
}

private static async Task LoadOpenAPIFiles(string folder, IProxyLogger logger)
private static async Task LoadOpenAPIFiles(string folder, ILogger logger)
{
logger.LogInformation("Loading OpenAPI files...");

Expand Down
28 changes: 26 additions & 2 deletions dev-proxy-abstractions/PluginEvents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.CommandLine;
using System.CommandLine.Invocation;
using System.Security.Cryptography.X509Certificates;
using System.Text.Json.Serialization;
using Titanium.Web.Proxy;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Http;
Expand All @@ -13,7 +14,6 @@ namespace Microsoft.DevProxy.Abstractions;
public interface IProxyContext
{
IProxyConfiguration Configuration { get; }
IProxyLogger Logger { get; }
X509Certificate2? Certificate { get; }
}

Expand Down Expand Up @@ -126,13 +126,37 @@ public class RequestLog
{
public string[] MessageLines { get; set; }
public MessageType MessageType { get; set; }
[JsonIgnore]
public LoggingContext? Context { get; set; }
public string? Method { get; init; }
public string? Url { get; init; }

public RequestLog(string[] messageLines, MessageType messageType, LoggingContext? context)
public RequestLog(string[] messageLines, MessageType messageType, LoggingContext? context) :
this(messageLines, messageType, context?.Session.HttpClient.Request.Method, context?.Session.HttpClient.Request.Url, context)
{
}

public RequestLog(string[] messageLines, MessageType messageType, string method, string url) :
this(messageLines, messageType, method, url, context: null)
{
}

private RequestLog(string[] messageLines, MessageType messageType, string? method, string? url, LoggingContext? context)
{
MessageLines = messageLines ?? throw new ArgumentNullException(nameof(messageLines));
MessageType = messageType;
Context = context;
Method = method;
Url = url;
}

public void Deconstruct(out string[] message, out MessageType messageType, out LoggingContext? context, out string? method, out string? url)
{
message = MessageLines;
messageType = MessageType;
context = Context;
method = Method;
url = Url;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ namespace Microsoft.DevProxy.Plugins.Behavior;

internal class RateLimitingCustomResponseLoader : IDisposable
{
private readonly IProxyLogger _logger;
private readonly ILogger _logger;
private readonly RateLimitConfiguration _configuration;

public RateLimitingCustomResponseLoader(IProxyLogger logger, RateLimitConfiguration configuration)
public RateLimitingCustomResponseLoader(ILogger logger, RateLimitConfiguration configuration)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
Expand Down
34 changes: 18 additions & 16 deletions dev-proxy-plugins/Behavior/RateLimitingPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.DevProxy.Abstractions;
using System.Net;
using System.Text.Json;
Expand Down Expand Up @@ -49,6 +50,10 @@ public class RateLimitingPlugin : BaseProxyPlugin
private DateTime _resetTime = DateTime.MinValue;
private RateLimitingCustomResponseLoader? _loader = null;

public RateLimitingPlugin(IPluginEvents pluginEvents, IProxyContext context, ILogger logger, ISet<UrlToWatch> urlsToWatch, IConfigurationSection? configSection = null) : base(pluginEvents, context, logger, urlsToWatch, configSection)
{
}

private ThrottlingInfo ShouldThrottle(Request request, string throttlingKey)
{
var throttleKeyForRequest = BuildThrottleKey(request);
Expand Down Expand Up @@ -123,31 +128,28 @@ private string BuildThrottleKey(Request r)
}
}

public override void Register(IPluginEvents pluginEvents,
IProxyContext context,
ISet<UrlToWatch> urlsToWatch,
IConfigurationSection? configSection = null)
public override void Register()
{
base.Register(pluginEvents, context, urlsToWatch, configSection);
base.Register();

configSection?.Bind(_configuration);
ConfigSection?.Bind(_configuration);
if (_configuration.WhenLimitExceeded == RateLimitResponseWhenLimitExceeded.Custom)
{
_configuration.CustomResponseFile = Path.GetFullPath(ProxyUtils.ReplacePathTokens(_configuration.CustomResponseFile), Path.GetDirectoryName(context.Configuration?.ConfigFile ?? string.Empty) ?? string.Empty);
_loader = new RateLimitingCustomResponseLoader(_logger!, _configuration);
_configuration.CustomResponseFile = Path.GetFullPath(ProxyUtils.ReplacePathTokens(_configuration.CustomResponseFile), Path.GetDirectoryName(Context.Configuration.ConfigFile ?? string.Empty) ?? string.Empty);
_loader = new RateLimitingCustomResponseLoader(Logger, _configuration);
// load the responses from the configured mocks file
_loader.InitResponsesWatcher();
}

pluginEvents.BeforeRequest += OnRequest;
pluginEvents.BeforeResponse += OnResponse;
PluginEvents.BeforeRequest += OnRequest;
PluginEvents.BeforeResponse += OnResponse;
}

// add rate limiting headers to the response from the API
private Task OnResponse(object? sender, ProxyResponseArgs e)
{
if (_urlsToWatch is null ||
!e.HasRequestUrlMatch(_urlsToWatch))
if (UrlsToWatch is null ||
!e.HasRequestUrlMatch(UrlsToWatch))
{
return Task.CompletedTask;
}
Expand All @@ -161,8 +163,8 @@ private Task OnRequest(object? sender, ProxyRequestArgs e)
var session = e.Session;
var state = e.ResponseState;
if (e.ResponseState.HasBeenSet ||
_urlsToWatch is null ||
!e.ShouldExecute(_urlsToWatch))
UrlsToWatch is null ||
!e.ShouldExecute(UrlsToWatch))
{
return Task.CompletedTask;
}
Expand Down Expand Up @@ -191,7 +193,7 @@ private Task OnRequest(object? sender, ProxyRequestArgs e)
_resourcesRemaining = 0;
var request = e.Session.HttpClient.Request;

_logger?.LogRequest([$"Exceeded resource limit when calling {request.Url}.", "Request will be throttled"], MessageType.Failed, new LoggingContext(e.Session));
Logger.LogRequest([$"Exceeded resource limit when calling {request.Url}.", "Request will be throttled"], MessageType.Failed, new LoggingContext(e.Session));
if (_configuration.WhenLimitExceeded == RateLimitResponseWhenLimitExceeded.Throttle)
{
if (!e.GlobalData.ContainsKey(RetryAfterPlugin.ThrottledRequestsKey))
Expand Down Expand Up @@ -250,7 +252,7 @@ private Task OnRequest(object? sender, ProxyRequestArgs e)
}
else
{
_logger?.LogRequest([$"Custom behavior not set. {_configuration.CustomResponseFile} not found."], MessageType.Failed, new LoggingContext(e.Session));
Logger.LogRequest([$"Custom behavior not set. {_configuration.CustomResponseFile} not found."], MessageType.Failed, new LoggingContext(e.Session));
}
}
}
Expand Down
Loading