@x-oasis/log 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,13 +1,23 @@
1
1
  {
2
2
  "name": "@x-oasis/log",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "A friendly logging library for browser environments",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
7
7
  "module": "dist/log.esm.js",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/log.esm.js",
12
+ "require": "./dist/index.js",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "./package.json": "./package.json"
16
+ },
8
17
  "files": [
9
18
  "dist",
10
- "index.ts"
19
+ "index.ts",
20
+ "src"
11
21
  ],
12
22
  "publishConfig": {
13
23
  "access": "public"
package/src/index.ts ADDED
@@ -0,0 +1,39 @@
1
+ import { Logger } from './logger';
2
+ import { LogLevel } from './types';
3
+
4
+ export { Logger, LogLevel };
5
+ export type {
6
+ LogEntry,
7
+ LoggerOptions,
8
+ LoggerChain,
9
+ OutputHandler,
10
+ } from './types';
11
+
12
+ /**
13
+ * Default logger instance
14
+ */
15
+ export const defaultLogger = new Logger();
16
+
17
+ /**
18
+ * Convenience methods using the default logger
19
+ */
20
+ export const log = {
21
+ trace: (message: string, context?: Record<string, unknown>) =>
22
+ defaultLogger.trace(message, context),
23
+ debug: (message: string, context?: Record<string, unknown>) =>
24
+ defaultLogger.debug(message, context),
25
+ info: (message: string, context?: Record<string, unknown>) =>
26
+ defaultLogger.info(message, context),
27
+ warn: (message: string, context?: Record<string, unknown>) =>
28
+ defaultLogger.warn(message, context),
29
+ error: (message: string, context?: Record<string, unknown>) =>
30
+ defaultLogger.error(message, context),
31
+ setLevel: (level: LogLevel | keyof typeof LogLevel) =>
32
+ defaultLogger.setLevel(level),
33
+ withContext: (context: Record<string, unknown>) =>
34
+ defaultLogger.withContext(context),
35
+ withPrefix: (prefix: string) => defaultLogger.withPrefix(prefix),
36
+ chain: () => defaultLogger.chain(),
37
+ };
38
+
39
+ export default Logger;
package/src/logger.ts ADDED
@@ -0,0 +1,247 @@
1
+ import {
2
+ LogLevel,
3
+ LogEntry,
4
+ LoggerOptions,
5
+ LoggerChain,
6
+ OutputHandler,
7
+ } from './types';
8
+ import { parseLogLevel, createConsoleHandler } from './utils';
9
+
10
+ /**
11
+ * Main Logger class
12
+ */
13
+ export class Logger {
14
+ private level: LogLevel;
15
+ private handler: OutputHandler;
16
+ private enableTimestamp: boolean;
17
+ private defaultContext?: Record<string, unknown>;
18
+ private defaultPrefix?: string;
19
+
20
+ constructor(options: LoggerOptions = {}) {
21
+ this.level = options.level ? parseLogLevel(options.level) : LogLevel.INFO;
22
+ this.handler = options.handler || createConsoleHandler();
23
+ this.enableTimestamp =
24
+ options.enableTimestamp !== undefined ? options.enableTimestamp : true;
25
+ this.defaultContext = options.defaultContext;
26
+ this.defaultPrefix = options.defaultPrefix;
27
+ }
28
+
29
+ /**
30
+ * Set the minimum log level
31
+ */
32
+ setLevel(level: LogLevel | keyof typeof LogLevel): void {
33
+ this.level = parseLogLevel(level);
34
+ }
35
+
36
+ /**
37
+ * Get the current log level
38
+ */
39
+ getLevel(): LogLevel {
40
+ return this.level;
41
+ }
42
+
43
+ /**
44
+ * Set a custom output handler
45
+ */
46
+ setHandler(handler: OutputHandler): void {
47
+ this.handler = handler;
48
+ }
49
+
50
+ /**
51
+ * Set default context that will be included in all logs
52
+ */
53
+ setDefaultContext(context: Record<string, unknown>): void {
54
+ this.defaultContext = context;
55
+ }
56
+
57
+ /**
58
+ * Set default prefix for all log messages
59
+ */
60
+ setDefaultPrefix(prefix: string): void {
61
+ this.defaultPrefix = prefix;
62
+ }
63
+
64
+ /**
65
+ * Create a chainable logger instance
66
+ */
67
+ chain(): LoggerChain {
68
+ return new LoggerChainBuilder(this);
69
+ }
70
+
71
+ /**
72
+ * Log at trace level
73
+ */
74
+ trace(message: string, context?: Record<string, unknown>): void {
75
+ this.log(LogLevel.TRACE, message, context);
76
+ }
77
+
78
+ /**
79
+ * Log at debug level
80
+ */
81
+ debug(message: string, context?: Record<string, unknown>): void {
82
+ this.log(LogLevel.DEBUG, message, context);
83
+ }
84
+
85
+ /**
86
+ * Log at info level
87
+ */
88
+ info(message: string, context?: Record<string, unknown>): void {
89
+ this.log(LogLevel.INFO, message, context);
90
+ }
91
+
92
+ /**
93
+ * Log at warn level
94
+ */
95
+ warn(message: string, context?: Record<string, unknown>): void {
96
+ this.log(LogLevel.WARN, message, context);
97
+ }
98
+
99
+ /**
100
+ * Log at error level
101
+ */
102
+ error(message: string, context?: Record<string, unknown>): void {
103
+ this.log(LogLevel.ERROR, message, context);
104
+ }
105
+
106
+ /**
107
+ * Internal log method
108
+ */
109
+ private log(
110
+ level: LogLevel,
111
+ message: string,
112
+ context?: Record<string, unknown>
113
+ ): void {
114
+ if (level < this.level) {
115
+ return;
116
+ }
117
+
118
+ const entry: LogEntry = {
119
+ level,
120
+ message,
121
+ timestamp: this.enableTimestamp ? Date.now() : 0,
122
+ prefix: this.defaultPrefix,
123
+ };
124
+
125
+ // Merge default context with provided context
126
+ if (this.defaultContext || context) {
127
+ entry.context = {
128
+ ...this.defaultContext,
129
+ ...context,
130
+ };
131
+ }
132
+
133
+ this.handler(entry);
134
+ }
135
+
136
+ /**
137
+ * Create a chainable logger with initial context
138
+ */
139
+ withContext(context: Record<string, unknown>): LoggerChain {
140
+ return new LoggerChainBuilder(this).withContext(context);
141
+ }
142
+
143
+ /**
144
+ * Create a chainable logger with initial prefix
145
+ */
146
+ withPrefix(prefix: string): LoggerChain {
147
+ return new LoggerChainBuilder(this).withPrefix(prefix);
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Chainable logger builder
153
+ */
154
+ class LoggerChainBuilder implements LoggerChain {
155
+ private logger: Logger;
156
+ private context?: Record<string, unknown>;
157
+ private metadata?: Record<string, unknown>;
158
+ private errorObj?: Error;
159
+ private prefix?: string;
160
+
161
+ constructor(logger: Logger) {
162
+ this.logger = logger;
163
+ }
164
+
165
+ withContext(context: Record<string, unknown>): LoggerChain {
166
+ this.context = {
167
+ ...this.context,
168
+ ...context,
169
+ };
170
+ return this;
171
+ }
172
+
173
+ withMetadata(metadata: Record<string, unknown>): LoggerChain {
174
+ this.metadata = {
175
+ ...this.metadata,
176
+ ...metadata,
177
+ };
178
+ return this;
179
+ }
180
+
181
+ withError(error: Error): LoggerChain {
182
+ this.errorObj = error;
183
+ return this;
184
+ }
185
+
186
+ withPrefix(prefix: string): LoggerChain {
187
+ this.prefix = prefix;
188
+ return this;
189
+ }
190
+
191
+ private createEntry(level: LogLevel, message: string): LogEntry {
192
+ const entry: LogEntry = {
193
+ level,
194
+ message,
195
+ timestamp: this.logger['enableTimestamp'] ? Date.now() : 0,
196
+ prefix: this.prefix || this.logger['defaultPrefix'],
197
+ };
198
+
199
+ // Merge contexts
200
+ const defaultContext = this.logger['defaultContext'];
201
+ if (defaultContext || this.context) {
202
+ entry.context = {
203
+ ...defaultContext,
204
+ ...this.context,
205
+ };
206
+ }
207
+
208
+ if (this.metadata) {
209
+ entry.metadata = this.metadata;
210
+ }
211
+
212
+ if (this.errorObj) {
213
+ entry.error = this.errorObj;
214
+ }
215
+
216
+ return entry;
217
+ }
218
+
219
+ private log(level: LogLevel, message: string): void {
220
+ if (level < this.logger.getLevel()) {
221
+ return;
222
+ }
223
+
224
+ const entry = this.createEntry(level, message);
225
+ this.logger['handler'](entry);
226
+ }
227
+
228
+ trace(message: string): void {
229
+ this.log(LogLevel.TRACE, message);
230
+ }
231
+
232
+ debug(message: string): void {
233
+ this.log(LogLevel.DEBUG, message);
234
+ }
235
+
236
+ info(message: string): void {
237
+ this.log(LogLevel.INFO, message);
238
+ }
239
+
240
+ warn(message: string): void {
241
+ this.log(LogLevel.WARN, message);
242
+ }
243
+
244
+ error(message: string): void {
245
+ this.log(LogLevel.ERROR, message);
246
+ }
247
+ }
package/src/types.ts ADDED
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Log levels in order of severity
3
+ */
4
+ export enum LogLevel {
5
+ TRACE = 0,
6
+ DEBUG = 1,
7
+ INFO = 2,
8
+ WARN = 3,
9
+ ERROR = 4,
10
+ SILENT = 5,
11
+ }
12
+
13
+ /**
14
+ * Log entry data structure
15
+ */
16
+ export interface LogEntry {
17
+ level: LogLevel;
18
+ message: string;
19
+ timestamp: number;
20
+ context?: Record<string, unknown>;
21
+ metadata?: Record<string, unknown>;
22
+ error?: Error;
23
+ prefix?: string;
24
+ }
25
+
26
+ /**
27
+ * Output handler function type
28
+ */
29
+ export type OutputHandler = (entry: LogEntry) => void;
30
+
31
+ /**
32
+ * Logger configuration options
33
+ */
34
+ export interface LoggerOptions {
35
+ /**
36
+ * Minimum log level to output (default: LogLevel.INFO)
37
+ */
38
+ level?: LogLevel | keyof typeof LogLevel;
39
+
40
+ /**
41
+ * Custom output handler (default: console)
42
+ */
43
+ handler?: OutputHandler;
44
+
45
+ /**
46
+ * Enable timestamps in log entries (default: true)
47
+ */
48
+ enableTimestamp?: boolean;
49
+
50
+ /**
51
+ * Default context that will be included in all logs
52
+ */
53
+ defaultContext?: Record<string, unknown>;
54
+
55
+ /**
56
+ * Default prefix for all log messages
57
+ */
58
+ defaultPrefix?: string;
59
+ }
60
+
61
+ /**
62
+ * Chainable logger interface
63
+ */
64
+ export interface LoggerChain {
65
+ /**
66
+ * Add context data that will be included in this log entry
67
+ */
68
+ withContext(context: Record<string, unknown>): LoggerChain;
69
+
70
+ /**
71
+ * Add metadata that will be included in this log entry
72
+ */
73
+ withMetadata(metadata: Record<string, unknown>): LoggerChain;
74
+
75
+ /**
76
+ * Add an error object to this log entry
77
+ */
78
+ withError(error: Error): LoggerChain;
79
+
80
+ /**
81
+ * Add a prefix to this log message
82
+ */
83
+ withPrefix(prefix: string): LoggerChain;
84
+
85
+ /**
86
+ * Log at trace level
87
+ */
88
+ trace(message: string): void;
89
+
90
+ /**
91
+ * Log at debug level
92
+ */
93
+ debug(message: string): void;
94
+
95
+ /**
96
+ * Log at info level
97
+ */
98
+ info(message: string): void;
99
+
100
+ /**
101
+ * Log at warn level
102
+ */
103
+ warn(message: string): void;
104
+
105
+ /**
106
+ * Log at error level
107
+ */
108
+ error(message: string): void;
109
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,63 @@
1
+ import { LogLevel, LogEntry } from './types';
2
+
3
+ /**
4
+ * Convert log level string to LogLevel enum
5
+ */
6
+ export function parseLogLevel(
7
+ level: LogLevel | keyof typeof LogLevel
8
+ ): LogLevel {
9
+ if (typeof level === 'number') {
10
+ return level;
11
+ }
12
+ return LogLevel[level] ?? LogLevel.INFO;
13
+ }
14
+
15
+ /**
16
+ * Get log level name
17
+ */
18
+ export function getLogLevelName(level: LogLevel): string {
19
+ return LogLevel[level] ?? 'UNKNOWN';
20
+ }
21
+
22
+ /**
23
+ * Default console output handler
24
+ */
25
+ export function createConsoleHandler(): (entry: LogEntry) => void {
26
+ const consoleMethods: Record<number, typeof console.log> = {
27
+ [LogLevel.TRACE]: console.trace || console.debug,
28
+ [LogLevel.DEBUG]: console.debug,
29
+ [LogLevel.INFO]: console.info,
30
+ [LogLevel.WARN]: console.warn,
31
+ [LogLevel.ERROR]: console.error,
32
+ };
33
+
34
+ return (entry) => {
35
+ const method = consoleMethods[entry.level] || console.log;
36
+ const parts: unknown[] = [];
37
+
38
+ // Add prefix if present
39
+ if (entry.prefix) {
40
+ parts.push(entry.prefix);
41
+ }
42
+
43
+ // Add message
44
+ parts.push(entry.message);
45
+
46
+ // Add context if present
47
+ if (entry.context && Object.keys(entry.context).length > 0) {
48
+ parts.push('\nContext:', entry.context);
49
+ }
50
+
51
+ // Add metadata if present
52
+ if (entry.metadata && Object.keys(entry.metadata).length > 0) {
53
+ parts.push('\nMetadata:', entry.metadata);
54
+ }
55
+
56
+ // Add error if present
57
+ if (entry.error) {
58
+ parts.push('\nError:', entry.error);
59
+ }
60
+
61
+ method(...parts);
62
+ };
63
+ }