clean up logger
This commit is contained in:
1 parent
71d3e789ff
commit
74783fd738
15 files changed
+61
-47
No files matched your search
@@ -1,12 +1,12 @@
|
||||
import type { NextFunction, Request, Response } from "express";
|
||||
import { MulterError } from "multer";
|
||||
import { logger } from "@/utils/logger.js";
|
||||
import type { ILogger } from "@/utils/logger.js";
|
||||
import { AppError } from "@/utils/AppError.js";
|
||||
|
||||
// Multer throws MulterError, send 413 Payload Too Large / 400
|
||||
const multerErrorStatus = (error: MulterError): number => (error.code === "LIMIT_FILE_SIZE" ? 413 : 400);
|
||||
|
||||
const handleErrors = (error: unknown, req: Request, res: Response, _next: NextFunction) => {
|
||||
const handleErrors = (logger: ILogger) => (error: unknown, req: Request, res: Response, _next: NextFunction) => {
|
||||
const status = error instanceof MulterError ? multerErrorStatus(error) : error instanceof AppError ? error.status || 500 : 500;
|
||||
const message = error instanceof AppError ? error.message : error instanceof Error ? error.message : "Server error";
|
||||
const service = error instanceof AppError ? error.service : error instanceof MulterError ? "uploadMiddleware" : "unknownService";
|
||||
|
||||
+1
-1
@@ -112,6 +112,6 @@ export const createApp = ({
|
||||
app.get("*", (req, res) => {
|
||||
res.sendFile(path.join(frontendPath, "index.html"));
|
||||
});
|
||||
app.use(handleErrors);
|
||||
app.use(handleErrors(services.logger));
|
||||
return app;
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
import { DbTypes, QueueModes } from "@/domain/app-settings/app-settings.type.js";
|
||||
import { booleanCoercion } from "@/api/validation/shared.js";
|
||||
import { ILogger } from "@/utils/logger.js";
|
||||
|
||||
const envSchema = z.object({
|
||||
// Server Configuration
|
||||
@@ -31,24 +32,39 @@ const envSchema = z.object({
|
||||
|
||||
export type ValidatedEnv = z.infer<typeof envSchema>;
|
||||
|
||||
export const validateEnv = (): ValidatedEnv => {
|
||||
export const validateEnv = (logger: ILogger): ValidatedEnv => {
|
||||
const result = envSchema.safeParse(process.env);
|
||||
|
||||
if (!result.success) {
|
||||
console.error("Environment validation failed:");
|
||||
console.error("");
|
||||
logger.error({
|
||||
message: "Env validation failed",
|
||||
method: "validateEnv",
|
||||
service: "Server",
|
||||
});
|
||||
|
||||
const errors = result.error.format();
|
||||
for (const [key, value] of Object.entries(errors)) {
|
||||
if (key !== "_errors" && value && typeof value === "object" && "_errors" in value) {
|
||||
console.error(` ${key}: ${value._errors.join(", ")}`);
|
||||
logger.error({
|
||||
message: `${key}: ${value._errors.join(", ")}`,
|
||||
method: "validateEnv",
|
||||
service: "Server",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
console.error("Please check your .env file and ensure all required variables are set.");
|
||||
logger.error({
|
||||
message: "Please check your .env file and ensure all required variables are set.",
|
||||
method: "validateEnv",
|
||||
service: "Server",
|
||||
});
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log("Environment variables validated successfully");
|
||||
logger.info({
|
||||
message: "Environment variables validated successfully",
|
||||
method: "validateEnv",
|
||||
service: "Server",
|
||||
});
|
||||
return result.data;
|
||||
};
|
||||
@@ -42,7 +42,7 @@ class MongoDB implements IDb<Mongoose> {
|
||||
// the unique index on MonitorStats.monitorId is enforced, otherwise databases
|
||||
// with stray duplicates (caused by the pre-fix race in updateRunningStats)
|
||||
// will crash at startup when the unique index fails to build.
|
||||
await runMigrations();
|
||||
await runMigrations(this.logger);
|
||||
|
||||
// Sync indexes
|
||||
const models = mongoose.modelNames();
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { MonitorStatsModel } from "../../domain/monitor-stats/monitor-stats.model.js";
|
||||
import { logger } from "@/utils/logger.js";
|
||||
import type { ILogger } from "@/utils/logger.js";
|
||||
|
||||
/**
|
||||
* Cleanup duplicate MonitorStats documents
|
||||
* Keeps the most recent document (by updatedAt) for each monitorId
|
||||
* and deletes all older duplicates
|
||||
*/
|
||||
export async function cleanupDuplicateMonitorStats(): Promise<void> {
|
||||
export async function cleanupDuplicateMonitorStats(logger: ILogger): Promise<void> {
|
||||
const SERVICE_NAME = "Migration:CleanupDuplicateMonitorStats";
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { MonitorModel } from "../../domain/monitors/monitor.model.js";
|
||||
import { logger } from "@/utils/logger.js";
|
||||
import type { ILogger } from "@/utils/logger.js";
|
||||
|
||||
/**
|
||||
* Fix infrastructure monitors that had thresholds incorrectly set to 0 or 5
|
||||
* due to the old default value. Updates them to 100 (disabled).
|
||||
*/
|
||||
export async function fixInfrastructureThresholds(): Promise<void> {
|
||||
export async function fixInfrastructureThresholds(logger: ILogger): Promise<void> {
|
||||
const SERVICE_NAME = "Migration:FixInfrastructureThresholds";
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import mongoose from "mongoose";
|
||||
import { logger } from "@/utils/logger.js";
|
||||
import type { ILogger } from "@/utils/logger.js";
|
||||
import StatusPageModel from "../../domain/status-pages/status-page.model.js";
|
||||
|
||||
export async function migrateStatusPageTypeToArray(): Promise<void> {
|
||||
export async function migrateStatusPageTypeToArray(logger: ILogger): Promise<void> {
|
||||
const SERVICE_NAME = "Migration:MigrateStatusPageTypeToArray";
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { MonitorStatsModel } from "../../domain/monitor-stats/monitor-stats.model.js";
|
||||
import { logger } from "@/utils/logger.js";
|
||||
import type { ILogger } from "@/utils/logger.js";
|
||||
|
||||
/**
|
||||
* Second-pass dedupe of MonitorStats before the unique index on monitorId is enforced.
|
||||
@@ -11,7 +11,7 @@ import { logger } from "@/utils/logger.js";
|
||||
* Keeps the document with the highest totalChecks for each monitorId (most data) and
|
||||
* deletes the rest. Falls back to most recent updatedAt when totalChecks tie.
|
||||
*/
|
||||
export async function cleanupDuplicateMonitorStatsForUniqueIndex(): Promise<void> {
|
||||
export async function cleanupDuplicateMonitorStatsForUniqueIndex(logger: ILogger): Promise<void> {
|
||||
const SERVICE_NAME = "Migration:CleanupDuplicateMonitorStatsForUniqueIndex";
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import mongoose from "mongoose";
|
||||
import type { AnyBulkWriteOperation, Document } from "mongodb";
|
||||
import { logger } from "@/utils/logger.js";
|
||||
import type { ILogger } from "@/utils/logger.js";
|
||||
import MaintenanceWindowModel from "../../domain/maintenance-windows/maintenance-window.model.js";
|
||||
|
||||
type GroupResult = {
|
||||
@@ -18,7 +18,7 @@ type GroupResult = {
|
||||
monitorIds: mongoose.Types.ObjectId[];
|
||||
};
|
||||
|
||||
export async function migrateMaintenanceWindowMonitorIdToArray(): Promise<void> {
|
||||
export async function migrateMaintenanceWindowMonitorIdToArray(logger: ILogger): Promise<void> {
|
||||
const SERVICE_NAME = "Migration:MigrateMaintenanceWindowMonitorIdToArray";
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import mongoose from "mongoose";
|
||||
import { MonitorModel } from "../../domain/monitors/monitor.model.js";
|
||||
import { logger } from "@/utils/logger.js";
|
||||
import type { ILogger } from "@/utils/logger.js";
|
||||
|
||||
const JOBS_COLLECTION = "jobs";
|
||||
|
||||
@@ -9,7 +9,7 @@ const JOBS_COLLECTION = "jobs";
|
||||
*
|
||||
* Stamp the upgrade moment, not 0. If set to 0, all checks will be replayed
|
||||
*/
|
||||
export async function backfillMonitorLastEvaluatedAt(): Promise<void> {
|
||||
export async function backfillMonitorLastEvaluatedAt(logger: ILogger): Promise<void> {
|
||||
const SERVICE_NAME = "Migration:BackfillMonitorLastEvaluatedAt";
|
||||
|
||||
try {
|
||||
|
||||
@@ -11,7 +11,7 @@ import type { ILogger } from "@/utils/logger.js";
|
||||
|
||||
type MigrationEntry = {
|
||||
name: string;
|
||||
execute: () => Promise<void>;
|
||||
execute: (logger: ILogger) => Promise<void>;
|
||||
};
|
||||
|
||||
const migrations: MigrationEntry[] = [
|
||||
@@ -25,34 +25,34 @@ const migrations: MigrationEntry[] = [
|
||||
{ name: "0008_backfillMonitorLastEvaluatedAt", execute: backfillMonitorLastEvaluatedAt },
|
||||
];
|
||||
|
||||
const runMigrations = async (logger?: ILogger) => {
|
||||
const runMigrations = async (logger: ILogger) => {
|
||||
try {
|
||||
logger?.info({ message: "Running migrations", service: "Migrations" });
|
||||
logger.info({ message: "Running migrations", service: "Migrations" });
|
||||
for (const migration of migrations) {
|
||||
const exists = await MigrationModel.findOne({ name: migration.name, status: "completed" });
|
||||
if (exists) {
|
||||
logger?.info({ message: `Skipping ${migration.name}`, service: "Migrations" });
|
||||
logger.info({ message: `Skipping ${migration.name}`, service: "Migrations" });
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
await migration.execute();
|
||||
await migration.execute(logger);
|
||||
await MigrationModel.findOneAndUpdate(
|
||||
{ name: migration.name },
|
||||
{ status: "completed", completedAt: new Date(), error: undefined },
|
||||
{ upsert: true }
|
||||
);
|
||||
logger?.info({ message: `Completed ${migration.name}`, service: "Migrations" });
|
||||
logger.info({ message: `Completed ${migration.name}`, service: "Migrations" });
|
||||
} catch (error) {
|
||||
const err = error as Error;
|
||||
await MigrationModel.findOneAndUpdate({ name: migration.name }, { status: "failed", error: err?.message }, { upsert: true });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
logger?.info({ message: "Migrations completed", service: "Migrations" });
|
||||
logger.info({ message: "Migrations completed", service: "Migrations" });
|
||||
} catch (error) {
|
||||
const err = error as Error;
|
||||
logger?.error({ message: "Migration failed", service: "Migrations", details: { error: err?.message }, stack: err?.stack });
|
||||
logger.error({ message: "Migration failed", service: "Migrations", details: { error: err?.message }, stack: err?.stack });
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -197,7 +197,7 @@ const checkSnapshotSchema = new Schema<CheckSnapshotDocument>(
|
||||
audits: { type: snapshotAuditsSchema },
|
||||
createdAt: { type: Date, required: true },
|
||||
},
|
||||
{ _id: false }
|
||||
{ _id: false, suppressReservedKeysWarning: true }
|
||||
);
|
||||
|
||||
const MonitorSchema = new Schema<MonitorDocument>(
|
||||
|
||||
+4
-3
@@ -22,10 +22,11 @@ const startApp = async () => {
|
||||
// Basic setup
|
||||
// ***********************
|
||||
|
||||
const env = validateEnv();
|
||||
logger = new Logger();
|
||||
const env = validateEnv(logger);
|
||||
logger.setLogLevel(env.LOG_LEVEL);
|
||||
const settingsService = new SettingsService(env);
|
||||
const envSettings = settingsService.loadSettings();
|
||||
logger = new Logger({ envSettings });
|
||||
const { queueMode, queuePrimaryProcesses } = envSettings;
|
||||
|
||||
logger.info({
|
||||
@@ -51,7 +52,7 @@ const startApp = async () => {
|
||||
if (queueMode === "worker") {
|
||||
const { worker } = await buildWorker(shared, envSettings);
|
||||
logger.info({ message: "Worker instance started. API will not be started", service: SERVICE_NAME });
|
||||
initShutdownListener(null, { worker, db: shared.db });
|
||||
initShutdownListener(null, { worker, db: shared.db, logger });
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { IDb } from "@/db/db.interface.js";
|
||||
import { logger } from "./utils/logger.js";
|
||||
import type { ILogger } from "@/utils/logger.js";
|
||||
import type { Server } from "http";
|
||||
import { Mongoose } from "mongoose";
|
||||
import { IJobScheduler } from "@/worker/worker.interface.js";
|
||||
@@ -7,9 +7,11 @@ import { IJobScheduler } from "@/worker/worker.interface.js";
|
||||
type ShutdownTargets = {
|
||||
worker: IJobScheduler;
|
||||
db: IDb<Mongoose>;
|
||||
logger: ILogger;
|
||||
};
|
||||
export const initShutdownListener = (server: Server | null, services: ShutdownTargets) => {
|
||||
const SERVICE_NAME = "Server";
|
||||
const logger = services.logger;
|
||||
|
||||
let isShuttingDown = false;
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { createLogger, format, transports, Logger as WinstonLogger } from "winston";
|
||||
import type { Logform } from "winston";
|
||||
import dotenv from "dotenv";
|
||||
import { EnvConfig } from "@/domain/app-settings/app-settings.service.js";
|
||||
dotenv.config();
|
||||
|
||||
const SERVICE_NAME = "Logger";
|
||||
@@ -28,18 +27,17 @@ export interface ILogger {
|
||||
cacheLog(entry: LogEntry): void;
|
||||
getLogs(): LogEntry[];
|
||||
buildLogEntry(level: string, config: LogConfig): LogEntry;
|
||||
setLogLevel(level: string): void;
|
||||
}
|
||||
|
||||
class Logger implements ILogger {
|
||||
static SERVICE_NAME = SERVICE_NAME;
|
||||
|
||||
private logger: WinstonLogger;
|
||||
private envSettings: Partial<EnvConfig>;
|
||||
private logCache: LogEntry[];
|
||||
private maxCacheSize: number;
|
||||
|
||||
constructor({ envSettings }: { envSettings: Partial<EnvConfig> }) {
|
||||
this.envSettings = envSettings;
|
||||
constructor() {
|
||||
this.logCache = [];
|
||||
this.maxCacheSize = 1000;
|
||||
const consoleFormat = format.printf((info: Logform.TransformableInfo) => {
|
||||
@@ -84,10 +82,8 @@ class Logger implements ILogger {
|
||||
return msg;
|
||||
});
|
||||
|
||||
const logLevel = this.envSettings.logLevel || "info";
|
||||
|
||||
this.logger = createLogger({
|
||||
level: logLevel,
|
||||
level: "info",
|
||||
format: format.combine(format.timestamp()),
|
||||
transports: [
|
||||
new transports.Console({
|
||||
@@ -151,11 +147,10 @@ class Logger implements ILogger {
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
setLogLevel(level: string): void {
|
||||
this.logger.level = level || "info";
|
||||
}
|
||||
}
|
||||
|
||||
export default Logger;
|
||||
|
||||
// Legacy logger
|
||||
const logger = new Logger({ envSettings: { logLevel: "debug" } });
|
||||
export { logger };
|
||||
export type { LogConfig, LogEntry };
|
||||
Reference in new issue
Block a user