Logger Guide
Use this guide when working with Logger instances, log levels, timing, and transport configuration in FaasJS apps.
Applicable Scenarios
- Logging in middleware, plugins, or background jobs
- Adding request, lifecycle, or failure logging to a FaasJS server
- Creating standalone scripts that need a shared logger
- Timing slow operations
- Adjusting log verbosity for debugging
Default Workflow
- Reuse the injected
loggerwhen FaasJS already gives you one. - Create
new Logger('label')only for standalone scripts, adapters, or infrastructure code. - Use
debugfor details,infofor normal milestones,warnfor degraded paths, anderrorfor failures. - Keep labels short and stable so related logs stay easy to scan.
- Use
time()andtimeEnd()around slow steps. - Change verbosity, color mode, truncation, and transport behavior with environment variables before changing log call sites.
Rules
1. Prefer the injected logger when the framework provides one
- In FaasJS runtime code,
loggeris often already part of the callback context. - Reuse that logger so labels, timing, and transport behavior stay consistent with the runtime.
- Do not create a second logger inside the same request or job handler unless you have a very specific reason.
Middleware example:
import { useMiddleware } from '@faasjs/core'
export default useMiddleware((request, response, { logger }) => {
logger.info('%s %s', request.method, request.url)
response.end('ok')
})
Job handler example:
import { defineJob } from '@faasjs/jobs'
export default defineJob({
async handler({ logger }) {
logger.info('run cleanup')
},
})
2. Create a labeled logger for standalone code
- Use
new Logger('label')in scripts, CLIs, build tools, or custom adapters. - A good label explains where the log came from without becoming noisy.
- Prefer labels like
seed,typegen,server, orsync:users.
import { Logger } from '@faasjs/node-utils'
const logger = new Logger('seed')
logger.info('start importing users')
logger.info('loaded config %o', { region: 'cn', dryRun: false })
3. Choose levels by intent
debug: internal steps, params, cache hits, and other noisy diagnosticsinfo: expected lifecycle messages such as startup, shutdown, and successful jobswarn: recoverable problems, fallbacks, skipped work, or unusual stateserror: failures that need attention- Pass an
Errorobject directly tologger.error(error)when you have one
Prefer this:
try {
await syncUsers()
logger.info('sync completed')
} catch (error) {
logger.error(error)
}
Over manually stringifying the error first.
4. Use format strings instead of building large strings by hand
Loggersupports format-style messages, which keeps logs compact and readable.- Reach for
%sfor text,%dfor numbers,%jfor JSON, and%ofor inspected objects. - This is usually clearer than manual concatenation or eager
JSON.stringify().
logger.debug('user=%s retries=%d payload=%j', user.id, retries, payload)
5. Time slow operations with time() and timeEnd()
- Use timers for network calls, database work, file IO, or startup hooks.
- Keep the timer key stable and make sure
timeEnd()uses the same key. - If the key is missing, FaasJS logs a warning, so mismatches are easy to spot.
logger.time('load-user', 'info')
const user = await loadUser(id)
logger.timeEnd('load-user', 'loaded user %s', user.id)
6. Tune output with environment variables
- Logger defaults are
FaasLog=info,FaasLogSize=1000, auto-detected terminal colors, and shared transport forwarding outside Vitest. FaasLog=debug|info|warn|errorsets the minimum level.FaasLogMode=plaindisables ANSI colors.FaasLogMode=prettyforces colorized terminal output.- Without
FaasLogMode, color output followsFORCE_COLOR,NO_COLOR,TERM, and whether stdout is a TTY. FaasLogSize=2000changes the truncation threshold for long debug/info logs. Warnings and errors are not truncated.FaasLogTransport=true|falseenables or disables shared transport forwarding. In Vitest, forwarding is disabled unlessFaasLogTransport=true.
Examples:
FaasLog=info npx vp test
FaasLog=debug FaasLogMode=plain node ./scripts/sync-users.ts
7. Use transport only when you really need log shipping
Loggerforwards messages to the shared transport by default.- Reach for
getTransport()when you want to batch logs into another system. - Transport handlers receive batches of formatted log messages on the configured interval.
- If a flush happens with no registered handlers, buffered messages are discarded and transport disables itself until a handler is registered or config is applied.
- If you register transport handlers, flush them during shutdown so buffered logs are not lost.
import { getTransport } from '@faasjs/node-utils'
const transport = getTransport()
transport.register('shipper', async (messages) => {
for (const message of messages) {
await sendToRemote(message)
}
})
process.on('SIGINT', async () => {
await transport.stop()
process.exit(0)
})
8. Do not log secrets or full sensitive payloads
- Avoid logging tokens, cookies, session content, passwords, or raw request bodies by default.
- Be extra careful with
debuglogs because they often survive in CI logs and incident reports. - If you must log request context, prefer IDs, counts, and safe summaries.
Review Checklist
- injected loggers are reused where available
- labels are short, stable, and meaningful
debugis used for noisy diagnostics instead ofinfo- caught errors are logged with
logger.error(error)when possible - slow steps use
time()andtimeEnd()with matching keys - environment variables are used to change verbosity, color mode, truncation, and transport forwarding
- secrets and sensitive payloads are not logged
- transport handlers call
stop()during shutdown when transport is enabled