diff --git a/samples/01_basic_ping_bot/Program.cs b/samples/01_basic_ping_bot/Program.cs index 7fbe04993f..874eeb23db 100644 --- a/samples/01_basic_ping_bot/Program.cs +++ b/samples/01_basic_ping_bot/Program.cs @@ -31,8 +31,7 @@ public Program() // It is recommended to Dispose of a client when you are finished // using it, at the end of your app's lifetime. _client = new DiscordSocketClient(); - - _client.Log += LogAsync; + _client.Ready += ReadyAsync; _client.MessageReceived += MessageReceivedAsync; } @@ -47,12 +46,6 @@ public async Task MainAsync() await Task.Delay(Timeout.Infinite); } - private Task LogAsync(LogMessage log) - { - Console.WriteLine(log.ToString()); - return Task.CompletedTask; - } - // The Ready event indicates that the client has opened a // connection and it is now safe to access the cache. private Task ReadyAsync() diff --git a/samples/02_commands_framework/Program.cs b/samples/02_commands_framework/Program.cs index 67cb877642..f66b1e441d 100644 --- a/samples/02_commands_framework/Program.cs +++ b/samples/02_commands_framework/Program.cs @@ -35,9 +35,6 @@ public async Task MainAsync() { var client = services.GetRequiredService(); - client.Log += LogAsync; - services.GetRequiredService().Log += LogAsync; - // Tokens should be considered secret data and never hard-coded. // We can read from the environment variable to avoid hardcoding. await client.LoginAsync(TokenType.Bot, Environment.GetEnvironmentVariable("token")); @@ -50,13 +47,6 @@ public async Task MainAsync() } } - private Task LogAsync(LogMessage log) - { - Console.WriteLine(log.ToString()); - - return Task.CompletedTask; - } - private ServiceProvider ConfigureServices() { return new ServiceCollection() diff --git a/samples/03_sharded_client/Program.cs b/samples/03_sharded_client/Program.cs index 753f400a1f..a5bbea1f2d 100644 --- a/samples/03_sharded_client/Program.cs +++ b/samples/03_sharded_client/Program.cs @@ -38,7 +38,6 @@ public async Task MainAsync() // The ShardReady event is used instead, allowing for individual // control per shard. client.ShardReady += ReadyAsync; - client.Log += LogAsync; await services.GetRequiredService().InitializeAsync(); @@ -65,11 +64,5 @@ private Task ReadyAsync(DiscordSocketClient shard) Console.WriteLine($"Shard Number {shard.ShardId} is connected and ready!"); return Task.CompletedTask; } - - private Task LogAsync(LogMessage log) - { - Console.WriteLine(log.ToString()); - return Task.CompletedTask; - } } } diff --git a/samples/03_sharded_client/Services/CommandHandlingService.cs b/samples/03_sharded_client/Services/CommandHandlingService.cs index 1230cbcff3..ec2e7f596d 100644 --- a/samples/03_sharded_client/Services/CommandHandlingService.cs +++ b/samples/03_sharded_client/Services/CommandHandlingService.cs @@ -21,7 +21,6 @@ public CommandHandlingService(IServiceProvider services) _services = services; _commands.CommandExecuted += CommandExecutedAsync; - _commands.Log += LogAsync; _discord.MessageReceived += MessageReceivedAsync; } @@ -61,12 +60,5 @@ public async Task CommandExecutedAsync(Optional command, ICommandCo // the command failed, let's notify the user that something happened. await context.Channel.SendMessageAsync($"error: {result.ToString()}"); } - - private Task LogAsync(LogMessage log) - { - Console.WriteLine(log.ToString()); - - return Task.CompletedTask; - } } } diff --git a/samples/idn/Program.cs b/samples/idn/Program.cs index ffd8fd1afb..628fb9c081 100644 --- a/samples/idn/Program.cs +++ b/samples/idn/Program.cs @@ -34,60 +34,15 @@ public class Program static async Task Main(string[] args) { var token = File.ReadAllText("token.ignore"); - var client = new DiscordSocketClient(new DiscordSocketConfig { LogLevel = LogSeverity.Debug }); - var logQueue = new ConcurrentQueue(); + var client = new DiscordSocketClient(new DiscordSocketConfig { }); var logCancelToken = new CancellationTokenSource(); int presenceUpdates = 0; - - client.Log += msg => - { - logQueue.Enqueue(msg); - return Task.CompletedTask; - }; + Console.CancelKeyPress += (_ev, _s) => { logCancelToken.Cancel(); }; - var logTask = Task.Run(async () => - { - var fs = new FileStream("idn.log", FileMode.Append); - var logStringBuilder = new StringBuilder(200); - string logString = ""; - - byte[] helloBytes = Encoding.UTF8.GetBytes($"### new log session: {DateTime.Now} ###\n\n"); - await fs.WriteAsync(helloBytes); - - while (!logCancelToken.IsCancellationRequested) - { - if (logQueue.TryDequeue(out var msg)) - { - if (msg.Message?.IndexOf("PRESENCE_UPDATE)") > 0) - { - presenceUpdates++; - continue; - } - - _ = msg.ToString(builder: logStringBuilder); - logStringBuilder.AppendLine(); - logString = logStringBuilder.ToString(); - - Debug.Write(logString, "DNET"); - await fs.WriteAsync(Encoding.UTF8.GetBytes(logString)); - } - await fs.FlushAsync(); - try - { - await Task.Delay(100, logCancelToken.Token); - } - finally { } - } - - byte[] goodbyeBytes = Encoding.UTF8.GetBytes($"#!! end log session: {DateTime.Now} !!#\n\n\n"); - await fs.WriteAsync(goodbyeBytes); - await fs.DisposeAsync(); - }); - await client.LoginAsync(TokenType.Bot, token); await client.StartAsync(); @@ -127,9 +82,9 @@ static async Task Main(string[] args) await client.StopAsync(); client.Dispose(); logCancelToken.Cancel(); - try - { await logTask; } - finally { Console.WriteLine("goodbye!"); } + + await Task.Delay(-1, logCancelToken.Token); + Console.WriteLine("goodbye!"); } static IEnumerable GetAssemblies() diff --git a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs index 28037b0fa1..b6a254fec5 100644 --- a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Discord.Commands.Builders; +using Microsoft.Extensions.Logging; namespace Discord.Commands { @@ -34,7 +35,7 @@ bool IsLoadableModule(TypeInfo info) } else if (IsLoadableModule(typeInfo)) { - await service._cmdLogger.WarningAsync($"Class {typeInfo.FullName} is not public and cannot be loaded. To suppress this message, mark the class with {nameof(DontAutoLoadAttribute)}.").ConfigureAwait(false); + service._cmdLogger.LogWarning($"Class {typeInfo.FullName} is not public and cannot be loaded. To suppress this message, mark the class with {nameof(DontAutoLoadAttribute)}."); } } @@ -69,7 +70,7 @@ public static async Task> BuildAsync(IEnumerable public class CommandService : IDisposable { - /// - /// Occurs when a command-related information is received. - /// - public event Func Log { add { _logEvent.Add(value); } remove { _logEvent.Remove(value); } } - internal readonly AsyncEvent> _logEvent = new AsyncEvent>(); - /// /// Occurs when a command is executed. /// @@ -56,8 +51,8 @@ public class CommandService : IDisposable internal readonly bool _caseSensitive, _throwOnError, _ignoreExtraArgs; internal readonly char _separatorChar; internal readonly RunMode _defaultRunMode; - internal readonly Logger _cmdLogger; - internal readonly LogManager _logManager; + internal readonly ILogger _cmdLogger; + internal readonly ILoggerFactory _logManager; internal readonly IReadOnlyDictionary _quotationMarkAliasMap; internal bool _isDisposed; @@ -100,8 +95,14 @@ public CommandService(CommandServiceConfig config) if (_defaultRunMode == RunMode.Default) throw new InvalidOperationException("The default run mode cannot be set to Default."); - _logManager = new LogManager(config.LogLevel); - _logManager.Message += async msg => await _logEvent.InvokeAsync(msg).ConfigureAwait(false); + _logManager = config.LoggerFactory; + + if (_logManager == null) + { + _logManager = new DefaultLoggerFactory(); + _logManager.AddProvider(new DefaultLoggerProvider()); + } + _cmdLogger = _logManager.CreateLogger("Command"); _moduleLock = new SemaphoreSlim(1, 1); @@ -349,7 +350,8 @@ public void AddTypeReader(TypeReader reader) public void AddTypeReader(Type type, TypeReader reader) { if (_defaultTypeReaders.ContainsKey(type)) - _ = _cmdLogger.WarningAsync($"The default TypeReader for {type.FullName} was replaced by {reader.GetType().FullName}." + + _cmdLogger.LogWarning( + $"The default TypeReader for {type.FullName} was replaced by {reader.GetType().FullName}." + "To suppress this message, use AddTypeReader(reader, true)."); AddTypeReader(type, reader, true); } diff --git a/src/Discord.Net.Commands/CommandServiceConfig.cs b/src/Discord.Net.Commands/CommandServiceConfig.cs index 3c62063c87..7b65476319 100644 --- a/src/Discord.Net.Commands/CommandServiceConfig.cs +++ b/src/Discord.Net.Commands/CommandServiceConfig.cs @@ -1,3 +1,4 @@ +using Microsoft.Extensions.Logging; using System.Collections.Generic; namespace Discord.Commands @@ -23,10 +24,7 @@ public class CommandServiceConfig /// public bool CaseSensitiveCommands { get; set; } = false; - /// - /// Gets or sets the minimum log level severity that will be sent to the event. - /// - public LogSeverity LogLevel { get; set; } = LogSeverity.Info; + public ILoggerFactory LoggerFactory { get; set; } /// /// Gets or sets whether commands should push exceptions up to the caller. diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs index 3bcef98311..890b956ba6 100644 --- a/src/Discord.Net.Commands/Info/CommandInfo.cs +++ b/src/Discord.Net.Commands/Info/CommandInfo.cs @@ -1,4 +1,5 @@ using Discord.Commands.Builders; +using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -240,7 +241,7 @@ public async Task ExecuteAsync(ICommandContext context, IEnumerable ExecuteInternalAsync(ICommandContext context, object[] args, IServiceProvider services) { - await Module.Service._cmdLogger.DebugAsync($"Executing {GetLogText(context)}").ConfigureAwait(false); + Module.Service._cmdLogger.LogDebug($"Executing {GetLogText(context)}"); try { var task = _action(context, args, services, this); @@ -274,7 +275,7 @@ private async Task ExecuteInternalAsync(ICommandContext context, object ex = ex.InnerException; var wrappedEx = new CommandException(this, context, ex); - await Module.Service._cmdLogger.ErrorAsync(wrappedEx).ConfigureAwait(false); + Module.Service._cmdLogger.LogError(wrappedEx, wrappedEx.Message); var result = ExecuteResult.FromError(ex); await Module.Service._commandExecutedEvent.InvokeAsync(this, context, result).ConfigureAwait(false); @@ -291,7 +292,7 @@ private async Task ExecuteInternalAsync(ICommandContext context, object } finally { - await Module.Service._cmdLogger.VerboseAsync($"Executed {GetLogText(context)}").ConfigureAwait(false); + Module.Service._cmdLogger.LogTrace($"Executed {GetLogText(context)}"); } } diff --git a/src/Discord.Net.Core/Discord.Net.Core.csproj b/src/Discord.Net.Core/Discord.Net.Core.csproj index bc513390c1..80c308132e 100644 --- a/src/Discord.Net.Core/Discord.Net.Core.csproj +++ b/src/Discord.Net.Core/Discord.Net.Core.csproj @@ -9,6 +9,7 @@ netstandard2.0;netstandard2.1 + diff --git a/src/Discord.Net.Core/DiscordConfig.cs b/src/Discord.Net.Core/DiscordConfig.cs index 429ad7b0c7..abda4a297e 100644 --- a/src/Discord.Net.Core/DiscordConfig.cs +++ b/src/Discord.Net.Core/DiscordConfig.cs @@ -1,3 +1,4 @@ +using Microsoft.Extensions.Logging; using System.Reflection; namespace Discord @@ -123,14 +124,8 @@ public class DiscordConfig /// The currently set . /// public RetryMode DefaultRetryMode { get; set; } = RetryMode.AlwaysRetry; - - /// - /// Gets or sets the minimum log level severity that will be sent to the Log event. - /// - /// - /// The currently set for logging level. - /// - public LogSeverity LogLevel { get; set; } = LogSeverity.Info; + + public ILoggerFactory LoggerFactory { get; set; } /// /// Gets or sets whether the initial log entry should be printed. diff --git a/src/Discord.Net.Core/Logging/DefaultLogger.cs b/src/Discord.Net.Core/Logging/DefaultLogger.cs new file mode 100644 index 0000000000..ab1e8528b5 --- /dev/null +++ b/src/Discord.Net.Core/Logging/DefaultLogger.cs @@ -0,0 +1,52 @@ +using Microsoft.Extensions.Logging; +using System; + +namespace Discord.Logging +{ + internal class DefaultLogger : ILogger + { + private static readonly object _lock = new object(); + + private LogLevel MinimumLevel { get; } + + internal DefaultLogger(LogLevel minLevel = LogLevel.Information) + { + this.MinimumLevel = minLevel; + } + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + if (!this.IsEnabled(logLevel)) + return; + + lock (_lock) + { + Console.Write($"{DateTime.Now} "); + + Console.Write(logLevel switch + { + LogLevel.Trace => "[Trace] ", + LogLevel.Debug => "[Debug] ", + LogLevel.Information => "[Info ] ", + LogLevel.Warning => "[Warn ] ", + LogLevel.Error => "[Error] ", + LogLevel.Critical => "[Crit ] ", + LogLevel.None => "[None ] ", + _ => "[?????] " + }); + + var message = formatter(state, exception); + Console.WriteLine(message); + if (exception != null) + Console.WriteLine(exception); + } + } + + public bool IsEnabled(LogLevel logLevel) => logLevel >= this.MinimumLevel; + + public IDisposable BeginScope(TState state) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Discord.Net.Core/Logging/DefaultLoggerFactory.cs b/src/Discord.Net.Core/Logging/DefaultLoggerFactory.cs new file mode 100644 index 0000000000..39f4e2a135 --- /dev/null +++ b/src/Discord.Net.Core/Logging/DefaultLoggerFactory.cs @@ -0,0 +1,41 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Discord.Logging +{ + internal class DefaultLoggerFactory : ILoggerFactory + { + private List Providers { get; } = new List(); + private bool _isDisposed = false; + + public void Dispose() + { + if (this._isDisposed) + return; + this._isDisposed = true; + + foreach (var provider in this.Providers) + provider.Dispose(); + + this.Providers.Clear(); + } + + public ILogger CreateLogger(string categoryName) + { + if (this._isDisposed) + throw new InvalidOperationException("This logger factory is already disposed."); + + // HEHEHE XDXD + var provider = Providers.FirstOrDefault(); + + return provider?.CreateLogger(categoryName) ?? throw new ArgumentNullException(nameof(provider)); + } + + public void AddProvider(ILoggerProvider provider) + { + this.Providers.Add(provider); + } + } +} diff --git a/src/Discord.Net.Core/Logging/DefaultLoggerProvider.cs b/src/Discord.Net.Core/Logging/DefaultLoggerProvider.cs new file mode 100644 index 0000000000..73391ef4b1 --- /dev/null +++ b/src/Discord.Net.Core/Logging/DefaultLoggerProvider.cs @@ -0,0 +1,31 @@ +using Microsoft.Extensions.Logging; +using System; + +namespace Discord.Logging +{ + internal class DefaultLoggerProvider : ILoggerProvider + { + private LogLevel MinimumLevel { get; } + + private bool _isDisposed = false; + + + internal DefaultLoggerProvider(LogLevel minLevel = LogLevel.Information) + { + this.MinimumLevel = minLevel; + } + + public ILogger CreateLogger(string categoryName) + { + if (this._isDisposed) + throw new InvalidOperationException("This logger provider is already disposed."); + + return new DefaultLogger(this.MinimumLevel); + } + + public void Dispose() + { + this._isDisposed = true; + } + } +} diff --git a/src/Discord.Net.Core/Logging/LogManager.cs b/src/Discord.Net.Core/Logging/LogManager.cs deleted file mode 100644 index a99c45b39d..0000000000 --- a/src/Discord.Net.Core/Logging/LogManager.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using System.Threading.Tasks; - -namespace Discord.Logging -{ - internal class LogManager - { - public LogSeverity Level { get; } - private Logger ClientLogger { get; } - - public event Func Message { add { _messageEvent.Add(value); } remove { _messageEvent.Remove(value); } } - private readonly AsyncEvent> _messageEvent = new AsyncEvent>(); - - public LogManager(LogSeverity minSeverity) - { - Level = minSeverity; - ClientLogger = new Logger(this, "Discord"); - } - - public async Task LogAsync(LogSeverity severity, string source, Exception ex) - { - try - { - if (severity <= Level) - await _messageEvent.InvokeAsync(new LogMessage(severity, source, null, ex)).ConfigureAwait(false); - } - catch - { - // ignored - } - } - public async Task LogAsync(LogSeverity severity, string source, string message, Exception ex = null) - { - try - { - if (severity <= Level) - await _messageEvent.InvokeAsync(new LogMessage(severity, source, message, ex)).ConfigureAwait(false); - } - catch - { - // ignored - } - } - - public async Task LogAsync(LogSeverity severity, string source, FormattableString message, Exception ex = null) - { - try - { - if (severity <= Level) - await _messageEvent.InvokeAsync(new LogMessage(severity, source, message.ToString(), ex)).ConfigureAwait(false); - } - catch { } - } - - - public Task ErrorAsync(string source, Exception ex) - => LogAsync(LogSeverity.Error, source, ex); - public Task ErrorAsync(string source, string message, Exception ex = null) - => LogAsync(LogSeverity.Error, source, message, ex); - - public Task ErrorAsync(string source, FormattableString message, Exception ex = null) - => LogAsync(LogSeverity.Error, source, message, ex); - - - public Task WarningAsync(string source, Exception ex) - => LogAsync(LogSeverity.Warning, source, ex); - public Task WarningAsync(string source, string message, Exception ex = null) - => LogAsync(LogSeverity.Warning, source, message, ex); - - public Task WarningAsync(string source, FormattableString message, Exception ex = null) - => LogAsync(LogSeverity.Warning, source, message, ex); - - - public Task InfoAsync(string source, Exception ex) - => LogAsync(LogSeverity.Info, source, ex); - public Task InfoAsync(string source, string message, Exception ex = null) - => LogAsync(LogSeverity.Info, source, message, ex); - public Task InfoAsync(string source, FormattableString message, Exception ex = null) - => LogAsync(LogSeverity.Info, source, message, ex); - - - public Task VerboseAsync(string source, Exception ex) - => LogAsync(LogSeverity.Verbose, source, ex); - public Task VerboseAsync(string source, string message, Exception ex = null) - => LogAsync(LogSeverity.Verbose, source, message, ex); - public Task VerboseAsync(string source, FormattableString message, Exception ex = null) - => LogAsync(LogSeverity.Verbose, source, message, ex); - - - public Task DebugAsync(string source, Exception ex) - => LogAsync(LogSeverity.Debug, source, ex); - public Task DebugAsync(string source, string message, Exception ex = null) - => LogAsync(LogSeverity.Debug, source, message, ex); - public Task DebugAsync(string source, FormattableString message, Exception ex = null) - => LogAsync(LogSeverity.Debug, source, message, ex); - - - public Logger CreateLogger(string name) => new Logger(this, name); - - public async Task WriteInitialLog() - { - await ClientLogger.InfoAsync($"Discord.Net v{DiscordConfig.Version} (API v{DiscordConfig.APIVersion})").ConfigureAwait(false); - } - } -} diff --git a/src/Discord.Net.Core/Logging/LogMessage.cs b/src/Discord.Net.Core/Logging/LogMessage.cs deleted file mode 100644 index 715cac6770..0000000000 --- a/src/Discord.Net.Core/Logging/LogMessage.cs +++ /dev/null @@ -1,136 +0,0 @@ -using System; -using System.Text; - -namespace Discord -{ - /// - /// Provides a message object used for logging purposes. - /// - public struct LogMessage - { - /// - /// Gets the severity of the log entry. - /// - /// - /// A enum to indicate the severeness of the incident or event. - /// - public LogSeverity Severity { get; } - /// - /// Gets the source of the log entry. - /// - /// - /// A string representing the source of the log entry. - /// - public string Source { get; } - /// - /// Gets the message of this log entry. - /// - /// - /// A string containing the message of this log entry. - /// - public string Message { get; } - /// - /// Gets the exception of this log entry. - /// - /// - /// An object associated with an incident; otherwise null. - /// - public Exception Exception { get; } - - /// - /// Initializes a new struct with the severity, source, message of the event, and - /// optionally, an exception. - /// - /// The severity of the event. - /// The source of the event. - /// The message of the event. - /// The exception of the event. - public LogMessage(LogSeverity severity, string source, string message, Exception exception = null) - { - Severity = severity; - Source = source; - Message = message; - Exception = exception; - } - - public override string ToString() => ToString(); - public string ToString(StringBuilder builder = null, bool fullException = true, bool prependTimestamp = true, DateTimeKind timestampKind = DateTimeKind.Local, int? padSource = 11) - { - string sourceName = Source; - string message = Message; - string exMessage = fullException ? Exception?.ToString() : Exception?.Message; - - int maxLength = 1 + - (prependTimestamp ? 8 : 0) + 1 + - (padSource.HasValue ? padSource.Value : sourceName?.Length ?? 0) + 1 + - (message?.Length ?? 0) + - (exMessage?.Length ?? 0) + 3; - - if (builder == null) - builder = new StringBuilder(maxLength); - else - { - builder.Clear(); - builder.EnsureCapacity(maxLength); - } - - if (prependTimestamp) - { - DateTime now; - if (timestampKind == DateTimeKind.Utc) - now = DateTime.UtcNow; - else - now = DateTime.Now; - if (now.Hour < 10) - builder.Append('0'); - builder.Append(now.Hour); - builder.Append(':'); - if (now.Minute < 10) - builder.Append('0'); - builder.Append(now.Minute); - builder.Append(':'); - if (now.Second < 10) - builder.Append('0'); - builder.Append(now.Second); - builder.Append(' '); - } - if (sourceName != null) - { - if (padSource.HasValue) - { - if (sourceName.Length < padSource.Value) - { - builder.Append(sourceName); - builder.Append(' ', padSource.Value - sourceName.Length); - } - else if (sourceName.Length > padSource.Value) - builder.Append(sourceName.Substring(0, padSource.Value)); - else - builder.Append(sourceName); - } - builder.Append(' '); - } - if (!string.IsNullOrEmpty(Message)) - { - for (int i = 0; i < message.Length; i++) - { - //Strip control chars - char c = message[i]; - if (!char.IsControl(c)) - builder.Append(c); - } - } - if (exMessage != null) - { - if (!string.IsNullOrEmpty(Message)) - { - builder.Append(':'); - builder.AppendLine(); - } - builder.Append(exMessage); - } - - return builder.ToString(); - } - } -} diff --git a/src/Discord.Net.Core/Logging/LogSeverity.cs b/src/Discord.Net.Core/Logging/LogSeverity.cs deleted file mode 100644 index f9b518c173..0000000000 --- a/src/Discord.Net.Core/Logging/LogSeverity.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace Discord -{ - /// - /// Specifies the severity of the log message. - /// - public enum LogSeverity - { - /// - /// Logs that contain the most severe level of error. This type of error indicate that immediate attention - /// may be required. - /// - Critical = 0, - /// - /// Logs that highlight when the flow of execution is stopped due to a failure. - /// - Error = 1, - /// - /// Logs that highlight an abnormal activity in the flow of execution. - /// - Warning = 2, - /// - /// Logs that track the general flow of the application. - /// - Info = 3, - /// - /// Logs that are used for interactive investigation during development. - /// - Verbose = 4, - /// - /// Logs that contain the most detailed messages. - /// - Debug = 5 - } -} diff --git a/src/Discord.Net.Core/Logging/Logger.cs b/src/Discord.Net.Core/Logging/Logger.cs deleted file mode 100644 index e71c569921..0000000000 --- a/src/Discord.Net.Core/Logging/Logger.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.Threading.Tasks; - -namespace Discord.Logging -{ - internal class Logger - { - private readonly LogManager _manager; - - public string Name { get; } - public LogSeverity Level => _manager.Level; - - public Logger(LogManager manager, string name) - { - _manager = manager; - Name = name; - } - - public Task LogAsync(LogSeverity severity, Exception exception = null) - => _manager.LogAsync(severity, Name, exception); - public Task LogAsync(LogSeverity severity, string message, Exception exception = null) - => _manager.LogAsync(severity, Name, message, exception); - public Task LogAsync(LogSeverity severity, FormattableString message, Exception exception = null) - => _manager.LogAsync(severity, Name, message, exception); - - - public Task ErrorAsync(Exception exception) - => _manager.ErrorAsync(Name, exception); - public Task ErrorAsync(string message, Exception exception = null) - => _manager.ErrorAsync(Name, message, exception); - - public Task ErrorAsync(FormattableString message, Exception exception = null) - => _manager.ErrorAsync(Name, message, exception); - - - public Task WarningAsync(Exception exception) - => _manager.WarningAsync(Name, exception); - public Task WarningAsync(string message, Exception exception = null) - => _manager.WarningAsync(Name, message, exception); - - public Task WarningAsync(FormattableString message, Exception exception = null) - => _manager.WarningAsync(Name, message, exception); - - - public Task InfoAsync(Exception exception) - => _manager.InfoAsync(Name, exception); - public Task InfoAsync(string message, Exception exception = null) - => _manager.InfoAsync(Name, message, exception); - - public Task InfoAsync(FormattableString message, Exception exception = null) - => _manager.InfoAsync(Name, message, exception); - - - public Task VerboseAsync(Exception exception) - => _manager.VerboseAsync(Name, exception); - public Task VerboseAsync(string message, Exception exception = null) - => _manager.VerboseAsync(Name, message, exception); - - public Task VerboseAsync(FormattableString message, Exception exception = null) - => _manager.VerboseAsync(Name, message, exception); - - - public Task DebugAsync(Exception exception) - => _manager.DebugAsync(Name, exception); - public Task DebugAsync(string message, Exception exception = null) - => _manager.DebugAsync(Name, message, exception); - - public Task DebugAsync(FormattableString message, Exception exception = null) - => _manager.DebugAsync(Name, message, exception); - - } -} diff --git a/src/Discord.Net.Rest/BaseDiscordClient.cs b/src/Discord.Net.Rest/BaseDiscordClient.cs index 68589a4f12..a17d11082a 100644 --- a/src/Discord.Net.Rest/BaseDiscordClient.cs +++ b/src/Discord.Net.Rest/BaseDiscordClient.cs @@ -1,4 +1,5 @@ using Discord.Logging; +using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -10,20 +11,17 @@ namespace Discord.Rest { public abstract class BaseDiscordClient : IDiscordClient { - public event Func Log { add { _logEvent.Add(value); } remove { _logEvent.Remove(value); } } - internal readonly AsyncEvent> _logEvent = new AsyncEvent>(); - public event Func LoggedIn { add { _loggedInEvent.Add(value); } remove { _loggedInEvent.Remove(value); } } private readonly AsyncEvent> _loggedInEvent = new AsyncEvent>(); public event Func LoggedOut { add { _loggedOutEvent.Add(value); } remove { _loggedOutEvent.Remove(value); } } private readonly AsyncEvent> _loggedOutEvent = new AsyncEvent>(); - internal readonly Logger _restLogger; + internal readonly ILogger _restLogger; private readonly SemaphoreSlim _stateLock; private bool _isFirstLogin, _isDisposed; internal API.DiscordRestApiClient ApiClient { get; } - internal LogManager LogManager { get; } + internal ILoggerFactory LogManager { get; } /// /// Gets the login state of the client. /// @@ -39,21 +37,32 @@ public abstract class BaseDiscordClient : IDiscordClient internal BaseDiscordClient(DiscordRestConfig config, API.DiscordRestApiClient client) { ApiClient = client; - LogManager = new LogManager(config.LogLevel); - LogManager.Message += async msg => await _logEvent.InvokeAsync(msg).ConfigureAwait(false); + LogManager = config.LoggerFactory; + + if (LogManager == null) + { + LogManager = new DefaultLoggerFactory(); + LogManager.AddProvider(new DefaultLoggerProvider()); + } _stateLock = new SemaphoreSlim(1, 1); _restLogger = LogManager.CreateLogger("Rest"); _isFirstLogin = config.DisplayInitialLog; - ApiClient.RequestQueue.RateLimitTriggered += async (id, info, endpoint) => + ApiClient.RequestQueue.RateLimitTriggered += (id, info, endpoint) => { if (info == null) - await _restLogger.VerboseAsync($"Preemptive Rate limit triggered: {endpoint} {(id.IsHashBucket ? $"(Bucket: {id.BucketHash})" : "")}").ConfigureAwait(false); + _restLogger.LogTrace($"Preemptive Rate limit triggered: {endpoint} {(id.IsHashBucket ? $"(Bucket: {id.BucketHash})" : "")}"); else - await _restLogger.WarningAsync($"Rate limit triggered: {endpoint} {(id.IsHashBucket ? $"(Bucket: {id.BucketHash})" : "")}").ConfigureAwait(false); + _restLogger.LogTrace($"Rate limit triggered: {endpoint} {(id.IsHashBucket ? $"(Bucket: {id.BucketHash})" : "")}"); + + return Task.CompletedTask; + }; + ApiClient.SentRequest += (method, endpoint, millis) => + { + _restLogger.LogTrace($"{method} {endpoint}: {millis} ms"); + return Task.CompletedTask; }; - ApiClient.SentRequest += async (method, endpoint, millis) => await _restLogger.VerboseAsync($"{method} {endpoint}: {millis} ms").ConfigureAwait(false); } public async Task LoginAsync(TokenType tokenType, string token, bool validateToken = true) @@ -70,7 +79,7 @@ internal virtual async Task LoginInternalAsync(TokenType tokenType, string token if (_isFirstLogin) { _isFirstLogin = false; - await LogManager.WriteInitialLog().ConfigureAwait(false); + _restLogger.LogInformation($"Discord.Net v{DiscordConfig.Version} (API v{DiscordConfig.APIVersion})"); } if (LoginState != LoginState.LoggedOut) @@ -90,7 +99,7 @@ internal virtual async Task LoginInternalAsync(TokenType tokenType, string token catch (ArgumentException ex) { // log these ArgumentExceptions and allow for the client to attempt to log in anyways - await LogManager.WarningAsync("Discord", "A supplied token was invalid.", ex).ConfigureAwait(false); + _restLogger.LogWarning(ex, "A supplied token was invalid."); } } diff --git a/src/Discord.Net.WebSocket/Audio/AudioClient.cs b/src/Discord.Net.WebSocket/Audio/AudioClient.cs index 3549fb1060..df3fc61235 100644 --- a/src/Discord.Net.WebSocket/Audio/AudioClient.cs +++ b/src/Discord.Net.WebSocket/Audio/AudioClient.cs @@ -3,6 +3,7 @@ using Discord.Logging; using Discord.Net.Converters; using Discord.WebSocket; +using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; @@ -30,7 +31,7 @@ public StreamPair(AudioInStream reader, AudioOutStream writer) } } - private readonly Logger _audioLogger; + private readonly ILogger _audioLogger; private readonly JsonSerializer _serializer; private readonly ConnectionManager _connection; private readonly SemaphoreSlim _stateLock; @@ -64,8 +65,16 @@ internal AudioClient(SocketGuild guild, int clientId, ulong channelId) _audioLogger = Discord.LogManager.CreateLogger($"Audio #{clientId}"); ApiClient = new DiscordVoiceAPIClient(guild.Id, Discord.WebSocketProvider, Discord.UdpSocketProvider); - ApiClient.SentGatewayMessage += async opCode => await _audioLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false); - ApiClient.SentDiscovery += async () => await _audioLogger.DebugAsync("Sent Discovery").ConfigureAwait(false); + ApiClient.SentGatewayMessage += opCode => + { + _audioLogger.LogDebug($"Sent {opCode}"); + return Task.CompletedTask; + }; + ApiClient.SentDiscovery += () => + { + _audioLogger.LogDebug("Sent Discovery"); + return Task.CompletedTask; + }; //ApiClient.SentData += async bytes => await _audioLogger.DebugAsync($"Sent {bytes} Bytes").ConfigureAwait(false); ApiClient.ReceivedEvent += ProcessMessageAsync; ApiClient.ReceivedPacket += ProcessPacketAsync; @@ -83,12 +92,20 @@ internal AudioClient(SocketGuild guild, int clientId, ulong channelId) _serializer = new JsonSerializer { ContractResolver = new DiscordContractResolver() }; _serializer.Error += (s, e) => { - _audioLogger.WarningAsync(e.ErrorContext.Error).GetAwaiter().GetResult(); + _audioLogger.LogWarning(e.ErrorContext.Error, e.ErrorContext.Error.Message); e.ErrorContext.Handled = true; }; - LatencyUpdated += async (old, val) => await _audioLogger.DebugAsync($"Latency = {val} ms").ConfigureAwait(false); - UdpLatencyUpdated += async (old, val) => await _audioLogger.DebugAsync($"UDP Latency = {val} ms").ConfigureAwait(false); + LatencyUpdated += (old, val) => + { + _audioLogger.LogDebug($"Latency = {val} ms"); + return Task.CompletedTask; + }; + UdpLatencyUpdated += (old, val) => + { + _audioLogger.LogDebug($"UDP Latency = {val} ms"); + return Task.CompletedTask; + }; } internal async Task StartAsync(string url, ulong userId, string sessionId, string token) @@ -112,10 +129,10 @@ public async Task StopAsync() private async Task OnConnectingAsync() { - await _audioLogger.DebugAsync("Connecting ApiClient").ConfigureAwait(false); + _audioLogger.LogDebug("Connecting ApiClient"); await ApiClient.ConnectAsync("wss://" + _url + "?v=" + DiscordConfig.VoiceAPIVersion).ConfigureAwait(false); - await _audioLogger.DebugAsync("Listening on port " + ApiClient.UdpPort).ConfigureAwait(false); - await _audioLogger.DebugAsync("Sending Identity").ConfigureAwait(false); + _audioLogger.LogDebug("Listening on port " + ApiClient.UdpPort); + _audioLogger.LogDebug("Sending Identity"); await ApiClient.SendIdentityAsync(_userId, _sessionId, _token).ConfigureAwait(false); //Wait for READY @@ -123,11 +140,11 @@ private async Task OnConnectingAsync() } private async Task OnDisconnectingAsync(Exception ex) { - await _audioLogger.DebugAsync("Disconnecting ApiClient").ConfigureAwait(false); + _audioLogger.LogDebug("Disconnecting ApiClient"); await ApiClient.DisconnectAsync().ConfigureAwait(false); //Wait for tasks to complete - await _audioLogger.DebugAsync("Waiting for heartbeater").ConfigureAwait(false); + _audioLogger.LogDebug("Waiting for heartbeater"); var heartbeatTask = _heartbeatTask; if (heartbeatTask != null) await heartbeatTask.ConfigureAwait(false); @@ -142,7 +159,7 @@ private async Task OnDisconnectingAsync(Exception ex) await ClearInputStreamsAsync().ConfigureAwait(false); - await _audioLogger.DebugAsync("Sending Voice State").ConfigureAwait(false); + _audioLogger.LogDebug("Sending Voice State"); await Discord.ApiClient.SendVoiceStateUpdateAsync(Guild.Id, null, false, false).ConfigureAwait(false); } @@ -224,7 +241,7 @@ private async Task ProcessMessageAsync(VoiceOpCode opCode, object payload) { case VoiceOpCode.Ready: { - await _audioLogger.DebugAsync("Received Ready").ConfigureAwait(false); + _audioLogger.LogDebug("Received Ready"); var data = (payload as JToken).ToObject(_serializer); _ssrc = data.SSRC; @@ -241,7 +258,7 @@ private async Task ProcessMessageAsync(VoiceOpCode opCode, object payload) break; case VoiceOpCode.SessionDescription: { - await _audioLogger.DebugAsync("Received SessionDescription").ConfigureAwait(false); + _audioLogger.LogDebug("Received SessionDescription"); var data = (payload as JToken).ToObject(_serializer); if (data.Mode != DiscordVoiceAPIClient.Mode) @@ -257,7 +274,7 @@ private async Task ProcessMessageAsync(VoiceOpCode opCode, object payload) break; case VoiceOpCode.HeartbeatAck: { - await _audioLogger.DebugAsync("Received HeartbeatAck").ConfigureAwait(false); + _audioLogger.LogDebug("Received HeartbeatAck"); if (_heartbeatTimes.TryDequeue(out long time)) { @@ -271,7 +288,7 @@ private async Task ProcessMessageAsync(VoiceOpCode opCode, object payload) break; case VoiceOpCode.Speaking: { - await _audioLogger.DebugAsync("Received Speaking").ConfigureAwait(false); + _audioLogger.LogDebug("Received Speaking"); var data = (payload as JToken).ToObject(_serializer); _ssrcMap[data.Ssrc] = data.UserId; //TODO: Memory Leak: SSRCs are never cleaned up @@ -280,13 +297,13 @@ private async Task ProcessMessageAsync(VoiceOpCode opCode, object payload) } break; default: - await _audioLogger.WarningAsync($"Unknown OpCode ({opCode})").ConfigureAwait(false); + _audioLogger.LogWarning($"Unknown OpCode ({opCode})"); return; } } catch (Exception ex) { - await _audioLogger.ErrorAsync($"Error handling {opCode}", ex).ConfigureAwait(false); + _audioLogger.LogError($"Error handling {opCode}", ex); return; } } @@ -298,7 +315,7 @@ private async Task ProcessPacketAsync(byte[] packet) { if (packet.Length != 70) { - await _audioLogger.DebugAsync("Malformed Packet").ConfigureAwait(false); + _audioLogger.LogDebug("Malformed Packet"); return; } string ip; @@ -310,18 +327,18 @@ private async Task ProcessPacketAsync(byte[] packet) } catch (Exception ex) { - await _audioLogger.DebugAsync("Malformed Packet", ex).ConfigureAwait(false); + _audioLogger.LogDebug("Malformed Packet", ex); return; } - await _audioLogger.DebugAsync("Received Discovery").ConfigureAwait(false); + _audioLogger.LogDebug("Received Discovery"); await ApiClient.SendSelectProtocol(ip, port).ConfigureAwait(false); } else if (_connection.State == ConnectionState.Connected) { if (packet.Length == 8) { - await _audioLogger.DebugAsync("Received Keepalive").ConfigureAwait(false); + _audioLogger.LogDebug("Received Keepalive"); ulong value = ((ulong)packet[0] >> 0) | @@ -350,17 +367,17 @@ private async Task ProcessPacketAsync(byte[] packet) { if (!RTPReadStream.TryReadSsrc(packet, 0, out var ssrc)) { - await _audioLogger.DebugAsync("Malformed Frame").ConfigureAwait(false); + _audioLogger.LogDebug("Malformed Frame"); return; } if (!_ssrcMap.TryGetValue(ssrc, out var userId)) { - await _audioLogger.DebugAsync($"Unknown SSRC {ssrc}").ConfigureAwait(false); + _audioLogger.LogDebug($"Unknown SSRC {ssrc}"); return; } if (!_streams.TryGetValue(userId, out var pair)) { - await _audioLogger.DebugAsync($"Unknown User {userId}").ConfigureAwait(false); + _audioLogger.LogDebug($"Unknown User {userId}"); return; } try @@ -369,7 +386,7 @@ private async Task ProcessPacketAsync(byte[] packet) } catch (Exception ex) { - await _audioLogger.DebugAsync("Malformed Frame", ex).ConfigureAwait(false); + _audioLogger.LogDebug(ex, "Malformed Frame"); return; } //await _audioLogger.DebugAsync($"Received {packet.Length} bytes from user {userId}").ConfigureAwait(false); @@ -378,7 +395,7 @@ private async Task ProcessPacketAsync(byte[] packet) } catch (Exception ex) { - await _audioLogger.WarningAsync("Failed to process UDP packet", ex).ConfigureAwait(false); + _audioLogger.LogWarning("Failed to process UDP packet", ex); return; } } @@ -388,7 +405,7 @@ private async Task RunHeartbeatAsync(int intervalMillis, CancellationToken cance // TODO: Clean this up when Discord's session patch is live try { - await _audioLogger.DebugAsync("Heartbeat Started").ConfigureAwait(false); + _audioLogger.LogDebug("Heartbeat Started"); while (!cancelToken.IsCancellationRequested) { var now = Environment.TickCount; @@ -408,27 +425,27 @@ private async Task RunHeartbeatAsync(int intervalMillis, CancellationToken cance } catch (Exception ex) { - await _audioLogger.WarningAsync("Failed to send heartbeat", ex).ConfigureAwait(false); + _audioLogger.LogWarning("Failed to send heartbeat", ex); } await Task.Delay(intervalMillis, cancelToken).ConfigureAwait(false); } - await _audioLogger.DebugAsync("Heartbeat Stopped").ConfigureAwait(false); + _audioLogger.LogDebug("Heartbeat Stopped"); } catch (OperationCanceledException) { - await _audioLogger.DebugAsync("Heartbeat Stopped").ConfigureAwait(false); + _audioLogger.LogDebug("Heartbeat Stopped"); } catch (Exception ex) { - await _audioLogger.ErrorAsync("Heartbeat Errored", ex).ConfigureAwait(false); + _audioLogger.LogError("Heartbeat Errored", ex); } } private async Task RunKeepaliveAsync(int intervalMillis, CancellationToken cancelToken) { try { - await _audioLogger.DebugAsync("Keepalive Started").ConfigureAwait(false); + _audioLogger.LogDebug("Keepalive Started"); while (!cancelToken.IsCancellationRequested) { var now = Environment.TickCount; @@ -441,20 +458,20 @@ private async Task RunKeepaliveAsync(int intervalMillis, CancellationToken cance } catch (Exception ex) { - await _audioLogger.WarningAsync("Failed to send keepalive", ex).ConfigureAwait(false); + _audioLogger.LogWarning("Failed to send keepalive", ex); } await Task.Delay(intervalMillis, cancelToken).ConfigureAwait(false); } - await _audioLogger.DebugAsync("Keepalive Stopped").ConfigureAwait(false); + _audioLogger.LogDebug("Keepalive Stopped"); } catch (OperationCanceledException) { - await _audioLogger.DebugAsync("Keepalive Stopped").ConfigureAwait(false); + _audioLogger.LogDebug("Keepalive Stopped"); } catch (Exception ex) { - await _audioLogger.ErrorAsync("Keepalive Errored", ex).ConfigureAwait(false); + _audioLogger.LogError("Keepalive Errored", ex); } } diff --git a/src/Discord.Net.WebSocket/Audio/Streams/BufferedWriteStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/BufferedWriteStream.cs index a2de252a2b..50f2bc606e 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/BufferedWriteStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/BufferedWriteStream.cs @@ -1,4 +1,5 @@ using Discord.Logging; +using Microsoft.Extensions.Logging; using System; using System.Collections.Concurrent; using System.Threading; @@ -33,14 +34,14 @@ public Frame(byte[] buffer, int bytes) private readonly ConcurrentQueue _queuedFrames; private readonly ConcurrentQueue _bufferPool; private readonly SemaphoreSlim _queueLock; - private readonly Logger _logger; + private readonly ILogger _logger; private readonly int _ticksPerFrame, _queueLength; private bool _isPreloaded; private int _silenceFrames; public BufferedWriteStream(AudioStream next, IAudioClient client, int bufferMillis, CancellationToken cancelToken, int maxFrameSize = 1500) : this(next, client as AudioClient, bufferMillis, cancelToken, null, maxFrameSize) { } - internal BufferedWriteStream(AudioStream next, AudioClient client, int bufferMillis, CancellationToken cancelToken, Logger logger, int maxFrameSize = 1500) + internal BufferedWriteStream(AudioStream next, AudioClient client, int bufferMillis, CancellationToken cancelToken, ILogger logger, int maxFrameSize = 1500) { //maxFrameSize = 1275 was too limiting at 128kbps,2ch,60ms _next = next; @@ -106,7 +107,7 @@ private Task Run() timestamp += OpusEncoder.FrameSamplesPerChannel; _silenceFrames = 0; #if DEBUG - var _ = _logger?.DebugAsync($"Sent {frame.Bytes} bytes ({_queuedFrames.Count} frames buffered)"); + _logger?.LogDebug($"Sent {frame.Bytes} bytes ({_queuedFrames.Count} frames buffered)"); #endif } else @@ -125,7 +126,7 @@ private Task Run() timestamp += OpusEncoder.FrameSamplesPerChannel; } #if DEBUG - var _ = _logger?.DebugAsync("Buffer underrun"); + _logger?.LogDebug("Buffer underrun"); #endif } } @@ -153,7 +154,7 @@ public override async Task WriteAsync(byte[] data, int offset, int count, Cancel if (!_bufferPool.TryDequeue(out byte[] buffer)) { #if DEBUG - var _ = _logger?.DebugAsync("Buffer overflow"); //Should never happen because of the queueLock + _logger?.LogDebug("Buffer overflow"); //Should never happen because of the queueLock #endif #pragma warning disable IDISP016 writeCancelToken?.Dispose(); @@ -165,7 +166,7 @@ public override async Task WriteAsync(byte[] data, int offset, int count, Cancel if (!_isPreloaded && _queuedFrames.Count == _queueLength) { #if DEBUG - var _ = _logger?.DebugAsync("Preloaded"); + _logger?.LogDebug("Preloaded"); #endif _isPreloaded = true; } diff --git a/src/Discord.Net.WebSocket/ConnectionManager.cs b/src/Discord.Net.WebSocket/ConnectionManager.cs index 2237e2d1f1..520cd81b92 100644 --- a/src/Discord.Net.WebSocket/ConnectionManager.cs +++ b/src/Discord.Net.WebSocket/ConnectionManager.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Threading.Tasks; using Discord.Net; +using Microsoft.Extensions.Logging; namespace Discord { @@ -14,7 +15,7 @@ internal class ConnectionManager : IDisposable private readonly AsyncEvent> _disconnectedEvent = new AsyncEvent>(); private readonly SemaphoreSlim _stateLock; - private readonly Logger _logger; + private readonly ILogger _logger; private readonly int _connectionTimeout; private readonly Func _onConnecting; private readonly Func _onDisconnecting; @@ -28,7 +29,7 @@ internal class ConnectionManager : IDisposable public ConnectionState State { get; private set; } public CancellationToken CancelToken { get; private set; } - internal ConnectionManager(SemaphoreSlim stateLock, Logger logger, int connectionTimeout, + internal ConnectionManager(SemaphoreSlim stateLock, ILogger logger, int connectionTimeout, Func onConnecting, Func onDisconnecting, Action> clientDisconnectHandler) { _stateLock = stateLock; @@ -85,12 +86,12 @@ public virtual async Task StartAsync() Error(ex); //In case this exception didn't come from another Error call if (!reconnectCancelToken.IsCancellationRequested) { - await _logger.WarningAsync(ex).ConfigureAwait(false); + _logger.LogWarning(ex, ex.Message); await DisconnectAsync(ex, true).ConfigureAwait(false); } else { - await _logger.ErrorAsync(ex).ConfigureAwait(false); + _logger.LogError(ex, ex.Message); await DisconnectAsync(ex, false).ConfigureAwait(false); } } @@ -124,7 +125,7 @@ private async Task ConnectAsync(CancellationTokenSource reconnectCancelToken) _connectionPromise = new TaskCompletionSource(); State = ConnectionState.Connecting; - await _logger.InfoAsync("Connecting").ConfigureAwait(false); + _logger.LogInformation("Connecting"); try { @@ -154,9 +155,9 @@ private async Task ConnectAsync(CancellationTokenSource reconnectCancelToken) throw innerEx; } - await _logger.InfoAsync("Connected").ConfigureAwait(false); + _logger.LogInformation("Connected"); State = ConnectionState.Connected; - await _logger.DebugAsync("Raising Event").ConfigureAwait(false); + _logger.LogDebug("Raising Event"); await _connectedEvent.InvokeAsync().ConfigureAwait(false); } catch (Exception ex) @@ -169,13 +170,13 @@ private async Task DisconnectAsync(Exception ex, bool isReconnecting) { if (State == ConnectionState.Disconnected) return; State = ConnectionState.Disconnecting; - await _logger.InfoAsync("Disconnecting").ConfigureAwait(false); + _logger.LogInformation("Disconnecting"); await _onDisconnecting(ex).ConfigureAwait(false); await _disconnectedEvent.InvokeAsync(ex, isReconnecting).ConfigureAwait(false); State = ConnectionState.Disconnected; - await _logger.InfoAsync("Disconnected").ConfigureAwait(false); + _logger.LogInformation("Disconnected"); } public async Task CompleteAsync() diff --git a/src/Discord.Net.WebSocket/DiscordShardedClient.cs b/src/Discord.Net.WebSocket/DiscordShardedClient.cs index 4c94b14e86..31ff641220 100644 --- a/src/Discord.Net.WebSocket/DiscordShardedClient.cs +++ b/src/Discord.Net.WebSocket/DiscordShardedClient.cs @@ -328,7 +328,6 @@ public override async Task SetActivityAsync(IActivity activity) private void RegisterEvents(DiscordSocketClient client, bool isPrimary) { - client.Log += (msg) => _logEvent.InvokeAsync(msg); client.LoggedOut += () => { var state = LoginState; diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index d4c96ab260..3435ea04db 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -5,6 +5,7 @@ using Discord.Net.Udp; using Discord.Net.WebSockets; using Discord.Rest; +using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; @@ -30,7 +31,7 @@ public partial class DiscordSocketClient : BaseSocketClient, IDiscordClient private readonly DiscordSocketClient _parentClient; private readonly ConcurrentQueue _heartbeatTimes; private readonly ConnectionManager _connection; - private readonly Logger _gatewayLogger; + private readonly ILogger _gatewayLogger; private readonly SemaphoreSlim _stateLock; private string _sessionId; @@ -144,6 +145,7 @@ private DiscordSocketClient(DiscordSocketConfig config, API.DiscordSocketApiClie _gatewayIntents = config.GatewayIntents; _stateLock = new SemaphoreSlim(1, 1); + _gatewayLogger = LogManager.CreateLogger(ShardId == 0 && TotalShards == 1 ? "Gateway" : $"Shard #{ShardId}"); _connection = new ConnectionManager(_stateLock, _gatewayLogger, config.ConnectionTimeout, OnConnectingAsync, OnDisconnectingAsync, x => ApiClient.Disconnected += x); @@ -157,18 +159,42 @@ private DiscordSocketClient(DiscordSocketConfig config, API.DiscordSocketApiClie _serializer = new JsonSerializer { ContractResolver = new DiscordContractResolver() }; _serializer.Error += (s, e) => { - _gatewayLogger.WarningAsync("Serializer Error", e.ErrorContext.Error).GetAwaiter().GetResult(); + _gatewayLogger.LogWarning(e.ErrorContext.Error, "Serializer Error"); e.ErrorContext.Handled = true; }; - ApiClient.SentGatewayMessage += async opCode => await _gatewayLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false); + ApiClient.SentGatewayMessage += opCode => + { + _gatewayLogger.LogDebug($"Sent {opCode}"); + return Task.CompletedTask; + }; ApiClient.ReceivedGatewayEvent += ProcessMessageAsync; - LeftGuild += async g => await _gatewayLogger.InfoAsync($"Left {g.Name}").ConfigureAwait(false); - JoinedGuild += async g => await _gatewayLogger.InfoAsync($"Joined {g.Name}").ConfigureAwait(false); - GuildAvailable += async g => await _gatewayLogger.VerboseAsync($"Connected to {g.Name}").ConfigureAwait(false); - GuildUnavailable += async g => await _gatewayLogger.VerboseAsync($"Disconnected from {g.Name}").ConfigureAwait(false); - LatencyUpdated += async (old, val) => await _gatewayLogger.DebugAsync($"Latency = {val} ms").ConfigureAwait(false); + LeftGuild += g => + { + _gatewayLogger.LogInformation($"Left {g.Name}"); + return Task.CompletedTask; + }; + JoinedGuild += g => + { + _gatewayLogger.LogInformation($"Joined {g.Name}"); + return Task.CompletedTask; + }; + GuildAvailable += g => + { + _gatewayLogger.LogTrace($"Connected to {g.Name}"); + return Task.CompletedTask; + }; + GuildUnavailable += g => + { + _gatewayLogger.LogTrace($"Disconnected from {g.Name}"); + return Task.CompletedTask; + }; + LatencyUpdated += (old, val) => + { + _gatewayLogger.LogDebug($"Latency = {val} ms"); + return Task.CompletedTask; + }; GuildAvailable += g => { @@ -232,17 +258,17 @@ private async Task OnConnectingAsync() } try { - await _gatewayLogger.DebugAsync("Connecting ApiClient").ConfigureAwait(false); + _gatewayLogger.LogDebug("Connecting ApiClient"); await ApiClient.ConnectAsync().ConfigureAwait(false); if (_sessionId != null) { - await _gatewayLogger.DebugAsync("Resuming").ConfigureAwait(false); + _gatewayLogger.LogDebug("Resuming"); await ApiClient.SendResumeAsync(_sessionId, _lastSeq).ConfigureAwait(false); } else { - await _gatewayLogger.DebugAsync("Identifying").ConfigureAwait(false); + _gatewayLogger.LogDebug("Identifying"); await ApiClient.SendIdentifyAsync(shardID: ShardId, totalShards: TotalShards, guildSubscriptions: _guildSubscriptions, gatewayIntents: _gatewayIntents, presence: BuildCurrentStatus()).ConfigureAwait(false); } @@ -258,11 +284,11 @@ private async Task OnConnectingAsync() private async Task OnDisconnectingAsync(Exception ex) { - await _gatewayLogger.DebugAsync("Disconnecting ApiClient").ConfigureAwait(false); + _gatewayLogger.LogDebug("Disconnecting ApiClient"); await ApiClient.DisconnectAsync(ex).ConfigureAwait(false); //Wait for tasks to complete - await _gatewayLogger.DebugAsync("Waiting for heartbeater").ConfigureAwait(false); + _gatewayLogger.LogDebug("Waiting for heartbeater"); var heartbeatTask = _heartbeatTask; if (heartbeatTask != null) await heartbeatTask.ConfigureAwait(false); @@ -271,18 +297,18 @@ private async Task OnDisconnectingAsync(Exception ex) while (_heartbeatTimes.TryDequeue(out _)) { } _lastMessageTime = 0; - await _gatewayLogger.DebugAsync("Waiting for guild downloader").ConfigureAwait(false); + _gatewayLogger.LogDebug("Waiting for guild downloader"); var guildDownloadTask = _guildDownloadTask; if (guildDownloadTask != null) await guildDownloadTask.ConfigureAwait(false); _guildDownloadTask = null; //Clear large guild queue - await _gatewayLogger.DebugAsync("Clearing large guild queue").ConfigureAwait(false); + _gatewayLogger.LogDebug("Clearing large guild queue"); while (_largeGuilds.TryDequeue(out _)) { } //Raise virtual GUILD_UNAVAILABLEs - await _gatewayLogger.DebugAsync("Raising virtual GuildUnavailables").ConfigureAwait(false); + _gatewayLogger.LogDebug("Raising virtual GuildUnavailables"); foreach (var guild in State.Guilds) { if (guild.IsAvailable) @@ -524,7 +550,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty { case GatewayOpCode.Hello: { - await _gatewayLogger.DebugAsync("Received Hello").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Hello"); var data = (payload as JToken).ToObject(_serializer); _heartbeatTask = RunHeartbeatAsync(data.HeartbeatInterval, _connection.CancelToken); @@ -532,14 +558,14 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty break; case GatewayOpCode.Heartbeat: { - await _gatewayLogger.DebugAsync("Received Heartbeat").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Heartbeat"); await ApiClient.SendHeartbeatAsync(_lastSeq).ConfigureAwait(false); } break; case GatewayOpCode.HeartbeatAck: { - await _gatewayLogger.DebugAsync("Received HeartbeatAck").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received HeartbeatAck"); if (_heartbeatTimes.TryDequeue(out long time)) { @@ -553,8 +579,8 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty break; case GatewayOpCode.InvalidSession: { - await _gatewayLogger.DebugAsync("Received InvalidSession").ConfigureAwait(false); - await _gatewayLogger.WarningAsync("Failed to resume previous session").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received InvalidSession"); + _gatewayLogger.LogWarning("Failed to resume previous session"); _sessionId = null; _lastSeq = 0; @@ -577,7 +603,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty break; case GatewayOpCode.Reconnect: { - await _gatewayLogger.DebugAsync("Received Reconnect").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Reconnect"); _connection.Error(new GatewayReconnectException("Server requested a reconnect")); } break; @@ -589,7 +615,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty { try { - await _gatewayLogger.DebugAsync("Received Dispatch (READY)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (READY)"); var data = (payload as JToken).ToObject(_serializer); var state = new ClientState(data.Guilds.Length, data.PrivateChannels.Length); @@ -637,14 +663,14 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty _ = DownloadUsersAsync(Guilds.Where(x => x.IsAvailable && !x.HasAllMembers)); await TimedInvokeAsync(_readyEvent, nameof(Ready)).ConfigureAwait(false); - await _gatewayLogger.InfoAsync("Ready").ConfigureAwait(false); + _gatewayLogger.LogInformation("Ready"); }); _ = _connection.CompleteAsync(); } break; case "RESUMED": { - await _gatewayLogger.DebugAsync("Received Dispatch (RESUMED)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (RESUMED)"); _ = _connection.CompleteAsync(); @@ -655,7 +681,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty await GuildAvailableAsync(guild).ConfigureAwait(false); } - await _gatewayLogger.InfoAsync("Resumed previous session").ConfigureAwait(false); + _gatewayLogger.LogInformation("Resumed previous session"); } break; @@ -668,7 +694,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty { type = "GUILD_AVAILABLE"; _lastGuildAvailableTime = Environment.TickCount; - await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_AVAILABLE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (GUILD_AVAILABLE)"); var guild = State.GetGuild(data.Id); if (guild != null) @@ -693,7 +719,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty } else { - await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_CREATE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (GUILD_CREATE)"); var guild = AddGuild(data, State); if (guild != null) @@ -711,7 +737,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty break; case "GUILD_UPDATE": { - await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_UPDATE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (GUILD_UPDATE)"); var data = (payload as JToken).ToObject(_serializer); var guild = State.GetGuild(data.Id); @@ -730,7 +756,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty break; case "GUILD_EMOJIS_UPDATE": { - await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_EMOJIS_UPDATE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (GUILD_EMOJIS_UPDATE)"); var data = (payload as JToken).ToObject(_serializer); var guild = State.GetGuild(data.GuildId); @@ -749,7 +775,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty break; case "GUILD_SYNC": { - await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_SYNC)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (GUILD_SYNC)"); var data = (payload as JToken).ToObject(_serializer); var guild = State.GetGuild(data.Id); if (guild != null) @@ -775,7 +801,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty if (data.Unavailable == true) { type = "GUILD_UNAVAILABLE"; - await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_UNAVAILABLE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (GUILD_UNAVAILABLE)"); var guild = State.GetGuild(data.Id); if (guild != null) @@ -791,7 +817,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty } else { - await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_DELETE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (GUILD_DELETE)"); var guild = RemoveGuild(data.Id); if (guild != null) @@ -812,7 +838,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty //Channels case "CHANNEL_CREATE": { - await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_CREATE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (CHANNEL_CREATE)"); var data = (payload as JToken).ToObject(_serializer); SocketChannel channel = null; @@ -849,7 +875,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty break; case "CHANNEL_UPDATE": { - await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_UPDATE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (CHANNEL_UPDATE)"); var data = (payload as JToken).ToObject(_serializer); var channel = State.GetChannel(data.Id); @@ -876,7 +902,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty break; case "CHANNEL_DELETE": { - await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_DELETE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (CHANNEL_DELETE)"); SocketChannel channel = null; var data = (payload as JToken).ToObject(_serializer); @@ -915,7 +941,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty //Members case "GUILD_MEMBER_ADD": { - await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_MEMBER_ADD)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (GUILD_MEMBER_ADD)"); var data = (payload as JToken).ToObject(_serializer); var guild = State.GetGuild(data.GuildId); @@ -941,7 +967,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty break; case "GUILD_MEMBER_UPDATE": { - await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_MEMBER_UPDATE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (GUILD_MEMBER_UPDATE)"); var data = (payload as JToken).ToObject(_serializer); var guild = State.GetGuild(data.GuildId); @@ -986,7 +1012,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty break; case "GUILD_MEMBER_REMOVE": { - await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_MEMBER_REMOVE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (GUILD_MEMBER_REMOVE)"); var data = (payload as JToken).ToObject(_serializer); var guild = State.GetGuild(data.GuildId); @@ -1021,7 +1047,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty break; case "GUILD_MEMBERS_CHUNK": { - await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_MEMBERS_CHUNK)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (GUILD_MEMBERS_CHUNK)"); var data = (payload as JToken).ToObject(_serializer); var guild = State.GetGuild(data.GuildId); @@ -1045,7 +1071,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty break; case "CHANNEL_RECIPIENT_ADD": { - await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_RECIPIENT_ADD)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (CHANNEL_RECIPIENT_ADD)"); var data = (payload as JToken).ToObject(_serializer); if (State.GetChannel(data.ChannelId) is SocketGroupChannel channel) @@ -1062,7 +1088,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty break; case "CHANNEL_RECIPIENT_REMOVE": { - await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_RECIPIENT_REMOVE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (CHANNEL_RECIPIENT_REMOVE)"); var data = (payload as JToken).ToObject(_serializer); if (State.GetChannel(data.ChannelId) is SocketGroupChannel channel) @@ -1087,7 +1113,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty //Roles case "GUILD_ROLE_CREATE": { - await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_ROLE_CREATE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (GUILD_ROLE_CREATE)"); var data = (payload as JToken).ToObject(_serializer); var guild = State.GetGuild(data.GuildId); @@ -1111,7 +1137,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty break; case "GUILD_ROLE_UPDATE": { - await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_ROLE_UPDATE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (GUILD_ROLE_UPDATE)"); var data = (payload as JToken).ToObject(_serializer); var guild = State.GetGuild(data.GuildId); @@ -1146,7 +1172,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty break; case "GUILD_ROLE_DELETE": { - await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_ROLE_DELETE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (GUILD_ROLE_DELETE)"); var data = (payload as JToken).ToObject(_serializer); var guild = State.GetGuild(data.GuildId); @@ -1180,7 +1206,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty //Bans case "GUILD_BAN_ADD": { - await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_BAN_ADD)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (GUILD_BAN_ADD)"); var data = (payload as JToken).ToObject(_serializer); var guild = State.GetGuild(data.GuildId); @@ -1206,7 +1232,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty break; case "GUILD_BAN_REMOVE": { - await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_BAN_REMOVE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (GUILD_BAN_REMOVE)"); var data = (payload as JToken).ToObject(_serializer); var guild = State.GetGuild(data.GuildId); @@ -1234,7 +1260,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty //Messages case "MESSAGE_CREATE": { - await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_CREATE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (MESSAGE_CREATE)"); var data = (payload as JToken).ToObject(_serializer); if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel) @@ -1291,7 +1317,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty break; case "MESSAGE_UPDATE": { - await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_UPDATE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (MESSAGE_UPDATE)"); var data = (payload as JToken).ToObject(_serializer); if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel) @@ -1344,7 +1370,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty break; case "MESSAGE_DELETE": { - await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (MESSAGE_DELETE)"); var data = (payload as JToken).ToObject(_serializer); if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel) @@ -1371,7 +1397,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty break; case "MESSAGE_REACTION_ADD": { - await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_REACTION_ADD)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (MESSAGE_REACTION_ADD)"); var data = (payload as JToken).ToObject(_serializer); if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel) @@ -1404,7 +1430,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty break; case "MESSAGE_REACTION_REMOVE": { - await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_REACTION_REMOVE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (MESSAGE_REACTION_REMOVE)"); var data = (payload as JToken).ToObject(_serializer); if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel) @@ -1437,7 +1463,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty break; case "MESSAGE_REACTION_REMOVE_ALL": { - await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_REACTION_REMOVE_ALL)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (MESSAGE_REACTION_REMOVE_ALL)"); var data = (payload as JToken).ToObject(_serializer); if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel) @@ -1459,7 +1485,7 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty break; case "MESSAGE_REACTION_REMOVE_EMOJI": { - await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_REACTION_REMOVE_EMOJI)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (MESSAGE_REACTION_REMOVE_EMOJI)"); var data = (payload as JToken).ToObject(_serializer); if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel) @@ -1487,11 +1513,11 @@ private async Task ProcessMessageAsync(GatewayOpCode opCode, int? seq, string ty break; case "MESSAGE_DELETE_BULK": { - await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE_BULK)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (MESSAGE_DELETE_BULK)"); if (!ExclusiveBulkDelete.HasValue) { - await _gatewayLogger.WarningAsync("A bulk delete event has been received, but the event handling behavior has not been set. " + + _gatewayLogger.LogWarning("A bulk delete event has been received, but the event handling behavior has not been set. " + "To suppress this message, set the ExclusiveBulkDelete configuration property. " + "This message will appear only once."); ExclusiveBulkDelete = false; @@ -1532,7 +1558,7 @@ await _gatewayLogger.WarningAsync("A bulk delete event has been received, but th //Statuses case "PRESENCE_UPDATE": { - await _gatewayLogger.DebugAsync("Received Dispatch (PRESENCE_UPDATE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (PRESENCE_UPDATE)"); var data = (payload as JToken).ToObject(_serializer); @@ -1591,7 +1617,7 @@ await _gatewayLogger.WarningAsync("A bulk delete event has been received, but th break; case "TYPING_START": { - await _gatewayLogger.DebugAsync("Received Dispatch (TYPING_START)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (TYPING_START)"); var data = (payload as JToken).ToObject(_serializer); if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel) @@ -1618,7 +1644,7 @@ await _gatewayLogger.WarningAsync("A bulk delete event has been received, but th //Users case "USER_UPDATE": { - await _gatewayLogger.DebugAsync("Received Dispatch (USER_UPDATE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (USER_UPDATE)"); var data = (payload as JToken).ToObject(_serializer); if (data.Id == CurrentUser.Id) @@ -1629,7 +1655,7 @@ await _gatewayLogger.WarningAsync("A bulk delete event has been received, but th } else { - await _gatewayLogger.WarningAsync("Received USER_UPDATE for wrong user.").ConfigureAwait(false); + _gatewayLogger.LogWarning("Received USER_UPDATE for wrong user."); return; } } @@ -1638,7 +1664,7 @@ await _gatewayLogger.WarningAsync("A bulk delete event has been received, but th //Voice case "VOICE_STATE_UPDATE": { - await _gatewayLogger.DebugAsync("Received Dispatch (VOICE_STATE_UPDATE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (VOICE_STATE_UPDATE)"); var data = (payload as JToken).ToObject(_serializer); SocketUser user; @@ -1712,7 +1738,7 @@ await _gatewayLogger.WarningAsync("A bulk delete event has been received, but th break; case "VOICE_SERVER_UPDATE": { - await _gatewayLogger.DebugAsync("Received Dispatch (VOICE_SERVER_UPDATE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (VOICE_SERVER_UPDATE)"); var data = (payload as JToken).ToObject(_serializer); var guild = State.GetGuild(data.GuildId); @@ -1745,7 +1771,7 @@ await _gatewayLogger.WarningAsync("A bulk delete event has been received, but th //Invites case "INVITE_CREATE": { - await _gatewayLogger.DebugAsync("Received Dispatch (INVITE_CREATE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (INVITE_CREATE)"); var data = (payload as JToken).ToObject(_serializer); if (State.GetChannel(data.ChannelId) is SocketGuildChannel channel) @@ -1778,7 +1804,7 @@ await _gatewayLogger.WarningAsync("A bulk delete event has been received, but th break; case "INVITE_DELETE": { - await _gatewayLogger.DebugAsync("Received Dispatch (INVITE_DELETE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Received Dispatch (INVITE_DELETE)"); var data = (payload as JToken).ToObject(_serializer); if (State.GetChannel(data.ChannelId) is SocketGuildChannel channel) @@ -1802,41 +1828,41 @@ await _gatewayLogger.WarningAsync("A bulk delete event has been received, but th //Ignored (User only) case "CHANNEL_PINS_ACK": - await _gatewayLogger.DebugAsync("Ignored Dispatch (CHANNEL_PINS_ACK)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Ignored Dispatch (CHANNEL_PINS_ACK)"); break; case "CHANNEL_PINS_UPDATE": - await _gatewayLogger.DebugAsync("Ignored Dispatch (CHANNEL_PINS_UPDATE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Ignored Dispatch (CHANNEL_PINS_UPDATE)"); break; case "GUILD_INTEGRATIONS_UPDATE": - await _gatewayLogger.DebugAsync("Ignored Dispatch (GUILD_INTEGRATIONS_UPDATE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Ignored Dispatch (GUILD_INTEGRATIONS_UPDATE)"); break; case "MESSAGE_ACK": - await _gatewayLogger.DebugAsync("Ignored Dispatch (MESSAGE_ACK)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Ignored Dispatch (MESSAGE_ACK)"); break; case "PRESENCES_REPLACE": - await _gatewayLogger.DebugAsync("Ignored Dispatch (PRESENCES_REPLACE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Ignored Dispatch (PRESENCES_REPLACE)"); break; case "USER_SETTINGS_UPDATE": - await _gatewayLogger.DebugAsync("Ignored Dispatch (USER_SETTINGS_UPDATE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Ignored Dispatch (USER_SETTINGS_UPDATE)"); break; case "WEBHOOKS_UPDATE": - await _gatewayLogger.DebugAsync("Ignored Dispatch (WEBHOOKS_UPDATE)").ConfigureAwait(false); + _gatewayLogger.LogDebug("Ignored Dispatch (WEBHOOKS_UPDATE)"); break; //Others default: - await _gatewayLogger.WarningAsync($"Unknown Dispatch ({type})").ConfigureAwait(false); + _gatewayLogger.LogWarning($"Unknown Dispatch ({type})"); break; } break; default: - await _gatewayLogger.WarningAsync($"Unknown OpCode ({opCode})").ConfigureAwait(false); + _gatewayLogger.LogWarning($"Unknown OpCode ({opCode})"); break; } } catch (Exception ex) { - await _gatewayLogger.ErrorAsync($"Error handling {opCode}{(type != null ? $" ({type})" : "")}", ex).ConfigureAwait(false); + _gatewayLogger.LogError(ex, $"Error handling {opCode}{(type != null ? $" ({type})" : "")}"); } } @@ -1844,7 +1870,7 @@ private async Task RunHeartbeatAsync(int intervalMillis, CancellationToken cance { try { - await _gatewayLogger.DebugAsync("Heartbeat Started").ConfigureAwait(false); + _gatewayLogger.LogDebug("Heartbeat Started"); while (!cancelToken.IsCancellationRequested) { int now = Environment.TickCount; @@ -1866,20 +1892,20 @@ private async Task RunHeartbeatAsync(int intervalMillis, CancellationToken cance } catch (Exception ex) { - await _gatewayLogger.WarningAsync("Heartbeat Errored", ex).ConfigureAwait(false); + _gatewayLogger.LogWarning("Heartbeat Errored", ex); } await Task.Delay(intervalMillis, cancelToken).ConfigureAwait(false); } - await _gatewayLogger.DebugAsync("Heartbeat Stopped").ConfigureAwait(false); + _gatewayLogger.LogDebug("Heartbeat Stopped"); } catch (OperationCanceledException) { - await _gatewayLogger.DebugAsync("Heartbeat Stopped").ConfigureAwait(false); + _gatewayLogger.LogDebug("Heartbeat Stopped"); } catch (Exception ex) { - await _gatewayLogger.ErrorAsync("Heartbeat Errored", ex).ConfigureAwait(false); + _gatewayLogger.LogError(ex, "Heartbeat Errored"); } } /*public async Task WaitForGuildsAsync() @@ -1888,23 +1914,23 @@ private async Task RunHeartbeatAsync(int intervalMillis, CancellationToken cance if (downloadTask != null) await _guildDownloadTask.ConfigureAwait(false); }*/ - private async Task WaitForGuildsAsync(CancellationToken cancelToken, Logger logger) + private async Task WaitForGuildsAsync(CancellationToken cancelToken, ILogger logger) { //Wait for GUILD_AVAILABLEs try { - await logger.DebugAsync("GuildDownloader Started").ConfigureAwait(false); + logger.LogDebug("GuildDownloader Started"); while ((_unavailableGuildCount != 0) && (Environment.TickCount - _lastGuildAvailableTime < BaseConfig.MaxWaitBetweenGuildAvailablesBeforeReady)) await Task.Delay(500, cancelToken).ConfigureAwait(false); - await logger.DebugAsync("GuildDownloader Stopped").ConfigureAwait(false); + logger.LogDebug("GuildDownloader Stopped"); } catch (OperationCanceledException) { - await logger.DebugAsync("GuildDownloader Stopped").ConfigureAwait(false); + logger.LogDebug("GuildDownloader Stopped"); } catch (Exception ex) { - await logger.ErrorAsync("GuildDownloader Errored", ex).ConfigureAwait(false); + logger.LogError(ex, "GuildDownloader Errored"); } } private async Task SyncGuildsAsync() @@ -2034,40 +2060,45 @@ private async Task TimeoutWrap(string name, Func action) var handlersTask = action(); if (await Task.WhenAny(timeoutTask, handlersTask).ConfigureAwait(false) == timeoutTask) { - await _gatewayLogger.WarningAsync($"A {name} handler is blocking the gateway task.").ConfigureAwait(false); + _gatewayLogger.LogWarning($"A {name} handler is blocking the gateway task."); } await handlersTask.ConfigureAwait(false); //Ensure the handler completes } catch (Exception ex) { - await _gatewayLogger.WarningAsync($"A {name} handler has thrown an unhandled exception.", ex).ConfigureAwait(false); + _gatewayLogger.LogWarning(ex, $"A {name} handler has thrown an unhandled exception."); } } - private async Task UnknownGlobalUserAsync(string evnt, ulong userId) + private Task UnknownGlobalUserAsync(string evnt, ulong userId) { string details = $"{evnt} User={userId}"; - await _gatewayLogger.WarningAsync($"Unknown User ({details}).").ConfigureAwait(false); + _gatewayLogger.LogWarning($"Unknown User ({details})."); + return Task.CompletedTask; } - private async Task UnknownChannelUserAsync(string evnt, ulong userId, ulong channelId) + private Task UnknownChannelUserAsync(string evnt, ulong userId, ulong channelId) { string details = $"{evnt} User={userId} Channel={channelId}"; - await _gatewayLogger.WarningAsync($"Unknown User ({details}).").ConfigureAwait(false); + _gatewayLogger.LogWarning($"Unknown User ({details})."); + return Task.CompletedTask; } - private async Task UnknownGuildUserAsync(string evnt, ulong userId, ulong guildId) + private Task UnknownGuildUserAsync(string evnt, ulong userId, ulong guildId) { string details = $"{evnt} User={userId} Guild={guildId}"; - await _gatewayLogger.WarningAsync($"Unknown User ({details}).").ConfigureAwait(false); + _gatewayLogger.LogWarning($"Unknown User ({details})."); + return Task.CompletedTask; } - private async Task IncompleteGuildUserAsync(string evnt, ulong userId, ulong guildId) + private Task IncompleteGuildUserAsync(string evnt, ulong userId, ulong guildId) { string details = $"{evnt} User={userId} Guild={guildId}"; - await _gatewayLogger.DebugAsync($"User has not been downloaded ({details}).").ConfigureAwait(false); + _gatewayLogger.LogDebug($"User has not been downloaded ({details})."); + return Task.CompletedTask; } - private async Task UnknownChannelAsync(string evnt, ulong channelId) + private Task UnknownChannelAsync(string evnt, ulong channelId) { string details = $"{evnt} Channel={channelId}"; - await _gatewayLogger.WarningAsync($"Unknown Channel ({details}).").ConfigureAwait(false); + _gatewayLogger.LogWarning($"Unknown Channel ({details})."); + return Task.CompletedTask; } private async Task UnknownChannelAsync(string evnt, ulong channelId, ulong guildId) { @@ -2077,22 +2108,25 @@ private async Task UnknownChannelAsync(string evnt, ulong channelId, ulong guild return; } string details = $"{evnt} Channel={channelId} Guild={guildId}"; - await _gatewayLogger.WarningAsync($"Unknown Channel ({details}).").ConfigureAwait(false); + _gatewayLogger.LogWarning($"Unknown Channel ({details})."); } - private async Task UnknownRoleAsync(string evnt, ulong roleId, ulong guildId) + private Task UnknownRoleAsync(string evnt, ulong roleId, ulong guildId) { string details = $"{evnt} Role={roleId} Guild={guildId}"; - await _gatewayLogger.WarningAsync($"Unknown Role ({details}).").ConfigureAwait(false); + _gatewayLogger.LogWarning($"Unknown Role ({details})."); + return Task.CompletedTask; } - private async Task UnknownGuildAsync(string evnt, ulong guildId) + private Task UnknownGuildAsync(string evnt, ulong guildId) { string details = $"{evnt} Guild={guildId}"; - await _gatewayLogger.WarningAsync($"Unknown Guild ({details}).").ConfigureAwait(false); + _gatewayLogger.LogWarning($"Unknown Guild ({details})."); + return Task.CompletedTask; } - private async Task UnsyncedGuildAsync(string evnt, ulong guildId) + private Task UnsyncedGuildAsync(string evnt, ulong guildId) { string details = $"{evnt} Guild={guildId}"; - await _gatewayLogger.DebugAsync($"Unsynced Guild ({details}).").ConfigureAwait(false); + _gatewayLogger.LogDebug($"Unsynced Guild ({details})."); + return Task.CompletedTask; } internal int GetAudioId() => _nextAudioId++; diff --git a/src/Discord.Net.Webhook/DiscordWebhookClient.cs b/src/Discord.Net.Webhook/DiscordWebhookClient.cs index a6d4ef1831..8453f1c9ef 100644 --- a/src/Discord.Net.Webhook/DiscordWebhookClient.cs +++ b/src/Discord.Net.Webhook/DiscordWebhookClient.cs @@ -6,21 +6,19 @@ using System.Threading.Tasks; using Discord.Logging; using Discord.Rest; +using Microsoft.Extensions.Logging; namespace Discord.Webhook { /// A client responsible for connecting as a Webhook. public class DiscordWebhookClient : IDisposable { - public event Func Log { add { _logEvent.Add(value); } remove { _logEvent.Remove(value); } } - internal readonly AsyncEvent> _logEvent = new AsyncEvent>(); - private readonly ulong _webhookId; internal IWebhook Webhook; - internal readonly Logger _restLogger; + internal readonly ILogger _restLogger; internal API.DiscordRestApiClient ApiClient { get; } - internal LogManager LogManager { get; } + internal ILoggerFactory LogManager { get; } /// Creates a new Webhook Discord client. public DiscordWebhookClient(IWebhook webhook) @@ -69,19 +67,31 @@ public DiscordWebhookClient(string webhookUrl, DiscordRestConfig config) : this( private DiscordWebhookClient(DiscordRestConfig config) { ApiClient = CreateApiClient(config); - LogManager = new LogManager(config.LogLevel); - LogManager.Message += async msg => await _logEvent.InvokeAsync(msg).ConfigureAwait(false); + + LogManager = config.LoggerFactory; + + if (LogManager == null) + { + LogManager = new DefaultLoggerFactory(); + LogManager.AddProvider(new DefaultLoggerProvider()); + } _restLogger = LogManager.CreateLogger("Rest"); - ApiClient.RequestQueue.RateLimitTriggered += async (id, info, endpoint) => + ApiClient.RequestQueue.RateLimitTriggered += (id, info, endpoint) => { if (info == null) - await _restLogger.VerboseAsync($"Preemptive Rate limit triggered: {endpoint} {(id.IsHashBucket ? $"(Bucket: {id.BucketHash})" : "")}").ConfigureAwait(false); + _restLogger.LogTrace($"Preemptive Rate limit triggered: {endpoint} {(id.IsHashBucket ? $"(Bucket: {id.BucketHash})" : "")}"); else - await _restLogger.WarningAsync($"Rate limit triggered: {endpoint} {(id.IsHashBucket ? $"(Bucket: {id.BucketHash})" : "")}").ConfigureAwait(false); + _restLogger.LogWarning($"Rate limit triggered: {endpoint} {(id.IsHashBucket ? $"(Bucket: {id.BucketHash})" : "")}"); + + return Task.CompletedTask; + }; + ApiClient.SentRequest += (method, endpoint, millis) => + { + _restLogger.LogTrace($"{method} {endpoint}: {millis} ms"); + return Task.CompletedTask; }; - ApiClient.SentRequest += async (method, endpoint, millis) => await _restLogger.VerboseAsync($"{method} {endpoint}: {millis} ms").ConfigureAwait(false); } private static API.DiscordRestApiClient CreateApiClient(DiscordRestConfig config) => new API.DiscordRestApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent); diff --git a/test/Discord.Net.Tests.Integration/ChannelsTests.cs b/test/Discord.Net.Tests.Integration/ChannelsTests.cs index 3bf60772f9..20087f872c 100644 --- a/test/Discord.Net.Tests.Integration/ChannelsTests.cs +++ b/test/Discord.Net.Tests.Integration/ChannelsTests.cs @@ -22,12 +22,6 @@ public ChannelsTests(RestGuildFixture guildFixture, ITestOutputHelper output) this.output = output; output.WriteLine($"RestGuildFixture using guild: {guild.Id}"); // capture all console output - guildFixture.Client.Log += LogAsync; - } - private Task LogAsync(LogMessage message) - { - output.WriteLine(message.ToString()); - return Task.CompletedTask; } /// diff --git a/test/Discord.Net.Tests.Integration/DiscordRestClientFixture.cs b/test/Discord.Net.Tests.Integration/DiscordRestClientFixture.cs index 810e458762..dbf8c89542 100644 --- a/test/Discord.Net.Tests.Integration/DiscordRestClientFixture.cs +++ b/test/Discord.Net.Tests.Integration/DiscordRestClientFixture.cs @@ -19,7 +19,6 @@ public DiscordRestClientFixture() throw new Exception("The DNET_TEST_TOKEN environment variable was not provided."); Client = new DiscordRestClient(new DiscordRestConfig() { - LogLevel = LogSeverity.Debug, DefaultRetryMode = RetryMode.AlwaysRetry }); Client.LoginAsync(TokenType.Bot, token).Wait(); diff --git a/test/Discord.Net.Tests.Integration/GuildTests.cs b/test/Discord.Net.Tests.Integration/GuildTests.cs index 40394a3a09..ef800a88e5 100644 --- a/test/Discord.Net.Tests.Integration/GuildTests.cs +++ b/test/Discord.Net.Tests.Integration/GuildTests.cs @@ -20,12 +20,6 @@ public GuildTests(RestGuildFixture guildFixture, ITestOutputHelper output) guild = guildFixture.Guild; this.output = output; output.WriteLine($"RestGuildFixture using guild: {guild.Id}"); - guildFixture.Client.Log += LogAsync; - } - private Task LogAsync(LogMessage message) - { - output.WriteLine(message.ToString()); - return Task.CompletedTask; } /// /// Ensures that the CurrentUser is the owner of the guild.