watchparty/server/src/Logger.ts
Nicolas 1e4a82df16
All checks were successful
Publier l'image Docker / test (push) Successful in 24s
Publier l'image Docker / publish (push) Successful in 24s
Log Emby's real status + response body on any proxied call failure
Every attempt to see Emby's actual error message by hand failed the same
way: hls.js's own error reporting only ever exposes the generic HTTP
reason phrase, never the real response body, and Emby's own live log
view didn't reliably surface the exception's headline message either -
across several different items and failure modes this session. Emby's
own access log confirmed a real, several-KB text body really was being
sent every time (Content-Type: text/plain, Content-Length: 5190) - it
just never survived being reported back through either of those paths.

Fixed at the root: EmbyClient.fetch - the one function every Emby call in
this app goes through (fetchJson's JSON API calls and proxyResponse's
streaming/image proxy alike) - now logs Emby's status and full response
body itself straight to docker logs whenever a call fails.

- server/src/emby/EmbyClient.ts: new private logErrorResponse, called
  from fetch() only when !res.ok. Reads the body via .clone() so the real
  caller still gets an untouched, fully readable response (a body can
  only be consumed once, and this had to not break either of the two very
  different ways this app's callers read one - fetchJson's .json(),
  proxyResponse's .text()/pipe). pathAndQuery is logged with api_key
  redacted - this can be called with Emby's own TranscodingUrl verbatim,
  which can carry one (same concern as CatalogService's logStreamRequest).
- server/src/Logger.ts: new emby() method, same shape as party()/stream().
- server/test/embyClient.test.ts (new): body/status logged and readable
  by the real caller afterward, api_key redaction, and silence on a
  successful response (only failures are worth logging, same discipline
  as this app's existing logging).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-14 15:03:17 +02:00

57 lines
2.5 KiB
TypeScript

'use strict';
/**
* Minimal server-side logging - plain stdout lines, no logging library.
* This app runs as a single Docker container watched via `docker logs`/
* journalctl (see README's Deploying section), not behind a log
* aggregator, so a consistent prefix + timestamp is what actually makes
* these greppable in practice, not structured JSON nobody's parsing.
*
* Static methods rather than a singleton instance: there's no state here
* at all (unlike PartyService/MusicQueueService, which really do own
* something) - a class is just a namespace for these two, kept as a class
* for consistency with the rest of server/src/services' shape.
*/
export class Logger {
/**
* Party business-lifecycle events - created/joined/left/destroyed, host
* handoff, queue advance, playstate transitions, pause/skip requests.
* Deliberately covers only that, not connection-level noise like every
* Socket.IO reconnect or the 3s playstate heartbeat re-sending the same
* state (see SocketGateway, which only calls this on an actual
* play/pause transition, not every heartbeat tick).
*/
static party(message: string): void {
console.log(`[watchparty:party] ${new Date().toISOString()} ${message}`);
}
/**
* The streaming route specifically (CatalogService's /master.m3u8
* handler) - a different concern from the party lifecycle above (it's
* about one HTTP request, not party state). Deliberately logs only the
* individual override values it cares about, never the full resolved
* URL or params blob - Emby's own TranscodingUrl can carry an api_key,
* and that has no business ending up in a log file.
*/
static stream(message: string): void {
console.log(`[watchparty:stream] ${new Date().toISOString()} ${message}`);
}
/** `"name(id)"` - the same shape in every party lifecycle log line. */
static who(userId: string, userName: string): string {
return `${userName}(${userId})`;
}
/**
* Emby answering anything but success, to any proxied call - see
* EmbyClient.fetch, the one place every Emby request goes through.
* Added after several rounds of chasing a real error message through
* browser devtools and Emby's own live log view (see docs/architecture.md's
* player-track-selection section): Emby often sends back a real,
* detailed text body on a failure, which neither of those reliably
* surfaced - this puts it directly in `docker logs` instead, from now on.
*/
static emby(message: string): void {
console.log(`[watchparty:emby] ${new Date().toISOString()} ${message}`);
}
}