@reliverse/relinka
Version:
@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.
308 lines (238 loc) • 8.42 kB
Markdown
# Relinka: Logging that Actually Feels Good
> **/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](https://github.com/sponsors/blefnk) — [discord](https://discord.gg/Pb8uKbwpsJ) — [repo](https://github.com/reliverse/relinka) — [npm](https://npmjs.com/@reliverse/relinka)
## Why Relinka
- 🧙 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 by `reconf`)
- 🚨 `fatal` logs halt execution and trigger `debugger` in dev
- 🧩 Sync-first, async, & CLI-friendly thanks to buffer flushing
## Getting Started
Make sure you have git, node.js, and bun/pnpm/yarn/npm installed.
### 1. Install
```bash
bun add /relinka
```
**Coming soon**:
```bash
bun i -g /dler
dler relinka --console-to-relinka
```
### 2. Use It
#### Direct Method (Recommended)
- Place this **at the START** of your app's main function:
```ts
await relinkaConfig;
```
- Place this **at the END** of your app's main function:
```ts
await relinkaShutdown();
```
**Usage example**:
```ts
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 ",
);
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();
```
#### [🔜 Soon] Singleton Method
```ts
const logger = initRelinkaInstance({/* per-project config */});
logger("info", "Looks great!");
```
#### [🔜 Soon] Object Method
```ts
await relinkaConfig;
relinka.info("Looks great!");
```
## Advanced Usage
```ts
// 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 });
```
## Config
Create `relinka.config.ts`:
```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
## Log Files
- Default: `logs/relinka.log`
- Daily mode: `2025-04-11-relinka.log`
- Auto-cleanup: keep latest N logs
## API Summary
```ts
relinka("info", "message", optionalDetails);
relinka("fatal", "something broke"); // throws
relinka("clear", ""); // clears terminal
await relinkaAsync("warn", "something async");
```
```ts
defineConfig({ ... }) // helper for relinka.config.ts
```
## Built-in Utilities
- ✅ Timestamping
- ✅ File-safe formatting
- ✅ Log rotation
- ✅ Fatal logging (with debugger)
- ✅ Colorized terminal output
## FAQ
### Why `relinka.config.ts` doesn't works for me?
→ You forget to load user's config by using `await relinkaConfig;` **at the START** of your app's main function.
### Why my terminal stucks after last relinka() usage?
→ You forget to flush the buffer. Place `await relinkaShutdown();` **at the END** of your app's main function.
### Why does TS linter tells that something wrong with `relinka("info", args)`?
→ Add empty string: `relinka("info", "", args)`
### Does `fatal` throw?
→ Yes, always. It will halt execution and trigger `debugger` in dev mode.
### What's coming next?
- 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).
## Tips
- Building CLIs? Use with [`/prompts`](https://npmjs.com//prompts)
- Want type-safe injections? Try [`/reinject`](https://npmjs.com//reinject)
- For advanced bundling? Pair with [`/dler`](https://npmjs.com//dler)
## Roadmap
- [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)
## Shoutouts
Relinka wouldn't exist without these gems:
- [unjs/consola](https://github.com/unjs/consola#readme)
- [winston](https://github.com/winstonjs/winston#readme)
- [pino](https://github.com/pinojs/pino#readme)
- [node-bunyan](https://github.com/trentm/node-bunyan#readme)
## License
💖 MIT © 2025 [blefnk Nazar Kornienko](https://github.com/blefnk)