hotelme-nuxt-logger 1.0.4 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/module.cjs CHANGED
@@ -14,12 +14,17 @@ const module$1 = kit.defineNuxtModule({
14
14
  }
15
15
  },
16
16
  defaults: {
17
- enableRequestLogging: true
17
+ enableRequestLogging: true,
18
+ enableErrorLogging: true,
19
+ enableClientErrorLogging: true,
20
+ clientErrorEndpoint: "/_logger/client-error"
18
21
  },
19
22
  setup(options, nuxt) {
20
23
  const moduleDir = typeof __dirname !== "undefined" ? __dirname : path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('module.cjs', document.baseURI).href))));
21
24
  const resolver = kit.createResolver(moduleDir);
22
25
  nuxt.options.runtimeConfig.public.projectName = options.projectName || process.env.NUXT_PUBLIC_PROJECT_NAME || "hotelme";
26
+ const clientErrorEndpoint = options.clientErrorEndpoint || "/_logger/client-error";
27
+ nuxt.options.runtimeConfig.public.loggerClientErrorEndpoint = clientErrorEndpoint;
23
28
  kit.addImportsDir(resolver.resolve("./runtime"));
24
29
  if (options.enableRequestLogging) {
25
30
  kit.addServerHandler({
@@ -27,6 +32,20 @@ const module$1 = kit.defineNuxtModule({
27
32
  middleware: true
28
33
  });
29
34
  }
35
+ if (options.enableErrorLogging) {
36
+ kit.addServerPlugin(resolver.resolve("./runtime/server/plugin"));
37
+ }
38
+ if (options.enableClientErrorLogging) {
39
+ kit.addPlugin({
40
+ src: resolver.resolve("./runtime/plugins/error"),
41
+ mode: "all"
42
+ });
43
+ kit.addServerHandler({
44
+ route: clientErrorEndpoint,
45
+ method: "post",
46
+ handler: resolver.resolve("./runtime/server/routes/client-error.post")
47
+ });
48
+ }
30
49
  }
31
50
  });
32
51
 
package/dist/module.d.cts CHANGED
@@ -9,6 +9,24 @@ interface ModuleOptions {
9
9
  * @default true
10
10
  */
11
11
  enableRequestLogging?: boolean;
12
+ /**
13
+ * Глобальний відлов помилок на сервері: Nitro `error` hook,
14
+ * `unhandledRejection` та `uncaughtException`.
15
+ * @default true
16
+ */
17
+ enableErrorLogging?: boolean;
18
+ /**
19
+ * Vue / Nuxt error hooks (SSR + браузер) та серверна ручка
20
+ * `/_logger/client-error`, через яку клієнтські помилки потрапляють
21
+ * у той самий stdout, що й серверні.
22
+ * @default true
23
+ */
24
+ enableClientErrorLogging?: boolean;
25
+ /**
26
+ * Шлях ручки, на яку браузерний плагін шле звіти про помилки.
27
+ * @default '/_logger/client-error'
28
+ */
29
+ clientErrorEndpoint?: string;
12
30
  }
13
31
  declare const _default: any;
14
32
 
package/dist/module.d.mts CHANGED
@@ -9,6 +9,24 @@ interface ModuleOptions {
9
9
  * @default true
10
10
  */
11
11
  enableRequestLogging?: boolean;
12
+ /**
13
+ * Глобальний відлов помилок на сервері: Nitro `error` hook,
14
+ * `unhandledRejection` та `uncaughtException`.
15
+ * @default true
16
+ */
17
+ enableErrorLogging?: boolean;
18
+ /**
19
+ * Vue / Nuxt error hooks (SSR + браузер) та серверна ручка
20
+ * `/_logger/client-error`, через яку клієнтські помилки потрапляють
21
+ * у той самий stdout, що й серверні.
22
+ * @default true
23
+ */
24
+ enableClientErrorLogging?: boolean;
25
+ /**
26
+ * Шлях ручки, на яку браузерний плагін шле звіти про помилки.
27
+ * @default '/_logger/client-error'
28
+ */
29
+ clientErrorEndpoint?: string;
12
30
  }
13
31
  declare const _default: any;
14
32
 
package/dist/module.d.ts CHANGED
@@ -9,6 +9,24 @@ interface ModuleOptions {
9
9
  * @default true
10
10
  */
11
11
  enableRequestLogging?: boolean;
12
+ /**
13
+ * Глобальний відлов помилок на сервері: Nitro `error` hook,
14
+ * `unhandledRejection` та `uncaughtException`.
15
+ * @default true
16
+ */
17
+ enableErrorLogging?: boolean;
18
+ /**
19
+ * Vue / Nuxt error hooks (SSR + браузер) та серверна ручка
20
+ * `/_logger/client-error`, через яку клієнтські помилки потрапляють
21
+ * у той самий stdout, що й серверні.
22
+ * @default true
23
+ */
24
+ enableClientErrorLogging?: boolean;
25
+ /**
26
+ * Шлях ручки, на яку браузерний плагін шле звіти про помилки.
27
+ * @default '/_logger/client-error'
28
+ */
29
+ clientErrorEndpoint?: string;
12
30
  }
13
31
  declare const _default: any;
14
32
 
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": "^3.0.0 || ^4.0.0"
6
6
  },
7
- "version": "1.0.4",
7
+ "version": "1.1.0",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "0.8.4",
10
10
  "unbuild": "2.0.0"
package/dist/module.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { defineNuxtModule, createResolver, addImportsDir, addServerHandler } from '@nuxt/kit';
1
+ import { defineNuxtModule, createResolver, addImportsDir, addServerHandler, addServerPlugin, addPlugin } from '@nuxt/kit';
2
2
  import { fileURLToPath } from 'url';
3
3
  import { dirname } from 'path';
4
4
 
@@ -20,12 +20,17 @@ const module = defineNuxtModule({
20
20
  }
21
21
  },
22
22
  defaults: {
23
- enableRequestLogging: true
23
+ enableRequestLogging: true,
24
+ enableErrorLogging: true,
25
+ enableClientErrorLogging: true,
26
+ clientErrorEndpoint: "/_logger/client-error"
24
27
  },
25
28
  setup(options, nuxt) {
26
29
  const moduleDir = typeof __dirname !== "undefined" ? __dirname : dirname(fileURLToPath(import.meta.url));
27
30
  const resolver = createResolver(moduleDir);
28
31
  nuxt.options.runtimeConfig.public.projectName = options.projectName || process.env.NUXT_PUBLIC_PROJECT_NAME || "hotelme";
32
+ const clientErrorEndpoint = options.clientErrorEndpoint || "/_logger/client-error";
33
+ nuxt.options.runtimeConfig.public.loggerClientErrorEndpoint = clientErrorEndpoint;
29
34
  addImportsDir(resolver.resolve("./runtime"));
30
35
  if (options.enableRequestLogging) {
31
36
  addServerHandler({
@@ -33,6 +38,20 @@ const module = defineNuxtModule({
33
38
  middleware: true
34
39
  });
35
40
  }
41
+ if (options.enableErrorLogging) {
42
+ addServerPlugin(resolver.resolve("./runtime/server/plugin"));
43
+ }
44
+ if (options.enableClientErrorLogging) {
45
+ addPlugin({
46
+ src: resolver.resolve("./runtime/plugins/error"),
47
+ mode: "all"
48
+ });
49
+ addServerHandler({
50
+ route: clientErrorEndpoint,
51
+ method: "post",
52
+ handler: resolver.resolve("./runtime/server/routes/client-error.post")
53
+ });
54
+ }
36
55
  }
37
56
  });
38
57
 
@@ -1,4 +1,5 @@
1
1
  import { defineEventHandler, getRequestURL } from "h3";
2
+ import { useRuntimeConfig } from "#imports";
2
3
  import { logger } from "../logger.js";
3
4
  import { v4 as uuidv4 } from "uuid";
4
5
  export default defineEventHandler(async (event) => {
@@ -6,7 +7,8 @@ export default defineEventHandler(async (event) => {
6
7
  return;
7
8
  }
8
9
  try {
9
- const projectName = event.context.$config?.public?.projectName || "hotelme";
10
+ const config = useRuntimeConfig();
11
+ const projectName = config.public.projectName || "hotelme";
10
12
  let reqid = event.node.req.headers["x-request-id"];
11
13
  if (!reqid) {
12
14
  reqid = uuidv4().replace(/-/g, "");
@@ -27,7 +29,8 @@ export default defineEventHandler(async (event) => {
27
29
  const statusCode = res.statusCode;
28
30
  const isApiRequest = fullUrl.startsWith("/api/");
29
31
  const msg = isApiRequest ? "api request" : "stats";
30
- logger.info(msg, {
32
+ const level = statusCode >= 500 ? "error" : statusCode >= 400 ? "warn" : "info";
33
+ logger[level](msg, {
31
34
  reqid,
32
35
  project: projectName,
33
36
  stage: "after",
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Captures errors that the Nitro `error` hook does not see:
3
+ * - SSR Vue render errors (`vue:error`, `app:error`) — logged directly through the JSON logger.
4
+ * - Browser Vue/JS errors and unhandled promise rejections — POSTed to `/_logger/client-error`,
5
+ * which then re-emits them through the same logger on the server.
6
+ */
7
+ declare const _default: any;
8
+ export default _default;
@@ -0,0 +1,73 @@
1
+ import { defineNuxtPlugin, useRequestEvent, useRuntimeConfig } from "#imports";
2
+ import { logger } from "../logger.js";
3
+ export default defineNuxtPlugin((nuxtApp) => {
4
+ if (import.meta.server) {
5
+ const event = useRequestEvent();
6
+ const reqid = event?.context?.reqid;
7
+ const request = event?.path;
8
+ nuxtApp.hook("vue:error", (err, _instance, info) => {
9
+ logger.error("vue_ssr_error", {
10
+ reqid,
11
+ request,
12
+ info,
13
+ error: err?.message ?? String(err),
14
+ error_name: err?.name,
15
+ stack: err?.stack
16
+ });
17
+ });
18
+ nuxtApp.hook("app:error", (err) => {
19
+ logger.error("app_ssr_error", {
20
+ reqid,
21
+ request,
22
+ error: err?.message ?? String(err),
23
+ error_name: err?.name,
24
+ stack: err?.stack
25
+ });
26
+ });
27
+ return;
28
+ }
29
+ const endpoint = useRuntimeConfig().public.loggerClientErrorEndpoint || "/_logger/client-error";
30
+ const post = (msg, payload) => {
31
+ try {
32
+ fetch(endpoint, {
33
+ method: "POST",
34
+ headers: { "content-type": "application/json" },
35
+ body: JSON.stringify({
36
+ msg,
37
+ page: typeof location !== "undefined" ? location.pathname + location.search : void 0,
38
+ ...payload
39
+ }),
40
+ keepalive: true
41
+ }).catch(() => {
42
+ });
43
+ } catch {
44
+ }
45
+ };
46
+ nuxtApp.hook("vue:error", (err, _i, info) => {
47
+ post("vue_client_error", {
48
+ info,
49
+ error: err?.message ?? String(err),
50
+ error_name: err?.name,
51
+ stack: err?.stack
52
+ });
53
+ });
54
+ window.addEventListener("error", (e) => {
55
+ const err = e.error;
56
+ post("window_error", {
57
+ error: err?.message ?? e.message,
58
+ error_name: err?.name,
59
+ stack: err?.stack,
60
+ file: e.filename,
61
+ line: e.lineno,
62
+ col: e.colno
63
+ });
64
+ });
65
+ window.addEventListener("unhandledrejection", (e) => {
66
+ const reason = e.reason;
67
+ post("window_unhandled_rejection", {
68
+ error: reason?.message ?? String(reason),
69
+ error_name: reason?.name,
70
+ stack: reason?.stack
71
+ });
72
+ });
73
+ });
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Nitro plugin that turns every unhandled error into a structured JSON log line.
3
+ *
4
+ * Sources covered:
5
+ * - All errors thrown from server middleware / event handlers (`nitroApp.hooks.hook('error')`).
6
+ * - `unhandledRejection` and `uncaughtException` at the Node process level.
7
+ *
8
+ * `reqid` is read from `event.context.reqid` set by `logger.server.ts` middleware,
9
+ * so SSR and backend logs stay correlated by the same id.
10
+ */
11
+ declare const _default: import("nitropack").NitroAppPlugin;
12
+ export default _default;
@@ -0,0 +1,36 @@
1
+ import { defineNitroPlugin } from "nitropack/runtime";
2
+ import { logger } from "../logger.js";
3
+ export default defineNitroPlugin((nitroApp) => {
4
+ nitroApp.hooks.hook("error", (error, ctx) => {
5
+ const event = ctx?.event;
6
+ const req = event?.node?.req;
7
+ const res = event?.node?.res;
8
+ logger.error("unhandled_error", {
9
+ reqid: event?.context?.reqid,
10
+ domain: req?.headers?.host,
11
+ request: event?.path || req?.url,
12
+ method: req?.method,
13
+ response: res?.statusCode,
14
+ error: error instanceof Error ? error.message : String(error),
15
+ error_name: error instanceof Error ? error.name : void 0,
16
+ stack: error instanceof Error ? error.stack : void 0
17
+ });
18
+ });
19
+ const g = globalThis;
20
+ if (g.__hotelmeLoggerProcessHooks) return;
21
+ g.__hotelmeLoggerProcessHooks = true;
22
+ process.on("unhandledRejection", (reason) => {
23
+ logger.fatal("unhandled_rejection", {
24
+ error: reason instanceof Error ? reason.message : String(reason),
25
+ error_name: reason instanceof Error ? reason.name : void 0,
26
+ stack: reason instanceof Error ? reason.stack : void 0
27
+ });
28
+ });
29
+ process.on("uncaughtException", (err) => {
30
+ logger.fatal("uncaught_exception", {
31
+ error: err.message,
32
+ error_name: err.name,
33
+ stack: err.stack
34
+ });
35
+ });
36
+ });
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Receives error reports from the browser plugin and emits them through the
3
+ * same JSON logger, so server stdout becomes the single sink for all errors.
4
+ */
5
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
6
+ ok: boolean;
7
+ }>>;
8
+ export default _default;
@@ -0,0 +1,14 @@
1
+ import { defineEventHandler, readBody } from "h3";
2
+ import { logger } from "../../logger.js";
3
+ export default defineEventHandler(async (event) => {
4
+ const body = await readBody(event).catch(() => ({}));
5
+ const { msg, ...rest } = body || {};
6
+ logger.error(typeof msg === "string" && msg ? msg : "client_error", {
7
+ reqid: event.context.reqid,
8
+ domain: event.node.req.headers.host,
9
+ referer: event.node.req.headers.referer,
10
+ user_agent: event.node.req.headers["user-agent"],
11
+ ...rest
12
+ });
13
+ return { ok: true };
14
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hotelme-nuxt-logger",
3
- "version": "1.0.4",
3
+ "version": "1.1.0",
4
4
  "description": "Стандартний logger для Nuxt проектів з JSON виводом сумісним з бекенд-логами",
5
5
  "keywords": [
6
6
  "nuxt",