@reliverse/relinka is a modern, minimal logging library that actually feels right. It's not just pretty output — it's a system: smart formatting, file-safe logging, runtime config support, and a
fatal
mode built for developers who care about correctness. Whether you're building CLI tools, SDKs, or full-stack apps — Relinka helps you log with intention.
sponsor — discord — repo — npm
- 🧙 Drop-in replacement for
node:console
,consola
, or your internal logger - 💬 Supports:
info
,warn
,success
,error
,verbose
,fatal
,clear
- 🎨 Terminal output that pops, with automatic color handling
- 📁 Save logs to file (with daily rotation, cleanup, and max-file limits)
- 🧠 Structured message formatting (objects, errors, stacks — handled!)
- ⚙️ Runtime config via
relinka.config.ts
(powered byreconf
) - 🚨
fatal
logs halt execution and triggerdebugger
in dev - 🧩 Sync-first, async, & CLI-friendly thanks to buffer flushing
Make sure you have git, node.js, and bun/pnpm/yarn/npm installed.
bun add @reliverse/relinka
Coming soon:
bun i -g @reliverse/dler
dler relinka --console-to-relinka
- Place this at the START of your app's main function:
await relinkaConfig;
- Place this at the END of your app's main function:
await relinkaShutdown();
Usage example:
import {
relinka,
relinkaAsync,
relinkaConfig,
relinkaShutdown,
} from "@reliverse/relinka";
export async function main() {
await relinkaAsync(
// this automatically loads the config
"verbose",
"This ASYNC verbose message can be seen only if verbose=true (in user config)",
);
await relinkaConfig; // place this at your main function or just at the top of your entry file
relinka(
"verbose",
"This SYNC verbose message can be seen only if verbose=true (in user config) AND config was loaded ",
);
// --- BOX LEVEL EXAMPLES ---
relinka("box", "This is a boxed message using direct syntax!");
relinka.box("This is a boxed message using method syntax!");
// --- MESSAGE LEVEL EXAMPLES ---
relinka("message", "This is a message using direct syntax!");
relinka.message("This is a message using method syntax!");
// --- STEP LEVEL EXAMPLES ---
relinka("step", "Step 1: Initialize application");
relinka.step("Step 2: Load configuration");
relinka.step("Step 3: Start services");
// --- LOG LEVEL EXAMPLES ---
relinka("log", "Hello! 👋");
relinka("log", "Great to see you here!");
relinka("info", "Everything is running smoothly");
relinka("warn", "This might be a problem");
relinka(
"error", // non-fatal issue level can be recovered
"Uh oh, something broke",
);
relinka(
"null",
"'null' level has a special handling case: no symbol or spacing",
);
// relinka(
// "fatal",
// "We should never reach this code! This should never happen! (see <anonymous> line)",
// ); // fatal level throws error and halts execution
relinka("success", "Thanks for using Relinka!");
// Make sure to shut down the logger at the end of your program
// This is important to flush all buffers and close file handles
await relinkaShutdown();
// Make sure to exit the program after your CLI is done
// It's not required for Bun-only apps, but recommended
// for other terminal runtimes like Node.js (incl. `tsx`)
// It's also not required for @reliverse/rempts `runMain()`
process.exit(0);
}
await main();
const logger = initRelinkaInstance({/* per-project config */});
logger("info", "Looks great!");
await relinkaConfig;
relinka.info("Looks great!");
// Clear terminal:
relinka("clear", "");
// Blank line:
relinka("info", "");
// Async variant:
import { relinkaAsync } from "@reliverse/relinka";
await relinkaAsync("info", "Logged from async context");
// Coming soon:
await relinkaAsync("info", "Something happened", { animate: true });
Create relinka.config.ts
:
import { defineConfig } from "@reliverse/relinka";
/**
* RELINKA CONFIGURATION FILE
* - Hover over a field to see the information
* - Use intellisense to see available options
* @see https://github.com/reliverse/relinka
*/
export default defineConfig({
// Enable to see verbose logs
verbose: false,
// Timestamp configuration
timestamp: {
enabled: false,
format: "HH:mm:ss",
},
// Control whether logs are saved to a file
saveLogsToFile: true,
// Disable colors in the console
disableColors: false,
// Log file path
logFilePath: "relinka.log",
levels: {
success: {
symbol: "✓",
fallbackSymbol: "[OK]",
color: "greenBright",
spacing: 3,
},
info: {
symbol: "i",
fallbackSymbol: "[i]",
color: "cyanBright",
spacing: 3,
},
error: {
symbol: "✖",
fallbackSymbol: "[ERR]",
color: "redBright",
spacing: 3,
},
warn: {
symbol: "⚠",
fallbackSymbol: "[WARN]",
color: "yellowBright",
spacing: 3,
},
fatal: {
symbol: "‼",
fallbackSymbol: "[FATAL]",
color: "redBright",
spacing: 3,
},
verbose: {
symbol: "✧",
fallbackSymbol: "[VERBOSE]",
color: "gray",
spacing: 3,
},
log: { symbol: "│", fallbackSymbol: "|", color: "dim", spacing: 3 },
},
// Directory settings
dirs: {
dailyLogs: true,
logDir: "logs", // store logs in a custom folder
maxLogFiles: 5, // keep only the 5 most recent log files
specialDirs: {
distDirNames: [],
useParentConfigInDist: true,
},
},
});
Supported files:
relinka.config.ts
- 🔜 other formats, supported by
reconf
, are coming soon
- Default:
logs/relinka.log
- Daily mode:
2025-04-11-relinka.log
- Auto-cleanup: keep latest N logs
relinka("info", "message", optionalDetails);
relinka("fatal", "something broke"); // throws
relinka("clear", ""); // clears terminal
await relinkaAsync("warn", "something async");
defineConfig({ ... }) // helper for relinka.config.ts
- ✅ Timestamping
- ✅ File-safe formatting
- ✅ Log rotation
- ✅ Fatal logging (with debugger)
- ✅ Colorized terminal output
→ You forget to load user's config by using await relinkaConfig;
at the START of your app's main function.
→ You forget to flush the buffer. Place await relinkaShutdown();
at the END of your app's main function.
→ Add empty string: relinka("info", "", args)
→ Yes, always. It will halt execution and trigger debugger
in dev mode.
- Relinka is designed to be used in the different ways:
- Use
relinka(level, message, ...args)
(recommended). - 🔜 Or, just
relinka.level(message, ...args)
- 🔜 Both designed to work with both sync (default) and async/await.
- 🔜 Both designed to work with both direct and wrapper methods.
- 🔜 Use the async logger if you want some advanced features (like typing text streaming animation).
- Building CLIs? Use with
@reliverse/prompts
- Want type-safe injections? Try
@reliverse/reinject
- For advanced bundling? Pair with
@reliverse/dler
- [x] File logging
- [x] Timestamps
- [x] Daily logs
- [x] Log rotation
- [x]
fatal
type - [x] Runtime config
- [ ] Implement per-project config redefinition
- [ ] Plugin support (custom formatters, hooks)
- [ ] CLI interface (to manage logs, config, etc)
Relinka wouldn't exist without these gems:
💖 MIT © 2025 blefnk Nazar Kornienko