From f1d8f27db158b7f0e7e203cf0ce36813c414d165 Mon Sep 17 00:00:00 2001 From: daimon Date: Sat, 28 Feb 2026 16:12:18 +0100 Subject: [PATCH] Revert "Revert "feat: add Prometheus/Micrometer custom metrics instrumentation (#28)"" This reverts commit 519fac1ec7b67c54a72a54c3f6311dda08f8cec6. --- .../ldrbot/group/TelegramGroupRepository.java | 2 + .../image/ImageGameDurationExtractor.java | 17 ++++++- .../ldrbot/message/ExceptionHandler.java | 10 ++++- .../ldrbot/message/MessageController.java | 35 +++++++++++++-- .../metrics/BusinessMetricsConfiguration.java | 31 +++++++++++++ .../metrics/GameSessionMetricsListener.java | 33 ++++++++++++++ .../ldrbot/metrics/MetricsConstants.java | 37 +++++++++++++++ .../DailyRankingRecalculationService.java | 31 +++++++++++-- .../ldrbot/reminder/RemindersService.java | 45 +++++++++++++++---- .../ldrbot/image/ImageTestConfiguration.java | 10 ++++- 10 files changed, 230 insertions(+), 21 deletions(-) create mode 100644 src/main/java/dev/rubasace/linkedin/games/ldrbot/metrics/BusinessMetricsConfiguration.java create mode 100644 src/main/java/dev/rubasace/linkedin/games/ldrbot/metrics/GameSessionMetricsListener.java create mode 100644 src/main/java/dev/rubasace/linkedin/games/ldrbot/metrics/MetricsConstants.java diff --git a/src/main/java/dev/rubasace/linkedin/games/ldrbot/group/TelegramGroupRepository.java b/src/main/java/dev/rubasace/linkedin/games/ldrbot/group/TelegramGroupRepository.java index 9edefd0..92f9778 100644 --- a/src/main/java/dev/rubasace/linkedin/games/ldrbot/group/TelegramGroupRepository.java +++ b/src/main/java/dev/rubasace/linkedin/games/ldrbot/group/TelegramGroupRepository.java @@ -11,6 +11,8 @@ public interface TelegramGroupRepository extends CrudRepository { + long countByActiveTrue(); + @Query(""" SELECT g FROM TelegramGroup g WHERE g.active = true diff --git a/src/main/java/dev/rubasace/linkedin/games/ldrbot/image/ImageGameDurationExtractor.java b/src/main/java/dev/rubasace/linkedin/games/ldrbot/image/ImageGameDurationExtractor.java index 4a4f13b..c7e5c53 100644 --- a/src/main/java/dev/rubasace/linkedin/games/ldrbot/image/ImageGameDurationExtractor.java +++ b/src/main/java/dev/rubasace/linkedin/games/ldrbot/image/ImageGameDurationExtractor.java @@ -1,8 +1,11 @@ package dev.rubasace.linkedin.games.ldrbot.image; +import dev.rubasace.linkedin.games.ldrbot.metrics.MetricsConstants; import dev.rubasace.linkedin.games.ldrbot.session.GameDuration; import dev.rubasace.linkedin.games.ldrbot.session.GameType; import dev.rubasace.linkedin.games.ldrbot.user.UserInfo; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; import org.bytedeco.opencv.global.opencv_imgcodecs; import org.bytedeco.opencv.opencv_core.Mat; import org.slf4j.Logger; @@ -20,10 +23,18 @@ public class ImageGameDurationExtractor { private final ImageGameExtractor imageGameExtractor; private final ImageDurationExtractor imageDurationExtractor; + private final MeterRegistry meterRegistry; + private final Counter ocrAttemptsCounter; + private final Counter ocrErrorsCounter; - ImageGameDurationExtractor(final ImageGameExtractor imageGameExtractor, final ImageDurationExtractor imageDurationExtractor) { + ImageGameDurationExtractor(final ImageGameExtractor imageGameExtractor, + final ImageDurationExtractor imageDurationExtractor, + final MeterRegistry meterRegistry) { this.imageGameExtractor = imageGameExtractor; this.imageDurationExtractor = imageDurationExtractor; + this.meterRegistry = meterRegistry; + this.ocrAttemptsCounter = meterRegistry.counter(MetricsConstants.OCR_ATTEMPTS); + this.ocrErrorsCounter = meterRegistry.counter(MetricsConstants.OCR_ERRORS); } public Optional extractGameDuration(final File imageFile, final Long chatId, final UserInfo userInfo) throws GameDurationExtractionException { @@ -32,10 +43,12 @@ public Optional extractGameDuration(final File imageFile, final Lo if (gameType.isEmpty()) { return Optional.empty(); } + ocrAttemptsCounter.increment(); try { Duration duration = imageDurationExtractor.extractDuration(image, gameType.get().getColors()); return Optional.of(new GameDuration(gameType.get(), duration)); } catch (DurationOCRException e) { + ocrErrorsCounter.increment(); if (e.getCause() != null) { LOGGER.error(e.getMessage(), e); } else { @@ -45,4 +58,4 @@ public Optional extractGameDuration(final File imageFile, final Lo } } } -} \ No newline at end of file +} diff --git a/src/main/java/dev/rubasace/linkedin/games/ldrbot/message/ExceptionHandler.java b/src/main/java/dev/rubasace/linkedin/games/ldrbot/message/ExceptionHandler.java index 30809a4..675a7ec 100644 --- a/src/main/java/dev/rubasace/linkedin/games/ldrbot/message/ExceptionHandler.java +++ b/src/main/java/dev/rubasace/linkedin/games/ldrbot/message/ExceptionHandler.java @@ -4,10 +4,12 @@ import dev.rubasace.linkedin.games.ldrbot.chat.CustomTelegramClient; import dev.rubasace.linkedin.games.ldrbot.chat.UserFeedbackException; import dev.rubasace.linkedin.games.ldrbot.image.GameDurationExtractionException; +import dev.rubasace.linkedin.games.ldrbot.metrics.MetricsConstants; import dev.rubasace.linkedin.games.ldrbot.session.GameNameNotFoundException; import dev.rubasace.linkedin.games.ldrbot.session.SessionAlreadyRegisteredException; import dev.rubasace.linkedin.games.ldrbot.user.UserNotFoundException; import dev.rubasace.linkedin.games.ldrbot.util.FormatUtils; +import io.micrometer.core.instrument.MeterRegistry; import org.springframework.stereotype.Component; @Component @@ -25,12 +27,18 @@ class ExceptionHandler { public static final String GAME_NOT_FOUND_EXCEPTION_MESSAGE = "'%s' is not a valid game name."; private final CustomTelegramClient customTelegramClient; + private final MeterRegistry meterRegistry; - ExceptionHandler(final CustomTelegramClient customTelegramClient) { + ExceptionHandler(final CustomTelegramClient customTelegramClient, final MeterRegistry meterRegistry) { this.customTelegramClient = customTelegramClient; + this.meterRegistry = meterRegistry; } void notifyUserFeedbackException(final UserFeedbackException userFeedbackException) { + meterRegistry.counter(MetricsConstants.ERRORS, + MetricsConstants.TAG_ERROR_TYPE, userFeedbackException.getClass().getSimpleName()) + .increment(); + if (userFeedbackException instanceof UnknownCommandException unknownCommandException) { customTelegramClient.sendErrorMessage(UNKNOWN_COMMAND_MESSAGE_TEMPLATE.formatted(unknownCommandException.getCommand()), unknownCommandException.getChatId()); } else if (userFeedbackException instanceof SessionAlreadyRegisteredException sessionAlreadyRegisteredException) { diff --git a/src/main/java/dev/rubasace/linkedin/games/ldrbot/message/MessageController.java b/src/main/java/dev/rubasace/linkedin/games/ldrbot/message/MessageController.java index ab1a123..620a835 100644 --- a/src/main/java/dev/rubasace/linkedin/games/ldrbot/message/MessageController.java +++ b/src/main/java/dev/rubasace/linkedin/games/ldrbot/message/MessageController.java @@ -4,8 +4,12 @@ import dev.rubasace.linkedin.games.ldrbot.configuration.TelegramBotProperties; import dev.rubasace.linkedin.games.ldrbot.group.GroupNotFoundException; import dev.rubasace.linkedin.games.ldrbot.image.GameDurationExtractionException; +import dev.rubasace.linkedin.games.ldrbot.metrics.MetricsConstants; import dev.rubasace.linkedin.games.ldrbot.session.SessionAlreadyRegisteredException; import dev.rubasace.linkedin.games.ldrbot.util.BackpressureExecutors; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Timer; import jakarta.annotation.PostConstruct; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,6 +28,7 @@ import java.util.List; import java.util.concurrent.ExecutorService; +import java.util.concurrent.atomic.AtomicInteger; @Component public class MessageController extends AbilityBot implements SpringLongPollingBot { @@ -36,17 +41,29 @@ public class MessageController extends AbilityBot implements SpringLongPollingBo private final String token; private final ExecutorService controllerExecutor; + private final MeterRegistry meterRegistry; + private final Counter messagesProcessedCounter; + private final Counter unexpectedErrorCounter; + private final AtomicInteger inFlightGauge; + MessageController(final TelegramClient telegramClient, final MessageService messageService, final ExceptionHandler exceptionHandler, final List abilityExtensions, - final TelegramBotProperties telegramBotProperties) { + final TelegramBotProperties telegramBotProperties, + final MeterRegistry meterRegistry) { super(telegramClient, telegramBotProperties.getUsername(), MapDBContext.onlineInstance("/tmp/" + telegramBotProperties.getUsername()), new BareboneToggle()); this.messageService = messageService; this.exceptionHandler = exceptionHandler; this.token = telegramBotProperties.getToken(); this.controllerExecutor = BackpressureExecutors.newBackPressureVirtualThreadPerTaskExecutor("message-controller", MAX_CONSUME_CONCURRENCY); this.addExtensions(abilityExtensions); + + this.meterRegistry = meterRegistry; + this.messagesProcessedCounter = meterRegistry.counter(MetricsConstants.MESSAGES_PROCESSED); + this.unexpectedErrorCounter = meterRegistry.counter(MetricsConstants.ERRORS_UNEXPECTED); + this.inFlightGauge = new AtomicInteger(0); + meterRegistry.gauge(MetricsConstants.MESSAGES_INFLIGHT, inFlightGauge, AtomicInteger::get); } @Override @@ -56,14 +73,24 @@ public void consume(final List updates) { @Override public void consume(Update update) { + inFlightGauge.incrementAndGet(); + Timer.Sample sample = Timer.start(meterRegistry); try { doConsume(update); + messagesProcessedCounter.increment(); } catch (Exception e) { - if (e instanceof UserFeedbackException) { - exceptionHandler.notifyUserFeedbackException((UserFeedbackException) e); + if (e instanceof UserFeedbackException userFeedbackException) { + meterRegistry.counter(MetricsConstants.ERRORS, + MetricsConstants.TAG_ERROR_TYPE, e.getClass().getSimpleName()) + .increment(); + exceptionHandler.notifyUserFeedbackException(userFeedbackException); } else { + unexpectedErrorCounter.increment(); LOGGER.error("An unexpected error occurred", e); } + } finally { + sample.stop(meterRegistry.timer(MetricsConstants.MESSAGES_LATENCY)); + inFlightGauge.decrementAndGet(); } } @@ -106,4 +133,4 @@ public long creatorId() { return -1; } -} \ No newline at end of file +} diff --git a/src/main/java/dev/rubasace/linkedin/games/ldrbot/metrics/BusinessMetricsConfiguration.java b/src/main/java/dev/rubasace/linkedin/games/ldrbot/metrics/BusinessMetricsConfiguration.java new file mode 100644 index 0000000..943e2e4 --- /dev/null +++ b/src/main/java/dev/rubasace/linkedin/games/ldrbot/metrics/BusinessMetricsConfiguration.java @@ -0,0 +1,31 @@ +package dev.rubasace.linkedin.games.ldrbot.metrics; + +import dev.rubasace.linkedin.games.ldrbot.group.TelegramGroupRepository; +import dev.rubasace.linkedin.games.ldrbot.user.TelegramUserRepository; +import io.micrometer.core.instrument.MeterRegistry; +import org.springframework.context.annotation.Configuration; + +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; + +@Configuration +public class BusinessMetricsConfiguration { + + private final TelegramGroupRepository telegramGroupRepository; + private final TelegramUserRepository telegramUserRepository; + private final MeterRegistry meterRegistry; + + BusinessMetricsConfiguration(final TelegramGroupRepository telegramGroupRepository, + final TelegramUserRepository telegramUserRepository, + final MeterRegistry meterRegistry) { + this.telegramGroupRepository = telegramGroupRepository; + this.telegramUserRepository = telegramUserRepository; + this.meterRegistry = meterRegistry; + registerGauges(); + } + + private void registerGauges() { + meterRegistry.gauge(MetricsConstants.GROUPS_ACTIVE, this, cfg -> cfg.telegramGroupRepository.countByActiveTrue()); + meterRegistry.gauge(MetricsConstants.USERS_TOTAL, this, cfg -> cfg.telegramUserRepository.count()); + } +} diff --git a/src/main/java/dev/rubasace/linkedin/games/ldrbot/metrics/GameSessionMetricsListener.java b/src/main/java/dev/rubasace/linkedin/games/ldrbot/metrics/GameSessionMetricsListener.java new file mode 100644 index 0000000..d908862 --- /dev/null +++ b/src/main/java/dev/rubasace/linkedin/games/ldrbot/metrics/GameSessionMetricsListener.java @@ -0,0 +1,33 @@ +package dev.rubasace.linkedin.games.ldrbot.metrics; + +import dev.rubasace.linkedin.games.ldrbot.session.GameSessionDeletionEvent; +import dev.rubasace.linkedin.games.ldrbot.session.GameSessionRegistrationEvent; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +@Component +public class GameSessionMetricsListener { + + private final MeterRegistry meterRegistry; + private final Counter sessionsDeletedCounter; + + GameSessionMetricsListener(final MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + this.sessionsDeletedCounter = meterRegistry.counter(MetricsConstants.SESSIONS_DELETED); + } + + @EventListener + public void onSessionRegistered(GameSessionRegistrationEvent event) { + String gameType = event.getGameInfo() != null ? event.getGameInfo().name() : "unknown"; + meterRegistry.counter(MetricsConstants.SESSIONS_REGISTERED, + MetricsConstants.TAG_GAME_TYPE, gameType) + .increment(); + } + + @EventListener + public void onSessionDeleted(GameSessionDeletionEvent event) { + sessionsDeletedCounter.increment(); + } +} diff --git a/src/main/java/dev/rubasace/linkedin/games/ldrbot/metrics/MetricsConstants.java b/src/main/java/dev/rubasace/linkedin/games/ldrbot/metrics/MetricsConstants.java new file mode 100644 index 0000000..1e28f1a --- /dev/null +++ b/src/main/java/dev/rubasace/linkedin/games/ldrbot/metrics/MetricsConstants.java @@ -0,0 +1,37 @@ +package dev.rubasace.linkedin.games.ldrbot.metrics; + +public final class MetricsConstants { + + private MetricsConstants() { + } + + // Message processing + public static final String MESSAGES_PROCESSED = "ldrbot.messages.processed"; + public static final String MESSAGES_LATENCY = "ldrbot.messages.latency"; + public static final String MESSAGES_INFLIGHT = "ldrbot.messages.inflight"; + + // OCR + public static final String OCR_ATTEMPTS = "ldrbot.ocr.attempts"; + public static final String OCR_ERRORS = "ldrbot.ocr.errors"; + + // Errors + public static final String ERRORS = "ldrbot.errors"; + public static final String ERRORS_UNEXPECTED = "ldrbot.errors.unexpected"; + + // Background tasks + public static final String BACKGROUND_DURATION = "ldrbot.background.duration"; + public static final String BACKGROUND_ERRORS = "ldrbot.background.errors"; + + // Game sessions + public static final String SESSIONS_REGISTERED = "ldrbot.sessions.registered"; + public static final String SESSIONS_DELETED = "ldrbot.sessions.deleted"; + + // Business gauges + public static final String GROUPS_ACTIVE = "ldrbot.groups.active"; + public static final String USERS_TOTAL = "ldrbot.users.total"; + + // Tags + public static final String TAG_ERROR_TYPE = "error_type"; + public static final String TAG_TASK_NAME = "task_name"; + public static final String TAG_GAME_TYPE = "game_type"; +} diff --git a/src/main/java/dev/rubasace/linkedin/games/ldrbot/ranking/DailyRankingRecalculationService.java b/src/main/java/dev/rubasace/linkedin/games/ldrbot/ranking/DailyRankingRecalculationService.java index 03a1e79..78c3197 100644 --- a/src/main/java/dev/rubasace/linkedin/games/ldrbot/ranking/DailyRankingRecalculationService.java +++ b/src/main/java/dev/rubasace/linkedin/games/ldrbot/ranking/DailyRankingRecalculationService.java @@ -4,8 +4,12 @@ import dev.rubasace.linkedin.games.ldrbot.group.TelegramGroup; import dev.rubasace.linkedin.games.ldrbot.group.TelegramGroupAdapter; import dev.rubasace.linkedin.games.ldrbot.group.TelegramGroupService; +import dev.rubasace.linkedin.games.ldrbot.metrics.MetricsConstants; import dev.rubasace.linkedin.games.ldrbot.util.BackpressureExecutors; import dev.rubasace.linkedin.games.ldrbot.util.LinkedinTimeUtils; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Timer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; @@ -20,23 +24,41 @@ public class DailyRankingRecalculationService { private static final Logger LOGGER = LoggerFactory.getLogger(DailyRankingRecalculationService.class); public static final int MAX_CONCURRENCY = 20; + private static final String TASK_NAME = "ranking"; + private final TelegramGroupService telegramGroupService; private final GroupRankingService groupRankingService; private final ExecutorService executorService; private final TelegramGroupAdapter telegramGroupAdapter; + private final MeterRegistry meterRegistry; + private final Timer backgroundDurationTimer; + private final Counter backgroundErrorsCounter; - DailyRankingRecalculationService(final TelegramGroupService telegramGroupService, final GroupRankingService groupRankingService, final TelegramGroupAdapter telegramGroupAdapter) { + DailyRankingRecalculationService(final TelegramGroupService telegramGroupService, + final GroupRankingService groupRankingService, + final TelegramGroupAdapter telegramGroupAdapter, + final MeterRegistry meterRegistry) { this.telegramGroupService = telegramGroupService; this.groupRankingService = groupRankingService; this.telegramGroupAdapter = telegramGroupAdapter; this.executorService = BackpressureExecutors.newBackPressureVirtualThreadPerTaskExecutor("ranking", MAX_CONCURRENCY); + this.meterRegistry = meterRegistry; + this.backgroundDurationTimer = meterRegistry.timer(MetricsConstants.BACKGROUND_DURATION, + MetricsConstants.TAG_TASK_NAME, TASK_NAME); + this.backgroundErrorsCounter = meterRegistry.counter(MetricsConstants.BACKGROUND_ERRORS, + MetricsConstants.TAG_TASK_NAME, TASK_NAME); } public void calculateMissingRankings() { - LocalDate previousGameDay = LinkedinTimeUtils.todayGameDay().minusDays(1); - telegramGroupService.findGroupsWithMissingScores(previousGameDay) - .forEach(telegramGroup -> executorService.execute(() -> generateDailyRanking(telegramGroup, previousGameDay))); + Timer.Sample sample = Timer.start(meterRegistry); + try { + LocalDate previousGameDay = LinkedinTimeUtils.todayGameDay().minusDays(1); + telegramGroupService.findGroupsWithMissingScores(previousGameDay) + .forEach(telegramGroup -> executorService.execute(() -> generateDailyRanking(telegramGroup, previousGameDay))); + } finally { + sample.stop(backgroundDurationTimer); + } } private void generateDailyRanking(TelegramGroup telegramGroup, final LocalDate gameDay) { @@ -44,6 +66,7 @@ private void generateDailyRanking(TelegramGroup telegramGroup, final LocalDate g ChatInfo chatInfo = telegramGroupAdapter.adapt(telegramGroup); groupRankingService.createDailyRanking(chatInfo, gameDay); } catch (Exception e) { + backgroundErrorsCounter.increment(); LOGGER.error("Failed to generate daily ranking for group {}, error message: {}", telegramGroup.getChatId(), e.getMessage(), e); } } diff --git a/src/main/java/dev/rubasace/linkedin/games/ldrbot/reminder/RemindersService.java b/src/main/java/dev/rubasace/linkedin/games/ldrbot/reminder/RemindersService.java index 2dfbef9..d6429e5 100644 --- a/src/main/java/dev/rubasace/linkedin/games/ldrbot/reminder/RemindersService.java +++ b/src/main/java/dev/rubasace/linkedin/games/ldrbot/reminder/RemindersService.java @@ -2,12 +2,16 @@ import dev.rubasace.linkedin.games.ldrbot.chat.CustomTelegramClient; import dev.rubasace.linkedin.games.ldrbot.group.ChatInfo; +import dev.rubasace.linkedin.games.ldrbot.metrics.MetricsConstants; import dev.rubasace.linkedin.games.ldrbot.user.MissingSessionUserProjection; import dev.rubasace.linkedin.games.ldrbot.user.TelegramUserService; import dev.rubasace.linkedin.games.ldrbot.user.UserInfo; import dev.rubasace.linkedin.games.ldrbot.util.BackpressureExecutors; import dev.rubasace.linkedin.games.ldrbot.util.FormatUtils; import dev.rubasace.linkedin.games.ldrbot.util.LinkedinTimeUtils; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Timer; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -23,31 +27,49 @@ public class RemindersService { private static final String USER_MISSING_SESSIONS_REMINDER = """ ⏰ Don't forget! ⏰ - Hey %s! Looks like you're missing some of today’s results. - Don’t leave your group hanging — submit your screenshots and climb the leaderboard! 💪 + Hey %s! Looks like you're missing some of today's results. + Don't leave your group hanging — submit your screenshots and climb the leaderboard! 💪 """; private static final int MAX_CONCURRENCY = 50; public static final int REMINDERS_HOUR = 20; + private static final String TASK_NAME = "reminder"; private final TelegramUserService telegramUserService; private final CustomTelegramClient customTelegramClient; private final MissingSessionUserProjectionUserInfoAdapter missingSessionUserProjectionUserInfoAdapter; private final MissingSessionUserProjectionChatInfoAdapter missingSessionUserProjectionChatInfoAdapter; private final ExecutorService reminderExecutor; + private final MeterRegistry meterRegistry; + private final Timer backgroundDurationTimer; + private final Counter backgroundErrorsCounter; - RemindersService(final TelegramUserService telegramUserService, final CustomTelegramClient customTelegramClient, final MissingSessionUserProjectionUserInfoAdapter missingSessionUserProjectionUserInfoAdapter, final MissingSessionUserProjectionChatInfoAdapter missingSessionUserProjectionChatInfoAdapter) { + RemindersService(final TelegramUserService telegramUserService, + final CustomTelegramClient customTelegramClient, + final MissingSessionUserProjectionUserInfoAdapter missingSessionUserProjectionUserInfoAdapter, + final MissingSessionUserProjectionChatInfoAdapter missingSessionUserProjectionChatInfoAdapter, + final MeterRegistry meterRegistry) { this.telegramUserService = telegramUserService; this.customTelegramClient = customTelegramClient; this.missingSessionUserProjectionUserInfoAdapter = missingSessionUserProjectionUserInfoAdapter; this.missingSessionUserProjectionChatInfoAdapter = missingSessionUserProjectionChatInfoAdapter; this.reminderExecutor = BackpressureExecutors.newBackPressureVirtualThreadPerTaskExecutor("reminders", MAX_CONCURRENCY); + this.meterRegistry = meterRegistry; + this.backgroundDurationTimer = meterRegistry.timer(MetricsConstants.BACKGROUND_DURATION, + MetricsConstants.TAG_TASK_NAME, TASK_NAME); + this.backgroundErrorsCounter = meterRegistry.counter(MetricsConstants.BACKGROUND_ERRORS, + MetricsConstants.TAG_TASK_NAME, TASK_NAME); } public void remindMissingUsers() { - telegramUserService.findUsersWithMissingSessions(LinkedinTimeUtils.todayGameDay()) - .filter(this::shouldRemindNow) - .forEach(missingSessionUserProjection -> reminderExecutor.execute(() -> remindMissingUser(missingSessionUserProjection))); + Timer.Sample sample = Timer.start(meterRegistry); + try { + telegramUserService.findUsersWithMissingSessions(LinkedinTimeUtils.todayGameDay()) + .filter(this::shouldRemindNow) + .forEach(missingSessionUserProjection -> reminderExecutor.execute(() -> remindMissingUser(missingSessionUserProjection))); + } finally { + sample.stop(backgroundDurationTimer); + } } private boolean shouldRemindNow(MissingSessionUserProjection missingSessionUserProjection) { @@ -58,9 +80,14 @@ private boolean shouldRemindNow(MissingSessionUserProjection missingSessionUserP } private void remindMissingUser(MissingSessionUserProjection missingSessionUserProjection) { - ChatInfo chatInfo = missingSessionUserProjectionChatInfoAdapter.adapt(missingSessionUserProjection); - UserInfo userInfo = missingSessionUserProjectionUserInfoAdapter.adapt(missingSessionUserProjection); - customTelegramClient.sendMessage(USER_MISSING_SESSIONS_REMINDER.formatted(FormatUtils.formatUserMention(userInfo)), chatInfo.chatId()); + try { + ChatInfo chatInfo = missingSessionUserProjectionChatInfoAdapter.adapt(missingSessionUserProjection); + UserInfo userInfo = missingSessionUserProjectionUserInfoAdapter.adapt(missingSessionUserProjection); + customTelegramClient.sendMessage(USER_MISSING_SESSIONS_REMINDER.formatted(FormatUtils.formatUserMention(userInfo)), chatInfo.chatId()); + } catch (Exception e) { + backgroundErrorsCounter.increment(); + throw e; + } } diff --git a/src/test/java/dev/rubasace/linkedin/games/ldrbot/image/ImageTestConfiguration.java b/src/test/java/dev/rubasace/linkedin/games/ldrbot/image/ImageTestConfiguration.java index ca293bf..334af79 100644 --- a/src/test/java/dev/rubasace/linkedin/games/ldrbot/image/ImageTestConfiguration.java +++ b/src/test/java/dev/rubasace/linkedin/games/ldrbot/image/ImageTestConfiguration.java @@ -1,8 +1,11 @@ package dev.rubasace.linkedin.games.ldrbot.image; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @@ -12,4 +15,9 @@ @EnableConfigurationProperties(TesseractProperties.class) class ImageTestConfiguration { -} \ No newline at end of file + @Bean + MeterRegistry meterRegistry() { + return new SimpleMeterRegistry(); + } + +}