Render errors beautifully โ colorized stack traces, syntax-highlighted source code, and YAML metadata display for TypeScript.
Transforms noisy stack traces into readable, source-mapped, context-rich error output for Node.js and browsers.
๐ Need structured error handling? Context-preserving, chainable, serializable errors with metadata embedding, namespace categorization, and tag inheritance are available in the companion package xception.
# npm
npm install sher.log xception
# pnpm
pnpm add sher.log xception
# yarn
yarn add sher.log xceptionimport { Xception } from 'xception';
import { renderError } from 'sher.log';
const error = new Xception('something went wrong', {
namespace: 'myapp',
tags: ['critical'],
meta: { requestId: 'abc-123' },
cause: new Error('Connection timeout'),
});
console.error(await renderError(error, { showSource: true }));Terminal output:
[Xception] something went wrong
myapp critical
METADATA
requestId: abc-123
at main (/app/server.ts:42:11)
40 | async function main() {
41 | try {
> 42 | throw new Xception('something went wrong', { ... });
43 | } catch (error) {
44 | // Error handling
45 | }
... further lines matching cause stack trace below ...
[Error] Connection timeout
at main (/app/server.ts:42:45)
Standard error output in JavaScript is noisy and unhelpful:
- Noisy stacks: Dozens of irrelevant
node:internalandnode_modulesframes drown out the real issue - Lost context: Error metadata, namespaces, and tags are invisible in default console output
- Transpilation gap: Stack traces point to compiled output, not your original TypeScript source
- No metadata display: Structured context attached via xception is lost in plain
console.error
sher.log turns raw errors into readable diagnostics:
- ๐จ Colorized output: Syntax-highlighted stack traces with chalk for instant visual parsing
- ๐ Source code display: Shows the actual source lines around the error location with line numbers
- ๐บ๏ธ Source map resolution: Automatically resolves transpiled code back to original TypeScript sources
- ๐ท๏ธ Metadata rendering: Displays xception namespace, tags, and metadata in structured YAML format
- ๐ Cause chain display: Recursively renders the full error causality chain with proper indentation
- ๐ Noise filtering: Excludes
node:internalandnode_modulesframes by default
| Feature | sher.log | pretty-error | Youch |
|---|---|---|---|
| Source code display | โ | โ | โ |
| Source map resolution | โ | โ | โ |
| Syntax highlighting | โ | โ | Partial |
| Metadata rendering | โ | โ | โ |
| Cause chain display | โ | โ | Partial |
| Browser support | โ | โ | โ |
| TypeScript-first | โ | โ | Partial |
Core Benefits:
- ๐ Debug faster: See the exact source code, metadata, and cause chain โ no more guessing from raw stacks
- ๐ฏ Find root causes: Full cause chain rendering shows the complete error story from origin to surface
- ๐ก๏ธ Production-ready: Automatic source map resolution works with compiled TypeScript out of the box
- ๐ Context-aware: Renders xception namespace, tags, and metadata so nothing is lost in console output
import { renderError } from 'sher.log';
const output = await renderError(error, {
showSource: true, // display source code context
showStack: true, // show full stack trace (default)
indent: 0, // indent spacing for each line (default)
filter: (path) =>
!path.includes('node:internal') && !path.includes('node_modules'),
});
console.error(output);import { createSourceResolver } from 'sher.log';
// Create a resolver for a compiled file (reads inline source maps)
const resolve = await createSourceResolver('/path/to/compiled.js');
// Map a transpiled location back to the original source
const resolved = resolve({ line: 10, column: 5 });
// {
// identifier: 'original.ts',
// source: '... full source content ...',
// line: 3,
// column: 2
// }import { disassembleStack, assembleStack } from 'sher.log';
// Parse a stack trace into structured blocks
const blocks = disassembleStack(error.stack!);
// [
// { type: 'description', name: 'Error', message: 'fail' },
// { type: 'location', entry: 'main', location: '/app.ts', line: 42, column: 11 },
// ]
// Filter and reassemble
const filtered = blocks.filter(
(block) =>
block.type !== 'location' || !block.location.includes('node_modules'),
);
const cleanStack = assembleStack(filtered);import chalk from 'chalk';
import { highlight } from 'sher.log';
const highlighted = highlight('const x: number = 42;', {
string: chalk.green,
punctuator: chalk.grey,
keyword: chalk.cyan,
number: chalk.magenta,
regex: chalk.magenta,
comment: chalk.grey.bold,
invalid: chalk.inverse,
});
console.log(highlighted);import { renderError } from 'sher.log';
// Express error middleware
app.use(async (err: Error, req: Request, res: Response, next: NextFunction) => {
console.error(
await renderError(err, {
showSource: process.env.NODE_ENV === 'development',
showStack: true,
}),
);
res.status(500).json({ error: err.message });
});
// Koa error middleware
app.use(async (ctx, next) => {
try {
await next();
} catch (err) {
console.error(await renderError(err as Error, { showSource: true }));
ctx.status = 500;
ctx.body = { error: (err as Error).message };
}
});Render an error in a human-readable format with colorized output, source code display, and structured metadata:
function renderError(error: Error, options?: RenderOptions): Promise<string>;interface RenderOptions {
/** indent spacing for each line */
indent?: number;
/** indicate whether a source frame should be shown */
showSource?: boolean;
/** indicate whether the full stack should be shown */
showStack?: boolean;
/** a filter function determining whether a stack should be shown given the file path */
filter?: (path: string) => boolean;
}| Option | Type | Default | Description |
|---|---|---|---|
indent |
number |
0 |
Indent spacing for each line |
showSource |
boolean |
false |
Display source code context at top frame |
showStack |
boolean |
true |
Show stack trace locations |
filter |
(path: string) => boolean |
Excludes node:internal and node_modules |
Filter which stack frames to display |
Render a single stack location block with optional source code display:
function renderLocation(
block: StackLocationBlock,
options: { indent: number; showSource: boolean },
): Promise<string>;| Parameter | Type | Description |
|---|---|---|
block |
StackLocationBlock |
A parsed stack location block |
options.indent |
number |
Indent level for each line |
options.showSource |
boolean |
Whether to display the source code frame |
Create a resolver function that maps transpiled source locations back to original sources using inline source maps:
function createSourceResolver(identifier: string): Promise<SourceResolver>;type SourceResolver = (params: {
line: number;
column: number;
}) => SourceResolution;
interface SourceResolution {
/** name or path of the original source */
identifier: string;
/** content of the original source */
source?: string;
/** line number of the original source */
line: number;
/** column number of the original source */
column: number;
}| Property | Type | Description |
|---|---|---|
identifier |
string |
Path or URL of the resolved source |
source |
string | undefined |
Full content of the resolved source |
line |
number |
Line number in the original source |
column |
number |
Column number in the original source |
Parse and reassemble stack traces as structured blocks:
function disassembleStack(stack: string): StackBlock[];
function assembleStack(blocks: StackBlock[]): string;type StackBlock = StackDescriptionBlock | StackLocationBlock;
interface StackDescriptionBlock {
type: 'description';
name: string;
message: string;
}
interface StackLocationBlock {
type: 'location';
entry: string;
location: string;
line: number;
column: number;
}| Property | Type | Description |
|---|---|---|
type |
string |
'description' or 'location' |
name |
string |
Error name (description blocks only) |
message |
string |
Error message (description blocks only) |
entry |
string |
Function name at this stack frame |
location |
string |
File path or URL |
line |
number |
Line number |
column |
number |
Column number |
Syntax-highlight JavaScript/TypeScript source code for terminal output:
function highlight(source: string, theme: HighlightTheme): string;type HighlightTheme = Record<
| 'string'
| 'punctuator'
| 'keyword'
| 'number'
| 'regex'
| 'comment'
| 'invalid',
ChalkInstance
>;| Theme Key | Applies To |
|---|---|
string |
String literals, template strings |
punctuator |
Operators, brackets, semicolons |
keyword |
JS/TS keywords and literals |
number |
Numeric literals |
regex |
Regular expression literals |
comment |
Single-line and multi-line comments |
invalid |
Invalid tokens |
| Requirement | Value |
|---|---|
| Node.js | >= 18.18 |
| TypeScript | 5.x+ |
| Module format | ESM only |
| Browsers | Modern browsers |
| Dependencies | chalk, js-tokens, yaml, @jridgewell/trace-mapping, type-fest (types only) |
| Peer deps | xception >= 9.0.0 |
Browser vs Node.js differences: In browser environments, local file reading is unavailable โ createSourceResolver can only resolve web URLs and inline source maps. Node.js environments support both local file reading and web URL resolution.
| Feature | sher.log | Youch | pretty-error | Stacktracey | clean-stack |
|---|---|---|---|---|---|
| Source code display | โ | โ | โ | โ | โ |
| Source map resolution | โ | โ | โ | โ | โ |
| Syntax highlighting | โ | Partial | โ | โ | โ |
| Metadata rendering | โ | โ | โ | โ | โ |
| Cause chain display | โ | Partial | โ | โ | โ |
| Browser support | โ | โ | โ | โ | โ |
| TypeScript-first | โ | Partial | โ | Partial | โ |
| HTML output | โ | โ | โ | โ | โ |
When to choose what:
- sher.log โ When you need source-mapped, syntax-highlighted error rendering with full xception metadata support
- Youch โ When you need HTML error pages for web frameworks with browser-based rendering
- pretty-error โ When you only need basic prettier console output without source code or metadata
- clean-stack โ When you only need to clean up stack traces without any rendering
sher.log is the rendering companion to xception. Error handling and error rendering were intentionally separated to keep both packages focused and lightweight.
| Package | Description |
|---|---|
| xception | Context-aware error handling โ metadata, chaining, serialization |
| sher.log | Beautiful error rendering โ colorized stack traces, source code display, YAML metadata (this package) |
sher.log reads xception internals via exported symbols ($namespace, $tags, $cause, $meta) โ no subclassing required. It also works with plain Error objects, rendering whatever context is available.
Control exactly which frames appear in the rendered output:
const output = await renderError(error, {
showStack: true,
// Show only your application code
filter: (path) => path.includes('/src/') && !path.includes('node_modules'),
});Resolve source maps from web-hosted scripts:
import { createSourceResolver } from 'sher.log';
// Works with HTTP/HTTPS URLs
const resolve = await createSourceResolver(
'https://cdn.example.com/app.bundle.js',
);
const original = resolve({ line: 1, column: 2345 });
// Maps back to original TypeScript source via inline source mapDefine your own color scheme for syntax highlighting:
import chalk from 'chalk';
import { highlight } from 'sher.log';
const customTheme = {
string: chalk.hex('#ce9178'),
punctuator: chalk.hex('#d4d4d4'),
keyword: chalk.hex('#569cd6'),
number: chalk.hex('#b5cea8'),
regex: chalk.hex('#d16969'),
comment: chalk.hex('#6a9955'),
invalid: chalk.bgRed.white,
};
const highlighted = highlight(sourceCode, customTheme);See CONTRIBUTING.md for full guidelines.
- Fork & Clone:
git clone https://github.com/alvis/sher.log.git - Install:
pnpm install - Develop:
pnpm test:watchfor development mode - Test:
pnpm test && pnpm lint - Submit: Create a pull request
Code Style:
- Conventional Commits
- ESLint + Prettier enforced
- 100% test coverage required
Found a vulnerability? Please email alvis@hilbert.space with details. We aim to respond within 48 hours and patch as quickly as possible.
| Issue | Solution |
|---|---|
| Source code not displayed | Ensure showSource: true is set โ it defaults to false |
| Source maps not resolving | Only inline source maps (//# sourceMappingURL=data:...) are supported โ external .map files are not |
| Cannot import (CJS) | sher.log is ESM-only; use dynamic import() in CommonJS or migrate to ESM |
| TypeScript errors | Ensure TypeScript 5.x+ and "moduleResolution": "bundler" or "node16" in tsconfig |
| Browser: local files return null | createSourceResolver cannot read local files in browsers โ only web URLs and inline source maps work |
| Stack frames missing | The default filter excludes node:internal and node_modules โ provide a custom filter to include them |
Does sher.log work without xception?
Yes. sher.log works with any Error object. When used with xception, it additionally renders namespace, tags, and metadata.
Can I use sher.log in the browser? Yes โ it works in modern browsers via the browser-specific content module. Local file reading is unavailable; only web URLs and inline source maps are supported.
Why is renderError async?
Source map resolution requires reading files (or fetching URLs) to locate inline source maps, which is inherently asynchronous.
Can I render only a single stack frame?
Yes โ use renderLocation() directly with a StackLocationBlock from disassembleStack().
More help: GitHub Issues ยท Discussions
See CHANGELOG.md for version history and migration guides.
MIT ยฉ 2020-2026 Alvis HT Tang
Free for personal and commercial use. See LICENSE for details.
โญ Star on GitHub ยท ๐ฆ View on npm ยท ๐ Documentation
Built for developers who believe every error deserves to be understood.