// ============================================================ // FILE LOG // ============================================================ // Appending a line to the dated log file, and nothing else. // // Split out of logger.js because logger requires the database at module load, // so importing it just to record a diagnostic pulls in a connection pool. That // is wrong on its own terms — a note about what happened should not need a // database — and it hung the test suite: a unit test that exercised a code path // containing a log call inherited an open pool handle and never exited. // // fs and the redactor only. logger.file delegates here, so there is one // implementation of where a line goes and how it is redacted. var fs = require('fs'); var path = require('path'); var { redact } = require('./redact'); var LOG_DIR = path.join(__dirname, '../../data/logs'); function write(level, message, data) { try { if (!fs.existsSync(LOG_DIR)) fs.mkdirSync(LOG_DIR, { recursive: true }); var now = new Date(); var file = path.join(LOG_DIR, now.toISOString().split('T')[0] + '.log'); // Defensive redaction: both message and data go through redact() so PHI // patterns cannot reach the file if a caller passes a request body, a // clinical string, or a stack trace containing transcript text. var line = '[' + now.toISOString() + '] [' + level.toUpperCase() + '] ' + redact(String(message == null ? '' : message)); if (data != null) { line += ' | ' + redact(typeof data === 'string' ? data : JSON.stringify(data)); } fs.appendFileSync(file, line + '\n'); if (level === 'error') console.error(line); } catch (e) { /* a diagnostic is never worth failing the caller for */ } } module.exports = { write, LOG_DIR };