@remote-logger/sdk 0.1.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 +177 -0
- package/SKILL.md +121 -0
- package/dist/index.d.mts +130 -0
- package/dist/index.d.ts +130 -0
- package/dist/index.js +548 -0
- package/dist/index.mjs +522 -0
- package/dist/init.js +34 -0
- package/package.json +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Remote Logger SDK
|
|
2
|
+
|
|
3
|
+
A lightweight logging SDK that sends structured logs to a Remote Logger backend via HTTP or WebSocket, while echoing to the local console.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { createLogger } from '@remote-logger/sdk';
|
|
9
|
+
|
|
10
|
+
const logger = createLogger({
|
|
11
|
+
logId: 'your-log-file-id',
|
|
12
|
+
apiKey: 'your-api-key',
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
logger.info("server started", { port: 3000 });
|
|
16
|
+
logger.warn("slow query", { duration: 1200, query: "SELECT ..." });
|
|
17
|
+
logger.error("payment failed", { orderId: "abc-123" });
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Configuration
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
const logger = createLogger({
|
|
24
|
+
// Required
|
|
25
|
+
logId: 'uuid', // Log file ID from the dashboard
|
|
26
|
+
apiKey: 'your-api-key', // API key for ingestion auth
|
|
27
|
+
|
|
28
|
+
// Optional
|
|
29
|
+
group: 'api', // Default group for all entries
|
|
30
|
+
traceId: 'req-123', // Default trace ID
|
|
31
|
+
silent: false, // Set true to suppress console output (default: false)
|
|
32
|
+
level: 'DEBUG', // Only send logs at this level and above (default: 'DEBUG')
|
|
33
|
+
onError: (err, logs) => { // Custom error handler for flush failures
|
|
34
|
+
// handle error
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### `level` option
|
|
40
|
+
|
|
41
|
+
Controls which logs are sent to the remote service. Defaults to `'DEBUG'` (send everything). Logs below this level are silently dropped — they won't be sent remotely or printed to the console.
|
|
42
|
+
|
|
43
|
+
This is useful when you want full remote logging in production but don't need to send every debug log during local development where you're already watching the console:
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
const logger = createLogger({
|
|
47
|
+
logId: 'your-log-file-id',
|
|
48
|
+
apiKey: 'your-api-key',
|
|
49
|
+
level: process.env.NODE_ENV === 'production' ? 'DEBUG' : 'ERROR',
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
In this setup, local dev only sends errors remotely (you're already seeing everything in your terminal), while production captures the full picture for dashboard viewing and LLM analysis.
|
|
54
|
+
|
|
55
|
+
### `silent` option
|
|
56
|
+
|
|
57
|
+
By default, every `logger.*` call also prints to the local console so developers don't lose visibility when replacing `console.log` with `logger.info`. Set `silent: true` in production to disable this.
|
|
58
|
+
|
|
59
|
+
## Log Methods
|
|
60
|
+
|
|
61
|
+
Every level supports two call styles:
|
|
62
|
+
|
|
63
|
+
### Simple form — message + optional metadata
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
logger.debug("cache miss", { key: "user:42" });
|
|
67
|
+
logger.info("request handled", { method: "GET", path: "/api/users", ms: 12 });
|
|
68
|
+
logger.warn("rate limit approaching", { current: 95, max: 100 });
|
|
69
|
+
logger.error("query failed", { table: "orders" });
|
|
70
|
+
logger.fatal("database unreachable");
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The second argument is a plain metadata object. It can contain nested objects and arrays — the SDK sends it as-is.
|
|
74
|
+
|
|
75
|
+
### Object form — for per-call group or trace ID
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
logger.info({
|
|
79
|
+
message: "user signed in",
|
|
80
|
+
group: "auth",
|
|
81
|
+
traceId: "req-abc",
|
|
82
|
+
metadata: { userId: 123, method: "oauth" },
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Use this when you need to override `group` or `traceId` on a single call without changing the logger's defaults.
|
|
87
|
+
|
|
88
|
+
## Error Logging
|
|
89
|
+
|
|
90
|
+
`error()` and `fatal()` accept Error objects directly — the SDK extracts the message, stack trace, and error type automatically:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
try {
|
|
94
|
+
await processPayment(order);
|
|
95
|
+
} catch (err) {
|
|
96
|
+
logger.error(err as Error, { orderId: order.id });
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
This populates the `stack` and `error_type` columns in ClickHouse so stack traces and exception class names are stored as structured fields, not mashed into the message.
|
|
101
|
+
|
|
102
|
+
## Scoped Loggers
|
|
103
|
+
|
|
104
|
+
### Group Logger
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
const authLogger = logger.withGroup("auth");
|
|
108
|
+
authLogger.info("login attempt", { email: "user@example.com" });
|
|
109
|
+
// → group: "auth"
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Trace Logger
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
const reqLogger = logger.withTraceId("req-abc-123");
|
|
116
|
+
reqLogger.info("handling request");
|
|
117
|
+
reqLogger.warn("slow downstream call", { service: "billing", ms: 800 });
|
|
118
|
+
// → trace_id: "req-abc-123" on both entries
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Mutating defaults
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
// Set for all subsequent calls
|
|
125
|
+
logger.setGroup("worker");
|
|
126
|
+
logger.setTraceId("job-456");
|
|
127
|
+
|
|
128
|
+
// Clear
|
|
129
|
+
logger.setGroup(undefined);
|
|
130
|
+
logger.setTraceId(undefined);
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Console Interception
|
|
134
|
+
|
|
135
|
+
Capture existing `console.*` calls without changing application code:
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
logger.interceptConsole(); // uses group "console"
|
|
139
|
+
logger.interceptConsole("app"); // uses custom group
|
|
140
|
+
|
|
141
|
+
// Later, to restore original console behavior:
|
|
142
|
+
logger.restoreConsole();
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
This wraps `console.debug`, `console.log`, `console.info`, and `console.warn`. Errors are captured via `window.addEventListener('error')` and `unhandledrejection` listeners instead of wrapping `console.error`, to avoid polluting stack traces in frameworks like Next.js/React.
|
|
146
|
+
|
|
147
|
+
## AI Assistant Integration
|
|
148
|
+
|
|
149
|
+
Run the init command to configure your AI coding assistant (Claude Code, Cursor, etc.) to use the SDK:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
npx @remote-logger/sdk init
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
This adds a reference to the SDK's workflow guide in your `CLAUDE.md` and `.cursorrules`. Once configured, your AI assistant will know how to place logs using the SDK and query them via MCP — no copy-pasting logs needed.
|
|
156
|
+
|
|
157
|
+
To set up manually, add this line to your `CLAUDE.md`:
|
|
158
|
+
|
|
159
|
+
```md
|
|
160
|
+
> See node_modules/@remote-logger/sdk/SKILL.md for the @remote-logger/sdk logging workflow.
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## MCP Server (AI Log Analysis)
|
|
164
|
+
|
|
165
|
+
Remote Logger includes an MCP server that lets AI assistants query your logs directly.
|
|
166
|
+
|
|
167
|
+
### Add
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
claude mcp add remote-logger https://log.terodato.com/mcp --header "Authorization: Bearer YOUR_API_KEY"
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Remove
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
claude mcp remove remote-logger
|
|
177
|
+
```
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# @remote-logger/sdk — AI Workflow Guide
|
|
2
|
+
|
|
3
|
+
This project uses Remote Logger to give you persistent runtime insight. You can place log statements freely — they are sent to a central server and you can query them via MCP at any time. This means you don't need to ask the user to copy-paste logs. You place them, the user reproduces the issue, and you query the results yourself.
|
|
4
|
+
|
|
5
|
+
## The Workflow
|
|
6
|
+
|
|
7
|
+
1. **Place logs** in the code using the SDK (see API below)
|
|
8
|
+
2. **Ask the user to reproduce** the issue (or wait for the next occurrence)
|
|
9
|
+
3. **Query the logs via MCP** using `logs_query` — you have direct access, no copy-pasting needed
|
|
10
|
+
4. **Analyze and fix** based on the runtime data
|
|
11
|
+
5. **Repeat** — leave useful logs in place, add more if needed
|
|
12
|
+
|
|
13
|
+
You should default to placing logs early when investigating runtime issues. Don't spend multiple rounds guessing from static code alone — instrument the code, get the data, then fix the bug. Logs persist across runs, so once placed, they keep providing insight.
|
|
14
|
+
|
|
15
|
+
**Cleanup rule:** Use `logger.debug()` for temporary diagnostic logs placed during investigation. Once the issue is resolved, remove all debug-level logs you added. Permanent logs that provide ongoing operational value (errors, warnings, key events) should use the appropriate level and can stay.
|
|
16
|
+
|
|
17
|
+
## When to Place Logs
|
|
18
|
+
|
|
19
|
+
- Debugging a runtime bug that isn't obvious from code alone
|
|
20
|
+
- Investigating timing issues, race conditions, or intermittent failures
|
|
21
|
+
- Understanding the flow through async code, event handlers, or callbacks
|
|
22
|
+
- Correlating behavior across multiple services or processes (use `traceId`)
|
|
23
|
+
- Monitoring a specific code path after a fix to verify it works
|
|
24
|
+
|
|
25
|
+
## SDK Quick Reference
|
|
26
|
+
|
|
27
|
+
### Setup (already done if the project uses this SDK)
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { createLogger } from '@remote-logger/sdk';
|
|
31
|
+
|
|
32
|
+
const logger = createLogger({
|
|
33
|
+
logId: 'uuid-from-dashboard', // required — identifies the log stream
|
|
34
|
+
apiKey: 'rl_...', // required — ingestion auth
|
|
35
|
+
group: 'api', // optional: default group for all entries
|
|
36
|
+
traceId: 'req-123', // optional: default trace ID
|
|
37
|
+
level: 'DEBUG', // optional: minimum level (default: 'DEBUG')
|
|
38
|
+
silent: false, // optional: suppress console echo (default: false)
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Logging
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
logger.debug("cache hit", { key: "user:42" });
|
|
46
|
+
logger.info("request handled", { method: "GET", path: "/api/users", ms: 12 });
|
|
47
|
+
logger.warn("slow query", { duration: 1200 });
|
|
48
|
+
logger.error(err as Error, { orderId: order.id }); // Error objects extract stack + error_type
|
|
49
|
+
logger.fatal(new Error("database unreachable"));
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Scoped Loggers (for grouping or tracing)
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
const authLogger = logger.withGroup("auth"); // all calls get group: "auth"
|
|
56
|
+
const reqLogger = logger.withTraceId("req-abc"); // all calls get trace_id: "req-abc"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Use `group` to categorize logs by subsystem (e.g., "auth", "billing", "renderer"). Use `traceId` to correlate logs across a single request or operation, especially across services.
|
|
60
|
+
|
|
61
|
+
### Console Interception
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
logger.interceptConsole(); // captures console.debug/log/info/warn + unhandled errors
|
|
65
|
+
logger.interceptConsole("app"); // with a custom group name
|
|
66
|
+
logger.restoreConsole(); // undo
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Use this to capture logs from third-party code or existing console.log statements without modifying them.
|
|
70
|
+
|
|
71
|
+
## Reading Logs via MCP
|
|
72
|
+
|
|
73
|
+
You have two MCP tools available:
|
|
74
|
+
|
|
75
|
+
- **`logs_list_log_files`** — Lists all log files with data. Call this first to find the log file ID.
|
|
76
|
+
- **`logs_query`** — Execute SQL (ClickHouse) against a log file. Tenant/log filtering is automatic.
|
|
77
|
+
|
|
78
|
+
### Common Queries
|
|
79
|
+
|
|
80
|
+
```sql
|
|
81
|
+
-- Recent errors
|
|
82
|
+
SELECT timestamp, level, message, stack FROM logs.entries WHERE level = 'ERROR' ORDER BY timestamp DESC LIMIT 20
|
|
83
|
+
|
|
84
|
+
-- Errors by type
|
|
85
|
+
SELECT error_type, count(*) as cnt FROM logs.entries WHERE error_type IS NOT NULL GROUP BY error_type ORDER BY cnt DESC
|
|
86
|
+
|
|
87
|
+
-- Search for a specific message pattern
|
|
88
|
+
SELECT timestamp, level, group, message FROM logs.entries WHERE message LIKE '%ECONNREFUSED%' ORDER BY timestamp DESC LIMIT 20
|
|
89
|
+
|
|
90
|
+
-- Error rate over time
|
|
91
|
+
SELECT toStartOfMinute(timestamp) as minute, count(*) FROM logs.entries WHERE level = 'ERROR' GROUP BY minute ORDER BY minute
|
|
92
|
+
|
|
93
|
+
-- Logs by group
|
|
94
|
+
SELECT group, count(*) FROM logs.entries GROUP BY group ORDER BY count() DESC
|
|
95
|
+
|
|
96
|
+
-- Trace a specific request across services
|
|
97
|
+
SELECT timestamp, group, level, message FROM logs.entries WHERE trace_id = 'req-abc-123' ORDER BY timestamp
|
|
98
|
+
|
|
99
|
+
-- Recent logs from a specific group
|
|
100
|
+
SELECT timestamp, level, message FROM logs.entries WHERE group = 'auth' ORDER BY timestamp DESC LIMIT 50
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Table Schema
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
seq UInt64 -- sequence number
|
|
107
|
+
timestamp DateTime64(3) -- e.g., '2025-01-22 14:30:00.123'
|
|
108
|
+
level String -- DEBUG, INFO, WARN, ERROR, FATAL
|
|
109
|
+
trace_id String -- request correlation
|
|
110
|
+
group String -- categorization (e.g., 'api/billing', 'main', 'renderer')
|
|
111
|
+
message String -- log content
|
|
112
|
+
stack Nullable(String) -- stack trace
|
|
113
|
+
error_type String -- exception class name
|
|
114
|
+
metadata Map(String, String) -- arbitrary key-value pairs
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Do NOT
|
|
118
|
+
|
|
119
|
+
- Do not pass `batchSize`, `flushInterval`, `httpEndpoint`, `wsEndpoint`, or `transport` — these do not exist.
|
|
120
|
+
- Do not call `errorWithStack()` — it does not exist. Use `logger.error(err)` instead.
|
|
121
|
+
- Do not call `logger.shutdown()` unless explicitly tearing down the logger mid-process. It is not required.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
type LogLevel = 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'FATAL';
|
|
2
|
+
interface LoggerConfig {
|
|
3
|
+
logId: string;
|
|
4
|
+
apiKey?: string;
|
|
5
|
+
group?: string;
|
|
6
|
+
/** Only send logs at this level and above to the remote service (default: 'DEBUG' — sends everything) */
|
|
7
|
+
level?: LogLevel;
|
|
8
|
+
onError?: (error: Error, logs: InternalLogEntry[]) => void;
|
|
9
|
+
traceId?: string;
|
|
10
|
+
/** When true, skip console output from logger.* methods; default false */
|
|
11
|
+
silent?: boolean;
|
|
12
|
+
/** When true, log SDK internal operations to console for troubleshooting */
|
|
13
|
+
debug?: boolean;
|
|
14
|
+
/** @internal Override base URL for development (e.g. 'http://localhost:3500') */
|
|
15
|
+
_endpoint?: string;
|
|
16
|
+
/** @internal Override WebSocket base URL for development (e.g. 'ws://localhost:3501') */
|
|
17
|
+
_wsEndpoint?: string;
|
|
18
|
+
/** @internal Full URL for log ingestion proxy (e.g. '/api/internal/ingest'). When set, uses this as the POST URL directly (no /ingest/http appended), skips WebSocket. */
|
|
19
|
+
_ingestUrl?: string;
|
|
20
|
+
}
|
|
21
|
+
interface InternalLogEntry {
|
|
22
|
+
timestamp: string;
|
|
23
|
+
level: LogLevel;
|
|
24
|
+
message: string;
|
|
25
|
+
logId: string;
|
|
26
|
+
group?: string;
|
|
27
|
+
trace_id?: string;
|
|
28
|
+
stack?: string;
|
|
29
|
+
error_type?: string;
|
|
30
|
+
metadata?: Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
/** Object form for logger.info({ message, group?, metadata? }) */
|
|
33
|
+
interface LogEntryObject {
|
|
34
|
+
message: string;
|
|
35
|
+
group?: string;
|
|
36
|
+
traceId?: string;
|
|
37
|
+
metadata?: Record<string, unknown>;
|
|
38
|
+
}
|
|
39
|
+
type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'failed';
|
|
40
|
+
declare class Logger {
|
|
41
|
+
private config;
|
|
42
|
+
private baseUrl;
|
|
43
|
+
private httpIngestUrl;
|
|
44
|
+
private wsIngestUrl;
|
|
45
|
+
private proxyMode;
|
|
46
|
+
private buffer;
|
|
47
|
+
private flushTimer;
|
|
48
|
+
private flushPromise;
|
|
49
|
+
private consoleIntercepted;
|
|
50
|
+
private httpBackoffMs;
|
|
51
|
+
private maxBackoffMs;
|
|
52
|
+
private consecutiveHttpFailures;
|
|
53
|
+
private _onError;
|
|
54
|
+
private _onUnhandledRejection;
|
|
55
|
+
private ws;
|
|
56
|
+
private wsState;
|
|
57
|
+
private wsReconnectTimer;
|
|
58
|
+
private wsReconnectAttempts;
|
|
59
|
+
private maxWsReconnectAttempts;
|
|
60
|
+
private _onVisibilityChange;
|
|
61
|
+
private _onBeforeUnload;
|
|
62
|
+
constructor(config: LoggerConfig);
|
|
63
|
+
private debugLog;
|
|
64
|
+
private connectWebSocket;
|
|
65
|
+
private startFlushTimer;
|
|
66
|
+
private shouldLog;
|
|
67
|
+
private formatArgs;
|
|
68
|
+
private static readonly CONSOLE_METHOD;
|
|
69
|
+
private log;
|
|
70
|
+
private parseArgs;
|
|
71
|
+
debug(entry: LogEntryObject): void;
|
|
72
|
+
debug(message: string, metadata?: Record<string, unknown>): void;
|
|
73
|
+
info(entry: LogEntryObject): void;
|
|
74
|
+
info(message: string, metadata?: Record<string, unknown>): void;
|
|
75
|
+
warn(entry: LogEntryObject): void;
|
|
76
|
+
warn(message: string, metadata?: Record<string, unknown>): void;
|
|
77
|
+
error(entry: LogEntryObject): void;
|
|
78
|
+
error(message: string, metadata?: Record<string, unknown>): void;
|
|
79
|
+
error(err: Error, metadata?: Record<string, unknown>): void;
|
|
80
|
+
fatal(entry: LogEntryObject): void;
|
|
81
|
+
fatal(message: string, metadata?: Record<string, unknown>): void;
|
|
82
|
+
fatal(err: Error, metadata?: Record<string, unknown>): void;
|
|
83
|
+
withTraceId(traceId: string): TraceLogger;
|
|
84
|
+
withGroup(group: string): GroupLogger;
|
|
85
|
+
setTraceId(traceId: string | undefined): void;
|
|
86
|
+
setGroup(group: string | undefined): void;
|
|
87
|
+
interceptConsole(group?: string): void;
|
|
88
|
+
restoreConsole(): void;
|
|
89
|
+
flush(): Promise<void>;
|
|
90
|
+
private doFlush;
|
|
91
|
+
private sendViaWebSocket;
|
|
92
|
+
private sendViaHttp;
|
|
93
|
+
/** Fire-and-forget flush using fetch with keepalive (works during page unload) */
|
|
94
|
+
private flushSync;
|
|
95
|
+
private registerLifecycleHooks;
|
|
96
|
+
/** Check server connectivity and API key validity */
|
|
97
|
+
ping(): Promise<{
|
|
98
|
+
ok: boolean;
|
|
99
|
+
latencyMs: number;
|
|
100
|
+
error?: string;
|
|
101
|
+
}>;
|
|
102
|
+
/** Get current connection status */
|
|
103
|
+
getConnectionStatus(): {
|
|
104
|
+
transport: 'websocket' | 'http';
|
|
105
|
+
state: ConnectionState;
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
declare class TraceLogger {
|
|
109
|
+
private parent;
|
|
110
|
+
private traceId;
|
|
111
|
+
constructor(parent: Logger, traceId: string);
|
|
112
|
+
debug(message: string, metadata?: Record<string, unknown>): void;
|
|
113
|
+
info(message: string, metadata?: Record<string, unknown>): void;
|
|
114
|
+
warn(message: string, metadata?: Record<string, unknown>): void;
|
|
115
|
+
error(message: string, metadata?: Record<string, unknown>): void;
|
|
116
|
+
fatal(message: string, metadata?: Record<string, unknown>): void;
|
|
117
|
+
}
|
|
118
|
+
declare class GroupLogger {
|
|
119
|
+
private parent;
|
|
120
|
+
private group;
|
|
121
|
+
constructor(parent: Logger, group: string);
|
|
122
|
+
debug(message: string, metadata?: Record<string, unknown>): void;
|
|
123
|
+
info(message: string, metadata?: Record<string, unknown>): void;
|
|
124
|
+
warn(message: string, metadata?: Record<string, unknown>): void;
|
|
125
|
+
error(message: string, metadata?: Record<string, unknown>): void;
|
|
126
|
+
fatal(message: string, metadata?: Record<string, unknown>): void;
|
|
127
|
+
}
|
|
128
|
+
declare function createLogger(config: LoggerConfig): Logger;
|
|
129
|
+
|
|
130
|
+
export { type InternalLogEntry, type LogEntryObject, type LogLevel, Logger, type LoggerConfig, createLogger, createLogger as default };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
type LogLevel = 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'FATAL';
|
|
2
|
+
interface LoggerConfig {
|
|
3
|
+
logId: string;
|
|
4
|
+
apiKey?: string;
|
|
5
|
+
group?: string;
|
|
6
|
+
/** Only send logs at this level and above to the remote service (default: 'DEBUG' — sends everything) */
|
|
7
|
+
level?: LogLevel;
|
|
8
|
+
onError?: (error: Error, logs: InternalLogEntry[]) => void;
|
|
9
|
+
traceId?: string;
|
|
10
|
+
/** When true, skip console output from logger.* methods; default false */
|
|
11
|
+
silent?: boolean;
|
|
12
|
+
/** When true, log SDK internal operations to console for troubleshooting */
|
|
13
|
+
debug?: boolean;
|
|
14
|
+
/** @internal Override base URL for development (e.g. 'http://localhost:3500') */
|
|
15
|
+
_endpoint?: string;
|
|
16
|
+
/** @internal Override WebSocket base URL for development (e.g. 'ws://localhost:3501') */
|
|
17
|
+
_wsEndpoint?: string;
|
|
18
|
+
/** @internal Full URL for log ingestion proxy (e.g. '/api/internal/ingest'). When set, uses this as the POST URL directly (no /ingest/http appended), skips WebSocket. */
|
|
19
|
+
_ingestUrl?: string;
|
|
20
|
+
}
|
|
21
|
+
interface InternalLogEntry {
|
|
22
|
+
timestamp: string;
|
|
23
|
+
level: LogLevel;
|
|
24
|
+
message: string;
|
|
25
|
+
logId: string;
|
|
26
|
+
group?: string;
|
|
27
|
+
trace_id?: string;
|
|
28
|
+
stack?: string;
|
|
29
|
+
error_type?: string;
|
|
30
|
+
metadata?: Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
/** Object form for logger.info({ message, group?, metadata? }) */
|
|
33
|
+
interface LogEntryObject {
|
|
34
|
+
message: string;
|
|
35
|
+
group?: string;
|
|
36
|
+
traceId?: string;
|
|
37
|
+
metadata?: Record<string, unknown>;
|
|
38
|
+
}
|
|
39
|
+
type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'failed';
|
|
40
|
+
declare class Logger {
|
|
41
|
+
private config;
|
|
42
|
+
private baseUrl;
|
|
43
|
+
private httpIngestUrl;
|
|
44
|
+
private wsIngestUrl;
|
|
45
|
+
private proxyMode;
|
|
46
|
+
private buffer;
|
|
47
|
+
private flushTimer;
|
|
48
|
+
private flushPromise;
|
|
49
|
+
private consoleIntercepted;
|
|
50
|
+
private httpBackoffMs;
|
|
51
|
+
private maxBackoffMs;
|
|
52
|
+
private consecutiveHttpFailures;
|
|
53
|
+
private _onError;
|
|
54
|
+
private _onUnhandledRejection;
|
|
55
|
+
private ws;
|
|
56
|
+
private wsState;
|
|
57
|
+
private wsReconnectTimer;
|
|
58
|
+
private wsReconnectAttempts;
|
|
59
|
+
private maxWsReconnectAttempts;
|
|
60
|
+
private _onVisibilityChange;
|
|
61
|
+
private _onBeforeUnload;
|
|
62
|
+
constructor(config: LoggerConfig);
|
|
63
|
+
private debugLog;
|
|
64
|
+
private connectWebSocket;
|
|
65
|
+
private startFlushTimer;
|
|
66
|
+
private shouldLog;
|
|
67
|
+
private formatArgs;
|
|
68
|
+
private static readonly CONSOLE_METHOD;
|
|
69
|
+
private log;
|
|
70
|
+
private parseArgs;
|
|
71
|
+
debug(entry: LogEntryObject): void;
|
|
72
|
+
debug(message: string, metadata?: Record<string, unknown>): void;
|
|
73
|
+
info(entry: LogEntryObject): void;
|
|
74
|
+
info(message: string, metadata?: Record<string, unknown>): void;
|
|
75
|
+
warn(entry: LogEntryObject): void;
|
|
76
|
+
warn(message: string, metadata?: Record<string, unknown>): void;
|
|
77
|
+
error(entry: LogEntryObject): void;
|
|
78
|
+
error(message: string, metadata?: Record<string, unknown>): void;
|
|
79
|
+
error(err: Error, metadata?: Record<string, unknown>): void;
|
|
80
|
+
fatal(entry: LogEntryObject): void;
|
|
81
|
+
fatal(message: string, metadata?: Record<string, unknown>): void;
|
|
82
|
+
fatal(err: Error, metadata?: Record<string, unknown>): void;
|
|
83
|
+
withTraceId(traceId: string): TraceLogger;
|
|
84
|
+
withGroup(group: string): GroupLogger;
|
|
85
|
+
setTraceId(traceId: string | undefined): void;
|
|
86
|
+
setGroup(group: string | undefined): void;
|
|
87
|
+
interceptConsole(group?: string): void;
|
|
88
|
+
restoreConsole(): void;
|
|
89
|
+
flush(): Promise<void>;
|
|
90
|
+
private doFlush;
|
|
91
|
+
private sendViaWebSocket;
|
|
92
|
+
private sendViaHttp;
|
|
93
|
+
/** Fire-and-forget flush using fetch with keepalive (works during page unload) */
|
|
94
|
+
private flushSync;
|
|
95
|
+
private registerLifecycleHooks;
|
|
96
|
+
/** Check server connectivity and API key validity */
|
|
97
|
+
ping(): Promise<{
|
|
98
|
+
ok: boolean;
|
|
99
|
+
latencyMs: number;
|
|
100
|
+
error?: string;
|
|
101
|
+
}>;
|
|
102
|
+
/** Get current connection status */
|
|
103
|
+
getConnectionStatus(): {
|
|
104
|
+
transport: 'websocket' | 'http';
|
|
105
|
+
state: ConnectionState;
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
declare class TraceLogger {
|
|
109
|
+
private parent;
|
|
110
|
+
private traceId;
|
|
111
|
+
constructor(parent: Logger, traceId: string);
|
|
112
|
+
debug(message: string, metadata?: Record<string, unknown>): void;
|
|
113
|
+
info(message: string, metadata?: Record<string, unknown>): void;
|
|
114
|
+
warn(message: string, metadata?: Record<string, unknown>): void;
|
|
115
|
+
error(message: string, metadata?: Record<string, unknown>): void;
|
|
116
|
+
fatal(message: string, metadata?: Record<string, unknown>): void;
|
|
117
|
+
}
|
|
118
|
+
declare class GroupLogger {
|
|
119
|
+
private parent;
|
|
120
|
+
private group;
|
|
121
|
+
constructor(parent: Logger, group: string);
|
|
122
|
+
debug(message: string, metadata?: Record<string, unknown>): void;
|
|
123
|
+
info(message: string, metadata?: Record<string, unknown>): void;
|
|
124
|
+
warn(message: string, metadata?: Record<string, unknown>): void;
|
|
125
|
+
error(message: string, metadata?: Record<string, unknown>): void;
|
|
126
|
+
fatal(message: string, metadata?: Record<string, unknown>): void;
|
|
127
|
+
}
|
|
128
|
+
declare function createLogger(config: LoggerConfig): Logger;
|
|
129
|
+
|
|
130
|
+
export { type InternalLogEntry, type LogEntryObject, type LogLevel, Logger, type LoggerConfig, createLogger, createLogger as default };
|