@application-kit/observability 0.0.0 → 0.2.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.
Files changed (35) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +41 -4
  3. package/dist/cjs/cqrs/constants.d.ts +1 -0
  4. package/dist/cjs/cqrs/constants.js +4 -0
  5. package/dist/cjs/cqrs/cqrs-logger.d.ts +12 -0
  6. package/dist/cjs/cqrs/cqrs-logger.js +72 -0
  7. package/dist/cjs/cqrs/cqrs-logger.module.d.ts +2 -0
  8. package/dist/cjs/cqrs/cqrs-logger.module.js +22 -0
  9. package/dist/cjs/cqrs/index.d.ts +4 -0
  10. package/dist/cjs/cqrs/index.js +20 -0
  11. package/dist/cjs/cqrs/observability-logger.interface.d.ts +4 -0
  12. package/dist/cjs/cqrs/observability-logger.interface.js +2 -0
  13. package/dist/cjs/http/client-ip-options.interface.d.ts +3 -0
  14. package/dist/cjs/http/client-ip-options.interface.js +2 -0
  15. package/dist/cjs/http/index.d.ts +2 -0
  16. package/dist/cjs/http/index.js +18 -0
  17. package/dist/cjs/http/resolve-client-ip.d.ts +14 -0
  18. package/dist/cjs/http/resolve-client-ip.js +22 -0
  19. package/dist/esm/cqrs/constants.d.ts +1 -0
  20. package/dist/esm/cqrs/constants.js +1 -0
  21. package/dist/esm/cqrs/cqrs-logger.d.ts +12 -0
  22. package/dist/esm/cqrs/cqrs-logger.js +69 -0
  23. package/dist/esm/cqrs/cqrs-logger.module.d.ts +2 -0
  24. package/dist/esm/cqrs/cqrs-logger.module.js +19 -0
  25. package/dist/esm/cqrs/index.d.ts +4 -0
  26. package/dist/esm/cqrs/index.js +4 -0
  27. package/dist/esm/cqrs/observability-logger.interface.d.ts +4 -0
  28. package/dist/esm/cqrs/observability-logger.interface.js +1 -0
  29. package/dist/esm/http/client-ip-options.interface.d.ts +3 -0
  30. package/dist/esm/http/client-ip-options.interface.js +1 -0
  31. package/dist/esm/http/index.d.ts +2 -0
  32. package/dist/esm/http/index.js +2 -0
  33. package/dist/esm/http/resolve-client-ip.d.ts +14 -0
  34. package/dist/esm/http/resolve-client-ip.js +18 -0
  35. package/package.json +32 -3
package/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # @application-kit/observability
2
+
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 06c0229: Вход `/cqrs`: `CqrsLoggerModule` и `CqrsLogger` — старт, длительность и исход каждой команды и запроса на шинах
8
+ `@nestjs/cqrs` (имя, а не полезная нагрузка); логгер приходит токеном `OBSERVABILITY_LOGGER` по контракту
9
+ `ObservabilityLoggerInterface`, `@nestjs/cqrs` — optional peer. Вход `/http`: `resolveClientIp(request, { trustedHeaders })` —
10
+ реальный IP клиента за прокси: доверенные заголовки по порядку, затем `request.ip` и адрес сокета; по умолчанию заголовкам
11
+ не доверяет.
12
+
13
+ ## 0.1.0
14
+
15
+ ### Minor Changes
16
+
17
+ - 2f8c709: Первый выпуск: вход `/metrics` — `MetricsRegistry` (реестр Prometheus с метриками процесса Node), RED-гистограммы
18
+ `HttpMetrics` (ярлык маршрута — шаблон пути, `resolveRouteLabel`) и `GraphqlMetrics` (ярлык operation — корневые поля
19
+ документа, адаптер хуков `asMercuriusHook`), `startMetricsServer` — HTTP-сервер `/metrics` для процессов без веб-слоя.
20
+ Peer `graphql` — optional: из него берутся только типы, нужен лишь GraphQL-гистограмме.
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # @application-kit/observability
2
2
 
3
- Наблюдаемость NestJS-процесса. Первый вход — `/metrics`: реестр Prometheus с метриками процесса Node, RED-гистограммы
4
- HTTP и GraphQL и HTTP-сервер экспозиции для процессов без своего веб-слоя. Пакет не знает ни маршрутов, ни схемы,
3
+ Наблюдаемость NestJS-процесса: метрики Prometheus (`/metrics`), логирование команд и запросов CQRS-шин (`/cqrs`)
4
+ и реальный IP клиента за прокси (`/http`). Пакет не знает ни маршрутов, ни схемы,
5
5
  ни портов приложения: ярлыки и порт приходят параметрами, а хуки веб-сервера и контроллер скрейпа остаются у потребителя.
6
6
 
7
7
  ## Установка
@@ -10,13 +10,15 @@ HTTP и GraphQL и HTTP-сервер экспозиции для процесс
10
10
  yarn add @application-kit/observability
11
11
  ```
12
12
 
13
- Peer-зависимости: `@nestjs/common`, `@prometheus-io/client`; `graphql` — optional, нужен только GraphQL-гистограмме
14
- (из него берутся типы документа, сам вход грузится и без него).
13
+ Peer-зависимости: `@nestjs/common`, `@prometheus-io/client`; optional — `graphql` (нужен только GraphQL-гистограмме,
14
+ из него берутся типы документа) и `@nestjs/cqrs` (нужен только входу `/cqrs`).
15
15
 
16
16
  ## Входы
17
17
 
18
18
  | Вход | Для чего | Тянет |
19
19
  |------|----------|-------|
20
+ | `@application-kit/observability/cqrs` | логирование команд и запросов обеих шин | `@nestjs/common`, `@nestjs/cqrs` |
21
+ | `@application-kit/observability/http` | `resolveClientIp` — IP клиента за прокси | ничего |
20
22
  | `@application-kit/observability/metrics` | реестр, гистограммы HTTP и GraphQL, сервер `/metrics` | `@nestjs/common`, `@prometheus-io/client` |
21
23
 
22
24
  Корневого входа нет: каждый вход несёт свои peer-зависимости, и лишнего потребитель не ставит.
@@ -56,3 +58,38 @@ app.register(async (instance) => {
56
58
  ```ts
57
59
  const server = startMetricsServer({ onError: (error) => logger.error(error), port: 9464, registry })
58
60
  ```
61
+
62
+ ## Логирование CQRS-шин
63
+
64
+ `CqrsLoggerModule` подключается корневым модулем процесса один раз. На каждую команду и запрос — запись о старте
65
+ (подпиской на шину: видит все отправки) и о завершении с `durationMs` и `outcome` (обёрткой `execute` экземпляра шины:
66
+ длительность и исход шина не публикует). В лог идёт ИМЯ класса, не полезная нагрузка; ошибка хендлера пишется на `error`
67
+ и пробрасывается дальше.
68
+
69
+ Логгер приложение приносит глобально токеном `OBSERVABILITY_LOGGER` — подходит любой логгер с `info(payload, message)`
70
+ и `error(payload, message)`, например `PinoLogger` из `nestjs-pino`:
71
+
72
+ ```ts
73
+ @Global()
74
+ @Module({
75
+ exports: [OBSERVABILITY_LOGGER],
76
+ providers: [AppLogger, { provide: OBSERVABILITY_LOGGER, useExisting: AppLogger }],
77
+ })
78
+ export class CommonModule {}
79
+
80
+ @Module({ imports: [CommonModule, CqrsLoggerModule] })
81
+ export class AppModule {}
82
+ ```
83
+
84
+ ## IP клиента за прокси
85
+
86
+ `resolveClientIp(request, { trustedHeaders })` принимает `IncomingMessage`, `FastifyRequest` или его обёртку из
87
+ сериалайзера pino. Порядок: доверенные заголовки по порядку списка (первый непустой), затем `request.ip` (у Fastify —
88
+ с учётом `trustProxy`), затем адрес сокета; источника нет — пустая строка.
89
+
90
+ По умолчанию заголовкам не доверяет: в список кладётся только заголовок, который прокси перед процессом ставит и
91
+ перезаписывает сам, — иначе клиент подставит в логи любой адрес.
92
+
93
+ ```ts
94
+ const clientIp = resolveClientIp(request, { trustedHeaders: ['cf-connecting-ip'] })
95
+ ```
@@ -0,0 +1 @@
1
+ export declare const OBSERVABILITY_LOGGER: unique symbol;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OBSERVABILITY_LOGGER = void 0;
4
+ exports.OBSERVABILITY_LOGGER = Symbol('OBSERVABILITY_LOGGER');
@@ -0,0 +1,12 @@
1
+ import { OnApplicationBootstrap } from '@nestjs/common';
2
+ import { CommandBus, QueryBus } from '@nestjs/cqrs';
3
+ import type { ObservabilityLoggerInterface } from './observability-logger.interface.js';
4
+ export declare class CqrsLogger implements OnApplicationBootstrap {
5
+ private readonly commandBus;
6
+ private readonly queryBus;
7
+ private readonly logger;
8
+ private instrumented;
9
+ constructor(commandBus: CommandBus, queryBus: QueryBus, logger: ObservabilityLoggerInterface);
10
+ onApplicationBootstrap(): void;
11
+ private instrumentCompletion;
12
+ }
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.CqrsLogger = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const cqrs_1 = require("@nestjs/cqrs");
18
+ const node_perf_hooks_1 = require("node:perf_hooks");
19
+ const constants_js_1 = require("./constants.js");
20
+ const KIND_COMMAND = 'command';
21
+ const KIND_QUERY = 'query';
22
+ let CqrsLogger = class CqrsLogger {
23
+ commandBus;
24
+ queryBus;
25
+ logger;
26
+ instrumented = false;
27
+ constructor(commandBus, queryBus, logger) {
28
+ this.commandBus = commandBus;
29
+ this.queryBus = queryBus;
30
+ this.logger = logger;
31
+ }
32
+ onApplicationBootstrap() {
33
+ if (this.instrumented) {
34
+ return;
35
+ }
36
+ this.instrumented = true;
37
+ this.commandBus.subscribe((command) => {
38
+ this.logger.info({ [KIND_COMMAND]: command.constructor.name }, 'command started');
39
+ });
40
+ this.queryBus.subscribe((query) => {
41
+ this.logger.info({ [KIND_QUERY]: query.constructor.name }, 'query started');
42
+ });
43
+ this.instrumentCompletion(this.commandBus, KIND_COMMAND);
44
+ this.instrumentCompletion(this.queryBus, KIND_QUERY);
45
+ }
46
+ instrumentCompletion(bus, kind) {
47
+ const target = bus;
48
+ const original = target.execute.bind(target);
49
+ target.execute = async (payload, context) => {
50
+ const startedAt = node_perf_hooks_1.performance.now();
51
+ const { name } = payload.constructor;
52
+ try {
53
+ const result = await original(payload, context);
54
+ this.logger.info({ durationMs: node_perf_hooks_1.performance.now() - startedAt, [kind]: name, outcome: 'ok' }, `${kind} finished`);
55
+ return result;
56
+ }
57
+ catch (error) {
58
+ this.logger.error({ durationMs: node_perf_hooks_1.performance.now() - startedAt, err: error, [kind]: name, outcome: 'error' }, `${kind} failed`);
59
+ throw error;
60
+ }
61
+ };
62
+ }
63
+ };
64
+ exports.CqrsLogger = CqrsLogger;
65
+ exports.CqrsLogger = CqrsLogger = __decorate([
66
+ (0, common_1.Injectable)(),
67
+ __param(0, (0, common_1.Inject)(cqrs_1.CommandBus)),
68
+ __param(1, (0, common_1.Inject)(cqrs_1.QueryBus)),
69
+ __param(2, (0, common_1.Inject)(constants_js_1.OBSERVABILITY_LOGGER)),
70
+ __metadata("design:paramtypes", [cqrs_1.CommandBus,
71
+ cqrs_1.QueryBus, Object])
72
+ ], CqrsLogger);
@@ -0,0 +1,2 @@
1
+ export declare class CqrsLoggerModule {
2
+ }
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.CqrsLoggerModule = void 0;
10
+ const common_1 = require("@nestjs/common");
11
+ const cqrs_1 = require("@nestjs/cqrs");
12
+ const cqrs_logger_js_1 = require("./cqrs-logger.js");
13
+ let CqrsLoggerModule = class CqrsLoggerModule {
14
+ };
15
+ exports.CqrsLoggerModule = CqrsLoggerModule;
16
+ exports.CqrsLoggerModule = CqrsLoggerModule = __decorate([
17
+ (0, common_1.Module)({
18
+ exports: [cqrs_logger_js_1.CqrsLogger],
19
+ imports: [cqrs_1.CqrsModule],
20
+ providers: [cqrs_logger_js_1.CqrsLogger],
21
+ })
22
+ ], CqrsLoggerModule);
@@ -0,0 +1,4 @@
1
+ export * from './constants.js';
2
+ export * from './cqrs-logger.js';
3
+ export * from './cqrs-logger.module.js';
4
+ export * from './observability-logger.interface.js';
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./constants.js"), exports);
18
+ __exportStar(require("./cqrs-logger.js"), exports);
19
+ __exportStar(require("./cqrs-logger.module.js"), exports);
20
+ __exportStar(require("./observability-logger.interface.js"), exports);
@@ -0,0 +1,4 @@
1
+ export interface ObservabilityLoggerInterface {
2
+ error(payload: object, message: string): void;
3
+ info(payload: object, message: string): void;
4
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,3 @@
1
+ export interface ClientIpOptionsInterface {
2
+ readonly trustedHeaders?: readonly string[];
3
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ export * from './client-ip-options.interface.js';
2
+ export * from './resolve-client-ip.js';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./client-ip-options.interface.js"), exports);
18
+ __exportStar(require("./resolve-client-ip.js"), exports);
@@ -0,0 +1,14 @@
1
+ import type { ClientIpOptionsInterface } from './client-ip-options.interface.js';
2
+ export interface ClientIpSourceInterface {
3
+ headers?: Record<string, string | string[] | undefined> | undefined;
4
+ ip?: string | undefined;
5
+ raw?: {
6
+ socket?: SocketLikeInterface | undefined;
7
+ } | undefined;
8
+ socket?: SocketLikeInterface | undefined;
9
+ }
10
+ interface SocketLikeInterface {
11
+ remoteAddress?: string | undefined;
12
+ }
13
+ export declare const resolveClientIp: (request: ClientIpSourceInterface, options?: ClientIpOptionsInterface) => string;
14
+ export {};
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveClientIp = void 0;
4
+ const firstHeaderValue = (value) => {
5
+ const single = Array.isArray(value) ? value[0] : value;
6
+ return (single ?? '').trim();
7
+ };
8
+ const trustedHeaderIp = (request, trustedHeaders) => {
9
+ const values = trustedHeaders?.map((header) => firstHeaderValue(request.headers?.[header]));
10
+ return values?.find((value) => value !== '') ?? '';
11
+ };
12
+ const rawSocketIp = (request) => request.raw?.socket?.remoteAddress ?? '';
13
+ const socketIp = (request) => request.socket?.remoteAddress ?? rawSocketIp(request);
14
+ const fallbackIp = (request) => {
15
+ const proxyAwareIp = (request.ip ?? '').trim();
16
+ return proxyAwareIp === '' ? socketIp(request) : proxyAwareIp;
17
+ };
18
+ const resolveClientIp = (request, options = {}) => {
19
+ const headerIp = trustedHeaderIp(request, options.trustedHeaders);
20
+ return headerIp === '' ? fallbackIp(request) : headerIp;
21
+ };
22
+ exports.resolveClientIp = resolveClientIp;
@@ -0,0 +1 @@
1
+ export declare const OBSERVABILITY_LOGGER: unique symbol;
@@ -0,0 +1 @@
1
+ export const OBSERVABILITY_LOGGER = Symbol('OBSERVABILITY_LOGGER');
@@ -0,0 +1,12 @@
1
+ import { OnApplicationBootstrap } from '@nestjs/common';
2
+ import { CommandBus, QueryBus } from '@nestjs/cqrs';
3
+ import type { ObservabilityLoggerInterface } from './observability-logger.interface.js';
4
+ export declare class CqrsLogger implements OnApplicationBootstrap {
5
+ private readonly commandBus;
6
+ private readonly queryBus;
7
+ private readonly logger;
8
+ private instrumented;
9
+ constructor(commandBus: CommandBus, queryBus: QueryBus, logger: ObservabilityLoggerInterface);
10
+ onApplicationBootstrap(): void;
11
+ private instrumentCompletion;
12
+ }
@@ -0,0 +1,69 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ import { Inject, Injectable } from '@nestjs/common';
14
+ import { CommandBus, QueryBus } from '@nestjs/cqrs';
15
+ import { performance } from 'node:perf_hooks';
16
+ import { OBSERVABILITY_LOGGER } from './constants.js';
17
+ const KIND_COMMAND = 'command';
18
+ const KIND_QUERY = 'query';
19
+ let CqrsLogger = class CqrsLogger {
20
+ commandBus;
21
+ queryBus;
22
+ logger;
23
+ instrumented = false;
24
+ constructor(commandBus, queryBus, logger) {
25
+ this.commandBus = commandBus;
26
+ this.queryBus = queryBus;
27
+ this.logger = logger;
28
+ }
29
+ onApplicationBootstrap() {
30
+ if (this.instrumented) {
31
+ return;
32
+ }
33
+ this.instrumented = true;
34
+ this.commandBus.subscribe((command) => {
35
+ this.logger.info({ [KIND_COMMAND]: command.constructor.name }, 'command started');
36
+ });
37
+ this.queryBus.subscribe((query) => {
38
+ this.logger.info({ [KIND_QUERY]: query.constructor.name }, 'query started');
39
+ });
40
+ this.instrumentCompletion(this.commandBus, KIND_COMMAND);
41
+ this.instrumentCompletion(this.queryBus, KIND_QUERY);
42
+ }
43
+ instrumentCompletion(bus, kind) {
44
+ const target = bus;
45
+ const original = target.execute.bind(target);
46
+ target.execute = async (payload, context) => {
47
+ const startedAt = performance.now();
48
+ const { name } = payload.constructor;
49
+ try {
50
+ const result = await original(payload, context);
51
+ this.logger.info({ durationMs: performance.now() - startedAt, [kind]: name, outcome: 'ok' }, `${kind} finished`);
52
+ return result;
53
+ }
54
+ catch (error) {
55
+ this.logger.error({ durationMs: performance.now() - startedAt, err: error, [kind]: name, outcome: 'error' }, `${kind} failed`);
56
+ throw error;
57
+ }
58
+ };
59
+ }
60
+ };
61
+ CqrsLogger = __decorate([
62
+ Injectable(),
63
+ __param(0, Inject(CommandBus)),
64
+ __param(1, Inject(QueryBus)),
65
+ __param(2, Inject(OBSERVABILITY_LOGGER)),
66
+ __metadata("design:paramtypes", [CommandBus,
67
+ QueryBus, Object])
68
+ ], CqrsLogger);
69
+ export { CqrsLogger };
@@ -0,0 +1,2 @@
1
+ export declare class CqrsLoggerModule {
2
+ }
@@ -0,0 +1,19 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { Module } from '@nestjs/common';
8
+ import { CqrsModule } from '@nestjs/cqrs';
9
+ import { CqrsLogger } from './cqrs-logger.js';
10
+ let CqrsLoggerModule = class CqrsLoggerModule {
11
+ };
12
+ CqrsLoggerModule = __decorate([
13
+ Module({
14
+ exports: [CqrsLogger],
15
+ imports: [CqrsModule],
16
+ providers: [CqrsLogger],
17
+ })
18
+ ], CqrsLoggerModule);
19
+ export { CqrsLoggerModule };
@@ -0,0 +1,4 @@
1
+ export * from './constants.js';
2
+ export * from './cqrs-logger.js';
3
+ export * from './cqrs-logger.module.js';
4
+ export * from './observability-logger.interface.js';
@@ -0,0 +1,4 @@
1
+ export * from './constants.js';
2
+ export * from './cqrs-logger.js';
3
+ export * from './cqrs-logger.module.js';
4
+ export * from './observability-logger.interface.js';
@@ -0,0 +1,4 @@
1
+ export interface ObservabilityLoggerInterface {
2
+ error(payload: object, message: string): void;
3
+ info(payload: object, message: string): void;
4
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ export interface ClientIpOptionsInterface {
2
+ readonly trustedHeaders?: readonly string[];
3
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export * from './client-ip-options.interface.js';
2
+ export * from './resolve-client-ip.js';
@@ -0,0 +1,2 @@
1
+ export * from './client-ip-options.interface.js';
2
+ export * from './resolve-client-ip.js';
@@ -0,0 +1,14 @@
1
+ import type { ClientIpOptionsInterface } from './client-ip-options.interface.js';
2
+ export interface ClientIpSourceInterface {
3
+ headers?: Record<string, string | string[] | undefined> | undefined;
4
+ ip?: string | undefined;
5
+ raw?: {
6
+ socket?: SocketLikeInterface | undefined;
7
+ } | undefined;
8
+ socket?: SocketLikeInterface | undefined;
9
+ }
10
+ interface SocketLikeInterface {
11
+ remoteAddress?: string | undefined;
12
+ }
13
+ export declare const resolveClientIp: (request: ClientIpSourceInterface, options?: ClientIpOptionsInterface) => string;
14
+ export {};
@@ -0,0 +1,18 @@
1
+ const firstHeaderValue = (value) => {
2
+ const single = Array.isArray(value) ? value[0] : value;
3
+ return (single ?? '').trim();
4
+ };
5
+ const trustedHeaderIp = (request, trustedHeaders) => {
6
+ const values = trustedHeaders?.map((header) => firstHeaderValue(request.headers?.[header]));
7
+ return values?.find((value) => value !== '') ?? '';
8
+ };
9
+ const rawSocketIp = (request) => request.raw?.socket?.remoteAddress ?? '';
10
+ const socketIp = (request) => request.socket?.remoteAddress ?? rawSocketIp(request);
11
+ const fallbackIp = (request) => {
12
+ const proxyAwareIp = (request.ip ?? '').trim();
13
+ return proxyAwareIp === '' ? socketIp(request) : proxyAwareIp;
14
+ };
15
+ export const resolveClientIp = (request, options = {}) => {
16
+ const headerIp = trustedHeaderIp(request, options.trustedHeaders);
17
+ return headerIp === '' ? fallbackIp(request) : headerIp;
18
+ };
package/package.json CHANGED
@@ -1,8 +1,10 @@
1
1
  {
2
2
  "name": "@application-kit/observability",
3
- "version": "0.0.0",
4
- "description": "Наблюдаемость NestJS-процесса: реестр метрик Prometheus, RED-гистограммы HTTP и GraphQL, HTTP-сервер /metrics для процессов без веб-слоя",
3
+ "version": "0.2.0",
4
+ "description": "Наблюдаемость NestJS-процесса: реестр метрик Prometheus и RED-гистограммы, логирование команд и запросов CQRS-шин, реальный IP клиента за прокси",
5
5
  "keywords": [
6
+ "cqrs",
7
+ "logging",
6
8
  "metrics",
7
9
  "nestjs",
8
10
  "observability",
@@ -22,6 +24,26 @@
22
24
  "sideEffects": false,
23
25
  "type": "module",
24
26
  "exports": {
27
+ "./cqrs": {
28
+ "import": {
29
+ "types": "./dist/esm/cqrs/index.d.ts",
30
+ "default": "./dist/esm/cqrs/index.js"
31
+ },
32
+ "require": {
33
+ "types": "./dist/cjs/cqrs/index.d.ts",
34
+ "default": "./dist/cjs/cqrs/index.js"
35
+ }
36
+ },
37
+ "./http": {
38
+ "import": {
39
+ "types": "./dist/esm/http/index.d.ts",
40
+ "default": "./dist/esm/http/index.js"
41
+ },
42
+ "require": {
43
+ "types": "./dist/cjs/http/index.d.ts",
44
+ "default": "./dist/cjs/http/index.js"
45
+ }
46
+ },
25
47
  "./metrics": {
26
48
  "import": {
27
49
  "types": "./dist/esm/metrics/index.d.ts",
@@ -41,13 +63,16 @@
41
63
  "dist"
42
64
  ],
43
65
  "scripts": {
44
- "build": "rm -rf ./dist && node ../../scripts/barrels.mjs create ./src/metrics && tsc --project tsconfig.build.json && tsc --project tsconfig.build.cjs.json && node ../../scripts/barrels.mjs clean ./src && node ../../scripts/mark-cjs.mjs ./dist/cjs",
66
+ "build": "rm -rf ./dist && node ../../scripts/barrels.mjs create ./src/cqrs && node ../../scripts/barrels.mjs create ./src/http && node ../../scripts/barrels.mjs create ./src/metrics && tsc --project tsconfig.build.json && tsc --project tsconfig.build.cjs.json && node ../../scripts/barrels.mjs clean ./src && node ../../scripts/mark-cjs.mjs ./dist/cjs",
45
67
  "lint:tsc": "tsc --noEmit --pretty --project .",
46
68
  "test:mutation": "stryker run",
47
69
  "test:unit": "vitest run"
48
70
  },
49
71
  "devDependencies": {
50
72
  "@nestjs/common": "^11.2.7",
73
+ "@nestjs/core": "^11.2.7",
74
+ "@nestjs/cqrs": "^11.0.3",
75
+ "@nestjs/testing": "^11.2.7",
51
76
  "@prometheus-io/client": "^0.16.0",
52
77
  "@stryker-mutator/core": "^10.0.0",
53
78
  "@stryker-mutator/vitest-runner": "^10.0.0",
@@ -60,10 +85,14 @@
60
85
  },
61
86
  "peerDependencies": {
62
87
  "@nestjs/common": "^11.0.0",
88
+ "@nestjs/cqrs": "^11.0.0",
63
89
  "@prometheus-io/client": "^0.16.0",
64
90
  "graphql": "^16.0.0"
65
91
  },
66
92
  "peerDependenciesMeta": {
93
+ "@nestjs/cqrs": {
94
+ "optional": true
95
+ },
67
96
  "graphql": {
68
97
  "optional": true
69
98
  }