@mpen/logger
v0.2.7
Published
Lightweight logging utilities.
Downloads
549
Readme
@mpen/logger
Lightweight logging utilities for structured JSON logs, styled browser console output, and terminal output.
Installation
bun add @mpen/loggerUsage
import { BrowserLogger, JsonLogger, LogLevel, TerminalLogger, type Logger } from '@mpen/logger'JSON Logger
import { JsonLogger, LogLevel } from '@mpen/logger'
const logger = new JsonLogger({ minLogLevel: LogLevel.INFO })
const requestLogger = logger
.withContext({ 'service.name': 'api' })
.withContext({ 'request.id': 'req.1' })
requestLogger.info('server started')
logger.warn('retrying request', { attempt: 2 })JsonLogger writes newline-delimited JSON records with OpenTelemetry-friendly timestamp
and severityText fields. Context attributes are flattened into every record, making logs
straightforward to parse from Kubernetes container output later:
{
"service.name": "api",
"request.id": "req.1",
"timestamp": "2026-05-24T20:00:00.000Z",
"severityText": "INFO",
"message": "server started"
}It converts common non-JSON values such as errors, symbols, bigint values, maps, sets, and circular references into serializable values.
Browser Logger
import { BrowserLogger } from '@mpen/logger'
const logger = new BrowserLogger()
logger.info('Loaded user', { id: 1, name: 'Ada' })
logger.table([{ id: 1, status: 'active' }])BrowserLogger writes to the browser DevTools console and formats structured values with CSS styles.
Terminal Logger
import { TerminalLogger } from '@mpen/logger'
const logger = new TerminalLogger()
logger.info('build started')
logger.table([
{ name: 'api', status: 'ok', ms: 42 },
{ name: 'worker', status: 'slow', ms: 180 },
])TerminalLogger uses Unicode-aware string width measurement to render wrapped terminal logs and tables.
Activities
Activities provide timed contextual logging without binding the logger to HTTP or tracing:
import { JsonLogger, TerminalLogger } from '@mpen/logger'
const local = new TerminalLogger()
const production = new JsonLogger().withContext({ 'service.name': 'api' })
const activity = production.startActivity('sync', {
job: 'users',
})
activity.logger.info('fetching user')
activity.end({
context: {
rows: 42,
},
})Activity name and phase are exposed to record formatters as non-serialized metadata, so structured JSON output does not leak lifecycle implementation fields by default.
Record Hooks
JsonLogger can transform records before serialization, and TerminalLogger can delegate
timestamped records to a formatter before falling back to its generic renderer:
import { JsonLogger, LogLevel, TerminalLogger, type LogRecordTransform } from '@mpen/logger'
const stripDebug: LogRecordTransform = (record) => (record.level === LogLevel.DEBUG ? null : record)
const json = new JsonLogger({ transformRecord: stripDebug })
const terminal = new TerminalLogger({
formatRecord(record, context) {
if (record.context.component !== 'api') return undefined
return `${context.icon} ${context.time} api ${context.formatData(record.data)}`
},
})Keep domain-specific formatting, such as HTTP access-log presentation, in the package that owns that domain and pass it to these hooks.
