hierarchical-area-logger 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.
package/dist/index.js ADDED
@@ -0,0 +1,117 @@
1
+ // src/Logger.ts
2
+ import { init } from "@paralleldrive/cuid2";
3
+
4
+ // src/utils.ts
5
+ var prettyStack = (stack) => {
6
+ if (!stack) return [];
7
+ let toReplace = "";
8
+ const regex = /file:\/\/\/(.*)(\.wrangler|node_modules)\/.*\)/gm;
9
+ const m = regex.exec(stack);
10
+ if (m && m.length > 1) {
11
+ toReplace = m[1];
12
+ }
13
+ return stack.split("\n").map((line) => line.trim()).filter((line) => !line.startsWith("Error: ")).map((line) => line.replace(toReplace, "")).map((line) => line.replace("file://", ""));
14
+ };
15
+ var prettyError = (err) => {
16
+ const stack = prettyStack(err.stack);
17
+ const message = err.message;
18
+ return { message, stack };
19
+ };
20
+ var createRootLogEntry = ({
21
+ path,
22
+ method,
23
+ details,
24
+ eventId,
25
+ parentEventId,
26
+ withParentEventId
27
+ }) => {
28
+ const url = new URL(`http://example.com${path ?? "/"}`);
29
+ const rootLogEntry = {
30
+ root: [
31
+ {
32
+ type: "info",
33
+ message: "Request received",
34
+ payload: {
35
+ path: `${url.pathname}${url.search}`,
36
+ method,
37
+ details,
38
+ eventId,
39
+ ...parentEventId && { parentEventId }
40
+ },
41
+ timestamp: Date.now()
42
+ }
43
+ ]
44
+ };
45
+ if (withParentEventId && !parentEventId) {
46
+ rootLogEntry.root.push({
47
+ type: "error",
48
+ message: "Parent event ID expected but not found",
49
+ timestamp: Date.now()
50
+ });
51
+ }
52
+ return rootLogEntry;
53
+ };
54
+
55
+ // src/Logger.ts
56
+ var Logger = class {
57
+ constructor(options) {
58
+ this.parentEventId = options.parentEventId;
59
+ this.eventId = init({ fingerprint: options.details.service })();
60
+ this.log = createRootLogEntry({
61
+ path: options.path || "/",
62
+ method: options.method,
63
+ details: options.details,
64
+ eventId: this.eventId,
65
+ parentEventId: options.parentEventId,
66
+ withParentEventId: options.withParentEventId
67
+ });
68
+ }
69
+ // Area function that returns area-specific logger methods
70
+ getArea(name = "dummy") {
71
+ if (!this.log[name]) {
72
+ this.log[name] = [];
73
+ }
74
+ return {
75
+ info: (message, payload) => {
76
+ this.log[name].push({
77
+ type: "info",
78
+ message,
79
+ payload,
80
+ timestamp: Date.now()
81
+ });
82
+ },
83
+ warn: (message, payload) => {
84
+ this.log[name].push({
85
+ type: "warn",
86
+ message,
87
+ payload,
88
+ timestamp: Date.now()
89
+ });
90
+ },
91
+ error: (message, payload) => {
92
+ this.log[name].push({
93
+ type: "error",
94
+ message,
95
+ payload: payload instanceof Error ? prettyError(payload) : payload,
96
+ timestamp: Date.now()
97
+ });
98
+ }
99
+ };
100
+ }
101
+ // Public methods on main instance
102
+ dump() {
103
+ return this.log;
104
+ }
105
+ appendLogData(logData) {
106
+ delete logData.root;
107
+ this.log = { ...logData, ...this.log };
108
+ }
109
+ };
110
+ var createLogger = (options) => {
111
+ return new Logger(options);
112
+ };
113
+ export {
114
+ Logger,
115
+ createLogger
116
+ };
117
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/Logger.ts","../src/utils.ts"],"sourcesContent":["import { init } from '@paralleldrive/cuid2';\nimport { createRootLogEntry, prettyError } from './utils';\nimport { LogData, LoggerOptions, LogEntry } from './types';\n\nexport class Logger {\n public eventId: string;\n private log: LogData;\n public parentEventId?: string;\n\n constructor(options: LoggerOptions) {\n this.parentEventId = options.parentEventId;\n this.eventId = init({ fingerprint: options.details.service })();\n\n this.log = createRootLogEntry({\n path: options.path || '/',\n method: options.method,\n details: options.details,\n eventId: this.eventId,\n parentEventId: options.parentEventId,\n withParentEventId: options.withParentEventId,\n });\n }\n\n // Area function that returns area-specific logger methods\n public getArea(name = 'dummy') {\n if (!this.log[name]) {\n this.log[name] = [];\n }\n\n return {\n info: (message: string, payload?: LogEntry['payload']) => {\n this.log[name]!.push({\n type: 'info',\n message,\n payload,\n timestamp: Date.now(),\n });\n },\n warn: (message: string, payload?: LogEntry['payload']) => {\n this.log[name]!.push({\n type: 'warn',\n message,\n payload,\n timestamp: Date.now(),\n });\n },\n error: (message: string, payload?: LogEntry['payload'] | Error) => {\n this.log[name]!.push({\n type: 'error',\n message,\n payload: payload instanceof Error ? prettyError(payload) : payload,\n timestamp: Date.now(),\n });\n },\n };\n }\n\n // Public methods on main instance\n public dump(): LogData {\n return this.log;\n }\n\n public appendLogData(logData: LogData): void {\n delete logData.root;\n this.log = { ...logData, ...this.log };\n }\n}\n\n// Factory function for convenience\nexport const createLogger = (options: LoggerOptions): Logger => {\n return new Logger(options);\n};\n","import { CreateRootLogEntryOptions, LogData, RootPayload } from './types';\n\nexport const prettyStack = (stack?: string): string[] => {\n if (!stack) return [];\n let toReplace = '';\n\n const regex = /file:\\/\\/\\/(.*)(\\.wrangler|node_modules)\\/.*\\)/gm;\n const m = regex.exec(stack);\n\n if (m && m.length > 1) {\n toReplace = m[1];\n }\n\n return stack\n .split('\\n')\n .map((line) => line.trim())\n .filter((line) => !line.startsWith('Error: '))\n .map((line) => line.replace(toReplace, ''))\n .map((line) => line.replace('file://', ''));\n};\n\nexport const prettyError = (err: Error) => {\n const stack = prettyStack(err.stack);\n const message = err.message;\n return { message, stack };\n};\n\nexport const createRootLogEntry = ({\n path,\n method,\n details,\n eventId,\n parentEventId,\n withParentEventId,\n}: CreateRootLogEntryOptions): LogData => {\n const url = new URL(`http://example.com${path ?? '/'}`);\n\n const rootLogEntry: LogData = {\n root: [\n {\n type: 'info',\n message: 'Request received',\n payload: {\n path: `${url.pathname}${url.search}`,\n method,\n details,\n eventId,\n ...(parentEventId && { parentEventId }),\n } as RootPayload,\n timestamp: Date.now(),\n },\n ],\n };\n\n if (withParentEventId && !parentEventId) {\n rootLogEntry.root.push({\n type: 'error',\n message: 'Parent event ID expected but not found',\n timestamp: Date.now(),\n });\n }\n\n return rootLogEntry;\n};\n"],"mappings":"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","names":[]}
@@ -0,0 +1,43 @@
1
+ import js from '@eslint/js';
2
+ import tseslint from '@typescript-eslint/eslint-plugin';
3
+ import tsparser from '@typescript-eslint/parser';
4
+
5
+ export default [
6
+ js.configs.recommended,
7
+ {
8
+ files: ['**/*.ts'],
9
+ languageOptions: {
10
+ parser: tsparser,
11
+ ecmaVersion: 2020,
12
+ sourceType: 'module',
13
+ globals: {
14
+ process: 'readonly',
15
+ Buffer: 'readonly',
16
+ __dirname: 'readonly',
17
+ __filename: 'readonly',
18
+ console: 'readonly',
19
+ URL: 'readonly',
20
+ URLSearchParams: 'readonly',
21
+ },
22
+ },
23
+ plugins: {
24
+ '@typescript-eslint': tseslint,
25
+ },
26
+ rules: {
27
+ ...tseslint.configs.recommended.rules,
28
+ '@typescript-eslint/no-unused-vars': [
29
+ 'error',
30
+ { argsIgnorePattern: '^_' },
31
+ ],
32
+ '@typescript-eslint/explicit-function-return-type': 'off',
33
+ '@typescript-eslint/no-explicit-any': 'warn',
34
+ '@typescript-eslint/no-empty-function': 'warn',
35
+ 'prefer-const': 'error',
36
+ 'no-var': 'error',
37
+ 'no-console': 'off',
38
+ },
39
+ },
40
+ {
41
+ ignores: ['dist/', 'node_modules/', '*.js'],
42
+ },
43
+ ];
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "hierarchical-area-logger",
3
+ "version": "0.2.0",
4
+ "type": "module",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "scripts": {
9
+ "build": "tsup",
10
+ "dev": "tsup --watch",
11
+ "test": "vitest",
12
+ "test:ui": "vitest --ui",
13
+ "coverage": "vitest --coverage",
14
+ "lint": "eslint .",
15
+ "lint:fix": "eslint . --fix",
16
+ "lint:check": "eslint . --max-warnings=0",
17
+ "format": "prettier --write .",
18
+ "format:check": "prettier --check ."
19
+ },
20
+ "pre-commit": [
21
+ "lint"
22
+ ],
23
+ "author": "Roman Jankowski",
24
+ "license": "MIT",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/Em3ODMe/hierarchical-area-logger.git"
28
+ },
29
+ "engines": {
30
+ "node": ">=18.0.0"
31
+ },
32
+ "description": "Hierarchical Area Logger",
33
+ "devDependencies": {
34
+ "@eslint/js": "^9.0.0",
35
+ "@typescript-eslint/eslint-plugin": "^8.0.0",
36
+ "@typescript-eslint/parser": "^8.0.0",
37
+ "@vitest/coverage-v8": "^4.0.18",
38
+ "eslint": "^9.0.0",
39
+ "prettier": "^3.8.1",
40
+ "tsup": "^8.5.1",
41
+ "typescript": "^5.0.0",
42
+ "vitest": "^4.0.18"
43
+ },
44
+ "dependencies": {
45
+ "@paralleldrive/cuid2": "^3.0.6"
46
+ },
47
+ "exports": {
48
+ ".": {
49
+ "types": "./dist/index.d.ts",
50
+ "import": "./dist/index.mjs",
51
+ "require": "./dist/index.js"
52
+ }
53
+ },
54
+ "keywords": [
55
+ "logger",
56
+ "logging",
57
+ "typescript",
58
+ "nodejs"
59
+ ]
60
+ }
package/src/Logger.ts ADDED
@@ -0,0 +1,72 @@
1
+ import { init } from '@paralleldrive/cuid2';
2
+ import { createRootLogEntry, prettyError } from './utils';
3
+ import { LogData, LoggerOptions, LogEntry } from './types';
4
+
5
+ export class Logger {
6
+ public eventId: string;
7
+ private log: LogData;
8
+ public parentEventId?: string;
9
+
10
+ constructor(options: LoggerOptions) {
11
+ this.parentEventId = options.parentEventId;
12
+ this.eventId = init({ fingerprint: options.details.service })();
13
+
14
+ this.log = createRootLogEntry({
15
+ path: options.path || '/',
16
+ method: options.method,
17
+ details: options.details,
18
+ eventId: this.eventId,
19
+ parentEventId: options.parentEventId,
20
+ withParentEventId: options.withParentEventId,
21
+ });
22
+ }
23
+
24
+ // Area function that returns area-specific logger methods
25
+ public getArea(name = 'dummy') {
26
+ if (!this.log[name]) {
27
+ this.log[name] = [];
28
+ }
29
+
30
+ return {
31
+ info: (message: string, payload?: LogEntry['payload']) => {
32
+ this.log[name]!.push({
33
+ type: 'info',
34
+ message,
35
+ payload,
36
+ timestamp: Date.now(),
37
+ });
38
+ },
39
+ warn: (message: string, payload?: LogEntry['payload']) => {
40
+ this.log[name]!.push({
41
+ type: 'warn',
42
+ message,
43
+ payload,
44
+ timestamp: Date.now(),
45
+ });
46
+ },
47
+ error: (message: string, payload?: LogEntry['payload'] | Error) => {
48
+ this.log[name]!.push({
49
+ type: 'error',
50
+ message,
51
+ payload: payload instanceof Error ? prettyError(payload) : payload,
52
+ timestamp: Date.now(),
53
+ });
54
+ },
55
+ };
56
+ }
57
+
58
+ // Public methods on main instance
59
+ public dump(): LogData {
60
+ return this.log;
61
+ }
62
+
63
+ public appendLogData(logData: LogData): void {
64
+ delete logData.root;
65
+ this.log = { ...logData, ...this.log };
66
+ }
67
+ }
68
+
69
+ // Factory function for convenience
70
+ export const createLogger = (options: LoggerOptions): Logger => {
71
+ return new Logger(options);
72
+ };
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { Logger, createLogger } from './Logger';
2
+ export { LogData, LogEntry } from './types';
package/src/types.ts ADDED
@@ -0,0 +1,38 @@
1
+ export type Details = { service: string } & Record<string, unknown>;
2
+ export type Method =
3
+ | 'get'
4
+ | 'post'
5
+ | 'put'
6
+ | 'delete'
7
+ | 'patch'
8
+ | 'head'
9
+ | 'options'
10
+ | 'trace'
11
+ | 'connect';
12
+
13
+ export type LogEntry = {
14
+ type: 'info' | 'warn' | 'error';
15
+ message: string;
16
+ payload?: object;
17
+ timestamp: number;
18
+ };
19
+ export type RootPayload = {
20
+ path?: string;
21
+ method?: Method;
22
+ eventId: string;
23
+ parentEventId?: string;
24
+ details: Details;
25
+ };
26
+ export type CreateRootLogEntryOptions = RootPayload & {
27
+ path?: string;
28
+ withParentEventId?: boolean;
29
+ };
30
+ export type LogData = Record<string, LogEntry[]>;
31
+
32
+ export interface LoggerOptions {
33
+ details: Details;
34
+ path?: string;
35
+ parentEventId?: string;
36
+ withParentEventId?: boolean;
37
+ method?: Method;
38
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,64 @@
1
+ import { CreateRootLogEntryOptions, LogData, RootPayload } from './types';
2
+
3
+ export const prettyStack = (stack?: string): string[] => {
4
+ if (!stack) return [];
5
+ let toReplace = '';
6
+
7
+ const regex = /file:\/\/\/(.*)(\.wrangler|node_modules)\/.*\)/gm;
8
+ const m = regex.exec(stack);
9
+
10
+ if (m && m.length > 1) {
11
+ toReplace = m[1];
12
+ }
13
+
14
+ return stack
15
+ .split('\n')
16
+ .map((line) => line.trim())
17
+ .filter((line) => !line.startsWith('Error: '))
18
+ .map((line) => line.replace(toReplace, ''))
19
+ .map((line) => line.replace('file://', ''));
20
+ };
21
+
22
+ export const prettyError = (err: Error) => {
23
+ const stack = prettyStack(err.stack);
24
+ const message = err.message;
25
+ return { message, stack };
26
+ };
27
+
28
+ export const createRootLogEntry = ({
29
+ path,
30
+ method,
31
+ details,
32
+ eventId,
33
+ parentEventId,
34
+ withParentEventId,
35
+ }: CreateRootLogEntryOptions): LogData => {
36
+ const url = new URL(`http://example.com${path ?? '/'}`);
37
+
38
+ const rootLogEntry: LogData = {
39
+ root: [
40
+ {
41
+ type: 'info',
42
+ message: 'Request received',
43
+ payload: {
44
+ path: `${url.pathname}${url.search}`,
45
+ method,
46
+ details,
47
+ eventId,
48
+ ...(parentEventId && { parentEventId }),
49
+ } as RootPayload,
50
+ timestamp: Date.now(),
51
+ },
52
+ ],
53
+ };
54
+
55
+ if (withParentEventId && !parentEventId) {
56
+ rootLogEntry.root.push({
57
+ type: 'error',
58
+ message: 'Parent event ID expected but not found',
59
+ timestamp: Date.now(),
60
+ });
61
+ }
62
+
63
+ return rootLogEntry;
64
+ };
@@ -0,0 +1,136 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { createLogger, Logger } from '../src/Logger';
3
+ import { Details } from '../src/types';
4
+
5
+ describe('Logger', () => {
6
+ const mockdetails: Details = { service: 'test-service' };
7
+
8
+ it('should create a logger with details', () => {
9
+ const logger = createLogger({ details: mockdetails });
10
+ expect(logger).toBeInstanceOf(Logger);
11
+ });
12
+
13
+ it('should create a logger with custom options', () => {
14
+ const logger = createLogger({
15
+ details: mockdetails,
16
+ path: 'test-path',
17
+ parentEventId: 'parent-123',
18
+ });
19
+ expect(logger).toBeInstanceOf(Logger);
20
+ });
21
+
22
+ it('should create area logger and log messages', () => {
23
+ const logger = createLogger({ details: mockdetails });
24
+ const areaLogger = logger.getArea('test-area');
25
+
26
+ areaLogger.info('test info message');
27
+ areaLogger.warn('test warn message');
28
+ areaLogger.error('test error message');
29
+
30
+ const logs = logger.dump();
31
+ expect(logs['test-area']).toHaveLength(3);
32
+ expect(logs['test-area'][0].message).toBe('test info message');
33
+ expect(logs['test-area'][1].type).toBe('warn');
34
+ expect(logs['test-area'][2].type).toBe('error');
35
+ });
36
+
37
+ it('should generate event IDs correctly', () => {
38
+ const logger = createLogger({ details: mockdetails });
39
+
40
+ expect(logger.eventId).toBeDefined();
41
+ expect(typeof logger.eventId).toBe('string');
42
+ });
43
+
44
+ it('should handle parent event ID', () => {
45
+ const logger = createLogger({
46
+ details: mockdetails,
47
+ parentEventId: 'parent-123',
48
+ });
49
+
50
+ expect(logger.parentEventId).toBe('parent-123');
51
+ });
52
+
53
+ it('should append log data', () => {
54
+ const logger = createLogger({ details: mockdetails });
55
+ const logDataToAdd = {
56
+ area1: [
57
+ {
58
+ type: 'info' as const,
59
+ message: 'existing log',
60
+ timestamp: Date.now(),
61
+ },
62
+ ],
63
+ };
64
+
65
+ logger.appendLogData(logDataToAdd);
66
+ const logs = logger.dump();
67
+
68
+ expect(logs.area1).toBeDefined();
69
+ expect(logs.area1[0].message).toBe('existing log');
70
+ });
71
+
72
+ it('should create root log entry', () => {
73
+ const logger = createLogger({ details: mockdetails, path: 'test' });
74
+ const logs = logger.dump();
75
+
76
+ expect(logs.root).toBeDefined();
77
+ expect(logs.root).toHaveLength(1);
78
+ expect(logs.root[0].message).toBe('Request received');
79
+ });
80
+
81
+ it('should handle errors in error logging', () => {
82
+ const logger = createLogger({ details: mockdetails });
83
+ const areaLogger = logger.getArea('test-area');
84
+
85
+ const testError = new Error('Test error');
86
+ areaLogger.error('error occurred', testError);
87
+
88
+ const logs = logger.dump();
89
+ expect(logs['test-area'][0].payload).toHaveProperty(
90
+ 'message',
91
+ 'Test error'
92
+ );
93
+ });
94
+
95
+ it('should use default area name when no name provided', () => {
96
+ const logger = createLogger({ details: mockdetails });
97
+ const areaLogger = logger.getArea();
98
+
99
+ areaLogger.info('test message');
100
+
101
+ const logs = logger.dump();
102
+ expect(logs.dummy).toBeDefined();
103
+ expect(logs.dummy).toHaveLength(1);
104
+ expect(logs.dummy[0].message).toBe('test message');
105
+ });
106
+
107
+ it('should handle withParentEventId true without parentEventId', () => {
108
+ const logger = createLogger({
109
+ details: mockdetails,
110
+ withParentEventId: true,
111
+ });
112
+
113
+ const logs = logger.dump();
114
+ expect(logs.root).toHaveLength(2);
115
+ expect(logs.root[1].type).toBe('error');
116
+ expect(logs.root[1].message).toBe('Parent event ID expected but not found');
117
+ });
118
+ });
119
+
120
+ describe('createLogger', () => {
121
+ const mockdetails: Details = { service: 'test-service' };
122
+
123
+ it('should create a logger instance', () => {
124
+ const logger = createLogger({ details: mockdetails });
125
+ expect(logger).toBeInstanceOf(Logger);
126
+ });
127
+
128
+ it('should create a logger with options', () => {
129
+ const logger = createLogger({
130
+ details: mockdetails,
131
+ path: 'test-path',
132
+ parentEventId: 'parent-123',
133
+ });
134
+ expect(logger).toBeInstanceOf(Logger);
135
+ });
136
+ });