mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
feat(monitoring): add date range filtering and log cleanup scheduling
- Implement date range filtering for access logs and request statistics - Add log cleanup scheduling with configurable cron expression - Update UI components to support date range selection - Refactor log processing and parsing to handle date filtering - Add new database migration for log cleanup cron configuration - Remove deprecated log rotation management logic
This commit is contained in:
@@ -1,121 +1,77 @@
|
||||
import { IS_CLOUD, paths } from "@dokploy/server/constants";
|
||||
import { type RotatingFileStream, createStream } from "rotating-file-stream";
|
||||
import { paths } from "@dokploy/server/constants";
|
||||
import { execAsync } from "../process/execAsync";
|
||||
import { findAdmin } from "@dokploy/server/services/admin";
|
||||
import { updateUser } from "@dokploy/server/services/user";
|
||||
import { scheduleJob, scheduledJobs } from "node-schedule";
|
||||
|
||||
class LogRotationManager {
|
||||
private static instance: LogRotationManager;
|
||||
private stream: RotatingFileStream | null = null;
|
||||
const LOG_CLEANUP_JOB_NAME = "access-log-cleanup";
|
||||
|
||||
private constructor() {
|
||||
if (IS_CLOUD) {
|
||||
return;
|
||||
}
|
||||
this.initialize().catch(console.error);
|
||||
}
|
||||
|
||||
public static getInstance(): LogRotationManager {
|
||||
if (!LogRotationManager.instance) {
|
||||
LogRotationManager.instance = new LogRotationManager();
|
||||
}
|
||||
return LogRotationManager.instance;
|
||||
}
|
||||
|
||||
private async initialize(): Promise<void> {
|
||||
const isActive = await this.getStateFromDB();
|
||||
if (isActive) {
|
||||
await this.activateStream();
|
||||
}
|
||||
}
|
||||
|
||||
private async getStateFromDB(): Promise<boolean> {
|
||||
const admin = await findAdmin();
|
||||
return admin?.user.enableLogRotation ?? false;
|
||||
}
|
||||
|
||||
private async setStateInDB(active: boolean): Promise<void> {
|
||||
const admin = await findAdmin();
|
||||
if (!admin) {
|
||||
return;
|
||||
}
|
||||
await updateUser(admin.user.id, {
|
||||
enableLogRotation: active,
|
||||
});
|
||||
}
|
||||
|
||||
private async activateStream(): Promise<void> {
|
||||
export const startLogCleanup = async (
|
||||
cronExpression = "0 0 * * *",
|
||||
): Promise<boolean> => {
|
||||
try {
|
||||
const { DYNAMIC_TRAEFIK_PATH } = paths();
|
||||
if (this.stream) {
|
||||
await this.deactivateStream();
|
||||
|
||||
const existingJob = scheduledJobs[LOG_CLEANUP_JOB_NAME];
|
||||
if (existingJob) {
|
||||
existingJob.cancel();
|
||||
}
|
||||
|
||||
this.stream = createStream("access.log", {
|
||||
size: "100M",
|
||||
interval: "1d",
|
||||
path: DYNAMIC_TRAEFIK_PATH,
|
||||
rotate: 6,
|
||||
compress: "gzip",
|
||||
});
|
||||
scheduleJob(LOG_CLEANUP_JOB_NAME, cronExpression, async () => {
|
||||
try {
|
||||
await execAsync(
|
||||
`tail -n 1000 ${DYNAMIC_TRAEFIK_PATH}/access.log > ${DYNAMIC_TRAEFIK_PATH}/access.log.tmp && mv ${DYNAMIC_TRAEFIK_PATH}/access.log.tmp ${DYNAMIC_TRAEFIK_PATH}/access.log`,
|
||||
);
|
||||
|
||||
this.stream.on("rotation", this.handleRotation.bind(this));
|
||||
}
|
||||
|
||||
private async deactivateStream(): Promise<void> {
|
||||
return new Promise<void>((resolve) => {
|
||||
if (this.stream) {
|
||||
this.stream.end(() => {
|
||||
this.stream = null;
|
||||
resolve();
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
await execAsync("docker exec dokploy-traefik kill -USR1 1");
|
||||
} catch (error) {
|
||||
console.error("Error during log cleanup:", error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async activate(): Promise<boolean> {
|
||||
const currentState = await this.getStateFromDB();
|
||||
if (currentState) {
|
||||
return true;
|
||||
const admin = await findAdmin();
|
||||
if (admin) {
|
||||
await updateUser(admin.user.id, {
|
||||
logCleanupCron: cronExpression,
|
||||
});
|
||||
}
|
||||
|
||||
await this.setStateInDB(true);
|
||||
await this.activateStream();
|
||||
return true;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
public async deactivate(): Promise<boolean> {
|
||||
console.log("Deactivating log rotation...");
|
||||
const currentState = await this.getStateFromDB();
|
||||
if (!currentState) {
|
||||
console.log("Log rotation is already inactive in DB");
|
||||
return true;
|
||||
export const stopLogCleanup = async (): Promise<boolean> => {
|
||||
try {
|
||||
const existingJob = scheduledJobs[LOG_CLEANUP_JOB_NAME];
|
||||
if (existingJob) {
|
||||
existingJob.cancel();
|
||||
}
|
||||
|
||||
// Update database
|
||||
const admin = await findAdmin();
|
||||
if (admin) {
|
||||
await updateUser(admin.user.id, {
|
||||
logCleanupCron: null,
|
||||
});
|
||||
}
|
||||
|
||||
await this.setStateInDB(false);
|
||||
await this.deactivateStream();
|
||||
console.log("Log rotation deactivated successfully");
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error stopping log cleanup:", error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
private async handleRotation() {
|
||||
try {
|
||||
const status = await this.getStatus();
|
||||
if (!status) {
|
||||
await this.deactivateStream();
|
||||
}
|
||||
await execAsync(
|
||||
"docker kill -s USR1 $(docker ps -q --filter name=dokploy-traefik)",
|
||||
);
|
||||
console.log("USR1 Signal send to Traefik");
|
||||
} catch (error) {
|
||||
console.error("Error sending USR1 Signal to Traefik:", error);
|
||||
}
|
||||
}
|
||||
public async getStatus(): Promise<boolean> {
|
||||
const dbState = await this.getStateFromDB();
|
||||
return dbState;
|
||||
}
|
||||
}
|
||||
export const logRotationManager = LogRotationManager.getInstance();
|
||||
export const getLogCleanupStatus = async (): Promise<{
|
||||
enabled: boolean;
|
||||
cronExpression: string | null;
|
||||
}> => {
|
||||
const admin = await findAdmin();
|
||||
const cronExpression = admin?.user.logCleanupCron ?? null;
|
||||
return {
|
||||
enabled: cronExpression !== null,
|
||||
cronExpression,
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user