Overview
Stepkit provides built-in logging and performance tracking:
- Structured logs for each step execution
- Stopwatch timing for performance analysis
- Performance summaries with statistics
- Custom log functions for integration with logging services
- Per-step logging control
Basic Logging
Enable logging by passing a log option:
const pipeline = stepkit<{ userId: string }>()
.step('fetch-user', async ({ userId }) => {
return { user: await getUser(userId) }
})
.step('process', ({ user }) => {
return { processed: true }
})
await pipeline.run({ userId: '42' }, { log: true })
Output:
đ Starting pipeline with input: {
userId: "42"
}
đ Step: fetch-user
â
fetch-user completed
Output: user
đ Step: process
â
process completed
Output: processed
⨠Pipeline completed successfully
Stopwatch Mode
Enable detailed performance tracking with the stopwatch option:
const pipeline = stepkit<{ userId: string }>()
.step('fetch-user', async ({ userId }) => {
await sleep(150)
return { user: { id: userId, email: '[email protected]' } }
})
.step(
'fetch-data',
async ({ user }) => {
await sleep(120)
return { orders: [{ id: 'o1' }, { id: 'o2' }] }
},
async ({ user }) => {
await sleep(80)
return { alerts: ['notice'] }
}
)
.step('process', ({ orders }) => ({ orderCount: orders.length }))
await pipeline.run({ userId: '42' }, { log: { stopwatch: true } })
Output:
đ Starting pipeline with input: {
userId: "42",
}
đ Step: fetch-user
â
fetch-user completed in 178ms
Output: user
đ Step: fetch-data
â
fetch-data completed in 121ms
Output: orders, alerts
đ Step: process
â
process completed in 0ms
Output: orderCount
âąī¸ Performance Summary:
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â â
fetch-user 178ms â
â â
fetch-data 121ms â
â â
process 0ms â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
đ Statistics:
Average: 100ms
Slowest: fetch-user (178ms)
Fastest: process (0ms)
â° Total Pipeline Time: 299ms
⨠Pipeline completed successfully
Log Configuration
Enable logging for the pipeline. Can be a boolean or a configuration object.
LogConfig Options
logFn
(message: string, ...args: unknown[]) => void
default:"console.log"
Custom function for standard log messages.
errorLogFn
(message: string, ...args: unknown[]) => void
default:"console.error"
Custom function for error messages.
stopwatch
boolean | StopwatchConfig
Enable performance tracking. Can be a boolean or a configuration object for fine-grained control.
Stopwatch Configuration
Show duration for each step as it completes.
Show the performance summary table at the end.
Show the total pipeline execution time.
Configuring Logs
At Build Time
Configure logging when creating the pipeline:
const pipeline = stepkit<{ id: string }>({
log: {
stopwatch: {
showStepDuration: true,
showSummary: true,
showTotal: true,
},
},
})
.step('step-1', () => ({ a: 1 }))
.step('step-2', () => ({ b: 2 }))
await pipeline.run({ id: '1' })
// Logging applies to all runs
At Runtime
Override logging configuration for a specific run:
const pipeline = stepkit<{ id: string }>()
.step('step-1', () => ({ a: 1 }))
.step('step-2', () => ({ b: 2 }))
// Run with logging
await pipeline.run({ id: '1' }, { log: { stopwatch: true } })
// Run without logging
await pipeline.run({ id: '2' })
Custom Log Functions
Integrate with logging services:
import { logger } from './logger'
const pipeline = stepkit<{ userId: string }>()
.step('fetch-user', async ({ userId }) => {
return { user: await getUser(userId) }
})
await pipeline.run(
{ userId: '42' },
{
log: {
logFn: (message, ...args) => {
logger.info(message, ...args)
},
errorLogFn: (message, ...args) => {
logger.error(message, ...args)
},
stopwatch: true,
},
},
)
Structured Logging
import pino from 'pino'
const logger = pino()
const pipeline = stepkit<{ requestId: string }>()
.step('process-request', async ({ requestId }) => {
return { result: 'done' }
})
await pipeline.run(
{ requestId: 'req-123' },
{
log: {
logFn: (message, ...args) => {
logger.info({ message, args }, 'Pipeline step')
},
errorLogFn: (message, ...args) => {
logger.error({ message, args }, 'Pipeline error')
},
stopwatch: true,
},
},
)
Per-Step Logging
Control logging for individual steps:
const pipeline = stepkit<{ id: string }>()
.step({ name: 'step-1', log: true }, () => ({ a: 1 }))
.step({ name: 'step-2', log: false }, () => ({ b: 2 })) // Silent
.step('step-3', () => ({ c: 3 }))
await pipeline.run({ id: '1' }, { log: true })
// Only step-1 and step-3 produce logs
Step Icons
Different step types have distinct icons:
- đ Step: Regular step execution
- đ Transform: Context transformation
- đ Branch: Conditional branching
- âī¸ Skip: Step skipped (condition or resume)
- â
Success: Step completed successfully
- â Error: Step failed
const pipeline = stepkit<{ value: number }>()
.step('regular', () => ({ a: 1 }))
.transform('cleanup', ({ value }) => ({ value }))
.branchOn(
'route',
{
when: ({ value }) => value > 0,
then: (b) => b.step('positive', () => ({ sign: 'positive' })),
},
{
default: (b) => b.step('negative', () => ({ sign: 'negative' })),
},
)
await pipeline.run({ value: 5 }, { log: { stopwatch: true } })
Output:
đ Step: regular
â
regular completed in 0ms
đ Transform: cleanup
â
cleanup completed in 0ms
đ Branch: route
âŗ Executing: positive
đ Step: route/positive/positive
â
route/positive/positive completed in 0ms
â
route completed in 1ms
The performance summary includes:
- Table of steps with status icons and durations
- Statistics:
- Average duration across successful steps
- Slowest step with duration
- Fastest step with duration
- Total pipeline time
âąī¸ Performance Summary:
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â â
fetch-user 178ms â
â â
fetch-data 121ms â
â â maybe-slow 201ms â
â â
process 0ms â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
đ Statistics:
Average: 50ms
Slowest: fetch-user (178ms)
Fastest: process (0ms)
â° Total Pipeline Time: 511ms
Error Logging
Failed steps are clearly indicated:
const pipeline = stepkit()
.step({ name: 'failing', onError: 'continue' }, () => {
throw new Error('Something went wrong')
})
.step('continue', () => ({ done: true }))
await pipeline.run({}, { log: { stopwatch: true } })
Output:
đ Step: failing
â failing failed after 2ms
Error: Error: Something went wrong
đ Step: continue
â
continue completed in 0ms
Logging with Branches
Branches show which path was taken:
const pipeline = stepkit<{ type: 'a' | 'b' }>()
.branchOn(
'route',
{
name: 'type-a',
when: ({ type }) => type === 'a',
then: (b) => b.step('handle-a', () => ({ result: 'A' })),
},
{
name: 'type-b',
when: ({ type }) => type === 'b',
then: (b) => b.step('handle-b', () => ({ result: 'B' })),
},
)
await pipeline.run({ type: 'a' }, { log: true })
Output:
đ Branch: route
âŗ Executing: type-a
đ Step: route/type-a/handle-a
â
route/type-a/handle-a completed
Disabling Specific Log Components
await pipeline.run(
{ userId: '42' },
{
log: {
stopwatch: {
showStepDuration: false, // Hide individual step durations
showSummary: true, // Show summary table
showTotal: true, // Show total time
},
},
},
)
Output:
đ Starting pipeline with input: { userId: "42" }
đ Step: fetch-user
â
fetch-user completed
đ Step: process
â
process completed
âąī¸ Performance Summary:
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â â
fetch-user 178ms â
â â
process 0ms â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â° Total Pipeline Time: 178ms
Combining with Other Features
With Checkpoints
Logs show resume information:
await pipeline.runCheckpoint(checkpoint, { log: { stopwatch: true } })
Output:
đ Resuming pipeline from checkpoint step: step-2
âī¸ Step: step-1 (resume-skip)
âī¸ Step: step-2 (resume-skip)
đ Step: step-3
â
step-3 completed in 5ms
âąī¸ Performance Summary:
âĒī¸ Resumed from checkpoint: step-2
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â âī¸ step-1 skipped â
â âī¸ step-2 skipped â
â â
step-3 5ms â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
With Stop Early
Logs indicate early termination:
await pipeline.run(
{ n: 0 },
{
log: { stopwatch: true },
onStepComplete: (e) => {
if (e.stepName === 's2') e.stopPipeline()
},
},
)
Output:
âšī¸ Early stop requested by: s2
âąī¸ Performance Summary:
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â â
s1 10ms â
â â
s2 5ms â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
âšī¸ Stopped early (requested by s2)
â° Total Pipeline Time: 15ms
⨠Pipeline stopped early
Best Practices
- Use
log: { stopwatch: true } during development for detailed insights
- Use custom
logFn to integrate with your logging infrastructure
- Disable step-level logging for noisy or sensitive operations
- Use performance summaries to identify bottlenecks
- Keep logging enabled in production with structured loggers