diff --git a/X.Serilog.Sinks.Telegram.sln b/X.Serilog.Sinks.Telegram.sln index 8396d0d..d4db7ff 100644 --- a/X.Serilog.Sinks.Telegram.sln +++ b/X.Serilog.Sinks.Telegram.sln @@ -8,6 +8,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{4A EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApp", "examples\WebApp\WebApp.csproj", "{15736D36-94A1-4E3F-8A5F-EFEFE27F1995}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApp", "examples\ConsoleApp\ConsoleApp.csproj", "{D007F793-195D-448A-A34E-74EF0245F9D3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -22,9 +24,14 @@ Global {15736D36-94A1-4E3F-8A5F-EFEFE27F1995}.Debug|Any CPU.Build.0 = Debug|Any CPU {15736D36-94A1-4E3F-8A5F-EFEFE27F1995}.Release|Any CPU.ActiveCfg = Release|Any CPU {15736D36-94A1-4E3F-8A5F-EFEFE27F1995}.Release|Any CPU.Build.0 = Release|Any CPU + {D007F793-195D-448A-A34E-74EF0245F9D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D007F793-195D-448A-A34E-74EF0245F9D3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D007F793-195D-448A-A34E-74EF0245F9D3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D007F793-195D-448A-A34E-74EF0245F9D3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {807F309C-4CE3-4E68-8D46-9044A0A7BD6A} = {652C6F31-3E8C-400E-9716-ACF591E48E6F} {15736D36-94A1-4E3F-8A5F-EFEFE27F1995} = {4A2DA76C-10DE-476D-A26E-16FAB3CBFC8C} + {D007F793-195D-448A-A34E-74EF0245F9D3} = {4A2DA76C-10DE-476D-A26E-16FAB3CBFC8C} EndGlobalSection EndGlobal diff --git a/examples/ConsoleApp/ConsoleApp.csproj b/examples/ConsoleApp/ConsoleApp.csproj new file mode 100644 index 0000000..b6ebeec --- /dev/null +++ b/examples/ConsoleApp/ConsoleApp.csproj @@ -0,0 +1,19 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + + + + diff --git a/examples/ConsoleApp/Program.cs b/examples/ConsoleApp/Program.cs new file mode 100644 index 0000000..b72b8b7 --- /dev/null +++ b/examples/ConsoleApp/Program.cs @@ -0,0 +1,22 @@ +using Serilog; +using Serilog.Events; +using X.Serilog.Sinks.Telegram.Extensions; + +const string botToken = "TELEGRAM_BOT_TOKEN"; +const string loggingChatId = "CHANNEL_OR_CHAT_ID"; + +Log.Logger = new LoggerConfiguration() + .WriteTo.TelegramCore( + token: botToken, + chatId: loggingChatId, + logLevel: LogEventLevel.Verbose) + .WriteTo.Console() + .CreateLogger(); + + +while (true) +{ + var level = Random.Shared.NextInt64(0, 6); + Log.Logger.Write((LogEventLevel)level, "Message"); + await Task.Delay(500); +} \ No newline at end of file diff --git a/examples/WebApp/Program.cs b/examples/WebApp/Program.cs index 084e75d..e22ff1a 100644 --- a/examples/WebApp/Program.cs +++ b/examples/WebApp/Program.cs @@ -7,6 +7,7 @@ using X.Serilog.Sinks.Telegram; using X.Serilog.Sinks.Telegram.Batch.Rules; using X.Serilog.Sinks.Telegram.Configuration; +using X.Serilog.Sinks.Telegram.Extensions; using X.Serilog.Sinks.Telegram.Filters; var builder = WebApplication.CreateBuilder(args); diff --git a/examples/WebApp/WebApp.csproj b/examples/WebApp/WebApp.csproj index 8d4a0ad..62db7f4 100644 --- a/examples/WebApp/WebApp.csproj +++ b/examples/WebApp/WebApp.csproj @@ -7,8 +7,9 @@ - - + + + diff --git a/src/X.Serilog.Sinks.Telegram/Configuration/BatchEmittingRulesConfiguration.cs b/src/X.Serilog.Sinks.Telegram/Configuration/BatchEmittingRulesConfiguration.cs index db5515d..d33f4bf 100644 --- a/src/X.Serilog.Sinks.Telegram/Configuration/BatchEmittingRulesConfiguration.cs +++ b/src/X.Serilog.Sinks.Telegram/Configuration/BatchEmittingRulesConfiguration.cs @@ -40,15 +40,7 @@ public TimeSpan RuleCheckPeriod /// public IImmutableList BatchProcessingExecutionHooks => BatchProcessingRules - .Select(rule => - { - if (rule is IExecutionHook hook) - { - return hook; - } - - return null; - }) - .Where(hook => hook != null) - .ToImmutableList()!; + .Where(rule => rule is IExecutionHook) + .Cast() + .ToImmutableList(); } \ No newline at end of file diff --git a/src/X.Serilog.Sinks.Telegram/Configuration/TelegramSinkDefaults.cs b/src/X.Serilog.Sinks.Telegram/Configuration/TelegramSinkDefaults.cs index c6d987d..9bef296 100644 --- a/src/X.Serilog.Sinks.Telegram/Configuration/TelegramSinkDefaults.cs +++ b/src/X.Serilog.Sinks.Telegram/Configuration/TelegramSinkDefaults.cs @@ -7,6 +7,32 @@ namespace X.Serilog.Sinks.Telegram.Configuration; /// public static class TelegramSinkDefaults { + /// + /// Gets the logging mode for the application. + /// + /// + /// The logging mode determines how log messages are processed and formatted before being sent. + /// In this case, it is set to return , which indicates that log messages + /// will be published individually to the specified Telegram channel. + /// + /// + /// This property is read-only and returns the default logging mode of the system. + /// It is crucial for configuring the overall logging strategy of the application. + /// For example, when set to , each log message is sent as it occurs. + /// Other modes, like , could aggregate messages over a period + /// or until a certain condition is met before sending. + /// + public static LoggingMode DefaultFormatterMode => LoggingMode.AggregatedNotifications; + + public static FormatterConfiguration DefaultFormatterConfiguration => new() + { + UseEmoji = true, + ReadableApplicationName = "X.Serilog.Telegram.Sink", + IncludeException = false, + IncludeProperties = false, + TimeZone = TimeZoneInfo.Utc + }; + /// /// The limit on the number of log events to be included in a single batch. /// diff --git a/src/X.Serilog.Sinks.Telegram/Extensions/DependencyInjectionExtensions.cs b/src/X.Serilog.Sinks.Telegram/Extensions/DependencyInjectionExtensions.cs new file mode 100644 index 0000000..f0f6c35 --- /dev/null +++ b/src/X.Serilog.Sinks.Telegram/Extensions/DependencyInjectionExtensions.cs @@ -0,0 +1,58 @@ +using System.Collections.Immutable; +using X.Serilog.Sinks.Telegram.Batch.Rules; +using X.Serilog.Sinks.Telegram.Configuration; +using X.Serilog.Sinks.Telegram.Filters; + +namespace X.Serilog.Sinks.Telegram.Extensions; + +public static class DependencyInjectionExtensions +{ + public static LoggerConfiguration TelegramCore( + this LoggerSinkConfiguration sinkConfig, + string token, + string chatId, + LogEventLevel logLevel + ) + { + ArgumentNullException.ThrowIfNull(sinkConfig); + ArgumentNullException.ThrowIfNull(token); + ArgumentNullException.ThrowIfNull(chatId); + + return TelegramCoreInternal(sinkConfig, token, chatId, logLevel); + } + + private static LoggerConfiguration TelegramCoreInternal( + LoggerSinkConfiguration sinkConfig, + string token, + string chatId, + LogEventLevel logLevel) + { + return sinkConfig.Telegram(config => + { + config.Token = token; + config.ChatId = chatId; + + config.Mode = TelegramSinkDefaults.DefaultFormatterMode; + + config.BatchPostingLimit = TelegramSinkDefaults.BatchPostingLimit; + config.BatchEmittingRulesConfiguration = new BatchEmittingRulesConfiguration + { + RuleCheckPeriod = TelegramSinkDefaults.RulesCheckPeriod, + BatchProcessingRules = new List + { + new BatchSizeRule(config.LogsAccessor, batchSize: config.BatchPostingLimit), + // send logs to the Telegram once per 250 seconds + new OncePerTimeRule(TelegramSinkDefaults.RulesCheckPeriod * 50) + }.ToImmutableList() + }; + config.FormatterConfiguration = TelegramSinkDefaults.DefaultFormatterConfiguration; + config.LogFiltersConfiguration = new LogsFiltersConfiguration() + { + ApplyLogFilters = false, + Filters = new ImmutableArray() + }; + }, + messageFormatter: null!, + restrictedToMinimumLevel: logLevel); + } +} \ No newline at end of file diff --git a/src/X.Serilog.Sinks.Telegram/Configuration/LoggerConfigurationTelegramExtensions.cs b/src/X.Serilog.Sinks.Telegram/Extensions/LoggerConfigurationTelegramExtensions.cs similarity index 93% rename from src/X.Serilog.Sinks.Telegram/Configuration/LoggerConfigurationTelegramExtensions.cs rename to src/X.Serilog.Sinks.Telegram/Extensions/LoggerConfigurationTelegramExtensions.cs index 06e88b2..db0fdbb 100644 --- a/src/X.Serilog.Sinks.Telegram/Configuration/LoggerConfigurationTelegramExtensions.cs +++ b/src/X.Serilog.Sinks.Telegram/Extensions/LoggerConfigurationTelegramExtensions.cs @@ -1,8 +1,9 @@ using System.Threading.Channels; using X.Serilog.Sinks.Telegram.Batch; +using X.Serilog.Sinks.Telegram.Configuration; using X.Serilog.Sinks.Telegram.Formatters; -namespace X.Serilog.Sinks.Telegram.Configuration; +namespace X.Serilog.Sinks.Telegram.Extensions; public static class LoggerConfigurationTelegramExtensions { diff --git a/src/X.Serilog.Sinks.Telegram/Formatters/DefaultAggregatedNotificationsFormatter.cs b/src/X.Serilog.Sinks.Telegram/Formatters/DefaultAggregatedNotificationsFormatter.cs index 308ce6e..24c01b7 100644 --- a/src/X.Serilog.Sinks.Telegram/Formatters/DefaultAggregatedNotificationsFormatter.cs +++ b/src/X.Serilog.Sinks.Telegram/Formatters/DefaultAggregatedNotificationsFormatter.cs @@ -7,7 +7,7 @@ public class DefaultAggregatedNotificationsFormatter : MessageFormatterBase public override List Format( ICollection logEntries, FormatterConfiguration config, - Func, FormatterConfiguration, List> formatter = null) + Func, FormatterConfiguration, List>? formatter = null) { formatter ??= DefaultFormatter; return base.Format(logEntries, config, formatter); diff --git a/src/X.Serilog.Sinks.Telegram/Formatters/DefaultLogFormatter.cs b/src/X.Serilog.Sinks.Telegram/Formatters/DefaultLogFormatter.cs index 0b155bd..d513967 100644 --- a/src/X.Serilog.Sinks.Telegram/Formatters/DefaultLogFormatter.cs +++ b/src/X.Serilog.Sinks.Telegram/Formatters/DefaultLogFormatter.cs @@ -9,7 +9,7 @@ internal class DefaultLogFormatter : MessageFormatterBase /// Throws when, after using the formatter, the message is null, empty, or whitespace. public override List Format(ICollection logEntries, FormatterConfiguration config, - Func, FormatterConfiguration, List> formatter = null) + Func, FormatterConfiguration, List>? formatter = null) { if (!NotEmpty(logEntries)) { diff --git a/src/X.Serilog.Sinks.Telegram/Formatters/IMessageFormatter.cs b/src/X.Serilog.Sinks.Telegram/Formatters/IMessageFormatter.cs index de80b8c..f15311e 100644 --- a/src/X.Serilog.Sinks.Telegram/Formatters/IMessageFormatter.cs +++ b/src/X.Serilog.Sinks.Telegram/Formatters/IMessageFormatter.cs @@ -13,5 +13,5 @@ public interface IMessageFormatter /// Human-readable message. List Format(ICollection logEntries, FormatterConfiguration config, - Func, FormatterConfiguration, List> formatter = null); + Func, FormatterConfiguration, List>? formatter = null); } \ No newline at end of file diff --git a/src/X.Serilog.Sinks.Telegram/Formatters/MessageFormatterBase.cs b/src/X.Serilog.Sinks.Telegram/Formatters/MessageFormatterBase.cs index e863569..a69df61 100644 --- a/src/X.Serilog.Sinks.Telegram/Formatters/MessageFormatterBase.cs +++ b/src/X.Serilog.Sinks.Telegram/Formatters/MessageFormatterBase.cs @@ -10,7 +10,7 @@ public abstract class MessageFormatterBase : IMessageFormatter /// public virtual List Format(ICollection logEntries, FormatterConfiguration config, - Func, FormatterConfiguration, List> formatter = null) + Func, FormatterConfiguration, List>? formatter = null) { if (!NotEmpty(logEntries)) { diff --git a/src/X.Serilog.Sinks.Telegram/TelegramSink.cs b/src/X.Serilog.Sinks.Telegram/TelegramSink.cs index eeb2175..371d4a6 100644 --- a/src/X.Serilog.Sinks.Telegram/TelegramSink.cs +++ b/src/X.Serilog.Sinks.Telegram/TelegramSink.cs @@ -112,7 +112,8 @@ private async Task EmitBatchInternalAsync(int batchSize, CancellationToken cance private async Task> GetMessagesFromQueueAsync(int amount) { var logsBatch = await _logsQueueAccessor.DequeueSeveralAsync(amount); - var events = logsBatch.Where(log => log is not null) + var events = logsBatch + .Where(log => log is not null) .Select(LogEntry.MapFrom) .ToList(); diff --git a/src/X.Serilog.Sinks.Telegram/X.Serilog.Sinks.Telegram.csproj b/src/X.Serilog.Sinks.Telegram/X.Serilog.Sinks.Telegram.csproj index d1fb817..159a61b 100644 --- a/src/X.Serilog.Sinks.Telegram/X.Serilog.Sinks.Telegram.csproj +++ b/src/X.Serilog.Sinks.Telegram/X.Serilog.Sinks.Telegram.csproj @@ -15,7 +15,7 @@ - +