Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
# cronbake

## 0.4.0

### Features

- **Custom Logger Support**: Add pluggable logger interface allowing integration with any logging library (pino, winston, bunyan, logtape, etc.)
- New `Logger` interface exported from package API
- Pass `logger` option to `Baker.create()` to set a global logger for all jobs
- Override logger per job via `logger` option in `baker.add()` or `Cron.create()`
- Default behavior unchanged (falls back to `console`)
- Thanks to [@nemvince](https://github.com/nemvince) for the work in [PR #17](https://github.com/chaqchase/cronbake/pull/17)

### Examples

- Added `examples/logging.ts` demonstrating custom logger usage with colored output, global logger, per-job overrides, and error logging

### Documentation

- Updated README with comprehensive custom logger documentation and examples

## 0.3.2

### Fixes
Expand Down
31 changes: 29 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,14 +332,41 @@ await baker.restoreState();
You can provide a custom logger that implements the `Logger` interface to log messages from Cronbake:

```typescript
import { Baker, Logger } from 'cronbake';

// Using a logging library like logtape
import { getLogger } from '@logtape/logtape';
import { Baker, FilePersistenceProvider } from 'cronbake';

const logger = getLogger(['mirar', 'cron']);
const logger = getLogger(['myapp', 'cron']);

export const baker = Baker.create({
logger
});

// Or create a custom logger
const customLogger: Logger = {
info: (msg, ...args) => console.log(`[INFO] ${msg}`, ...args),
warn: (msg, ...args) => console.warn(`[WARN] ${msg}`, ...args),
error: (msg, ...args) => console.error(`[ERROR] ${msg}`, ...args),
debug: (msg, ...args) => console.debug(`[DEBUG] ${msg}`, ...args),
};

export const bakerWithCustomLogger = Baker.create({
logger: customLogger
});
```

You can also override the logger per job:

```typescript
const baker = Baker.create({ logger: defaultLogger });

baker.add({
name: 'special-job',
cron: '@daily',
logger: specialLogger, // This job uses a different logger
callback: () => console.log('Hello!'),
});
```

#### Persistence Providers (File and Redis)
Expand Down
13 changes: 13 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,17 @@ bun run examples/persistence.ts

The script writes state to `./.cronbake-state.json`, demonstrating `persist: true` vs. `persist: false` jobs and `await baker.ready()`.

## Run the logging example

```bash
bun run examples/logging.ts
```

This demonstrates custom logger support:

- Creating a custom logger with colored output
- Using a global logger for all jobs via Baker
- Overriding the logger per job
- Error logging when jobs fail

Feel free to copy these files or adapt them into your own app bootstrap logic.
129 changes: 129 additions & 0 deletions examples/logging.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import Baker, { Logger } from "../lib/index";

/**
* Custom logger with colored output and timestamps.
* You can replace this with any logger that implements the Logger interface,
* such as pino, winston, bunyan, or logtape.
*/
const createColoredLogger = (prefix: string): Logger => {
const colors = {
reset: "\x1b[0m",
dim: "\x1b[2m",
blue: "\x1b[34m",
yellow: "\x1b[33m",
red: "\x1b[31m",
cyan: "\x1b[36m",
};

const timestamp = () => new Date().toISOString();

return {
info: (message, ...args) => {
console.log(
`${colors.dim}${timestamp()}${colors.reset} ${colors.blue}[INFO]${
colors.reset
} ${colors.cyan}[${prefix}]${colors.reset}`,
message,
...args
);
},
warn: (message, ...args) => {
console.warn(
`${colors.dim}${timestamp()}${colors.reset} ${colors.yellow}[WARN]${
colors.reset
} ${colors.cyan}[${prefix}]${colors.reset}`,
message,
...args
);
},
error: (message, ...args) => {
console.error(
`${colors.dim}${timestamp()}${colors.reset} ${colors.red}[ERROR]${
colors.reset
} ${colors.cyan}[${prefix}]${colors.reset}`,
message,
...args
);
},
debug: (message, ...args) => {
console.debug(
`${colors.dim}${timestamp()}${colors.reset} ${colors.dim}[DEBUG]${
colors.reset
} ${colors.cyan}[${prefix}]${colors.reset}`,
message,
...args
);
},
};
};

async function main() {
// Create a custom logger for the Baker instance
const bakerLogger = createColoredLogger("cronbake");

const baker = Baker.create({
autoStart: true,
logger: bakerLogger,
});

// This job uses the Baker's logger (bakerLogger)
baker.add({
name: "heartbeat",
cron: "@every_5_seconds",
immediate: true,
callback: () => {
console.log("💓 Heartbeat job executed successfully");
},
});

// This job has its own dedicated logger
const paymentLogger = createColoredLogger("payments");
baker.add({
name: "process-payments",
cron: "@every_10_seconds",
logger: paymentLogger, // Override with job-specific logger
immediate: true,
callback: () => {
console.log("💰 Processing payments...");
},
});

// This job will fail, demonstrating error logging
baker.add({
name: "flaky-job",
cron: "@every_15_seconds",
immediate: true,
delay: "2s",
callback: () => {
// Simulate a random failure
if (Math.random() > 0.5) {
throw new Error("Random failure occurred!");
}
console.log("🎲 Flaky job succeeded this time!");
},
});

console.log("\n🚀 Logging example started!");
console.log(" - heartbeat: runs every 5 seconds (uses Baker logger)");
console.log(
" - process-payments: runs every 10 seconds (uses custom payments logger)"
);
console.log(
" - flaky-job: runs every 15 seconds, fails randomly to demo error logging"
);
console.log("\nPress Ctrl+C to stop.\n");

const shutdown = () => {
console.log("\n👋 Stopping all jobs...");
baker.destroyAll();
process.exit(0);
};

process.once("SIGINT", shutdown);
process.once("SIGTERM", shutdown);
}

main().catch((error) => {
console.error("Logging example failed:", error);
process.exit(1);
});
1 change: 1 addition & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export {
type OnDayStrType,
type day,
type unit,
type Logger,
} from "./lib";

export { Cron, Baker, CronParser } from "./lib";
Expand Down
1 change: 1 addition & 0 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export {
type OnDayStrType,
type day,
type unit,
type Logger,
} from "./types";

export { Cron, Baker, CronParser, FilePersistenceProvider, RedisPersistenceProvider };
Expand Down
49 changes: 23 additions & 26 deletions lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,36 +42,36 @@ interface ICronParser {
}

type unit =
| 'seconds'
| 'minutes'
| 'hours'
| 'dayOfMonth'
| 'months'
| 'dayOfWeek';
| "seconds"
| "minutes"
| "hours"
| "dayOfMonth"
| "months"
| "dayOfWeek";

type day =
| 'sunday'
| 'monday'
| 'tuesday'
| 'wednesday'
| 'thursday'
| 'friday'
| 'saturday';
| "sunday"
| "monday"
| "tuesday"
| "wednesday"
| "thursday"
| "friday"
| "saturday";

type EveryStrType<U extends unit = unit> = `@every_${string}_${U}`;
type AtHourStrType = `@at_${number}:${number}`;
type OnDayStrType<D extends day = day> = `@on_${D}`;
type BetweenStrType = `@between_${number}_${number}`;

type CronExprs =
| '@every_second'
| '@every_minute'
| '@yearly'
| '@annually'
| '@monthly'
| '@weekly'
| '@daily'
| '@hourly'
| "@every_second"
| "@every_minute"
| "@yearly"
| "@annually"
| "@monthly"
| "@weekly"
| "@daily"
| "@hourly"
| EveryStrType
| AtHourStrType
| OnDayStrType
Expand Down Expand Up @@ -275,9 +275,6 @@ type CronOptions<T extends string = string> = {
* Defaults to true for safety.
*/
overrunProtection?: boolean;
/**
* If true, run the first callback immediately on start (before schedule)
*/
/**
* If true, run the first callback immediately on start (before schedule)
*/
Expand All @@ -293,7 +290,7 @@ type CronOptions<T extends string = string> = {
delay?: number | string;
};

type Status = 'running' | 'stopped' | 'paused' | 'error';
type Status = "running" | "stopped" | "paused" | "error";

/**
* Execution history entry for a cron job
Expand Down Expand Up @@ -323,7 +320,7 @@ type JobMetrics = {
*/
import type { PersistenceProvider } from "./persistence/types";

type PersistenceStrategy = 'file' | 'redis' | 'custom';
type PersistenceStrategy = "file" | "redis" | "custom";

type RedisPersistenceOptions = {
key?: string;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "cronbake",
"description": "A powerful and flexible cron job manager built with TypeScript",
"module": "dist/index.js",
"version": "0.3.2",
"version": "0.4.0",
"publishConfig": {
"access": "public"
},
Expand Down