From a87a07d939072af36c67a7d103844f8a96ad5e06 Mon Sep 17 00:00:00 2001 From: Rikitav Date: Sun, 3 Aug 2025 03:29:15 +0400 Subject: [PATCH] * Added Logging abstraction for base library * Version incremented --- Telegrator.Hosting.Web/TelegramBotWebHost.cs | 2 + .../Telegrator.Hosting.Web.csproj | 2 +- .../Logging/MicrosoftLoggingAdapter.cs | 45 ++++++ Telegrator.Hosting/TelegramBotHost.cs | 2 + Telegrator.Hosting/Telegrator.Hosting.csproj | 2 +- Telegrator.Hosting/TypesExtensions.cs | 15 ++ Telegrator/Alligator.cs | 122 --------------- Telegrator/Logging/Alligator.cs | 145 ++++++++++++++++++ Telegrator/Logging/ConsoleLogger.cs | 70 +++++++++ Telegrator/Logging/ITelegratorLogger.cs | 50 ++++++ Telegrator/Logging/NullLogger.cs | 21 +++ Telegrator/Telegrator.csproj | 2 +- Telegrator/TelegratorClient.cs | 24 ++- 13 files changed, 374 insertions(+), 128 deletions(-) create mode 100644 Telegrator.Hosting/Logging/MicrosoftLoggingAdapter.cs delete mode 100644 Telegrator/Alligator.cs create mode 100644 Telegrator/Logging/Alligator.cs create mode 100644 Telegrator/Logging/ConsoleLogger.cs create mode 100644 Telegrator/Logging/ITelegratorLogger.cs create mode 100644 Telegrator/Logging/NullLogger.cs diff --git a/Telegrator.Hosting.Web/TelegramBotWebHost.cs b/Telegrator.Hosting.Web/TelegramBotWebHost.cs index 44eb3e8..b70a229 100644 --- a/Telegrator.Hosting.Web/TelegramBotWebHost.cs +++ b/Telegrator.Hosting.Web/TelegramBotWebHost.cs @@ -12,6 +12,8 @@ using Telegrator.Configuration; using Telegrator.Hosting.Components; using Telegrator.Hosting.Providers; using Telegrator.Hosting.Web.Components; +using Telegrator.Hosting.Logging; +using Telegrator.Logging; using Telegrator.MadiatorCore; using Telegrator.MadiatorCore.Descriptors; diff --git a/Telegrator.Hosting.Web/Telegrator.Hosting.Web.csproj b/Telegrator.Hosting.Web/Telegrator.Hosting.Web.csproj index 9ce821d..aa94d56 100644 --- a/Telegrator.Hosting.Web/Telegrator.Hosting.Web.csproj +++ b/Telegrator.Hosting.Web/Telegrator.Hosting.Web.csproj @@ -15,7 +15,7 @@ True LICENSE README.md - 1.0.9 + 1.0.10 diff --git a/Telegrator.Hosting/Logging/MicrosoftLoggingAdapter.cs b/Telegrator.Hosting/Logging/MicrosoftLoggingAdapter.cs new file mode 100644 index 0000000..c4b4bc1 --- /dev/null +++ b/Telegrator.Hosting/Logging/MicrosoftLoggingAdapter.cs @@ -0,0 +1,45 @@ +using Telegrator.Logging; + +namespace Telegrator.Hosting.Logging +{ + /// + /// Adapter for Microsoft.Extensions.Logging to work with Telegrator logging system. + /// This allows seamless integration with ASP.NET Core logging infrastructure. + /// + public class MicrosoftLoggingAdapter : ITelegratorLogger + { + private readonly Microsoft.Extensions.Logging.ILogger _logger; + + /// + /// Initializes a new instance of MicrosoftLoggingAdapter. + /// + /// The Microsoft.Extensions.Logging logger instance. + public MicrosoftLoggingAdapter(Microsoft.Extensions.Logging.ILogger logger) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + /// + public void Log(LogLevel level, string message, Exception? exception = null) + { + var msLogLevel = level switch + { + LogLevel.Trace => Microsoft.Extensions.Logging.LogLevel.Trace, + LogLevel.Debug => Microsoft.Extensions.Logging.LogLevel.Debug, + LogLevel.Information => Microsoft.Extensions.Logging.LogLevel.Information, + LogLevel.Warning => Microsoft.Extensions.Logging.LogLevel.Warning, + LogLevel.Error => Microsoft.Extensions.Logging.LogLevel.Error, + _ => Microsoft.Extensions.Logging.LogLevel.Information + }; + + if (exception != null) + { + _logger.Log(msLogLevel, default, exception, message); + } + else + { + _logger.Log(msLogLevel, message); + } + } + } +} \ No newline at end of file diff --git a/Telegrator.Hosting/TelegramBotHost.cs b/Telegrator.Hosting/TelegramBotHost.cs index 96744c2..cf84b1e 100644 --- a/Telegrator.Hosting/TelegramBotHost.cs +++ b/Telegrator.Hosting/TelegramBotHost.cs @@ -5,6 +5,8 @@ using System.Text; using Telegram.Bot.Types.Enums; using Telegrator.Configuration; using Telegrator.Hosting.Components; +using Telegrator.Hosting.Logging; +using Telegrator.Logging; using Telegrator.Hosting.Providers; using Telegrator.MadiatorCore; using Telegrator.MadiatorCore.Descriptors; diff --git a/Telegrator.Hosting/Telegrator.Hosting.csproj b/Telegrator.Hosting/Telegrator.Hosting.csproj index 189d22a..ea5cac7 100644 --- a/Telegrator.Hosting/Telegrator.Hosting.csproj +++ b/Telegrator.Hosting/Telegrator.Hosting.csproj @@ -16,7 +16,7 @@ True LICENSE README.md - 1.0.9 + 1.0.10 diff --git a/Telegrator.Hosting/TypesExtensions.cs b/Telegrator.Hosting/TypesExtensions.cs index f1a4331..d188ac8 100644 --- a/Telegrator.Hosting/TypesExtensions.cs +++ b/Telegrator.Hosting/TypesExtensions.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System.Diagnostics.CodeAnalysis; @@ -9,8 +10,10 @@ using Telegram.Bot.Types; using Telegrator.Configuration; using Telegrator.Hosting.Components; using Telegrator.Hosting.Configuration; +using Telegrator.Hosting.Logging; using Telegrator.Hosting.Polling; using Telegrator.Hosting.Providers; +using Telegrator.Logging; using Telegrator.MadiatorCore; namespace Telegrator.Hosting @@ -92,6 +95,18 @@ namespace Telegrator.Hosting client.SetMyCommands(aliases).Wait(); return botHost; } + + /// + /// Adds a Microsoft.Extensions.Logging adapter to Alligator using a logger factory. + /// + /// + public static void AddLoggingAdapter(this ITelegramBotHost host) + { + ILoggerFactory loggerFactory = host.Services.GetRequiredService(); + ILogger logger = loggerFactory.CreateLogger("Telegrator"); + MicrosoftLoggingAdapter adapter = new MicrosoftLoggingAdapter(logger); + Alligator.AddAdapter(adapter); + } } /// diff --git a/Telegrator/Alligator.cs b/Telegrator/Alligator.cs deleted file mode 100644 index b3407bc..0000000 --- a/Telegrator/Alligator.cs +++ /dev/null @@ -1,122 +0,0 @@ -using System.Diagnostics; - -namespace Telegrator -{ - /// - /// Telegrator's FUNNY debug logger helper - /// - public static class Alligator - { - /// - /// Gets or sets flags of what trace messages to write - /// - public static DebugLevel Allowed { get; set; } = DebugLevel.None; - - /// - /// Writes trace message if Indent level has Router flag - /// - /// - public static void RouterWriteLine(string message) - { - if (Allowed.HasFlag(DebugLevel.Router)) - Trace.WriteLine(message); - } - - /// - /// Writes debug message if Indent level has Router flag - /// - /// - /// - public static void RouterWriteLine(string message, params object[] args) - { - if (Allowed.HasFlag(DebugLevel.Router)) - Trace.WriteLine(string.Format(message, args)); - } - - /// - /// Writes trace message if Indent level has Providers flag - /// - /// - public static void ProviderWriteLine(string message) - { - if (Allowed.HasFlag(DebugLevel.Providers)) - Trace.WriteLine(message); - } - - /// - /// Writes trace message if Indent level has Providers flag - /// - /// - /// - public static void ProviderWriteLine(string message, params object[] args) - { - if (Allowed.HasFlag(DebugLevel.Providers)) - Trace.WriteLine(string.Format(message, args)); - } - - /// - /// Writes trace message if Indent level has Filters flag - /// - /// - public static void FilterWriteLine(string message) - { - if (Allowed.HasFlag(DebugLevel.Filters)) - Trace.WriteLine(message); - } - - /// - /// Writes trace message if Indent level has Filters flag - /// - /// - /// - public static void FilterWriteLine(string message, params object[] args) - { - if (Allowed.HasFlag(DebugLevel.Filters)) - Trace.WriteLine(string.Format(message, args)); - } - - /// - /// Writes trace message if Indent level has Pool flag - /// - /// - public static void PoolWriteLine(string message) - { - if (Allowed.HasFlag(DebugLevel.HandlersPool)) - Trace.WriteLine(message); - } - - /// - /// Writes trace message if Indent level has Pool flag - /// - /// - /// - public static void PoolWriteLine(string message, params object[] args) - { - if (Allowed.HasFlag(DebugLevel.HandlersPool)) - Trace.WriteLine(string.Format(message, args)); - } - - /// - /// Writes trace message if flag was set - /// - /// - /// - public static void WriteLine(DebugLevel level, string message) - { - if (Allowed.HasFlag(level)) - Trace.WriteLine(message); - } - - /// - /// Writes trace message if flag was set - /// - /// - /// - /// - public static void WriteLine(DebugLevel level, string message, params object[] args) - { - if (Allowed.HasFlag(level)) - Trace.WriteLine(string.Format(message, args)); - } - } -} diff --git a/Telegrator/Logging/Alligator.cs b/Telegrator/Logging/Alligator.cs new file mode 100644 index 0000000..5ada902 --- /dev/null +++ b/Telegrator/Logging/Alligator.cs @@ -0,0 +1,145 @@ +namespace Telegrator.Logging +{ + /// + /// Centralized logging system for Telegrator. + /// Provides static access to logging functionality with adapter support. + /// + public static class Alligator + { + private static readonly List _adapters = new(); + private static readonly object _lock = new(); + + /// + /// Gets the current adapters count. + /// + public static int AdaptersCount => _adapters.Count; + + /// + /// Adds a logger adapter to the centralized logging system. + /// + /// The logger adapter to add. + public static void AddAdapter(ITelegratorLogger adapter) + { + if (adapter == null) + throw new ArgumentNullException(nameof(adapter)); + + lock (_lock) + { + if (!_adapters.Contains(adapter)) + { + _adapters.Add(adapter); + } + } + } + + /// + /// Removes a logger adapter from the centralized logging system. + /// + /// The logger adapter to remove. + public static void RemoveAdapter(ITelegratorLogger adapter) + { + if (adapter == null) + return; + + lock (_lock) + { + _adapters.Remove(adapter); + } + } + + /// + /// Clears all logger adapters. + /// + public static void ClearAdapters() + { + lock (_lock) + { + _adapters.Clear(); + } + } + + /// + /// Logs a message to all registered adapters. + /// + /// The log level. + /// The message to log. + /// Optional exception. + public static void Log(LogLevel level, string message, Exception? exception = null) + { + // Fast path: if no adapters, do nothing + if (_adapters.Count == 0) + return; + + // Lock only during enumeration to prevent collection modification during iteration + lock (_lock) + { + foreach (var adapter in _adapters) + { + try + { + adapter.Log(level, message, exception); + } + catch + { + _ = 0xBAD + 0xC0DE; // Ignore adapter errors to prevent logging failures + } + } + } + } + + /// + /// Logs a trace message to all registered adapters. + /// + /// The message to log. + public static void LogTrace(string message) + { + Log(LogLevel.Trace, message); + } + + /// + /// Logs a debug message to all registered adapters. + /// + /// The message to log. + public static void LogDebug(string message) + { + Log(LogLevel.Debug, message); + } + + /// + /// Logs an information message to all registered adapters. + /// + /// The message to log. + public static void LogInformation(string message) + { + Log(LogLevel.Information, message); + } + + /// + /// Logs a warning message to all registered adapters. + /// + /// The message to log. + public static void LogWarning(string message) + { + Log(LogLevel.Warning, message); + } + + /// + /// Logs an error message to all registered adapters. + /// + /// The message to log. + /// Optional exception. + public static void LogError(string message, Exception? exception = null) + { + Log(LogLevel.Error, message, exception); + } + + /// + /// Logs an error message with exception only to all registered adapters. + /// + /// The exception to log. + public static void LogError(Exception exception) + { + Log(LogLevel.Error, exception.Message, exception); + } + } +} \ No newline at end of file diff --git a/Telegrator/Logging/ConsoleLogger.cs b/Telegrator/Logging/ConsoleLogger.cs new file mode 100644 index 0000000..dbd9aee --- /dev/null +++ b/Telegrator/Logging/ConsoleLogger.cs @@ -0,0 +1,70 @@ +using System; + +namespace Telegrator.Logging +{ + /// + /// Console logger implementation that writes to System.Console. + /// This logger is optional and can be used for simple console output. + /// + public class ConsoleLogger : ITelegratorLogger + { + private readonly LogLevel _minimumLevel; + private readonly bool _includeTimestamp; + + /// + /// Initializes a new instance of ConsoleLogger. + /// + /// Minimum log level to output. Default is Information. + /// Whether to include timestamp in log messages. Default is true. + public ConsoleLogger(LogLevel minimumLevel = LogLevel.Information, bool includeTimestamp = true) + { + _minimumLevel = minimumLevel; + _includeTimestamp = includeTimestamp; + } + + /// + public void Log(LogLevel level, string message, Exception? exception = null) + { + if (level < _minimumLevel) + return; + + var timestamp = _includeTimestamp ? $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] " : ""; + var levelStr = $"[{level.ToString().ToUpper()}] "; + var logMessage = $"{timestamp}{levelStr}{message}"; + + // Add exception if present + if (exception != null) + { + logMessage += $" | Exception: {exception.Message}"; + } + + // Write to console with appropriate color + var originalColor = Console.ForegroundColor; + try + { + Console.ForegroundColor = level switch + { + LogLevel.Trace => ConsoleColor.Gray, + LogLevel.Debug => ConsoleColor.Cyan, + LogLevel.Information => ConsoleColor.White, + LogLevel.Warning => ConsoleColor.Yellow, + LogLevel.Error => ConsoleColor.Red, + _ => ConsoleColor.White + }; + + Console.WriteLine(logMessage); + + // Write exception details if present + if (exception != null) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine($"Exception Details: {exception}"); + } + } + finally + { + Console.ForegroundColor = originalColor; + } + } + } +} \ No newline at end of file diff --git a/Telegrator/Logging/ITelegratorLogger.cs b/Telegrator/Logging/ITelegratorLogger.cs new file mode 100644 index 0000000..f1b9b6d --- /dev/null +++ b/Telegrator/Logging/ITelegratorLogger.cs @@ -0,0 +1,50 @@ +using System; + +namespace Telegrator.Logging +{ + /// + /// Interface for Telegrator logging system. + /// Provides abstraction for logging without external dependencies. + /// + public interface ITelegratorLogger + { + /// + /// Logs a message with specified level. + /// + /// The log level. + /// The message to log. + /// Optional exception. + void Log(LogLevel level, string message, Exception? exception = null); + } + + /// + /// Log levels for Telegrator logging system. + /// + public enum LogLevel + { + /// + /// Trace level - most detailed logging. + /// + Trace = 0, + + /// + /// Debug level - detailed debugging information. + /// + Debug = 1, + + /// + /// Information level - general information. + /// + Information = 2, + + /// + /// Warning level - warning messages. + /// + Warning = 3, + + /// + /// Error level - error messages. + /// + Error = 4 + } +} \ No newline at end of file diff --git a/Telegrator/Logging/NullLogger.cs b/Telegrator/Logging/NullLogger.cs new file mode 100644 index 0000000..28c67e4 --- /dev/null +++ b/Telegrator/Logging/NullLogger.cs @@ -0,0 +1,21 @@ +using System; + +namespace Telegrator.Logging +{ + /// + /// Null logger implementation that does nothing. + /// Used when logging is not required or disabled. + /// + public class NullLogger : ITelegratorLogger + { + /// + /// Singleton instance of NullLogger. + /// + public static readonly NullLogger Instance = new(); + + private NullLogger() { } + + /// + public void Log(LogLevel level, string message, Exception? exception = null) { } + } +} \ No newline at end of file diff --git a/Telegrator/Telegrator.csproj b/Telegrator/Telegrator.csproj index 64128fa..9b632ee 100644 --- a/Telegrator/Telegrator.csproj +++ b/Telegrator/Telegrator.csproj @@ -17,7 +17,7 @@ True True LICENSE - 1.0.9 + 1.0.10 diff --git a/Telegrator/TelegratorClient.cs b/Telegrator/TelegratorClient.cs index 499d1b3..44bb4ce 100644 --- a/Telegrator/TelegratorClient.cs +++ b/Telegrator/TelegratorClient.cs @@ -1,6 +1,7 @@ using Telegram.Bot; using Telegram.Bot.Polling; using Telegrator.Configuration; +using Telegrator.Logging; using Telegrator.MadiatorCore; using Telegrator.Polling; using Telegrator.Providers; @@ -37,7 +38,7 @@ namespace Telegrator /// Optional HTTP client for making requests. /// The cancellation token. public TelegratorClient(string token, HttpClient? httpClient = null, CancellationToken cancellationToken = default) - : this(new TelegramBotClientOptions(token), httpClient, cancellationToken) { } + : this(new TelegramBotClientOptions(token), null, httpClient, cancellationToken) { } /// /// Initializes a new instance of the class with bot options. @@ -45,9 +46,19 @@ namespace Telegrator /// The Telegram bot client options. /// Optional HTTP client for making requests. /// The cancellation token. - public TelegratorClient(TelegramBotClientOptions options, HttpClient? httpClient = null, CancellationToken cancellationToken = default) : base(options, httpClient, cancellationToken) + public TelegratorClient(TelegramBotClientOptions options, HttpClient? httpClient = null, CancellationToken cancellationToken = default) + : this(options, null, httpClient, cancellationToken) { } + + /// + /// Initializes a new instance of the class with bot options and Telegrator options. + /// + /// The Telegram bot client options. + /// The Telegrator options. + /// Optional HTTP client for making requests. + /// The cancellation token. + public TelegratorClient(TelegramBotClientOptions options, TelegratorOptions? telegratorOptions, HttpClient? httpClient = null, CancellationToken cancellationToken = default) : base(options, httpClient, cancellationToken) { - Options = new TelegratorOptions(); + Options = telegratorOptions ?? new TelegratorOptions(); Handlers = new HandlersCollection(default); BotInfo = new TelegramBotInfo(this.GetMe(cancellationToken).Result); } @@ -67,6 +78,10 @@ namespace Telegrator AwaitingProvider awaitingProvider = new AwaitingProvider(Options); updateRouter = new UpdateRouter(handlerProvider, awaitingProvider, Options, BotInfo); + + // Log startup + Alligator.LogInformation($"Telegrator bot starting up - BotId: {BotInfo.Id}, Username: {BotInfo.Username}, MaxParallelHandlers: {Options.MaximumParallelWorkingHandlers ?? -1}"); + StartReceivingInternal(receiverOptions, cancellationToken); } @@ -96,7 +111,10 @@ namespace Telegrator catch (OperationCanceledException) { // Cancelled + Alligator.LogInformation("Telegrator bot stopped (cancelled)"); } } + + } }