Browse Source

Merge fe4c6fbd45 into 8afef8245c

pull/1765/merge
Waterball GitHub 4 years ago
parent
commit
2d04ef9288
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 407 additions and 651 deletions
  1. +1
    -8
      samples/01_basic_ping_bot/Program.cs
  2. +0
    -10
      samples/02_commands_framework/Program.cs
  3. +0
    -7
      samples/03_sharded_client/Program.cs
  4. +0
    -8
      samples/03_sharded_client/Services/CommandHandlingService.cs
  5. +5
    -50
      samples/idn/Program.cs
  6. +3
    -2
      src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs
  7. +13
    -11
      src/Discord.Net.Commands/CommandService.cs
  8. +2
    -4
      src/Discord.Net.Commands/CommandServiceConfig.cs
  9. +4
    -3
      src/Discord.Net.Commands/Info/CommandInfo.cs
  10. +1
    -0
      src/Discord.Net.Core/Discord.Net.Core.csproj
  11. +3
    -8
      src/Discord.Net.Core/DiscordConfig.cs
  12. +52
    -0
      src/Discord.Net.Core/Logging/DefaultLogger.cs
  13. +41
    -0
      src/Discord.Net.Core/Logging/DefaultLoggerFactory.cs
  14. +31
    -0
      src/Discord.Net.Core/Logging/DefaultLoggerProvider.cs
  15. +0
    -105
      src/Discord.Net.Core/Logging/LogManager.cs
  16. +0
    -136
      src/Discord.Net.Core/Logging/LogMessage.cs
  17. +0
    -34
      src/Discord.Net.Core/Logging/LogSeverity.cs
  18. +0
    -72
      src/Discord.Net.Core/Logging/Logger.cs
  19. +22
    -13
      src/Discord.Net.Rest/BaseDiscordClient.cs
  20. +54
    -37
      src/Discord.Net.WebSocket/Audio/AudioClient.cs
  21. +7
    -6
      src/Discord.Net.WebSocket/Audio/Streams/BufferedWriteStream.cs
  22. +10
    -9
      src/Discord.Net.WebSocket/ConnectionManager.cs
  23. +0
    -1
      src/Discord.Net.WebSocket/DiscordShardedClient.cs
  24. +137
    -103
      src/Discord.Net.WebSocket/DiscordSocketClient.cs
  25. +21
    -11
      src/Discord.Net.Webhook/DiscordWebhookClient.cs
  26. +0
    -6
      test/Discord.Net.Tests.Integration/ChannelsTests.cs
  27. +0
    -1
      test/Discord.Net.Tests.Integration/DiscordRestClientFixture.cs
  28. +0
    -6
      test/Discord.Net.Tests.Integration/GuildTests.cs

+ 1
- 8
samples/01_basic_ping_bot/Program.cs View File

@@ -31,8 +31,7 @@ namespace _01_basic_ping_bot
// It is recommended to Dispose of a client when you are finished // It is recommended to Dispose of a client when you are finished
// using it, at the end of your app's lifetime. // using it, at the end of your app's lifetime.
_client = new DiscordSocketClient(); _client = new DiscordSocketClient();

_client.Log += LogAsync;
_client.Ready += ReadyAsync; _client.Ready += ReadyAsync;
_client.MessageReceived += MessageReceivedAsync; _client.MessageReceived += MessageReceivedAsync;
} }
@@ -47,12 +46,6 @@ namespace _01_basic_ping_bot
await Task.Delay(Timeout.Infinite); 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 // The Ready event indicates that the client has opened a
// connection and it is now safe to access the cache. // connection and it is now safe to access the cache.
private Task ReadyAsync() private Task ReadyAsync()


+ 0
- 10
samples/02_commands_framework/Program.cs View File

@@ -35,9 +35,6 @@ namespace _02_commands_framework
{ {
var client = services.GetRequiredService<DiscordSocketClient>(); var client = services.GetRequiredService<DiscordSocketClient>();


client.Log += LogAsync;
services.GetRequiredService<CommandService>().Log += LogAsync;

// Tokens should be considered secret data and never hard-coded. // Tokens should be considered secret data and never hard-coded.
// We can read from the environment variable to avoid hardcoding. // We can read from the environment variable to avoid hardcoding.
await client.LoginAsync(TokenType.Bot, Environment.GetEnvironmentVariable("token")); await client.LoginAsync(TokenType.Bot, Environment.GetEnvironmentVariable("token"));
@@ -50,13 +47,6 @@ namespace _02_commands_framework
} }
} }


private Task LogAsync(LogMessage log)
{
Console.WriteLine(log.ToString());

return Task.CompletedTask;
}

private ServiceProvider ConfigureServices() private ServiceProvider ConfigureServices()
{ {
return new ServiceCollection() return new ServiceCollection()


+ 0
- 7
samples/03_sharded_client/Program.cs View File

@@ -38,7 +38,6 @@ namespace _03_sharded_client
// The ShardReady event is used instead, allowing for individual // The ShardReady event is used instead, allowing for individual
// control per shard. // control per shard.
client.ShardReady += ReadyAsync; client.ShardReady += ReadyAsync;
client.Log += LogAsync;


await services.GetRequiredService<CommandHandlingService>().InitializeAsync(); await services.GetRequiredService<CommandHandlingService>().InitializeAsync();


@@ -65,11 +64,5 @@ namespace _03_sharded_client
Console.WriteLine($"Shard Number {shard.ShardId} is connected and ready!"); Console.WriteLine($"Shard Number {shard.ShardId} is connected and ready!");
return Task.CompletedTask; return Task.CompletedTask;
} }

private Task LogAsync(LogMessage log)
{
Console.WriteLine(log.ToString());
return Task.CompletedTask;
}
} }
} }

+ 0
- 8
samples/03_sharded_client/Services/CommandHandlingService.cs View File

@@ -21,7 +21,6 @@ namespace _03_sharded_client.Services
_services = services; _services = services;


_commands.CommandExecuted += CommandExecutedAsync; _commands.CommandExecuted += CommandExecutedAsync;
_commands.Log += LogAsync;
_discord.MessageReceived += MessageReceivedAsync; _discord.MessageReceived += MessageReceivedAsync;
} }


@@ -61,12 +60,5 @@ namespace _03_sharded_client.Services
// the command failed, let's notify the user that something happened. // the command failed, let's notify the user that something happened.
await context.Channel.SendMessageAsync($"error: {result.ToString()}"); await context.Channel.SendMessageAsync($"error: {result.ToString()}");
} }

private Task LogAsync(LogMessage log)
{
Console.WriteLine(log.ToString());

return Task.CompletedTask;
}
} }
} }

+ 5
- 50
samples/idn/Program.cs View File

@@ -34,60 +34,15 @@ namespace idn
static async Task Main(string[] args) static async Task Main(string[] args)
{ {
var token = File.ReadAllText("token.ignore"); var token = File.ReadAllText("token.ignore");
var client = new DiscordSocketClient(new DiscordSocketConfig { LogLevel = LogSeverity.Debug });
var logQueue = new ConcurrentQueue<LogMessage>();
var client = new DiscordSocketClient(new DiscordSocketConfig { });
var logCancelToken = new CancellationTokenSource(); var logCancelToken = new CancellationTokenSource();
int presenceUpdates = 0; int presenceUpdates = 0;

client.Log += msg =>
{
logQueue.Enqueue(msg);
return Task.CompletedTask;
};
Console.CancelKeyPress += (_ev, _s) => Console.CancelKeyPress += (_ev, _s) =>
{ {
logCancelToken.Cancel(); 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.LoginAsync(TokenType.Bot, token);
await client.StartAsync(); await client.StartAsync();


@@ -127,9 +82,9 @@ namespace idn
await client.StopAsync(); await client.StopAsync();
client.Dispose(); client.Dispose();
logCancelToken.Cancel(); logCancelToken.Cancel();
try
{ await logTask; }
finally { Console.WriteLine("goodbye!"); }
await Task.Delay(-1, logCancelToken.Token);
Console.WriteLine("goodbye!");
} }


static IEnumerable<Assembly> GetAssemblies() static IEnumerable<Assembly> GetAssemblies()


+ 3
- 2
src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs View File

@@ -5,6 +5,7 @@ using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;


using Discord.Commands.Builders; using Discord.Commands.Builders;
using Microsoft.Extensions.Logging;


namespace Discord.Commands namespace Discord.Commands
{ {
@@ -34,7 +35,7 @@ namespace Discord.Commands
} }
else if (IsLoadableModule(typeInfo)) 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 @@ namespace Discord.Commands
result[typeInfo.AsType()] = module.Build(service, services); result[typeInfo.AsType()] = module.Build(service, services);
} }


await service._cmdLogger.DebugAsync($"Successfully built {builtTypes.Count} modules.").ConfigureAwait(false);
service._cmdLogger.LogDebug($"Successfully built {builtTypes.Count} modules.");


return result; return result;
} }


+ 13
- 11
src/Discord.Net.Commands/CommandService.cs View File

@@ -8,6 +8,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Discord.Commands.Builders; using Discord.Commands.Builders;
using Discord.Logging; using Discord.Logging;
using Microsoft.Extensions.Logging;


namespace Discord.Commands namespace Discord.Commands
{ {
@@ -29,12 +30,6 @@ namespace Discord.Commands
/// </remarks> /// </remarks>
public class CommandService : IDisposable public class CommandService : IDisposable
{ {
/// <summary>
/// Occurs when a command-related information is received.
/// </summary>
public event Func<LogMessage, Task> Log { add { _logEvent.Add(value); } remove { _logEvent.Remove(value); } }
internal readonly AsyncEvent<Func<LogMessage, Task>> _logEvent = new AsyncEvent<Func<LogMessage, Task>>();

/// <summary> /// <summary>
/// Occurs when a command is executed. /// Occurs when a command is executed.
/// </summary> /// </summary>
@@ -56,8 +51,8 @@ namespace Discord.Commands
internal readonly bool _caseSensitive, _throwOnError, _ignoreExtraArgs; internal readonly bool _caseSensitive, _throwOnError, _ignoreExtraArgs;
internal readonly char _separatorChar; internal readonly char _separatorChar;
internal readonly RunMode _defaultRunMode; internal readonly RunMode _defaultRunMode;
internal readonly Logger _cmdLogger;
internal readonly LogManager _logManager;
internal readonly ILogger _cmdLogger;
internal readonly ILoggerFactory _logManager;
internal readonly IReadOnlyDictionary<char, char> _quotationMarkAliasMap; internal readonly IReadOnlyDictionary<char, char> _quotationMarkAliasMap;


internal bool _isDisposed; internal bool _isDisposed;
@@ -100,8 +95,14 @@ namespace Discord.Commands
if (_defaultRunMode == RunMode.Default) if (_defaultRunMode == RunMode.Default)
throw new InvalidOperationException("The default run mode cannot be set to 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"); _cmdLogger = _logManager.CreateLogger("Command");


_moduleLock = new SemaphoreSlim(1, 1); _moduleLock = new SemaphoreSlim(1, 1);
@@ -349,7 +350,8 @@ namespace Discord.Commands
public void AddTypeReader(Type type, TypeReader reader) public void AddTypeReader(Type type, TypeReader reader)
{ {
if (_defaultTypeReaders.ContainsKey(type)) 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<T>(reader, true)."); "To suppress this message, use AddTypeReader<T>(reader, true).");
AddTypeReader(type, reader, true); AddTypeReader(type, reader, true);
} }


+ 2
- 4
src/Discord.Net.Commands/CommandServiceConfig.cs View File

@@ -1,3 +1,4 @@
using Microsoft.Extensions.Logging;
using System.Collections.Generic; using System.Collections.Generic;


namespace Discord.Commands namespace Discord.Commands
@@ -23,10 +24,7 @@ namespace Discord.Commands
/// </summary> /// </summary>
public bool CaseSensitiveCommands { get; set; } = false; public bool CaseSensitiveCommands { get; set; } = false;


/// <summary>
/// Gets or sets the minimum log level severity that will be sent to the <see cref="CommandService.Log"/> event.
/// </summary>
public LogSeverity LogLevel { get; set; } = LogSeverity.Info;
public ILoggerFactory LoggerFactory { get; set; }


/// <summary> /// <summary>
/// Gets or sets whether <see cref="RunMode.Sync"/> commands should push exceptions up to the caller. /// Gets or sets whether <see cref="RunMode.Sync"/> commands should push exceptions up to the caller.


+ 4
- 3
src/Discord.Net.Commands/Info/CommandInfo.cs View File

@@ -1,4 +1,5 @@
using Discord.Commands.Builders; using Discord.Commands.Builders;
using Microsoft.Extensions.Logging;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
@@ -240,7 +241,7 @@ namespace Discord.Commands


private async Task<IResult> ExecuteInternalAsync(ICommandContext context, object[] args, IServiceProvider services) private async Task<IResult> 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 try
{ {
var task = _action(context, args, services, this); var task = _action(context, args, services, this);
@@ -274,7 +275,7 @@ namespace Discord.Commands
ex = ex.InnerException; ex = ex.InnerException;


var wrappedEx = new CommandException(this, context, ex); 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); var result = ExecuteResult.FromError(ex);
await Module.Service._commandExecutedEvent.InvokeAsync(this, context, result).ConfigureAwait(false); await Module.Service._commandExecutedEvent.InvokeAsync(this, context, result).ConfigureAwait(false);
@@ -291,7 +292,7 @@ namespace Discord.Commands
} }
finally finally
{ {
await Module.Service._cmdLogger.VerboseAsync($"Executed {GetLogText(context)}").ConfigureAwait(false);
Module.Service._cmdLogger.LogTrace($"Executed {GetLogText(context)}");
} }
} }




+ 1
- 0
src/Discord.Net.Core/Discord.Net.Core.csproj View File

@@ -9,6 +9,7 @@
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard2.0;netstandard2.1</TargetFrameworks> <TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard2.0;netstandard2.1</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="System.Collections.Immutable" Version="1.3.1" /> <PackageReference Include="System.Collections.Immutable" Version="1.3.1" />
<PackageReference Include="System.Interactive.Async" Version="4.0.0" /> <PackageReference Include="System.Interactive.Async" Version="4.0.0" />


+ 3
- 8
src/Discord.Net.Core/DiscordConfig.cs View File

@@ -1,3 +1,4 @@
using Microsoft.Extensions.Logging;
using System.Reflection; using System.Reflection;


namespace Discord namespace Discord
@@ -123,14 +124,8 @@ namespace Discord
/// The currently set <see cref="RetryMode"/>. /// The currently set <see cref="RetryMode"/>.
/// </returns> /// </returns>
public RetryMode DefaultRetryMode { get; set; } = RetryMode.AlwaysRetry; public RetryMode DefaultRetryMode { get; set; } = RetryMode.AlwaysRetry;

/// <summary>
/// Gets or sets the minimum log level severity that will be sent to the Log event.
/// </summary>
/// <returns>
/// The currently set <see cref="LogSeverity"/> for logging level.
/// </returns>
public LogSeverity LogLevel { get; set; } = LogSeverity.Info;
public ILoggerFactory LoggerFactory { get; set; }


/// <summary> /// <summary>
/// Gets or sets whether the initial log entry should be printed. /// Gets or sets whether the initial log entry should be printed.


+ 52
- 0
src/Discord.Net.Core/Logging/DefaultLogger.cs View File

@@ -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<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> 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>(TState state)
{
throw new NotImplementedException();
}
}
}

+ 41
- 0
src/Discord.Net.Core/Logging/DefaultLoggerFactory.cs View File

@@ -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<ILoggerProvider> Providers { get; } = new List<ILoggerProvider>();
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);
}
}
}

+ 31
- 0
src/Discord.Net.Core/Logging/DefaultLoggerProvider.cs View File

@@ -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;
}
}
}

+ 0
- 105
src/Discord.Net.Core/Logging/LogManager.cs View File

@@ -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<LogMessage, Task> Message { add { _messageEvent.Add(value); } remove { _messageEvent.Remove(value); } }
private readonly AsyncEvent<Func<LogMessage, Task>> _messageEvent = new AsyncEvent<Func<LogMessage, Task>>();

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);
}
}
}

+ 0
- 136
src/Discord.Net.Core/Logging/LogMessage.cs View File

@@ -1,136 +0,0 @@
using System;
using System.Text;

namespace Discord
{
/// <summary>
/// Provides a message object used for logging purposes.
/// </summary>
public struct LogMessage
{
/// <summary>
/// Gets the severity of the log entry.
/// </summary>
/// <returns>
/// A <see cref="LogSeverity"/> enum to indicate the severeness of the incident or event.
/// </returns>
public LogSeverity Severity { get; }
/// <summary>
/// Gets the source of the log entry.
/// </summary>
/// <returns>
/// A string representing the source of the log entry.
/// </returns>
public string Source { get; }
/// <summary>
/// Gets the message of this log entry.
/// </summary>
/// <returns>
/// A string containing the message of this log entry.
/// </returns>
public string Message { get; }
/// <summary>
/// Gets the exception of this log entry.
/// </summary>
/// <returns>
/// An <see cref="Discord.LogMessage.Exception" /> object associated with an incident; otherwise <c>null</c>.
/// </returns>
public Exception Exception { get; }

/// <summary>
/// Initializes a new <see cref="LogMessage"/> struct with the severity, source, message of the event, and
/// optionally, an exception.
/// </summary>
/// <param name="severity">The severity of the event.</param>
/// <param name="source">The source of the event.</param>
/// <param name="message">The message of the event.</param>
/// <param name="exception">The exception of the event.</param>
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();
}
}
}

+ 0
- 34
src/Discord.Net.Core/Logging/LogSeverity.cs View File

@@ -1,34 +0,0 @@
namespace Discord
{
/// <summary>
/// Specifies the severity of the log message.
/// </summary>
public enum LogSeverity
{
/// <summary>
/// Logs that contain the most severe level of error. This type of error indicate that immediate attention
/// may be required.
/// </summary>
Critical = 0,
/// <summary>
/// Logs that highlight when the flow of execution is stopped due to a failure.
/// </summary>
Error = 1,
/// <summary>
/// Logs that highlight an abnormal activity in the flow of execution.
/// </summary>
Warning = 2,
/// <summary>
/// Logs that track the general flow of the application.
/// </summary>
Info = 3,
/// <summary>
/// Logs that are used for interactive investigation during development.
/// </summary>
Verbose = 4,
/// <summary>
/// Logs that contain the most detailed messages.
/// </summary>
Debug = 5
}
}

+ 0
- 72
src/Discord.Net.Core/Logging/Logger.cs View File

@@ -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);

}
}

+ 22
- 13
src/Discord.Net.Rest/BaseDiscordClient.cs View File

@@ -1,4 +1,5 @@
using Discord.Logging; using Discord.Logging;
using Microsoft.Extensions.Logging;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
@@ -10,20 +11,17 @@ namespace Discord.Rest
{ {
public abstract class BaseDiscordClient : IDiscordClient public abstract class BaseDiscordClient : IDiscordClient
{ {
public event Func<LogMessage, Task> Log { add { _logEvent.Add(value); } remove { _logEvent.Remove(value); } }
internal readonly AsyncEvent<Func<LogMessage, Task>> _logEvent = new AsyncEvent<Func<LogMessage, Task>>();

public event Func<Task> LoggedIn { add { _loggedInEvent.Add(value); } remove { _loggedInEvent.Remove(value); } } public event Func<Task> LoggedIn { add { _loggedInEvent.Add(value); } remove { _loggedInEvent.Remove(value); } }
private readonly AsyncEvent<Func<Task>> _loggedInEvent = new AsyncEvent<Func<Task>>(); private readonly AsyncEvent<Func<Task>> _loggedInEvent = new AsyncEvent<Func<Task>>();
public event Func<Task> LoggedOut { add { _loggedOutEvent.Add(value); } remove { _loggedOutEvent.Remove(value); } } public event Func<Task> LoggedOut { add { _loggedOutEvent.Add(value); } remove { _loggedOutEvent.Remove(value); } }
private readonly AsyncEvent<Func<Task>> _loggedOutEvent = new AsyncEvent<Func<Task>>(); private readonly AsyncEvent<Func<Task>> _loggedOutEvent = new AsyncEvent<Func<Task>>();


internal readonly Logger _restLogger;
internal readonly ILogger _restLogger;
private readonly SemaphoreSlim _stateLock; private readonly SemaphoreSlim _stateLock;
private bool _isFirstLogin, _isDisposed; private bool _isFirstLogin, _isDisposed;


internal API.DiscordRestApiClient ApiClient { get; } internal API.DiscordRestApiClient ApiClient { get; }
internal LogManager LogManager { get; }
internal ILoggerFactory LogManager { get; }
/// <summary> /// <summary>
/// Gets the login state of the client. /// Gets the login state of the client.
/// </summary> /// </summary>
@@ -39,21 +37,32 @@ namespace Discord.Rest
internal BaseDiscordClient(DiscordRestConfig config, API.DiscordRestApiClient client) internal BaseDiscordClient(DiscordRestConfig config, API.DiscordRestApiClient client)
{ {
ApiClient = 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); _stateLock = new SemaphoreSlim(1, 1);
_restLogger = LogManager.CreateLogger("Rest"); _restLogger = LogManager.CreateLogger("Rest");
_isFirstLogin = config.DisplayInitialLog; _isFirstLogin = config.DisplayInitialLog;


ApiClient.RequestQueue.RateLimitTriggered += async (id, info, endpoint) =>
ApiClient.RequestQueue.RateLimitTriggered += (id, info, endpoint) =>
{ {
if (info == null) 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 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) public async Task LoginAsync(TokenType tokenType, string token, bool validateToken = true)
@@ -70,7 +79,7 @@ namespace Discord.Rest
if (_isFirstLogin) if (_isFirstLogin)
{ {
_isFirstLogin = false; _isFirstLogin = false;
await LogManager.WriteInitialLog().ConfigureAwait(false);
_restLogger.LogInformation($"Discord.Net v{DiscordConfig.Version} (API v{DiscordConfig.APIVersion})");
} }


if (LoginState != LoginState.LoggedOut) if (LoginState != LoginState.LoggedOut)
@@ -90,7 +99,7 @@ namespace Discord.Rest
catch (ArgumentException ex) catch (ArgumentException ex)
{ {
// log these ArgumentExceptions and allow for the client to attempt to log in anyways // 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.");
} }
} }




+ 54
- 37
src/Discord.Net.WebSocket/Audio/AudioClient.cs View File

@@ -3,6 +3,7 @@ using Discord.Audio.Streams;
using Discord.Logging; using Discord.Logging;
using Discord.Net.Converters; using Discord.Net.Converters;
using Discord.WebSocket; using Discord.WebSocket;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using System; using System;
@@ -30,7 +31,7 @@ namespace Discord.Audio
} }
} }


private readonly Logger _audioLogger;
private readonly ILogger _audioLogger;
private readonly JsonSerializer _serializer; private readonly JsonSerializer _serializer;
private readonly ConnectionManager _connection; private readonly ConnectionManager _connection;
private readonly SemaphoreSlim _stateLock; private readonly SemaphoreSlim _stateLock;
@@ -64,8 +65,16 @@ namespace Discord.Audio
_audioLogger = Discord.LogManager.CreateLogger($"Audio #{clientId}"); _audioLogger = Discord.LogManager.CreateLogger($"Audio #{clientId}");


ApiClient = new DiscordVoiceAPIClient(guild.Id, Discord.WebSocketProvider, Discord.UdpSocketProvider); 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.SentData += async bytes => await _audioLogger.DebugAsync($"Sent {bytes} Bytes").ConfigureAwait(false);
ApiClient.ReceivedEvent += ProcessMessageAsync; ApiClient.ReceivedEvent += ProcessMessageAsync;
ApiClient.ReceivedPacket += ProcessPacketAsync; ApiClient.ReceivedPacket += ProcessPacketAsync;
@@ -83,12 +92,20 @@ namespace Discord.Audio
_serializer = new JsonSerializer { ContractResolver = new DiscordContractResolver() }; _serializer = new JsonSerializer { ContractResolver = new DiscordContractResolver() };
_serializer.Error += (s, e) => _serializer.Error += (s, e) =>
{ {
_audioLogger.WarningAsync(e.ErrorContext.Error).GetAwaiter().GetResult();
_audioLogger.LogWarning(e.ErrorContext.Error, e.ErrorContext.Error.Message);
e.ErrorContext.Handled = true; 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) internal async Task StartAsync(string url, ulong userId, string sessionId, string token)
@@ -112,10 +129,10 @@ namespace Discord.Audio


private async Task OnConnectingAsync() 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 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); await ApiClient.SendIdentityAsync(_userId, _sessionId, _token).ConfigureAwait(false);


//Wait for READY //Wait for READY
@@ -123,11 +140,11 @@ namespace Discord.Audio
} }
private async Task OnDisconnectingAsync(Exception ex) private async Task OnDisconnectingAsync(Exception ex)
{ {
await _audioLogger.DebugAsync("Disconnecting ApiClient").ConfigureAwait(false);
_audioLogger.LogDebug("Disconnecting ApiClient");
await ApiClient.DisconnectAsync().ConfigureAwait(false); await ApiClient.DisconnectAsync().ConfigureAwait(false);


//Wait for tasks to complete //Wait for tasks to complete
await _audioLogger.DebugAsync("Waiting for heartbeater").ConfigureAwait(false);
_audioLogger.LogDebug("Waiting for heartbeater");
var heartbeatTask = _heartbeatTask; var heartbeatTask = _heartbeatTask;
if (heartbeatTask != null) if (heartbeatTask != null)
await heartbeatTask.ConfigureAwait(false); await heartbeatTask.ConfigureAwait(false);
@@ -142,7 +159,7 @@ namespace Discord.Audio


await ClearInputStreamsAsync().ConfigureAwait(false); 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); await Discord.ApiClient.SendVoiceStateUpdateAsync(Guild.Id, null, false, false).ConfigureAwait(false);
} }


@@ -224,7 +241,7 @@ namespace Discord.Audio
{ {
case VoiceOpCode.Ready: case VoiceOpCode.Ready:
{ {
await _audioLogger.DebugAsync("Received Ready").ConfigureAwait(false);
_audioLogger.LogDebug("Received Ready");
var data = (payload as JToken).ToObject<ReadyEvent>(_serializer); var data = (payload as JToken).ToObject<ReadyEvent>(_serializer);


_ssrc = data.SSRC; _ssrc = data.SSRC;
@@ -241,7 +258,7 @@ namespace Discord.Audio
break; break;
case VoiceOpCode.SessionDescription: case VoiceOpCode.SessionDescription:
{ {
await _audioLogger.DebugAsync("Received SessionDescription").ConfigureAwait(false);
_audioLogger.LogDebug("Received SessionDescription");
var data = (payload as JToken).ToObject<SessionDescriptionEvent>(_serializer); var data = (payload as JToken).ToObject<SessionDescriptionEvent>(_serializer);


if (data.Mode != DiscordVoiceAPIClient.Mode) if (data.Mode != DiscordVoiceAPIClient.Mode)
@@ -257,7 +274,7 @@ namespace Discord.Audio
break; break;
case VoiceOpCode.HeartbeatAck: case VoiceOpCode.HeartbeatAck:
{ {
await _audioLogger.DebugAsync("Received HeartbeatAck").ConfigureAwait(false);
_audioLogger.LogDebug("Received HeartbeatAck");


if (_heartbeatTimes.TryDequeue(out long time)) if (_heartbeatTimes.TryDequeue(out long time))
{ {
@@ -271,7 +288,7 @@ namespace Discord.Audio
break; break;
case VoiceOpCode.Speaking: case VoiceOpCode.Speaking:
{ {
await _audioLogger.DebugAsync("Received Speaking").ConfigureAwait(false);
_audioLogger.LogDebug("Received Speaking");


var data = (payload as JToken).ToObject<SpeakingEvent>(_serializer); var data = (payload as JToken).ToObject<SpeakingEvent>(_serializer);
_ssrcMap[data.Ssrc] = data.UserId; //TODO: Memory Leak: SSRCs are never cleaned up _ssrcMap[data.Ssrc] = data.UserId; //TODO: Memory Leak: SSRCs are never cleaned up
@@ -280,13 +297,13 @@ namespace Discord.Audio
} }
break; break;
default: default:
await _audioLogger.WarningAsync($"Unknown OpCode ({opCode})").ConfigureAwait(false);
_audioLogger.LogWarning($"Unknown OpCode ({opCode})");
return; return;
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
await _audioLogger.ErrorAsync($"Error handling {opCode}", ex).ConfigureAwait(false);
_audioLogger.LogError($"Error handling {opCode}", ex);
return; return;
} }
} }
@@ -298,7 +315,7 @@ namespace Discord.Audio
{ {
if (packet.Length != 70) if (packet.Length != 70)
{ {
await _audioLogger.DebugAsync("Malformed Packet").ConfigureAwait(false);
_audioLogger.LogDebug("Malformed Packet");
return; return;
} }
string ip; string ip;
@@ -310,18 +327,18 @@ namespace Discord.Audio
} }
catch (Exception ex) catch (Exception ex)
{ {
await _audioLogger.DebugAsync("Malformed Packet", ex).ConfigureAwait(false);
_audioLogger.LogDebug("Malformed Packet", ex);
return; return;
} }


await _audioLogger.DebugAsync("Received Discovery").ConfigureAwait(false);
_audioLogger.LogDebug("Received Discovery");
await ApiClient.SendSelectProtocol(ip, port).ConfigureAwait(false); await ApiClient.SendSelectProtocol(ip, port).ConfigureAwait(false);
} }
else if (_connection.State == ConnectionState.Connected) else if (_connection.State == ConnectionState.Connected)
{ {
if (packet.Length == 8) if (packet.Length == 8)
{ {
await _audioLogger.DebugAsync("Received Keepalive").ConfigureAwait(false);
_audioLogger.LogDebug("Received Keepalive");


ulong value = ulong value =
((ulong)packet[0] >> 0) | ((ulong)packet[0] >> 0) |
@@ -350,17 +367,17 @@ namespace Discord.Audio
{ {
if (!RTPReadStream.TryReadSsrc(packet, 0, out var ssrc)) if (!RTPReadStream.TryReadSsrc(packet, 0, out var ssrc))
{ {
await _audioLogger.DebugAsync("Malformed Frame").ConfigureAwait(false);
_audioLogger.LogDebug("Malformed Frame");
return; return;
} }
if (!_ssrcMap.TryGetValue(ssrc, out var userId)) if (!_ssrcMap.TryGetValue(ssrc, out var userId))
{ {
await _audioLogger.DebugAsync($"Unknown SSRC {ssrc}").ConfigureAwait(false);
_audioLogger.LogDebug($"Unknown SSRC {ssrc}");
return; return;
} }
if (!_streams.TryGetValue(userId, out var pair)) if (!_streams.TryGetValue(userId, out var pair))
{ {
await _audioLogger.DebugAsync($"Unknown User {userId}").ConfigureAwait(false);
_audioLogger.LogDebug($"Unknown User {userId}");
return; return;
} }
try try
@@ -369,7 +386,7 @@ namespace Discord.Audio
} }
catch (Exception ex) catch (Exception ex)
{ {
await _audioLogger.DebugAsync("Malformed Frame", ex).ConfigureAwait(false);
_audioLogger.LogDebug(ex, "Malformed Frame");
return; return;
} }
//await _audioLogger.DebugAsync($"Received {packet.Length} bytes from user {userId}").ConfigureAwait(false); //await _audioLogger.DebugAsync($"Received {packet.Length} bytes from user {userId}").ConfigureAwait(false);
@@ -378,7 +395,7 @@ namespace Discord.Audio
} }
catch (Exception ex) catch (Exception ex)
{ {
await _audioLogger.WarningAsync("Failed to process UDP packet", ex).ConfigureAwait(false);
_audioLogger.LogWarning("Failed to process UDP packet", ex);
return; return;
} }
} }
@@ -388,7 +405,7 @@ namespace Discord.Audio
// TODO: Clean this up when Discord's session patch is live // TODO: Clean this up when Discord's session patch is live
try try
{ {
await _audioLogger.DebugAsync("Heartbeat Started").ConfigureAwait(false);
_audioLogger.LogDebug("Heartbeat Started");
while (!cancelToken.IsCancellationRequested) while (!cancelToken.IsCancellationRequested)
{ {
var now = Environment.TickCount; var now = Environment.TickCount;
@@ -408,27 +425,27 @@ namespace Discord.Audio
} }
catch (Exception ex) 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 Task.Delay(intervalMillis, cancelToken).ConfigureAwait(false);
} }
await _audioLogger.DebugAsync("Heartbeat Stopped").ConfigureAwait(false);
_audioLogger.LogDebug("Heartbeat Stopped");
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
await _audioLogger.DebugAsync("Heartbeat Stopped").ConfigureAwait(false);
_audioLogger.LogDebug("Heartbeat Stopped");
} }
catch (Exception ex) catch (Exception ex)
{ {
await _audioLogger.ErrorAsync("Heartbeat Errored", ex).ConfigureAwait(false);
_audioLogger.LogError("Heartbeat Errored", ex);
} }
} }
private async Task RunKeepaliveAsync(int intervalMillis, CancellationToken cancelToken) private async Task RunKeepaliveAsync(int intervalMillis, CancellationToken cancelToken)
{ {
try try
{ {
await _audioLogger.DebugAsync("Keepalive Started").ConfigureAwait(false);
_audioLogger.LogDebug("Keepalive Started");
while (!cancelToken.IsCancellationRequested) while (!cancelToken.IsCancellationRequested)
{ {
var now = Environment.TickCount; var now = Environment.TickCount;
@@ -441,20 +458,20 @@ namespace Discord.Audio
} }
catch (Exception ex) 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 Task.Delay(intervalMillis, cancelToken).ConfigureAwait(false);
} }
await _audioLogger.DebugAsync("Keepalive Stopped").ConfigureAwait(false);
_audioLogger.LogDebug("Keepalive Stopped");
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
await _audioLogger.DebugAsync("Keepalive Stopped").ConfigureAwait(false);
_audioLogger.LogDebug("Keepalive Stopped");
} }
catch (Exception ex) catch (Exception ex)
{ {
await _audioLogger.ErrorAsync("Keepalive Errored", ex).ConfigureAwait(false);
_audioLogger.LogError("Keepalive Errored", ex);
} }
} }




+ 7
- 6
src/Discord.Net.WebSocket/Audio/Streams/BufferedWriteStream.cs View File

@@ -1,4 +1,5 @@
using Discord.Logging; using Discord.Logging;
using Microsoft.Extensions.Logging;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Threading; using System.Threading;
@@ -33,14 +34,14 @@ namespace Discord.Audio.Streams
private readonly ConcurrentQueue<Frame> _queuedFrames; private readonly ConcurrentQueue<Frame> _queuedFrames;
private readonly ConcurrentQueue<byte[]> _bufferPool; private readonly ConcurrentQueue<byte[]> _bufferPool;
private readonly SemaphoreSlim _queueLock; private readonly SemaphoreSlim _queueLock;
private readonly Logger _logger;
private readonly ILogger _logger;
private readonly int _ticksPerFrame, _queueLength; private readonly int _ticksPerFrame, _queueLength;
private bool _isPreloaded; private bool _isPreloaded;
private int _silenceFrames; private int _silenceFrames;


public BufferedWriteStream(AudioStream next, IAudioClient client, int bufferMillis, CancellationToken cancelToken, int maxFrameSize = 1500) public BufferedWriteStream(AudioStream next, IAudioClient client, int bufferMillis, CancellationToken cancelToken, int maxFrameSize = 1500)
: this(next, client as AudioClient, bufferMillis, cancelToken, null, maxFrameSize) { } : 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 //maxFrameSize = 1275 was too limiting at 128kbps,2ch,60ms
_next = next; _next = next;
@@ -106,7 +107,7 @@ namespace Discord.Audio.Streams
timestamp += OpusEncoder.FrameSamplesPerChannel; timestamp += OpusEncoder.FrameSamplesPerChannel;
_silenceFrames = 0; _silenceFrames = 0;
#if DEBUG #if DEBUG
var _ = _logger?.DebugAsync($"Sent {frame.Bytes} bytes ({_queuedFrames.Count} frames buffered)");
_logger?.LogDebug($"Sent {frame.Bytes} bytes ({_queuedFrames.Count} frames buffered)");
#endif #endif
} }
else else
@@ -125,7 +126,7 @@ namespace Discord.Audio.Streams
timestamp += OpusEncoder.FrameSamplesPerChannel; timestamp += OpusEncoder.FrameSamplesPerChannel;
} }
#if DEBUG #if DEBUG
var _ = _logger?.DebugAsync("Buffer underrun");
_logger?.LogDebug("Buffer underrun");
#endif #endif
} }
} }
@@ -153,7 +154,7 @@ namespace Discord.Audio.Streams
if (!_bufferPool.TryDequeue(out byte[] buffer)) if (!_bufferPool.TryDequeue(out byte[] buffer))
{ {
#if DEBUG #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 #endif
#pragma warning disable IDISP016 #pragma warning disable IDISP016
writeCancelToken?.Dispose(); writeCancelToken?.Dispose();
@@ -165,7 +166,7 @@ namespace Discord.Audio.Streams
if (!_isPreloaded && _queuedFrames.Count == _queueLength) if (!_isPreloaded && _queuedFrames.Count == _queueLength)
{ {
#if DEBUG #if DEBUG
var _ = _logger?.DebugAsync("Preloaded");
_logger?.LogDebug("Preloaded");
#endif #endif
_isPreloaded = true; _isPreloaded = true;
} }


+ 10
- 9
src/Discord.Net.WebSocket/ConnectionManager.cs View File

@@ -3,6 +3,7 @@ using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Discord.Net; using Discord.Net;
using Microsoft.Extensions.Logging;


namespace Discord namespace Discord
{ {
@@ -14,7 +15,7 @@ namespace Discord
private readonly AsyncEvent<Func<Exception, bool, Task>> _disconnectedEvent = new AsyncEvent<Func<Exception, bool, Task>>(); private readonly AsyncEvent<Func<Exception, bool, Task>> _disconnectedEvent = new AsyncEvent<Func<Exception, bool, Task>>();


private readonly SemaphoreSlim _stateLock; private readonly SemaphoreSlim _stateLock;
private readonly Logger _logger;
private readonly ILogger _logger;
private readonly int _connectionTimeout; private readonly int _connectionTimeout;
private readonly Func<Task> _onConnecting; private readonly Func<Task> _onConnecting;
private readonly Func<Exception, Task> _onDisconnecting; private readonly Func<Exception, Task> _onDisconnecting;
@@ -28,7 +29,7 @@ namespace Discord
public ConnectionState State { get; private set; } public ConnectionState State { get; private set; }
public CancellationToken CancelToken { 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<Task> onConnecting, Func<Exception, Task> onDisconnecting, Action<Func<Exception, Task>> clientDisconnectHandler) Func<Task> onConnecting, Func<Exception, Task> onDisconnecting, Action<Func<Exception, Task>> clientDisconnectHandler)
{ {
_stateLock = stateLock; _stateLock = stateLock;
@@ -85,12 +86,12 @@ namespace Discord
Error(ex); //In case this exception didn't come from another Error call Error(ex); //In case this exception didn't come from another Error call
if (!reconnectCancelToken.IsCancellationRequested) if (!reconnectCancelToken.IsCancellationRequested)
{ {
await _logger.WarningAsync(ex).ConfigureAwait(false);
_logger.LogWarning(ex, ex.Message);
await DisconnectAsync(ex, true).ConfigureAwait(false); await DisconnectAsync(ex, true).ConfigureAwait(false);
} }
else else
{ {
await _logger.ErrorAsync(ex).ConfigureAwait(false);
_logger.LogError(ex, ex.Message);
await DisconnectAsync(ex, false).ConfigureAwait(false); await DisconnectAsync(ex, false).ConfigureAwait(false);
} }
} }
@@ -124,7 +125,7 @@ namespace Discord


_connectionPromise = new TaskCompletionSource<bool>(); _connectionPromise = new TaskCompletionSource<bool>();
State = ConnectionState.Connecting; State = ConnectionState.Connecting;
await _logger.InfoAsync("Connecting").ConfigureAwait(false);
_logger.LogInformation("Connecting");


try try
{ {
@@ -154,9 +155,9 @@ namespace Discord
throw innerEx; throw innerEx;
} }


await _logger.InfoAsync("Connected").ConfigureAwait(false);
_logger.LogInformation("Connected");
State = ConnectionState.Connected; State = ConnectionState.Connected;
await _logger.DebugAsync("Raising Event").ConfigureAwait(false);
_logger.LogDebug("Raising Event");
await _connectedEvent.InvokeAsync().ConfigureAwait(false); await _connectedEvent.InvokeAsync().ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
@@ -169,13 +170,13 @@ namespace Discord
{ {
if (State == ConnectionState.Disconnected) return; if (State == ConnectionState.Disconnected) return;
State = ConnectionState.Disconnecting; State = ConnectionState.Disconnecting;
await _logger.InfoAsync("Disconnecting").ConfigureAwait(false);
_logger.LogInformation("Disconnecting");


await _onDisconnecting(ex).ConfigureAwait(false); await _onDisconnecting(ex).ConfigureAwait(false);


await _disconnectedEvent.InvokeAsync(ex, isReconnecting).ConfigureAwait(false); await _disconnectedEvent.InvokeAsync(ex, isReconnecting).ConfigureAwait(false);
State = ConnectionState.Disconnected; State = ConnectionState.Disconnected;
await _logger.InfoAsync("Disconnected").ConfigureAwait(false);
_logger.LogInformation("Disconnected");
} }


public async Task CompleteAsync() public async Task CompleteAsync()


+ 0
- 1
src/Discord.Net.WebSocket/DiscordShardedClient.cs View File

@@ -328,7 +328,6 @@ namespace Discord.WebSocket


private void RegisterEvents(DiscordSocketClient client, bool isPrimary) private void RegisterEvents(DiscordSocketClient client, bool isPrimary)
{ {
client.Log += (msg) => _logEvent.InvokeAsync(msg);
client.LoggedOut += () => client.LoggedOut += () =>
{ {
var state = LoginState; var state = LoginState;


+ 137
- 103
src/Discord.Net.WebSocket/DiscordSocketClient.cs View File

@@ -5,6 +5,7 @@ using Discord.Net.Converters;
using Discord.Net.Udp; using Discord.Net.Udp;
using Discord.Net.WebSockets; using Discord.Net.WebSockets;
using Discord.Rest; using Discord.Rest;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using System; using System;
@@ -30,7 +31,7 @@ namespace Discord.WebSocket
private readonly DiscordSocketClient _parentClient; private readonly DiscordSocketClient _parentClient;
private readonly ConcurrentQueue<long> _heartbeatTimes; private readonly ConcurrentQueue<long> _heartbeatTimes;
private readonly ConnectionManager _connection; private readonly ConnectionManager _connection;
private readonly Logger _gatewayLogger;
private readonly ILogger _gatewayLogger;
private readonly SemaphoreSlim _stateLock; private readonly SemaphoreSlim _stateLock;


private string _sessionId; private string _sessionId;
@@ -144,6 +145,7 @@ namespace Discord.WebSocket
_gatewayIntents = config.GatewayIntents; _gatewayIntents = config.GatewayIntents;


_stateLock = new SemaphoreSlim(1, 1); _stateLock = new SemaphoreSlim(1, 1);

_gatewayLogger = LogManager.CreateLogger(ShardId == 0 && TotalShards == 1 ? "Gateway" : $"Shard #{ShardId}"); _gatewayLogger = LogManager.CreateLogger(ShardId == 0 && TotalShards == 1 ? "Gateway" : $"Shard #{ShardId}");
_connection = new ConnectionManager(_stateLock, _gatewayLogger, config.ConnectionTimeout, _connection = new ConnectionManager(_stateLock, _gatewayLogger, config.ConnectionTimeout,
OnConnectingAsync, OnDisconnectingAsync, x => ApiClient.Disconnected += x); OnConnectingAsync, OnDisconnectingAsync, x => ApiClient.Disconnected += x);
@@ -157,18 +159,42 @@ namespace Discord.WebSocket
_serializer = new JsonSerializer { ContractResolver = new DiscordContractResolver() }; _serializer = new JsonSerializer { ContractResolver = new DiscordContractResolver() };
_serializer.Error += (s, e) => _serializer.Error += (s, e) =>
{ {
_gatewayLogger.WarningAsync("Serializer Error", e.ErrorContext.Error).GetAwaiter().GetResult();
_gatewayLogger.LogWarning(e.ErrorContext.Error, "Serializer Error");
e.ErrorContext.Handled = true; 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; 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 => GuildAvailable += g =>
{ {
@@ -232,17 +258,17 @@ namespace Discord.WebSocket
} }
try try
{ {
await _gatewayLogger.DebugAsync("Connecting ApiClient").ConfigureAwait(false);
_gatewayLogger.LogDebug("Connecting ApiClient");
await ApiClient.ConnectAsync().ConfigureAwait(false); await ApiClient.ConnectAsync().ConfigureAwait(false);


if (_sessionId != null) if (_sessionId != null)
{ {
await _gatewayLogger.DebugAsync("Resuming").ConfigureAwait(false);
_gatewayLogger.LogDebug("Resuming");
await ApiClient.SendResumeAsync(_sessionId, _lastSeq).ConfigureAwait(false); await ApiClient.SendResumeAsync(_sessionId, _lastSeq).ConfigureAwait(false);
} }
else 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); await ApiClient.SendIdentifyAsync(shardID: ShardId, totalShards: TotalShards, guildSubscriptions: _guildSubscriptions, gatewayIntents: _gatewayIntents, presence: BuildCurrentStatus()).ConfigureAwait(false);
} }
} }
@@ -258,11 +284,11 @@ namespace Discord.WebSocket
private async Task OnDisconnectingAsync(Exception ex) private async Task OnDisconnectingAsync(Exception ex)
{ {


await _gatewayLogger.DebugAsync("Disconnecting ApiClient").ConfigureAwait(false);
_gatewayLogger.LogDebug("Disconnecting ApiClient");
await ApiClient.DisconnectAsync(ex).ConfigureAwait(false); await ApiClient.DisconnectAsync(ex).ConfigureAwait(false);


//Wait for tasks to complete //Wait for tasks to complete
await _gatewayLogger.DebugAsync("Waiting for heartbeater").ConfigureAwait(false);
_gatewayLogger.LogDebug("Waiting for heartbeater");
var heartbeatTask = _heartbeatTask; var heartbeatTask = _heartbeatTask;
if (heartbeatTask != null) if (heartbeatTask != null)
await heartbeatTask.ConfigureAwait(false); await heartbeatTask.ConfigureAwait(false);
@@ -271,18 +297,18 @@ namespace Discord.WebSocket
while (_heartbeatTimes.TryDequeue(out _)) { } while (_heartbeatTimes.TryDequeue(out _)) { }
_lastMessageTime = 0; _lastMessageTime = 0;


await _gatewayLogger.DebugAsync("Waiting for guild downloader").ConfigureAwait(false);
_gatewayLogger.LogDebug("Waiting for guild downloader");
var guildDownloadTask = _guildDownloadTask; var guildDownloadTask = _guildDownloadTask;
if (guildDownloadTask != null) if (guildDownloadTask != null)
await guildDownloadTask.ConfigureAwait(false); await guildDownloadTask.ConfigureAwait(false);
_guildDownloadTask = null; _guildDownloadTask = null;


//Clear large guild queue //Clear large guild queue
await _gatewayLogger.DebugAsync("Clearing large guild queue").ConfigureAwait(false);
_gatewayLogger.LogDebug("Clearing large guild queue");
while (_largeGuilds.TryDequeue(out _)) { } while (_largeGuilds.TryDequeue(out _)) { }


//Raise virtual GUILD_UNAVAILABLEs //Raise virtual GUILD_UNAVAILABLEs
await _gatewayLogger.DebugAsync("Raising virtual GuildUnavailables").ConfigureAwait(false);
_gatewayLogger.LogDebug("Raising virtual GuildUnavailables");
foreach (var guild in State.Guilds) foreach (var guild in State.Guilds)
{ {
if (guild.IsAvailable) if (guild.IsAvailable)
@@ -524,7 +550,7 @@ namespace Discord.WebSocket
{ {
case GatewayOpCode.Hello: case GatewayOpCode.Hello:
{ {
await _gatewayLogger.DebugAsync("Received Hello").ConfigureAwait(false);
_gatewayLogger.LogDebug("Received Hello");
var data = (payload as JToken).ToObject<HelloEvent>(_serializer); var data = (payload as JToken).ToObject<HelloEvent>(_serializer);


_heartbeatTask = RunHeartbeatAsync(data.HeartbeatInterval, _connection.CancelToken); _heartbeatTask = RunHeartbeatAsync(data.HeartbeatInterval, _connection.CancelToken);
@@ -532,14 +558,14 @@ namespace Discord.WebSocket
break; break;
case GatewayOpCode.Heartbeat: case GatewayOpCode.Heartbeat:
{ {
await _gatewayLogger.DebugAsync("Received Heartbeat").ConfigureAwait(false);
_gatewayLogger.LogDebug("Received Heartbeat");


await ApiClient.SendHeartbeatAsync(_lastSeq).ConfigureAwait(false); await ApiClient.SendHeartbeatAsync(_lastSeq).ConfigureAwait(false);
} }
break; break;
case GatewayOpCode.HeartbeatAck: case GatewayOpCode.HeartbeatAck:
{ {
await _gatewayLogger.DebugAsync("Received HeartbeatAck").ConfigureAwait(false);
_gatewayLogger.LogDebug("Received HeartbeatAck");


if (_heartbeatTimes.TryDequeue(out long time)) if (_heartbeatTimes.TryDequeue(out long time))
{ {
@@ -553,8 +579,8 @@ namespace Discord.WebSocket
break; break;
case GatewayOpCode.InvalidSession: 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; _sessionId = null;
_lastSeq = 0; _lastSeq = 0;
@@ -577,7 +603,7 @@ namespace Discord.WebSocket
break; break;
case GatewayOpCode.Reconnect: case GatewayOpCode.Reconnect:
{ {
await _gatewayLogger.DebugAsync("Received Reconnect").ConfigureAwait(false);
_gatewayLogger.LogDebug("Received Reconnect");
_connection.Error(new GatewayReconnectException("Server requested a reconnect")); _connection.Error(new GatewayReconnectException("Server requested a reconnect"));
} }
break; break;
@@ -589,7 +615,7 @@ namespace Discord.WebSocket
{ {
try try
{ {
await _gatewayLogger.DebugAsync("Received Dispatch (READY)").ConfigureAwait(false);
_gatewayLogger.LogDebug("Received Dispatch (READY)");


var data = (payload as JToken).ToObject<ReadyEvent>(_serializer); var data = (payload as JToken).ToObject<ReadyEvent>(_serializer);
var state = new ClientState(data.Guilds.Length, data.PrivateChannels.Length); var state = new ClientState(data.Guilds.Length, data.PrivateChannels.Length);
@@ -637,14 +663,14 @@ namespace Discord.WebSocket
_ = DownloadUsersAsync(Guilds.Where(x => x.IsAvailable && !x.HasAllMembers)); _ = DownloadUsersAsync(Guilds.Where(x => x.IsAvailable && !x.HasAllMembers));


await TimedInvokeAsync(_readyEvent, nameof(Ready)).ConfigureAwait(false); await TimedInvokeAsync(_readyEvent, nameof(Ready)).ConfigureAwait(false);
await _gatewayLogger.InfoAsync("Ready").ConfigureAwait(false);
_gatewayLogger.LogInformation("Ready");
}); });
_ = _connection.CompleteAsync(); _ = _connection.CompleteAsync();
} }
break; break;
case "RESUMED": case "RESUMED":
{ {
await _gatewayLogger.DebugAsync("Received Dispatch (RESUMED)").ConfigureAwait(false);
_gatewayLogger.LogDebug("Received Dispatch (RESUMED)");


_ = _connection.CompleteAsync(); _ = _connection.CompleteAsync();


@@ -655,7 +681,7 @@ namespace Discord.WebSocket
await GuildAvailableAsync(guild).ConfigureAwait(false); await GuildAvailableAsync(guild).ConfigureAwait(false);
} }


await _gatewayLogger.InfoAsync("Resumed previous session").ConfigureAwait(false);
_gatewayLogger.LogInformation("Resumed previous session");
} }
break; break;


@@ -668,7 +694,7 @@ namespace Discord.WebSocket
{ {
type = "GUILD_AVAILABLE"; type = "GUILD_AVAILABLE";
_lastGuildAvailableTime = Environment.TickCount; _lastGuildAvailableTime = Environment.TickCount;
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_AVAILABLE)").ConfigureAwait(false);
_gatewayLogger.LogDebug("Received Dispatch (GUILD_AVAILABLE)");


var guild = State.GetGuild(data.Id); var guild = State.GetGuild(data.Id);
if (guild != null) if (guild != null)
@@ -693,7 +719,7 @@ namespace Discord.WebSocket
} }
else else
{ {
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_CREATE)").ConfigureAwait(false);
_gatewayLogger.LogDebug("Received Dispatch (GUILD_CREATE)");


var guild = AddGuild(data, State); var guild = AddGuild(data, State);
if (guild != null) if (guild != null)
@@ -711,7 +737,7 @@ namespace Discord.WebSocket
break; break;
case "GUILD_UPDATE": case "GUILD_UPDATE":
{ {
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_UPDATE)").ConfigureAwait(false);
_gatewayLogger.LogDebug("Received Dispatch (GUILD_UPDATE)");


var data = (payload as JToken).ToObject<API.Guild>(_serializer); var data = (payload as JToken).ToObject<API.Guild>(_serializer);
var guild = State.GetGuild(data.Id); var guild = State.GetGuild(data.Id);
@@ -730,7 +756,7 @@ namespace Discord.WebSocket
break; break;
case "GUILD_EMOJIS_UPDATE": 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<API.Gateway.GuildEmojiUpdateEvent>(_serializer); var data = (payload as JToken).ToObject<API.Gateway.GuildEmojiUpdateEvent>(_serializer);
var guild = State.GetGuild(data.GuildId); var guild = State.GetGuild(data.GuildId);
@@ -749,7 +775,7 @@ namespace Discord.WebSocket
break; break;
case "GUILD_SYNC": case "GUILD_SYNC":
{ {
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_SYNC)").ConfigureAwait(false);
_gatewayLogger.LogDebug("Received Dispatch (GUILD_SYNC)");
var data = (payload as JToken).ToObject<GuildSyncEvent>(_serializer); var data = (payload as JToken).ToObject<GuildSyncEvent>(_serializer);
var guild = State.GetGuild(data.Id); var guild = State.GetGuild(data.Id);
if (guild != null) if (guild != null)
@@ -775,7 +801,7 @@ namespace Discord.WebSocket
if (data.Unavailable == true) if (data.Unavailable == true)
{ {
type = "GUILD_UNAVAILABLE"; type = "GUILD_UNAVAILABLE";
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_UNAVAILABLE)").ConfigureAwait(false);
_gatewayLogger.LogDebug("Received Dispatch (GUILD_UNAVAILABLE)");


var guild = State.GetGuild(data.Id); var guild = State.GetGuild(data.Id);
if (guild != null) if (guild != null)
@@ -791,7 +817,7 @@ namespace Discord.WebSocket
} }
else else
{ {
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_DELETE)").ConfigureAwait(false);
_gatewayLogger.LogDebug("Received Dispatch (GUILD_DELETE)");


var guild = RemoveGuild(data.Id); var guild = RemoveGuild(data.Id);
if (guild != null) if (guild != null)
@@ -812,7 +838,7 @@ namespace Discord.WebSocket
//Channels //Channels
case "CHANNEL_CREATE": case "CHANNEL_CREATE":
{ {
await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_CREATE)").ConfigureAwait(false);
_gatewayLogger.LogDebug("Received Dispatch (CHANNEL_CREATE)");


var data = (payload as JToken).ToObject<API.Channel>(_serializer); var data = (payload as JToken).ToObject<API.Channel>(_serializer);
SocketChannel channel = null; SocketChannel channel = null;
@@ -849,7 +875,7 @@ namespace Discord.WebSocket
break; break;
case "CHANNEL_UPDATE": case "CHANNEL_UPDATE":
{ {
await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_UPDATE)").ConfigureAwait(false);
_gatewayLogger.LogDebug("Received Dispatch (CHANNEL_UPDATE)");


var data = (payload as JToken).ToObject<API.Channel>(_serializer); var data = (payload as JToken).ToObject<API.Channel>(_serializer);
var channel = State.GetChannel(data.Id); var channel = State.GetChannel(data.Id);
@@ -876,7 +902,7 @@ namespace Discord.WebSocket
break; break;
case "CHANNEL_DELETE": case "CHANNEL_DELETE":
{ {
await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_DELETE)").ConfigureAwait(false);
_gatewayLogger.LogDebug("Received Dispatch (CHANNEL_DELETE)");


SocketChannel channel = null; SocketChannel channel = null;
var data = (payload as JToken).ToObject<API.Channel>(_serializer); var data = (payload as JToken).ToObject<API.Channel>(_serializer);
@@ -915,7 +941,7 @@ namespace Discord.WebSocket
//Members //Members
case "GUILD_MEMBER_ADD": 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<GuildMemberAddEvent>(_serializer); var data = (payload as JToken).ToObject<GuildMemberAddEvent>(_serializer);
var guild = State.GetGuild(data.GuildId); var guild = State.GetGuild(data.GuildId);
@@ -941,7 +967,7 @@ namespace Discord.WebSocket
break; break;
case "GUILD_MEMBER_UPDATE": 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<GuildMemberUpdateEvent>(_serializer); var data = (payload as JToken).ToObject<GuildMemberUpdateEvent>(_serializer);
var guild = State.GetGuild(data.GuildId); var guild = State.GetGuild(data.GuildId);
@@ -986,7 +1012,7 @@ namespace Discord.WebSocket
break; break;
case "GUILD_MEMBER_REMOVE": 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<GuildMemberRemoveEvent>(_serializer); var data = (payload as JToken).ToObject<GuildMemberRemoveEvent>(_serializer);
var guild = State.GetGuild(data.GuildId); var guild = State.GetGuild(data.GuildId);
@@ -1021,7 +1047,7 @@ namespace Discord.WebSocket
break; break;
case "GUILD_MEMBERS_CHUNK": 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<GuildMembersChunkEvent>(_serializer); var data = (payload as JToken).ToObject<GuildMembersChunkEvent>(_serializer);
var guild = State.GetGuild(data.GuildId); var guild = State.GetGuild(data.GuildId);
@@ -1045,7 +1071,7 @@ namespace Discord.WebSocket
break; break;
case "CHANNEL_RECIPIENT_ADD": 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<RecipientEvent>(_serializer); var data = (payload as JToken).ToObject<RecipientEvent>(_serializer);
if (State.GetChannel(data.ChannelId) is SocketGroupChannel channel) if (State.GetChannel(data.ChannelId) is SocketGroupChannel channel)
@@ -1062,7 +1088,7 @@ namespace Discord.WebSocket
break; break;
case "CHANNEL_RECIPIENT_REMOVE": 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<RecipientEvent>(_serializer); var data = (payload as JToken).ToObject<RecipientEvent>(_serializer);
if (State.GetChannel(data.ChannelId) is SocketGroupChannel channel) if (State.GetChannel(data.ChannelId) is SocketGroupChannel channel)
@@ -1087,7 +1113,7 @@ namespace Discord.WebSocket
//Roles //Roles
case "GUILD_ROLE_CREATE": 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<GuildRoleCreateEvent>(_serializer); var data = (payload as JToken).ToObject<GuildRoleCreateEvent>(_serializer);
var guild = State.GetGuild(data.GuildId); var guild = State.GetGuild(data.GuildId);
@@ -1111,7 +1137,7 @@ namespace Discord.WebSocket
break; break;
case "GUILD_ROLE_UPDATE": 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<GuildRoleUpdateEvent>(_serializer); var data = (payload as JToken).ToObject<GuildRoleUpdateEvent>(_serializer);
var guild = State.GetGuild(data.GuildId); var guild = State.GetGuild(data.GuildId);
@@ -1146,7 +1172,7 @@ namespace Discord.WebSocket
break; break;
case "GUILD_ROLE_DELETE": 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<GuildRoleDeleteEvent>(_serializer); var data = (payload as JToken).ToObject<GuildRoleDeleteEvent>(_serializer);
var guild = State.GetGuild(data.GuildId); var guild = State.GetGuild(data.GuildId);
@@ -1180,7 +1206,7 @@ namespace Discord.WebSocket
//Bans //Bans
case "GUILD_BAN_ADD": 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<GuildBanEvent>(_serializer); var data = (payload as JToken).ToObject<GuildBanEvent>(_serializer);
var guild = State.GetGuild(data.GuildId); var guild = State.GetGuild(data.GuildId);
@@ -1206,7 +1232,7 @@ namespace Discord.WebSocket
break; break;
case "GUILD_BAN_REMOVE": 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<GuildBanEvent>(_serializer); var data = (payload as JToken).ToObject<GuildBanEvent>(_serializer);
var guild = State.GetGuild(data.GuildId); var guild = State.GetGuild(data.GuildId);
@@ -1234,7 +1260,7 @@ namespace Discord.WebSocket
//Messages //Messages
case "MESSAGE_CREATE": case "MESSAGE_CREATE":
{ {
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_CREATE)").ConfigureAwait(false);
_gatewayLogger.LogDebug("Received Dispatch (MESSAGE_CREATE)");


var data = (payload as JToken).ToObject<API.Message>(_serializer); var data = (payload as JToken).ToObject<API.Message>(_serializer);
if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel) if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
@@ -1291,7 +1317,7 @@ namespace Discord.WebSocket
break; break;
case "MESSAGE_UPDATE": case "MESSAGE_UPDATE":
{ {
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_UPDATE)").ConfigureAwait(false);
_gatewayLogger.LogDebug("Received Dispatch (MESSAGE_UPDATE)");


var data = (payload as JToken).ToObject<API.Message>(_serializer); var data = (payload as JToken).ToObject<API.Message>(_serializer);
if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel) if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
@@ -1344,7 +1370,7 @@ namespace Discord.WebSocket
break; break;
case "MESSAGE_DELETE": case "MESSAGE_DELETE":
{ {
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE)").ConfigureAwait(false);
_gatewayLogger.LogDebug("Received Dispatch (MESSAGE_DELETE)");


var data = (payload as JToken).ToObject<API.Message>(_serializer); var data = (payload as JToken).ToObject<API.Message>(_serializer);
if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel) if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
@@ -1371,7 +1397,7 @@ namespace Discord.WebSocket
break; break;
case "MESSAGE_REACTION_ADD": 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<API.Gateway.Reaction>(_serializer); var data = (payload as JToken).ToObject<API.Gateway.Reaction>(_serializer);
if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel) if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
@@ -1412,7 +1438,7 @@ namespace Discord.WebSocket
break; break;
case "MESSAGE_REACTION_REMOVE": 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<API.Gateway.Reaction>(_serializer); var data = (payload as JToken).ToObject<API.Gateway.Reaction>(_serializer);
if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel) if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
@@ -1445,7 +1471,7 @@ namespace Discord.WebSocket
break; break;
case "MESSAGE_REACTION_REMOVE_ALL": 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<RemoveAllReactionsEvent>(_serializer); var data = (payload as JToken).ToObject<RemoveAllReactionsEvent>(_serializer);
if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel) if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
@@ -1467,7 +1493,7 @@ namespace Discord.WebSocket
break; break;
case "MESSAGE_REACTION_REMOVE_EMOJI": 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<API.Gateway.RemoveAllReactionsForEmoteEvent>(_serializer); var data = (payload as JToken).ToObject<API.Gateway.RemoveAllReactionsForEmoteEvent>(_serializer);
if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel) if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
@@ -1495,11 +1521,11 @@ namespace Discord.WebSocket
break; break;
case "MESSAGE_DELETE_BULK": case "MESSAGE_DELETE_BULK":
{ {
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE_BULK)").ConfigureAwait(false);
_gatewayLogger.LogDebug("Received Dispatch (MESSAGE_DELETE_BULK)");


if (!ExclusiveBulkDelete.HasValue) 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. " + "To suppress this message, set the ExclusiveBulkDelete configuration property. " +
"This message will appear only once."); "This message will appear only once.");
ExclusiveBulkDelete = false; ExclusiveBulkDelete = false;
@@ -1540,7 +1566,7 @@ namespace Discord.WebSocket
//Statuses //Statuses
case "PRESENCE_UPDATE": case "PRESENCE_UPDATE":
{ {
await _gatewayLogger.DebugAsync("Received Dispatch (PRESENCE_UPDATE)").ConfigureAwait(false);
_gatewayLogger.LogDebug("Received Dispatch (PRESENCE_UPDATE)");


var data = (payload as JToken).ToObject<API.Presence>(_serializer); var data = (payload as JToken).ToObject<API.Presence>(_serializer);


@@ -1599,7 +1625,7 @@ namespace Discord.WebSocket
break; break;
case "TYPING_START": case "TYPING_START":
{ {
await _gatewayLogger.DebugAsync("Received Dispatch (TYPING_START)").ConfigureAwait(false);
_gatewayLogger.LogDebug("Received Dispatch (TYPING_START)");


var data = (payload as JToken).ToObject<TypingStartEvent>(_serializer); var data = (payload as JToken).ToObject<TypingStartEvent>(_serializer);
if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel) if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
@@ -1626,7 +1652,7 @@ namespace Discord.WebSocket
//Users //Users
case "USER_UPDATE": case "USER_UPDATE":
{ {
await _gatewayLogger.DebugAsync("Received Dispatch (USER_UPDATE)").ConfigureAwait(false);
_gatewayLogger.LogDebug("Received Dispatch (USER_UPDATE)");


var data = (payload as JToken).ToObject<API.User>(_serializer); var data = (payload as JToken).ToObject<API.User>(_serializer);
if (data.Id == CurrentUser.Id) if (data.Id == CurrentUser.Id)
@@ -1637,7 +1663,7 @@ namespace Discord.WebSocket
} }
else else
{ {
await _gatewayLogger.WarningAsync("Received USER_UPDATE for wrong user.").ConfigureAwait(false);
_gatewayLogger.LogWarning("Received USER_UPDATE for wrong user.");
return; return;
} }
} }
@@ -1646,7 +1672,7 @@ namespace Discord.WebSocket
//Voice //Voice
case "VOICE_STATE_UPDATE": 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<API.VoiceState>(_serializer); var data = (payload as JToken).ToObject<API.VoiceState>(_serializer);
SocketUser user; SocketUser user;
@@ -1720,7 +1746,7 @@ namespace Discord.WebSocket
break; break;
case "VOICE_SERVER_UPDATE": 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<VoiceServerUpdateEvent>(_serializer); var data = (payload as JToken).ToObject<VoiceServerUpdateEvent>(_serializer);
var guild = State.GetGuild(data.GuildId); var guild = State.GetGuild(data.GuildId);
@@ -1753,7 +1779,7 @@ namespace Discord.WebSocket
//Invites //Invites
case "INVITE_CREATE": case "INVITE_CREATE":
{ {
await _gatewayLogger.DebugAsync("Received Dispatch (INVITE_CREATE)").ConfigureAwait(false);
_gatewayLogger.LogDebug("Received Dispatch (INVITE_CREATE)");


var data = (payload as JToken).ToObject<API.Gateway.InviteCreateEvent>(_serializer); var data = (payload as JToken).ToObject<API.Gateway.InviteCreateEvent>(_serializer);
if (State.GetChannel(data.ChannelId) is SocketGuildChannel channel) if (State.GetChannel(data.ChannelId) is SocketGuildChannel channel)
@@ -1786,7 +1812,7 @@ namespace Discord.WebSocket
break; break;
case "INVITE_DELETE": case "INVITE_DELETE":
{ {
await _gatewayLogger.DebugAsync("Received Dispatch (INVITE_DELETE)").ConfigureAwait(false);
_gatewayLogger.LogDebug("Received Dispatch (INVITE_DELETE)");


var data = (payload as JToken).ToObject<API.Gateway.InviteDeleteEvent>(_serializer); var data = (payload as JToken).ToObject<API.Gateway.InviteDeleteEvent>(_serializer);
if (State.GetChannel(data.ChannelId) is SocketGuildChannel channel) if (State.GetChannel(data.ChannelId) is SocketGuildChannel channel)
@@ -1810,41 +1836,41 @@ namespace Discord.WebSocket


//Ignored (User only) //Ignored (User only)
case "CHANNEL_PINS_ACK": case "CHANNEL_PINS_ACK":
await _gatewayLogger.DebugAsync("Ignored Dispatch (CHANNEL_PINS_ACK)").ConfigureAwait(false);
_gatewayLogger.LogDebug("Ignored Dispatch (CHANNEL_PINS_ACK)");
break; break;
case "CHANNEL_PINS_UPDATE": case "CHANNEL_PINS_UPDATE":
await _gatewayLogger.DebugAsync("Ignored Dispatch (CHANNEL_PINS_UPDATE)").ConfigureAwait(false);
_gatewayLogger.LogDebug("Ignored Dispatch (CHANNEL_PINS_UPDATE)");
break; break;
case "GUILD_INTEGRATIONS_UPDATE": case "GUILD_INTEGRATIONS_UPDATE":
await _gatewayLogger.DebugAsync("Ignored Dispatch (GUILD_INTEGRATIONS_UPDATE)").ConfigureAwait(false);
_gatewayLogger.LogDebug("Ignored Dispatch (GUILD_INTEGRATIONS_UPDATE)");
break; break;
case "MESSAGE_ACK": case "MESSAGE_ACK":
await _gatewayLogger.DebugAsync("Ignored Dispatch (MESSAGE_ACK)").ConfigureAwait(false);
_gatewayLogger.LogDebug("Ignored Dispatch (MESSAGE_ACK)");
break; break;
case "PRESENCES_REPLACE": case "PRESENCES_REPLACE":
await _gatewayLogger.DebugAsync("Ignored Dispatch (PRESENCES_REPLACE)").ConfigureAwait(false);
_gatewayLogger.LogDebug("Ignored Dispatch (PRESENCES_REPLACE)");
break; break;
case "USER_SETTINGS_UPDATE": case "USER_SETTINGS_UPDATE":
await _gatewayLogger.DebugAsync("Ignored Dispatch (USER_SETTINGS_UPDATE)").ConfigureAwait(false);
_gatewayLogger.LogDebug("Ignored Dispatch (USER_SETTINGS_UPDATE)");
break; break;
case "WEBHOOKS_UPDATE": case "WEBHOOKS_UPDATE":
await _gatewayLogger.DebugAsync("Ignored Dispatch (WEBHOOKS_UPDATE)").ConfigureAwait(false);
_gatewayLogger.LogDebug("Ignored Dispatch (WEBHOOKS_UPDATE)");
break; break;


//Others //Others
default: default:
await _gatewayLogger.WarningAsync($"Unknown Dispatch ({type})").ConfigureAwait(false);
_gatewayLogger.LogWarning($"Unknown Dispatch ({type})");
break; break;
} }
break; break;
default: default:
await _gatewayLogger.WarningAsync($"Unknown OpCode ({opCode})").ConfigureAwait(false);
_gatewayLogger.LogWarning($"Unknown OpCode ({opCode})");
break; break;
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
await _gatewayLogger.ErrorAsync($"Error handling {opCode}{(type != null ? $" ({type})" : "")}", ex).ConfigureAwait(false);
_gatewayLogger.LogError(ex, $"Error handling {opCode}{(type != null ? $" ({type})" : "")}");
} }
} }


@@ -1852,7 +1878,7 @@ namespace Discord.WebSocket
{ {
try try
{ {
await _gatewayLogger.DebugAsync("Heartbeat Started").ConfigureAwait(false);
_gatewayLogger.LogDebug("Heartbeat Started");
while (!cancelToken.IsCancellationRequested) while (!cancelToken.IsCancellationRequested)
{ {
int now = Environment.TickCount; int now = Environment.TickCount;
@@ -1874,20 +1900,20 @@ namespace Discord.WebSocket
} }
catch (Exception ex) catch (Exception ex)
{ {
await _gatewayLogger.WarningAsync("Heartbeat Errored", ex).ConfigureAwait(false);
_gatewayLogger.LogWarning("Heartbeat Errored", ex);
} }


await Task.Delay(intervalMillis, cancelToken).ConfigureAwait(false); await Task.Delay(intervalMillis, cancelToken).ConfigureAwait(false);
} }
await _gatewayLogger.DebugAsync("Heartbeat Stopped").ConfigureAwait(false);
_gatewayLogger.LogDebug("Heartbeat Stopped");
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
await _gatewayLogger.DebugAsync("Heartbeat Stopped").ConfigureAwait(false);
_gatewayLogger.LogDebug("Heartbeat Stopped");
} }
catch (Exception ex) catch (Exception ex)
{ {
await _gatewayLogger.ErrorAsync("Heartbeat Errored", ex).ConfigureAwait(false);
_gatewayLogger.LogError(ex, "Heartbeat Errored");
} }
} }
/*public async Task WaitForGuildsAsync() /*public async Task WaitForGuildsAsync()
@@ -1896,23 +1922,23 @@ namespace Discord.WebSocket
if (downloadTask != null) if (downloadTask != null)
await _guildDownloadTask.ConfigureAwait(false); await _guildDownloadTask.ConfigureAwait(false);
}*/ }*/
private async Task WaitForGuildsAsync(CancellationToken cancelToken, Logger logger)
private async Task WaitForGuildsAsync(CancellationToken cancelToken, ILogger logger)
{ {
//Wait for GUILD_AVAILABLEs //Wait for GUILD_AVAILABLEs
try try
{ {
await logger.DebugAsync("GuildDownloader Started").ConfigureAwait(false);
logger.LogDebug("GuildDownloader Started");
while ((_unavailableGuildCount != 0) && (Environment.TickCount - _lastGuildAvailableTime < BaseConfig.MaxWaitBetweenGuildAvailablesBeforeReady)) while ((_unavailableGuildCount != 0) && (Environment.TickCount - _lastGuildAvailableTime < BaseConfig.MaxWaitBetweenGuildAvailablesBeforeReady))
await Task.Delay(500, cancelToken).ConfigureAwait(false); await Task.Delay(500, cancelToken).ConfigureAwait(false);
await logger.DebugAsync("GuildDownloader Stopped").ConfigureAwait(false);
logger.LogDebug("GuildDownloader Stopped");
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
await logger.DebugAsync("GuildDownloader Stopped").ConfigureAwait(false);
logger.LogDebug("GuildDownloader Stopped");
} }
catch (Exception ex) catch (Exception ex)
{ {
await logger.ErrorAsync("GuildDownloader Errored", ex).ConfigureAwait(false);
logger.LogError(ex, "GuildDownloader Errored");
} }
} }
private async Task SyncGuildsAsync() private async Task SyncGuildsAsync()
@@ -2042,40 +2068,45 @@ namespace Discord.WebSocket
var handlersTask = action(); var handlersTask = action();
if (await Task.WhenAny(timeoutTask, handlersTask).ConfigureAwait(false) == timeoutTask) 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 await handlersTask.ConfigureAwait(false); //Ensure the handler completes
} }
catch (Exception ex) 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}"; 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}"; 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}"; 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}"; 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}"; 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) private async Task UnknownChannelAsync(string evnt, ulong channelId, ulong guildId)
{ {
@@ -2085,22 +2116,25 @@ namespace Discord.WebSocket
return; return;
} }
string details = $"{evnt} Channel={channelId} Guild={guildId}"; 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}"; 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}"; 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}"; 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++; internal int GetAudioId() => _nextAudioId++;


+ 21
- 11
src/Discord.Net.Webhook/DiscordWebhookClient.cs View File

@@ -6,21 +6,19 @@ using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using Discord.Logging; using Discord.Logging;
using Discord.Rest; using Discord.Rest;
using Microsoft.Extensions.Logging;


namespace Discord.Webhook namespace Discord.Webhook
{ {
/// <summary> A client responsible for connecting as a Webhook. </summary> /// <summary> A client responsible for connecting as a Webhook. </summary>
public class DiscordWebhookClient : IDisposable public class DiscordWebhookClient : IDisposable
{ {
public event Func<LogMessage, Task> Log { add { _logEvent.Add(value); } remove { _logEvent.Remove(value); } }
internal readonly AsyncEvent<Func<LogMessage, Task>> _logEvent = new AsyncEvent<Func<LogMessage, Task>>();

private readonly ulong _webhookId; private readonly ulong _webhookId;
internal IWebhook Webhook; internal IWebhook Webhook;
internal readonly Logger _restLogger;
internal readonly ILogger _restLogger;
internal API.DiscordRestApiClient ApiClient { get; } internal API.DiscordRestApiClient ApiClient { get; }
internal LogManager LogManager { get; }
internal ILoggerFactory LogManager { get; }


/// <summary> Creates a new Webhook Discord client. </summary> /// <summary> Creates a new Webhook Discord client. </summary>
public DiscordWebhookClient(IWebhook webhook) public DiscordWebhookClient(IWebhook webhook)
@@ -69,19 +67,31 @@ namespace Discord.Webhook
private DiscordWebhookClient(DiscordRestConfig config) private DiscordWebhookClient(DiscordRestConfig config)
{ {
ApiClient = CreateApiClient(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"); _restLogger = LogManager.CreateLogger("Rest");


ApiClient.RequestQueue.RateLimitTriggered += async (id, info, endpoint) =>
ApiClient.RequestQueue.RateLimitTriggered += (id, info, endpoint) =>
{ {
if (info == null) 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 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) private static API.DiscordRestApiClient CreateApiClient(DiscordRestConfig config)
=> new API.DiscordRestApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent); => new API.DiscordRestApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent);


+ 0
- 6
test/Discord.Net.Tests.Integration/ChannelsTests.cs View File

@@ -22,12 +22,6 @@ namespace Discord
this.output = output; this.output = output;
output.WriteLine($"RestGuildFixture using guild: {guild.Id}"); output.WriteLine($"RestGuildFixture using guild: {guild.Id}");
// capture all console output // capture all console output
guildFixture.Client.Log += LogAsync;
}
private Task LogAsync(LogMessage message)
{
output.WriteLine(message.ToString());
return Task.CompletedTask;
} }


/// <summary> /// <summary>


+ 0
- 1
test/Discord.Net.Tests.Integration/DiscordRestClientFixture.cs View File

@@ -19,7 +19,6 @@ namespace Discord
throw new Exception("The DNET_TEST_TOKEN environment variable was not provided."); throw new Exception("The DNET_TEST_TOKEN environment variable was not provided.");
Client = new DiscordRestClient(new DiscordRestConfig() Client = new DiscordRestClient(new DiscordRestConfig()
{ {
LogLevel = LogSeverity.Debug,
DefaultRetryMode = RetryMode.AlwaysRetry DefaultRetryMode = RetryMode.AlwaysRetry
}); });
Client.LoginAsync(TokenType.Bot, token).Wait(); Client.LoginAsync(TokenType.Bot, token).Wait();


+ 0
- 6
test/Discord.Net.Tests.Integration/GuildTests.cs View File

@@ -20,12 +20,6 @@ namespace Discord
guild = guildFixture.Guild; guild = guildFixture.Guild;
this.output = output; this.output = output;
output.WriteLine($"RestGuildFixture using guild: {guild.Id}"); output.WriteLine($"RestGuildFixture using guild: {guild.Id}");
guildFixture.Client.Log += LogAsync;
}
private Task LogAsync(LogMessage message)
{
output.WriteLine(message.ToString());
return Task.CompletedTask;
} }
/// <summary> /// <summary>
/// Ensures that the CurrentUser is the owner of the guild. /// Ensures that the CurrentUser is the owner of the guild.


Loading…
Cancel
Save