0
# Browser Support
1
2
Browser-compatible logging with console output and transmit functionality for client-side applications. Pino provides a browser-specific build that maintains API compatibility while adapting to browser constraints.
3
4
## Capabilities
5
6
### Browser Logger
7
8
Use Pino in browser environments with console output and optional remote logging.
9
10
```typescript { .api }
11
/**
12
* Create a browser-compatible logger instance
13
* @param options - Browser-specific logger options
14
* @returns Logger instance optimized for browser use
15
*/
16
function pino(options?: BrowserLoggerOptions): Logger;
17
18
interface BrowserLoggerOptions extends LoggerOptions {
19
/** Browser-specific configuration */
20
browser?: BrowserOptions;
21
}
22
```
23
24
**Usage Examples:**
25
26
```javascript
27
// Basic browser logger
28
const logger = pino();
29
logger.info('Hello from browser'); // Outputs to console
30
31
// Browser logger with custom options
32
const logger = pino({
33
name: 'my-web-app',
34
level: 'debug',
35
browser: {
36
asObject: true,
37
transmit: {
38
level: 'error',
39
send: (level, logEvent) => {
40
// Send errors to monitoring service
41
fetch('/api/logs', {
42
method: 'POST',
43
body: JSON.stringify(logEvent)
44
});
45
}
46
}
47
}
48
});
49
```
50
51
### Browser Options
52
53
Configure browser-specific behavior and output formatting.
54
55
```typescript { .api }
56
interface BrowserOptions {
57
/** Create log objects instead of calling console methods directly */
58
asObject?: boolean;
59
60
/** Keep message formatting for deferred console rendering */
61
asObjectBindingsOnly?: boolean;
62
63
/** Custom write functions for different log levels */
64
write?: WriteFn | { [level: string]: WriteFn };
65
66
/** Enable serializers in browser (disabled by default) */
67
serialize?: boolean | string[];
68
69
/** Disable browser logging entirely */
70
disabled?: boolean;
71
72
/** Browser-specific formatters */
73
formatters?: {
74
level?: (label: string, number: number) => object;
75
log?: (object: Record<string, unknown>) => Record<string, unknown>;
76
};
77
78
/** Remote log transmission configuration */
79
transmit?: {
80
level?: string;
81
send: (level: string, logEvent: LogEvent) => void;
82
};
83
}
84
85
type WriteFn = (obj: object) => void;
86
```
87
88
## Console Output Modes
89
90
### Object Mode
91
92
Output structured log objects instead of formatted strings.
93
94
```typescript { .api }
95
interface BrowserOptions {
96
/** Create pino-like log objects instead of console calls */
97
asObject?: boolean;
98
}
99
```
100
101
**Usage Examples:**
102
103
```javascript
104
// Default console output
105
const logger = pino();
106
logger.info('Hello world'); // → console.info('Hello world')
107
108
// Object mode
109
const logger = pino({
110
browser: {
111
asObject: true
112
}
113
});
114
logger.info('Hello world');
115
// → console.info({msg: 'Hello world', level: 30, time: 1531171074631})
116
117
// With additional data
118
logger.info({ userId: 123 }, 'User action');
119
// → console.info({msg: 'User action', level: 30, time: 1531171074631, userId: 123})
120
```
121
122
### Deferred Formatting Mode
123
124
Preserve message formatting for better browser devtools rendering.
125
126
```typescript { .api }
127
interface BrowserOptions {
128
/** Keep messages unformatted for better browser devtools display */
129
asObjectBindingsOnly?: boolean;
130
}
131
```
132
133
**Usage Examples:**
134
135
```javascript
136
const logger = pino({
137
browser: {
138
asObjectBindingsOnly: true
139
}
140
});
141
142
logger.info('hello %s', 'world');
143
// → console.info({level: 30, time: 1531171074631}, 'hello %s', 'world')
144
// Browser devtools will format 'hello world' with proper styling
145
```
146
147
## Custom Write Functions
148
149
### Level-Specific Writers
150
151
Override default console methods with custom write functions.
152
153
```typescript { .api }
154
interface BrowserOptions {
155
/** Custom write functions */
156
write?: WriteFn | { [level: string]: WriteFn };
157
}
158
```
159
160
**Usage Examples:**
161
162
```javascript
163
// Single write function for all levels
164
const logger = pino({
165
browser: {
166
write: (obj) => {
167
// Custom logging implementation
168
const logEntry = {
169
timestamp: new Date().toISOString(),
170
level: obj.level,
171
message: obj.msg,
172
data: obj
173
};
174
175
// Send to custom logging service
176
window.customLogger.log(logEntry);
177
}
178
}
179
});
180
181
// Level-specific write functions
182
const logger = pino({
183
browser: {
184
write: {
185
info: (obj) => {
186
console.log('INFO:', obj);
187
},
188
error: (obj) => {
189
console.error('ERROR:', obj);
190
// Also send errors to error tracking
191
window.errorTracker.captureError(obj);
192
},
193
debug: (obj) => {
194
// Only log debug in development
195
if (process.env.NODE_ENV === 'development') {
196
console.debug('DEBUG:', obj);
197
}
198
}
199
}
200
}
201
});
202
```
203
204
## Serialization in Browser
205
206
### Serializer Control
207
208
Control which serializers are active in browser environments.
209
210
```typescript { .api }
211
interface BrowserOptions {
212
/** Enable serializers (disabled by default in browser) */
213
serialize?: boolean | string[];
214
}
215
```
216
217
**Usage Examples:**
218
219
```javascript
220
// Enable all serializers
221
const logger = pino({
222
serializers: {
223
error: (err) => ({
224
name: err.name,
225
message: err.message,
226
stack: err.stack
227
})
228
},
229
browser: {
230
serialize: true
231
}
232
});
233
234
// Enable specific serializers
235
const logger = pino({
236
serializers: {
237
req: reqSerializer,
238
res: resSerializer,
239
err: errSerializer
240
},
241
browser: {
242
serialize: ['err'] // Only error serializer active
243
}
244
});
245
246
// Explicitly disable standard error serializer
247
const logger = pino({
248
browser: {
249
serialize: ['!stdSerializers.err', 'custom']
250
}
251
});
252
```
253
254
## Remote Log Transmission
255
256
### Transmit Configuration
257
258
Send browser logs to remote endpoints for centralized logging.
259
260
```typescript { .api }
261
interface BrowserOptions {
262
/** Remote transmission configuration */
263
transmit?: {
264
level?: string;
265
send: (level: string, logEvent: LogEvent) => void;
266
};
267
}
268
269
interface LogEvent {
270
/** Timestamp when log method was called */
271
ts: number;
272
273
/** Array of arguments passed to log method */
274
messages: any[];
275
276
/** Child logger bindings hierarchy */
277
bindings: Bindings[];
278
279
/** Log level information */
280
level: {
281
label: string;
282
value: number;
283
};
284
}
285
```
286
287
**Usage Examples:**
288
289
```javascript
290
// Basic remote logging
291
const logger = pino({
292
browser: {
293
transmit: {
294
level: 'error',
295
send: (level, logEvent) => {
296
fetch('/api/logs', {
297
method: 'POST',
298
headers: { 'Content-Type': 'application/json' },
299
body: JSON.stringify({
300
level,
301
timestamp: logEvent.ts,
302
messages: logEvent.messages,
303
bindings: logEvent.bindings
304
})
305
}).catch(console.error);
306
}
307
}
308
}
309
});
310
311
// Advanced transmission with batching
312
const logger = pino({
313
browser: {
314
transmit: {
315
level: 'warn',
316
send: (() => {
317
const logBuffer = [];
318
let sendTimer;
319
320
return (level, logEvent) => {
321
logBuffer.push({ level, logEvent, timestamp: Date.now() });
322
323
// Batch send every 5 seconds or on critical errors
324
if (level === 'fatal' || level === 'error') {
325
sendLogs();
326
} else {
327
clearTimeout(sendTimer);
328
sendTimer = setTimeout(sendLogs, 5000);
329
}
330
};
331
332
function sendLogs() {
333
if (logBuffer.length === 0) return;
334
335
fetch('/api/logs/batch', {
336
method: 'POST',
337
headers: { 'Content-Type': 'application/json' },
338
body: JSON.stringify(logBuffer.splice(0))
339
}).catch(console.error);
340
}
341
})()
342
}
343
}
344
});
345
```
346
347
## Browser-Specific Patterns
348
349
### Error Tracking Integration
350
351
Integrate with browser error tracking services.
352
353
**Usage Examples:**
354
355
```javascript
356
// Sentry integration
357
const logger = pino({
358
browser: {
359
transmit: {
360
level: 'error',
361
send: (level, logEvent) => {
362
if (typeof Sentry !== 'undefined') {
363
Sentry.captureMessage(logEvent.messages[0], level);
364
365
// Add context from bindings
366
logEvent.bindings.forEach(binding => {
367
Sentry.setContext('logger', binding);
368
});
369
}
370
}
371
}
372
}
373
});
374
375
// Google Analytics event tracking
376
const logger = pino({
377
browser: {
378
transmit: {
379
level: 'info',
380
send: (level, logEvent) => {
381
if (typeof gtag !== 'undefined' && level === 'info') {
382
// Track important events
383
if (logEvent.messages[0].includes('user_action')) {
384
gtag('event', 'user_action', {
385
event_category: 'engagement',
386
event_label: logEvent.messages[0]
387
});
388
}
389
}
390
}
391
}
392
}
393
});
394
```
395
396
### Local Storage Logging
397
398
Store logs locally for offline scenarios.
399
400
**Usage Examples:**
401
402
```javascript
403
const logger = pino({
404
browser: {
405
write: {
406
error: (obj) => {
407
// Always show errors in console
408
console.error(obj);
409
410
// Store errors locally
411
try {
412
const errors = JSON.parse(localStorage.getItem('app_errors') || '[]');
413
errors.push({
414
...obj,
415
timestamp: Date.now(),
416
url: window.location.href,
417
userAgent: navigator.userAgent
418
});
419
420
// Keep only last 50 errors
421
if (errors.length > 50) {
422
errors.splice(0, errors.length - 50);
423
}
424
425
localStorage.setItem('app_errors', JSON.stringify(errors));
426
} catch (e) {
427
console.warn('Failed to store error in localStorage:', e);
428
}
429
}
430
}
431
}
432
});
433
434
// Function to retrieve stored errors
435
function getStoredErrors() {
436
try {
437
return JSON.parse(localStorage.getItem('app_errors') || '[]');
438
} catch (e) {
439
return [];
440
}
441
}
442
443
// Function to send stored errors when online
444
function syncStoredErrors() {
445
const errors = getStoredErrors();
446
if (errors.length > 0) {
447
fetch('/api/logs/sync', {
448
method: 'POST',
449
headers: { 'Content-Type': 'application/json' },
450
body: JSON.stringify(errors)
451
}).then(() => {
452
localStorage.removeItem('app_errors');
453
}).catch(console.error);
454
}
455
}
456
```
457
458
## Development vs Production
459
460
### Environment-Specific Configuration
461
462
Configure different behaviors for development and production builds.
463
464
**Usage Examples:**
465
466
```javascript
467
function createBrowserLogger() {
468
const isDevelopment = process.env.NODE_ENV === 'development';
469
470
return pino({
471
level: isDevelopment ? 'debug' : 'warn',
472
browser: {
473
asObject: isDevelopment, // Objects in dev, formatted in prod
474
disabled: false,
475
476
transmit: isDevelopment ? undefined : {
477
level: 'error',
478
send: (level, logEvent) => {
479
// Only transmit in production
480
fetch('/api/logs', {
481
method: 'POST',
482
body: JSON.stringify(logEvent)
483
});
484
}
485
},
486
487
write: isDevelopment ? undefined : {
488
// Custom production logging
489
error: (obj) => {
490
console.error(obj);
491
// Track errors in production
492
window.analytics?.track('error', obj);
493
}
494
}
495
}
496
});
497
}
498
499
const logger = createBrowserLogger();
500
```
501
502
### Browser Formatters
503
504
Custom formatting for log levels and objects in browser environments.
505
506
```typescript { .api }
507
interface BrowserOptions {
508
/** Browser-specific formatting functions */
509
formatters?: {
510
/** Changes the shape of the log level in browser output */
511
level?: (label: string, number: number) => object;
512
/** Changes the shape of the log object in browser output */
513
log?: (object: Record<string, unknown>) => Record<string, unknown>;
514
};
515
}
516
```
517
518
**Usage Examples:**
519
520
```javascript
521
// Custom level formatting
522
const logger = pino({
523
browser: {
524
formatters: {
525
level: (label, number) => {
526
return {
527
severity: label.toUpperCase(),
528
levelNum: number,
529
timestamp: Date.now()
530
};
531
},
532
log: (object) => {
533
// Add browser-specific context
534
return {
535
...object,
536
userAgent: navigator.userAgent,
537
url: window.location.href,
538
viewport: {
539
width: window.innerWidth,
540
height: window.innerHeight
541
}
542
};
543
}
544
}
545
}
546
});
547
548
logger.info({ action: 'page_load' }, 'Page loaded');
549
// Output includes custom formatting with browser context
550
```
551
552
### Bundle Size Optimization
553
554
Minimize browser bundle size when using Pino.
555
556
**Usage Examples:**
557
558
```javascript
559
// Import only what you need
560
import pino from 'pino/browser';
561
562
// Or use a custom build
563
const logger = pino({
564
browser: {
565
// Disable features not needed in browser
566
serialize: false,
567
disabled: process.env.NODE_ENV === 'test'
568
}
569
});
570
571
// Tree-shake unused features in webpack
572
module.exports = {
573
resolve: {
574
alias: {
575
'pino': 'pino/browser'
576
}
577
}
578
};
579
```