From ff7b704357ae2ef30c9c33564ea3023aed31382d Mon Sep 17 00:00:00 2001 From: Thomas Aunvik Date: Sun, 17 May 2020 10:00:21 +0200 Subject: [PATCH] Moving to another place --- AnimeListBot/Handler/Logging/BotLogger.cs | 197 ++++++++++++++++++ .../Handler/Logging/BotLoggerConfiguration.cs | 17 ++ .../Handler/Logging/BotLoggerExtensions.cs | 32 +++ .../Handler/Logging/BotLoggerProvider.cs | 29 +++ AnimeListBot/Handler/Logging/Logger.cs | 2 + AnimeListBot/Modules/Administrator.cs | 7 +- AnimeListBot/Program.cs | 62 ++++-- 7 files changed, 329 insertions(+), 17 deletions(-) create mode 100644 AnimeListBot/Handler/Logging/BotLogger.cs create mode 100644 AnimeListBot/Handler/Logging/BotLoggerConfiguration.cs create mode 100644 AnimeListBot/Handler/Logging/BotLoggerExtensions.cs create mode 100644 AnimeListBot/Handler/Logging/BotLoggerProvider.cs diff --git a/AnimeListBot/Handler/Logging/BotLogger.cs b/AnimeListBot/Handler/Logging/BotLogger.cs new file mode 100644 index 0000000..967811f --- /dev/null +++ b/AnimeListBot/Handler/Logging/BotLogger.cs @@ -0,0 +1,197 @@ +/* + * This file is part of AnimeList Bot + * + * AnimeList Bot is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AnimeList Bot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AnimeList Bot. If not, see + */ +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; +using System.Threading.Tasks; +using Discord; +using Discord.Commands; +using Discord.WebSocket; +using System.Globalization; +using Microsoft.Extensions.Logging; +using AnimeListBot.Handler.Logging; + +namespace AnimeListBot.Handler +{ + public class BotLogger : ILogger + { + const int MAX_FIELD_VALUE_LENGTH = 1024; + + private readonly string _name; + private readonly BotLoggerConfiguration _config; + + public BotLogger(string name, BotLoggerConfiguration config) + { + _name = name; + _config = config; + } + + public void LogDiscordError(string errorMessage, IUser user = null, IGuildChannel guildChannel = null) + { + EmbedHandler embed = new EmbedHandler(user, "Error", string.Empty, true); + if (guildChannel != null) + { + embed.AddFieldSecure("Channel Info", + "Server Id: " + guildChannel.GuildId + + "\nChannel Id: <#" + guildChannel.Id + ">" + + "\nUser Id: <@" + user.Id + ">" + ); + } + embed.AddFieldSecure("ErrorMessage", errorMessage); + } + + public void LogDiscordError(LogMessage message) + { + EmbedHandler embed = new EmbedHandler(null, "Exception", string.Empty, true); + embed.AddFieldSecure("Severity", Enum.GetName(typeof(LogSeverity), message.Severity)); + embed.AddFieldSecure("Source", message.Source); + embed.AddFieldSecure("Exception Message", message.Exception.Message); + if (message.Exception.StackTrace != null) + { + embed.AddFieldSecure("Type", message.Exception.GetType().FullName); + embed.AddFieldSecure("Stacktrace", TrancuateStacktrace(message.Exception.StackTrace)); + } + } + + public void LogDiscordError(Exception exception, IUser user = null, IGuildChannel guildChannel = null) + { + EmbedHandler embed = new EmbedHandler(user, "Exception", string.Empty, true); + if (guildChannel != null) + { + embed.AddFieldSecure("Channel Info", + "Server Id: " + guildChannel.GuildId + + "\nChannel Id: <#" + guildChannel.Id + ">" + + "\nUser Id: <@" + user.Id + ">" + ); + } + embed.AddFieldSecure("Exception Message", exception.Message); + embed.AddFieldSecure("Type", exception.GetType().FullName); + embed.AddFieldSecure("Stacktrace", TrancuateStacktrace(exception.StackTrace)); + } + + public void LogDiscordError(CommandInfo info, ICommandContext context, IResult result) + { + if (result is ExecuteResult) + { + Exception e = ((ExecuteResult)result).Exception; + + EmbedHandler embed = new EmbedHandler(context.User, "Command Exception", string.Empty, true); + embed.AddFieldSecure("Channel Info", + "Server Id: " + ((IGuildChannel)context.Channel).GuildId + + "\nChannel Id: <#" + context.Channel.Id + ">" + + "\nUser Id: <@" + context.User.Id + ">" + ); + embed.AddFieldSecure("Command Used", context.Message.Content); + embed.AddFieldSecure("Exception Message", e.Message); + embed.AddFieldSecure("Type", e.GetType().FullName); + embed.AddFieldSecure("Stacktrace", TrancuateStacktrace(e.StackTrace)); + } + } + + public string TrancuateStacktrace(string input) + { + if (input == null) return ""; + + input = Format.Sanitize(input); + if (input.Length > MAX_FIELD_VALUE_LENGTH) + { + input = input.Substring(0, MAX_FIELD_VALUE_LENGTH); + } + return input; + } + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + if (!IsEnabled(logLevel)) + { + return; + } + EmbedHandler embed = new EmbedHandler(null); + embed.Title = state.ToString(); + formatter.Invoke(state, exception); + + + LogToFile(state.ToString()); + + if (_config.SendToOwner) + { + if (Program._client == null) return; + + const ulong ownerId = 96580514021912576; + ulong channelId = Program.TestingMode ? Config.cached.test_error_channel : Config.cached.error_channel; + SocketUser owner = Program._client.GetUser(ownerId); + + SocketTextChannel channel = (SocketTextChannel)Program._client.GetChannel(channelId); + + if (owner != null) + { + owner?.GetOrCreateDMChannelAsync(); + owner.SendMessageAsync("", false, embed.Build()); + } + + if (channel != null) + { + channel.SendMessageAsync("", false, embed.Build()); + } + } + } + + public void TryCreateLogFile() + { + if (!Directory.Exists(_config.FileDirectory)) + { + Directory.CreateDirectory(_config.FileDirectory); + } + if (Directory.Exists(_config.FileDirectory)) + { + using (FileStream logStream = File.Create(_config.FileDirectory + "/" + _config.FileName)) + { + logStream.Close(); + } + } + } + + public void LogToFile(string message) + { + try + { + DateTime localTime = DateTime.Now; + string dateTimeMessage = "[" + localTime.ToString("T", DateTimeFormatInfo.InvariantInfo) + "] " + message; + string path = _config.FileDirectory + "/" + _config.FileName; + Console.WriteLine(dateTimeMessage); + + using StreamWriter writer = new StreamWriter(path, true); + writer.WriteLine(dateTimeMessage); + } + catch (IOException e) + { + Console.WriteLine(e); + } + } + + public bool IsEnabled(LogLevel logLevel) + { + return logLevel == _config.LogLevel; + } + + public IDisposable BeginScope(TState state) + { + return null; + } + } +} diff --git a/AnimeListBot/Handler/Logging/BotLoggerConfiguration.cs b/AnimeListBot/Handler/Logging/BotLoggerConfiguration.cs new file mode 100644 index 0000000..f2a3857 --- /dev/null +++ b/AnimeListBot/Handler/Logging/BotLoggerConfiguration.cs @@ -0,0 +1,17 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Text; + +namespace AnimeListBot.Handler.Logging +{ + public class BotLoggerConfiguration + { + public LogLevel LogLevel { get; set; } = LogLevel.Warning; + public int EventId { get; set; } = 0; + public bool SendToOwner { get; set; } = false; + + public string FileDirectory { get; set; } = "logs"; + public string FileName { get; set; } = "newest.log"; + } +} diff --git a/AnimeListBot/Handler/Logging/BotLoggerExtensions.cs b/AnimeListBot/Handler/Logging/BotLoggerExtensions.cs new file mode 100644 index 0000000..bcb33d7 --- /dev/null +++ b/AnimeListBot/Handler/Logging/BotLoggerExtensions.cs @@ -0,0 +1,32 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Text; + +namespace AnimeListBot.Handler.Logging +{ + public static class BotLoggerExtensions + { + public static ILoggerFactory AddBotLogger( + this ILoggerFactory loggerFactory, + BotLoggerConfiguration config) + { + loggerFactory.AddProvider(new BotLoggerProvider(config)); + return loggerFactory; + } + public static ILoggerFactory AddBotLogger( + this ILoggerFactory loggerFactory) + { + var config = new BotLoggerConfiguration(); + return loggerFactory.AddBotLogger(config); + } + public static ILoggerFactory AddBotLogger( + this ILoggerFactory loggerFactory, + Action configure) + { + var config = new BotLoggerConfiguration(); + configure(config); + return loggerFactory.AddBotLogger(config); + } + } +} diff --git a/AnimeListBot/Handler/Logging/BotLoggerProvider.cs b/AnimeListBot/Handler/Logging/BotLoggerProvider.cs new file mode 100644 index 0000000..b3290b2 --- /dev/null +++ b/AnimeListBot/Handler/Logging/BotLoggerProvider.cs @@ -0,0 +1,29 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Text; + +namespace AnimeListBot.Handler.Logging +{ + public class BotLoggerProvider : ILoggerProvider + { + private readonly BotLoggerConfiguration _config; + private readonly ConcurrentDictionary _loggers = new ConcurrentDictionary(); + + public BotLoggerProvider(BotLoggerConfiguration config) + { + _config = config; + } + + public ILogger CreateLogger(string categoryName) + { + return _loggers.GetOrAdd(categoryName, name => new BotLogger(name, _config)); + } + + public void Dispose() + { + _loggers.Clear(); + } + } +} diff --git a/AnimeListBot/Handler/Logging/Logger.cs b/AnimeListBot/Handler/Logging/Logger.cs index 4129377..69331f8 100644 --- a/AnimeListBot/Handler/Logging/Logger.cs +++ b/AnimeListBot/Handler/Logging/Logger.cs @@ -23,6 +23,8 @@ using Discord.Commands; using Discord.WebSocket; using System.Globalization; +using Microsoft.Extensions.Logging; +using AnimeListBot.Handler.Logging; namespace AnimeListBot.Handler { diff --git a/AnimeListBot/Modules/Administrator.cs b/AnimeListBot/Modules/Administrator.cs index b618649..a9b1205 100644 --- a/AnimeListBot/Modules/Administrator.cs +++ b/AnimeListBot/Modules/Administrator.cs @@ -30,6 +30,7 @@ using Microsoft.EntityFrameworkCore.Storage; using JikanDotNet; using AnimeListBot.Handler.Database; +using Microsoft.Extensions.Logging; namespace AnimeListBot.Modules { @@ -37,10 +38,12 @@ namespace AnimeListBot.Modules public class Administrator : ModuleBase { private IDatabaseService _db; + private ILogger _logger; - public Administrator(IDatabaseService db) + public Administrator(IDatabaseService db, ILogger logger) { _db = db; + _logger = logger; } [Command("stop")] @@ -223,6 +226,8 @@ public async Task TestCommand() DiscordServer server = await _db.GetServerById(Context.Guild.Id); embed.Description = server.Prefix; + _logger.LogError("Test"); + await _db.SaveChangesAsync(); await embed.SendMessage(Context.Channel); } diff --git a/AnimeListBot/Program.cs b/AnimeListBot/Program.cs index db74b3e..7941560 100644 --- a/AnimeListBot/Program.cs +++ b/AnimeListBot/Program.cs @@ -35,6 +35,8 @@ using Npgsql; using Microsoft.Extensions.Options; using AnimeListBot.Handler.API.SauceNAO; +using Microsoft.Extensions.Logging; +using AnimeListBot.Handler.Logging; namespace AnimeListBot { @@ -50,6 +52,7 @@ public class Program public static DiscordShardedClient _client; public static IServiceProvider _services; public static IDatabaseService db; + public static LoggerFactory _loggerfactory; public static Logger _logger; @@ -76,6 +79,7 @@ public class Program public async Task RunBotAsync(string[] args) { if (args.Contains("-testing")) TestingMode = true; + _loggerfactory = new LoggerFactory(); BOT_START_TIME = DateTime.Now; @@ -84,10 +88,9 @@ public async Task RunBotAsync(string[] args) Config bot_config = Config.GetConfig(); botOwners = bot_config.bot_owners; - //db = new DatabaseService(new DatabaseConnection(); - //Cluster cluster = db.GetCluster(bot_config.cluster_id); - - Cluster cluster = new Cluster() { Id = 0, ShardIdStart = 0, ShardIdEnd = 0 }; + var dbContext = new DbContextOptionsBuilder().UseNpgsql(DatabaseConnection.GetConnectionString()); + db = new DatabaseService(new DatabaseConnection(dbContext.Options)); + Cluster cluster = db.GetCluster(bot_config.cluster_id); if (File.Exists("current_commit.txt")) { @@ -103,9 +106,13 @@ public async Task RunBotAsync(string[] args) _sauceNao = new SauceNao(bot_config.saucenao_token); _dbl = new AuthDiscordBotListApi(botID, bot_config.dbl_token); + int total = 0; + if (Config.cached.override_shard_amount > 0) total = Config.cached.override_shard_amount; + else db.GetAllClusters().ForEach(x => total += x.GetShardCount()); + var shard_config = new DiscordSocketConfig { - TotalShards = GetTotalShards(), + TotalShards = total, }; int[] shardIds = cluster.GetShardIds(); @@ -117,7 +124,7 @@ await _logger.Log("ShardStart: " + cluster.ShardIdStart + "\nShardEnd: " + cluster.ShardIdEnd + "\nShards: " + shardIdstring); - using (var services = ConfigureServices(shard_config, cluster)) + using (var services = ConfigureServices(shard_config, cluster, _loggerfactory)) { _services = services; _client?.Dispose(); @@ -151,23 +158,46 @@ await _logger.Log("ShardStart: " + cluster.ShardIdStart return; } - public int GetTotalShards() + private ServiceProvider ConfigureServices(DiscordSocketConfig shard_config, Cluster cluster, ILoggerFactory loggerFactory) { - if (Config.cached.override_shard_amount > 0) return Config.cached.override_shard_amount; - - int total = 0; - - db.GetAllClusters().ForEach(x => total += x.GetShardCount()); - return total; - } + DateTime localTime = DateTime.Now; + string logPath = localTime.Year + "-" + localTime.Month + "-" + localTime.Day + "-" + localTime.Hour + "-" + localTime.Minute + "-" + localTime.Second + ".log"; + + _loggerfactory.AddBotLogger(new BotLoggerConfiguration(){ + LogLevel = LogLevel.Error, + SendToOwner = true, + FileName = logPath, + FileDirectory = "logs" + }); + _loggerfactory.AddBotLogger(new BotLoggerConfiguration(){ + LogLevel = LogLevel.Warning, + SendToOwner = true, + FileName = logPath, + FileDirectory = "logs" + }); + _loggerfactory.AddBotLogger(new BotLoggerConfiguration(){ + LogLevel = LogLevel.Debug, + SendToOwner = true, + FileName = logPath, + FileDirectory = "logs" + }); + _loggerfactory.AddBotLogger(new BotLoggerConfiguration(){ + LogLevel = LogLevel.Information, + SendToOwner = false, + FileName = logPath, + FileDirectory = "logs" + }); + + ILogger botLogger = _loggerfactory.CreateLogger(); - private ServiceProvider ConfigureServices(DiscordSocketConfig shard_config, Cluster cluster) - { return new ServiceCollection() .AddSingleton(new DiscordShardedClient(cluster.GetShardIds(), shard_config)) .AddSingleton() .AddSingleton() + .AddLogging() + .AddSingleton(botLogger) + .AddDbContext(options => options.UseNpgsql(DatabaseConnection.GetConnectionString()), ServiceLifetime.Scoped) .AddScoped() .BuildServiceProvider();