be4d43105e
Node.js/Express backend + vanilla JS frontend. Features: real-time collaborative whiteboard (SSE), multi-page support, LaTeX formulas, shapes/connectors, coordinate systems, number lines, compass, zoom/pan, Catmull-Rom pencil smoothing, ruler/protractor with rotation & resize controls, minimap navigation overlay, auto-measurements, multi-page thumbnails sidebar, PNG export, page templates. Student/teacher workflows: classes, assignments, library, dashboard. Mobile responsive. SQLite (better-sqlite3). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
62 lines
2.0 KiB
JavaScript
62 lines
2.0 KiB
JavaScript
'use strict';
|
|
|
|
/* ── Structured logger — JSON in prod, pretty in dev ──────────────────────
|
|
Usage: logger.info('msg', { key: val })
|
|
logger.error('msg', { err: e.message })
|
|
──────────────────────────────────────────────────────────────────────── */
|
|
|
|
const LEVELS = { error: 0, warn: 1, info: 2, debug: 3 };
|
|
|
|
const COLORS = {
|
|
error: '\x1b[31m', // red
|
|
warn: '\x1b[33m', // yellow
|
|
info: '\x1b[36m', // cyan
|
|
debug: '\x1b[90m', // gray
|
|
};
|
|
const RESET = '\x1b[0m';
|
|
|
|
const isProd = process.env.NODE_ENV === 'production';
|
|
|
|
function _currentLevel() {
|
|
const env = process.env.LOG_LEVEL;
|
|
if (env && LEVELS[env] !== undefined) return LEVELS[env];
|
|
return isProd ? LEVELS.info : LEVELS.debug;
|
|
}
|
|
|
|
function log(level, msg, meta) {
|
|
if (LEVELS[level] > _currentLevel()) return;
|
|
|
|
const ts = new Date().toISOString();
|
|
|
|
if (isProd) {
|
|
/* JSON — one line per entry, parseable by log aggregators */
|
|
const entry = { level, ts, msg };
|
|
if (meta && typeof meta === 'object') Object.assign(entry, meta);
|
|
process.stdout.write(JSON.stringify(entry) + '\n');
|
|
} else {
|
|
/* Pretty — coloured label + message + optional meta */
|
|
const color = COLORS[level] || '';
|
|
const label = `[${ts}] ${color}${level.toUpperCase().padEnd(5)}${RESET}`;
|
|
const metaStr = meta && Object.keys(meta).length
|
|
? ' ' + JSON.stringify(meta, null, 0)
|
|
: '';
|
|
process.stdout.write(`${label} ${msg}${metaStr}\n`);
|
|
}
|
|
}
|
|
|
|
const logger = {
|
|
error: (msg, meta) => log('error', msg, meta),
|
|
warn: (msg, meta) => log('warn', msg, meta),
|
|
info: (msg, meta) => log('info', msg, meta),
|
|
debug: (msg, meta) => log('debug', msg, meta),
|
|
|
|
/* Convenience: log an Error object */
|
|
exception: (msg, err, meta) => log('error', msg, {
|
|
err: err?.message,
|
|
stack: !isProd ? err?.stack : undefined,
|
|
...meta,
|
|
}),
|
|
};
|
|
|
|
module.exports = logger;
|