@eqxjs/nest-logger 3.1.0-beta.9 → 3.1.1-beta.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/CHANGELOG +57 -1
- package/MIGRATION.md +234 -0
- package/PERFORMANCE_IMPROVEMENTS.md +158 -0
- package/README.md +2008 -16
- package/RESTRUCTURING_SUMMARY.md +272 -0
- package/STRUCTURE.md +110 -0
- package/dist/constants/action-message.constant.d.ts +187 -0
- package/dist/constants/action-message.constant.js +220 -0
- package/dist/constants/action-message.constant.js.map +1 -0
- package/dist/constants/index.d.ts +2 -0
- package/dist/constants/index.js +9 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/constants/logger.constants.d.ts +17 -0
- package/dist/constants/logger.constants.js +20 -0
- package/dist/constants/logger.constants.js.map +1 -0
- package/dist/core/formatters/index.d.ts +1 -0
- package/dist/core/formatters/index.js +6 -0
- package/dist/core/formatters/index.js.map +1 -0
- package/dist/core/formatters/logger.formatter.d.ts +141 -0
- package/dist/core/formatters/logger.formatter.js +268 -0
- package/dist/core/formatters/logger.formatter.js.map +1 -0
- package/dist/core/loggers/app.logger.d.ts +46 -0
- package/dist/core/loggers/app.logger.js +92 -0
- package/dist/core/loggers/app.logger.js.map +1 -0
- package/dist/core/loggers/base-app.logger.d.ts +299 -0
- package/dist/core/loggers/base-app.logger.js +517 -0
- package/dist/core/loggers/base-app.logger.js.map +1 -0
- package/dist/core/loggers/custom.logger.d.ts +127 -0
- package/dist/core/loggers/custom.logger.js +260 -0
- package/dist/core/loggers/custom.logger.js.map +1 -0
- package/dist/core/loggers/index.d.ts +3 -0
- package/dist/core/loggers/index.js +10 -0
- package/dist/core/loggers/index.js.map +1 -0
- package/dist/helpers/datetime.helper.d.ts +24 -0
- package/dist/helpers/datetime.helper.js +36 -0
- package/dist/helpers/datetime.helper.js.map +1 -0
- package/dist/helpers/index.d.ts +5 -0
- package/dist/helpers/index.js +17 -0
- package/dist/helpers/index.js.map +1 -0
- package/dist/helpers/log.helper.d.ts +84 -0
- package/dist/helpers/log.helper.js +109 -0
- package/dist/helpers/log.helper.js.map +1 -0
- package/dist/helpers/logger-builder.helper.d.ts +242 -0
- package/dist/helpers/logger-builder.helper.js +345 -0
- package/dist/helpers/logger-builder.helper.js.map +1 -0
- package/dist/helpers/message-formatter.helper.d.ts +88 -0
- package/dist/helpers/message-formatter.helper.js +159 -0
- package/dist/helpers/message-formatter.helper.js.map +1 -0
- package/dist/helpers/time-performance.helper.d.ts +68 -0
- package/dist/helpers/time-performance.helper.js +82 -0
- package/dist/helpers/time-performance.helper.js.map +1 -0
- package/dist/index.d.ts +13 -5
- package/dist/index.js +24 -9
- package/dist/index.js.map +1 -1
- package/dist/interfaces/data-header.interface.d.ts +21 -0
- package/dist/{dto/m2.dto.js → interfaces/data-header.interface.js} +1 -1
- package/dist/interfaces/data-header.interface.js.map +1 -0
- package/dist/interfaces/data-protocol.interface.d.ts +14 -0
- package/dist/interfaces/data-protocol.interface.js +3 -0
- package/dist/interfaces/data-protocol.interface.js.map +1 -0
- package/dist/interfaces/data-service.interface.d.ts +21 -0
- package/dist/{dto/m3.dto.js → interfaces/data-service.interface.js} +1 -1
- package/dist/interfaces/data-service.interface.js.map +1 -0
- package/dist/{types.d.ts → interfaces/data.interface.d.ts} +4 -4
- package/dist/{dto/m1.dto.js → interfaces/data.interface.js} +1 -1
- package/dist/interfaces/data.interface.js.map +1 -0
- package/dist/interfaces/index.d.ts +5 -0
- package/dist/{types.js → interfaces/index.js} +1 -1
- package/dist/interfaces/index.js.map +1 -0
- package/dist/{dto/header.dto.js → interfaces/logger-opt.interface.js} +1 -1
- package/dist/interfaces/logger-opt.interface.js.map +1 -0
- package/dist/logger.module.js +4 -4
- package/dist/logger.module.js.map +1 -1
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.js +6 -0
- package/dist/models/index.js.map +1 -0
- package/dist/models/logger.dto.d.ts +71 -0
- package/dist/models/logger.dto.js +76 -0
- package/dist/models/logger.dto.js.map +1 -0
- package/package.json +30 -33
- package/dist/dto/header.dto.d.ts +0 -21
- package/dist/dto/header.dto.js.map +0 -1
- package/dist/dto/logger.dto.d.ts +0 -28
- package/dist/dto/logger.dto.js +0 -33
- package/dist/dto/logger.dto.js.map +0 -1
- package/dist/dto/m1.dto.d.ts +0 -7
- package/dist/dto/m1.dto.js.map +0 -1
- package/dist/dto/m2.dto.d.ts +0 -5
- package/dist/dto/m2.dto.js.map +0 -1
- package/dist/dto/m3.dto.d.ts +0 -5
- package/dist/dto/m3.dto.js.map +0 -1
- package/dist/dto/protocol.dto.d.ts +0 -14
- package/dist/dto/protocol.dto.js +0 -3
- package/dist/dto/protocol.dto.js.map +0 -1
- package/dist/dto/service.dto.d.ts +0 -25
- package/dist/dto/service.dto.js +0 -3
- package/dist/dto/service.dto.js.map +0 -1
- package/dist/logger.app.d.ts +0 -62
- package/dist/logger.app.js +0 -504
- package/dist/logger.app.js.map +0 -1
- package/dist/logger.service.d.ts +0 -15
- package/dist/logger.service.js +0 -158
- package/dist/logger.service.js.map +0 -1
- package/dist/logger.util.d.ts +0 -3
- package/dist/logger.util.js +0 -28
- package/dist/logger.util.js.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/utils/action.common.d.ts +0 -14
- package/dist/utils/action.common.js +0 -43
- package/dist/utils/action.common.js.map +0 -1
- package/dist/utils/datetime.util.d.ts +0 -1
- package/dist/utils/datetime.util.js +0 -13
- package/dist/utils/datetime.util.js.map +0 -1
- package/dist/utils/logger.opt.js +0 -3
- package/dist/utils/logger.opt.js.map +0 -1
- package/dist/utils/m1.utils.d.ts +0 -3
- package/dist/utils/m1.utils.js +0 -79
- package/dist/utils/m1.utils.js.map +0 -1
- package/dist/utils/time.performance.d.ts +0 -6
- package/dist/utils/time.performance.js +0 -18
- package/dist/utils/time.performance.js.map +0 -1
- /package/dist/{utils/logger.opt.d.ts → interfaces/logger-opt.interface.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -1,36 +1,2028 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @eqxjs/nest-logger
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A comprehensive, production-ready logging module for NestJS applications with built-in OpenTelemetry integration (metrics, traces, and logs API), structured logging, and support for multiple message formats.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/@eqxjs/nest-logger)
|
|
6
|
+
[](./coverage)
|
|
7
|
+
[](https://nodejs.org)
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
## Features
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
✨ **Key Features:**
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
- 🚀 **NestJS Integration** - Seamless integration with NestJS framework (v11+)
|
|
14
|
+
- 📊 **OpenTelemetry Support** - Built-in metrics, traces, spans, and logs API with distributed tracing
|
|
15
|
+
- 🏷️ **Structured Logging** - Consistent log format with rich metadata (27+ fields)
|
|
16
|
+
- 🔒 **Sensitive Data Masking** - Automatic masking of sensitive fields (password, token, apiKey, etc.)
|
|
17
|
+
- 📝 **Multiple Log Levels** - Debug, Info, Log, Warn, Error, Verbose support with environment filtering
|
|
18
|
+
- 🎯 **Message Formats** - Support for M1 (broker/queue), M2 (HTTP/protocol), and M3 (service) message protocols
|
|
19
|
+
- ⚡ **Performance Optimized** - Efficient logging with caching, fast paths, and minimal overhead
|
|
20
|
+
- 🧪 **Well Tested** - 100% line coverage, 98%+ statement coverage with 297 test cases
|
|
21
|
+
- 🔍 **Telemetry Metrics** - Counter and histogram metrics for observability with configurable filtering
|
|
22
|
+
- 📦 **TypeScript** - Full TypeScript support with comprehensive type definitions and JSDoc
|
|
23
|
+
- ⏱️ **High-Resolution Timing** - Built-in TimeDiff class for precise performance measurements
|
|
24
|
+
- 🎨 **Builder Pattern** - Fluent LoggerDtoBuilder for complex log construction
|
|
25
|
+
- 🌍 **UTC+7 Timezone** - Standardized timestamps with configurable timezone support
|
|
26
|
+
- 🔧 **Message Truncation** - Automatic message truncation (4096 chars) to prevent log overflow
|
|
27
|
+
- 📋 **14 Action Tags** - Predefined action constants for consistent categorization
|
|
17
28
|
|
|
18
29
|
## Installation
|
|
19
30
|
|
|
20
31
|
```bash
|
|
21
|
-
|
|
32
|
+
# Using yarn
|
|
33
|
+
yarn add @eqxjs/nest-logger
|
|
34
|
+
|
|
35
|
+
# Using npm
|
|
36
|
+
npm install @eqxjs/nest-logger
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Requirements:**
|
|
40
|
+
- Node.js >= 22
|
|
41
|
+
- NestJS >= 11
|
|
42
|
+
|
|
43
|
+
**OpenTelemetry Setup (Optional):**
|
|
44
|
+
|
|
45
|
+
For OpenTelemetry Logs API support, ensure you have the OpenTelemetry SDK configured in your application:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Install OpenTelemetry SDK packages (if not already installed)
|
|
49
|
+
yarn add @opentelemetry/sdk-logs @opentelemetry/sdk-node
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Configure the SDK in your application bootstrap:
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { NodeSDK } from '@opentelemetry/sdk-node';
|
|
56
|
+
import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';
|
|
57
|
+
|
|
58
|
+
const sdk = new NodeSDK({
|
|
59
|
+
logRecordProcessor: new BatchLogRecordProcessor(
|
|
60
|
+
new OTLPLogExporter({
|
|
61
|
+
url: 'http://your-collector:4318/v1/logs',
|
|
62
|
+
})
|
|
63
|
+
),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
sdk.start();
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Feature Overview
|
|
70
|
+
|
|
71
|
+
### Logger Types Comparison
|
|
72
|
+
|
|
73
|
+
| Feature | CustomLogger | BaseAppLogger | AppLogger | LoggerFormat (M1/M2/M3) |
|
|
74
|
+
|---------|--------------|---------------|-----------|-------------------------|
|
|
75
|
+
| **Basic Logging** | ✅ | ✅ | ✅ | ✅ |
|
|
76
|
+
| **Structured Metadata** | ❌ | ✅ | ✅ | ✅ |
|
|
77
|
+
| **OpenTelemetry** | ❌ | ✅ | ✅ | ✅ |
|
|
78
|
+
| **Message Formats** | ❌ | ❌ | ✅ | ✅ |
|
|
79
|
+
| **Summary Logs** | ❌ | ✅ | ✅ | ✅ |
|
|
80
|
+
| **Sensitive Masking** | ✅ | ✅ | ✅ | ✅ |
|
|
81
|
+
| **Use Case** | Simple apps | Advanced apps | Multi-format | Specific protocols |
|
|
82
|
+
|
|
83
|
+
### Supported Log Levels
|
|
84
|
+
|
|
85
|
+
All loggers support these levels (controlled by `LOG_LEVEL` environment variable):
|
|
86
|
+
|
|
87
|
+
- **debug** - Detailed diagnostic information
|
|
88
|
+
- **info** - General informational messages
|
|
89
|
+
- **log** - Alias for info level
|
|
90
|
+
- **warn** - Warning messages for potentially harmful situations
|
|
91
|
+
- **error** - Error events that might still allow the app to continue
|
|
92
|
+
- **verbose** - Most detailed information for deep debugging
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
### Metadata Fields
|
|
96
|
+
|
|
97
|
+
LoggerDto includes 27+ fields for comprehensive logging:
|
|
98
|
+
|
|
99
|
+
| Category | Fields |
|
|
100
|
+
|----------|--------|
|
|
101
|
+
| **Core** | level, timestamp, message, action |
|
|
102
|
+
| **Application** | appName, componentName, componentVersion |
|
|
103
|
+
| **Tracking** | sessionId, transactionId, recordName, guid |
|
|
104
|
+
| **Context** | channel, broker, device, user, public |
|
|
105
|
+
| **Use Case** | useCase, useCaseStep |
|
|
106
|
+
| **Result** | appResult, appResultCode, serviceTime, stack |
|
|
107
|
+
| **Infrastructure** | instance, originateServiceName, recordType |
|
|
108
|
+
|
|
109
|
+
## Quick Start
|
|
110
|
+
|
|
111
|
+
### 1. Import the Module
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
import { Module } from '@nestjs/common';
|
|
115
|
+
import { CustomLoggerModule } from '@eqxjs/nest-logger';
|
|
116
|
+
|
|
117
|
+
@Module({
|
|
118
|
+
imports: [CustomLoggerModule],
|
|
119
|
+
})
|
|
120
|
+
export class AppModule {}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### 2. Use the Logger
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import { Injectable } from '@nestjs/common';
|
|
127
|
+
import { CustomLogger, AppLogger } from '@eqxjs/nest-logger';
|
|
128
|
+
|
|
129
|
+
@Injectable()
|
|
130
|
+
export class MyService {
|
|
131
|
+
constructor(
|
|
132
|
+
private readonly logger: CustomLogger,
|
|
133
|
+
private readonly appLogger: AppLogger,
|
|
134
|
+
) {}
|
|
135
|
+
|
|
136
|
+
doSomething() {
|
|
137
|
+
this.logger.log('Simple log message', 'MyService');
|
|
138
|
+
this.logger.error('Error occurred', 'MyService');
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Message Protocol Data Structures
|
|
144
|
+
|
|
145
|
+
The logger supports three message protocols (M1, M2, M3), each with specific required properties:
|
|
146
|
+
|
|
147
|
+
### DataM1I - Broker/Queue Messages
|
|
148
|
+
|
|
149
|
+
**Required Properties:**
|
|
150
|
+
- `header`: DataHeaderI - Message header with identity
|
|
151
|
+
- `protocol`: DataProtocolI - Protocol-specific information
|
|
152
|
+
- `body`: Record<string, unknown> - Message payload
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
import { DataM1I } from '@eqxjs/nest-logger';
|
|
156
|
+
|
|
157
|
+
const data: DataM1I = {
|
|
158
|
+
header: {
|
|
159
|
+
sessionId: 'session-123',
|
|
160
|
+
transactionId: 'txn-456',
|
|
161
|
+
broker: 'kafka',
|
|
162
|
+
channel: 'order-queue',
|
|
163
|
+
useCase: 'ProcessOrder',
|
|
164
|
+
identity: {
|
|
165
|
+
device: 'mobile-app',
|
|
166
|
+
user: 'user-123',
|
|
167
|
+
public: 'public-id'
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
protocol: {
|
|
171
|
+
topic: 'order.created',
|
|
172
|
+
command: 'PROCESS',
|
|
173
|
+
qos: '1'
|
|
174
|
+
},
|
|
175
|
+
body: {
|
|
176
|
+
orderId: '12345',
|
|
177
|
+
amount: 100.00
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### DataM2I - HTTP/Protocol Messages
|
|
183
|
+
|
|
184
|
+
**Required Properties:**
|
|
185
|
+
- `header`: DataHeaderI - Message header with identity
|
|
186
|
+
- `body`: Record<string, unknown> - Message payload
|
|
187
|
+
|
|
188
|
+
**Optional Properties:**
|
|
189
|
+
- `protocol`: DataProtocolI - HTTP/protocol information
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
import { DataM2I } from '@eqxjs/nest-logger';
|
|
193
|
+
|
|
194
|
+
const data: DataM2I = {
|
|
195
|
+
header: {
|
|
196
|
+
sessionId: 'session-123',
|
|
197
|
+
transactionId: 'txn-456',
|
|
198
|
+
channel: 'web',
|
|
199
|
+
useCase: 'CreatePayment',
|
|
200
|
+
identity: {
|
|
201
|
+
user: 'user-123',
|
|
202
|
+
public: 'public-id'
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
body: {
|
|
206
|
+
paymentId: 'pay-123',
|
|
207
|
+
status: 'completed'
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### DataM3I - Service/Database Messages
|
|
213
|
+
|
|
214
|
+
**Required Properties:**
|
|
215
|
+
- `service`: DataServiceI - Service-specific information with identity
|
|
216
|
+
- `body`: Record<string, unknown> - Message payload
|
|
217
|
+
|
|
218
|
+
**Optional Properties:**
|
|
219
|
+
- `header`: DataHeaderI - Message header with identity
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
import { DataM3I } from '@eqxjs/nest-logger';
|
|
223
|
+
|
|
224
|
+
const data: DataM3I = {
|
|
225
|
+
service: {
|
|
226
|
+
serviceName: 'PostgreSQL',
|
|
227
|
+
name: 'users-db',
|
|
228
|
+
invoke: 'SELECT',
|
|
229
|
+
identity: {
|
|
230
|
+
device: 'db-server-1',
|
|
231
|
+
user: 'db-user'
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
body: {
|
|
235
|
+
query: 'SELECT * FROM users WHERE id = $1',
|
|
236
|
+
resultCount: 1
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Identity Structure
|
|
242
|
+
|
|
243
|
+
All message types include an `identity` object with optional fields:
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
identity: {
|
|
247
|
+
device?: string | string[]; // Device identifier(s)
|
|
248
|
+
public?: string; // Public identifier
|
|
249
|
+
user?: string; // User identifier
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Usage Examples
|
|
254
|
+
|
|
255
|
+
### Basic Logging
|
|
256
|
+
|
|
257
|
+
#### CustomLogger - Simple Logging
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
import { CustomLogger } from '@eqxjs/nest-logger';
|
|
261
|
+
|
|
262
|
+
export class UserService {
|
|
263
|
+
private readonly logger = new CustomLogger('UserService');
|
|
264
|
+
|
|
265
|
+
createUser(userData: any) {
|
|
266
|
+
this.logger.log('Creating new user');
|
|
267
|
+
this.logger.debug('User data received', 'UserService');
|
|
268
|
+
this.logger.warn('Deprecated API used', 'UserService');
|
|
269
|
+
this.logger.error('Failed to create user', 'UserService');
|
|
270
|
+
this.logger.verbose('Detailed processing info', 'UserService');
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Real-World Examples
|
|
276
|
+
|
|
277
|
+
#### Example 1: HTTP Request/Response Logging
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
import { Injectable } from '@nestjs/common';
|
|
281
|
+
import { AppLogger, ActionMessage, TimeDiff, DataM2I } from '@eqxjs/nest-logger';
|
|
282
|
+
|
|
283
|
+
@Injectable()
|
|
284
|
+
export class PaymentController {
|
|
285
|
+
private readonly logger = new AppLogger('PaymentAPI', 'PaymentController');
|
|
286
|
+
|
|
287
|
+
async processPayment(request: any) {
|
|
288
|
+
const timer = new TimeDiff();
|
|
289
|
+
const sessionId = request.headers['x-session-id'];
|
|
290
|
+
const transactionId = request.headers['x-transaction-id'];
|
|
291
|
+
|
|
292
|
+
const data: DataM2I = {
|
|
293
|
+
header: {
|
|
294
|
+
sessionId,
|
|
295
|
+
transactionId,
|
|
296
|
+
channel: 'web',
|
|
297
|
+
useCase: 'ProcessPayment',
|
|
298
|
+
identity: {},
|
|
299
|
+
},
|
|
300
|
+
body: {
|
|
301
|
+
amount: request.body.amount,
|
|
302
|
+
currency: request.body.currency,
|
|
303
|
+
},
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
// Log incoming request
|
|
307
|
+
this.logger.loggerM2.info(
|
|
308
|
+
'api.payment',
|
|
309
|
+
ActionMessage.httpRequest(),
|
|
310
|
+
data,
|
|
311
|
+
`Payment request received: ${request.body.amount}`
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
try {
|
|
315
|
+
// Process payment logic...
|
|
316
|
+
const result = await this.executePayment(request.body);
|
|
317
|
+
|
|
318
|
+
// Log successful response
|
|
319
|
+
this.logger.loggerM2.summarySuccess(
|
|
320
|
+
'api.payment',
|
|
321
|
+
'Payment processed successfully',
|
|
322
|
+
'200',
|
|
323
|
+
timer.diff(),
|
|
324
|
+
data
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
return result;
|
|
328
|
+
} catch (error) {
|
|
329
|
+
// Log error response
|
|
330
|
+
this.logger.loggerM2.summaryError(
|
|
331
|
+
'api.payment',
|
|
332
|
+
'Payment processing failed',
|
|
333
|
+
'500',
|
|
334
|
+
timer.diff(),
|
|
335
|
+
data,
|
|
336
|
+
[error.stack]
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
throw error;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
#### Example 2: Kafka Message Consumer
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
import { Injectable } from '@nestjs/common';
|
|
349
|
+
import { AppLogger, ActionMessage, TimeDiff, DataM1I } from '@eqxjs/nest-logger';
|
|
350
|
+
|
|
351
|
+
@Injectable()
|
|
352
|
+
export class OrderConsumer {
|
|
353
|
+
private readonly logger = new AppLogger('OrderService', 'KafkaConsumer');
|
|
354
|
+
|
|
355
|
+
async consumeOrderMessage(message: any) {
|
|
356
|
+
const timer = new TimeDiff();
|
|
357
|
+
|
|
358
|
+
const data: DataM1I = {
|
|
359
|
+
header: {
|
|
360
|
+
sessionId: message.sessionId,
|
|
361
|
+
transactionId: message.transactionId,
|
|
362
|
+
broker: 'kafka',
|
|
363
|
+
channel: 'order-topic',
|
|
364
|
+
useCase: 'ProcessOrder',
|
|
365
|
+
useCaseStep: 'Consume',
|
|
366
|
+
identity: {},
|
|
367
|
+
},
|
|
368
|
+
protocol: {
|
|
369
|
+
topic: 'order.created',
|
|
370
|
+
command: 'CONSUME',
|
|
371
|
+
},
|
|
372
|
+
body: {
|
|
373
|
+
orderId: message.orderId,
|
|
374
|
+
customerId: message.customerId,
|
|
375
|
+
},
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
// Log message consumption start
|
|
379
|
+
this.logger.loggerM1.info(
|
|
380
|
+
'order.created',
|
|
381
|
+
ActionMessage.consume(),
|
|
382
|
+
data,
|
|
383
|
+
`Consuming order message: ${message.orderId}`
|
|
384
|
+
);
|
|
385
|
+
|
|
386
|
+
try {
|
|
387
|
+
// Process the message
|
|
388
|
+
await this.processOrder(message);
|
|
389
|
+
|
|
390
|
+
// Log successful consumption
|
|
391
|
+
this.logger.loggerM1.info(
|
|
392
|
+
'order.created',
|
|
393
|
+
ActionMessage.consumed(),
|
|
394
|
+
data,
|
|
395
|
+
`Order message consumed successfully`
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
this.logger.loggerM1.summarySuccess(
|
|
399
|
+
'order.created',
|
|
400
|
+
'Order processed',
|
|
401
|
+
'200',
|
|
402
|
+
timer.diff(),
|
|
403
|
+
data
|
|
404
|
+
);
|
|
405
|
+
} catch (error) {
|
|
406
|
+
// Log consumption failure
|
|
407
|
+
this.logger.loggerM1.error(
|
|
408
|
+
'order.created',
|
|
409
|
+
ActionMessage.exception(),
|
|
410
|
+
data,
|
|
411
|
+
`Failed to consume order message: ${error.message}`
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
this.logger.loggerM1.summaryError(
|
|
415
|
+
'order.created',
|
|
416
|
+
'Order processing failed',
|
|
417
|
+
'500',
|
|
418
|
+
timer.diff(),
|
|
419
|
+
data,
|
|
420
|
+
[error.stack]
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
throw error;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
#### Example 3: Database Operations
|
|
430
|
+
|
|
431
|
+
```typescript
|
|
432
|
+
import { Injectable } from '@nestjs/common';
|
|
433
|
+
import { AppLogger, ActionMessage, TimeDiff, DataM3I } from '@eqxjs/nest-logger';
|
|
434
|
+
|
|
435
|
+
@Injectable()
|
|
436
|
+
export class UserRepository {
|
|
437
|
+
private readonly logger = new AppLogger('UserService', 'UserRepository');
|
|
438
|
+
|
|
439
|
+
async findUserById(userId: string, sessionId: string, transactionId: string) {
|
|
440
|
+
const timer = new TimeDiff();
|
|
441
|
+
|
|
442
|
+
const data: DataM3I = {
|
|
443
|
+
|
|
444
|
+
service: {
|
|
445
|
+
serviceName: 'PostgreSQL',
|
|
446
|
+
invoke: 'SELECT',
|
|
447
|
+
name: 'users',
|
|
448
|
+
identity: {},
|
|
449
|
+
},
|
|
450
|
+
body: {
|
|
451
|
+
userId: userId,
|
|
452
|
+
operation: 'findById',
|
|
453
|
+
},
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
// Log database request
|
|
457
|
+
this.logger.loggerM3.debug(
|
|
458
|
+
'db.users',
|
|
459
|
+
ActionMessage.dbRequest(),
|
|
460
|
+
data,
|
|
461
|
+
`Querying user by ID: ${userId}`
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
try {
|
|
465
|
+
const user = await this.executeQuery(`SELECT * FROM users WHERE id = $1`, [userId]);
|
|
466
|
+
|
|
467
|
+
// Log database response
|
|
468
|
+
this.logger.loggerM3.debug(
|
|
469
|
+
'db.users',
|
|
470
|
+
ActionMessage.dbResponse(),
|
|
471
|
+
data,
|
|
472
|
+
`User found: ${user.email}`
|
|
473
|
+
);
|
|
474
|
+
|
|
475
|
+
this.logger.loggerM3.summarySuccess(
|
|
476
|
+
'db.users',
|
|
477
|
+
'User retrieved',
|
|
478
|
+
'200',
|
|
479
|
+
timer.diff(),
|
|
480
|
+
data
|
|
481
|
+
);
|
|
482
|
+
|
|
483
|
+
return user;
|
|
484
|
+
} catch (error) {
|
|
485
|
+
this.logger.loggerM3.summaryError(
|
|
486
|
+
'db.users',
|
|
487
|
+
'Database query failed',
|
|
488
|
+
'500',
|
|
489
|
+
timer.diff(),
|
|
490
|
+
data,
|
|
491
|
+
[error.stack]
|
|
492
|
+
);
|
|
493
|
+
|
|
494
|
+
throw error;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
#### Example 4: Microservice Communication
|
|
501
|
+
|
|
502
|
+
```typescript
|
|
503
|
+
import { Injectable } from '@nestjs/common';
|
|
504
|
+
import { AppLogger, ActionMessage, TimeDiff, DataM3I } from '@eqxjs/nest-logger';
|
|
505
|
+
|
|
506
|
+
@Injectable()
|
|
507
|
+
export class NotificationService {
|
|
508
|
+
private readonly logger = new AppLogger('NotificationService', 'EmailClient');
|
|
509
|
+
|
|
510
|
+
async sendEmail(emailData: any, sessionId: string, transactionId: string) {
|
|
511
|
+
const timer = new TimeDiff();
|
|
512
|
+
|
|
513
|
+
const data: DataM3I = {
|
|
514
|
+
service: {
|
|
515
|
+
serviceName: 'EmailService',
|
|
516
|
+
operation: 'SEND',
|
|
517
|
+
endpoint: '/api/v1/email/send',
|
|
518
|
+
},
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
// Log outbound call
|
|
522
|
+
this.logger.loggerM3.info(
|
|
523
|
+
'notification.email',
|
|
524
|
+
ActionMessage.outbound(),
|
|
525
|
+
data,
|
|
526
|
+
`Sending email to: ${emailData.recipient}`
|
|
527
|
+
);
|
|
528
|
+
|
|
529
|
+
try {
|
|
530
|
+
const response = await this.emailClient.send(emailData);
|
|
531
|
+
|
|
532
|
+
// Log successful response
|
|
533
|
+
this.logger.loggerM3.info(
|
|
534
|
+
'notification.email',
|
|
535
|
+
ActionMessage.inbound(),
|
|
536
|
+
data,
|
|
537
|
+
`Email sent successfully`
|
|
538
|
+
);
|
|
539
|
+
|
|
540
|
+
this.logger.loggerM3.summarySuccess(
|
|
541
|
+
'notification.email',
|
|
542
|
+
'Email delivered',
|
|
543
|
+
'200',
|
|
544
|
+
timer.diff(),
|
|
545
|
+
data
|
|
546
|
+
);
|
|
547
|
+
|
|
548
|
+
return response;
|
|
549
|
+
} catch (error) {
|
|
550
|
+
this.logger.loggerM3.summaryError(
|
|
551
|
+
'notification.email',
|
|
552
|
+
'Email delivery failed',
|
|
553
|
+
'500',
|
|
554
|
+
timer.diff(),
|
|
555
|
+
data,
|
|
556
|
+
[error.stack]
|
|
557
|
+
);
|
|
558
|
+
|
|
559
|
+
throw error;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
#### Example 5: Business Logic with Comprehensive Logging
|
|
566
|
+
|
|
567
|
+
```typescript
|
|
568
|
+
import { Injectable } from '@nestjs/common';
|
|
569
|
+
import {
|
|
570
|
+
AppLogger,
|
|
571
|
+
ActionMessage,
|
|
572
|
+
TimeDiff,
|
|
573
|
+
logStringify
|
|
574
|
+
} from '@eqxjs/nest-logger';
|
|
575
|
+
|
|
576
|
+
@Injectable()
|
|
577
|
+
export class OrderProcessingService {
|
|
578
|
+
private readonly logger = new AppLogger('OrderService', 'OrderProcessor');
|
|
579
|
+
|
|
580
|
+
async processOrder(orderData: any, sessionId: string, transactionId: string) {
|
|
581
|
+
const overallTimer = new TimeDiff();
|
|
582
|
+
|
|
583
|
+
// Log start of business logic
|
|
584
|
+
this.logger.app.info(
|
|
585
|
+
'Starting order processing',
|
|
586
|
+
ActionMessage.appLogic(),
|
|
587
|
+
'OrderProcessingService',
|
|
588
|
+
orderData.orderId,
|
|
589
|
+
sessionId,
|
|
590
|
+
transactionId,
|
|
591
|
+
orderData.channel,
|
|
592
|
+
'1.0.0',
|
|
593
|
+
'ProcessOrder',
|
|
594
|
+
'Start',
|
|
595
|
+
orderData.userId,
|
|
596
|
+
orderData.device
|
|
597
|
+
);
|
|
598
|
+
|
|
599
|
+
try {
|
|
600
|
+
// Step 1: Validate order
|
|
601
|
+
const validationTimer = new TimeDiff();
|
|
602
|
+
this.logger.app.debug(
|
|
603
|
+
'Validating order data',
|
|
604
|
+
'[VALIDATION]',
|
|
605
|
+
'OrderProcessingService',
|
|
606
|
+
orderData.orderId,
|
|
607
|
+
sessionId,
|
|
608
|
+
transactionId
|
|
609
|
+
);
|
|
610
|
+
|
|
611
|
+
await this.validateOrder(orderData);
|
|
612
|
+
|
|
613
|
+
this.logger.app.verbose(
|
|
614
|
+
`Order validation completed in ${validationTimer.diff()}ms`,
|
|
615
|
+
'[VALIDATION]'
|
|
616
|
+
);
|
|
617
|
+
|
|
618
|
+
// Step 2: Check inventory
|
|
619
|
+
const inventoryTimer = new TimeDiff();
|
|
620
|
+
this.logger.app.debug(
|
|
621
|
+
'Checking inventory availability',
|
|
622
|
+
'[INVENTORY_CHECK]',
|
|
623
|
+
'OrderProcessingService',
|
|
624
|
+
orderData.orderId,
|
|
625
|
+
sessionId,
|
|
626
|
+
transactionId
|
|
627
|
+
);
|
|
628
|
+
|
|
629
|
+
const inventory = await this.checkInventory(orderData.items);
|
|
630
|
+
|
|
631
|
+
this.logger.app.info(
|
|
632
|
+
`Inventory checked: ${logStringify(inventory)}`,
|
|
633
|
+
'[INVENTORY_CHECK]',
|
|
634
|
+
'OrderProcessingService',
|
|
635
|
+
orderData.orderId
|
|
636
|
+
);
|
|
637
|
+
|
|
638
|
+
// Step 3: Process payment
|
|
639
|
+
const paymentTimer = new TimeDiff();
|
|
640
|
+
this.logger.app.info(
|
|
641
|
+
'Processing payment',
|
|
642
|
+
'[PAYMENT]',
|
|
643
|
+
'OrderProcessingService',
|
|
644
|
+
orderData.orderId,
|
|
645
|
+
sessionId,
|
|
646
|
+
transactionId
|
|
647
|
+
);
|
|
648
|
+
|
|
649
|
+
const payment = await this.processPayment(orderData.payment);
|
|
650
|
+
|
|
651
|
+
this.logger.app.info(
|
|
652
|
+
'Payment processed successfully',
|
|
653
|
+
'[PAYMENT]',
|
|
654
|
+
'OrderProcessingService',
|
|
655
|
+
orderData.orderId
|
|
656
|
+
);
|
|
657
|
+
|
|
658
|
+
// Step 4: Create order
|
|
659
|
+
this.logger.app.info(
|
|
660
|
+
'Creating order record',
|
|
661
|
+
'[ORDER_CREATE]',
|
|
662
|
+
'OrderProcessingService',
|
|
663
|
+
orderData.orderId,
|
|
664
|
+
sessionId,
|
|
665
|
+
transactionId
|
|
666
|
+
);
|
|
667
|
+
|
|
668
|
+
const order = await this.createOrder(orderData, payment);
|
|
669
|
+
|
|
670
|
+
// Log final success summary with telemetry
|
|
671
|
+
this.logger.app.summarySuccess(
|
|
672
|
+
'Order processed successfully',
|
|
673
|
+
'200',
|
|
674
|
+
overallTimer.diff(),
|
|
675
|
+
'OrderProcessingService',
|
|
676
|
+
orderData.orderId,
|
|
677
|
+
sessionId,
|
|
678
|
+
transactionId,
|
|
679
|
+
orderData.channel,
|
|
680
|
+
'1.0.0',
|
|
681
|
+
'ProcessOrder',
|
|
682
|
+
'Complete',
|
|
683
|
+
orderData.userId,
|
|
684
|
+
orderData.device,
|
|
685
|
+
order.publicId
|
|
686
|
+
);
|
|
687
|
+
|
|
688
|
+
return order;
|
|
689
|
+
|
|
690
|
+
} catch (error) {
|
|
691
|
+
// Log detailed error with context
|
|
692
|
+
this.logger.app.error(
|
|
693
|
+
`Order processing failed: ${error.message}`,
|
|
694
|
+
ActionMessage.exception(),
|
|
695
|
+
'OrderProcessingService',
|
|
696
|
+
orderData.orderId,
|
|
697
|
+
sessionId,
|
|
698
|
+
transactionId
|
|
699
|
+
);
|
|
700
|
+
|
|
701
|
+
// Log error summary with telemetry
|
|
702
|
+
this.logger.app.summaryError(
|
|
703
|
+
'Order processing failed',
|
|
704
|
+
error.code || '500',
|
|
705
|
+
overallTimer.diff(),
|
|
706
|
+
'OrderProcessingService',
|
|
707
|
+
orderData.orderId,
|
|
708
|
+
sessionId,
|
|
709
|
+
transactionId,
|
|
710
|
+
orderData.channel,
|
|
711
|
+
'1.0.0',
|
|
712
|
+
'ProcessOrder',
|
|
713
|
+
'Failed',
|
|
714
|
+
orderData.userId,
|
|
715
|
+
orderData.device,
|
|
716
|
+
undefined,
|
|
717
|
+
[error.stack]
|
|
718
|
+
);
|
|
719
|
+
|
|
720
|
+
throw error;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
### Advanced Logging with AppLogger
|
|
727
|
+
|
|
728
|
+
#### BaseAppLogger - Structured Logging
|
|
729
|
+
|
|
730
|
+
```typescript
|
|
731
|
+
import { AppLogger } from '@eqxjs/nest-logger';
|
|
732
|
+
|
|
733
|
+
export class PaymentService {
|
|
734
|
+
private readonly logger = new AppLogger('PaymentService', 'PaymentContext');
|
|
735
|
+
|
|
736
|
+
processPayment(paymentData: any) {
|
|
737
|
+
// Detailed structured log
|
|
738
|
+
this.logger.app.info(
|
|
739
|
+
'Processing payment',
|
|
740
|
+
'[PAYMENT_PROCESSING]',
|
|
741
|
+
'PaymentService',
|
|
742
|
+
'payment-record-123',
|
|
743
|
+
'session-456',
|
|
744
|
+
'txn-789',
|
|
745
|
+
'mobile-app',
|
|
746
|
+
'1.0.0',
|
|
747
|
+
'ProcessPayment',
|
|
748
|
+
'Validation',
|
|
749
|
+
'user@example.com',
|
|
750
|
+
['mobile', 'ios'],
|
|
751
|
+
'public-id-123'
|
|
752
|
+
);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
#### Summary Logging with Telemetry
|
|
758
|
+
|
|
759
|
+
```typescript
|
|
760
|
+
import { AppLogger, TimeDiff } from '@eqxjs/nest-logger';
|
|
761
|
+
|
|
762
|
+
export class OrderService {
|
|
763
|
+
private readonly logger = new AppLogger('OrderService');
|
|
764
|
+
|
|
765
|
+
async createOrder(orderData: any) {
|
|
766
|
+
const timer = new TimeDiff();
|
|
767
|
+
|
|
768
|
+
try {
|
|
769
|
+
// Process order...
|
|
770
|
+
const result = await this.processOrder(orderData);
|
|
771
|
+
|
|
772
|
+
// Log success with metrics
|
|
773
|
+
this.logger.app.summarySuccess(
|
|
774
|
+
'Order created successfully',
|
|
775
|
+
'200',
|
|
776
|
+
timer.diff(),
|
|
777
|
+
'OrderService',
|
|
778
|
+
'order-123',
|
|
779
|
+
'session-456',
|
|
780
|
+
'txn-789',
|
|
781
|
+
'web',
|
|
782
|
+
'2.0.0',
|
|
783
|
+
'CreateOrder',
|
|
784
|
+
'Complete'
|
|
785
|
+
);
|
|
786
|
+
|
|
787
|
+
return result;
|
|
788
|
+
} catch (error) {
|
|
789
|
+
// Log error with stack trace
|
|
790
|
+
this.logger.app.summaryError(
|
|
791
|
+
'Order creation failed',
|
|
792
|
+
'500',
|
|
793
|
+
timer.diff(),
|
|
794
|
+
'OrderService',
|
|
795
|
+
'order-123',
|
|
796
|
+
'session-456',
|
|
797
|
+
'txn-789',
|
|
798
|
+
'web',
|
|
799
|
+
'2.0.0',
|
|
800
|
+
'CreateOrder',
|
|
801
|
+
'Failed',
|
|
802
|
+
undefined,
|
|
803
|
+
undefined,
|
|
804
|
+
[error.stack]
|
|
805
|
+
);
|
|
806
|
+
|
|
807
|
+
throw error;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
```
|
|
812
|
+
|
|
813
|
+
### Message Format Loggers (M1, M2, M3)
|
|
814
|
+
|
|
815
|
+
#### M1 Message Format
|
|
816
|
+
|
|
817
|
+
```typescript
|
|
818
|
+
import { AppLogger } from '@eqxjs/nest-logger';
|
|
819
|
+
import { DataM1I } from '@eqxjs/nest-logger';
|
|
820
|
+
|
|
821
|
+
export class KafkaConsumerService {
|
|
822
|
+
private readonly logger = new AppLogger('KafkaConsumer');
|
|
823
|
+
|
|
824
|
+
consumeMessage(message: any) {
|
|
825
|
+
const data: DataM1I = {
|
|
826
|
+
header: {
|
|
827
|
+
sessionId: 'session-123',
|
|
828
|
+
transactionId: 'txn-456',
|
|
829
|
+
broker: 'kafka',
|
|
830
|
+
channel: 'queue',
|
|
831
|
+
useCase: 'MessageProcessing',
|
|
832
|
+
identity: {},
|
|
833
|
+
},
|
|
834
|
+
protocol: {
|
|
835
|
+
topic: 'payment.topic',
|
|
836
|
+
command: 'PROCESS',
|
|
837
|
+
},
|
|
838
|
+
body: {
|
|
839
|
+
messageId: message.id,
|
|
840
|
+
payload: message.data,
|
|
841
|
+
},
|
|
842
|
+
};
|
|
843
|
+
|
|
844
|
+
// Log with M1 format
|
|
845
|
+
this.logger.loggerM1.info(
|
|
846
|
+
'payment.topic',
|
|
847
|
+
'[CONSUMING]',
|
|
848
|
+
data,
|
|
849
|
+
'Processing payment message'
|
|
850
|
+
);
|
|
851
|
+
|
|
852
|
+
// Summary for M1
|
|
853
|
+
this.logger.loggerM1.summarySuccess(
|
|
854
|
+
'payment.topic',
|
|
855
|
+
'Message processed',
|
|
856
|
+
'200',
|
|
857
|
+
150,
|
|
858
|
+
data
|
|
859
|
+
);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
```
|
|
863
|
+
|
|
864
|
+
#### M2 Message Format
|
|
865
|
+
|
|
866
|
+
```typescript
|
|
867
|
+
import { AppLogger } from '@eqxjs/nest-logger';
|
|
868
|
+
import { DataM2I } from '@eqxjs/nest-logger';
|
|
869
|
+
|
|
870
|
+
export class HttpService {
|
|
871
|
+
private readonly logger = new AppLogger('HttpService');
|
|
872
|
+
|
|
873
|
+
handleRequest(req: any) {
|
|
874
|
+
const data: DataM2I = {
|
|
875
|
+
header: {
|
|
876
|
+
sessionId: req.sessionId,
|
|
877
|
+
transactionId: req.transactionId,
|
|
878
|
+
identity: {},
|
|
879
|
+
},
|
|
880
|
+
protocol: {
|
|
881
|
+
requestId: req.id,
|
|
882
|
+
method: req.method,
|
|
883
|
+
path: req.path,
|
|
884
|
+
},
|
|
885
|
+
body: {
|
|
886
|
+
endpoint: req.path,
|
|
887
|
+
params: req.params,
|
|
888
|
+
},
|
|
889
|
+
};
|
|
890
|
+
|
|
891
|
+
this.logger.loggerM2.info(
|
|
892
|
+
'api.endpoint',
|
|
893
|
+
'[HTTP_REQUEST]',
|
|
894
|
+
data,
|
|
895
|
+
'Incoming HTTP request'
|
|
896
|
+
);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
```
|
|
900
|
+
|
|
901
|
+
#### M3 Message Format
|
|
902
|
+
|
|
903
|
+
```typescript
|
|
904
|
+
import { AppLogger } from '@eqxjs/nest-logger';
|
|
905
|
+
import { DataM3I } from '@eqxjs/nest-logger';
|
|
906
|
+
|
|
907
|
+
export class DatabaseService {
|
|
908
|
+
private readonly logger = new AppLogger('DatabaseService');
|
|
909
|
+
|
|
910
|
+
queryDatabase(query: string) {
|
|
911
|
+
const data: DataM3I = {
|
|
912
|
+
header: {
|
|
913
|
+
sessionId: 'session-123',
|
|
914
|
+
transactionId: 'txn-456',
|
|
915
|
+
identity: {},
|
|
916
|
+
},
|
|
917
|
+
service: {
|
|
918
|
+
serviceName: 'PostgreSQL',
|
|
919
|
+
invoke: 'SELECT',
|
|
920
|
+
name: 'users',
|
|
921
|
+
identity: {},
|
|
922
|
+
},
|
|
923
|
+
body: {
|
|
924
|
+
query: query,
|
|
925
|
+
database: 'users_db',
|
|
926
|
+
},
|
|
927
|
+
};
|
|
928
|
+
|
|
929
|
+
this.logger.loggerM3.debug(
|
|
930
|
+
'db.query',
|
|
931
|
+
'[DB_REQUEST]',
|
|
932
|
+
data,
|
|
933
|
+
`Executing query: ${query}`
|
|
934
|
+
);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
```
|
|
938
|
+
|
|
939
|
+
### Utility Functions
|
|
940
|
+
|
|
941
|
+
#### Time Performance Measurement
|
|
942
|
+
|
|
943
|
+
```typescript
|
|
944
|
+
import { TimeDiff } from '@eqxjs/nest-logger';
|
|
945
|
+
|
|
946
|
+
export class PerformanceService {
|
|
947
|
+
async trackOperation() {
|
|
948
|
+
const timer = new TimeDiff();
|
|
949
|
+
|
|
950
|
+
// Perform operation
|
|
951
|
+
await this.heavyOperation();
|
|
952
|
+
|
|
953
|
+
const duration = timer.diff(); // Returns time in milliseconds
|
|
954
|
+
console.log(`Operation took ${duration}ms`);
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
```
|
|
958
|
+
|
|
959
|
+
#### Date Formatting
|
|
960
|
+
|
|
961
|
+
```typescript
|
|
962
|
+
import { dateFormat } from '@eqxjs/nest-logger';
|
|
963
|
+
|
|
964
|
+
const timestamp = dateFormat(); // Returns: "2026-01-12T10:30:00.000Z"
|
|
965
|
+
```
|
|
966
|
+
|
|
967
|
+
#### Action Message Constants
|
|
968
|
+
|
|
969
|
+
```typescript
|
|
970
|
+
import { ActionMessage } from '@eqxjs/nest-logger';
|
|
971
|
+
|
|
972
|
+
// Use predefined action tags
|
|
973
|
+
console.log(ActionMessage.consume()); // [CONSUMING]
|
|
974
|
+
console.log(ActionMessage.consumed()); // [CONSUMED]
|
|
975
|
+
console.log(ActionMessage.produce()); // [PRODUCING]
|
|
976
|
+
console.log(ActionMessage.produced()); // [PRODUCED]
|
|
977
|
+
console.log(ActionMessage.httpRequest()); // [HTTP_REQUEST]
|
|
978
|
+
console.log(ActionMessage.httpResponse()); // [HTTP_RESPONSE]
|
|
979
|
+
console.log(ActionMessage.wsRecv()); // [WS_RECEIVED]
|
|
980
|
+
console.log(ActionMessage.wsSent()); // [WS_SENT]
|
|
981
|
+
console.log(ActionMessage.dbRequest()); // [DB_REQUEST]
|
|
982
|
+
console.log(ActionMessage.dbResponse()); // [DB_RESPONSE]
|
|
983
|
+
console.log(ActionMessage.appLogic()); // [APP_LOGIC]
|
|
984
|
+
console.log(ActionMessage.inbound()); // [INBOUND]
|
|
985
|
+
console.log(ActionMessage.outbound()); // [OUTBOUND]
|
|
986
|
+
console.log(ActionMessage.exception()); // [EXCEPTION]
|
|
987
|
+
```
|
|
988
|
+
|
|
989
|
+
#### Additional Helper Functions
|
|
990
|
+
|
|
991
|
+
**Log Level Checking**
|
|
992
|
+
|
|
993
|
+
```typescript
|
|
994
|
+
import { isLevelEnable } from '@eqxjs/nest-logger';
|
|
995
|
+
|
|
996
|
+
process.env.LOG_LEVEL = 'info,error';
|
|
997
|
+
|
|
998
|
+
if (isLevelEnable('debug')) {
|
|
999
|
+
// This won't execute
|
|
1000
|
+
console.log('Debug is enabled');
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
if (isLevelEnable('info')) {
|
|
1004
|
+
// This will execute
|
|
1005
|
+
console.log('Info is enabled');
|
|
1006
|
+
}
|
|
1007
|
+
```
|
|
1008
|
+
|
|
1009
|
+
**Message Masking**
|
|
1010
|
+
|
|
1011
|
+
```typescript
|
|
1012
|
+
import { maskMessageReplacer, logStringify } from '@eqxjs/nest-logger';
|
|
1013
|
+
|
|
1014
|
+
process.env.LOG_MASK_KEYS = 'password,apiKey,token';
|
|
1015
|
+
|
|
1016
|
+
const sensitiveData = {
|
|
1017
|
+
username: 'john',
|
|
1018
|
+
password: 'secret123',
|
|
1019
|
+
apiKey: 'abc-def-ghi',
|
|
1020
|
+
email: 'john@example.com'
|
|
1021
|
+
};
|
|
1022
|
+
|
|
1023
|
+
// Stringify with automatic masking
|
|
1024
|
+
const masked = logStringify(sensitiveData);
|
|
1025
|
+
console.log(masked);
|
|
1026
|
+
// Output: {"username":"john","password":"*****","apiKey":"*****","email":"john@example.com"}
|
|
1027
|
+
|
|
1028
|
+
// Use replacer function directly with JSON.stringify
|
|
1029
|
+
const manualMask = JSON.stringify(sensitiveData, maskMessageReplacer);
|
|
1030
|
+
```
|
|
1031
|
+
|
|
1032
|
+
## Configuration
|
|
1033
|
+
|
|
1034
|
+
### Environment Variables
|
|
1035
|
+
|
|
1036
|
+
Configure the logger behavior using these environment variables:
|
|
1037
|
+
|
|
1038
|
+
| Variable | Type | Default | Description |
|
|
1039
|
+
|----------|------|---------|-------------|
|
|
1040
|
+
| `LOG_LEVEL` | string | - | Comma-separated list of enabled log levels: `debug,info,log,warn,error,verbose` |
|
|
1041
|
+
| `LOG_MASK_KEYS` | string | - | Comma-separated list of field names to mask (e.g., `password,token,apiKey,secret`) |
|
|
1042
|
+
| `APP_VERSION` | string | - | Application version included in telemetry and logs |
|
|
1043
|
+
| `DESTINATION` | string | - | Deployment destination/environment name |
|
|
1044
|
+
| `TELEMETRY_IGNORE_CODE` | string | - | Comma-separated list of result codes to exclude from telemetry metrics |
|
|
1045
|
+
|
|
1046
|
+
**Example Configuration:**
|
|
1047
|
+
|
|
1048
|
+
```bash
|
|
1049
|
+
# .env file
|
|
1050
|
+
LOG_LEVEL=debug,info,warn,error,verbose
|
|
1051
|
+
LOG_MASK_KEYS=password,secret,token,apiKey,creditCard,ssn
|
|
1052
|
+
APP_VERSION=1.0.0
|
|
1053
|
+
DESTINATION=production
|
|
1054
|
+
TELEMETRY_IGNORE_CODE=404,401,403
|
|
1055
|
+
```
|
|
1056
|
+
|
|
1057
|
+
### Log Level Filtering
|
|
1058
|
+
|
|
1059
|
+
Control which log levels are output by configuring the `LOG_LEVEL` environment variable:
|
|
1060
|
+
|
|
1061
|
+
```typescript
|
|
1062
|
+
// Only logs matching the LOG_LEVEL env var will be output
|
|
1063
|
+
process.env.LOG_LEVEL = 'info,error';
|
|
1064
|
+
|
|
1065
|
+
logger.debug('This will NOT be logged');
|
|
1066
|
+
logger.info('This WILL be logged');
|
|
1067
|
+
logger.error('This WILL be logged');
|
|
1068
|
+
logger.verbose('This will NOT be logged');
|
|
1069
|
+
```
|
|
1070
|
+
|
|
1071
|
+
### Sensitive Data Masking
|
|
1072
|
+
|
|
1073
|
+
Protect sensitive information by automatically masking specified fields:
|
|
1074
|
+
|
|
1075
|
+
```typescript
|
|
1076
|
+
// Set masked fields via environment
|
|
1077
|
+
process.env.LOG_MASK_KEYS = 'password,creditCard,ssn,token';
|
|
1078
|
+
|
|
1079
|
+
const userData = {
|
|
1080
|
+
username: 'john',
|
|
1081
|
+
password: 'secret123',
|
|
1082
|
+
creditCard: '4111-1111-1111-1111',
|
|
1083
|
+
email: 'john@example.com',
|
|
1084
|
+
token: 'abc-def-123'
|
|
1085
|
+
};
|
|
1086
|
+
|
|
1087
|
+
logger.log(userData);
|
|
1088
|
+
// Output: { username: 'john', password: '*****', creditCard: '*****', email: 'john@example.com', token: '*****' }
|
|
1089
|
+
```
|
|
1090
|
+
|
|
1091
|
+
### Telemetry Filtering
|
|
1092
|
+
|
|
1093
|
+
Exclude specific result codes from telemetry metrics to reduce noise:
|
|
1094
|
+
|
|
1095
|
+
```typescript
|
|
1096
|
+
// Don't track 404 and 401 errors in telemetry
|
|
1097
|
+
process.env.TELEMETRY_IGNORE_CODE = '404,401';
|
|
1098
|
+
|
|
1099
|
+
// These won't be recorded in OpenTelemetry metrics
|
|
1100
|
+
logger.app.summaryError('Not found', '404', 100);
|
|
1101
|
+
logger.app.summaryError('Unauthorized', '401', 50);
|
|
1102
|
+
|
|
1103
|
+
// This will be recorded
|
|
1104
|
+
logger.app.summaryError('Server error', '500', 200);
|
|
1105
|
+
```
|
|
1106
|
+
|
|
1107
|
+
### Message Truncation
|
|
1108
|
+
|
|
1109
|
+
Long messages are automatically truncated to prevent log overflow:
|
|
1110
|
+
|
|
1111
|
+
```typescript
|
|
1112
|
+
// Messages longer than 4096 characters are truncated
|
|
1113
|
+
const longMessage = 'a'.repeat(5000);
|
|
1114
|
+
logger.info(longMessage);
|
|
1115
|
+
// Logged message will be truncated to 4096 chars + '...'
|
|
1116
|
+
|
|
1117
|
+
// The limit is defined in DEFAULT_VALUES.MAX_MESSAGE_LENGTH
|
|
1118
|
+
import { DEFAULT_VALUES } from '@eqxjs/nest-logger';
|
|
1119
|
+
console.log(DEFAULT_VALUES.MAX_MESSAGE_LENGTH); // 4096
|
|
1120
|
+
```
|
|
1121
|
+
|
|
1122
|
+
## OpenTelemetry Integration
|
|
1123
|
+
|
|
1124
|
+
The logger automatically integrates with OpenTelemetry for metrics, tracing, and logs:
|
|
1125
|
+
|
|
1126
|
+
### Metrics Collected
|
|
1127
|
+
|
|
1128
|
+
- **Counter**: `total_request` - Total number of operations
|
|
1129
|
+
- **Histogram**: `latency` - Operation duration in milliseconds
|
|
1130
|
+
|
|
1131
|
+
### Span Attributes
|
|
1132
|
+
|
|
1133
|
+
- `application.code` - Result code
|
|
1134
|
+
- `application.success` - Operation success status
|
|
1135
|
+
- `application.duration` - Processing time
|
|
1136
|
+
- `application.time` - Timestamp
|
|
1137
|
+
- `application.source` - Originating service
|
|
1138
|
+
- `application.stack` - Error stack trace (if applicable)
|
|
1139
|
+
|
|
1140
|
+
### Logs API Integration
|
|
1141
|
+
|
|
1142
|
+
Starting from v3.1.0, summary logs are automatically pushed to OpenTelemetry Logs API with structured attributes:
|
|
1143
|
+
|
|
1144
|
+
**Log Record Attributes:**
|
|
1145
|
+
- `log.type` - Type of log (summary)
|
|
1146
|
+
- `app.result` - Result description
|
|
1147
|
+
- `app.result.code` - Result code
|
|
1148
|
+
- `service.time` - Service processing time (ms)
|
|
1149
|
+
- `originate.service.name` - Source service name
|
|
1150
|
+
- `record.name` - Record identifier
|
|
1151
|
+
- `session.id` - Session identifier
|
|
1152
|
+
- `transaction.id` - Transaction identifier
|
|
1153
|
+
- `channel` - Communication channel
|
|
1154
|
+
- `use.case` - Use case name
|
|
1155
|
+
- `use.case.step` - Use case step
|
|
1156
|
+
- `user` - User identifier
|
|
1157
|
+
- `device` - Device identifier(s)
|
|
1158
|
+
- `instance` - Instance/hostname
|
|
1159
|
+
- `component.name` - Component name
|
|
1160
|
+
- `component.version` - Component version
|
|
1161
|
+
- `stack` - Error stack trace (included only for summaryError calls)
|
|
1162
|
+
|
|
1163
|
+
**Severity Levels:**
|
|
1164
|
+
- `SeverityNumber.INFO` - For summarySuccess calls
|
|
1165
|
+
- `SeverityNumber.ERROR` - For summaryError calls
|
|
1166
|
+
|
|
1167
|
+
### Example with Telemetry
|
|
1168
|
+
|
|
1169
|
+
```typescript
|
|
1170
|
+
// Summary methods automatically record telemetry
|
|
1171
|
+
this.logger.app.summarySuccess(
|
|
1172
|
+
'Operation completed',
|
|
1173
|
+
'200',
|
|
1174
|
+
150, // Service time in ms
|
|
1175
|
+
'UserService',
|
|
1176
|
+
'record-123'
|
|
1177
|
+
);
|
|
1178
|
+
|
|
1179
|
+
// This will:
|
|
1180
|
+
// 1. Log the summary to Winston
|
|
1181
|
+
// 2. Push log to OpenTelemetry Logs API
|
|
1182
|
+
// 3. Increment the counter metric
|
|
1183
|
+
// 4. Record histogram metric
|
|
1184
|
+
// 5. Create a span with attributes
|
|
1185
|
+
```
|
|
1186
|
+
|
|
1187
|
+
## API Reference
|
|
1188
|
+
|
|
1189
|
+
### CustomLogger
|
|
1190
|
+
|
|
1191
|
+
Basic logger with standard log levels. Extends Winston logger functionality.
|
|
1192
|
+
|
|
1193
|
+
**Constructor:**
|
|
1194
|
+
```typescript
|
|
1195
|
+
new CustomLogger(context?: string)
|
|
1196
|
+
```
|
|
1197
|
+
|
|
1198
|
+
**Methods:**
|
|
1199
|
+
- `log(message: any, context?: string): void` - Info level logging
|
|
1200
|
+
- `error(message: any, trace?: string, context?: string): void` - Error level logging
|
|
1201
|
+
- `warn(message: any, context?: string): void` - Warning level logging
|
|
1202
|
+
- `debug(message: any, context?: string): void` - Debug level logging
|
|
1203
|
+
- `verbose(message: any, context?: string): void` - Verbose level logging
|
|
1204
|
+
|
|
1205
|
+
**Example:**
|
|
1206
|
+
```typescript
|
|
1207
|
+
const logger = new CustomLogger('MyService');
|
|
1208
|
+
logger.log('Application started');
|
|
1209
|
+
logger.debug('Debug information');
|
|
1210
|
+
logger.warn('Warning message');
|
|
1211
|
+
logger.error('Error occurred', 'stack trace');
|
|
1212
|
+
logger.verbose('Detailed verbose log');
|
|
1213
|
+
```
|
|
1214
|
+
|
|
1215
|
+
### BaseAppLogger
|
|
1216
|
+
|
|
1217
|
+
Advanced logger with structured logging and OpenTelemetry integration.
|
|
1218
|
+
|
|
1219
|
+
**Constructor:**
|
|
1220
|
+
```typescript
|
|
1221
|
+
new BaseAppLogger(appName?: string, context?: string)
|
|
1222
|
+
```
|
|
1223
|
+
|
|
1224
|
+
**All Parameters (for logging methods):**
|
|
1225
|
+
- `message: any` - The log message (required)
|
|
1226
|
+
- `action?: string` - Action tag (default: 'none')
|
|
1227
|
+
- `originateServiceName?: string` - Source service (default: 'none')
|
|
1228
|
+
- `recordName?: string` - Record identifier (default: 'none')
|
|
1229
|
+
- `sessionId?: string` - Session ID (default: 'none')
|
|
1230
|
+
- `transactionId?: string` - Transaction ID (default: 'none')
|
|
1231
|
+
- `channel?: string` - Channel identifier (default: 'none')
|
|
1232
|
+
- `componentVersion?: string` - Component version (default: 'none')
|
|
1233
|
+
- `useCase?: string` - Use case name (default: 'none')
|
|
1234
|
+
- `useCaseStep?: string` - Use case step (default: 'none')
|
|
1235
|
+
- `user?: string` - User identifier (default: 'none')
|
|
1236
|
+
- `device?: string | string[]` - Device identifier(s) (default: 'none')
|
|
1237
|
+
- `public_?: string` - Public identifier (default: 'none')
|
|
1238
|
+
- `opt?: LoggerOpt` - Optional configuration object
|
|
1239
|
+
|
|
1240
|
+
**Logging Methods:**
|
|
1241
|
+
```typescript
|
|
1242
|
+
debug(message, action?, ...): void
|
|
1243
|
+
info(message, action?, ...): void
|
|
1244
|
+
log(message, action?, ...): void // Alias for info()
|
|
1245
|
+
warn(message, action?, ...): void
|
|
1246
|
+
error(message, action?, ...): void
|
|
1247
|
+
verbose(message, action?, ...): void
|
|
1248
|
+
```
|
|
1249
|
+
|
|
1250
|
+
**Summary Methods:**
|
|
1251
|
+
```typescript
|
|
1252
|
+
summarySuccess(
|
|
1253
|
+
appResult?: string,
|
|
1254
|
+
appResultCode?: string,
|
|
1255
|
+
serviceTime?: number,
|
|
1256
|
+
originateServiceName?: string,
|
|
1257
|
+
recordName?: string,
|
|
1258
|
+
sessionId?: string,
|
|
1259
|
+
transactionId?: string,
|
|
1260
|
+
channel?: string,
|
|
1261
|
+
componentVersion?: string,
|
|
1262
|
+
useCase?: string,
|
|
1263
|
+
useCaseStep?: string,
|
|
1264
|
+
user?: string,
|
|
1265
|
+
device?: string | string[],
|
|
1266
|
+
public_?: string,
|
|
1267
|
+
opt?: LoggerOpt
|
|
1268
|
+
): LoggerDto
|
|
1269
|
+
|
|
1270
|
+
summaryError(
|
|
1271
|
+
appResult?: string,
|
|
1272
|
+
appResultCode?: string,
|
|
1273
|
+
serviceTime?: number,
|
|
1274
|
+
originateServiceName?: string,
|
|
1275
|
+
recordName?: string,
|
|
1276
|
+
sessionId?: string,
|
|
1277
|
+
transactionId?: string,
|
|
1278
|
+
channel?: string,
|
|
1279
|
+
componentVersion?: string,
|
|
1280
|
+
useCase?: string,
|
|
1281
|
+
useCaseStep?: string,
|
|
1282
|
+
user?: string,
|
|
1283
|
+
device?: string | string[],
|
|
1284
|
+
public_?: string,
|
|
1285
|
+
stack?: string[],
|
|
1286
|
+
opt?: LoggerOpt
|
|
1287
|
+
): LoggerDto
|
|
1288
|
+
```
|
|
1289
|
+
|
|
1290
|
+
**Example:**
|
|
1291
|
+
```typescript
|
|
1292
|
+
const logger = new BaseAppLogger('MyApp', 'MyContext');
|
|
1293
|
+
|
|
1294
|
+
// Simple logging
|
|
1295
|
+
logger.info('User logged in', '[USER_LOGIN]');
|
|
1296
|
+
|
|
1297
|
+
// Full structured logging
|
|
1298
|
+
logger.info(
|
|
1299
|
+
'Payment processed',
|
|
1300
|
+
'[PAYMENT_SUCCESS]',
|
|
1301
|
+
'PaymentService',
|
|
1302
|
+
'pay-123',
|
|
1303
|
+
'sess-456',
|
|
1304
|
+
'txn-789',
|
|
1305
|
+
'mobile',
|
|
1306
|
+
'1.0.0',
|
|
1307
|
+
'ProcessPayment',
|
|
1308
|
+
'Complete',
|
|
1309
|
+
'user@example.com',
|
|
1310
|
+
['mobile', 'android'],
|
|
1311
|
+
'public-ref-123'
|
|
1312
|
+
);
|
|
1313
|
+
|
|
1314
|
+
// Summary with telemetry
|
|
1315
|
+
logger.summarySuccess('Success', '200', 150, 'PaymentService', 'pay-123');
|
|
1316
|
+
logger.summaryError('Failed', '500', 200, 'PaymentService', 'pay-123', undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, ['Error stack']);
|
|
1317
|
+
```
|
|
1318
|
+
|
|
1319
|
+
### AppLogger
|
|
1320
|
+
|
|
1321
|
+
Wrapper class providing access to all logger types (app, M1, M2, M3).
|
|
1322
|
+
|
|
1323
|
+
**Constructor:**
|
|
1324
|
+
```typescript
|
|
1325
|
+
new AppLogger(appName?: string, context?: string)
|
|
1326
|
+
```
|
|
1327
|
+
|
|
1328
|
+
**Properties:**
|
|
1329
|
+
- `app: BaseAppLogger` - Standard structured logger
|
|
1330
|
+
- `loggerM1: LoggerFormat` - M1 message format logger
|
|
1331
|
+
- `loggerM2: LoggerFormat` - M2 message format logger
|
|
1332
|
+
- `loggerM3: LoggerFormat` - M3 message format logger
|
|
1333
|
+
|
|
1334
|
+
**Example:**
|
|
1335
|
+
```typescript
|
|
1336
|
+
const logger = new AppLogger('MyApp');
|
|
1337
|
+
|
|
1338
|
+
// Use standard logger
|
|
1339
|
+
logger.app.info('Standard log');
|
|
1340
|
+
|
|
1341
|
+
// Use M1 format
|
|
1342
|
+
logger.loggerM1.info('topic', '[ACTION]', dataM1, 'Message');
|
|
1343
|
+
|
|
1344
|
+
// Use M2 format
|
|
1345
|
+
logger.loggerM2.info('topic', '[ACTION]', dataM2, 'Message');
|
|
1346
|
+
|
|
1347
|
+
// Use M3 format
|
|
1348
|
+
logger.loggerM3.info('topic', '[ACTION]', dataM3, 'Message');
|
|
1349
|
+
```
|
|
1350
|
+
|
|
1351
|
+
### LoggerFormat (M1/M2/M3)
|
|
1352
|
+
|
|
1353
|
+
Format-specific loggers for different message protocols.
|
|
1354
|
+
|
|
1355
|
+
**Constructor:**
|
|
1356
|
+
```typescript
|
|
1357
|
+
new LoggerFormat(baseAppLogger: BaseAppLogger, messageType: 'M1' | 'M2' | 'M3')
|
|
1358
|
+
```
|
|
1359
|
+
|
|
1360
|
+
**Logging Methods:**
|
|
1361
|
+
```typescript
|
|
1362
|
+
debug(topic: string, action: string, data: DataM1I | DataM2I | DataM3I, message?: string, opt?: LoggerOpt): void
|
|
1363
|
+
info(topic: string, action: string, data: DataM1I | DataM2I | DataM3I, message?: string, opt?: LoggerOpt): void
|
|
1364
|
+
log(topic: string, action: string, data: DataM1I | DataM2I | DataM3I, message?: string, opt?: LoggerOpt): void
|
|
1365
|
+
error(topic: string, action: string, data: DataM1I | DataM2I | DataM3I, message?: string, opt?: LoggerOpt): void
|
|
1366
|
+
warn(topic: string, action: string, data: DataM1I | DataM2I | DataM3I, message?: string, opt?: LoggerOpt): void
|
|
1367
|
+
verbose(topic: string, action: string, data: DataM1I | DataM2I | DataM3I, message?: string, opt?: LoggerOpt): void
|
|
1368
|
+
```
|
|
1369
|
+
|
|
1370
|
+
**Summary Methods:**
|
|
1371
|
+
```typescript
|
|
1372
|
+
summarySuccess(
|
|
1373
|
+
topic: string,
|
|
1374
|
+
appResult: string,
|
|
1375
|
+
appResultCode: string,
|
|
1376
|
+
serviceTime: number,
|
|
1377
|
+
data: DataM1I | DataM2I | DataM3I,
|
|
1378
|
+
opt?: LoggerOpt
|
|
1379
|
+
): LoggerDto
|
|
1380
|
+
|
|
1381
|
+
summaryError(
|
|
1382
|
+
topic: string,
|
|
1383
|
+
appResult: string,
|
|
1384
|
+
appResultCode: string,
|
|
1385
|
+
serviceTime: number,
|
|
1386
|
+
data: DataM1I | DataM2I | DataM3I,
|
|
1387
|
+
stack?: string[],
|
|
1388
|
+
opt?: LoggerOpt
|
|
1389
|
+
): LoggerDto
|
|
1390
|
+
```
|
|
1391
|
+
|
|
1392
|
+
**Example:**
|
|
1393
|
+
```typescript
|
|
1394
|
+
import { AppLogger, DataM1I } from '@eqxjs/nest-logger';
|
|
1395
|
+
|
|
1396
|
+
const logger = new AppLogger('KafkaService');
|
|
1397
|
+
const data: DataM1I = {
|
|
1398
|
+
header: {
|
|
1399
|
+
sessionId: 'sess-123',
|
|
1400
|
+
transactionId: 'txn-456',
|
|
1401
|
+
broker: 'kafka'
|
|
1402
|
+
}
|
|
1403
|
+
};
|
|
1404
|
+
|
|
1405
|
+
// M1 format logging
|
|
1406
|
+
logger.loggerM1.info('payment.topic', '[CONSUMING]', data, 'Processing message');
|
|
1407
|
+
logger.loggerM1.summarySuccess('payment.topic', 'Success', '200', 150, data);
|
|
1408
|
+
```
|
|
1409
|
+
|
|
1410
|
+
### Utility Classes and Functions
|
|
1411
|
+
|
|
1412
|
+
#### TimeDiff
|
|
1413
|
+
|
|
1414
|
+
Measures time elapsed using high-resolution timer.
|
|
1415
|
+
|
|
1416
|
+
**Constructor:**
|
|
1417
|
+
```typescript
|
|
1418
|
+
new TimeDiff()
|
|
1419
|
+
```
|
|
1420
|
+
|
|
1421
|
+
**Methods:**
|
|
1422
|
+
- `diff(): number` - Returns elapsed time in milliseconds
|
|
1423
|
+
|
|
1424
|
+
**Example:**
|
|
1425
|
+
```typescript
|
|
1426
|
+
import { TimeDiff } from '@eqxjs/nest-logger';
|
|
1427
|
+
|
|
1428
|
+
const timer = new TimeDiff();
|
|
1429
|
+
await someAsyncOperation();
|
|
1430
|
+
const duration = timer.diff();
|
|
1431
|
+
console.log(`Operation took ${duration}ms`);
|
|
1432
|
+
```
|
|
1433
|
+
|
|
1434
|
+
#### ActionMessage
|
|
1435
|
+
|
|
1436
|
+
Static class providing predefined action tags for consistent logging.
|
|
1437
|
+
|
|
1438
|
+
**Static Methods:**
|
|
1439
|
+
- `consume(): string` - Returns `[CONSUMING]`
|
|
1440
|
+
- `consumed(): string` - Returns `[CONSUMED]`
|
|
1441
|
+
- `produce(): string` - Returns `[PRODUCING]`
|
|
1442
|
+
- `produced(): string` - Returns `[PRODUCED]`
|
|
1443
|
+
- `httpRequest(): string` - Returns `[HTTP_REQUEST]`
|
|
1444
|
+
- `httpResponse(): string` - Returns `[HTTP_RESPONSE]`
|
|
1445
|
+
- `wsRecv(): string` - Returns `[WS_RECEIVED]`
|
|
1446
|
+
- `wsSent(): string` - Returns `[WS_SENT]`
|
|
1447
|
+
- `dbRequest(): string` - Returns `[DB_REQUEST]`
|
|
1448
|
+
- `dbResponse(): string` - Returns `[DB_RESPONSE]`
|
|
1449
|
+
- `appLogic(): string` - Returns `[APP_LOGIC]`
|
|
1450
|
+
- `inbound(): string` - Returns `[INBOUND]`
|
|
1451
|
+
- `outbound(): string` - Returns `[OUTBOUND]`
|
|
1452
|
+
- `exception(): string` - Returns `[EXCEPTION]`
|
|
1453
|
+
|
|
1454
|
+
**Example:**
|
|
1455
|
+
```typescript
|
|
1456
|
+
import { ActionMessage } from '@eqxjs/nest-logger';
|
|
1457
|
+
|
|
1458
|
+
logger.info('Consuming message', ActionMessage.consume());
|
|
1459
|
+
logger.info('Message consumed', ActionMessage.consumed());
|
|
1460
|
+
logger.info('HTTP request received', ActionMessage.httpRequest());
|
|
1461
|
+
```
|
|
1462
|
+
|
|
1463
|
+
#### Helper Functions
|
|
1464
|
+
|
|
1465
|
+
**dateFormat()**
|
|
1466
|
+
|
|
1467
|
+
Returns current timestamp in ISO 8601 format.
|
|
1468
|
+
|
|
1469
|
+
```typescript
|
|
1470
|
+
import { dateFormat } from '@eqxjs/nest-logger';
|
|
1471
|
+
|
|
1472
|
+
const timestamp = dateFormat();
|
|
1473
|
+
console.log(timestamp); // "2026-01-12T10:30:00.000Z"
|
|
1474
|
+
```
|
|
1475
|
+
|
|
1476
|
+
**isLevelEnable(level: string): boolean**
|
|
1477
|
+
|
|
1478
|
+
Checks if a log level is enabled based on `LOG_LEVEL` environment variable.
|
|
1479
|
+
|
|
1480
|
+
```typescript
|
|
1481
|
+
import { isLevelEnable } from '@eqxjs/nest-logger';
|
|
1482
|
+
|
|
1483
|
+
process.env.LOG_LEVEL = 'info,error';
|
|
1484
|
+
|
|
1485
|
+
if (isLevelEnable('debug')) {
|
|
1486
|
+
// Won't execute
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
if (isLevelEnable('info')) {
|
|
1490
|
+
// Will execute
|
|
1491
|
+
}
|
|
1492
|
+
```
|
|
1493
|
+
|
|
1494
|
+
**logStringify(message: any): string**
|
|
1495
|
+
|
|
1496
|
+
Converts any value to string with automatic sensitive data masking.
|
|
1497
|
+
|
|
1498
|
+
```typescript
|
|
1499
|
+
import { logStringify } from '@eqxjs/nest-logger';
|
|
1500
|
+
|
|
1501
|
+
process.env.LOG_MASK_KEYS = 'password,token';
|
|
1502
|
+
|
|
1503
|
+
const data = { username: 'john', password: 'secret' };
|
|
1504
|
+
const masked = logStringify(data);
|
|
1505
|
+
console.log(masked); // {"username":"john","password":"*****"}
|
|
1506
|
+
```
|
|
1507
|
+
|
|
1508
|
+
**maskMessageReplacer(key: string, value: any): any**
|
|
1509
|
+
|
|
1510
|
+
JSON replacer function for masking sensitive fields.
|
|
1511
|
+
|
|
1512
|
+
```typescript
|
|
1513
|
+
import { maskMessageReplacer } from '@eqxjs/nest-logger';
|
|
1514
|
+
|
|
1515
|
+
process.env.LOG_MASK_KEYS = 'apiKey,secret';
|
|
1516
|
+
|
|
1517
|
+
const data = { apiKey: 'abc123', name: 'John' };
|
|
1518
|
+
const masked = JSON.stringify(data, maskMessageReplacer);
|
|
1519
|
+
console.log(masked); // {"apiKey":"*****","name":"John"}
|
|
1520
|
+
```
|
|
1521
|
+
|
|
1522
|
+
### Interfaces
|
|
1523
|
+
|
|
1524
|
+
#### DataM1I
|
|
1525
|
+
|
|
1526
|
+
Message format for broker/queue operations.
|
|
1527
|
+
|
|
1528
|
+
```typescript
|
|
1529
|
+
interface DataM1I {
|
|
1530
|
+
header: DataHeaderI;
|
|
1531
|
+
}
|
|
1532
|
+
```
|
|
1533
|
+
|
|
1534
|
+
#### DataM2I
|
|
1535
|
+
|
|
1536
|
+
Message format for HTTP/protocol operations.
|
|
1537
|
+
|
|
1538
|
+
```typescript
|
|
1539
|
+
interface DataM2I {
|
|
1540
|
+
header: DataHeaderI;
|
|
1541
|
+
protocol: DataProtocolI;
|
|
1542
|
+
}
|
|
1543
|
+
```
|
|
1544
|
+
|
|
1545
|
+
#### DataM3I
|
|
1546
|
+
|
|
1547
|
+
Message format for service-to-service operations.
|
|
1548
|
+
|
|
1549
|
+
```typescript
|
|
1550
|
+
interface DataM3I {
|
|
1551
|
+
header: DataHeaderI;
|
|
1552
|
+
service: DataServiceI;
|
|
1553
|
+
}
|
|
1554
|
+
```
|
|
1555
|
+
|
|
1556
|
+
#### DataHeaderI
|
|
1557
|
+
|
|
1558
|
+
Common header fields for all message formats.
|
|
1559
|
+
|
|
1560
|
+
```typescript
|
|
1561
|
+
interface DataHeaderI {
|
|
1562
|
+
sessionId?: string;
|
|
1563
|
+
transactionId?: string;
|
|
1564
|
+
broker?: string;
|
|
1565
|
+
channel?: string;
|
|
1566
|
+
useCase?: string;
|
|
1567
|
+
useCaseStep?: string;
|
|
1568
|
+
[key: string]: unknown;
|
|
1569
|
+
}
|
|
1570
|
+
```
|
|
1571
|
+
|
|
1572
|
+
#### LoggerOpt
|
|
1573
|
+
|
|
1574
|
+
Optional configuration for logging.
|
|
1575
|
+
|
|
1576
|
+
```typescript
|
|
1577
|
+
interface LoggerOpt {
|
|
1578
|
+
broker?: string;
|
|
1579
|
+
[key: string]: unknown;
|
|
1580
|
+
}
|
|
1581
|
+
```
|
|
1582
|
+
|
|
1583
|
+
#### LoggerDto
|
|
1584
|
+
|
|
1585
|
+
Data transfer object for log entries.
|
|
1586
|
+
|
|
1587
|
+
```typescript
|
|
1588
|
+
class LoggerDto {
|
|
1589
|
+
level?: string;
|
|
1590
|
+
timestamp?: string;
|
|
1591
|
+
message?: string;
|
|
1592
|
+
action?: string;
|
|
1593
|
+
componentName?: string;
|
|
1594
|
+
appName?: string;
|
|
1595
|
+
// ... and many more fields
|
|
1596
|
+
}
|
|
1597
|
+
```
|
|
1598
|
+
|
|
1599
|
+
## Best Practices
|
|
1600
|
+
|
|
1601
|
+
### 1. Choose the Right Logger
|
|
1602
|
+
|
|
1603
|
+
```typescript
|
|
1604
|
+
// For simple applications - use CustomLogger
|
|
1605
|
+
const logger = new CustomLogger('MyApp');
|
|
1606
|
+
logger.log('Simple message');
|
|
1607
|
+
|
|
1608
|
+
// For structured logging - use BaseAppLogger
|
|
1609
|
+
const appLogger = new BaseAppLogger('MyApp', 'UserService');
|
|
1610
|
+
appLogger.info('User action', '[USER_CREATE]');
|
|
1611
|
+
|
|
1612
|
+
// For multiple formats - use AppLogger
|
|
1613
|
+
const multiLogger = new AppLogger('MyApp');
|
|
1614
|
+
multiLogger.app.info('Standard log');
|
|
1615
|
+
multiLogger.loggerM1.info('kafka.topic', '[CONSUMING]', data);
|
|
1616
|
+
```
|
|
1617
|
+
|
|
1618
|
+
### 2. Use Appropriate Log Levels
|
|
1619
|
+
|
|
1620
|
+
Choose the right level based on the situation:
|
|
1621
|
+
|
|
1622
|
+
```typescript
|
|
1623
|
+
// DEBUG - Detailed diagnostic information (development only)
|
|
1624
|
+
logger.debug('Request payload:', { userId: 123, action: 'create' });
|
|
1625
|
+
|
|
1626
|
+
// INFO - General informational messages (normal operations)
|
|
1627
|
+
logger.info('User logged in successfully', '[USER_LOGIN]');
|
|
1628
|
+
|
|
1629
|
+
// WARN - Potentially harmful situations (recoverable issues)
|
|
1630
|
+
logger.warn('API rate limit approaching 80%', '[RATE_LIMIT]');
|
|
1631
|
+
|
|
1632
|
+
// ERROR - Error events that might still allow the app to continue
|
|
1633
|
+
logger.error('Failed to send email notification', '[EMAIL_ERROR]');
|
|
1634
|
+
|
|
1635
|
+
// VERBOSE - Most detailed information (deep debugging only)
|
|
1636
|
+
logger.verbose('Internal state:', { state: complexObject });
|
|
1637
|
+
```
|
|
1638
|
+
|
|
1639
|
+
### 3. Include Meaningful Context
|
|
1640
|
+
|
|
1641
|
+
Always provide context to make logs searchable and traceable:
|
|
1642
|
+
|
|
1643
|
+
```typescript
|
|
1644
|
+
// ❌ Bad - No context
|
|
1645
|
+
logger.log('User created');
|
|
1646
|
+
|
|
1647
|
+
// ✅ Good - With context
|
|
1648
|
+
logger.log('User created', 'UserService');
|
|
1649
|
+
|
|
1650
|
+
// ✅ Better - Structured with action tag
|
|
1651
|
+
logger.app.info(
|
|
1652
|
+
'User created successfully',
|
|
1653
|
+
'[USER_CREATE]',
|
|
1654
|
+
'UserService',
|
|
1655
|
+
'user-123' // recordName
|
|
1656
|
+
);
|
|
1657
|
+
|
|
1658
|
+
// ✅ Best - Full metadata for distributed tracing
|
|
1659
|
+
logger.app.info(
|
|
1660
|
+
'User created successfully',
|
|
1661
|
+
'[USER_CREATE]',
|
|
1662
|
+
'UserService',
|
|
1663
|
+
'user-123', // recordName
|
|
1664
|
+
'sess-456', // sessionId
|
|
1665
|
+
'txn-789', // transactionId
|
|
1666
|
+
'mobile', // channel
|
|
1667
|
+
'1.0.0', // componentVersion
|
|
1668
|
+
'CreateUser', // useCase
|
|
1669
|
+
'Complete' // useCaseStep
|
|
1670
|
+
);
|
|
1671
|
+
```
|
|
1672
|
+
|
|
1673
|
+
### 4. Use Summary Logging for Operations
|
|
1674
|
+
|
|
1675
|
+
Track operation duration and outcomes with summary logs:
|
|
1676
|
+
|
|
1677
|
+
```typescript
|
|
1678
|
+
// ✅ Best Practice - Track operation with telemetry
|
|
1679
|
+
const timer = new TimeDiff();
|
|
1680
|
+
|
|
1681
|
+
try {
|
|
1682
|
+
const result = await operation();
|
|
1683
|
+
|
|
1684
|
+
// Log success with metrics (automatically creates OpenTelemetry data)
|
|
1685
|
+
logger.app.summarySuccess(
|
|
1686
|
+
'Operation completed',
|
|
1687
|
+
'200',
|
|
1688
|
+
timer.diff(),
|
|
1689
|
+
'ServiceName',
|
|
1690
|
+
'record-123',
|
|
1691
|
+
sessionId,
|
|
1692
|
+
transactionId
|
|
1693
|
+
);
|
|
1694
|
+
|
|
1695
|
+
return result;
|
|
1696
|
+
} catch (error) {
|
|
1697
|
+
// Log error with stack trace and metrics
|
|
1698
|
+
logger.app.summaryError(
|
|
1699
|
+
'Operation failed',
|
|
1700
|
+
error.code || '500',
|
|
1701
|
+
timer.diff(),
|
|
1702
|
+
'ServiceName',
|
|
1703
|
+
'record-123',
|
|
1704
|
+
sessionId,
|
|
1705
|
+
transactionId,
|
|
1706
|
+
undefined,
|
|
1707
|
+
undefined,
|
|
1708
|
+
undefined,
|
|
1709
|
+
undefined,
|
|
1710
|
+
undefined,
|
|
1711
|
+
undefined,
|
|
1712
|
+
[error.stack] // Include stack trace
|
|
1713
|
+
);
|
|
1714
|
+
|
|
1715
|
+
throw error;
|
|
1716
|
+
}
|
|
1717
|
+
```
|
|
1718
|
+
|
|
1719
|
+
### 5. Leverage Message Formats
|
|
1720
|
+
|
|
1721
|
+
Use the appropriate format for your use case:
|
|
1722
|
+
|
|
1723
|
+
```typescript
|
|
1724
|
+
// ✅ M1 for message broker/queue operations
|
|
1725
|
+
logger.loggerM1.info(
|
|
1726
|
+
'order.created',
|
|
1727
|
+
ActionMessage.consume(),
|
|
1728
|
+
dataM1,
|
|
1729
|
+
'Processing order message'
|
|
1730
|
+
);
|
|
1731
|
+
|
|
1732
|
+
// ✅ M2 for HTTP/protocol operations
|
|
1733
|
+
logger.loggerM2.info(
|
|
1734
|
+
'api.users',
|
|
1735
|
+
ActionMessage.httpRequest(),
|
|
1736
|
+
dataM2,
|
|
1737
|
+
'POST /api/users'
|
|
1738
|
+
);
|
|
1739
|
+
|
|
1740
|
+
// ✅ M3 for database/service operations
|
|
1741
|
+
logger.loggerM3.debug(
|
|
1742
|
+
'db.users',
|
|
1743
|
+
ActionMessage.dbRequest(),
|
|
1744
|
+
dataM3,
|
|
1745
|
+
'SELECT * FROM users WHERE id = ?'
|
|
1746
|
+
);
|
|
1747
|
+
```
|
|
1748
|
+
|
|
1749
|
+
### 6. Use Action Constants
|
|
1750
|
+
|
|
1751
|
+
Leverage predefined action tags for consistency:
|
|
1752
|
+
|
|
1753
|
+
```typescript
|
|
1754
|
+
import { ActionMessage } from '@eqxjs/nest-logger';
|
|
1755
|
+
|
|
1756
|
+
// ✅ Use constants instead of strings
|
|
1757
|
+
logger.info('Message received', ActionMessage.consume());
|
|
1758
|
+
logger.info('Request sent', ActionMessage.httpRequest());
|
|
1759
|
+
logger.error('Exception occurred', ActionMessage.exception());
|
|
1760
|
+
|
|
1761
|
+
// ❌ Avoid - Manual strings (typos, inconsistency)
|
|
1762
|
+
logger.info('Message received', '[CONSUMMING]'); // Typo!
|
|
1763
|
+
logger.info('Request sent', '[HTTP-REQUEST]'); // Wrong format
|
|
1764
|
+
```
|
|
1765
|
+
|
|
1766
|
+
### 7. Mask Sensitive Data
|
|
1767
|
+
|
|
1768
|
+
Configure masking to protect sensitive information:
|
|
1769
|
+
|
|
1770
|
+
```typescript
|
|
1771
|
+
// Set up masking in environment
|
|
1772
|
+
process.env.LOG_MASK_KEYS = 'password,apiKey,token,creditCard,ssn';
|
|
1773
|
+
|
|
1774
|
+
// ✅ All sensitive fields will be automatically masked
|
|
1775
|
+
const user = {
|
|
1776
|
+
username: 'john',
|
|
1777
|
+
password: 'secret123', // Will be masked
|
|
1778
|
+
email: 'john@example.com'
|
|
1779
|
+
};
|
|
1780
|
+
|
|
1781
|
+
logger.info('User data', user);
|
|
1782
|
+
// Output: { username: 'john', password: '*****', email: 'john@example.com' }
|
|
1783
|
+
```
|
|
1784
|
+
|
|
1785
|
+
### 8. Configure Environment Variables
|
|
1786
|
+
|
|
1787
|
+
Set up proper configuration for each environment:
|
|
1788
|
+
|
|
1789
|
+
```typescript
|
|
1790
|
+
// Development
|
|
1791
|
+
process.env.LOG_LEVEL = 'debug,info,log,warn,error,verbose';
|
|
1792
|
+
process.env.LOG_MASK_KEYS = 'password,token';
|
|
1793
|
+
process.env.TELEMETRY_IGNORE_CODE = '';
|
|
1794
|
+
|
|
1795
|
+
// Production
|
|
1796
|
+
process.env.LOG_LEVEL = 'info,warn,error';
|
|
1797
|
+
process.env.LOG_MASK_KEYS = 'password,token,apiKey,secret,creditCard,ssn';
|
|
1798
|
+
process.env.TELEMETRY_IGNORE_CODE = '404,401,403';
|
|
1799
|
+
process.env.APP_VERSION = '1.0.0';
|
|
1800
|
+
process.env.DESTINATION = 'production';
|
|
1801
|
+
```
|
|
1802
|
+
|
|
1803
|
+
### 9. Use TimeDiff for Performance Tracking
|
|
1804
|
+
|
|
1805
|
+
Measure and log operation performance:
|
|
1806
|
+
|
|
1807
|
+
```typescript
|
|
1808
|
+
import { TimeDiff } from '@eqxjs/nest-logger';
|
|
1809
|
+
|
|
1810
|
+
// ✅ Track operation duration
|
|
1811
|
+
async function processData() {
|
|
1812
|
+
const timer = TimeDiff.start();
|
|
1813
|
+
|
|
1814
|
+
// Step 1
|
|
1815
|
+
await step1();
|
|
1816
|
+
logger.debug(`Step 1 completed in ${timer.diff()}ms`);
|
|
1817
|
+
|
|
1818
|
+
// Step 2
|
|
1819
|
+
await step2();
|
|
1820
|
+
logger.debug(`Step 2 completed in ${timer.diff()}ms`);
|
|
1821
|
+
|
|
1822
|
+
// Total time
|
|
1823
|
+
const total = timer.end();
|
|
1824
|
+
logger.info(`Total processing time: ${total}ms`);
|
|
1825
|
+
}
|
|
1826
|
+
```
|
|
1827
|
+
|
|
1828
|
+
### 10. Handle Errors Consistently
|
|
1829
|
+
|
|
1830
|
+
Establish a consistent pattern for error handling:
|
|
1831
|
+
|
|
1832
|
+
```typescript
|
|
1833
|
+
async function businessOperation() {
|
|
1834
|
+
const timer = new TimeDiff();
|
|
1835
|
+
const sessionId = 'sess-123';
|
|
1836
|
+
const transactionId = 'txn-456';
|
|
1837
|
+
|
|
1838
|
+
try {
|
|
1839
|
+
logger.app.info(
|
|
1840
|
+
'Starting operation',
|
|
1841
|
+
ActionMessage.appLogic(),
|
|
1842
|
+
'MyService',
|
|
1843
|
+
'record-123',
|
|
1844
|
+
sessionId,
|
|
1845
|
+
transactionId
|
|
1846
|
+
);
|
|
1847
|
+
|
|
1848
|
+
const result = await performOperation();
|
|
1849
|
+
|
|
1850
|
+
logger.app.summarySuccess(
|
|
1851
|
+
'Operation successful',
|
|
1852
|
+
'200',
|
|
1853
|
+
timer.diff(),
|
|
1854
|
+
'MyService',
|
|
1855
|
+
'record-123',
|
|
1856
|
+
sessionId,
|
|
1857
|
+
transactionId
|
|
1858
|
+
);
|
|
1859
|
+
|
|
1860
|
+
return result;
|
|
1861
|
+
|
|
1862
|
+
} catch (error) {
|
|
1863
|
+
logger.app.error(
|
|
1864
|
+
`Operation failed: ${error.message}`,
|
|
1865
|
+
ActionMessage.exception(),
|
|
1866
|
+
'MyService',
|
|
1867
|
+
'record-123',
|
|
1868
|
+
sessionId,
|
|
1869
|
+
transactionId
|
|
1870
|
+
);
|
|
1871
|
+
|
|
1872
|
+
logger.app.summaryError(
|
|
1873
|
+
'Operation failed',
|
|
1874
|
+
error.code || '500',
|
|
1875
|
+
timer.diff(),
|
|
1876
|
+
'MyService',
|
|
1877
|
+
'record-123',
|
|
1878
|
+
sessionId,
|
|
1879
|
+
transactionId,
|
|
1880
|
+
undefined,
|
|
1881
|
+
undefined,
|
|
1882
|
+
undefined,
|
|
1883
|
+
undefined,
|
|
1884
|
+
undefined,
|
|
1885
|
+
undefined,
|
|
1886
|
+
[error.stack]
|
|
1887
|
+
);
|
|
1888
|
+
|
|
1889
|
+
throw error;
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
```
|
|
1893
|
+
|
|
1894
|
+
## Performance Considerations
|
|
1895
|
+
|
|
1896
|
+
- **Caching**: Logger caches hostname and app version for performance
|
|
1897
|
+
- **Fast Paths**: String messages use optimized fast paths
|
|
1898
|
+
- **Message Truncation**: Long messages automatically truncated to 4096 chars
|
|
1899
|
+
- **Level Filtering**: Disabled log levels have minimal overhead (early return)
|
|
1900
|
+
- **Lazy Evaluation**: Complex operations only performed for enabled levels
|
|
1901
|
+
|
|
1902
|
+
## Troubleshooting
|
|
1903
|
+
|
|
1904
|
+
### Logs Not Appearing
|
|
1905
|
+
|
|
1906
|
+
Check that the log level is enabled:
|
|
1907
|
+
|
|
1908
|
+
```typescript
|
|
1909
|
+
process.env.LOG_LEVEL = 'debug,info,warn,error,verbose';
|
|
1910
|
+
// Make sure the level you're using is in this list
|
|
1911
|
+
```
|
|
1912
|
+
|
|
1913
|
+
### Sensitive Data Still Visible
|
|
1914
|
+
|
|
1915
|
+
Ensure masking is configured:
|
|
1916
|
+
|
|
1917
|
+
```typescript
|
|
1918
|
+
process.env.LOG_MASK_KEYS = 'password,token,apiKey,secret';
|
|
1919
|
+
// Add all field names that should be masked
|
|
1920
|
+
```
|
|
1921
|
+
|
|
1922
|
+
### Telemetry Not Working
|
|
1923
|
+
|
|
1924
|
+
Verify OpenTelemetry is configured in your application and the result codes aren't ignored:
|
|
1925
|
+
|
|
1926
|
+
```typescript
|
|
1927
|
+
// Check if code is in ignore list
|
|
1928
|
+
process.env.TELEMETRY_IGNORE_CODE = '404,401';
|
|
1929
|
+
// These codes won't be tracked in telemetry
|
|
1930
|
+
```
|
|
1931
|
+
|
|
1932
|
+
### TypeScript Type Errors
|
|
1933
|
+
|
|
1934
|
+
Import the correct interfaces:
|
|
1935
|
+
|
|
1936
|
+
```typescript
|
|
1937
|
+
import {
|
|
1938
|
+
DataM1I,
|
|
1939
|
+
DataM2I,
|
|
1940
|
+
DataM3I,
|
|
1941
|
+
LoggerOpt
|
|
1942
|
+
} from '@eqxjs/nest-logger';
|
|
1943
|
+
```
|
|
1944
|
+
|
|
1945
|
+
## Testing
|
|
1946
|
+
|
|
1947
|
+
The module includes comprehensive test coverage (100% lines):
|
|
1948
|
+
|
|
1949
|
+
```bash
|
|
1950
|
+
# Run tests
|
|
1951
|
+
yarn test
|
|
1952
|
+
|
|
1953
|
+
# Run tests with coverage
|
|
1954
|
+
yarn test:cov
|
|
1955
|
+
|
|
1956
|
+
# Watch mode
|
|
1957
|
+
yarn test:watch
|
|
22
1958
|
```
|
|
23
1959
|
|
|
24
1960
|
## Development
|
|
25
1961
|
|
|
26
|
-
Please see [docs/README.md](docs/README.md)
|
|
1962
|
+
Please see [docs/README.md](docs/README.md) for development guidelines.
|
|
27
1963
|
|
|
28
1964
|
## Contributing
|
|
29
1965
|
|
|
30
|
-
Please see [
|
|
1966
|
+
Please see [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines.
|
|
31
1967
|
|
|
32
1968
|
## License
|
|
33
1969
|
|
|
34
|
-
|
|
1970
|
+
ISC
|
|
1971
|
+
|
|
1972
|
+
## Support
|
|
1973
|
+
|
|
1974
|
+
For issues, questions, or contributions, please visit the [GitHub repository](https://github.com/eqxjs/nest-logger).
|
|
1975
|
+
|
|
1976
|
+
## Changelog
|
|
1977
|
+
|
|
1978
|
+
See [CHANGELOG](CHANGELOG) for release notes and version history.
|
|
1979
|
+
|
|
1980
|
+
---
|
|
1981
|
+
|
|
1982
|
+
**Made with ❤️ by the EQXJS Team**
|
|
1983
|
+
- Use M2 for HTTP/protocol-specific operations
|
|
1984
|
+
- Use M3 for service-to-service operations
|
|
1985
|
+
|
|
1986
|
+
5. **Configure Environment Variables**
|
|
1987
|
+
- Set `LOG_LEVEL` in production to reduce log volume
|
|
1988
|
+
- Configure `LOG_MASK_KEYS` to protect sensitive data
|
|
1989
|
+
- Use `TELEMETRY_IGNORE_CODE` to filter noise from metrics
|
|
1990
|
+
|
|
1991
|
+
## Testing
|
|
1992
|
+
|
|
1993
|
+
The module includes comprehensive test coverage (98.32%):
|
|
1994
|
+
|
|
1995
|
+
```bash
|
|
1996
|
+
# Run tests
|
|
1997
|
+
yarn test
|
|
1998
|
+
|
|
1999
|
+
# Run tests with coverage
|
|
2000
|
+
yarn test:cov
|
|
2001
|
+
|
|
2002
|
+
# Watch mode
|
|
2003
|
+
yarn test:watch
|
|
2004
|
+
```
|
|
2005
|
+
|
|
2006
|
+
## Development
|
|
2007
|
+
|
|
2008
|
+
Please see [docs/README.md](docs/README.md) for development guidelines.
|
|
2009
|
+
|
|
2010
|
+
## Contributing
|
|
2011
|
+
|
|
2012
|
+
Please see [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines.
|
|
2013
|
+
|
|
2014
|
+
## License
|
|
2015
|
+
|
|
2016
|
+
ISC
|
|
2017
|
+
|
|
2018
|
+
## Support
|
|
2019
|
+
|
|
2020
|
+
For issues, questions, or contributions, please visit the [GitHub repository](https://github.com/eqxjs/nest-logger).
|
|
2021
|
+
|
|
2022
|
+
## Changelog
|
|
2023
|
+
|
|
2024
|
+
See [CHANGELOG](CHANGELOG) for release notes and version history.
|
|
2025
|
+
|
|
2026
|
+
---
|
|
35
2027
|
|
|
36
|
-
|
|
2028
|
+
**Made with ❤️ by the EQXJS Team**
|