feat: JSON access log in the platform logging contract
All checks were successful
/ checks (push) Successful in 38s
/ deploy (push) Successful in 4s

This commit is contained in:
nholtware 2026-11-05 10:54:12 -06:00
parent fc7ebd5798
commit 444044b99a
5 changed files with 46 additions and 1 deletions

View file

@ -5,5 +5,6 @@
- Routes: table in `src/routes.tsx`, one component per file in `src/routes/`, loaders exported next to their component.
- Runtime config: read only via `getConfig()` (`src/lib/config.ts`). A new browser-visible env var must be added to `VARS` in `docker-entrypoint.d/10-config.sh`, `AppConfig`, and the gitops Deployment. No secrets: config.js is public.
- Backend calls: `apiGet()` in `src/lib/api.ts`, relative to `API_BASE_URL` (`/api` same-host ingress path, stripped before the backend). No CORS.
- Logging: nginx access log is JSON in the platform logging contract (`log_format contract` in `nginx/default.conf`; `$app_name`/`$git_sha` from `conf.d/service-vars.conf` written by `10-config.sh`). Keep field names identical to the backend template's pino-http lines.
- Serving contract: port 8080, `/healthz` 200, SPA fallback, immutable `/assets/`, no-cache `index.html`/`config.js` (`nginx/default.conf`).
- Deploy: push to `main` -> Forgejo CI -> image -> gitops bump -> ArgoCD. Never `kubectl apply` by hand.

View file

@ -15,4 +15,7 @@ COPY nginx/default.conf /etc/nginx/conf.d/default.conf
COPY --chmod=0755 docker-entrypoint.d/10-config.sh /docker-entrypoint.d/10-config.sh
# html dir owned by nginx so 10-config.sh can write config.js at start.
COPY --from=build --chown=101:101 /app/dist /usr/share/nginx/html
# Logged as "version" in every access-log line.
ARG GIT_SHA=dev
ENV GIT_SHA=${GIT_SHA}
EXPOSE 8080

View file

@ -34,6 +34,13 @@ Everything in `config.js` is public. Never put secrets there.
- nginx listens on 8080: `/healthz` returns 200, unknown paths fall back to
`index.html`, `/assets/*` are cached as immutable, and `index.html` and
`config.js` are not cached.
- Access log: one JSON line per request in the platform logging contract
(`level`, `time`, `service` = `APP_NAME`, `version` = `GIT_SHA`, `req.id`
(echoes `X-Request-Id`), `req.method`, `req.url`, `res.statusCode`,
`responseTime` in ms, `msg: "request completed"`), the same shape as the backend
template. In Kibana (http://kibana.192.168.1.83.nip.io, data view `kube-*`):
`service:"hello-web" and res.statusCode >= 400`. `/healthz` isn't logged.
nginx's own startup/error lines stay plain text (field `log`).
## Deploy

View file

@ -23,4 +23,12 @@ for name in $VARS; do
done
printf 'window.__APP_CONFIG__ = {%s};\n' "$body" > "$OUT"
echo "10-config.sh: wrote $OUT"
# nginx variables for the JSON access log (nginx/default.conf). Restricted
# charset: these land inside nginx string literals.
safe() { printf '%s' "$1" | tr -cd 'A-Za-z0-9._-'; }
cat > /etc/nginx/conf.d/service-vars.conf <<NGINX
map \$host \$app_name { default "$(safe "$APP_NAME")"; }
map \$host \$git_sha { default "$(safe "${GIT_SHA:-dev}")"; }
NGINX
echo "10-config.sh: wrote $OUT and service-vars.conf"

View file

@ -1,7 +1,33 @@
# Access log in the platform logging contract (same shape as the backend
# template's pino-http lines), so Fluent Bit turns every key into a Kibana
# field. $app_name / $git_sha come from conf.d/service-vars.conf, written at
# container start by docker-entrypoint.d/10-config.sh.
map $status $log_level {
~^5 error;
default info;
}
# Echo X-Request-Id if the caller sent one, else nginx's own id.
map $http_x_request_id $req_id {
"" $request_id;
default $http_x_request_id;
}
# $request_time is seconds ("0.002"); the contract's responseTime is integer ms.
map $request_time $response_ms {
"~^0\.0*(?<ms>\d+)$" $ms;
"~^(?<s>[1-9]\d*)\.(?<ms>\d{3})$" $s$ms;
default 0;
}
log_format contract escape=json
'{"level":"$log_level","time":"$time_iso8601","service":"$app_name",'
'"version":"$git_sha","req":{"id":"$req_id","method":"$request_method",'
'"url":"$request_uri"},"res":{"statusCode":$status},'
'"responseTime":$response_ms,"msg":"request completed"}';
server {
listen 8080;
server_name _;
root /usr/share/nginx/html;
access_log /dev/stdout contract;
# Liveness/readiness for k8s probes.
location = /healthz {