diff --git a/src/cli.ts b/src/cli.ts index 7f064ea..4faea07 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -59,8 +59,15 @@ export const cli = new Command().name("cronx") }, }) .option( - `--suppress-stdio`, - `Whether or not to suppress your executable's "stdout" and "stderr"`, + `--suppress-stdout`, + `Whether or not to suppress your executable's "stdout"`, + { + default: false, + }, + ) + .option( + `--suppress-stderr`, + `Whether or not to suppress your executable's "stderr"`, { default: false, }, @@ -76,7 +83,8 @@ export const cli = new Command().name("cronx") const { tab, offset, - suppressStdio, + suppressStdout, + suppressStderr, } = options; if (options.label) { @@ -183,21 +191,16 @@ export const cli = new Command().name("cronx") const jobLogger = new JobLogger(label); if (options.run) { - cconsole.debug(); - cconsole.debug( - `Running job: ${job} ${ - suppressStdio ? "and suppressing output to" : "writing output to" - } stdout and stderr`, - ); - cconsole.debug(); await runExecutable(job, { - suppressStdio, + suppressStdout, + suppressStderr, jobLogger, }); } scheduleCronWithExecutable(job, { - suppressStdio, + suppressStdout, + suppressStderr, jobLogger, label, cronxExpression: cronExpression!, diff --git a/src/cronx.ts b/src/cronx.ts index ff1dbe9..0b9716e 100644 --- a/src/cronx.ts +++ b/src/cronx.ts @@ -9,11 +9,15 @@ import { cconsole } from "cconsole"; -import { convertCronxExpressionToDenoCronExpression } from "./cron.ts"; +import { + convertCronxExpressionToDenoCronExpression, + getLocalUTCOffset, +} from "./cron.ts"; import { runExecutable } from "src/executables.ts"; -import type { JobLogger } from "./JobLogger.ts"; +import type { Logger } from "./JobLogger.ts"; +import type { LogLevel } from "@polyseam/cconsole"; /** * Validates a job label accepting only alphanumeric characters, whitespace, hyphens, and underscores. @@ -31,8 +35,10 @@ export type ScheduleExecutableOptions = { cronxExpression: string; label?: string; offset?: number; // timezone utc offset in hours - suppressStdio?: boolean; - jobLogger?: JobLogger; + suppressStdout?: boolean; + suppressStderr?: boolean; + jobLogger?: Logger; + logLevel?: LogLevel; }; /** @@ -40,7 +46,9 @@ export type ScheduleExecutableOptions = { * * @param job - The command-line executable to run * @param opt - Configuration options for the scheduled job - * @param opt.suppressStdio - Whether to suppress standard input/output + * @param opt.logLevel - The log level for cronx + * @param opt.suppressStdout - Whether to suppress stdout + * @param opt.suppressStderr - Whether to suppress stderr * @param opt.jobLogger - Optional custom logger function for job execution * @param opt.cronxExpression - The cron expression defining the schedule * @param opt.offset - Optional timezone offset in hours (defaults to local timezone offset) @@ -58,8 +66,12 @@ export function scheduleCronWithExecutable( job: string, opt: ScheduleExecutableOptions, ) { - const { suppressStdio, jobLogger, cronxExpression } = opt; - const offset = opt.offset ?? new Date().getTimezoneOffset() / -60; + const { suppressStdout, suppressStderr, jobLogger, cronxExpression } = opt; + + const offset = opt?.offset ?? getLocalUTCOffset(); + const logLevel = opt?.logLevel ?? "INFO"; + + cconsole.setLogLevel(logLevel); let label = job; @@ -82,14 +94,7 @@ export function scheduleCronWithExecutable( ); Deno.cron(label, denoCronExpression, async () => { - cconsole.debug(); - cconsole.debug( - `Running job: ${job} ${ - suppressStdio ? "and suppressing output to" : "writing output to" - } stdout and stderr`, - ); - cconsole.debug(); - await runExecutable(job, { suppressStdio, jobLogger }); + await runExecutable(job, { suppressStdout, suppressStderr, jobLogger }); }); } @@ -97,6 +102,7 @@ export type ScheduleFunctionOptions = { cronxExpression: string; label?: string; offset?: number; + logLevel?: LogLevel; }; /** @@ -124,10 +130,14 @@ export function scheduleCronWithFunction( ) { const { cronxExpression } = opt; - const offset = opt.offset ?? new Date().getTimezoneOffset() / -60; + const offset = opt.offset ?? getLocalUTCOffset(); const label = opt.label ?? jobFn.name; + const logLevel = opt.logLevel ?? "INFO"; + + cconsole.setLogLevel(logLevel); + const denoCronExpression = convertCronxExpressionToDenoCronExpression( cronxExpression, offset, @@ -135,7 +145,7 @@ export function scheduleCronWithFunction( Deno.cron(label, denoCronExpression, async () => { cconsole.debug(); - cconsole.debug("Running job function: " + label); + cconsole.debug("Running function job: " + label); cconsole.debug(); await jobFn(); }); diff --git a/src/executables.ts b/src/executables.ts index 13bd8c0..80b46a0 100644 --- a/src/executables.ts +++ b/src/executables.ts @@ -5,9 +5,11 @@ */ import { JobLogger, type Logger } from "./JobLogger.ts"; +import { cconsole } from "cconsole"; type RunExecutableOptions = { - suppressStdio?: boolean; + suppressStdout?: boolean; + suppressStderr?: boolean; jobLogger?: Logger; }; @@ -26,7 +28,7 @@ type RunExecutableOptions = { * @example * ```ts * await runExecutable("echo hello", { - * suppressStdio: false, + * suppressStderr: true, * jobLogger: console * }); * ``` @@ -35,11 +37,15 @@ export async function runExecutable( job: string, options: RunExecutableOptions, ) { - const suppressStdio = options?.suppressStdio || false; + const suppressStdout = options?.suppressStdout || false; + const suppressStderr = options?.suppressStderr || false; + + printDebugJobStatus({ suppressStderr, suppressStdout, job }); + const jobLogger = options?.jobLogger || new JobLogger(); - const stdout = suppressStdio ? "null" : "piped"; - const stderr = suppressStdio ? "null" : "piped"; + const stdout = suppressStdout ? "null" : "piped"; + const stderr = suppressStderr ? "null" : "piped"; const [c, ...args] = job.split(" "); @@ -51,15 +57,34 @@ export async function runExecutable( const output = await cmd.output(); const d = new TextDecoder(); - if (!suppressStdio) { - const stdoutTxt = d.decode(output.stdout); - const stderrTxt = d.decode(output.stderr); - if (stdoutTxt) { - jobLogger.log(stdoutTxt); - } - if (stderrTxt) { - jobLogger.error(stderrTxt); - } + if (!suppressStdout) { + jobLogger.log(d.decode(output.stdout)); + } + + // note: stderr is not always an error + // unix philosophy says to use stderr for any diagnostic logging + if (!suppressStderr) { + jobLogger.error(d.decode(output.stderr)); } } + +function printDebugJobStatus( + { suppressStdout, suppressStderr, job }: { + suppressStdout: boolean; + suppressStderr: boolean; + job: string; + }, +) { + cconsole.debug(); + cconsole.debug( + `Running job: ${job} with`, + ); + cconsole.debug( + `stdout: ${suppressStdout ? "suppressed" : "reflected"}`, + ); + cconsole.debug( + `stderr: ${suppressStderr ? "suppressed" : "reflected"}`, + ); + cconsole.debug(); +}