From 350da5e916595ffe49ecd9d44449462f6f97f065 Mon Sep 17 00:00:00 2001 From: Liza Carvelli Date: Thu, 24 Nov 2022 23:28:31 +0100 Subject: [PATCH] Include gRPC messages in plugin log --- Pal.Client/Net/GrpcLogger.cs | 77 ++++++++++++++++++++++++++++ Pal.Client/Net/GrpcLoggerProvider.cs | 14 +++++ Pal.Client/{ => Net}/RemoteApi.cs | 18 ++++--- Pal.Client/Pal.Client.csproj | 3 +- Pal.Client/Service.cs | 1 + 5 files changed, 105 insertions(+), 8 deletions(-) create mode 100644 Pal.Client/Net/GrpcLogger.cs create mode 100644 Pal.Client/Net/GrpcLoggerProvider.cs rename Pal.Client/{ => Net}/RemoteApi.cs (95%) diff --git a/Pal.Client/Net/GrpcLogger.cs b/Pal.Client/Net/GrpcLogger.cs new file mode 100644 index 0000000..ec68920 --- /dev/null +++ b/Pal.Client/Net/GrpcLogger.cs @@ -0,0 +1,77 @@ +using Dalamud.Logging; +using Microsoft.Extensions.Logging; +using System; +using System.Runtime.CompilerServices; + +namespace Pal.Client.Net +{ + internal class GrpcLogger : ILogger + { + private readonly string _name; + + public GrpcLogger(string name) + { + _name = name; + } + + public IDisposable BeginScope(TState state) => NullScope.Instance; + + public bool IsEnabled(LogLevel logLevel) => logLevel != LogLevel.None; + + [MethodImpl(MethodImplOptions.NoInlining)] // PluginLog detects the plugin name as `Microsoft.Extensions.Logging` if inlined + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) + { + if (!IsEnabled(logLevel)) + return; + + if (formatter == null) + throw new ArgumentNullException(nameof(formatter)); + + string message = $"gRPC[{_name}] {formatter(state, null)}"; + if (string.IsNullOrEmpty(message)) + return; + +#pragma warning disable CS8604 // the nullability on PluginLog methods is wrong and allows nulls for exceptions, WriteLog even declares the parameter as `Exception? exception = null` + switch (logLevel) + { + case LogLevel.Critical: + PluginLog.Fatal(exception, message); + break; + + case LogLevel.Error: + PluginLog.Error(exception, message); + break; + + case LogLevel.Warning: + PluginLog.Warning(exception, message); + break; + + case LogLevel.Information: + PluginLog.Information(exception, message); + break; + + case LogLevel.Debug: + PluginLog.Debug(exception, message); + break; + + case LogLevel.Trace: + PluginLog.Verbose(exception, message); + break; + } +#pragma warning restore CS8604 + } + + private class NullScope : IDisposable + { + public static NullScope Instance { get; } = new NullScope(); + + private NullScope() + { + } + + public void Dispose() + { + } + } + } +} diff --git a/Pal.Client/Net/GrpcLoggerProvider.cs b/Pal.Client/Net/GrpcLoggerProvider.cs new file mode 100644 index 0000000..bf702e5 --- /dev/null +++ b/Pal.Client/Net/GrpcLoggerProvider.cs @@ -0,0 +1,14 @@ +using Microsoft.Extensions.Logging; +using System; + +namespace Pal.Client.Net +{ + internal class GrpcLoggerProvider : ILoggerProvider + { + public ILogger CreateLogger(string categoryName) => new GrpcLogger(categoryName); + + public void Dispose() + { + } + } +} diff --git a/Pal.Client/RemoteApi.cs b/Pal.Client/Net/RemoteApi.cs similarity index 95% rename from Pal.Client/RemoteApi.cs rename to Pal.Client/Net/RemoteApi.cs index 0e77027..0f721a1 100644 --- a/Pal.Client/RemoteApi.cs +++ b/Pal.Client/Net/RemoteApi.cs @@ -2,6 +2,7 @@ using Dalamud.Logging; using Grpc.Core; using Grpc.Net.Client; +using Microsoft.Extensions.Logging; using Palace; using System; using System.Collections.Generic; @@ -13,7 +14,7 @@ using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; -namespace Pal.Client +namespace Pal.Client.Net { internal class RemoteApi : IDisposable { @@ -24,6 +25,8 @@ namespace Pal.Client #endif private readonly string UserAgent = $"{typeof(RemoteApi).Assembly.GetName().Name?.Replace(" ", "")}/{typeof(RemoteApi).Assembly.GetName().Version?.ToString(2)}"; + private readonly ILoggerFactory _grpcToPluginLogLoggerFactory = LoggerFactory.Create(builder => builder.AddProvider(new GrpcLoggerProvider()).AddFilter("Grpc", LogLevel.Trace)); + private GrpcChannel? _channel; private LoginReply? _lastLoginReply; private bool _warnedAboutUpgrade = false; @@ -43,7 +46,7 @@ namespace Pal.Client private string PartialAccountId => AccountId?.ToString()?.PadRight(14).Substring(0, 13) ?? "[no account id]"; - private async Task<(bool Success, string Error)> TryConnect(CancellationToken cancellationToken, bool retry = true) + private async Task<(bool Success, string Error)> TryConnect(CancellationToken cancellationToken, ILoggerFactory? loggerFactory = null, bool retry = true) { if (Service.Configuration.Mode != Configuration.EMode.Online) { @@ -62,7 +65,8 @@ namespace Pal.Client { ConnectTimeout = TimeSpan.FromSeconds(5), SslOptions = GetSslClientAuthenticationOptions(), - } + }, + LoggerFactory = loggerFactory, }); PluginLog.Information($"TryConnect: Connecting to upstream service at {remoteUrl}"); @@ -105,11 +109,11 @@ namespace Pal.Client _lastLoginReply = await accountClient.LoginAsync(new LoginRequest { AccountId = AccountId?.ToString() }, headers: UnauthorizedHeaders(), deadline: DateTime.UtcNow.AddSeconds(10), cancellationToken: cancellationToken); if (_lastLoginReply.Success) { - PluginLog.Information($"TryConnect: Login successful with account id: {PartialAccountId}, auth token: {_lastLoginReply.AuthToken}"); + PluginLog.Information($"TryConnect: Login successful with account id: {PartialAccountId}"); } else { - PluginLog.Error($"TryConnect: Login failed with error { _lastLoginReply.Error}"); + PluginLog.Error($"TryConnect: Login failed with error {_lastLoginReply.Error}"); if (_lastLoginReply.Error == LoginError.InvalidAccountId) { AccountId = null; @@ -154,7 +158,7 @@ namespace Pal.Client { _warnedAboutUpgrade = false; - var connectionResult = await TryConnect(cancellationToken); + var connectionResult = await TryConnect(cancellationToken, loggerFactory: _grpcToPluginLogLoggerFactory); if (!connectionResult.Success) return $"Could not connect to server: {connectionResult.Error}"; @@ -217,7 +221,7 @@ namespace Pal.Client private Marker CreateMarkerFromNetworkObject(PalaceObject obj) => new Marker((Marker.EType)obj.Type, new Vector3(obj.X, obj.Y, obj.Z), Guid.Parse(obj.NetworkId)); - + public async Task<(bool, List)> FetchStatistics(CancellationToken cancellationToken = default) { if (!await Connect(cancellationToken)) diff --git a/Pal.Client/Pal.Client.csproj b/Pal.Client/Pal.Client.csproj index 2c483b6..f540498 100644 --- a/Pal.Client/Pal.Client.csproj +++ b/Pal.Client/Pal.Client.csproj @@ -3,7 +3,7 @@ net6.0-windows 9.0 - 1.17.0.0 + 1.18.0.0 enable @@ -37,6 +37,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/Pal.Client/Service.cs b/Pal.Client/Service.cs index 2805836..b2a88e7 100644 --- a/Pal.Client/Service.cs +++ b/Pal.Client/Service.cs @@ -8,6 +8,7 @@ using Dalamud.Game.Gui; using Dalamud.Interface.Windowing; using Dalamud.IoC; using Dalamud.Plugin; +using Pal.Client.Net; namespace Pal.Client {