Skip to content

Наблюдаемость и аудит ​

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

Цель диагностикиТочка входа
Что искал некоторый вопрос-ответ, сколько раз вызывались модели, сколько потрачено tokenПосле подключения Langfuse смотрите полную цепочку вызовов в Langfuse
Изменения баз знаний, участников или системных настроек«Активность» в настройках базы знаний, а также «Настройки → Журнал аудита»
Накапливаются ли фоновые задачи разбора, аннотирования и Wiki или падают ли они«Настройки → Очереди среды выполнения»
Жив ли сервисGET /health
Связать логи сервиса по одному запросуИщите в логах по X-Request-ID из заголовка ответа

При диагностике проблем с запросом сначала получите X-Request-ID из заголовка ответа, затем свяжите логи и записи трассировки.

Быстрая справка по эксплуатации ​

Хочу узнать…Куда идти
Что произошло на всём пути некоторого запросаgrep по логам приложения по X-Request-ID из заголовка ответа; после включения LLM_DEBUG_LOG смотрите llm_debug/<request_id>.log
Дерево вызовов LLM и расход token для одного чата/разбораLangfuse UI (имя trace POST /api/v1/agent-chat или asynq.document:process)
Кто, когда и что изменилАудит пространства /tenants/:id/audit-log; активность KB /knowledge-bases/:id/activity; аудит платформы /system/admin/audit-log
Почему некоторый документ постоянно падаетТаблица task_dead_letters (scope=knowledge/knowledge_base) + last_error задач archived на панели среды выполнения
Жив ли сервисGET /health (200 {"status":"ok"})
Загрузилась ли конфигурация как ожидалосьБаннер [startup-env] в логе запуска (internal/runtime/startup.go, секретные значения показываются только длиной)

Проверка здоровья ​

internal/router/router.go регистрирует пробу здоровья без аутентификации (белый список публичных путей в internal/middleware/auth.go включает /health):

go
// internal/router/router.go
r.GET("/health", func(c *gin.Context) {
    c.JSON(200, gin.H{"status": "ok"})
})

Это чистая проба живости (liveness, не проверяет зависимости DB/Redis), подходит в качестве цели проверки здоровья контейнера / LB. langfuse.shouldTrace и выборка для журнала запросов также исключают её, чтобы избежать шума от проб. Uptime процесса предоставляется панели эксплуатации через MarkServerStarted/ServerUptime из internal/runtime/server.go.

Справочник по логам, трассировке и аудиту ​

Система логирования (internal/logger) ​

Формат и уровни ​

  • В основе — приватный экземпляр logrus (appLogger, чтобы внешние зависимости не перезаписывали глобальный logrus и не теряли логи), с пользовательским CustomFormatter.
  • Формат по умолчанию в одну строку: LEVEL[метка времени] [поле request_id...] caller | message, где caller — файл:строка[имя функции] (addCaller).
  • Через переменную окружения LOG_FORMAT можно задать шаблон; заполнители: %d=время, %level=уровень, %thread=ID goroutine (берётся, только если на него ссылается шаблон, чтобы не запускать runtime.Stack на каждой строке), %logger=caller, %traceId=request_id, %msg=сообщение+структурированные поля. Однопроходная замена strings.NewReplacer исключает повторную подстановку.
  • Уровень управляется LOG_LEVEL (debug/info/warn/error/fatal; если не задан или недопустим, по умолчанию debug).
  • Цвета: ANSI-цвета включаются, когда stdout — терминал; при не-терминале (сбор в Docker) отключаются; при записи в файл ansiStripWriter удаляет ANSI-последовательности, сохраняя чистый текст.
  • API структурированных полей: logger.WithField(ctx, k, v) / WithFields сохраняют entry с полями в context (types.LoggerContextKey), а последующие logger.Infof(ctx, ...) подхватывают их автоматически; WarnWithFields предназначен для событий, связанных с аудитом (кросс-тенантные пробы, нарушение инвариантов), что упрощает индексацию агрегаторами логов по tenant/ресурсу.
  • CloneContext при порождении фоновой goroutine копирует ключевые ключи context (tenant/user/request_id/роль/язык и т. д.) и одновременно сохраняет дескриптор *Trace Langfuse и активный OTel span, чтобы дочерний span не стал осиротевшей trace.

Вывод и ротация ​

ConfigureFromEnv() (выполняется при init, может вызываться повторно после загрузки .env в main): всегда пишет в stdout; при непустом LOG_PATH (или при работе упакованного macOS .app автоматически попадает в ~/Library/Logs/<App>/<App>.log) дополнительно пишет на диск через lumberjack:

go
// internal/logger/logger.go openLogFile()
return &lumberjack.Logger{
    Filename:   logPath,
    MaxSize:    50, // megabytes
    MaxBackups: 3,
    MaxAge:     28, // days
    Compress:   true,
}, nil

Отладочный журнал LLM (internal/logger/llm_logger.go) ​

При включении LLM_DEBUG_LOG=true|1|<каталог> каждый вызов модели (Chat / Chat Stream / Embedding / Rerank / VLM) записывает полные входные сообщения, вызовы инструментов, вывод и ошибки в каталог llm_debug/, причём все вызовы с одним request_id дописываются в один файл (<request_id>.log), что упрощает восстановление всех взаимодействий с моделью в одной сессии. Файлы старше 7 дней в каталоге очищаются в фоне при запуске (cleanupOldDebugFiles).

Промежуточное ПО журнала запросов (internal/middleware/logger.go) ​

  • RequestID(): читает или генерирует X-Request-ID, записывает его обратно в заголовок ответа и вместе с logger с полями помещает request_id в gin context и context http.Request — так сквозные логи (включая прокинутые со стороны asynq worker метки сессии) связываются по request_id.
  • Logger(): записывает method, path (query через sanitizeQuery вычищает чувствительные к OAuth параметры token/code/state и т. п.), status_code, latency, client_ip, size, а также тело запроса/ответа размером до 10KB. Тело запроса/ответа обезличивается через sensitiveFieldRegex (значения полей password/token/api_key/secret/private_key и т. п. заменяются на "***", поддерживаются snake_case/camelCase); тело ответа SSE записывается как [потоковый ответ SSE, пропущено]; пути /assets/ и опроса wiki stats пропускаются полностью.
  • Доверенные прокси: r.SetTrustedProxies(...) (WEKNORA_TRUSTED_PROXIES) предотвращает подделку X-Forwarded-For для обхода ограничения частоты на основе ClientIP.

Трассировка Langfuse (internal/tracing/langfuse) ​

Распределённая трассировка использует OpenTelemetry Go SDK, записывает атрибуты span по семантическим соглашениям Langfuse (langfuse.observation.*) и экспортирует через OTLP/HTTP на POST <host>/api/public/otel/v1/traces, совместимо с Langfuse v3+ и LiteFuse. Трассировка по умолчанию выключена; пока не включена, соответствующие точки входа не выполняют экспорт.

Конфигурация (переменные окружения, config.go) ​

Переменная окруженияЗначение по умолчаниюОписание
LANGFUSE_ENABLEDавтовключение при наличии публичного и секретного ключейОбщий переключатель (согласуется с конвенцией Python SDK)
LANGFUSE_HOSThttps://cloud.langfuse.comБазовый адрес Langfuse/LiteFuse (можно развернуть самостоятельно)
LANGFUSE_PUBLIC_KEY / LANGFUSE_SECRET_KEY—Учётные данные проекта Basic Auth
LANGFUSE_RELEASE / LANGFUSE_ENVIRONMENT—Добавляются к каждой trace для фильтрации в UI
LANGFUSE_FLUSH_AT15Размер пакета экспорта (BatchSpanProcessor MaxExportBatchSize)
LANGFUSE_FLUSH_INTERVAL3sМаксимальный интервал пакетного экспорта (BatchTimeout)
LANGFUSE_QUEUE_SIZE2048Верхняя граница буфера в памяти (защита от неограниченного роста при недоступности эндпоинта)
LANGFUSE_REQUEST_TIMEOUT10sТаймаут одного HTTP ingestion
LANGFUSE_SAMPLE_RATE1.0Частота выборки ParentBased(TraceIDRatioBased), 0..1
LANGFUSE_DEBUGfalseПодробный лог ошибок пакетной отправки

Экспортёр (exporter.go) ​

Экспортёр OTLP/HTTP, Authorization: Basic base64(public:secret); этот экспортёр использует собственный HTTP-клиент, не проходит через защиту SSRF и не подпадает под белый список даже при включённом SSRF_DNS_WHITELIST_ONLY; x-langfuse-ingestion-version: 4 — обязательный пороговый заголовок для пути прямой записи OTel в Langfuse v3/LiteFuse (при его отсутствии возвращается 400), а x-langfuse-sdk-name/version — маркеры совместимости. Manager (manager.go) держит отдельный TracerProvider (resource service.name=weknora) и не вызывает глобальных изменений OTel вроде otel.SetTextMapPropagator, чтобы не влиять на другие точки инструментирования OTel в процессе; W3C TraceContext propagator — приватное значение уровня пакета.

Модель наблюдений и точки инструментирования ​

Три вида дескрипторов (tracer.go): Trace (корень, один запрос), Span (логическая единица работы, не LLM), Generation (один вызов модели, включая статистику token TokenUsage и потоковый time-to-first-token MarkCompletionStart). Родительско-дочерние связи устанавливаются автоматически через OTel span context; при отсутствии trace автоматически открывается auto-trace, чтобы не было осиротевших span.

Основные точки инструментирования:

ТочкаИсходный кодРезультат
Вход HTTPmiddleware.go GinMiddlewareНа пути белого списка shouldTrace (knowledge-chat / agent-chat / knowledge-search / различные ingestion POST/PUT / импорт FAQ / wiki auto-fix / evaluation / определение initialization и т. д.) открывает корневую Trace с именем METHOD /path, metadata содержит http.method/path/query/request_id, вывод — status и response.size; извлекает вышестоящий заголовок W3C traceparent и наследует trace id внешнего вызывающего
asynq workerasynq.go AsynqMiddlewareВосстанавливает traceparent из payload и продолжает HTTP trace, иначе открывает новую trace asynq.<task_type>; оборачивает в SPAN, metadata содержит task_id/queue/retry/max_retry/payload_bytes; payload показывается только первые 1KB
Инъекция на стороне постановки в очередьasynq.go InjectTracing + internal/types/tracing.go TracingContextВстраивает traceparent и метки user/session в payload задачи как JSON-поля lf_* для передачи между процессами
Вызов моделиinternal/models/{chat,embedding,rerank,vlm,asr}/langfuse_wrapper.goОдин Generation на вызов (имя модели, вход, параметры, выход, token usage, ошибка)
Сводка поиска/реранжированияretrieval_obs.goSummarizeRetrieveOutput / SummarizeSearchResults и т. п. сжимают результаты поиска в предпросмотр top-25 (rank/chunk_id/score/preview на 160 символов), чтобы в trace не попадал полный текст
Выполнение Agentinternal/agent/engine.go, act.goSPAN'ы agent.execute и т. п., через logger.CloneContext сохраняются в одном дереве с корневой HTTP trace

Отправляемое содержимое (атрибуты span, events.go): langfuse.observation.type/input/output/metadata/model.name/model.parameters/usage_details/completion_start_time, langfuse.trace.name/input/output/metadata/tags, user.id (явный user или tenant:<id>), session.id, langfuse.environment/release.

Вызовы поиска и диагностические записи ​

embedding.embed, retrieve и rerank фиксируют время вычисления вектора запроса, фактического этапа поиска и вызова модели переранжирования соответственно. Узлы с префиксом diagnostics. записывают только статистику уже полученных результатов и решения о фильтрации, дополнительных запросов к поиску или модели они не выполняют и не должны считаться отдельными вызовами.

Диагностический узелЗначение
diagnostics.retrieval.vector.result_pool.initialПул результатов, впервые возвращённый векторным поиском
diagnostics.retrieval.keywords.result_pool.initialПул результатов, впервые возвращённый поиском по ключевым словам
diagnostics.retrieval.<vector/keywords>.result_pool.refillПул результатов, возвращённый дополнительным поиском (появляется только при реально выполненном дополнительном поиске)
diagnostics.retrieval.<vector/keywords>.filter_decisionПул результатов до применения фильтров и решение о сохранении/отсеве
diagnostics.retrieval.fusion.candidate_selectionРешение о обрезке по лимиту числа кандидатов после слияния
diagnostics.rerank.selectionРешение о сохранении/отсеве после переранжирования

Эти пулы результатов содержат текст, выдержки, описания изображений, OCR и векторы оригинальных изображений — все записи в пределах области запроса, а не только изображения. Общий подсчёт охватывает всех кандидатов, image_samples — только выборка попаданий по векторам оригинальных изображений; в metadata назначение помечается через observation_kind=diagnostic и sample_scope=image_vectors. Если дополнительного поиска не было, сэмплы result_pool.initial и filter_decision могут совпадать — это одни и те же данные, наблюдавшиеся на двух этапах.

Изменение имён затрагивает только новые трейсы; в исторических записях сохраняются прежние имена retrieval.image_* и rerank.selection. Дашборды и запросы, фильтрующиеся по именам узлов, следует соответствующим образом обновить.

Расход за раунд, назначение вызовов и кэш ​

Каждый вызов модели по-прежнему создаёт отдельный Generation; usage сообщения хранит агрегированный расход token за текущий раунд, событие завершения Agent возвращает его через turn_usage, и при повторном открытии исторических сообщений его всё ещё можно прочитать. prompt_tokens, completion_tokens, total_tokens — это итоговые значения; cached_tokens — совместимый псевдоним cache_read_tokens, а cache_read/write/miss — детализация входных token, которую нельзя повторно прибавлять к prompt_tokens.

cache_reported и cache_status различают hit, miss, unreported, unsupported; отсутствие отчёта нельзя считать промахом кэша. Metadata Generation фиксирует call_purpose и отпечаток префикса промпта, чтобы различать назначения (ответ, перезапись, аннотирование и т. п.) и объяснять изменения кэша.

При сборке промпта стабильные инструкции размещаются в начале, а динамическое содержимое сессии — в конце; поддерживаемые провайдеры используют cache marker. Попадание по-прежнему зависит от протокола провайдера, модели и префикса запроса, и наличие кэша для каждого запроса не гарантируется. При диагностике сначала сравните назначение вызова, отпечаток префикса и модель, а затем смотрите состояние отчётности.

Вызовы песочницы тоже имеют span Langfuse, что позволяет связывать время операций с экземпляром и выполнения инструментов с текущей trace и различать ожидание песочницы и ожидание модели. Фоновое сопровождение песочницы (например, сборка сессий) записывает дочерние span только при наличии родительской trace и не создаёт trace самостоятельно; задачи установки, удаления и сопровождения навыков открывают trace сами. Пока переключатель трассировки не включён, экспорт в Langfuse не выполняется.

Журнал аудита ​

Модель данных (internal/types/audit_log.go) ​

Таблица audit_logs работает append-only (без UpdatedAt, без мягкого удаления); монотонный id служит одновременно первичным ключом и курсором:

ПолеТипОписание
iduint64 автоинкрементПервичный ключ + курсор постраничной навигации (WHERE id < after_id ORDER BY id DESC)
tenant_iduint64Пространство; 0 = событие системного уровня (system-scope)
actor_user_id / actor_rolevarcharАвтор операции и его тогдашняя роль (пусто при системном срабатывании)
actionvarchar(64)Именование через точку <area>.<event> (см. 4.2)
scope_type / scope_idvarcharОбласть ресурса (например, knowledge_base + kbID, управляет страницей активности KB)
target_type / target_id / target_user_idvarcharКонкретный целевой ресурс / пользователь
request_path / request_methodvarcharШаблон маршрута (не исходный URL, чтобы курсор не разрастался; исходный URL хранится в Details.raw_path)
outcomevarchar(16)success / accepted (асинхронно принято, но не в конечном состоянии) / denied / failed / partial / canceled
detailsjsonbНагрузка, зависящая от действия; значения секретов никогда не попадают в БД (например, для vector_store записываются только имена изменённых полей). Активность базы знаний, инициированная API Key, дополнительно записывает api_key_id / api_key_name (снимок имени, без открытого Key)
created_attimestampОснова для очистки по политике хранения

Список действий аудита ​

ГруппаДействия
RBAC / участникиrbac.member_added, rbac.member_removed, rbac.member_role_changed, rbac.member_left, rbac.access_denied, rbac.invitation_sent, rbac.invitation_accepted, rbac.invitation_declined, rbac.invitation_revoked, rbac.invitation_expired
Векторное хранилищеvector_store.created, vector_store.updated, vector_store.deleted
Производные ресурсы OpenSearchopensearch.index_created, opensearch.index_deleted, opensearch.reindex_executed
Системное управление (tenant_id=0)system.setting_changed, system.admin_promoted, system.admin_revoked, system.user_created, system.user_password_reset, system.api_key_created, system.api_key_revoked
Операции с очередями среды выполнения (tenant_id=0)system.queue_task_retried, system.queue_task_deleted, system.queue_task_run_now, system.queue_task_cancelled, system.queue_archived_purged
Базы знанийkb.created, kb.updated, kb.deleted, kb.duplicated, kb.clone_started, kb.clone_completed, kb.clone_failed, kb.share_added, kb.share_permission_changed, kb.share_removed
Знанияknowledge.created, knowledge.updated, knowledge.deleted, knowledge.batch_deleted, knowledge.reparse_started, knowledge.parse_canceled, knowledge.move_started, knowledge.move_completed, knowledge.move_failed
Теги / источники данныхtag.created, tag.updated, tag.deleted, datasource.created, datasource.updated, datasource.deleted, datasource.sync_started, datasource.sync_completed, datasource.sync_failed, datasource.paused, datasource.resumed
Wiki / FAQwiki.content_changed, faq.import_started, faq.import_completed, faq.import_failed

Путь записи (service + middleware) ​

  • auditLogService.Log (internal/application/service/audit_log.go) — каноническая точка записи: по умолчанию outcome=success, заполняется CreatedAt; при сбое записи только пишется лог ERROR, ошибка не распространяется вверх — сбой аудита ни в коем случае не должен прерывать бизнес-операцию.
  • LogDenied фиксирует отказы промежуточного ПО RBAC: выполняет дедупликацию в скользящем окне 1 минуты по ключу (tenant_id, actor, action=rbac.access_denied, шаблон route) (denyDedupWindow, repo.CountSinceForDedup), чтобы клиенты-пробники не заполнили таблицу (100 RPS по одному эндпоинту дают лишь 1 строку в минуту); в качестве ключа дедупликации используется шаблон route, а не исходный URL, чтобы перебор UUID не обходил окно. Лог [rbac] role insufficient в stderr не подвержен дедупликации и пишется при каждом отказе.
  • AuditServiceProvider из middleware/audit_provider.go внедряет service в gin context (ключ weknora.audit_service); промежуточное ПО RBAC получает его через AuditServiceFromContext, безопасно при nil (в режиме Lite аудит можно не настраивать).

API запросов (internal/handler/audit_log.go) ​

МаршрутПраваОписание
GET /api/v1/tenants/:id/audit-logPathTenantMatch + AdminПоток аудита пространства; возвращает только строки уровня пространства с scope_type='' (UnscopedOnly)
GET /api/v1/knowledge-bases/:id/activityСоздатель KB или Admin пространства, и обязательно пространство-владелец (потребители общего доступа организации читать не могут)Проекция активности KB с scope_type=knowledge_base + scope_id=kbID
GET /api/v1/system/admin/audit-logSystemAdmin (+ Platform API Key system.audit_read)События уровня платформы с tenant_id=0 (настройки / повышение / операции с очередями и т. д.)

Единые параметры запроса: after_id (курсор, возвращает строки с меньшим id), limit (1–100, по умолчанию 50, жёсткий предел auditLogListLimitMax=100), точные фильтры action / outcome / actor. Ответ содержит next_cursor (минимальный id на странице, 0 означает достижение конца).

Политика хранения (internal/application/service/audit_log_retention.go) ​

  • Конфигурация: audit.retention_days (YAML) / WEKNORA_AUDIT_RETENTION_DAYS (переопределение через env); при отсутствии секции audit: по умолчанию 90 дней; явный 0 отключает очистку (архивирование вне БД для сценариев соответствия), отрицательное значение вызывает ошибку при проверке config.
  • AuditLogRetentionRunner: фоновая goroutine на чистом time.Ticker (без зависимостей от cron / asynq), задержка запуска 10 минут (чтобы избежать пересечения с миграциями и стартовой нагрузкой), затем каждые 24 ч выполняется Purge → DeleteOlderThan(now - retention_days) (одиночный с индексированным DELETE, таймаут 30s). Число удалённых записей логируется на уровне INFO, сбой — WARN (повтор в следующем раунде). Собирается в internal/container/container.go и регистрирует ResourceCleaner для корректной остановки (Stop идемпотентен, при отсутствии Start возвращается сразу).

Ограничение частоты (internal/ratelimit и промежуточное ПО) ​

Универсальный ограничитель со скользящим окном (internal/ratelimit/limiter.go) ​

  • Приоритет Redis: Lua-скрипт атомарно выполняет «удаление устаревших членов ZSET → подсчёт ZCARD → при непревышении ZADD + PEXPIRE», бюджет совместно используется несколькими экземплярами; member вида <instanceID>:<ms> гарантирует уникальность.
  • Когда Redis недоступен (ошибка или Lite без Redis), происходит автоматическая деградация до внутрипроцессного localLimiter (sync.Map + массив меток времени на ключ); StartCleanup периодически вычищает пустые ключи.
  • max передаётся при каждом вызове Allow, поэтому один limiter может использовать разные бюджеты для разных ключей (например, своя квота у каждого канала embed).
  • Потребители: публичные интерфейсы Web embed (два limiter'а — на минуту + на 24 ч, по channel+ClientIP, internal/middleware/embed_auth.go), сервис IM (internal/im/service.go).

Ограничение по IP на публичных эндпоинтах аутентификации (internal/middleware/auth_public_ratelimit.go) ​

PublicAuthRateLimit() защищает неаутентифицированные эндпоинты ссылок-приглашений (/auth/invitations/lookup, /auth/register-by-invite): внутрипроцессное скользящее окно, 30 раз в минуту на IP (общий бакет на оба эндпоинта); при превышении возвращается 429 (ErrTooManyRequests). Реализация чисто локальная (низконагруженные эндпоинты); в комментариях указано, что при горизонтальном масштабировании следует перейти на Redis-версию из internal/ratelimit.

Статистика ссылок на модели (internal/application/repository/model_usage.go) ​

Этот файл предоставляет запрос ссылок на модель (usage-by-reference), то есть отвечает на вопрос «какие ресурсы используют данную модель», и служит защитой зависимостей перед удалением модели, а не тарификацией по расходу token:

  • scopeKnowledgeBasesByModelID: сопоставляет любое поле привязки модели в knowledge_bases — embedding_model_id, summary_model_id, image_processing_config.model_id, vlm_config.model_id, asr_config.model_id, wiki_config.synthesis_model_id, auto_tag_config.model_id (в Postgres используется оператор ->> JSON, в SQLite — json_extract; оба диалекта эквивалентны).
  • scopeCustomAgentsByModelID: сопоставляет model_id, rerank_model_id, vlm_model_id, asr_model_id, query_understand_model_id, question_suggestions.follow_ups.model_id в custom_agents.config.
  • Конфигурация долговременной памяти пространства: extract_model_id и embedding_model_id из memory_config также считаются ссылками и появляются в long_term_memory.bindings.
  • Потребитель: ListModelUsages из репозиториев knowledgebase.go / custom_agent.go переиспользует указанные scope и возвращает минимальную проекцию активных объектов тенанта (id, name, объединённые bindings) с верхним пределом ModelUsageListLimit (50). Защита от удаления в internal/application/service/model.go использует knowledge_base_total / agent_total и привязки долговременной памяти как основание для блокировки и в HTTP 400 в error.details одновременно возвращает усечённый список, общее число и long_term_memory.

Расход моделей на уровне token передаётся через usage_details Generation Langfuse (TokenUsage: input/output/total/cache_*) и просматривается в Langfuse UI с агрегацией по модели / пользователю (tenant:<id>) / сессии.

Обзор потоков данных наблюдаемости ​

Справочник по реализации ​

Все пути ниже указаны относительно корня репозитория:

ВозможностьПуть к исходному коду
Логи приложенияinternal/logger/logger.go
Отладочный журнал вызовов LLMinternal/logger/llm_logger.go
Журнал запросов / промежуточное ПО RequestIDinternal/middleware/logger.go
Трассировка Langfuse (OTel SDK)internal/tracing/langfuse/ (config.go, manager.go, exporter.go, tracer.go, middleware.go, asynq.go, events.go, retrieval_obs.go, context.go)
Носитель trace между процессамиinternal/types/tracing.go
Журнал аудита handler / service / repointernal/handler/audit_log.go, internal/application/service/audit_log.go, internal/application/repository/audit_log.go
Политика хранения аудитаinternal/application/service/audit_log_retention.go, internal/config/config.go (applyAuditDefaults)
Действия / модель аудитаinternal/types/audit_log.go
Ограничение частотыinternal/ratelimit/limiter.go, internal/middleware/auth_public_ratelimit.go
Проверка здоровьяinternal/router/router.go (GET /health)
Статистика ссылок на моделиinternal/application/repository/model_usage.go

Версия v0.8.3 · MIT License