@hardlydifficult/logger 1.0.11 → 1.0.12

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 +136 -28
  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 configurable log levels and extensible output handlers for Console, File, and Discord.
3
+ Plugin-based structured logger with configurable log levels and extensible output handlers for Console, File, Discord, and Session-based JSONL tracking.
4
4
 
5
5
  ## Installation
6
6
 
@@ -60,6 +60,15 @@ logger.debug("This goes to console only");
60
60
  logger.error("This goes to both console and file");
61
61
  ```
62
62
 
63
+ ### Notifications
64
+
65
+ Out-of-band notifications are sent to plugins that implement `notify()`.
66
+
67
+ ```typescript
68
+ logger.notify("Deployment complete");
69
+ // DiscordPlugin will send this as a standalone message; other plugins ignore it.
70
+ ```
71
+
63
72
  ## Console Plugin
64
73
 
65
74
  Outputs formatted log entries to the console, routing to `console.log`, `console.warn`, or `console.error` based on log level.
@@ -106,39 +115,130 @@ logger.info("Request processed", { method: "GET", path: "/api/users" });
106
115
 
107
116
  ## Discord Plugin
108
117
 
109
- Forwards warn/error log entries and notifications to Discord via a configurable sender function. Useful for alerting on critical issues.
118
+ Forwards `warn` and `error` log entries and notifications to Discord via a configurable sender function. Useful for alerting on critical issues.
119
+
120
+ ### Setup
121
+
122
+ 1. Create a Discord webhook for your channel.
123
+ 2. Configure the sender to POST to the webhook URL.
110
124
 
111
125
  ```typescript
112
126
  import { Logger, DiscordPlugin } from "@hardlydifficult/logger";
113
127
 
128
+ const logger = new Logger("warn");
114
129
  const discord = new DiscordPlugin();
115
- const logger = new Logger("info").use(discord);
116
130
 
117
- // Set the sender once your Discord bot is ready
118
- discord.setSender((msg) => channel.send(msg));
131
+ discord.setSender((message) => {
132
+ // Example: use node:fetch or your preferred HTTP client
133
+ // fetch("https://discord.com/api/webhooks/...", {
134
+ // method: "POST",
135
+ // headers: { "Content-Type": "application/json" },
136
+ // body: JSON.stringify({ content: message })
137
+ // });
138
+ });
119
139
 
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
140
+ logger.use(discord);
130
141
  ```
131
142
 
132
143
  ### Behavior
133
144
 
134
145
  | Behavior | Description |
135
- |----------|-------------|
146
+ |--------|-------------|
136
147
  | Only `warn` and `error` log entries are sent (debug/info are filtered) | |
137
148
  | Warn entries use ⚠️ emoji; error entries use 🚨 emoji | |
138
149
  | Context is formatted as a JSON code block when present | |
139
150
  | `notify()` sends messages directly without level filtering | |
140
151
  | If `setSender` is not called, entries are silently dropped | |
141
152
 
153
+ ### Formatting
154
+
155
+ Error entries include a siren emoji and JSON context in a code block; warnings use a warning emoji.
156
+
157
+ ```typescript
158
+ logger.error("Database unavailable", { host: "db.example.com", retry: 3 });
159
+ // ➡️ Sends to Discord:
160
+ // 🚨 **ERROR**: Database unavailable
161
+ // ```json
162
+ // {
163
+ // "host": "db.example.com",
164
+ // "retry": 3
165
+ // }
166
+ // ```
167
+
168
+ logger.warn("Slow query detected", { durationMs: 2500 });
169
+ // ➡️ Sends to Discord:
170
+ // ⚠️ **WARN**: Slow query detected
171
+ ```
172
+
173
+ ## Session Tracking
174
+
175
+ The `SessionTracker` class persists structured session logs as JSONL files for debugging and analysis.
176
+
177
+ ### SessionTracker
178
+
179
+ Create a tracker pointing to a writable state directory.
180
+
181
+ ```typescript
182
+ import { SessionTracker } from "@hardlydifficult/logger";
183
+
184
+ const tracker = new SessionTracker({
185
+ stateDirectory: "/var/log", // Required
186
+ subdirectory: "ai-sessions", // Optional, defaults to "sessions"
187
+ maxAgeMs: 7 * 24 * 60 * 60 * 1000, // Optional, defaults to 7 days
188
+ });
189
+ ```
190
+
191
+ ### Append Entries
192
+
193
+ Each entry includes a type discriminator and arbitrary data.
194
+
195
+ ```typescript
196
+ tracker.append("sess-abc123", {
197
+ type: "session_start",
198
+ data: { userId: 456, prompt: "Hello" },
199
+ });
200
+
201
+ tracker.append("sess-abc123", {
202
+ type: "ai_response",
203
+ data: { response: "Hi there!", model: "gpt-4" },
204
+ });
205
+
206
+ tracker.append("sess-abc123", {
207
+ type: "tool_call",
208
+ data: { name: "Search", input: { query: "weather" } },
209
+ });
210
+ ```
211
+
212
+ ### Read & List Sessions
213
+
214
+ ```typescript
215
+ // Read all entries for a session
216
+ const entries = tracker.read("sess-abc123");
217
+ // Returns SessionEntry[] in chronological order
218
+
219
+ // List all tracked sessions with metadata
220
+ const sessions = tracker.list();
221
+ // Returns SessionInfo[] sorted by lastModifiedAt descending
222
+ ```
223
+
224
+ ### Session Metadata
225
+
226
+ | Field | Type | Description |
227
+ |-------|------|-------------|
228
+ | `sessionId` | string | Name of the `.jsonl` file (without extension) |
229
+ | `sizeBytes` | number | File size on disk |
230
+ | `startedAt` | string | ISO timestamp (first entry or file creation time) |
231
+ | `lastModifiedAt` | string | ISO timestamp of file’s last write |
232
+ | `entryCount` | number | Number of JSONL lines in the file |
233
+
234
+ ### Session Operations
235
+
236
+ ```typescript
237
+ tracker.has("sess-abc123"); // true/false
238
+ tracker.delete("sess-abc123"); // true if deleted, false if missing
239
+ tracker.cleanup(); // Deletes files older than maxAgeMs, returns count deleted
240
+ ```
241
+
142
242
  ## Custom Plugins
143
243
 
144
244
  Implement the `LoggerPlugin` interface to create custom output handlers:
@@ -165,20 +265,28 @@ const logger = new Logger("info").use(new SlackPlugin());
165
265
  ## Types
166
266
 
167
267
  ```typescript
168
- type LogLevel = "debug" | "info" | "warn" | "error";
169
-
170
- interface LogEntry {
171
- readonly level: LogLevel;
172
- readonly message: string;
173
- readonly timestamp: string; // ISO 8601 format
174
- readonly context?: Readonly<Record<string, unknown>>;
175
- }
268
+ import type {
269
+ LogLevel,
270
+ LogEntry,
271
+ LoggerPlugin,
272
+ SessionEntry,
273
+ SessionEntryType,
274
+ SessionInfo,
275
+ SessionTrackerOptions,
276
+ } from "@hardlydifficult/logger";
277
+ ```
176
278
 
177
- interface LoggerPlugin {
178
- log(entry: LogEntry): void;
179
- notify?(message: string): void;
180
- }
279
+ | Type | Description |
280
+ |------|-------------|
281
+ | `LogLevel` | `"debug" \| "info" \| "warn" \| "error"` |
282
+ | `LogEntry` | `{ level, message, timestamp, context? }` |
283
+ | `LoggerPlugin` | `{ log(entry): void; notify?(message): void }` |
284
+ | `SessionEntryType` | `"session_start" \| "ai_request" \| "ai_response" \| "tool_call" \| "tool_result" \| "error" \| "session_end" \| "metadata"` |
285
+ | `SessionEntry` | `{ type, timestamp, data }` |
286
+ | `SessionInfo` | Metadata about persisted session files |
287
+ | `SessionTrackerOptions` | `stateDirectory` (required), `subdirectory?`, `maxAgeMs?` |
181
288
 
289
+ ```typescript
182
290
  type DiscordSender = (message: string) => void;
183
291
  ```
184
292
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hardlydifficult/logger",
3
- "version": "1.0.11",
3
+ "version": "1.0.12",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "files": [