@mark1russell7/service 0.5.16 → 0.5.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.js CHANGED
@@ -609,6 +609,140 @@ const healthRuntime = (description, runs, options = {}) => {
609
609
  };
610
610
  };
611
611
  //#endregion
612
+ //#region src/logger.ts
613
+ /** These are the levels of a log entry, from the least to the most severe. */
614
+ const LOG_LEVELS = [
615
+ "debug",
616
+ "info",
617
+ "warn",
618
+ "error"
619
+ ];
620
+ /** This function tells if `level` is at `least` or more severe. */
621
+ const atLeast = (level, least) => LOG_LEVELS.indexOf(level) >= LOG_LEVELS.indexOf(least);
622
+ /**
623
+ * This function makes the console logger. It writes each entry at its level or more severe to the
624
+ * method of that level, with the message first and then the fields.
625
+ */
626
+ const consoleLogger = (options = {}) => {
627
+ const least = options.level ?? "info";
628
+ return { log(level, message, fields) {
629
+ if (!atLeast(level, least)) return;
630
+ const target = options.console ?? globalThis.console;
631
+ if (Object.keys(fields).length === 0) target[level](message);
632
+ else target[level](message, fields);
633
+ } };
634
+ };
635
+ const seconds = (cap) => `${valueIn(cap, "s")} s`;
636
+ /** This function gives the exit code from the detail of an exit event, for example `exit 1`. */
637
+ const exitCodeOf = (detail) => {
638
+ const found = /^exit (\d+)$/.exec(detail ?? "");
639
+ return found === null ? null : Number(found[1]);
640
+ };
641
+ /**
642
+ * This function makes a recorder that writes each lifecycle event to a logger. Thus the logger is
643
+ * one sink of the same stream as the recorder, not a second set of calls. The levels are these:
644
+ * - `error`: each error of the lifecycle, and an exit at once on a repeat signal.
645
+ * - `warn`: a phase at its cap, a failed startup, a startup over its budget, a tripped latch and a blocked event loop.
646
+ * - `info`: each step of the startup, the start and the end of each phase, the exit, and each recovery.
647
+ * - `debug`: each other transition, for example each poll of a check.
648
+ * With the description, the entry of a phase also gives its cap. A failure of the logger does not stop the lifecycle.
649
+ */
650
+ const logEvents = (logger, description) => {
651
+ const caps = new Map((description?.shutdown.phases ?? []).map((phase) => [phase.name, phase.cap]));
652
+ const startedAt = /* @__PURE__ */ new Map();
653
+ const write = (level, message, fields) => {
654
+ try {
655
+ logger.log(level, message, fields);
656
+ } catch {}
657
+ };
658
+ const phaseEnd = (event, base) => {
659
+ const phase = event.from;
660
+ const cap = caps.get(phase);
661
+ const begun = startedAt.get(phase);
662
+ const reached = event.on === "phase.cap";
663
+ const fields = {
664
+ ...base,
665
+ phase,
666
+ outcome: reached ? "cap" : "done",
667
+ ...cap === void 0 ? {} : { cap },
668
+ ...begun === void 0 ? {} : { took: {
669
+ value: event.at - begun,
670
+ unit: "ms"
671
+ } }
672
+ };
673
+ if (reached) write("warn", `the phase ${phase} reached its cap${cap === void 0 ? "" : ` of ${seconds(cap)}`}`, fields);
674
+ else write("info", `the phase ${phase} ended in its cap`, fields);
675
+ };
676
+ const phaseStart = (event, base) => {
677
+ const phase = event.to;
678
+ const cap = caps.get(phase);
679
+ startedAt.set(phase, event.at);
680
+ write("info", `the phase ${phase} starts${cap === void 0 ? "" : `, with a cap of ${seconds(cap)}`}`, {
681
+ ...base,
682
+ phase,
683
+ ...cap === void 0 ? {} : { cap }
684
+ });
685
+ };
686
+ return (event) => {
687
+ const { machine, instance, from, to, on, at, detail } = event;
688
+ const base = {
689
+ machine,
690
+ instance,
691
+ from,
692
+ to,
693
+ on,
694
+ at,
695
+ ...detail === void 0 ? {} : { detail }
696
+ };
697
+ switch (machine) {
698
+ case "gate":
699
+ if (on === "component.started") return write("info", `${detail ?? "a component"} started`, {
700
+ ...base,
701
+ component: detail
702
+ });
703
+ if (on === "startup.begin") return write("info", `the startup of ${instance} begins`, base);
704
+ if (on === "mark-ready") return write("info", "the warm-up sweep begins", base);
705
+ if (on === "sweep.done") return write("info", `${instance} is ready: the startup gate is open`, base);
706
+ if (on === "startup.fail") return write("warn", `the startup of ${instance} failed`, base);
707
+ if (on === "startup.over-budget") return write("warn", `the startup of ${instance} is over its budget`, base);
708
+ break;
709
+ case "exit":
710
+ if (on === "error") return write("error", detail ?? "an error of the lifecycle", base);
711
+ if (to === "exited") {
712
+ const code = exitCodeOf(detail);
713
+ const fields = {
714
+ ...base,
715
+ code
716
+ };
717
+ if (on === "exit.repeat") return write("error", `a signal came again, and ${instance} exits at once with the code ${code}`, fields);
718
+ return write("info", `${instance} exited with the code ${code}`, fields);
719
+ }
720
+ if (from === "running" && to === "stopping") return write("info", `the shutdown of ${instance} begins`, {
721
+ ...base,
722
+ cause: detail
723
+ });
724
+ if (on === "exit.repeat") return write("info", "a trigger came again while the shutdown runs", base);
725
+ break;
726
+ case "drain":
727
+ if (on === "signal.abort") return write("info", `the signal of ${instance} aborts`, base);
728
+ if (on === "drain.cooperative-abort") return write("info", "the cooperative abort of the drain", base);
729
+ if (on === "phase.done" || on === "phase.cap") phaseEnd(event, base);
730
+ if (to !== "done" && from !== to) phaseStart(event, base);
731
+ else if (to === "done") write("info", `the shutdown of ${instance} ended its phases`, base);
732
+ return;
733
+ case "latch":
734
+ case "window":
735
+ if (to === "tripped" && from !== "tripped") return write("warn", `the check ${instance} tripped its latch`, base);
736
+ if (to === "released" && (from === "tripped" || from === "recovering")) return write("info", `the check ${instance} released its latch`, base);
737
+ break;
738
+ case "watchdog":
739
+ if (to === "blocked" && from !== "blocked") return write("warn", "the event loop is blocked", base);
740
+ if (to === "watching" && from === "blocked") return write("info", "the event loop is no longer blocked", base);
741
+ }
742
+ write("debug", `${machine} ${instance}: ${from} -> ${to} on ${on}`, base);
743
+ };
744
+ };
745
+ //#endregion
612
746
  //#region src/process.ts
613
747
  /** These are the signals that the runtime handles. */
614
748
  const EXIT_SIGNALS = [
@@ -1820,6 +1954,22 @@ const shutdownProblems = (floor, repeat, triggers, exitCodes, messages) => {
1820
1954
  }
1821
1955
  return problems;
1822
1956
  };
1957
+ /**
1958
+ * This function gives the options with one recorder for the recorder and the logger of the options.
1959
+ * Without a logger, it gives the same options.
1960
+ */
1961
+ const withLogger = (options, description) => {
1962
+ if (options.logger === void 0) return options;
1963
+ const given = options.record;
1964
+ const log = logEvents(options.logger, description);
1965
+ return {
1966
+ ...options,
1967
+ record: (event) => {
1968
+ given?.(event);
1969
+ log(event);
1970
+ }
1971
+ };
1972
+ };
1823
1973
  /** This function starts the composition of a service. */
1824
1974
  const service = (name) => {
1825
1975
  if (name.trim().length === 0) throw new Error("a service needs a name");
@@ -1871,8 +2021,9 @@ const service = (name) => {
1871
2021
  return builder;
1872
2022
  };
1873
2023
  /** This function makes the runtime of the declaration: its checks, and a watchdog for each spec. */
1874
- const makeRuntime = (options) => {
2024
+ const makeRuntime = (given) => {
1875
2025
  const description = builder.describe();
2026
+ const options = withLogger(given, description);
1876
2027
  const clock = options.clock ?? realClock;
1877
2028
  const dogs = [];
1878
2029
  const all = new Map(runs);
@@ -1907,7 +2058,8 @@ const service = (name) => {
1907
2058
  }
1908
2059
  },
1909
2060
  description,
1910
- stopPollers
2061
+ stopPollers,
2062
+ options
1911
2063
  };
1912
2064
  };
1913
2065
  const builder = {
@@ -2223,7 +2375,7 @@ const service = (name) => {
2223
2375
  const ran = lifecycle(made.description, made.runtime, {
2224
2376
  ...components,
2225
2377
  stopPollers: made.stopPollers
2226
- }, options, (given) => {
2378
+ }, made.options, (given) => {
2227
2379
  life = given;
2228
2380
  });
2229
2381
  running = ran.started.then(() => ran);
@@ -3927,6 +4079,6 @@ const MACHINES = {
3927
4079
  watchdog: WATCHDOG_MACHINE
3928
4080
  };
3929
4081
  //#endregion
3930
- export { DECLARATION_RULE, DRAIN_RULE_IDS, DURATION_FRACTIONS, EPISODES_KEPT, EXIT_MACHINE, EXIT_SIGNALS, FORMAT, GATE_MACHINE, LATCH_MACHINE, LAW_STATISTIC, LIMITER_RULES, MACHINES, PLATFORM_MARKS, POLLER_MACHINE, PROCESS_TRIGGERS, PROGRESS_RULE, SERVICE_RULES, SLIDING_WINDOW_MACHINE, STAGES, STOPS, STOP_RULE, TOKEN_BUCKET_MACHINE, WATCHDOG_MACHINE, WINDOW_MACHINE, asDebounce, asWindow, backoffBand, backoffDelay, backoffProblems, capKnob, compress, compression, compressionOf, cooperativeAbortAt, daemon, decide, detectionTime, drainMachine, embeddedProcess, ephemeral, evaluate, failSpan, freeAt, heartbeat, heartbeatDetection, idleSpan, inequalities, isExitCode, isExitSignal, isMessageShape, keyedLimiter, kubernetes, limitRule, limiterMachine, limiterProblems, limiterStateOf, lint, lintStop, localDev, longestRetryAfter, matchesMessage, mayReissue, memoryStore, nodeProcess, platformClock, processPortOf, queueDrain, realClock, recoverSpan, redact, redeliverOnce, replay, retryAfterSeconds, service, serviceBindings, stageRank, symbolsOf, verdictOf, watchdog, workerLoop };
4082
+ export { DECLARATION_RULE, DRAIN_RULE_IDS, DURATION_FRACTIONS, EPISODES_KEPT, EXIT_MACHINE, EXIT_SIGNALS, FORMAT, GATE_MACHINE, LATCH_MACHINE, LAW_STATISTIC, LIMITER_RULES, LOG_LEVELS, MACHINES, PLATFORM_MARKS, POLLER_MACHINE, PROCESS_TRIGGERS, PROGRESS_RULE, SERVICE_RULES, SLIDING_WINDOW_MACHINE, STAGES, STOPS, STOP_RULE, TOKEN_BUCKET_MACHINE, WATCHDOG_MACHINE, WINDOW_MACHINE, asDebounce, asWindow, atLeast, backoffBand, backoffDelay, backoffProblems, capKnob, compress, compression, compressionOf, consoleLogger, cooperativeAbortAt, daemon, decide, detectionTime, drainMachine, embeddedProcess, ephemeral, evaluate, failSpan, freeAt, heartbeat, heartbeatDetection, idleSpan, inequalities, isExitCode, isExitSignal, isMessageShape, keyedLimiter, kubernetes, limitRule, limiterMachine, limiterProblems, limiterStateOf, lint, lintStop, localDev, logEvents, longestRetryAfter, matchesMessage, mayReissue, memoryStore, nodeProcess, platformClock, processPortOf, queueDrain, realClock, recoverSpan, redact, redeliverOnce, replay, retryAfterSeconds, service, serviceBindings, stageRank, symbolsOf, verdictOf, watchdog, workerLoop };
3931
4083
 
3932
4084
  //# sourceMappingURL=index.js.map