using Serilog; using Serilog.Events; using Serilog.Filters; using Serilog.Sinks.Grafana.Loki; using System.Collections.Immutable; using System.Text.RegularExpressions; namespace KLHZ.Trader.Service.Infrastructure { internal static class IHostBuilderExtensions { private readonly static Regex urlCheckRegex = new("^http://|https://.+"); private readonly static ImmutableArray DefaultLabels = new List() { "action", }.ToImmutableArray(); /// /// Добавить логирование в Loki /// /// /// url loki, куда писать логи. /// Название сервиса, от имени которого пишутся логи. Если передан null - берется название домена. /// Флаг, включающий дублирование логов в консоль. /// Минимальный уровень логирования приложения. /// Минимальный уровень логирования для EF. /// Удалять из логов записи, сгенерированные EF. /// Удалять из логов записи, возникшие в результате скреппинга метрик прометеусом.. /// Набор тегов, который будет извлекаться при отправке в Loki /// и помечаться как Label для ускорения поиска по логам. /// /// public static IHostBuilder ConfigureSerilog(this IHostBuilder hostBuilder, string? lokiUrl, string? serviceName = null, bool writeToConsole = false, List? additionalFiltratonLabels = null, LogEventLevel minLevel = LogEventLevel.Information, LogEventLevel EFMinLogLevel = LogEventLevel.Information, bool excludeEFLogs = true, bool excludeMetricsScrapingLogs = true ) { if (string.IsNullOrEmpty(lokiUrl) || !urlCheckRegex.IsMatch(lokiUrl)) throw new ArgumentException("Bad lokiUrl!"); var labels = new List(); if (additionalFiltratonLabels != null && additionalFiltratonLabels.Count > 0) { labels.AddRange(additionalFiltratonLabels); } labels.AddRange(DefaultLabels); hostBuilder.UseSerilog((ctx, lc) => { if (excludeEFLogs) lc.Filter.ByExcluding(Matching.WithProperty("SourceContext", "Microsoft.EntityFrameworkCore.Database.Command")); if (excludeMetricsScrapingLogs) lc.Filter.ByExcluding(Matching.WithProperty("RequestPath", "/metrics")); lc.MinimumLevel.Is(minLevel); lc.MinimumLevel.Override("Microsoft.EntityFrameworkCore.Database.Command", EFMinLogLevel); lc.WriteTo.GrafanaLoki(lokiUrl, queueLimit: 1000000, labels: new List { new () { Key = "service", Value = serviceName ?? AppDomain.CurrentDomain.FriendlyName } }, restrictedToMinimumLevel: minLevel, propertiesAsLabels: labels, textFormatter: new LokiJsonTextFormatter() ); if (writeToConsole) lc.WriteTo.Console(); }); return hostBuilder; } } }