#!/bin/sh
#
# /etc/cron.daily/fpm-traffic-snapshot
#
# Persists yesterday's per-route duration, status and concurrency figures.
#
# Why this is not on-demand: the pool status page cannot see request durations at
# all, so the access log is the only source for them -- and Apache's logrotate
# discards it long before you would want to compare this week against last month.
# Without a nightly snapshot, every timeout and routing decision is limited to
# whatever is still in access.log.
#
# Runs BEFORE logrotate in cron.daily's alphabetical order ("f" < "l"), so
# yesterday's lines are normally still in access.log; the rotated files are read
# as well in case that assumption ever fails.
#
# Output goes into the metrics directory so it inherits the same retention and
# permissions as the sampler's files -- the *.log glob in fpm-metrics-prune
# already covers it.
#
# TWO outputs, with deliberately different lifetimes:
#
#   traffic-YYYY-MM-DD.log   human-readable, both scopes, PRUNED at 45 days by the
#                            *.log glob. This is the one an operator reads by eye.
#   traffic-YYYY-MM-DD.json  machine-readable, --all scope, KEPT FOREVER because
#                            .json matches none of fpm-metrics-prune's globs
#                            (*.jsonl, *.log, *.jsonl.gz, *.log.gz). Same trick the
#                            hourly rollups use, for the same reason: the portal
#                            monitoring UI needs ranges older than the raw retention.
#
# Do NOT "protect" these by adding a `! -name 'traffic-*'` guard to the prune, the
# way rollup-* carries one. That pattern would also immortalise the .log files above,
# which are supposed to expire. The extension is doing the work here.
#
# The JSON pass backfills. logrotate keeps 14 days of access logs, so a first run
# populates roughly two weeks of history instead of starting from tomorrow.
#
set -eu

DIR="${DIR:-/var/log/php8.3-fpm/metrics}"
REPORT="${REPORT:-/usr/local/bin/pool-traffic-report.pl}"
LOGDIR="${LOGDIR:-/var/log/apache2}"

[ -x "$REPORT" ] || exit 0
[ -d "$DIR" ]    || exit 0

DAY=$(date -d yesterday +%y-%m-%d)
OUT="$DIR/traffic-$(date -d yesterday +%Y-%m-%d).log"

# Every access log still on disk, not just the newest three. --since/--until filter by
# request timestamp, so feeding a superset costs a few MB of parsing and removes the
# dependency on when logrotate ran relative to this job -- and the backfill pass below
# genuinely needs the older files. Only files that exist: zcat -f fails on a missing
# argument.
set --
for f in "$LOGDIR"/access.log "$LOGDIR"/access.log.[0-9] "$LOGDIR"/access.log.[0-9][0-9] \
         "$LOGDIR"/access.log.*.gz; do
	[ -f "$f" ] && set -- "$@" "$f"
done
[ $# -gt 0 ] || exit 0

if [ ! -e "$OUT" ]; then
{
	echo "### $DAY  long pool"
	zcat -f "$@" | "$REPORT" --since "$DAY 00:00" --until "$DAY 23:59:59" || true

	# The fast pool serves three applications, and its 300s request_terminate_timeout
	# already truncates routes that ask for more -- internal/supportCard/
	# analyzeSupportCard requests 600s and is cut at half that. Those durations only
	# exist here.
	echo
	echo "### $DAY  all routes"
	zcat -f "$@" | "$REPORT" --all --since "$DAY 00:00" --until "$DAY 23:59:59" || true
} > "$OUT"

chgrp www-data "$OUT" 2>/dev/null || true
chmod 640 "$OUT" 2>/dev/null || true

logger -t fpm-traffic-snapshot "wrote $OUT ($(wc -l < "$OUT") lines)"
fi

# --- JSON for the portal monitoring UI, backfilled ---------------------------
#
# --all rather than the pool-routed default: the document carries per-pool attribution
# inside it, so the wider scope is a superset and one file answers both questions. It
# also covers [www], which the pool-routed filter drops entirely.
#
# 14 days because that is logrotate's `rotate 14`. Days with no recoverable data stay
# missing and are retried until they fall out of this window, which is self-limiting.
made=0
n=1
while [ "$n" -le 14 ]; do
	d_iso=$(date -d "$n days ago" +%Y-%m-%d)
	d_log=$(date -d "$n days ago" +%y-%m-%d)
	n=$((n + 1))

	json="$DIR/traffic-$d_iso.json"
	[ -e "$json" ] && continue

	tmp="$json.tmp.$$"
	if ! zcat -f "$@" | "$REPORT" --all --json \
		--since "$d_log 00:00" --until "$d_log 23:59:59" > "$tmp" 2>/dev/null; then
		rm -f "$tmp"
		continue
	fi

	# A document with parsed == 0 is what a LogFormat change, a wrong path or a day
	# that has rotated away all look like -- and it is valid JSON that exits 0, so
	# `[ -s ]` would happily persist it and then skip the day forever. This is the
	# same guard fpm-daily-rollup needed for the same reason.
	if grep -q '"parsed": 0,' "$tmp"; then
		rm -f "$tmp"
		continue
	fi

	mv -f "$tmp" "$json"
	chgrp www-data "$json" 2>/dev/null || true
	chmod 640 "$json" 2>/dev/null || true
	made=$((made + 1))
done

[ "$made" -gt 0 ] && logger -t fpm-traffic-snapshot "wrote $made traffic JSON file(s)"

exit 0
