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 logger #21

Merged
merged 10 commits into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions Commands/InteractionServiceHandler.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Discord;
using Discord;
using Discord.Interactions;
using Discord.WebSocket;
using Serilog;

namespace QuickEdit.Commands;
public class InteractionServiceHandler
Expand All @@ -23,9 +24,9 @@ public static async Task InitAsync()
// So the event has to be used to handle the result
_interactionService.SlashCommandExecuted += OnSlashCommandExecutedAsync;
}
catch
catch (Exception e)
{
await Program.LogAsync("InteractionServiceHandler", "Error initializing InteractionService", LogSeverity.Critical);
Log.Fatal($"Error initializing InteractionService: {e.Message}");
throw;
}
}
Expand All @@ -38,7 +39,7 @@ public static async Task RegisterModulesAsync()
// The service might not have been initialized yet
if (_interactionService == null)
{
await Program.LogAsync("InteractionServiceManager.RegisterModulesAsync()", "InteractionService not initialized yet", LogSeverity.Error);
Log.Error("Failed to register modules: InteractionService not initialized.");
throw new InvalidOperationException("InteractionService not initialized while trying to register commands");
}

Expand All @@ -52,11 +53,11 @@ public static async Task RegisterModulesAsync()

await _interactionService.RegisterCommandsGloballyAsync();
_client!.InteractionCreated += OnInteractionCreatedAsync;
await Program.LogAsync("InteractionServiceManager", "Modules registered successfully", LogSeverity.Info);
Log.Information("Modules registered successfully");
}
catch (Exception e)
{
await Program.LogAsync("InteractionServiceManager", $"Error registering modules. ({e})", LogSeverity.Critical);
Log.Fatal($"Error registering modules: {(Program.config != null && Program.config.debug ? e : e.Message)}");
throw;
}
}
Expand All @@ -66,7 +67,7 @@ public static async Task OnInteractionCreatedAsync(SocketInteraction interaction
// The service might not have been initialized yet
if (_interactionService == null)
{
await Program.LogAsync("InteractionServiceManager.OnInteractionCreatedAsync()", "InteractionService not initialized yet", LogSeverity.Error);
Log.Error("Error handling interaction: InteractionService not initialized.");
return;
}

Expand All @@ -79,7 +80,7 @@ public static async Task OnInteractionCreatedAsync(SocketInteraction interaction
}
catch (Exception e)
{
await Program.LogAsync("InteractionServiceManager", $"Error handling interaction. {e.Message}", LogSeverity.Error);
Log.Error($"Error handling interaction: {e.Message}");

if (interaction.Type is InteractionType.ApplicationCommand)
{
Expand All @@ -90,19 +91,20 @@ public static async Task OnInteractionCreatedAsync(SocketInteraction interaction
}
}

public static async Task OnSlashCommandExecutedAsync(SlashCommandInfo commandInfo, IInteractionContext interactionContext, IResult result) {
public static async Task OnSlashCommandExecutedAsync(SlashCommandInfo commandInfo, IInteractionContext interactionContext, IResult result)
{
// Only trying to handle errors lol
if (result.IsSuccess)
return;

try
{
await Program.LogAsync("InteractionServiceManager", $"Error handling interaction: {result.Error}", LogSeverity.Error);
Log.Error($"Error handling interaction: {result.Error}");
await interactionContext.Interaction.FollowupAsync("An error occurred while executing the command.", ephemeral: true);
}
catch (Exception e)
{
await Program.LogAsync("InteractionServiceManager", $"Error handling interaction exception bruh: {e.ToString()}", LogSeverity.Error);
Log.Error($"Error handling interaction exception bruh: {e.ToString()}");
throw;
}
}
Expand Down
16 changes: 11 additions & 5 deletions Commands/Modules/VideoUtils.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Discord;
using Discord.Interactions;
using FFMpegCore;
using Serilog;
using System.Text.RegularExpressions;

namespace QuickEdit.Commands.Modules;
Expand Down Expand Up @@ -40,20 +41,25 @@ public async Task TrimVideoAsync(
// Get TimeSpans
TimeSpan trimStart = TimeSpan.Zero;
TimeSpan trimEnd = TimeSpan.Zero;
try {
try
{
// Avoid invalid format exceptions
if (!string.IsNullOrEmpty(trimStartString)) trimStart = TimeSpanFromHMS(trimStartString);
if (!string.IsNullOrEmpty(trimEndString)) trimEnd = TimeSpanFromHMS(trimEndString);
} catch (Exception e) {
}
catch (Exception e)
{
if (e is ArgumentException)
{
await FollowupAsync("Invalid time format. Please provide a valid time format (XXh XXm XXs XXms).", ephemeral: true);
} else {
}
else
{
throw;
}
return;
}

// Make sure the times are not negative | https://stackoverflow.com/a/1018659/17003609 (comment)
trimStart = trimStart.Duration();
trimEnd = trimEnd.Duration();
Expand All @@ -69,7 +75,7 @@ public async Task TrimVideoAsync(
if (!Directory.Exists("./tmp"))
{
Directory.CreateDirectory("./tmp");
await Program.LogAsync("VideoUtils", "TMP directory not found. Created it automatically.", LogSeverity.Info);
Log.Information("TMP directory not found. Created it automatically");
}

await DownloadVideoAsync(video.Url, videoInputPath);
Expand Down
9 changes: 5 additions & 4 deletions ConfigManager.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Discord;
using Discord;
using Newtonsoft.Json;
using Serilog;

namespace QuickEdit;
public class Config
Expand All @@ -14,17 +15,17 @@ public class Config
string path = "./config.json";
if (!File.Exists(path))
{
Program.LogAsync("Config", $"Config file not found at: {path}", LogSeverity.Critical);
Log.Fatal($"Config file not found at: {Path.GetFullPath(path)}");
return null;
}

try
{
return JsonConvert.DeserializeObject<Config>(File.ReadAllText(path))!;
}
catch
catch (Exception e)
{
Program.LogAsync("Config", "Failed to parse config file.", LogSeverity.Critical);
Log.Fatal($"Failed to parse config file: {e.Message}");
return null;
}
}
Expand Down
21 changes: 12 additions & 9 deletions Discord QuickEdit.csproj
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>QuickEdit</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AssemblyName>quickedit</AssemblyName>
<AssemblyVersion>0.0.0.0</AssemblyVersion>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>QuickEdit</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AssemblyName>quickedit</AssemblyName>
<AssemblyVersion>0.0.0.0</AssemblyVersion>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Discord.Net" Version="3.15.2" />
<PackageReference Include="FFMpegCore" Version="5.1.0" />
<PackageReference Include="Discord.Net" Version="3.15.2" />
<PackageReference Include="FFMpegCore" Version="5.1.0" />
<PackageReference Include="Serilog" Version="4.0.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
</ItemGroup>

<Target Name="Date" BeforeTargets="CoreCompile">
Expand Down
27 changes: 27 additions & 0 deletions Logger/AutoLog.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Discord;
using Serilog;
using Serilog.Events;

namespace QuickEdit.Logger;

public class AutoLog
{
// LogMessage does not run asynchronously, so we can ignore the DeepSource error
// skipcq: CS-R1073
public static Task LogMessage(LogMessage message)
{
var logLevel = message.Severity switch
{
LogSeverity.Critical => LogEventLevel.Fatal,
LogSeverity.Error => LogEventLevel.Error,
LogSeverity.Warning => LogEventLevel.Warning,
LogSeverity.Info => LogEventLevel.Information,
LogSeverity.Verbose => LogEventLevel.Verbose,
LogSeverity.Debug => LogEventLevel.Debug,
_ => LogEventLevel.Information
};

Log.Write(logLevel, message.Exception, "{Source}: {Message}", message.Source, message.Message);
return Task.CompletedTask;
}
}
27 changes: 27 additions & 0 deletions Logger/SerilogConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Serilog;

namespace QuickEdit.Logger;

public class SerilogConfiguration
{

public static void ConfigureLogger()
{
var logDirectory = "logs";

Directory.CreateDirectory(logDirectory);

var logPath = Path.Combine(logDirectory, "quickedit-.log");

var loggerConfig = new LoggerConfiguration()
.WriteTo.Console()
.WriteTo.File(logPath, rollingInterval: RollingInterval.Day);

if (Program.config != null && Program.config.debug)
loggerConfig.MinimumLevel.Debug();
else
loggerConfig.MinimumLevel.Information();

Log.Logger = loggerConfig.CreateLogger();
}
}
66 changes: 11 additions & 55 deletions Program.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
using Discord;
using Discord;
using Discord.WebSocket;
using FFMpegCore;
using FFMpegCore.Helpers;
using QuickEdit.Commands;
using System.Reflection;
using QuickEdit.Logger;
using Serilog;

namespace QuickEdit;
class Program

internal class Program
{
public static DiscordSocketClient? client;
public static Config? config = Config.GetConfig();
Expand All @@ -16,6 +18,10 @@ class Program

public async Task MainAsync()
{
SerilogConfiguration.ConfigureLogger();

AppDomain.CurrentDomain.ProcessExit += (s, e) => Log.CloseAndFlush();

ShowStartMessage();

// If the config is null, we can't continue as the bot won't have a token to login with
Expand All @@ -24,7 +30,7 @@ public async Task MainAsync()

client = new DiscordSocketClient(socketConfig);

client.Log += LogAsync;
client.Log += AutoLog.LogMessage;
client.Ready += OnReadyAsync;

await client.LoginAsync(TokenType.Bot, config.token);
Expand Down Expand Up @@ -60,59 +66,9 @@ private async Task OnReadyAsync()
}
catch
{
await LogAsync("Program", "Exiting", LogSeverity.Info);
Log.Fatal("Program is exiting due to an error in InteractionServiceHandler.");
// The program cannot continue without the InteractionService, so terminate it. Nothing important should be running at this point.
Environment.Exit(1); // skipcq: CS-W1005
}
}

public Task LogAsync(LogMessage message)
{
string msg = $"[{DateTime.UtcNow.ToString("HH.mm.ss")}] {message.Source}: {message.Message}";
Console.WriteLine(msg + " " + message.Exception);
return Task.CompletedTask;
}

public static Task LogAsync(string source, string message, LogSeverity severity = LogSeverity.Info)
{
string msg = $"[{DateTime.UtcNow.ToString("HH.mm.ss")}] {source}: {message}";

// Change color / display based on severity
// TODO: Maybe use ANSI escape codes instead?
switch (severity)
{
case LogSeverity.Warning:
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(msg);
Console.ResetColor();
break;
case LogSeverity.Critical:
Console.ForegroundColor = ConsoleColor.DarkRed;
Console.WriteLine(msg);
Console.ResetColor();
break;

case LogSeverity.Error:
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(msg);
Console.ResetColor();
break;

case LogSeverity.Verbose:
// Verbose logs are only displayed if the debug flag is set to true in the config
if (config == null || !config.debug) break;

Console.ForegroundColor = ConsoleColor.DarkGray;
Console.WriteLine(msg);
Console.ResetColor();
break;

default:
// All other severities should have the default color of the console
Console.ResetColor();
Console.WriteLine(msg);
break;
}
return Task.CompletedTask;
}
}