feat: add frontend logger module with level filtering and sanitization (refs #84)
All checks were successful
Deploy to Staging / Build Images (pull_request) Successful in 3m13s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 21s
Deploy to Staging / Verify Staging (pull_request) Successful in 2m25s
Deploy to Staging / Notify Staging Ready (pull_request) Successful in 8s
Deploy to Staging / Notify Staging Failure (pull_request) Has been skipped
All checks were successful
Deploy to Staging / Build Images (pull_request) Successful in 3m13s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 21s
Deploy to Staging / Verify Staging (pull_request) Successful in 2m25s
Deploy to Staging / Notify Staging Ready (pull_request) Successful in 8s
Deploy to Staging / Notify Staging Failure (pull_request) Has been skipped
- Create centralized logger utility at frontend/src/utils/logger.ts - Support debug/info/warn/error levels controlled by VITE_LOG_LEVEL - Sanitize sensitive data (tokens, passwords, secrets) in log output - Graceful fallback to 'info' level for invalid VITE_LOG_LEVEL values - Add VITE_LOG_LEVEL to ImportMetaEnv type definitions Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
94
frontend/src/utils/logger.ts
Normal file
94
frontend/src/utils/logger.ts
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
/**
|
||||||
|
* @ai-summary Centralized frontend logger with level filtering and sanitization
|
||||||
|
* @ai-context Use this instead of console.log for consistent logging
|
||||||
|
*/
|
||||||
|
|
||||||
|
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
||||||
|
|
||||||
|
const LEVELS: Record<LogLevel, number> = {
|
||||||
|
debug: 0,
|
||||||
|
info: 1,
|
||||||
|
warn: 2,
|
||||||
|
error: 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
const validLevels: LogLevel[] = ['debug', 'info', 'warn', 'error'];
|
||||||
|
|
||||||
|
const rawLevel = (
|
||||||
|
import.meta.env.VITE_LOG_LEVEL || 'info'
|
||||||
|
).toLowerCase() as LogLevel;
|
||||||
|
const LOG_LEVEL: LogLevel = validLevels.includes(rawLevel) ? rawLevel : 'info';
|
||||||
|
|
||||||
|
// Warn once on startup if invalid level
|
||||||
|
if (import.meta.env.VITE_LOG_LEVEL && rawLevel !== LOG_LEVEL) {
|
||||||
|
console.warn(
|
||||||
|
`[Logger] Invalid VITE_LOG_LEVEL "${import.meta.env.VITE_LOG_LEVEL}", using "info"`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const shouldLog = (level: LogLevel): boolean => LEVELS[level] >= LEVELS[LOG_LEVEL];
|
||||||
|
|
||||||
|
// Sanitize sensitive data from logs
|
||||||
|
const sensitiveKeys = [
|
||||||
|
'token',
|
||||||
|
'password',
|
||||||
|
'secret',
|
||||||
|
'authorization',
|
||||||
|
'apikey',
|
||||||
|
'credential',
|
||||||
|
];
|
||||||
|
|
||||||
|
const sanitize = (meta?: object): object | undefined => {
|
||||||
|
if (!meta) return undefined;
|
||||||
|
const sanitized = { ...meta };
|
||||||
|
for (const key of Object.keys(sanitized)) {
|
||||||
|
if (sensitiveKeys.some((s) => key.toLowerCase().includes(s))) {
|
||||||
|
(sanitized as Record<string, unknown>)[key] = '[REDACTED]';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sanitized;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const logger = {
|
||||||
|
debug: (msg: string, meta?: object): void => {
|
||||||
|
if (shouldLog('debug')) {
|
||||||
|
try {
|
||||||
|
console.debug(`[DEBUG] ${msg}`, sanitize(meta) ?? '');
|
||||||
|
} catch {
|
||||||
|
/* Fail silently */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
info: (msg: string, meta?: object): void => {
|
||||||
|
if (shouldLog('info')) {
|
||||||
|
try {
|
||||||
|
console.log(`[INFO] ${msg}`, sanitize(meta) ?? '');
|
||||||
|
} catch {
|
||||||
|
/* Fail silently */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
warn: (msg: string, meta?: object): void => {
|
||||||
|
if (shouldLog('warn')) {
|
||||||
|
try {
|
||||||
|
console.warn(`[WARN] ${msg}`, sanitize(meta) ?? '');
|
||||||
|
} catch {
|
||||||
|
/* Fail silently */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
error: (msg: string, meta?: object): void => {
|
||||||
|
if (shouldLog('error')) {
|
||||||
|
try {
|
||||||
|
console.error(`[ERROR] ${msg}`, sanitize(meta) ?? '');
|
||||||
|
} catch {
|
||||||
|
/* Fail silently */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default logger;
|
||||||
1
frontend/src/vite-env.d.ts
vendored
1
frontend/src/vite-env.d.ts
vendored
@@ -5,6 +5,7 @@ interface ImportMetaEnv {
|
|||||||
readonly VITE_AUTH0_DOMAIN: string
|
readonly VITE_AUTH0_DOMAIN: string
|
||||||
readonly VITE_AUTH0_CLIENT_ID: string
|
readonly VITE_AUTH0_CLIENT_ID: string
|
||||||
readonly VITE_AUTH0_AUDIENCE: string
|
readonly VITE_AUTH0_AUDIENCE: string
|
||||||
|
readonly VITE_LOG_LEVEL?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ImportMeta {
|
interface ImportMeta {
|
||||||
|
|||||||
Reference in New Issue
Block a user