diff --git a/common.sh b/common.sh index ea6c57a..5c354ce 100755 --- a/common.sh +++ b/common.sh @@ -2115,6 +2115,19 @@ _orch_child_record() { return 0 } +# Keeps a child's own log to the same ceiling run_job.sh keeps a job's: the last 1000 lines, +# replaced atomically. LOG_DIR is tmpfs, so an untrimmed chatty script spends RAM rather than disk, +# which is the worse of the two places to leak. +_orch_trim_log() { + local f="$1" n + [[ -f "$f" ]] || return 0 + n=$(wc -l < "$f" 2>/dev/null) || return 0 + (( n > 1000 )) || return 0 + tail -n 1000 "$f" > "$f.tmp" 2>/dev/null && mv -f "$f.tmp" "$f" 2>/dev/null + rm -f "$f.tmp" 2>/dev/null + return 0 +} + run_orch_child() { local entry="$1" local script_args script_path script_name label extra_args run_args @@ -2137,17 +2150,43 @@ run_orch_child() { [[ "$DRY_RUN" == true ]] && run_args+=("--dry-run") [[ "$ENABLE_LOGGING" == true ]] && run_args+=("--log") - local _start _ec + local _start _ec _clog _start=$(date +%s) log "Running: $label" _orch_child_record "${script_args[0]}" "$_start" - if bash "$script_path" "${run_args[@]}"; then + + # The child's output goes to its own log as well as the orchestrator's, so a script can be + # read on its own terms. Until now everything a child printed was interleaved into the + # parent's log with forty other scripts, and the only marker separating them was a human + # heading like "━━━ Media Cleaner — anime ━━━" that maps to no script id — which is why + # scoped_log could answer for ten orchestrators and nothing else. + # + # tee, not a redirect: the parent's log must keep receiving everything exactly as before, so + # this adds a destination rather than moving one. PIPESTATUS[0] is what preserves the child's + # exit code through the pipe — $? would report tee's. + # + # The whole thing is conditional on the log being writable, and falls back to the original + # unpiped call when it is not. A child must never fail, or be denied a run, because somewhere + # to write about it could not be opened. + _clog="${LOG_DIR:-}/${script_args[0]%.sh}.log" + if [[ -n "${LOG_DIR:-}" ]] && mkdir -p "$(dirname "$_clog")" 2>/dev/null \ + && : >> "$_clog" 2>/dev/null; then + printf '\n── %s ────────────────────────────────────────────────\n' \ + "$(date '+%Y-%m-%d %H:%M:%S')" >> "$_clog" 2>/dev/null + bash "$script_path" "${run_args[@]}" 2>&1 | tee -a "$_clog" + _ec=${PIPESTATUS[0]} + _orch_trim_log "$_clog" + else + bash "$script_path" "${run_args[@]}" + _ec=$? + fi + + if (( _ec == 0 )); then log "$script_name — done in $(format_duration $(( $(date +%s) - _start )))" _orch_child_record "${script_args[0]}" "$_start" 0 JOB_PASS+=("$label") return 0 else - _ec=$? _orch_child_record "${script_args[0]}" "$_start" "$_ec" # Match the severity ladder run_job.sh already records against: exit 1 is a warning, # exit 2+ is an error. The status in the run record was always derived that way, so a