feat(logging): wire LOG_DIR through env and align systemd helper
- logger uses config/env for LOG_DIR and LOG_LEVEL (single source of truth) - Document absolute LOG_DIR for wipe/redeploy (Jenkins) in env and .env.example - setup-kord-user-log-file.sh reads LOG_DIR from KORD_HOME/.env and syncs StandardOutput/StandardError/ExecStartPre mkdir to the same path Made-with: Cursor
This commit is contained in:
parent
5280f1987b
commit
dbadd936ca
|
|
@ -9,5 +9,6 @@ DATABASE_URL="postgresql://kord:password@localhost:5432/kord_db?schema=public"
|
||||||
# Logging (log4js — file only under LOG_DIR, no console appender)
|
# Logging (log4js — file only under LOG_DIR, no console appender)
|
||||||
# Levels: trace, debug, info, warn, error, fatal
|
# Levels: trace, debug, info, warn, error, fatal
|
||||||
LOG_LEVEL=info
|
LOG_LEVEL=info
|
||||||
# Daily log files; keep 7 rotated days (see logger.ts). Directory is created at startup if missing.
|
# Log directory (kord.log + dated rotations). Relative = from process cwd; use an absolute path on servers
|
||||||
|
# if the deploy directory is wiped (e.g. Jenkins): LOG_DIR=/var/lib/kord/logs
|
||||||
LOG_DIR=logs
|
LOG_DIR=logs
|
||||||
|
|
@ -1,12 +1,35 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Run ON THE SERVER as the same user that runs kord (e.g. psa), after: ssh psa@server
|
# Run ON THE SERVER as the same user that runs kord (e.g. psa), after: ssh psa@server
|
||||||
# Switches kord user service from journal-only to append logs under ~/kord/logs/kord.log
|
# Switches kord user service from journal-only to append stdout/stderr under LOG_DIR/kord.log
|
||||||
|
# LOG_DIR is read from $KORD_HOME/.env (LOG_DIR=...) when present, else $KORD_HOME/logs.
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
KORD_HOME="${KORD_HOME:-$HOME/kord}"
|
||||||
|
ENV_FILE="${KORD_ENV_FILE:-$KORD_HOME/.env}"
|
||||||
UNIT="${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user/kord.service"
|
UNIT="${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user/kord.service"
|
||||||
LOG_DIR="$HOME/kord/logs"
|
|
||||||
LOG_FILE="$LOG_DIR/kord.log"
|
# Last LOG_DIR= line from .env; strip quotes and ~ ; relative paths are under KORD_HOME
|
||||||
|
resolve_log_dir() {
|
||||||
|
local default="${KORD_HOME}/logs" line raw
|
||||||
|
[[ -f "$ENV_FILE" ]] || { echo "$default"; return; }
|
||||||
|
line="$(grep -E '^[[:space:]]*LOG_DIR[[:space:]]*=' "$ENV_FILE" | tail -n1 || true)"
|
||||||
|
[[ -z "$line" ]] && { echo "$default"; return; }
|
||||||
|
raw="${line#*=}"
|
||||||
|
raw="$(printf '%s' "$raw" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' -e $'s/\r$//')"
|
||||||
|
if [[ "$raw" =~ ^\".*\"$ ]]; then raw="${raw#\"}"; raw="${raw%\"}"; fi
|
||||||
|
if [[ "$raw" =~ ^\'.*\'$ ]]; then raw="${raw#\'}"; raw="${raw%\'}"; fi
|
||||||
|
raw="${raw//\~/$HOME}"
|
||||||
|
[[ -z "$raw" ]] && { echo "$default"; return; }
|
||||||
|
if [[ "$raw" = /* ]]; then
|
||||||
|
echo "$raw"
|
||||||
|
else
|
||||||
|
echo "${KORD_HOME}/${raw#./}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DIR="$(resolve_log_dir)"
|
||||||
|
LOG_FILE="${LOG_DIR}/kord.log"
|
||||||
|
|
||||||
mkdir -p "$LOG_DIR"
|
mkdir -p "$LOG_DIR"
|
||||||
|
|
||||||
|
|
@ -17,17 +40,28 @@ fi
|
||||||
|
|
||||||
cp -a "$UNIT" "${UNIT}.bak.$(date +%Y%m%d%H%M%S)"
|
cp -a "$UNIT" "${UNIT}.bak.$(date +%Y%m%d%H%M%S)"
|
||||||
|
|
||||||
# Replace journal with file append (idempotent if already set)
|
# Point journal or any previous append paths at the log file derived from .env LOG_DIR
|
||||||
sed -i \
|
sed -i \
|
||||||
-e 's|^StandardOutput=journal|StandardOutput=append:'"$LOG_FILE"'|' \
|
-e "s|^StandardOutput=journal|StandardOutput=append:${LOG_FILE}|" \
|
||||||
-e 's|^StandardError=journal|StandardError=append:'"$LOG_FILE"'|' \
|
-e "s|^StandardError=journal|StandardError=append:${LOG_FILE}|" \
|
||||||
|
"$UNIT"
|
||||||
|
sed -i \
|
||||||
|
-e "s|^StandardOutput=append:.*|StandardOutput=append:${LOG_FILE}|" \
|
||||||
|
-e "s|^StandardError=append:.*|StandardError=append:${LOG_FILE}|" \
|
||||||
"$UNIT"
|
"$UNIT"
|
||||||
|
|
||||||
# systemd opens StandardOutput=append before ExecStart; if ~/kord/logs was deleted,
|
# systemd opens StandardOutput=append before ExecStart; missing parent dir → status 209/STDOUT
|
||||||
# the service fails with (code=exited, status=209/STDOUT). Ensure the dir exists first.
|
sed -i '/^ExecStartPre=-\/usr\/bin\/mkdir -p /d' "$UNIT"
|
||||||
if ! grep -qE '^ExecStartPre=.*mkdir.*kord/logs' "$UNIT"; then
|
tmp="$(mktemp)"
|
||||||
sed -i '/^ExecStart=/i ExecStartPre=-/usr/bin/mkdir -p %h/kord/logs' "$UNIT"
|
inserted=0
|
||||||
fi
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||||
|
if [[ "$line" =~ ^ExecStart= ]] && [[ "$inserted" -eq 0 ]]; then
|
||||||
|
printf '%s\n' "ExecStartPre=-/usr/bin/mkdir -p ${LOG_DIR}"
|
||||||
|
inserted=1
|
||||||
|
fi
|
||||||
|
printf '%s\n' "$line"
|
||||||
|
done <"$UNIT" >"$tmp"
|
||||||
|
mv "$tmp" "$UNIT"
|
||||||
|
|
||||||
systemctl --user daemon-reload
|
systemctl --user daemon-reload
|
||||||
systemctl --user restart kord
|
systemctl --user restart kord
|
||||||
|
|
@ -43,4 +77,5 @@ else
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo
|
echo
|
||||||
|
echo "LOG_DIR=$LOG_DIR"
|
||||||
echo "Follow logs: tail -f $LOG_FILE"
|
echo "Follow logs: tail -f $LOG_FILE"
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,11 @@ export const env = {
|
||||||
VOICE_CATEGORY_ID: process.env.VOICE_CATEGORY_ID || '',
|
VOICE_CATEGORY_ID: process.env.VOICE_CATEGORY_ID || '',
|
||||||
/** log4js level: trace | debug | info | warn | error | fatal */
|
/** log4js level: trace | debug | info | warn | error | fatal */
|
||||||
LOG_LEVEL: process.env.LOG_LEVEL || 'info',
|
LOG_LEVEL: process.env.LOG_LEVEL || 'info',
|
||||||
/** Directory for rotated kord.log (see src/utils/logger.ts) */
|
/**
|
||||||
|
* Directory for log4js `kord.log` (created at startup). Relative paths resolve from `process.cwd()`.
|
||||||
|
* For Jenkins or wipe-and-redeploy flows, set an absolute path **outside** the deploy tree (e.g. `/var/lib/kord/logs`)
|
||||||
|
* so logs survive redeploys and match `StandardOutput=append` in systemd if you point it at the same file.
|
||||||
|
*/
|
||||||
LOG_DIR: process.env.LOG_DIR || 'logs',
|
LOG_DIR: process.env.LOG_DIR || 'logs',
|
||||||
INSTANCE_ID: generateInstanceId(),
|
INSTANCE_ID: generateInstanceId(),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,25 @@
|
||||||
import { mkdirSync } from 'fs';
|
import { mkdirSync } from 'fs';
|
||||||
import { config } from 'dotenv';
|
|
||||||
import log4js from 'log4js';
|
import log4js from 'log4js';
|
||||||
import { resolve } from 'path';
|
import { resolve } from 'path';
|
||||||
|
import { env } from '../config/env';
|
||||||
// Load .env before reading LOG_LEVEL / LOG_DIR (same rule as config/env.ts).
|
|
||||||
config({ path: process.env.DOTENV_CONFIG_PATH || resolve(process.cwd(), '.env') });
|
|
||||||
|
|
||||||
const LOG_LEVELS = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'] as const;
|
const LOG_LEVELS = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'] as const;
|
||||||
type LogLevel = (typeof LOG_LEVELS)[number];
|
type LogLevel = (typeof LOG_LEVELS)[number];
|
||||||
|
|
||||||
function resolveLogLevel(): LogLevel {
|
function resolveLogLevel(): LogLevel {
|
||||||
const raw = (process.env.LOG_LEVEL || 'info').toLowerCase();
|
const raw = env.LOG_LEVEL.toLowerCase();
|
||||||
return (LOG_LEVELS as readonly string[]).includes(raw) ? (raw as LogLevel) : 'info';
|
return (LOG_LEVELS as readonly string[]).includes(raw) ? (raw as LogLevel) : 'info';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Resolves LOG_DIR from .env: absolute paths unchanged; relative paths from cwd. */
|
||||||
|
function resolveLogDir(raw: string): string {
|
||||||
|
const trimmed = raw.trim();
|
||||||
|
if (!trimmed) {
|
||||||
|
return resolve('logs');
|
||||||
|
}
|
||||||
|
return resolve(trimmed);
|
||||||
|
}
|
||||||
|
|
||||||
function ensureLogDir(dir: string): void {
|
function ensureLogDir(dir: string): void {
|
||||||
try {
|
try {
|
||||||
mkdirSync(dir, { recursive: true });
|
mkdirSync(dir, { recursive: true });
|
||||||
|
|
@ -24,7 +30,7 @@ function ensureLogDir(dir: string): void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const logDir = resolve((process.env.LOG_DIR || 'logs').trim());
|
const logDir = resolveLogDir(env.LOG_DIR);
|
||||||
const level = resolveLogLevel();
|
const level = resolveLogLevel();
|
||||||
|
|
||||||
ensureLogDir(logDir);
|
ensureLogDir(logDir);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue