@rollercoaster-dev/rd-logger
Version:
A neurodivergent-friendly logger for Rollercoaster.dev projects
128 lines (127 loc) • 4.57 kB
JavaScript
export const DEFAULT_QUERY_LOGGER_CONFIG = {
enabled: process.env.NODE_ENV !== 'production', // Enable by default in non-prod
slowQueryThreshold: 100, // Default slow query threshold (ms)
maxLogs: 1000, // Default max logs to store
logDebugQueries: process.env.DEBUG_QUERIES === 'true', // Log all queries if DEBUG_QUERIES is set
};
/**
* Logs database queries and identifies slow ones.
*/
export class QueryLogger {
constructor(logger, options) {
this.logs = [];
this.logger = logger;
this.config = Object.assign(Object.assign({}, DEFAULT_QUERY_LOGGER_CONFIG), options);
}
/**
* Logs a query execution.
* @param query The SQL query string.
* @param params Query parameters (optional).
* @param duration Query execution duration in milliseconds.
* @param database Optional database type/name.
* @param requestId Optional associated request ID.
*/
logQuery(query, params, duration, database, requestId) {
if (!this.config.enabled)
return;
const entry = {
query,
params,
duration,
timestamp: new Date().toISOString(),
database,
requestId,
};
// Add to logs (with size limit)
this.logs.push(entry);
if (this.logs.length > this.config.maxLogs) {
this.logs.shift(); // Remove oldest entry
}
const context = {
duration: `${duration}ms`,
query,
params: params, // Let the main logger handle stringification
database,
requestId,
};
// Log slow queries
if (duration >= this.config.slowQueryThreshold) {
this.logger.warn(`Slow query detected`, context);
}
// Log all queries if debug logging for queries is enabled
if (this.config.logDebugQueries) {
this.logger.debug(`Database query executed`, context);
}
}
/**
* Gets all logged queries.
* @returns Array of query log entries.
*/
getLogs() {
return [...this.logs]; // Return a copy
}
/**
* Gets slow queries (queries that took longer than the configured threshold).
* @param threshold Optional custom threshold in milliseconds to override config for this call.
* @returns Array of slow query log entries.
*/
getSlowQueries(threshold) {
const actualThreshold = threshold !== null && threshold !== void 0 ? threshold : this.config.slowQueryThreshold;
return this.logs.filter((log) => log.duration >= actualThreshold);
}
/**
* Clears all stored logs.
*/
clearLogs() {
this.logs = [];
}
/**
* Updates the query logger's configuration dynamically.
* @param options Partial configuration options to update.
*/
configure(options) {
this.config = Object.assign(Object.assign({}, this.config), options);
}
/**
* Gets query statistics.
*/
getStats() {
if (this.logs.length === 0) {
return {
totalQueries: 0,
slowQueries: 0,
averageDuration: 0,
maxDuration: 0,
byDatabase: {},
};
}
const totalDuration = this.logs.reduce((sum, log) => sum + log.duration, 0);
const maxDuration = Math.max(...this.logs.map((log) => log.duration));
const slowQueriesCount = this.logs.filter((log) => log.duration >= this.config.slowQueryThreshold).length;
// Group by database
const byDatabase = {};
for (const log of this.logs) {
const dbKey = log.database || 'unknown';
if (!byDatabase[dbKey]) {
byDatabase[dbKey] = { count: 0, totalDuration: 0 };
}
byDatabase[dbKey].count++;
byDatabase[dbKey].totalDuration += log.duration;
}
// Calculate average duration by database
const byDatabaseStats = {};
for (const [db, stats] of Object.entries(byDatabase)) {
byDatabaseStats[db] = {
count: stats.count,
avgDuration: stats.totalDuration > 0 ? stats.totalDuration / stats.count : 0,
};
}
return {
totalQueries: this.logs.length,
slowQueries: slowQueriesCount,
averageDuration: totalDuration > 0 ? totalDuration / this.logs.length : 0,
maxDuration,
byDatabase: byDatabaseStats,
};
}
}