diff --git a/Dockerfile b/Dockerfile index 00b76f2c..edee74eb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,10 +25,11 @@ FROM node:22-alpine AS runner WORKDIR /app # Curl used for the health checks -RUN apk add --no-cache curl +RUN apk add --no-cache curl tzdata # COPY package.json package-lock.json ./ COPY package*.json ./ + RUN npm ci --omit=dev && npm cache clean --force COPY --from=builder /app/.next/standalone ./ diff --git a/server/logger.ts b/server/logger.ts index 15dd6e3f..6d3a539b 100644 --- a/server/logger.ts +++ b/server/logger.ts @@ -5,6 +5,22 @@ import path from "path"; import { APP_PATH } from "./lib/consts"; import telemetryClient from "./lib/telemetry"; +// helper to get ISO8601 string in the TZ from process.env.TZ +// This replaces the default Z (UTC) with the local offset from process.env.TZ +const isoLocal = () => { + const tz = process.env.TZ || "UTC"; + const d = new Date(); + const s = d.toLocaleString("sv-SE", { timeZone: tz, hour12: false }); + const tzOffsetMin = d.getTimezoneOffset(); + const sign = tzOffsetMin <= 0 ? "+" : "-"; + const pad = (n: number) => String(n).padStart(2, "0"); + const hours = pad(Math.floor(Math.abs(tzOffsetMin) / 60)); + const mins = pad(Math.abs(tzOffsetMin) % 60); + + // Replace Z in ISO string with local offset + return s.replace(" ", "T") + `${sign}${hours}:${mins}`; +}; + const hformat = winston.format.printf( ({ level, label, message, timestamp, stack, ...metadata }) => { let msg = `${timestamp} [${level}]${label ? `[${label}]` : ""}: ${message}`; @@ -29,7 +45,12 @@ if (config.getRawConfig().app.save_logs) { maxSize: "20m", maxFiles: "7d", createSymlink: true, - symlinkName: "pangolin.log" + symlinkName: "pangolin.log", + format: winston.format.combine( + winston.format.timestamp({ format: isoLocal }), + winston.format.splat(), + hformat + ) }) ); transports.push( @@ -42,7 +63,7 @@ if (config.getRawConfig().app.save_logs) { createSymlink: true, symlinkName: ".machinelogs.json", format: winston.format.combine( - winston.format.timestamp(), + winston.format.timestamp({ format: isoLocal }), winston.format.splat(), winston.format.json() ) @@ -56,7 +77,9 @@ const logger = winston.createLogger({ winston.format.errors({ stack: true }), winston.format.colorize(), winston.format.splat(), - winston.format.timestamp(), + + // Use isoLocal so timestamps respect TZ env, not just UTC + winston.format.timestamp({ format: isoLocal }), hformat ), transports