@adatechnology/logger 0.0.7 → 0.0.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adatechnology/logger",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
package/src/index.ts CHANGED
@@ -1,11 +1,14 @@
1
1
  export { LoggerModule } from "./logger.module";
2
- export { LOGGER_PROVIDER } from "./logger.token";
2
+ export { LOGGER_PROVIDER, LOGGER_CONFIG, HTTP_LOGGING_INTERCEPTOR } from "./logger.token";
3
3
  export type {
4
4
  LoggerProviderInterface,
5
5
  LogPayload,
6
6
  LoggerLevel,
7
7
  } from "./logger.interface";
8
8
  export { RequestContextMiddleware } from "./middleware/request-context.middleware";
9
+ export { HttpLoggingInterceptor } from "./interceptors/http-logging.interceptor";
10
+ export { HTTP_LOGGING_INTERCEPTOR_CONTEXT } from "./interceptors/http-logging.interceptor.constant";
11
+ export { ExcludeHttpLogging } from "./interceptors/exclude-http-logging.decorator";
9
12
  export { getContext, runWithContext } from "./context/async-context.service";
10
13
  export type { LoggerConfig } from "./logger.config";
11
14
  export { DEFAULT_LOGGER_CONFIG } from "./logger.config";
@@ -0,0 +1,6 @@
1
+ import { SetMetadata } from "@nestjs/common";
2
+
3
+ export const EXCLUDE_HTTP_LOGGING_KEY = "EXCLUDE_HTTP_LOGGING";
4
+
5
+ export const ExcludeHttpLogging = () =>
6
+ SetMetadata(EXCLUDE_HTTP_LOGGING_KEY, true);
@@ -0,0 +1,5 @@
1
+ export const HTTP_LOGGING_INTERCEPTOR_CONTEXT = {
2
+ INTERCEPT: "HttpLoggingInterceptor.intercept",
3
+ ON_RESPONSE: "HttpLoggingInterceptor.onResponse",
4
+ ON_ERROR: "HttpLoggingInterceptor.onError",
5
+ } as const;
@@ -0,0 +1,110 @@
1
+ import { CallHandler, ExecutionContext, Inject, Injectable, NestInterceptor, Optional } from "@nestjs/common";
2
+ import { Reflector } from "@nestjs/core";
3
+ import { Observable } from "rxjs";
4
+ import { tap } from "rxjs/operators";
5
+ import { LOGGER_PROVIDER, LOGGER_CONFIG } from "../logger.token";
6
+ import type { LoggerProviderInterface } from "../logger.interface";
7
+ import type { LoggerConfig } from "../logger.config";
8
+ import { HTTP_LOGGING_INTERCEPTOR_CONTEXT } from "./http-logging.interceptor.constant";
9
+ import { EXCLUDE_HTTP_LOGGING_KEY } from "./exclude-http-logging.decorator";
10
+
11
+ @Injectable()
12
+ export class HttpLoggingInterceptor implements NestInterceptor {
13
+ private readonly excludedPaths: string[];
14
+
15
+ constructor(
16
+ @Inject(LOGGER_PROVIDER)
17
+ private readonly logger: LoggerProviderInterface,
18
+ private readonly reflector: Reflector,
19
+ @Optional() @Inject(LOGGER_CONFIG)
20
+ config?: LoggerConfig,
21
+ ) {
22
+ this.excludedPaths = config?.interceptorExcludedPaths ?? [];
23
+ }
24
+
25
+ private isExcluded(url: string): boolean {
26
+ const normalized = url.replace(/^\/v\d+/, "");
27
+ return this.excludedPaths.some(
28
+ (path) => normalized === path || normalized.startsWith(path + "/"),
29
+ );
30
+ }
31
+
32
+ intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
33
+ const http = context.switchToHttp();
34
+ const request = http.getRequest<Record<string, any>>();
35
+ const response = http.getResponse<Record<string, any>>();
36
+ const start = Date.now();
37
+
38
+ const isDecoratorExcluded = this.reflector.getAllAndOverride<boolean>(
39
+ EXCLUDE_HTTP_LOGGING_KEY,
40
+ [context.getHandler(), context.getClass()],
41
+ );
42
+
43
+ if (isDecoratorExcluded || this.isExcluded(request.url)) {
44
+ return next.handle();
45
+ }
46
+
47
+ const { method, url, headers, body, query, params } = request;
48
+
49
+ this.logger.info({
50
+ message: "HTTP Request",
51
+ context: HTTP_LOGGING_INTERCEPTOR_CONTEXT.INTERCEPT,
52
+ meta: {
53
+ request: {
54
+ method,
55
+ path: url,
56
+ headers,
57
+ query: query && Object.keys(query).length ? query : undefined,
58
+ params: params && Object.keys(params).length ? params : undefined,
59
+ body: body && Object.keys(body).length ? body : undefined,
60
+ },
61
+ },
62
+ });
63
+
64
+ return next.handle().pipe(
65
+ tap({
66
+ next: (responseBody) => {
67
+ const durationMs = Date.now() - start;
68
+ const statusCode =
69
+ response?.statusCode ??
70
+ response?.raw?.statusCode ??
71
+ 200;
72
+
73
+ this.logger.info({
74
+ message: "HTTP Response",
75
+ context: HTTP_LOGGING_INTERCEPTOR_CONTEXT.ON_RESPONSE,
76
+ meta: {
77
+ request: {
78
+ method,
79
+ path: url,
80
+ },
81
+ response: {
82
+ statusCode,
83
+ durationMs,
84
+ body: responseBody ?? undefined,
85
+ },
86
+ },
87
+ });
88
+ },
89
+ error: (error: unknown) => {
90
+ const durationMs = Date.now() - start;
91
+
92
+ this.logger.error({
93
+ message: "HTTP Response Error",
94
+ context: HTTP_LOGGING_INTERCEPTOR_CONTEXT.ON_ERROR,
95
+ meta: {
96
+ request: {
97
+ method,
98
+ path: url,
99
+ },
100
+ response: {
101
+ durationMs,
102
+ error: error instanceof Error ? error.message : String(error),
103
+ },
104
+ },
105
+ });
106
+ },
107
+ }),
108
+ );
109
+ }
110
+ }
@@ -46,6 +46,12 @@ export interface LoggerConfig extends WinstonModuleConfig {
46
46
  * Versão da biblioteca/módulo que está gerando o log
47
47
  */
48
48
  libVersion?: string;
49
+
50
+ /**
51
+ * Rotas excluídas do HttpLoggingInterceptor (ex.: ['/health'])
52
+ * Suporta prefixo exato ou parcial via startsWith
53
+ */
54
+ interceptorExcludedPaths?: string[];
49
55
  }
50
56
 
51
57
  export const DEFAULT_LOGGER_CONFIG: LoggerConfig = {
@@ -1,27 +1,37 @@
1
1
  import { DynamicModule, Module, Scope, Provider, Global } from "@nestjs/common";
2
2
  import { LoggerProvider } from "./logger.provider";
3
- import { LOGGER_PROVIDER } from "./logger.token";
3
+ import { LOGGER_PROVIDER, LOGGER_CONFIG, HTTP_LOGGING_INTERCEPTOR } from "./logger.token";
4
4
  import { WinstonImplementationModule } from "./implementations/winston/winston.logger.module";
5
+ import { HttpLoggingInterceptor } from "./interceptors/http-logging.interceptor";
5
6
  import type { LoggerConfig } from "./logger.config";
6
7
 
8
+ const httpLoggingInterceptorProvider: Provider = {
9
+ provide: HTTP_LOGGING_INTERCEPTOR,
10
+ useClass: HttpLoggingInterceptor,
11
+ };
12
+
7
13
  @Global()
8
14
  @Module({})
9
15
  export class LoggerModule {
10
16
  static forRoot(config?: LoggerConfig): DynamicModule {
11
17
  const implModule = WinstonImplementationModule.forRoot(config);
12
- const provider: Provider = {
18
+ const loggerProvider: Provider = {
13
19
  provide: LOGGER_PROVIDER,
14
20
  useClass: LoggerProvider,
15
21
  };
16
22
  if (config && config.requestScoped) {
17
- provider.scope = Scope.REQUEST;
23
+ loggerProvider.scope = Scope.REQUEST;
18
24
  }
19
25
 
20
26
  return {
21
27
  module: LoggerModule,
22
28
  imports: [implModule],
23
- providers: [provider],
24
- exports: [LOGGER_PROVIDER],
29
+ providers: [
30
+ { provide: LOGGER_CONFIG, useValue: config ?? {} },
31
+ loggerProvider,
32
+ httpLoggingInterceptorProvider,
33
+ ],
34
+ exports: [LOGGER_PROVIDER, LOGGER_CONFIG, HTTP_LOGGING_INTERCEPTOR],
25
35
  };
26
36
  }
27
37
 
@@ -37,12 +47,18 @@ export class LoggerModule {
37
47
  WinstonImplementationModule.forRootAsync(options),
38
48
  ],
39
49
  providers: [
50
+ {
51
+ provide: LOGGER_CONFIG,
52
+ useFactory: options.useFactory,
53
+ inject: options.inject || [],
54
+ },
40
55
  {
41
56
  provide: LOGGER_PROVIDER,
42
57
  useClass: LoggerProvider,
43
58
  },
59
+ httpLoggingInterceptorProvider,
44
60
  ],
45
- exports: [LOGGER_PROVIDER],
61
+ exports: [LOGGER_PROVIDER, LOGGER_CONFIG, HTTP_LOGGING_INTERCEPTOR],
46
62
  };
47
63
  }
48
64
  }
@@ -1 +1,3 @@
1
1
  export const LOGGER_PROVIDER = "LOGGER_PROVIDER";
2
+ export const LOGGER_CONFIG = "LOGGER_CONFIG";
3
+ export const HTTP_LOGGING_INTERCEPTOR = "HTTP_LOGGING_INTERCEPTOR";