-
Notifications
You must be signed in to change notification settings - Fork 0
Logging RU
LogsParser пишет диагностику через Microsoft.Extensions.Logging. На этой странице описаны
LogsParserLogging — общий для всего процесса держатель фабрики, из которого каждый класс библиотеки получает
свой логгер, — категории, попадающие в вывод, шкала уровней и способы подключить фабрику через DI, вручную или
через Serilog.
public static class LogsParserLoggingПространство имён: LogsParser.Diagnostics.
Держатель хранит один ILoggerFactory и кеш ConcurrentDictionary<string, ILogger> с ключами по
StringComparer.Ordinal. Пока фабрика не задана, держатель содержит NullLoggerFactory.Instance.
| Член | Сигнатура | Описание |
|---|---|---|
CreateLogger<T>() |
public static ILogger CreateLogger<T>() |
Возвращает закешированный логгер с категорией typeof(T).Name
|
CreateLogger(string) |
public static ILogger CreateLogger(string categoryName) |
Возвращает закешированный логгер для явно заданной категории |
UseLoggerFactory(ILoggerFactory) |
public static void UseLoggerFactory(ILoggerFactory loggerFactory) |
Заменяет фабрику для всего процесса и очищает кеш логгеров |
Reset() |
public static void Reset() |
Восстанавливает NullLoggerFactory.Instance и очищает кеш логгеров |
public static ILogger CreateLogger<T>()Делегирует вызов в CreateLogger(typeof(T).Name), поэтому категорией становится короткое имя типа —
LogsParserClient, а не LogsParser.LogsParserClient.
| Параметр типа | Описание |
|---|---|
T |
Тип, короткое имя которого становится категорией лога |
using LogsParser.Diagnostics;
using Microsoft.Extensions.Logging;
ILogger logger = LogsParserLogging.CreateLogger<MyImporter>();
logger.LogInformation("import started");Исключения: не выбрасывает.
public static ILogger CreateLogger(string categoryName)Достаёт логгер из кеша, создавая его через текущую фабрику при первом обращении. Повторные вызовы с той же
категорией возвращают тот же экземпляр ILogger, пока фабрика не будет заменена или сброшена.
| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
categoryName |
string |
— | Категория создаваемого логгера; используется как ключ кеша без изменений |
using LogsParser.Diagnostics;
using Microsoft.Extensions.Logging;
ILogger logger = LogsParserLogging.CreateLogger("LogsHtmlParser");
logger.LogDebug("parsing a locally cached page");| Исключение | Когда |
|---|---|
ArgumentNullException |
categoryName равен null — он используется как ключ словаря |
public static void UseLoggerFactory(ILoggerFactory loggerFactory)Устанавливает loggerFactory в качестве фабрики для всего процесса и сбрасывает кеш логгеров, поэтому все
логгеры, созданные после вызова, приходят уже из новой фабрики. Замена выполняется под внутренней блокировкой.
| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
loggerFactory |
ILoggerFactory |
— | Фабрика, из которой будут создаваться логгеры при последующих вызовах CreateLogger
|
using LogsParser.Diagnostics;
using Microsoft.Extensions.Logging;
using var loggerFactory = LoggerFactory.Create(builder => builder
.AddSimpleConsole()
.SetMinimumLevel(LogLevel.Debug));
LogsParserLogging.UseLoggerFactory(loggerFactory);| Исключение | Когда |
|---|---|
ArgumentNullException |
loggerFactory равен null
|
public static void Reset()Возвращает NullLoggerFactory.Instance и очищает кеш. Метод нужен прежде всего для очистки в тестах: тест,
установивший перехватывающую фабрику, обязан сбросить держатель после себя, иначе фабрика протечёт во все
остальные тесты того же процесса.
using LogsParser.Diagnostics;
try
{
LogsParserLogging.UseLoggerFactory(capturingFactory);
// выполняем работу с библиотекой
}
finally
{
LogsParserLogging.Reset();
}Исключения: не выбрасывает.
Фабрика одна на процесс, а не на клиент или на источник данных. Записывают в неё три действия:
| Действие | Эффект |
|---|---|
LogsParserLogging.UseLoggerFactory(factory) |
Устанавливает factory глобально |
new LogsParserHttpDataSource(..., loggerFactory: factory) |
Вызывает UseLoggerFactory(factory), если аргумент не null
|
services.AddLogsParser(...) + разрешение ILogsParserDataSource
|
Зарегистрированная фабрика достаёт ILoggerFactory из контейнера (с откатом на NullLoggerFactory.Instance) и устанавливает его |
Два следствия, которые стоит учитывать:
- Создание второго
LogsParserHttpDataSourceс другимILoggerFactoryперенастраивает логирование для всего уже созданного, потому что каждый класс заново получает свой логгер при каждом обращении. - Разрешение источника данных из контейнера, в котором
ILoggerFactoryне зарегистрирован, установитNullLoggerFactory.Instanceи тем самым молча заменит фабрику, установленную вручную раньше. Настраивайте логирование в том же контейнере либо вызывайтеUseLoggerFactoryпосле первого разрешения.
using LogsParser.Diagnostics;
using LogsParser.Models;
using LogsParser.Net;
using Microsoft.Extensions.Logging;
using var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
// Оба варианта ведут к одному и тому же глобальному держателю.
using var dataSource = new LogsParserHttpDataSource(
credentials: new LogsParserCredentials("my_login", "my_password", "BASE32SECRET"),
loggerFactory: loggerFactory);
LogsParserLogging.UseLoggerFactory(loggerFactory);Если фабрика не задана, любая категория разрешается в NullLogger. Его IsEnabled возвращает false, поэтому
вызовы логирования внутри библиотеки не форматируют сообщения и не обращаются ни к одному приёмнику. Для работы
библиотеки настройка не нужна, и до установки фабрики никуда ничего не пишется.
Категории — это короткие имена типов, а не полные имена с пространством имён. Вот те, что реально встречаются в выводе:
| Категория | Кто пишет | О чём сообщает |
|---|---|---|
LogsParserClient |
LogsParser.LogsParserClient |
Параметры вызовов и количество разобранных записей для каждого публичного метода |
LogsParserHttpDataSource |
LogsParser.Net.LogsParserHttpDataSource |
Цикл «запрос — ответ», обработка редиректов, повторы, rate limit |
LogsHtmlParser |
LogsParser.LogsHtmlParser |
Размеры HTML, количество записей, пропущенные строки, отсутствующая разметка |
ReactShieldBypass |
внутренний решатель challenge | Обнаружение challenge и результат его решения |
CookieStorageExtensions |
внутренние помощники для cookies | Имена cookies, добавленных в запрос, и cookies, добавленные или обновлённые из ответа |
LogsParserAuthenticator |
внутренний auth flow | Шаги входа и двухфакторного подтверждения, извлечение CSRF, отказы |
ReactShieldBypass, CookieStorageExtensions и LogsParserAuthenticator — внутренние типы реализации: они не
входят в публичный API, но их категории всё равно попадают в лог и фильтруются по имени.
Так как Microsoft.Extensions.Logging сопоставляет фильтры по префиксу категории, эти короткие имена работают
напрямую и в AddFilter, и в appsettings.json:
using Microsoft.Extensions.Logging;
using var loggerFactory = LoggerFactory.Create(builder => builder
.AddConsole()
.SetMinimumLevel(LogLevel.Warning)
.AddFilter("LogsParserHttpDataSource", LogLevel.Trace));| Уровень | Область | Примеры из исходного кода |
|---|---|---|
Trace |
Детали обмена и cookies |
Sending GET {RelativeUri} (attempt {Attempt}/{MaxAttempts}); Response {RelativeUri}: StatusCode={StatusCode}, RateLimit={Remaining}/{Max}; Rate limit updated: {Remaining}/{Max}, Reset={Reset}; Cookies applied to request: {CookieCount} cookies [{CookieNames}]; Cookie {Action}: {CookieName}; CSRF token extracted from login page ({TokenLength} chars); TOTP code generated ({CodeLength} digits)
|
Debug |
Шаги потока выполнения и параметры вызовов |
LogsParserHttpDataSource initialized: BaseUri=…, HasCredentials=…, MaxRetryAttempts=…; GetLogsAsync: ServerId=…, Filters=[…], Period=…, Page=…, Limit=…; Step 1/4: GET /login — loading login page; ParseLogs: parsing HTML ({ContentLength} chars); Request completed: {RelativeUri}, {ContentLength} characters
|
Information |
Результаты и смена состояний |
GetLogsAsync: parsed {EntryCount} entries, MetaInfo={Start}-{End}/{Total}; Authentication required for {RelativeUri}, starting auth flow…; Two-factor confirmation completed for {Login}; React challenge detected for {RelativeUri}, solving…; React challenge solved via indexed array method
|
Warning |
Обратимая деградация |
Rate limit hit for {RelativeUri}, waiting until {ResetAt} ({DelaySeconds:F0}s); Transient failure for {RelativeUri}: retrying in {DelaySeconds}s (attempt {Attempt}/{MaxAttempts}); GetCurrentAccountAsync: account info not found in response; TOTP code rejected for {Login}: redirected back to /authenticator; Failed to parse IP address: '{RawValue}', using loopback fallback
|
Error |
Необратимые сбои |
Request failed: {RelativeUri} returned {StatusCode}; Account not configured: redirected to /profile for {RelativeUri}; Authentication did not stabilize for {RelativeUri} after {Attempts} attempts; Request failed after {MaxRetryAttempts} attempts: {RelativeUri}; React challenge unsolvable: tried {CandidateCount} candidates from {ValueCount} hex values
|
За каждой записью уровня Error следует выброшенное исключение — см. Исключения. Warning
обычно означает обратимую ситуацию: цикл продолжает работу, выжидает окончание лимита, повторяет запрос или
подставляет значение по умолчанию. Но не всегда: отклонённые учётные данные, отклонённый TOTP-код и
отсутствующая разметка, из-за которой падают ParseAdminActivity и ParseTopOperations, тоже сначала
записываются как Warning, а затем приводят к исключению.
Разумные отправные точки: Information для продакшена, Debug — когда запрос возвращает неожиданное
количество записей, Trace — когда сбоит аутентификация или хранилище cookies.
Библиотека пишет логин аккаунта и больше ничего, что позволяет опознать учётные данные:
| Логируется | Не логируется никогда |
|---|---|
Login из LogsParserCredentials
|
Пароль |
Длина CSRF-токена ({TokenLength} chars) |
Значение CSRF-токена |
Длина сгенерированного TOTP-кода ({CodeLength} digits) |
TOTP-секрет и любой сгенерированный код |
| Имена cookies и их количество | Значение любой cookie, включая arizonarp_session, XSRF-TOKEN и R3ACTLB
|
| Коды состояния ответов и длина содержимого | Тела ответов |
На уровне Trace вывод всё же показывает, какие страницы запрашивались и какие cookies существуют по именам,
поэтому относитесь к trace-логам как к метаданным сессии, хотя секретов в них нет.
AddLogsParser подхватывает тот ILoggerFactory, который предоставляет контейнер, поэтому единственный
дополнительный вызов — AddLogging.
using LogsParser;
using LogsParser.DependencyInjection;
using LogsParser.Models;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
var services = new ServiceCollection();
services.AddLogging(builder => builder
.AddConsole()
.SetMinimumLevel(LogLevel.Debug));
services.AddLogsParser(options =>
{
options.Credentials = new LogsParserCredentials("my_login", "my_password", "BASE32SECRET");
});
await using var provider = services.BuildServiceProvider();
var client = provider.GetRequiredService<LogsParserClient>();
var page = await client.GetLogsAsync(new LogsQuery(ServerId: 201));
Console.WriteLine(page.Entries.Count);Фабрику можно передать в источник данных либо установить напрямую; оба пути ведут к одному держателю.
using LogsParser;
using LogsParser.Abstractions;
using LogsParser.Models;
using LogsParser.Net;
using Microsoft.Extensions.Logging;
using var loggerFactory = LoggerFactory.Create(builder => builder
.AddSimpleConsole(options => options.SingleLine = true)
.SetMinimumLevel(LogLevel.Trace));
using var dataSource = new LogsParserHttpDataSource(
credentials: new LogsParserCredentials("my_login", "my_password", "BASE32SECRET"),
cookieStorage: new MemoryCookieStorage(),
loggerFactory: loggerFactory);
var client = new LogsParserClient(dataSource);
var catalog = await client.GetLogsFilterCatalogAsync();
Console.WriteLine(catalog.Filters.Count);У статических парсеров нет конструктора, куда можно передать фабрику, поэтому программа, использующая только Парсеры, устанавливает фабрику сама:
using LogsParser;
using LogsParser.Diagnostics;
using Microsoft.Extensions.Logging;
using var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
LogsParserLogging.UseLoggerFactory(loggerFactory);
var page = LogsHtmlParser.ParseLogs(File.ReadAllText("saved-page.html"));Требуется пакет Serilog.Extensions.Logging, который адаптирует логгер Serilog к ILoggerFactory.
using LogsParser.Diagnostics;
using Microsoft.Extensions.Logging;
using Serilog;
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.WriteTo.File("logs/logsparser-.log", rollingInterval: RollingInterval.Day)
.CreateLogger();
using var loggerFactory = LoggerFactory.Create(builder => builder.AddSerilog(Log.Logger, dispose: true));
LogsParserLogging.UseLoggerFactory(loggerFactory);Собственная фильтрация Serilog работает по тем же коротким категориям, сопоставляемым через SourceContext:
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Warning()
.MinimumLevel.Override("LogsParserHttpDataSource", Serilog.Events.LogEventLevel.Debug)
.WriteTo.Console()
.CreateLogger();Приглушить парсеры, оставив подробный транспорт:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
services.AddLogging(builder => builder
.AddConsole()
.SetMinimumLevel(LogLevel.Information)
.AddFilter("LogsParserHttpDataSource", LogLevel.Trace)
.AddFilter("LogsParserAuthenticator", LogLevel.Debug)
.AddFilter("CookieStorageExtensions", LogLevel.Warning)
.AddFilter("LogsHtmlParser", LogLevel.Warning));Та же конфигурация в appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"LogsParserHttpDataSource": "Trace",
"LogsParserAuthenticator": "Debug",
"CookieStorageExtensions": "Warning",
"LogsHtmlParser": "Warning"
}
}
}-
Внедрение зависимостей —
AddLogsParserи то, как он получаетILoggerFactory -
Транспорт и аутентификация — о чём именно сообщают категории
LogsParserHttpDataSourceиLogsParserAuthenticator -
Исключения — исключение, выбрасываемое после каждой записи уровня
Error -
Хранилище cookies — хранилище, стоящее за trace-записями
CookieStorageExtensions - Быстрый старт — первый запрос в ручном режиме и через DI
LogsParser · CC BY-NC 4.0 — non-commercial use only / только некоммерческое использование
English
- Home
- Getting Started
- LogsParserClient
- Requests and URI Builder
- Models
- Parsers
- Transport and Authentication
- Cookie Storage
- Dependency Injection
- Logging
- Exceptions
Русский
- Главная
- Начало работы
- LogsParserClient
- Запросы и построитель URI
- Модели
- Парсеры
- Транспорт и авторизация
- Хранение cookies
- Внедрение зависимостей
- Логирование
- Исключения