Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Feature: Add ILogger #1765

Open
wants to merge 9 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 1 addition & 8 deletions samples/01_basic_ping_bot/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ public Program()
// It is recommended to Dispose of a client when you are finished
// using it, at the end of your app's lifetime.
_client = new DiscordSocketClient();

_client.Log += LogAsync;

_client.Ready += ReadyAsync;
_client.MessageReceived += MessageReceivedAsync;
}
Expand All @@ -47,12 +46,6 @@ public async Task MainAsync()
await Task.Delay(Timeout.Infinite);
}

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

// The Ready event indicates that the client has opened a
// connection and it is now safe to access the cache.
private Task ReadyAsync()
Expand Down
10 changes: 0 additions & 10 deletions samples/02_commands_framework/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,6 @@ public async Task MainAsync()
{
var client = services.GetRequiredService<DiscordSocketClient>();

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

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

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

return Task.CompletedTask;
}

private ServiceProvider ConfigureServices()
{
return new ServiceCollection()
Expand Down
7 changes: 0 additions & 7 deletions samples/03_sharded_client/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ public async Task MainAsync()
// The ShardReady event is used instead, allowing for individual
// control per shard.
client.ShardReady += ReadyAsync;
client.Log += LogAsync;

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

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

private Task LogAsync(LogMessage log)
{
Console.WriteLine(log.ToString());
return Task.CompletedTask;
}
}
}
8 changes: 0 additions & 8 deletions samples/03_sharded_client/Services/CommandHandlingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ public CommandHandlingService(IServiceProvider services)
_services = services;

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

Expand Down Expand Up @@ -61,12 +60,5 @@ public async Task CommandExecutedAsync(Optional<CommandInfo> command, ICommandCo
// the command failed, let's notify the user that something happened.
await context.Channel.SendMessageAsync($"error: {result.ToString()}");
}

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

return Task.CompletedTask;
}
}
}
55 changes: 5 additions & 50 deletions samples/idn/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,60 +34,15 @@ public class Program
static async Task Main(string[] args)
{
var token = File.ReadAllText("token.ignore");
var client = new DiscordSocketClient(new DiscordSocketConfig { LogLevel = LogSeverity.Debug });
var logQueue = new ConcurrentQueue<LogMessage>();
var client = new DiscordSocketClient(new DiscordSocketConfig { });
var logCancelToken = new CancellationTokenSource();
int presenceUpdates = 0;

client.Log += msg =>
{
logQueue.Enqueue(msg);
return Task.CompletedTask;
};

Console.CancelKeyPress += (_ev, _s) =>
{
logCancelToken.Cancel();
};

var logTask = Task.Run(async () =>
{
var fs = new FileStream("idn.log", FileMode.Append);
var logStringBuilder = new StringBuilder(200);
string logString = "";

byte[] helloBytes = Encoding.UTF8.GetBytes($"### new log session: {DateTime.Now} ###\n\n");
await fs.WriteAsync(helloBytes);

while (!logCancelToken.IsCancellationRequested)
{
if (logQueue.TryDequeue(out var msg))
{
if (msg.Message?.IndexOf("PRESENCE_UPDATE)") > 0)
{
presenceUpdates++;
continue;
}

_ = msg.ToString(builder: logStringBuilder);
logStringBuilder.AppendLine();
logString = logStringBuilder.ToString();

Debug.Write(logString, "DNET");
await fs.WriteAsync(Encoding.UTF8.GetBytes(logString));
}
await fs.FlushAsync();
try
{
await Task.Delay(100, logCancelToken.Token);
}
finally { }
}

byte[] goodbyeBytes = Encoding.UTF8.GetBytes($"#!! end log session: {DateTime.Now} !!#\n\n\n");
await fs.WriteAsync(goodbyeBytes);
await fs.DisposeAsync();
});

await client.LoginAsync(TokenType.Bot, token);
await client.StartAsync();

Expand Down Expand Up @@ -127,9 +82,9 @@ static async Task Main(string[] args)
await client.StopAsync();
client.Dispose();
logCancelToken.Cancel();
try
{ await logTask; }
finally { Console.WriteLine("goodbye!"); }

await Task.Delay(-1, logCancelToken.Token);
Console.WriteLine("goodbye!");
}

static IEnumerable<Assembly> GetAssemblies()
Expand Down
5 changes: 3 additions & 2 deletions src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Threading.Tasks;

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

namespace Discord.Commands
{
Expand Down Expand Up @@ -34,7 +35,7 @@ bool IsLoadableModule(TypeInfo info)
}
else if (IsLoadableModule(typeInfo))
{
await service._cmdLogger.WarningAsync($"Class {typeInfo.FullName} is not public and cannot be loaded. To suppress this message, mark the class with {nameof(DontAutoLoadAttribute)}.").ConfigureAwait(false);
service._cmdLogger.LogWarning($"Class {typeInfo.FullName} is not public and cannot be loaded. To suppress this message, mark the class with {nameof(DontAutoLoadAttribute)}.");
}
}

Expand Down Expand Up @@ -69,7 +70,7 @@ public static async Task<Dictionary<Type, ModuleInfo>> BuildAsync(IEnumerable<Ty
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;
}
Expand Down
24 changes: 13 additions & 11 deletions src/Discord.Net.Commands/CommandService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Threading.Tasks;
using Discord.Commands.Builders;
using Discord.Logging;
using Microsoft.Extensions.Logging;

namespace Discord.Commands
{
Expand All @@ -29,12 +30,6 @@ namespace Discord.Commands
/// </remarks>
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>
/// Occurs when a command is executed.
/// </summary>
Expand All @@ -56,8 +51,8 @@ public class CommandService : IDisposable
internal readonly bool _caseSensitive, _throwOnError, _ignoreExtraArgs;
internal readonly char _separatorChar;
internal readonly RunMode _defaultRunMode;
internal readonly Logger _cmdLogger;
internal readonly LogManager _logManager;
internal readonly ILogger _cmdLogger;
internal readonly ILoggerFactory _logManager;
internal readonly IReadOnlyDictionary<char, char> _quotationMarkAliasMap;

internal bool _isDisposed;
Expand Down Expand Up @@ -100,8 +95,14 @@ public CommandService(CommandServiceConfig config)
if (_defaultRunMode == RunMode.Default)
throw new InvalidOperationException("The default run mode cannot be set to Default.");

_logManager = new LogManager(config.LogLevel);
_logManager.Message += async msg => await _logEvent.InvokeAsync(msg).ConfigureAwait(false);
_logManager = config.LoggerFactory;

if (_logManager == null)
{
_logManager = new DefaultLoggerFactory();
_logManager.AddProvider(new DefaultLoggerProvider());
}

_cmdLogger = _logManager.CreateLogger("Command");

_moduleLock = new SemaphoreSlim(1, 1);
Expand Down Expand Up @@ -349,7 +350,8 @@ public void AddTypeReader<T>(TypeReader reader)
public void AddTypeReader(Type type, TypeReader reader)
{
if (_defaultTypeReaders.ContainsKey(type))
_ = _cmdLogger.WarningAsync($"The default TypeReader for {type.FullName} was replaced by {reader.GetType().FullName}." +
_cmdLogger.LogWarning(
$"The default TypeReader for {type.FullName} was replaced by {reader.GetType().FullName}." +
"To suppress this message, use AddTypeReader<T>(reader, true).");
AddTypeReader(type, reader, true);
}
Expand Down
6 changes: 2 additions & 4 deletions src/Discord.Net.Commands/CommandServiceConfig.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Microsoft.Extensions.Logging;
using System.Collections.Generic;

namespace Discord.Commands
Expand All @@ -23,10 +24,7 @@ public class CommandServiceConfig
/// </summary>
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>
/// Gets or sets whether <see cref="RunMode.Sync"/> commands should push exceptions up to the caller.
Expand Down
7 changes: 4 additions & 3 deletions src/Discord.Net.Commands/Info/CommandInfo.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Discord.Commands.Builders;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
Expand Down Expand Up @@ -240,7 +241,7 @@ public async Task<IResult> ExecuteAsync(ICommandContext context, IEnumerable<obj

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
{
var task = _action(context, args, services, this);
Expand Down Expand Up @@ -274,7 +275,7 @@ private async Task<IResult> ExecuteInternalAsync(ICommandContext context, object
ex = ex.InnerException;

var wrappedEx = new CommandException(this, context, ex);
await Module.Service._cmdLogger.ErrorAsync(wrappedEx).ConfigureAwait(false);
Module.Service._cmdLogger.LogError(wrappedEx, wrappedEx.Message);

var result = ExecuteResult.FromError(ex);
await Module.Service._commandExecutedEvent.InvokeAsync(this, context, result).ConfigureAwait(false);
Expand All @@ -291,7 +292,7 @@ private async Task<IResult> ExecuteInternalAsync(ICommandContext context, object
}
finally
{
await Module.Service._cmdLogger.VerboseAsync($"Executed {GetLogText(context)}").ConfigureAwait(false);
Module.Service._cmdLogger.LogTrace($"Executed {GetLogText(context)}");
}
}

Expand Down
1 change: 1 addition & 0 deletions src/Discord.Net.Core/Discord.Net.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard2.0;netstandard2.1</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="System.Collections.Immutable" Version="1.3.1" />
<PackageReference Include="System.Interactive.Async" Version="4.0.0" />
Expand Down
11 changes: 3 additions & 8 deletions src/Discord.Net.Core/DiscordConfig.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Microsoft.Extensions.Logging;
using System.Reflection;

namespace Discord
Expand Down Expand Up @@ -123,14 +124,8 @@ public class DiscordConfig
/// The currently set <see cref="RetryMode"/>.
/// </returns>
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>
/// Gets or sets whether the initial log entry should be printed.
Expand Down
52 changes: 52 additions & 0 deletions src/Discord.Net.Core/Logging/DefaultLogger.cs
Original file line number Diff line number Diff line change
@@ -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();
}
}
}
Loading