Add logger

This commit is contained in:
Dmitri Shimanski
2025-03-20 23:34:34 +02:00
parent aa4b86d12f
commit 343ab4de7f
3 changed files with 198 additions and 118 deletions

View File

@@ -1,4 +1,6 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Telegram.Net.Models; using Telegram.Net.Models;
using Telegram.Net.Services; using Telegram.Net.Services;
@@ -8,7 +10,8 @@ public static class ServiceBindings
{ {
public static IServiceCollection ConnectTelegram(this IServiceCollection isc, TelegramBotConfig config) public static IServiceCollection ConnectTelegram(this IServiceCollection isc, TelegramBotConfig config)
{ {
isc.AddHostedService<TelegramHostedService>(k => new(config, isc)); var logger = isc.BuildServiceProvider().GetRequiredService<ILogger<TelegramHostedService>>();
isc.AddHostedService<TelegramHostedService>(k => new(config, isc, logger));
return isc; return isc;
} }
} }

View File

@@ -1,7 +1,9 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices.JavaScript;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Telegram.Bot; using Telegram.Bot;
using Telegram.Bot.Types; using Telegram.Bot.Types;
using Telegram.Bot.Types.Payments; using Telegram.Bot.Types.Payments;
@@ -22,14 +24,25 @@ public class TelegramHostedService : IHostedService
internal Dictionary<string, Func<ITelegramBotClient, InlineQuery ,CancellationToken, Task>> InlineHandler { get; } = new(); internal Dictionary<string, Func<ITelegramBotClient, InlineQuery ,CancellationToken, Task>> InlineHandler { get; } = new();
internal Func<ITelegramBotClient, PreCheckoutQuery,CancellationToken, Task>? PreCheckoutHandler { get; set; } internal Func<ITelegramBotClient, PreCheckoutQuery,CancellationToken, Task>? PreCheckoutHandler { get; set; }
internal List<Func<ITelegramBotClient, Update, CancellationToken, Task>> DefaultUpdateHandler { get; } = new(); internal List<Func<ITelegramBotClient, Update, CancellationToken, Task>> DefaultUpdateHandler { get; } = new();
internal static ILogger<TelegramHostedService> _logger;
public TelegramHostedService(ITelegramBotConfig config, IServiceCollection isc) public TelegramHostedService(ITelegramBotConfig config, IServiceCollection isc, ILogger<TelegramHostedService> logger)
{ {
try
{
_logger = logger;
Client = new TelegramBotClient(config.Token); Client = new TelegramBotClient(config.Token);
Config = config; Config = config;
this.isc = isc; this.isc = isc;
} }
catch (Exception ex)
{
_logger.Log(LogLevel.Critical, new EventId(), ex, "Catched exception when creating TelegramHostedService: ");
}
}
internal static bool IsValidHandlerMethod(MethodInfo method, Type parameterType) internal static bool IsValidHandlerMethod(MethodInfo method, Type parameterType)
{
try
{ {
var parameters = method.GetParameters(); var parameters = method.GetParameters();
return method.ReturnType == typeof(Task) && return method.ReturnType == typeof(Task) &&
@@ -38,16 +51,34 @@ public class TelegramHostedService : IHostedService
parameters[1].ParameterType == parameterType && parameters[1].ParameterType == parameterType &&
parameters[2].ParameterType == typeof(CancellationToken); parameters[2].ParameterType == typeof(CancellationToken);
} }
catch (Exception ex)
{
_logger.LogError(ex, "Catched exception in parsing and checking params.");
return false;
}
}
internal static Func<ITelegramBotClient, T, CancellationToken, Task> CreateDelegate<T>(MethodInfo method) internal static Func<ITelegramBotClient, T, CancellationToken, Task> CreateDelegate<T>(MethodInfo method)
{
try
{ {
var delegateType = typeof(Func<ITelegramBotClient, T, CancellationToken, Task>); var delegateType = typeof(Func<ITelegramBotClient, T, CancellationToken, Task>);
return (Delegate.CreateDelegate(delegateType, null, method) as Func<ITelegramBotClient, T, CancellationToken, Task>)!; return (Delegate.CreateDelegate(delegateType, null, method) as
Func<ITelegramBotClient, T, CancellationToken, Task>)!;
}
catch (Exception ex)
{
_logger.Log(LogLevel.Critical, new EventId(), ex, "Catched exception in CreateDelegate function: ");
return null;
}
} }
internal async Task AddAttributes(CancellationToken cancellationToken) internal async Task AddAttributes(CancellationToken cancellationToken)
{ {
await Task.Run(async () => await Task.Run(async () =>
{
try
{ {
var implementations = AppDomain.CurrentDomain.GetAssemblies() var implementations = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetTypes()) .SelectMany(a => a.GetTypes())
@@ -55,7 +86,7 @@ public class TelegramHostedService : IHostedService
foreach (var implementation in implementations) foreach (var implementation in implementations)
{ {
isc.AddSingleton(implementation); isc.AddScoped(implementation);
} }
var methods = implementations var methods = implementations
@@ -71,17 +102,24 @@ public class TelegramHostedService : IHostedService
m.GetCustomAttribute<PreCheckoutAttribute>() != null || m.GetCustomAttribute<PreCheckoutAttribute>() != null ||
m.GetCustomAttribute<UpdateAttribute>() != null); m.GetCustomAttribute<UpdateAttribute>() != null);
if (methods.Count() == 0)
{
_logger.LogWarning("Not founded methods with attributes.");
}
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
foreach (var method in methods) foreach (var method in methods)
{ {
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
var commandAttr = method.GetCustomAttribute<CommandAttribute>(); var commandAttr = method.GetCustomAttribute<CommandAttribute>();
if (commandAttr != null) if (commandAttr != null)
{ {
if (IsValidHandlerMethod(method, typeof(Message))) if (IsValidHandlerMethod(method, typeof(Message)))
{ {
var handler = CreateDelegate<Message>(method); var handler = CreateDelegate<Message>(method);
CommandHandler.TryAdd(commandAttr.Command, handler); if (!CommandHandler.TryAdd(commandAttr.Command, handler))
throw new Exception($"Failed to add in commandHandler: {commandAttr.Command}");
} }
continue; continue;
} }
@@ -91,8 +129,10 @@ public class TelegramHostedService : IHostedService
if (IsValidHandlerMethod(method, typeof(CallbackQuery))) if (IsValidHandlerMethod(method, typeof(CallbackQuery)))
{ {
var handler = CreateDelegate<CallbackQuery>(method); var handler = CreateDelegate<CallbackQuery>(method);
CallbackQueryHandler.TryAdd(callbackAttr.QueryId, handler); if (!CallbackQueryHandler.TryAdd(callbackAttr.QueryId, handler))
throw new Exception($"Failed to add in callbacKQuery: {callbackAttr.QueryId}");;
} }
continue; continue;
} }
@@ -104,6 +144,7 @@ public class TelegramHostedService : IHostedService
var handler = CreateDelegate<Message>(method); var handler = CreateDelegate<Message>(method);
EditedMessageHandler.Add(handler); EditedMessageHandler.Add(handler);
} }
continue; continue;
} }
@@ -113,8 +154,10 @@ public class TelegramHostedService : IHostedService
if (IsValidHandlerMethod(method, typeof(InlineQuery))) if (IsValidHandlerMethod(method, typeof(InlineQuery)))
{ {
var handler = CreateDelegate<InlineQuery>(method); var handler = CreateDelegate<InlineQuery>(method);
InlineHandler.TryAdd(inlineAttr.InlineId, handler); if (!InlineHandler.TryAdd(inlineAttr.InlineId, handler))
throw new Exception($"Failed to add in inlineHandler: {inlineAttr.InlineId}");;
} }
continue; continue;
} }
@@ -126,6 +169,7 @@ public class TelegramHostedService : IHostedService
var handler = CreateDelegate<PreCheckoutQuery>(method); var handler = CreateDelegate<PreCheckoutQuery>(method);
PreCheckoutHandler = handler; PreCheckoutHandler = handler;
} }
continue; continue;
} }
@@ -137,28 +181,39 @@ public class TelegramHostedService : IHostedService
var handler = CreateDelegate<Update>(method); var handler = CreateDelegate<Update>(method);
DefaultUpdateHandler.Add(handler); DefaultUpdateHandler.Add(handler);
} }
continue; continue;
} }
} }
}
catch (Exception ex)
{
_logger.Log(LogLevel.Critical, new EventId(), ex, "Catched new exception when added methods: ");
}
}, cancellationToken); }, cancellationToken);
} }
internal async Task UpdateHandler(ITelegramBotClient client, Update update, CancellationToken ctx) internal async Task UpdateHandler(ITelegramBotClient client, Update update, CancellationToken ctx)
{
try
{ {
switch (update) switch (update)
{ {
case { Message: { } message }: case { Message: { } message }:
await CommandHandler.FirstOrDefault(k => message.Text!.StartsWith(k.Key)).Value(client, message, ctx); await CommandHandler.FirstOrDefault(k => message.Text!.StartsWith(k.Key))
.Value(client, message, ctx);
break; break;
case { EditedMessage: { } message }: case { EditedMessage: { } message }:
EditedMessageHandler.ForEach(async k => await k(client, message, ctx)); EditedMessageHandler.ForEach(async k => await k(client, message, ctx));
break; break;
case { CallbackQuery: { } callbackQuery }: case { CallbackQuery: { } callbackQuery }:
await CallbackQueryHandler.FirstOrDefault(k => callbackQuery.Data!.StartsWith(k.Key)).Value(client, callbackQuery, ctx); await CallbackQueryHandler.FirstOrDefault(k => callbackQuery.Data!.StartsWith(k.Key))
.Value(client, callbackQuery, ctx);
break; break;
case { InlineQuery: { } inlineQuery }: case { InlineQuery: { } inlineQuery }:
await InlineHandler.FirstOrDefault(k => inlineQuery.Id.StartsWith(k.Key)).Value(client, inlineQuery, ctx); await InlineHandler.FirstOrDefault(k => inlineQuery.Id.StartsWith(k.Key))
.Value(client, inlineQuery, ctx);
break; break;
case { PreCheckoutQuery: { } preCheckoutQuery }: case { PreCheckoutQuery: { } preCheckoutQuery }:
if (PreCheckoutHandler != null) await PreCheckoutHandler(client, preCheckoutQuery, ctx); if (PreCheckoutHandler != null) await PreCheckoutHandler(client, preCheckoutQuery, ctx);
@@ -168,9 +223,16 @@ public class TelegramHostedService : IHostedService
break; break;
} }
} }
catch (Exception ex)
{
_logger.Log(LogLevel.Error, new EventId(), ex, "Catched exception in UpdateHandler: ");
}
}
[SuppressMessage("ReSharper", "AsyncVoidLambda")] [SuppressMessage("ReSharper", "AsyncVoidLambda")]
public async Task StartAsync(CancellationToken cancellationToken) public async Task StartAsync(CancellationToken cancellationToken)
{
try
{ {
await AddAttributes(cancellationToken); await AddAttributes(cancellationToken);
@@ -182,9 +244,21 @@ public class TelegramHostedService : IHostedService
Config.ReceiverOptions, Config.ReceiverOptions,
cancellationToken); cancellationToken);
} }
catch (Exception ex)
{
_logger.Log(LogLevel.Critical, new EventId(), ex, "Failed to start. Catched exception: ");
}
}
public async Task StopAsync(CancellationToken cancellationToken) public async Task StopAsync(CancellationToken cancellationToken)
{
try
{ {
await Client.DropPendingUpdates(cancellationToken); await Client.DropPendingUpdates(cancellationToken);
} }
catch (Exception ex)
{
_logger.LogCritical(ex, "Failed to stop. Exception: ");
}
}
} }

View File

@@ -10,6 +10,7 @@ using Telegram.Net.Services;
using System.Reflection; using System.Reflection;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Moq; using Moq;
using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.ReplyMarkups; using Telegram.Bot.Types.ReplyMarkups;
@@ -22,6 +23,7 @@ namespace Telegram.Tests
{ {
private TelegramBotConfig _configMock; private TelegramBotConfig _configMock;
private ServiceCollection _services; private ServiceCollection _services;
private ILogger<TelegramHostedService> logger;
[SetUp] [SetUp]
public void Setup() public void Setup()
@@ -31,7 +33,8 @@ namespace Telegram.Tests
.AddJsonFile("appsettings.json", false) .AddJsonFile("appsettings.json", false)
.AddEnvironmentVariables() .AddEnvironmentVariables()
.Build(); .Build();
var loggerFactory = new LoggerFactory();
logger = loggerFactory.CreateLogger<TelegramHostedService>();
_configMock = new TelegramBotConfig(conf.GetValue<string>("telegram_test_token") ?? throw new Exception("Provide telegram token first")); _configMock = new TelegramBotConfig(conf.GetValue<string>("telegram_test_token") ?? throw new Exception("Provide telegram token first"));
_services = new ServiceCollection(); _services = new ServiceCollection();
} }
@@ -67,7 +70,7 @@ namespace Telegram.Tests
public void AddAttributes_RegistersCommandHandlersCorrectly() public void AddAttributes_RegistersCommandHandlersCorrectly()
{ {
_services.AddSingleton<TestHandler>(); _services.AddSingleton<TestHandler>();
var service = new TelegramHostedService(_configMock, _services); var service = new TelegramHostedService(_configMock, _services, logger);
service.AddAttributes(CancellationToken.None).Wait(); service.AddAttributes(CancellationToken.None).Wait();
@@ -132,8 +135,8 @@ namespace Telegram.Tests
var services = new ServiceCollection(); var services = new ServiceCollection();
services.AddSingleton<TelegramHostedServiceTests.TestHandler>(); services.AddSingleton<TelegramHostedServiceTests.TestHandler>();
_botClientMock = new Mock<ITelegramBotClient>(); _botClientMock = new Mock<ITelegramBotClient>();
var logger = new LoggerFactory().CreateLogger<TelegramHostedService>();
_hostedService = new TelegramHostedService(_configMock.Object, services); _hostedService = new TelegramHostedService(_configMock.Object, services, logger);
_hostedService.AddAttributes(CancellationToken.None).Wait(); _hostedService.AddAttributes(CancellationToken.None).Wait();
} }