hotelme-nuxt-logger

Стандартний logger для Nuxt проектів з JSON виводом сумісним з бекенд-логами
5,091
Install
npm install hotelme-nuxt-logger
Latest Version:1.6.1
Vue:3
License:MIT
Last Updated:Oct 22, 2026
Links: npm
Maintainer: gribodemon

@hotelme/nuxt-logger

Стандартний logger для Nuxt 3 проектів з JSON виводом, сумісним з бекенд-логами.

Особливості

✅ JSON формат виводу в stdout ✅ Обов'язкові поля: ts, level, reqid, msg, project, log_type, pid ✅ Автоматичне логування всіх HTTP запитів з часом виконання ✅ X-Request-ID support - використовує reqid з header або генерує новий ✅ Timestamp з timezone - формат 2025-06-17T09:58:18.539733-0500 ✅ Auto-import - logger доступний глобально ✅ TypeScript підтримка з повною типізацією ✅ Сумісність з бекенд-логами для єдиної системи моніторингу

Покращення порівняно зі стандартними логами

1. reqid з X-Request-ID header

// Береться з header або генерується автоматично
const reqid = event.node.req.headers['x-request-id'] || generateUUID()

2. Timestamp з timezone та мікросекундами

{
  "ts": "2025-06-17T09:58:18.539733-0500"
}

3. Правильний msg для API запитів

// Для /api/* запитів
{ "msg": "api request", "request": "/api/hotels" }

// Для інших запитів
{ "msg": "stats", "request": "/hotels" }

4. response замість status_code

{ "response": 200 }

5. Всі логи в JSON форматі

Ніяких console.log з текстом - тільки structured JSON!

Встановлення

npm install @hotelme/nuxt-logger uuid
# або
yarn add @hotelme/nuxt-logger uuid
# або
pnpm add @hotelme/nuxt-logger uuid

Налаштування

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@hotelme/nuxt-logger'],

  logger: {
    // Назва проекту для логів
    projectName: 'my-project',

    // Увімкнути автоматичне логування запитів
    enableRequestLogging: true
  },

  // Назва потрапляє в runtimeConfig.public.projectName і використовується для всіх
  // записів (запити, помилки Nitro, SSR, клієнтські помилки, ручні logger.*).
  // Альтернативно через env
  runtimeConfig: {
    public: {
      projectName: process.env.NUXT_PUBLIC_PROJECT_NAME || 'my-project'
    }
  }
})

Використання

Автоматичне логування запитів

Модуль автоматично логує кожен HTTP запит:

{
  "msg": "api request",
  "level": "Info",
  "reqid": "aec7c27a231d1843",
  "project": "hotelme",
  "log_type": "frontend",
  "pid": 53,
  "ts": "2025-06-17T09:58:18.539733-0500",
  "stage": "after",
  "domain": "localhost:3000",
  "request": "/api/hotels?city=kyiv",
  "request_time": "0.045",
  "method": "GET",
  "response": 200,
  "user_id": "a1b2c3d4e5f6"
}

Ручне логування

// Auto-imported - працює всюди
logger.info('User logged in', { user_id: '123' })
logger.error('Failed to fetch', { error: err.message })
logger.warn('Rate limit approaching', { remaining: 10 })

В API handlers

logger та getRequestId auto-import'яться і в серверному коді Nitro (з 1.4.0). Явний імпорт, якщо треба: import { logger } from 'hotelme-nuxt-logger/runtime/logger'.

// server/api/hotels.get.ts
export default defineEventHandler((event) => {
  const reqid = event.context.reqid  // reqid з X-Request-ID

  logger.info('Fetching hotels', { reqid, city: event.query.city })

  try {
    const hotels = await fetchHotels(event.query.city)
    return hotels
  } catch (error) {
    logger.error('Failed to fetch hotels', {
      reqid,
      error: error.message,
      city: event.query.city
    })
    throw error
  }
})

Передача X-Request-ID між сервісами

// Передаємо reqid в запити до бекенду
const response = await $fetch('/api/hotels', {
  headers: {
    'X-Request-ID': event.context.reqid
  }
})

OpenTelemetry (з 1.5.0)

Трейсинг вимкнено за замовчуванням. Вмикається в nuxt.config — SDK, OTLP gRPC експортер і @opentelemetry/api вже є в залежностях модуля, ставити окремо не треба.

logger: {
  projectName: 'widgets-ssr',
  otel: {
    enabled: true,
    ignorePaths: ['/nuxt_img/'],                 // + типові /_nuxt/, /_ipx/, /__nuxt, clientErrorEndpoint
    ignoreUserAgents: ['^Slackbot-LinkExpanding ', '^kube-probe/'],
    slowMs: 1000,                                // порятунок повільних, env OTEL_TRACES_SLOW_MS
  },
}

Env (читає SDK):

Змінна Типово
OTEL_SERVICE_NAME projectName
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT / OTEL_EXPORTER_OTLP_ENDPOINT — (обов'язкова: без неї трейсинг не вмикається, лог otel_disabled)
OTEL_TRACES_SAMPLER / OTEL_TRACES_SAMPLER_ARG parentbased_always_on

Env модуля (перекриває slowMs з nuxt.config):

Змінна Типово
OTEL_TRACES_SLOW_MS — (0 — без порятунку)

Напр. 25% трейсів + повільні ≥ 1 с з решти:

OTEL_TRACES_SAMPLER: parentbased_traceidratio
OTEL_TRACES_SAMPLER_ARG: "0.25"
OTEL_TRACES_SLOW_MS: "1000"

Що робить модуль:

  • Активний контекст на весь запит. Плагін обгортає nitroApp.h3App.handler, тож middleware, handler'и та SSR-рендер виконуються всередині context.with(serverSpan) — span'и з коду застосунку стають дочірніми без обгорток.
  • Server span на кожен запит, батько — вхідний traceparent. Без нього trace id = X-Request-ID без дефісів (2e285875-f20c-… → 2e285875f20c…, як у libwts3), якщо це UUID / 32 hex; інакше випадковий. У event.context: otelSpan; у відповіді — X-Trace-Id. 5xx → статус ERROR, 4xx — ні.
  • Вихідні fetch (native fetch, $fetch, ofetch на зовнішній URL) — @opentelemetry/instrumentation-undici через diagnostics_channel: CLIENT span GET <host> (повний URL — в url.full), traceparent і X-Request-ID сторінки, якщо виклик не поставив свій. Поза запитом span'и не пишуться.
  • Внутрішні SSR-виклики ($fetch('/api/…') → Nitro localFetch) ідуть у тому ж async-ланцюжку, тож span роуту стає дочірнім span'а сторінки, а logger-middleware бере reqid сторінки. Заголовки руками не потрібні. Внутрішній підзапит на відфільтрований шлях (/__nuxt_island/…) span'а не отримує, але лишається в контексті сторінки — його /api/…-виклики стають прямими дітьми span'а сторінки.
  • Назва span'а — шаблон, ніколи сирий шлях (кожна назва — окремий stream у VictoriaTraces), визначається сама після роутингу:
    • сторінка → route.name з Vue Router без i18n-суфікса: GET weather-citynameid, GET index (аліаси теж). Локаль — в атрибуті i18n.locale;
    • Nitro-хендлер → шаблон з файлової структури: GET /api/city_info, GET /record/**;
    • решта (відповідь з server middleware, невідомий шлях) → GET /api/* / GET /*; точний шлях — в url.path, статус — в http.response.status_code.
  • Фільтри (статика, ignorePaths, ignoreUserAgents) не міняють поведінку запиту: він обробляється як завжди, але в невибраному (sampled=0) контексті — без span'ів у нього, його внутрішніх викликів і бекенда (traceparent …-00; libwts3 з OTEL_TRACES_SLOW_MS все ж відправить свій повільний запит — без батька з цього боку). Потрібен parent-based семплер (типовий).
  • Вибірка «head + порятунок повільних» (slowMs > 0 і семплер вибирає < 100%):
    • частка з OTEL_TRACES_SAMPLER_ARG вибирається на старті root span'а за trace id, як у parentbased_traceidratio — traceparent …-01 іде в бекенд, трейс повний. Підтримуються always_on / always_off / traceidratio (з parentbased_ чи без — семплер завжди parent-based); інший семплер (jaeger_remote тощо) → warn otel_config_invalid, порятунок вимкнено;
    • решта пишеться в пам'ять (traceparent …-00) і буферизується до кінця свого локального root span'а — окремо для кожного запиту, навіть з одним trace id. Root тривав ≥ slowMs → експортуються він (sampling.kept_by: "slow") і всі його локальні дочірні span'и (/api/*, CLIENT span'и вихідних fetch з тривалістю); інакше буфер скидається. Так само працює OTEL_TRACES_SLOW_MS у libwts3;
    • span'и бекенда в такому трейсі є, лише якщо бекенд врятував їх сам: libwts3 на …-00 теж пише в пам'ять і відправляє запит, що тривав ≥ свого OTEL_TRACES_SLOW_MS (типово 1000). Повільний бекенд робить повільним і root тут, тож трейс зв'язний; швидкого бекенда в ньому немає;
    • вхідний traceparent …-01 — вибраний, …-00 — та сама логіка порятунку; фільтри (статика, ignorePaths, ignoreUserAgents) не пишуться взагалі;
    • буфер обмежено: 2000 трейсів у польоті, 512 span'ів на трейс.
  • ?force_otel=1 у запиті (як у libwts3) — трейс вибраний завжди: попри частку семплера, фільтри (ignorePaths, ignoreUserAgents, статика) і вхідний traceparent …-00. Позначка в контексті, тож вибрані й дочірні span'и (внутрішні /api/*, вихідні fetch), а бекенд отримує traceparent …-01 і пише свою частину повністю. Root — з sampling.kept_by: "force". Працює з always_* / traceidratio (з parentbased_ чи без); з іншим семплером (jaeger_remote) параметр ігнорується, otel_initialized пише force_otel: false. Параметр публічний: будь-хто може форсувати трейс — як і в libwts3.
  • Flush при зупинці: span'и батчуються до 5 с, на close provider робить shutdown.
  • Помилки експорту (diag) пишуться JSON-логером як otel_diag.

Хелпер (auto-import у Nitro або import … from 'hotelme-nuxt-logger/runtime/otel'), працює і з вимкненим трейсингом:

// X-Request-ID сторінки (явна подія або поточна) — для бекендів, коли OTel вимкнено
const headers = traceHeaders(event)

API

Logger методи

logger.trace(msg: string, details?: Record<string, any>): void
logger.debug(msg: string, details?: Record<string, any>): void
logger.info(msg: string, details?: Record<string, any>): void
logger.warn(msg: string, details?: Record<string, any>): void
logger.error(msg: string, details?: Record<string, any>): void
logger.fatal(msg: string, details?: Record<string, any>): void

Event Context

export default defineEventHandler((event) => {
  const reqid = event.context.reqid    // З X-Request-ID header
  const userId = event.context.userId   // UUID з cookie
})

Рівні для помилок сервера

Nitro error hook (модульна опція enableErrorLogging):

Що сталося msg level stack
createError({ statusCode: 4xx }), відсутній /_nuxt/* chunk, валідація request_error Warn ні
5xx або не-HTTP виняток у handler/middleware unhandled_error Error так
unhandledRejection / uncaughtException процесу unhandled_rejection / uncaught_exception Fatal так

4xx не є помилкою застосунку, тому не засмічує рівень Error; сам запит при цьому все одно логується request-middleware як stats / api request з кодом відповіді.

Формат логів

Поле Тип Опис
msg string "api request" для /api/*, "stats" для інших
level string Trace/Debug/Info/Warn/Error/Fatal
reqid string З header X-Request-ID або UUID
project string Назва проекту
log_type string "frontend"
pid number Process ID
ts string Timestamp з timezone
stage string "after" для завершених запитів
domain string Домен запиту
request string URL запиту
request_time string Час виконання в секундах
method string HTTP метод
response number HTTP статус код

Інтеграція з системами моніторингу

Легко інтегрується з:

  • ELK Stack (Elasticsearch, Logstash, Kibana)
  • Grafana Loki
  • Datadog
  • CloudWatch
  • Graylog
# Просто направ stdout в систему збору логів
node .output/server/index.mjs | your-log-collector

# Або в Docker
docker logs your-container | jq '.msg'

Приклади фільтрації

# Логи з конкретним reqid
npm run dev | jq 'select(.reqid == "abc123")'

# Тільки помилки
npm run dev | jq 'select(.level == "Error")'

# API запити
npm run dev | jq 'select(.msg == "api request")'

# Повільні запити (>1s)
npm run dev | jq 'select(.request_time and (.request_time | tonumber) > 1.0)'

Ліцензія

MIT

Підтримка

Для питань та багів: GitHub Issues

Related Packages

nitro-opentelemetry

Opentelemetry module for the server framework nitro. Compatible with Nuxt.

228,218
nuxt-otel-appinsights

Nuxt module to integrate OpenTelemetry (Azure Monitor) and Azure Application Ins...

3,161
@scayle/nuxt-opentelemetry

A Nuxt module for OpenTelemetry integration

292,491 25
nuxt-betterstack

Nuxt module for BetterStack logging - send logs from both SSR and client-side to...

3,642 2
@privyid/nuxt-csrf

Module CSRF for NUXT

26,088 9

Version History

Version Released Vue License
1.6.1 ^3.0 MIT
1.6.0 ^3.0 MIT
1.5.0 ^3.0 MIT
1.4.0 ^3.0 MIT
1.3.0 ^3.0 MIT