@hardlydifficult/logger 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +116 -38
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @hardlydifficult/logger
2
2
 
3
- Plugin-based structured logger with Console, Discord, and File output plugins.
3
+ Plugin-based structured logger with configurable log levels and extensible output handlers for Console, File, and Discord.
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,97 +8,161 @@ Plugin-based structured logger with Console, Discord, and File output plugins.
8
8
  npm install @hardlydifficult/logger
9
9
  ```
10
10
 
11
- ## Usage
11
+ ## Quick Start
12
12
 
13
13
  ```typescript
14
- import { Logger, ConsolePlugin, FilePlugin, DiscordPlugin } from "@hardlydifficult/logger";
15
-
16
- const discord = new DiscordPlugin();
14
+ import { Logger, ConsolePlugin, FilePlugin } from "@hardlydifficult/logger";
17
15
 
18
16
  const logger = new Logger("info")
19
17
  .use(new ConsolePlugin())
20
- .use(new FilePlugin("/var/log/app.log"))
21
- .use(discord);
22
-
23
- // Wire up Discord sender once the bot is ready
24
- discord.setSender((msg) => channel.send(msg));
18
+ .use(new FilePlugin("./app.log"));
25
19
 
26
20
  logger.info("Server started", { port: 3000 });
27
- logger.warn("High memory usage", { usage: "85%" });
28
- logger.error("Request failed", { url: "/api/data", status: 500 });
29
-
30
- // Out-of-band notification (goes to plugins that support notify)
31
- logger.notify("Deployment complete");
21
+ // Output to console: [2025-01-15T10:30:00.000Z] INFO: Server started {"port":3000}
22
+ // Output to file: {"level":"info","message":"Server started","timestamp":"2025-01-15T10:30:00.000Z","context":{"port":3000}}
32
23
  ```
33
24
 
34
- ## API
25
+ ## Core Logger
35
26
 
36
- ### `Logger`
27
+ The `Logger` class dispatches log entries to registered plugins based on a minimum log level.
37
28
 
38
29
  ```typescript
39
- new Logger(minLevel?: LogLevel) // default: "info"
30
+ import { Logger } from "@hardlydifficult/logger";
31
+
32
+ const logger = new Logger("info"); // default: "info"
40
33
  ```
41
34
 
35
+ ### Log Levels
36
+
37
+ Log levels in order of severity: `debug` < `info` < `warn` < `error`. Entries below the logger's `minLevel` are filtered out before reaching plugins.
38
+
39
+ ### Methods
40
+
42
41
  | Method | Description |
43
42
  |--------|-------------|
44
- | `use(plugin)` | Register a plugin (returns `this` for chaining) |
43
+ | `use(plugin, options?)` | Register a plugin; returns `this` for chaining. Optional `minLevel` filters entries per-plugin. |
45
44
  | `debug(message, context?)` | Log at debug level |
46
45
  | `info(message, context?)` | Log at info level |
47
46
  | `warn(message, context?)` | Log at warn level |
48
47
  | `error(message, context?)` | Log at error level |
49
48
  | `notify(message)` | Send out-of-band notification to plugins that support it |
50
49
 
51
- Log levels: `debug` < `info` < `warn` < `error`. Entries below `minLevel` are filtered out.
50
+ ### Plugin-Level Filtering
51
+
52
+ Each plugin can have its own minimum log level, independent of the logger's global level:
53
+
54
+ ```typescript
55
+ const logger = new Logger("debug")
56
+ .use(new ConsolePlugin()) // receives all levels
57
+ .use(new FilePlugin("./errors.log"), { minLevel: "error" }); // only errors
58
+
59
+ logger.debug("This goes to console only");
60
+ logger.error("This goes to both console and file");
61
+ ```
62
+
63
+ ## Console Plugin
64
+
65
+ Outputs formatted log entries to the console, routing to `console.log`, `console.warn`, or `console.error` based on log level.
66
+
67
+ ```typescript
68
+ import { Logger, ConsolePlugin } from "@hardlydifficult/logger";
69
+
70
+ const logger = new Logger("info").use(new ConsolePlugin());
52
71
 
53
- ### Plugins
72
+ logger.info("Server started", { port: 3000 });
73
+ // Output: [2025-01-15T10:30:00.000Z] INFO: Server started {"port":3000}
74
+ ```
54
75
 
55
- #### `ConsolePlugin`
76
+ ### Format
56
77
 
57
- Logs to `console.log`/`console.warn`/`console.error` with formatted timestamps.
78
+ The `formatEntry` function is also exported for custom formatting:
58
79
 
59
80
  ```typescript
60
- new ConsolePlugin()
81
+ import { formatEntry } from "@hardlydifficult/logger";
82
+
83
+ const entry = {
84
+ level: "warn",
85
+ message: "High memory",
86
+ timestamp: "2025-01-15T10:30:00.000Z",
87
+ context: { usage: "85%" },
88
+ };
89
+
90
+ console.log(formatEntry(entry));
91
+ // Output: [2025-01-15T10:30:00.000Z] WARN: High memory {"usage":"85%"}
61
92
  ```
62
93
 
63
- #### `FilePlugin`
94
+ ## File Plugin
64
95
 
65
- Appends JSON log entries to a file (one entry per line). Creates the directory if needed.
96
+ Appends JSON-serialized log entries to a file (one entry per line, JSONL format). Creates parent directories automatically.
66
97
 
67
98
  ```typescript
68
- new FilePlugin(filePath: string)
99
+ import { Logger, FilePlugin } from "@hardlydifficult/logger";
100
+
101
+ const logger = new Logger("info").use(new FilePlugin("./logs/app.log"));
102
+
103
+ logger.info("Request processed", { method: "GET", path: "/api/users" });
104
+ // Appends: {"level":"info","message":"Request processed","timestamp":"2025-01-15T10:30:00.000Z","context":{"method":"GET","path":"/api/users"}}
69
105
  ```
70
106
 
71
- #### `DiscordPlugin`
107
+ ## Discord Plugin
72
108
 
73
- Sends warn/error logs and notifications to Discord. The sender is set lazily since the Discord connection may not be ready at logger creation time.
109
+ Forwards warn/error log entries and notifications to Discord via a configurable sender function. Useful for alerting on critical issues.
74
110
 
75
111
  ```typescript
112
+ import { Logger, DiscordPlugin } from "@hardlydifficult/logger";
113
+
76
114
  const discord = new DiscordPlugin();
115
+ const logger = new Logger("info").use(discord);
116
+
117
+ // Set the sender once your Discord bot is ready
77
118
  discord.setSender((msg) => channel.send(msg));
119
+
120
+ logger.warn("High memory usage", { usage: "85%" });
121
+ // Sends to Discord: ⚠️ **WARN**: High memory usage
122
+ // ```json
123
+ // {
124
+ // "usage": "85%"
125
+ // }
126
+ // ```
127
+
128
+ logger.notify("Deployment complete");
129
+ // Sends to Discord: Deployment complete
78
130
  ```
79
131
 
80
- ### Custom Plugins
132
+ ### Behavior
133
+
134
+ | Behavior | Description |
135
+ |----------|-------------|
136
+ | Only `warn` and `error` log entries are sent (debug/info are filtered) | |
137
+ | Warn entries use ⚠️ emoji; error entries use 🚨 emoji | |
138
+ | Context is formatted as a JSON code block when present | |
139
+ | `notify()` sends messages directly without level filtering | |
140
+ | If `setSender` is not called, entries are silently dropped | |
141
+
142
+ ## Custom Plugins
81
143
 
82
- Implement the `LoggerPlugin` interface:
144
+ Implement the `LoggerPlugin` interface to create custom output handlers:
83
145
 
84
146
  ```typescript
85
147
  import type { LoggerPlugin, LogEntry } from "@hardlydifficult/logger";
148
+ import { Logger } from "@hardlydifficult/logger";
86
149
 
87
- class MyPlugin implements LoggerPlugin {
150
+ class SlackPlugin implements LoggerPlugin {
88
151
  log(entry: LogEntry): void {
89
- // Handle log entry
152
+ if (entry.level === "error") {
153
+ // Send to Slack
154
+ }
90
155
  }
91
156
 
92
- // Optional: handle notify() calls
93
157
  notify?(message: string): void {
94
- // Handle notification
158
+ // Send notification to Slack
95
159
  }
96
160
  }
97
161
 
98
- logger.use(new MyPlugin());
162
+ const logger = new Logger("info").use(new SlackPlugin());
99
163
  ```
100
164
 
101
- ### Types
165
+ ## Types
102
166
 
103
167
  ```typescript
104
168
  type LogLevel = "debug" | "info" | "warn" | "error";
@@ -106,7 +170,7 @@ type LogLevel = "debug" | "info" | "warn" | "error";
106
170
  interface LogEntry {
107
171
  readonly level: LogLevel;
108
172
  readonly message: string;
109
- readonly timestamp: string;
173
+ readonly timestamp: string; // ISO 8601 format
110
174
  readonly context?: Readonly<Record<string, unknown>>;
111
175
  }
112
176
 
@@ -114,4 +178,18 @@ interface LoggerPlugin {
114
178
  log(entry: LogEntry): void;
115
179
  notify?(message: string): void;
116
180
  }
181
+
182
+ type DiscordSender = (message: string) => void;
117
183
  ```
184
+
185
+ ## Error Handling
186
+
187
+ All plugins are isolated—if one plugin throws an error, it does not affect other plugins or the logger itself. Errors are silently swallowed to ensure logging never crashes your application.
188
+
189
+ ```typescript
190
+ const logger = new Logger("info")
191
+ .use(new ConsolePlugin()) // works fine
192
+ .use(brokenPlugin); // throws, but doesn't break the logger
193
+
194
+ logger.info("This still works"); // ConsolePlugin receives it
195
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hardlydifficult/logger",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "files": [