hotelme-nuxt-logger
| Install | |
|---|---|
npm install hotelme-nuxt-logger |
|
| Latest Version: | 1.6.1 |
| Vue: | 3 |
| License: | MIT |
| Last Updated: | Oct 22, 2026 |
| Links: | npm |
@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 spanGET <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тощо) → warnotel_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 с, на
closeprovider робить 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
Opentelemetry module for the server framework nitro. Compatible with Nuxt.
Nuxt module to integrate OpenTelemetry (Azure Monitor) and Azure Application Ins...
Nuxt module for BetterStack logging - send logs from both SSR and client-side to...
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 | |
| 1.2.1 | ^3.0 | MIT | |
| 1.2.0 | ^3.0 | MIT | |
| 1.1.1 | ^3.0 | MIT | |
| 1.1.0 | ^3.0 | MIT | |
| 1.0.5 | ^3.0 | MIT | |
| 1.0.4 | ^3.0 | MIT | |
| 1.0.3 | ^3.0 | MIT | |
| 1.0.2 | ^3.0 | MIT | |
| 1.0.1 | ^3.0 | MIT | |
| 1.0.0 | ^3.0 | MIT |