@evdy-consumer/dailyom-suite-logging 0.0.2-dev.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/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # DailyOM Logging Library
2
+
3
+ This library was generated with Nx.
4
+
5
+ The logging library provides a unified logging solution for Next.js and backend systems in the DailyOM Suite monorepo. It implements distributed tracing through trace and span IDs, allowing for correlation of logs across service boundaries.
6
+
7
+ ## Features
8
+ - **Trace Context Management**: Automatic generation and propagation of trace and span IDs
9
+ - **Hierarchical Spans**: Create child spans for tracking operations within a request
10
+ - **Configurable Output**: Log to console, files, or both
11
+ - **Daily Log Rotation**: Automated log file rotation with size thresholds
12
+ - **Context Preservation**: Maintains trace context across asynchronous operations using AsyncLocalStorage
13
+
14
+
15
+ ## Usage
16
+ Basic Logging
17
+ ```typescript
18
+ import { TracedLogger } from '@dailyom-suite/logging';
19
+
20
+ // Create a logger instance
21
+ const logger = new TracedLogger({
22
+ name: 'my-service',
23
+ level: 'info', // Optional: default is 'info'
24
+ logToConsole: true, // Optional: default is true
25
+ logToFile: true, // Optional: default is true
26
+ fileName: 'app-name.log' // Optional: default uses name + timestamp
27
+ });
28
+
29
+ // Log messages with context
30
+ logger.info({ userId: '123' }, 'User logged in');
31
+ logger.error({ orderId: '456' }, 'Failed to process order');
32
+ ```
33
+
34
+ Trace Context and Spans
35
+ ```typescript
36
+ // Create a traced operation
37
+ logger.withSpan('database-query', () => {
38
+ // All logs in this function will share the same trace context
39
+ logger.debug({ query: 'SELECT * FROM users' }, 'Executing query');
40
+ // ...
41
+ return result;
42
+ });
43
+
44
+ // For async operations
45
+ await logger.withSpanAsync('api-request', async () => {
46
+ logger.info({ url: '/api/data' }, 'Making API request');
47
+ const response = await fetch('/api/data');
48
+ logger.info({ status: response.status }, 'Received API response');
49
+ return response.json();
50
+ });
51
+ ```
52
+
53
+ Manual Trace Context Manipulation
54
+ ```typescript
55
+ import {
56
+ createTraceContext,
57
+ createChildSpan,
58
+ traceContextStorage
59
+ } from '@dailyom-suite/logging';
60
+
61
+ // Create a new trace context
62
+ const context = createTraceContext();
63
+
64
+ // Create a child span from parent context
65
+ const childContext = createChildSpan(context);
66
+
67
+ // Run code with a specific trace context
68
+ traceContextStorage.run(context, () => {
69
+ // All logs in this scope will use this context
70
+ });
71
+ ```
72
+
73
+ ## Configuration
74
+ The library looks for the following environment variables:
75
+
76
+ - [LOG_DIR](src/lib/logging.ts): Directory where log files will be stored (defaults to ./logs)
77
+
78
+ ## Log File Management
79
+ Log files follow this naming pattern:
80
+ ```
81
+ {app-name}.{YYYY-MM-DD}.{HH-MM-SS}.log
82
+ ```
83
+
84
+ Files rotate:
85
+
86
+ - Every day (24 hours)
87
+ - When they exceed 10MB in size
88
+
89
+ ## Building
90
+ Run `nx build logging` to build the library.
91
+
92
+ ## Running unit tests
93
+ Run `nx test logging` to execute the unit tests via Jest.
94
+
95
+ ## CI
96
+
97
+ PRs that contain changes to logging will run tests and builds to ensure project structure is adhered to
package/package.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "@evdy-consumer/dailyom-suite-logging",
3
+ "version": "0.0.2-dev.0",
4
+ "type": "commonjs",
5
+ "main": "./src/index.js",
6
+ "types": "./src/index.d.ts",
7
+ "dependencies": {
8
+ "bunyan": "1.8.15",
9
+ "bunyan-rotating-file-stream": "2.0.6",
10
+ "tslib": "2.3.0",
11
+ "uuid": "11.1.0"
12
+ },
13
+ "devDependencies": {
14
+ "@types/bunyan": "1.8.11",
15
+ "@types/uuid": "10.0.0"
16
+ }
17
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './lib/logging';
package/src/index.js ADDED
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./lib/logging"), exports);
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../libs/logging/src/index.ts"],"names":[],"mappings":";;;AAAA,wDAA8B"}
@@ -0,0 +1,31 @@
1
+ import * as bunyan from 'bunyan';
2
+ import { AsyncLocalStorage } from 'async_hooks';
3
+ export interface TraceContext {
4
+ traceId: string;
5
+ spanId: string;
6
+ }
7
+ export declare function createTraceContext(): TraceContext;
8
+ export declare function createChildSpan(parentContext: TraceContext): TraceContext;
9
+ export interface LoggerOptions {
10
+ name: string;
11
+ level?: bunyan.LogLevel;
12
+ logToConsole?: boolean;
13
+ logToFile?: boolean;
14
+ fileName?: string;
15
+ }
16
+ export declare function createLogger(options: LoggerOptions): bunyan;
17
+ export declare const traceContextStorage: AsyncLocalStorage<TraceContext>;
18
+ export declare function getCurrentTraceContext(): TraceContext;
19
+ export declare class TracedLogger {
20
+ private logger;
21
+ constructor(options: LoggerOptions);
22
+ private getLoggerWithContext;
23
+ trace(data: object, msg: string, ...args: any[]): void;
24
+ debug(data: object, msg: string, ...args: any[]): void;
25
+ info(data: object, msg: string, ...args: any[]): void;
26
+ warn(data: object, msg: string, ...args: any[]): void;
27
+ error(data: object, msg: string, ...args: any[]): void;
28
+ fatal(data: object, msg: string, ...args: any[]): void;
29
+ withSpan<T>(name: string, fn: () => T): T;
30
+ withSpanAsync<T>(name: string, fn: () => Promise<T>): Promise<T>;
31
+ }
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TracedLogger = exports.traceContextStorage = void 0;
4
+ exports.createTraceContext = createTraceContext;
5
+ exports.createChildSpan = createChildSpan;
6
+ exports.createLogger = createLogger;
7
+ exports.getCurrentTraceContext = getCurrentTraceContext;
8
+ const bunyan = require("bunyan");
9
+ const path = require("path");
10
+ const fs = require("fs");
11
+ const uuid_1 = require("uuid");
12
+ const async_hooks_1 = require("async_hooks");
13
+ const RotatingFileStream = require('bunyan-rotating-file-stream');
14
+ const LOG_DIR = process.env["LOG_DIR"] || path.join(process.cwd(), 'logs');
15
+ if (!fs.existsSync(LOG_DIR)) {
16
+ fs.mkdirSync(LOG_DIR, { recursive: true });
17
+ }
18
+ function createTraceContext() {
19
+ return {
20
+ traceId: (0, uuid_1.v4)(),
21
+ spanId: (0, uuid_1.v4)(),
22
+ };
23
+ }
24
+ function createChildSpan(parentContext) {
25
+ return {
26
+ traceId: parentContext.traceId,
27
+ spanId: (0, uuid_1.v4)()
28
+ };
29
+ }
30
+ //Returns formatted date in the format YYYY-MM-DD.
31
+ function getFormattedDate() {
32
+ return new Date().toISOString().split('T')[0];
33
+ }
34
+ // Returns formatted time in the format HH-MM-SS.
35
+ function getFormattedTime() {
36
+ return new Date().toTimeString().split(' ')[0].replace(/:/g, '-');
37
+ }
38
+ // Generate formatted date and time
39
+ const formattedDate = getFormattedDate();
40
+ const formattedTime = getFormattedTime();
41
+ function createLogger(options) {
42
+ const streams = [];
43
+ // mainly for development purposes but can be used in production as well
44
+ if (options.logToConsole !== false) {
45
+ streams.push({
46
+ level: options.level || 'info',
47
+ stream: process.stdout,
48
+ });
49
+ }
50
+ if (options.logToFile !== false) {
51
+ const fileName = `${options.name}.${formattedDate}.${formattedTime}.log`;
52
+ streams.push({
53
+ type: 'raw',
54
+ level: options.level || 'info',
55
+ stream: new RotatingFileStream({
56
+ path: path.join(LOG_DIR, fileName),
57
+ period: '1d', // daily rotation
58
+ rotateExisting: true, // Give ourselves a clean file when we start up, based on period
59
+ threshold: '10m', // Rotate log files larger than 10 megabytes
60
+ }), // Type mismatch with Bunyan
61
+ });
62
+ }
63
+ return bunyan.createLogger({
64
+ name: options.name,
65
+ streams,
66
+ serializers: bunyan.stdSerializers,
67
+ });
68
+ }
69
+ exports.traceContextStorage = new async_hooks_1.AsyncLocalStorage();
70
+ // Get current trace context or create a new one
71
+ function getCurrentTraceContext() {
72
+ const context = exports.traceContextStorage.getStore();
73
+ if (context) {
74
+ return context;
75
+ }
76
+ return createTraceContext();
77
+ }
78
+ class TracedLogger {
79
+ logger;
80
+ constructor(options) {
81
+ this.logger = createLogger(options);
82
+ }
83
+ getLoggerWithContext() {
84
+ const traceContext = getCurrentTraceContext();
85
+ return this.logger.child({
86
+ traceId: traceContext.traceId,
87
+ spanId: traceContext.spanId
88
+ });
89
+ }
90
+ trace(data, msg, ...args) {
91
+ this.getLoggerWithContext().trace({ ...data }, msg, ...args);
92
+ }
93
+ debug(data, msg, ...args) {
94
+ this.getLoggerWithContext().debug({ ...data }, msg, ...args);
95
+ }
96
+ info(data, msg, ...args) {
97
+ this.getLoggerWithContext().info({ ...data }, msg, ...args);
98
+ }
99
+ warn(data, msg, ...args) {
100
+ this.getLoggerWithContext().warn({ ...data }, msg, ...args);
101
+ }
102
+ error(data, msg, ...args) {
103
+ this.getLoggerWithContext().error({ ...data }, msg, ...args);
104
+ }
105
+ fatal(data, msg, ...args) {
106
+ this.getLoggerWithContext().fatal({ ...data }, msg, ...args);
107
+ }
108
+ // Create a new span and run function within it
109
+ withSpan(name, fn) {
110
+ const currentContext = getCurrentTraceContext();
111
+ const spanContext = createChildSpan(currentContext);
112
+ const spanLogger = this.logger.child({
113
+ traceId: spanContext.traceId,
114
+ spanId: spanContext.spanId,
115
+ operation: name,
116
+ });
117
+ spanLogger.debug(`Starting span: ${name}`);
118
+ return exports.traceContextStorage.run(spanContext, () => {
119
+ try {
120
+ const result = fn();
121
+ spanLogger.debug(`Completed span: ${name}`);
122
+ return result;
123
+ }
124
+ catch (error) {
125
+ spanLogger.error({ err: error }, `Error in span: ${name}`);
126
+ throw error;
127
+ }
128
+ });
129
+ }
130
+ // Create a new span and run async function within it
131
+ async withSpanAsync(name, fn) {
132
+ const currentContext = getCurrentTraceContext();
133
+ const spanContext = createChildSpan(currentContext);
134
+ const spanLogger = this.logger.child({
135
+ traceId: spanContext.traceId,
136
+ spanId: spanContext.spanId,
137
+ operation: name,
138
+ });
139
+ spanLogger.debug(`Starting span: ${name}`);
140
+ return exports.traceContextStorage.run(spanContext, async () => {
141
+ try {
142
+ const result = await fn();
143
+ spanLogger.debug(`Completed span: ${name}`);
144
+ return result;
145
+ }
146
+ catch (error) {
147
+ spanLogger.error({ err: error }, `Error in span: ${name}`);
148
+ throw error;
149
+ }
150
+ });
151
+ }
152
+ }
153
+ exports.TracedLogger = TracedLogger;
154
+ //# sourceMappingURL=logging.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logging.js","sourceRoot":"","sources":["../../../../../libs/logging/src/lib/logging.ts"],"names":[],"mappings":"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"}