Перейти к содержанию

6. Кэширование и логирование

Аналитические запросы часто выполняются над большим количеством записей. Если результат нужен многим пользователям, его можно временно хранить в памяти.

Кэширование и логирование решают разные задачи, но обе важны для производственного качества.

Кэширование ускоряет повторные чтения. Например, если десять аналитиков за минуту открыли одну и ту же сводку за неделю, нет смысла десять раз пересчитывать одинаковые агрегаты по базе.

Логирование помогает понять, что происходило в приложении. Когда пользователь говорит “отчёт не сформировался”, разработчик смотрит логи и видит ошибку, время, параметры и место сбоя.

MemoryCache

Пакет обычно уже доступен в ASP.NET Core. Регистрация:

Эта строка включает встроенный in-memory cache в DI-контейнере. После регистрации сервисы смогут получать IMemoryCache через конструктор и хранить временные результаты расчётов.

builder.Services.AddMemoryCache();

Пример сервиса аналитики показывает, где кэш полезен на практике. Сводка за один и тот же период может запрашиваться много раз, поэтому результат агрегирующего запроса сохраняется на короткое время.

public class AnalyticsService
{
    private readonly AppDbContext _db;
    private readonly IMemoryCache _cache;

    public AnalyticsService(AppDbContext db, IMemoryCache cache)
    {
        _db = db;
        _cache = cache;
    }

    public async Task<AnalyticsSummaryDto> GetSummaryAsync(DateTime from, DateTime to, CancellationToken ct)
    {
        var cacheKey = $"analytics-summary:{from:yyyyMMddHH}:{to:yyyyMMddHH}";

        return await _cache.GetOrCreateAsync(cacheKey, async entry =>
        {
            entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(2);

            var events = _db.ActivityEvents.Where(x => x.CreatedAt >= from && x.CreatedAt <= to);

            return new AnalyticsSummaryDto(
                TotalEvents: await events.CountAsync(ct),
                UniqueUsers: await events.Select(x => x.UserId).Distinct().CountAsync(ct),
                Errors: await events.CountAsync(x => x.Severity == "Error", ct));
        }) ?? new AnalyticsSummaryDto(0, 0, 0);
    }
}

Когда кэш сбрасывать

Если появилось новое событие, старый результат аналитики может устареть. В учебной версии достаточно короткого TTL 2 минуты. В продвинутой версии можно удалять ключи кэша после создания события.

TTL — это время жизни записи в кэше. Маленький TTL даёт более свежие данные, но чаще обращается к базе. Большой TTL сильнее ускоряет приложение, но повышает риск показать устаревшую аналитику.

Логирование ASP.NET Core

ASP.NET Core уже имеет встроенный ILogger<T>.

В сервисе событий лог пишется вокруг ключевой бизнес-операции. Это помогает отличать успешное создание события от ошибки, которая произошла во время обработки запроса.

public class ActivityEventService : IActivityEventService
{
    private readonly ILogger<ActivityEventService> _logger;

    public ActivityEventService(ILogger<ActivityEventService> logger)
    {
        _logger = logger;
    }

    public async Task<ActivityEventDto> CreateAsync(CreateActivityEventRequest request, int userId, CancellationToken ct)
    {
        _logger.LogInformation("Creating activity event {Type} for user {UserId}", request.Type, userId);

        try
        {
            ...
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to create activity event for user {UserId}", userId);
            throw;
        }
    }
}

Настройка уровней логов

Конфигурация уровней нужна, чтобы в разработке видеть важные сообщения приложения и при этом не утонуть в техническом шуме фреймворка.

appsettings.Development.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning",
      "Microsoft.EntityFrameworkCore.Database.Command": "Information"
    }
  }
}

Для практики достаточно консольных логов. Для реального проекта можно добавить Serilog и запись в файл или внешнюю систему мониторинга.

Что логировать

Логировать нужно не каждую строку кода, а важные события:

  • вход пользователя;
  • создание события активности;
  • ошибки сохранения в базу;
  • запуск генератора нагрузки;
  • формирование отчёта;
  • отказ в доступе к важной операции.

Не записывайте в логи пароли, JWT-токены и персональные данные без необходимости.