@visulima/pail 3.0.3 → 3.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/CHANGELOG.md +47 -0
- package/LICENSE.md +18 -391
- package/README.md +578 -0
- package/dist/index.browser.d.ts +4 -3
- package/dist/index.browser.js +12 -1
- package/dist/index.server.d.ts +19 -74
- package/dist/index.server.js +535 -5
- package/dist/interactive/index.d.ts +28 -0
- package/dist/interactive/index.js +2 -0
- package/dist/object-tree.d.ts +17 -0
- package/dist/object-tree.js +89 -0
- package/dist/packem_shared/AbstractJsonReporter-intFdT_A.js +204 -0
- package/dist/packem_shared/InteractiveManager-CZ85hGNW.js +172 -0
- package/dist/packem_shared/InteractiveStreamHook-DiSubbJ1.js +21 -0
- package/dist/packem_shared/JsonReporter-BqWsVkHP.js +60 -0
- package/dist/packem_shared/JsonReporter-DcM2LBX9.js +28 -0
- package/dist/packem_shared/PrettyReporter-BFWaYP_J.js +222 -0
- package/dist/packem_shared/PrettyReporter-CuLLKr6-.js +169 -0
- package/dist/packem_shared/{abstract-json-reporter-wUti0B3k.d.ts → abstract-json-reporter-DiyVyU0j.d.ts} +3 -3
- package/dist/packem_shared/{abstract-pretty-reporter-MOYolfGR.d.ts → abstract-pretty-reporter-BbOWXMCs.d.ts} +1 -1
- package/dist/packem_shared/abstract-pretty-reporter-DMPDCslJ.js +50 -0
- package/dist/packem_shared/constants-DfDr4MHC.js +119 -0
- package/dist/packem_shared/format-label-Btft2KGP.js +1194 -0
- package/dist/packem_shared/get-longest-label-C9PWeyKq.js +9 -0
- package/dist/packem_shared/index-BomQ3E6J.js +650 -0
- package/dist/packem_shared/index-DqKWykfa.js +1146 -0
- package/dist/packem_shared/interactive-stream-hook-DG4BtN12.js +141 -0
- package/dist/packem_shared/{pail.browser-Bs2ng_Qj.d.ts → pail.browser-By9KjOH7.d.ts} +8 -3
- package/dist/packem_shared/pail.browser-CPDOE_d1.js +1427 -0
- package/dist/packem_shared/{types-RidvA4RN.d.ts → types-D3ycu8-x.d.ts} +2 -29
- package/dist/packem_shared/write-console-log-based-on-level-DBmRYXpj.js +14 -0
- package/dist/packem_shared/write-stream-BG8fhcs3.js +6 -0
- package/dist/processor/caller/caller-processor.d.ts +2 -1
- package/dist/processor/caller/caller-processor.js +59 -1
- package/dist/processor/message-formatter-processor.d.ts +2 -1
- package/dist/processor/message-formatter-processor.js +67 -1
- package/dist/processor/opentelemetry-processor.d.ts +19 -0
- package/dist/processor/opentelemetry-processor.js +52 -0
- package/dist/processor/redact-processor.d.ts +2 -1
- package/dist/processor/redact-processor.js +30 -1
- package/dist/progress-bar.d.ts +76 -0
- package/dist/progress-bar.js +404 -0
- package/dist/reporter/file/json-file-reporter.d.ts +3 -2
- package/dist/reporter/file/json-file-reporter.js +136 -4
- package/dist/reporter/http/abstract-http-reporter.d.ts +102 -0
- package/dist/reporter/http/abstract-http-reporter.js +435 -0
- package/dist/reporter/http/http-reporter.d.ts +13 -0
- package/dist/reporter/http/http-reporter.edge-light.d.ts +168 -0
- package/dist/reporter/http/http-reporter.edge-light.js +651 -0
- package/dist/reporter/http/http-reporter.js +13 -0
- package/dist/reporter/json/index.browser.d.ts +3 -2
- package/dist/reporter/json/index.browser.js +2 -1
- package/dist/reporter/json/index.d.ts +3 -2
- package/dist/reporter/json/index.js +2 -1
- package/dist/reporter/pretty/index.browser.d.ts +3 -2
- package/dist/reporter/pretty/index.browser.js +1 -1
- package/dist/reporter/pretty/index.d.ts +3 -2
- package/dist/reporter/pretty/index.js +1 -1
- package/dist/reporter/simple/simple-reporter.server.d.ts +3 -2
- package/dist/reporter/simple/simple-reporter.server.js +186 -8
- package/dist/spinner.d.ts +154 -0
- package/dist/spinner.js +2150 -0
- package/package.json +69 -3
- package/dist/packem_shared/AbstractJsonReporter-UftN6CIL.js +0 -1
- package/dist/packem_shared/JsonReporter-DTBtHNaD.js +0 -2
- package/dist/packem_shared/JsonReporter-Dl4m0xZe.js +0 -1
- package/dist/packem_shared/PrettyReporter-APmxUrnh.js +0 -12
- package/dist/packem_shared/PrettyReporter-CGKSTI7X.js +0 -5
- package/dist/packem_shared/abstract-pretty-reporter-CUtSm20r.js +0 -1
- package/dist/packem_shared/constants-DKfCaSUR.js +0 -1
- package/dist/packem_shared/format-label-DqvZRRR6.js +0 -33
- package/dist/packem_shared/get-longest-label-B0NrI-o2.js +0 -1
- package/dist/packem_shared/getBarChar-mKDZW32R.js +0 -1
- package/dist/packem_shared/index-D9hWq9ka.js +0 -1
- package/dist/packem_shared/pail.browser-BmHoDvEA.js +0 -19
- package/dist/packem_shared/write-console-log-based-on-level-BP95fgQZ.js +0 -1
- package/dist/packem_shared/write-stream-CD8XFv1L.js +0 -1
package/README.md
CHANGED
|
@@ -104,6 +104,27 @@ Pail supports the logging levels described by [RFC 5424][rfc-5424].
|
|
|
104
104
|
|
|
105
105
|
- `EMERGENCY`: Emergency: system is unusable.
|
|
106
106
|
|
|
107
|
+
### Bypass Log Level Filter with Force
|
|
108
|
+
|
|
109
|
+
Normally, the logger will only output messages at or above the configured level.
|
|
110
|
+
|
|
111
|
+
However, you can force a log to be emitted regardless of the current level:
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
import { createPail } from "@visulima/pail";
|
|
115
|
+
|
|
116
|
+
const logger = createPail({
|
|
117
|
+
logLevel: "warn", // Only show warning and above
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
logger.info("This won't be logged"); // Filtered out
|
|
121
|
+
|
|
122
|
+
logger.force.error("Something went wrong!");
|
|
123
|
+
logger.force.info("This will show even if level is set to 'warn'");
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
⚠️ **Use this with care**—force is designed for exceptional situations where logs must be guaranteed to appear.
|
|
127
|
+
|
|
107
128
|
### Reporters
|
|
108
129
|
|
|
109
130
|
Reporters are responsible for writing the log messages to the console or a file. `pail` comes with a few built-in reporters:
|
|
@@ -112,6 +133,7 @@ Reporters are responsible for writing the log messages to the console or a file.
|
|
|
112
133
|
| ---------------------------- | ------------------------- |
|
|
113
134
|
| `JsonReporter` | `JsonReporter` |
|
|
114
135
|
| `PrettyReporter` | `PrettyReporter` |
|
|
136
|
+
| `HttpReporter` | `HttpReporter` |
|
|
115
137
|
| x | `SimpleReporter` |
|
|
116
138
|
| x | `FileReporter` |
|
|
117
139
|
|
|
@@ -131,6 +153,10 @@ A processor can be added to a logger directly (and is subsequently applied to lo
|
|
|
131
153
|
> Use `npm install @visulima/redact`, `pnpm add @visulima/redact` or `yarn add @visulima/redact` to install it.
|
|
132
154
|
- `MessageFormatterProcessor` - formats the log message (Util.format-like unescaped string formatting utility) [@visulima/fmt][fmt]
|
|
133
155
|
- `ErrorProcessor` - serializes the error with cause object to a std error object that can be serialized.
|
|
156
|
+
- `OpenTelemetryProcessor` - adds OpenTelemetry trace context to log metadata
|
|
157
|
+
> The OpenTelemetry processor needs the "@opentelemetry/api" package to work.
|
|
158
|
+
> Use `npm install @opentelemetry/api`, `pnpm add @opentelemetry/api` or `yarn add @opentelemetry/api` to install it.
|
|
159
|
+
> Extracts trace ID, span ID, and trace flags from the active OpenTelemetry span and adds them to the log context for distributed tracing correlation.
|
|
134
160
|
|
|
135
161
|
## Usage
|
|
136
162
|
|
|
@@ -249,6 +275,66 @@ foo();
|
|
|
249
275
|
|
|
250
276
|

|
|
251
277
|
|
|
278
|
+
## Child Loggers
|
|
279
|
+
|
|
280
|
+
Create child loggers that inherit settings from their parent while overriding only what you need. Child loggers are independent instances with their own state (timers, counters, etc.), but they inherit configuration like reporters, processors, types, log levels, and throttle settings.
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
import { createPail } from "@visulima/pail";
|
|
284
|
+
import { PrettyReporter } from "@visulima/pail/reporter/pretty";
|
|
285
|
+
|
|
286
|
+
const parent = createPail({
|
|
287
|
+
logLevel: "informational",
|
|
288
|
+
types: {
|
|
289
|
+
http: {
|
|
290
|
+
label: "HTTP",
|
|
291
|
+
logLevel: "informational",
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
reporters: [new PrettyReporter()],
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
// Child inherits all parent settings
|
|
298
|
+
const child = parent.child();
|
|
299
|
+
child.http("GET /api/users 200"); // Inherits http type from parent
|
|
300
|
+
child.info("Request processed"); // Inherits log level from parent
|
|
301
|
+
|
|
302
|
+
// Child can override specific settings
|
|
303
|
+
const debugChild = parent.child({ logLevel: "debug" });
|
|
304
|
+
debugChild.debug("Detailed debug info"); // Uses debug level
|
|
305
|
+
|
|
306
|
+
// Child can add new types
|
|
307
|
+
const dbChild = parent.child({
|
|
308
|
+
types: {
|
|
309
|
+
db: {
|
|
310
|
+
label: "DB",
|
|
311
|
+
logLevel: "informational",
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
dbChild.db("Query executed"); // New type available
|
|
316
|
+
dbChild.http("GET /api 200"); // Still has parent types
|
|
317
|
+
|
|
318
|
+
// Child scope extends parent scope
|
|
319
|
+
const scopedChild = parent.child({ scope: ["api"] });
|
|
320
|
+
// Logs will include both parent and child scope if parent had scope set
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### What Gets Inherited
|
|
324
|
+
|
|
325
|
+
- **Types**: Child types are merged with parent types (child can add new or override existing)
|
|
326
|
+
- **Reporters**: Child reporters are added to parent reporters (both are used)
|
|
327
|
+
- **Processors**: Child processors are added to parent processors (both are applied)
|
|
328
|
+
- **Log Levels**: Child log levels override parent log levels
|
|
329
|
+
- **Scope**: Child scope extends parent scope (combined into array)
|
|
330
|
+
- **Throttle Settings**: Child inherits parent throttle settings unless overridden
|
|
331
|
+
- **Timer Messages**: Child inherits parent timer messages unless overridden
|
|
332
|
+
|
|
333
|
+
### What's Independent
|
|
334
|
+
|
|
335
|
+
- **State**: Each child logger has its own timers, counters, and message queue
|
|
336
|
+
- **Disabled/Paused**: Child logger state is independent from parent
|
|
337
|
+
|
|
252
338
|
## Interactive Loggers (Only on if stdout and stderr is a TTY)
|
|
253
339
|
|
|
254
340
|
To initialize an interactive logger, create a new pail instance with the `interactive` attribute set to `true`.
|
|
@@ -485,6 +571,498 @@ bar.update(50, { speed: "2.5" });
|
|
|
485
571
|
bar.stop();
|
|
486
572
|
```
|
|
487
573
|
|
|
574
|
+
### Gradient Character Arrays
|
|
575
|
+
|
|
576
|
+
Create smooth gradient animations by providing arrays of characters that progressively fill:
|
|
577
|
+
|
|
578
|
+
```typescript
|
|
579
|
+
import { createPail } from "@visulima/pail";
|
|
580
|
+
|
|
581
|
+
const logger = createPail({ interactive: true });
|
|
582
|
+
|
|
583
|
+
// Shade gradient (light to dark)
|
|
584
|
+
const bar = logger.createProgressBar({
|
|
585
|
+
total: 100,
|
|
586
|
+
barCompleteChar: ["░", "▒", "▓", "█"], // Gradient array
|
|
587
|
+
barIncompleteChar: " ",
|
|
588
|
+
format: "Downloading [{bar}] {percentage}%",
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
bar.start();
|
|
592
|
+
for (let i = 0; i <= 100; i++) {
|
|
593
|
+
bar.update(i);
|
|
594
|
+
}
|
|
595
|
+
bar.stop();
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
Supported gradients:
|
|
599
|
+
|
|
600
|
+
- **Shades**: `["░", "▒", "▓", "█"]` (light to dark)
|
|
601
|
+
- **Blocks**: `["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"]` (small to large)
|
|
602
|
+
- **Temperature**: `["🔵", "🟢", "🟡", "🟠", "🔴"]` (cold to hot)
|
|
603
|
+
- **Custom**: Any array of characters for your own gradient effect
|
|
604
|
+
|
|
605
|
+
### Composite Progress Bars
|
|
606
|
+
|
|
607
|
+
Track multiple related progress bars simultaneously with automatic color layering based on progress percentage. This is perfect for operations with multiple parallel sources or stages:
|
|
608
|
+
|
|
609
|
+
```typescript
|
|
610
|
+
import { createPail } from "@visulima/pail";
|
|
611
|
+
import colorize from "@visulima/colorize";
|
|
612
|
+
|
|
613
|
+
const logger = createPail({ interactive: true });
|
|
614
|
+
|
|
615
|
+
// Create a composite multi-bar that displays all bars as a single layered composite
|
|
616
|
+
const multiBar = logger.createMultiProgressBar({
|
|
617
|
+
composite: true, // Enable composite mode
|
|
618
|
+
format: "[{bar}] ① {r}% ② {y}% ③ {b}%",
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
const source1 = multiBar.create(100, 0, { r: "0", y: "0", b: "0" });
|
|
622
|
+
const source2 = multiBar.create(100, 0, { r: "0", y: "0", b: "0" });
|
|
623
|
+
const source3 = multiBar.create(100, 0, { r: "0", y: "0", b: "0" });
|
|
624
|
+
|
|
625
|
+
// Apply colors to each source
|
|
626
|
+
multiBar.setBarColor(source1, colorize.red);
|
|
627
|
+
multiBar.setBarColor(source2, colorize.yellow);
|
|
628
|
+
multiBar.setBarColor(source3, colorize.blue);
|
|
629
|
+
|
|
630
|
+
// Update sources with different speeds
|
|
631
|
+
for (let i = 0; i <= 100; i++) {
|
|
632
|
+
source1.update(i); // Red: 100% progress
|
|
633
|
+
source2.update(Math.floor(i * 0.7)); // Yellow: 70% progress
|
|
634
|
+
source3.update(Math.floor(i * 0.4)); // Blue: 40% progress
|
|
635
|
+
await new Promise((r) => setTimeout(r, 40));
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
multiBar.stop();
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
**Output example:**
|
|
642
|
+
|
|
643
|
+
```txt
|
|
644
|
+
[▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓██████████████████████] ① 100% ② 70% ③ 40%
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
#### Color Layering & Character Shading
|
|
648
|
+
|
|
649
|
+
The composite bar uses character shading to represent overlapping progress bars:
|
|
650
|
+
|
|
651
|
+
| Character | Meaning | Example |
|
|
652
|
+
| ---------------- | ------------------ | ------------------- |
|
|
653
|
+
| **█** (solid) | One bar visible | Red at 100% |
|
|
654
|
+
| **▓** (medium) | Two bars overlap | Red + Yellow |
|
|
655
|
+
| **▒** (light) | Three bars overlap | Red + Yellow + Blue |
|
|
656
|
+
| **░** (lightest) | Four+ bars overlap | All bars present |
|
|
657
|
+
|
|
658
|
+
#### How It Works
|
|
659
|
+
|
|
660
|
+
The composite bar renders based on **progress percentage at each position**:
|
|
661
|
+
|
|
662
|
+
1. **At position 0-40%**: Only Red (①) is filled → shows **█** with red color
|
|
663
|
+
2. **At position 40-70%**: Red + Yellow (①+②) filled → shows **▓** with yellow color (highest index shown)
|
|
664
|
+
3. **At position 70-100%**: All three (①+②+③) filled → shows **▒** with blue color (highest index)
|
|
665
|
+
|
|
666
|
+
The **highest-indexed bar's color** is used at each position, creating a natural progression.
|
|
667
|
+
|
|
668
|
+
**Dynamic Visibility Based on Progress:**
|
|
669
|
+
|
|
670
|
+
The bar with the **smallest progress percentage** is shown on top for maximum visibility:
|
|
671
|
+
|
|
672
|
+
- Red at 30%, Yellow at 60% → Red is visible first (30% < 60%)
|
|
673
|
+
- When Red reaches 30% and Yellow is only at 20% → Yellow becomes visible (20% < 30%)
|
|
674
|
+
- This ensures slower-progressing operations are always visible and not hidden beneath faster ones
|
|
675
|
+
|
|
676
|
+
#### Use Cases
|
|
677
|
+
|
|
678
|
+
1. **Multi-source Download**: Show progress from multiple download sources
|
|
679
|
+
2. **Parallel Tasks**: Monitor concurrent operations (build, test, lint)
|
|
680
|
+
3. **Pipeline Stages**: Upload → Process → Finalize with different completion times
|
|
681
|
+
4. **Batch Operations**: Parse → Validate → Compile at different speeds
|
|
682
|
+
5. **Resource Tracking**: CPU, Memory, Disk usage in parallel
|
|
683
|
+
|
|
684
|
+
Each progress bar:
|
|
685
|
+
|
|
686
|
+
- Updates independently at its own speed
|
|
687
|
+
- Can have different total values and progress rates
|
|
688
|
+
- Uses color to distinguish sources
|
|
689
|
+
- Shows through character shading when overlapping
|
|
690
|
+
|
|
691
|
+
## Spinners (Server Only)
|
|
692
|
+
|
|
693
|
+
Pail includes a comprehensive spinner system inspired by cli-progress, with support for single and multi-spinner modes, various styles, and interactive terminal output.
|
|
694
|
+
|
|
695
|
+
### Single Spinner
|
|
696
|
+
|
|
697
|
+
```typescript
|
|
698
|
+
import { createPail } from "@visulima/pail";
|
|
699
|
+
|
|
700
|
+
const logger = createPail({ interactive: true });
|
|
701
|
+
const spinner = logger.createSpinner({
|
|
702
|
+
text: "Loading...",
|
|
703
|
+
color: "blue",
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
spinner.start();
|
|
707
|
+
spinner.succeed("Loaded!");
|
|
708
|
+
spinner.fail("Failed!");
|
|
709
|
+
spinner.stop();
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
### Multi Spinner
|
|
713
|
+
|
|
714
|
+
```typescript
|
|
715
|
+
import { createPail } from "@visulima/pail";
|
|
716
|
+
|
|
717
|
+
const logger = createPail({ interactive: true });
|
|
718
|
+
const multiSpinner = logger.createMultiSpinner({
|
|
719
|
+
style: "dots", // Apply style to all spinners
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
const spinner1 = multiSpinner.create("Loading A");
|
|
723
|
+
const spinner2 = multiSpinner.create("Loading B");
|
|
724
|
+
const spinner3 = multiSpinner.create("Loading C");
|
|
725
|
+
|
|
726
|
+
// Update spinners as needed
|
|
727
|
+
spinner1.succeed("Loaded A!");
|
|
728
|
+
spinner2.succeed("Loaded B!");
|
|
729
|
+
spinner3.succeed("Loaded C!");
|
|
730
|
+
|
|
731
|
+
// Clean up when done
|
|
732
|
+
multiSpinner.stop();
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
### Custom Spinner
|
|
736
|
+
|
|
737
|
+
Create fully customized spinners with your own characters and formatting:
|
|
738
|
+
|
|
739
|
+
```typescript
|
|
740
|
+
import { createPail } from "@visulima/pail";
|
|
741
|
+
|
|
742
|
+
const logger = createPail({ interactive: true });
|
|
743
|
+
const spinner = logger.createSpinner({
|
|
744
|
+
text: "🚀 Downloading {filename}: [{bar}] {percentage}% | Speed: {speed} MB/s | ETA: {eta}s",
|
|
745
|
+
barCompleteChar: "🚀",
|
|
746
|
+
barIncompleteChar: "⚪",
|
|
747
|
+
width: 20,
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
spinner.start(0, 0, {
|
|
751
|
+
filename: "large-file.zip",
|
|
752
|
+
speed: "0.0",
|
|
753
|
+
});
|
|
754
|
+
|
|
755
|
+
// Update with payload data
|
|
756
|
+
spinner.update(50, { speed: "2.5" });
|
|
757
|
+
spinner.succeed();
|
|
758
|
+
```
|
|
759
|
+
|
|
760
|
+
## Object Tree
|
|
761
|
+
|
|
762
|
+
Render objects and data structures as formatted ASCII trees for better terminal visualization and debugging:
|
|
763
|
+
|
|
764
|
+
```typescript
|
|
765
|
+
import { renderObjectTree } from "@visulima/pail/object-tree";
|
|
766
|
+
|
|
767
|
+
const data = {
|
|
768
|
+
user: {
|
|
769
|
+
name: "John Doe",
|
|
770
|
+
email: "john@example.com",
|
|
771
|
+
profile: {
|
|
772
|
+
age: 30,
|
|
773
|
+
location: "New York",
|
|
774
|
+
skills: ["JavaScript", "TypeScript", "Node.js"],
|
|
775
|
+
},
|
|
776
|
+
},
|
|
777
|
+
settings: {
|
|
778
|
+
theme: "dark",
|
|
779
|
+
notifications: true,
|
|
780
|
+
},
|
|
781
|
+
};
|
|
782
|
+
|
|
783
|
+
console.log(renderObjectTree(data));
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
**Output:**
|
|
787
|
+
|
|
788
|
+
```txt
|
|
789
|
+
├─ user:
|
|
790
|
+
│ ├─ name: John Doe
|
|
791
|
+
│ ├─ email: john@example.com
|
|
792
|
+
│ └─ profile:
|
|
793
|
+
│ ├─ age: 30
|
|
794
|
+
│ ├─ location: New York
|
|
795
|
+
│ └─ skills:
|
|
796
|
+
├─ settings:
|
|
797
|
+
│ ├─ theme: dark
|
|
798
|
+
│ └─ notifications: true
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
> **Note:** `renderObjectTree` is exported as a separate module (`@visulima/pail/object-tree`) to reduce main bundle size. It works in both **Node.js** and **Browser** environments and is a pure utility function with no platform-specific dependencies.
|
|
802
|
+
|
|
803
|
+
### Custom Rendering
|
|
804
|
+
|
|
805
|
+
Customize how object trees are rendered:
|
|
806
|
+
|
|
807
|
+
```typescript
|
|
808
|
+
import { renderObjectTree } from "@visulima/pail/object-tree";
|
|
809
|
+
|
|
810
|
+
const obj = {
|
|
811
|
+
name: "John",
|
|
812
|
+
age: 30,
|
|
813
|
+
address: {
|
|
814
|
+
street: "Main St",
|
|
815
|
+
city: "New York",
|
|
816
|
+
},
|
|
817
|
+
};
|
|
818
|
+
|
|
819
|
+
// Custom rendering with sorting and formatting
|
|
820
|
+
const tree = renderObjectTree(obj, {
|
|
821
|
+
sortFn: (a, b) => a.localeCompare(b), // Sort keys alphabetically
|
|
822
|
+
renderFn: (node) => {
|
|
823
|
+
if (typeof node === "string") return node.toUpperCase();
|
|
824
|
+
return ["boolean", "string", "number"].includes(typeof node) ? String(node) : undefined;
|
|
825
|
+
},
|
|
826
|
+
joined: true, // Return as single string (false for array of lines)
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
console.log(tree);
|
|
830
|
+
```
|
|
831
|
+
|
|
832
|
+
### Configuration Options
|
|
833
|
+
|
|
834
|
+
| Option | Description | Default |
|
|
835
|
+
| ------------------- | ----------------------------------------------------- | -------------------- |
|
|
836
|
+
| `joined` | Return as single string or array of lines | `true` |
|
|
837
|
+
| `sortFn` | Function to sort object keys (null for natural order) | `null` |
|
|
838
|
+
| `renderFn` | Function to render node values | Renders primitives |
|
|
839
|
+
| `separator` | Separator between key and value | `": "` |
|
|
840
|
+
| `keyNeighbour` | Connector for keys with siblings | `"├─ "` |
|
|
841
|
+
| `keyNoNeighbour` | Connector for last keys | `"└─ "` |
|
|
842
|
+
| `spacerNeighbour` | Spacer for branches with siblings | `"│ "` |
|
|
843
|
+
| `spacerNoNeighbour` | Spacer for branches without siblings | `" "` |
|
|
844
|
+
| `breakCircularWith` | Text for circular references | `" (circular ref.)"` |
|
|
845
|
+
|
|
846
|
+
### Use Cases
|
|
847
|
+
|
|
848
|
+
1. **Debugging**: Visualize complex object structures during development
|
|
849
|
+
2. **Logging**: Pretty-print objects for better readability in logs
|
|
850
|
+
3. **API Responses**: Display JSON API responses in tree format
|
|
851
|
+
4. **Configuration Display**: Show configuration trees in terminal applications
|
|
852
|
+
5. **Data Inspection**: Inspect deeply nested data structures
|
|
853
|
+
|
|
854
|
+
## HTTP Reporter
|
|
855
|
+
|
|
856
|
+
The HTTP Reporter sends logs to HTTP endpoints, making it perfect for centralized logging services, log aggregation platforms, or custom logging APIs. It supports batching, compression, retries, rate limiting, and Edge Runtime compatibility.
|
|
857
|
+
|
|
858
|
+
**Note:** When used in Edge Runtime environments (Next.js Edge, Cloudflare Workers, etc.), the package automatically resolves to an Edge-compatible version that disables compression and uses Edge-compatible APIs. You don't need to import a separate class - just use `HttpReporter` and the package handles the routing.
|
|
859
|
+
|
|
860
|
+
### Basic Usage
|
|
861
|
+
|
|
862
|
+
```typescript
|
|
863
|
+
import { createPail } from "@visulima/pail";
|
|
864
|
+
import { HttpReporter } from "@visulima/pail/reporter/http";
|
|
865
|
+
|
|
866
|
+
const logger = createPail({
|
|
867
|
+
reporters: [
|
|
868
|
+
new HttpReporter({
|
|
869
|
+
url: "https://api.example.com/logs",
|
|
870
|
+
method: "POST",
|
|
871
|
+
headers: {
|
|
872
|
+
Authorization: "Bearer your-token",
|
|
873
|
+
},
|
|
874
|
+
}),
|
|
875
|
+
],
|
|
876
|
+
});
|
|
877
|
+
|
|
878
|
+
logger.info("Application started", { version: "1.0.0" });
|
|
879
|
+
```
|
|
880
|
+
|
|
881
|
+
### Edge Runtime Compatibility
|
|
882
|
+
|
|
883
|
+
For Edge Runtime environments, enable Edge compatibility mode explicitly (or let the package auto-detect):
|
|
884
|
+
|
|
885
|
+
```typescript
|
|
886
|
+
import { createPail } from "@visulima/pail";
|
|
887
|
+
import { HttpReporter } from "@visulima/pail/reporter/http";
|
|
888
|
+
|
|
889
|
+
const logger = createPail({
|
|
890
|
+
reporters: [
|
|
891
|
+
new HttpReporter({
|
|
892
|
+
url: "https://api.example.com/logs",
|
|
893
|
+
edgeCompat: true, // Enable Edge compatibility (auto-enabled in Edge environments)
|
|
894
|
+
headers: {
|
|
895
|
+
Authorization: "Bearer your-token",
|
|
896
|
+
},
|
|
897
|
+
}),
|
|
898
|
+
],
|
|
899
|
+
});
|
|
900
|
+
|
|
901
|
+
logger.info("Edge function executed");
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
When `edgeCompat` is enabled (or automatically enabled in Edge environments), compression is disabled and Edge-compatible APIs are used.
|
|
905
|
+
|
|
906
|
+
### Batching
|
|
907
|
+
|
|
908
|
+
HTTP Reporter supports automatic batching to reduce network requests:
|
|
909
|
+
|
|
910
|
+
```typescript
|
|
911
|
+
const logger = createPail({
|
|
912
|
+
reporters: [
|
|
913
|
+
new HttpReporter({
|
|
914
|
+
url: "https://api.example.com/logs",
|
|
915
|
+
enableBatchSend: true,
|
|
916
|
+
batchSize: 100, // Send when 100 logs are queued
|
|
917
|
+
batchSendTimeout: 5000, // Or send after 5 seconds
|
|
918
|
+
batchMode: "delimiter", // Options: "delimiter", "array", "field"
|
|
919
|
+
batchSendDelimiter: "\n", // Delimiter for batch entries
|
|
920
|
+
}),
|
|
921
|
+
],
|
|
922
|
+
});
|
|
923
|
+
```
|
|
924
|
+
|
|
925
|
+
**Batch Modes:**
|
|
926
|
+
|
|
927
|
+
- `delimiter` (default): Join entries with a delimiter (e.g., newline-delimited JSON)
|
|
928
|
+
- `array`: Send entries as a JSON array
|
|
929
|
+
- `field`: Wrap entries in an object with a field name (requires `batchFieldName`)
|
|
930
|
+
|
|
931
|
+
```typescript
|
|
932
|
+
// Field mode example (e.g., for Logflare)
|
|
933
|
+
new HttpReporter({
|
|
934
|
+
url: "https://api.logflare.app/logs",
|
|
935
|
+
batchMode: "field",
|
|
936
|
+
batchFieldName: "batch", // Wraps entries in { batch: [...] }
|
|
937
|
+
});
|
|
938
|
+
```
|
|
939
|
+
|
|
940
|
+
### Compression
|
|
941
|
+
|
|
942
|
+
Enable gzip compression to reduce payload size:
|
|
943
|
+
|
|
944
|
+
```typescript
|
|
945
|
+
const logger = createPail({
|
|
946
|
+
reporters: [
|
|
947
|
+
new HttpReporter({
|
|
948
|
+
url: "https://api.example.com/logs",
|
|
949
|
+
compression: true, // Enable gzip compression
|
|
950
|
+
}),
|
|
951
|
+
],
|
|
952
|
+
});
|
|
953
|
+
```
|
|
954
|
+
|
|
955
|
+
**Note:** Compression is automatically disabled when Edge compatibility mode is enabled (either via `edgeCompat: true` or automatically in Edge Runtime environments).
|
|
956
|
+
|
|
957
|
+
### Retry Logic
|
|
958
|
+
|
|
959
|
+
Configure retry behavior for failed requests:
|
|
960
|
+
|
|
961
|
+
```typescript
|
|
962
|
+
const logger = createPail({
|
|
963
|
+
reporters: [
|
|
964
|
+
new HttpReporter({
|
|
965
|
+
url: "https://api.example.com/logs",
|
|
966
|
+
maxRetries: 3, // Retry up to 3 times
|
|
967
|
+
retryDelay: 1000, // Base delay: 1 second
|
|
968
|
+
respectRateLimit: true, // Wait on 429 responses
|
|
969
|
+
}),
|
|
970
|
+
],
|
|
971
|
+
});
|
|
972
|
+
```
|
|
973
|
+
|
|
974
|
+
### Error Handling
|
|
975
|
+
|
|
976
|
+
Handle errors with custom callbacks:
|
|
977
|
+
|
|
978
|
+
```typescript
|
|
979
|
+
const logger = createPail({
|
|
980
|
+
reporters: [
|
|
981
|
+
new HttpReporter({
|
|
982
|
+
url: "https://api.example.com/logs",
|
|
983
|
+
onError: (error) => {
|
|
984
|
+
console.error("Failed to send log:", error);
|
|
985
|
+
// Send to fallback service, alert, etc.
|
|
986
|
+
},
|
|
987
|
+
onDebug: (entry) => {
|
|
988
|
+
console.log("Sending log entry:", entry);
|
|
989
|
+
},
|
|
990
|
+
onDebugRequestResponse: ({ req, res }) => {
|
|
991
|
+
console.log("Request:", req.method, req.url);
|
|
992
|
+
console.log("Response:", res.status, res.statusText);
|
|
993
|
+
},
|
|
994
|
+
}),
|
|
995
|
+
],
|
|
996
|
+
});
|
|
997
|
+
```
|
|
998
|
+
|
|
999
|
+
### Payload Customization
|
|
1000
|
+
|
|
1001
|
+
Customize the payload format with a template function:
|
|
1002
|
+
|
|
1003
|
+
```typescript
|
|
1004
|
+
const logger = createPail({
|
|
1005
|
+
reporters: [
|
|
1006
|
+
new HttpReporter({
|
|
1007
|
+
url: "https://api.example.com/logs",
|
|
1008
|
+
payloadTemplate: ({ data, logLevel, message }) => {
|
|
1009
|
+
return JSON.stringify({
|
|
1010
|
+
timestamp: new Date().toISOString(),
|
|
1011
|
+
level: logLevel,
|
|
1012
|
+
message,
|
|
1013
|
+
metadata: data,
|
|
1014
|
+
});
|
|
1015
|
+
},
|
|
1016
|
+
}),
|
|
1017
|
+
],
|
|
1018
|
+
});
|
|
1019
|
+
```
|
|
1020
|
+
|
|
1021
|
+
### Size Limits
|
|
1022
|
+
|
|
1023
|
+
Configure maximum sizes to prevent oversized payloads:
|
|
1024
|
+
|
|
1025
|
+
```typescript
|
|
1026
|
+
const logger = createPail({
|
|
1027
|
+
reporters: [
|
|
1028
|
+
new HttpReporter({
|
|
1029
|
+
url: "https://api.example.com/logs",
|
|
1030
|
+
maxLogSize: 1_048_576, // 1MB per log entry
|
|
1031
|
+
maxPayloadSize: 5_242_880, // 5MB per batch payload
|
|
1032
|
+
}),
|
|
1033
|
+
],
|
|
1034
|
+
});
|
|
1035
|
+
```
|
|
1036
|
+
|
|
1037
|
+
When a log entry exceeds `maxLogSize`, a `LogSizeError` is thrown. When a batch exceeds `maxPayloadSize`, it's automatically split into smaller batches.
|
|
1038
|
+
|
|
1039
|
+
### Configuration Options
|
|
1040
|
+
|
|
1041
|
+
| Option | Type | Default | Description |
|
|
1042
|
+
| ------------------------ | ------------------------------------------ | -------------------- | --------------------------------------------- |
|
|
1043
|
+
| `url` | `string` | **required** | HTTP endpoint URL |
|
|
1044
|
+
| `method` | `string` | `"POST"` | HTTP method |
|
|
1045
|
+
| `headers` | `Record<string, string> \| (() => ...)` | `{}` | Request headers (object or function) |
|
|
1046
|
+
| `contentType` | `string` | `"application/json"` | Content type for single requests |
|
|
1047
|
+
| `batchContentType` | `string` | `"application/json"` | Content type for batch requests |
|
|
1048
|
+
| `enableBatchSend` | `boolean` | `true` | Enable automatic batching |
|
|
1049
|
+
| `batchSize` | `number` | `100` | Number of logs to batch before sending |
|
|
1050
|
+
| `batchSendTimeout` | `number` | `5000` | Timeout (ms) to send batch regardless of size |
|
|
1051
|
+
| `batchMode` | `"delimiter" \| "array" \| "field"` | `"delimiter"` | Batch format mode |
|
|
1052
|
+
| `batchSendDelimiter` | `string` | `"\n"` | Delimiter for batch entries |
|
|
1053
|
+
| `batchFieldName` | `string` | `undefined` | Field name for "field" batch mode |
|
|
1054
|
+
| `compression` | `boolean` | `false` | Enable gzip compression |
|
|
1055
|
+
| `maxRetries` | `number` | `3` | Maximum retry attempts |
|
|
1056
|
+
| `retryDelay` | `number` | `1000` | Base delay between retries (ms) |
|
|
1057
|
+
| `respectRateLimit` | `boolean` | `true` | Wait on 429 rate limit responses |
|
|
1058
|
+
| `maxLogSize` | `number` | `1048576` | Maximum size per log entry (bytes) |
|
|
1059
|
+
| `maxPayloadSize` | `number` | `5242880` | Maximum size per payload (bytes) |
|
|
1060
|
+
| `edgeCompat` | `boolean` | `false` | Enable Edge Runtime compatibility |
|
|
1061
|
+
| `onError` | `(error: Error) => void` | `undefined` | Error callback |
|
|
1062
|
+
| `onDebug` | `(entry: Record<string, unknown>) => void` | `undefined` | Debug callback for log entries |
|
|
1063
|
+
| `onDebugRequestResponse` | `(reqRes: {...}) => void` | `undefined` | Debug callback for HTTP requests/responses |
|
|
1064
|
+
| `payloadTemplate` | `(data: {...}) => string` | `undefined` | Custom payload formatter |
|
|
1065
|
+
|
|
488
1066
|
## Integrations
|
|
489
1067
|
|
|
490
1068
|
### Use with @visulima/boxen
|
package/dist/index.browser.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { P as PailBrowserType } from './packem_shared/pail.browser-
|
|
2
|
-
import { C as ConstructorOptions } from './packem_shared/types-
|
|
3
|
-
export { a as DefaultLogTypes, D as DefaultLoggerTypes, E as ExtendedRfc5424LogLevels, L as LoggerConfiguration, b as LoggerFunction, c as LoggerTypesAwareReporter, d as LoggerTypesConfig, P as Processor, R as Reporter, S as StreamAwareReporter } from './packem_shared/types-
|
|
1
|
+
import { P as PailBrowserType } from './packem_shared/pail.browser-By9KjOH7.js';
|
|
2
|
+
import { C as ConstructorOptions } from './packem_shared/types-D3ycu8-x.js';
|
|
3
|
+
export { a as DefaultLogTypes, D as DefaultLoggerTypes, E as ExtendedRfc5424LogLevels, L as LoggerConfiguration, b as LoggerFunction, c as LoggerTypesAwareReporter, d as LoggerTypesConfig, P as Processor, R as Reporter, S as StreamAwareReporter } from './packem_shared/types-D3ycu8-x.js';
|
|
4
4
|
import './packem_shared/index.d-oxZvg_y7.js';
|
|
5
5
|
import 'type-fest';
|
|
6
6
|
import '@visulima/colorize';
|
|
7
|
+
import './interactive/index.js';
|
|
7
8
|
|
|
8
9
|
declare const createPail: <T extends string = string, L extends string = string>(options?: ConstructorOptions<T, L>) => PailBrowserType<T, L>;
|
|
9
10
|
declare const pail: PailBrowserType<string, string>;
|
package/dist/index.browser.js
CHANGED
|
@@ -1 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
import { P as PailBrowser } from './packem_shared/pail.browser-CPDOE_d1.js';
|
|
2
|
+
import MessageFormatterProcessor from './processor/message-formatter-processor.js';
|
|
3
|
+
import JsonReporter from './packem_shared/JsonReporter-DcM2LBX9.js';
|
|
4
|
+
|
|
5
|
+
const createPail = (options) => new PailBrowser({
|
|
6
|
+
processors: [new MessageFormatterProcessor()],
|
|
7
|
+
reporters: [new JsonReporter()],
|
|
8
|
+
...options
|
|
9
|
+
});
|
|
10
|
+
const pail = createPail();
|
|
11
|
+
|
|
12
|
+
export { createPail, pail };
|