fix: adding comprehensive logs for live server (#7947)

* fix: adding comprehensive logs

* fix: document controller naming convention

* fix: axios interception logger
This commit is contained in:
sriram veeraghanta 2025-10-10 18:28:52 +05:30 committed by GitHub
parent 9ce6179421
commit 8cd29c5009
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 112 additions and 72 deletions

View file

@ -22,11 +22,11 @@ export class CollaborationController {
// Set up error handling for the connection // Set up error handling for the connection
ws.on("error", (error: Error) => { ws.on("error", (error: Error) => {
logger.error("WebSocket connection error:", error); logger.error("COLLABORATION_CONTROLLER: WebSocket connection error:", error);
ws.close(1011, "Internal server error"); ws.close(1011, "Internal server error");
}); });
} catch (error) { } catch (error) {
logger.error("WebSocket connection error:", error); logger.error("COLLABORATION_CONTROLLER: WebSocket connection error:", error);
ws.close(1011, "Internal server error"); ws.close(1011, "Internal server error");
} }
} }

View file

@ -1,37 +0,0 @@
import type { Request, Response } from "express";
// plane imports
import { Controller, Post } from "@plane/decorators";
import { logger } from "@plane/logger";
// types
import type { TConvertDocumentRequestBody } from "@/types";
// utils
import { convertHTMLDocumentToAllFormats } from "@/utils";
@Controller("/convert-document")
export class ConvertDocumentController {
@Post("/")
handleConvertDocument(req: Request, res: Response) {
const { description_html, variant } = req.body as TConvertDocumentRequestBody;
try {
if (typeof description_html !== "string" || variant === undefined) {
res.status(400).json({
message: "Missing required fields",
});
return;
}
const { description, description_binary } = convertHTMLDocumentToAllFormats({
document_html: description_html,
variant,
});
res.status(200).json({
description,
description_binary,
});
} catch (error) {
logger.error("Error in /convert-document endpoint:", error);
res.status(500).json({
message: `Internal server error.`,
});
}
}
}

View file

@ -0,0 +1,63 @@
import type { Request, Response } from "express";
import { z } from "zod";
// helpers
import { Controller, Post } from "@plane/decorators";
import { convertHTMLDocumentToAllFormats } from "@plane/editor";
// logger
import { logger } from "@plane/logger";
import { type TConvertDocumentRequestBody } from "@/types";
// Define the schema with more robust validation
const convertDocumentSchema = z.object({
description_html: z
.string()
.min(1, "HTML content cannot be empty")
.refine((html) => html.trim().length > 0, "HTML content cannot be just whitespace")
.refine((html) => html.includes("<") && html.includes(">"), "Content must be valid HTML"),
variant: z.enum(["rich", "document"]),
});
@Controller("/convert-document")
export class DocumentController {
@Post("/")
async convertDocument(req: Request, res: Response) {
try {
// Validate request body
const validatedData = convertDocumentSchema.parse(req.body as TConvertDocumentRequestBody);
const { description_html, variant } = validatedData;
// Process document conversion
const { description, description_binary } = convertHTMLDocumentToAllFormats({
document_html: description_html,
variant,
});
// Return successful response
res.status(200).json({
description,
description_binary,
});
} catch (error) {
if (error instanceof z.ZodError) {
const validationErrors = error.errors.map((err) => ({
path: err.path.join("."),
message: err.message,
}));
logger.error("DOCUMENT_CONTROLLER: Validation error", {
validationErrors,
});
return res.status(400).json({
message: `Validation error`,
context: {
validationErrors,
},
});
} else {
logger.error("DOCUMENT_CONTROLLER: Internal server error", error);
return res.status(500).json({
message: `Internal server error.`,
});
}
}
}
}

View file

@ -1,5 +1,5 @@
import { CollaborationController } from "./collaboration.controller"; import { CollaborationController } from "./collaboration.controller";
import { ConvertDocumentController } from "./convert-document.controller"; import { DocumentController } from "./document.controller";
import { HealthController } from "./health.controller"; import { HealthController } from "./health.controller";
export const CONTROLLERS = [CollaborationController, ConvertDocumentController, HealthController]; export const CONTROLLERS = [CollaborationController, DocumentController, HealthController];

View file

@ -1,5 +1,6 @@
import * as dotenv from "@dotenvx/dotenvx"; import * as dotenv from "@dotenvx/dotenvx";
import { z } from "zod"; import { z } from "zod";
import { logger } from "@plane/logger";
dotenv.config(); dotenv.config();
@ -27,7 +28,7 @@ const envSchema = z.object({
const validateEnv = () => { const validateEnv = () => {
const result = envSchema.safeParse(process.env); const result = envSchema.safeParse(process.env);
if (!result.success) { if (!result.success) {
console.error("❌ Invalid environment variables:", JSON.stringify(result.error.format(), null, 4)); logger.error("❌ Invalid environment variables:", JSON.stringify(result.error.format(), null, 4));
process.exit(1); process.exit(1);
} }
return result.data; return result.data;

View file

@ -38,7 +38,7 @@ const fetchDocument = async ({ context, documentName: pageId }: FetchPayloadWith
// return binary data // return binary data
return binaryData; return binaryData;
} catch (error) { } catch (error) {
logger.error("Error in fetching document", error); logger.error("DATABASE_EXTENSION: Error in fetching document", error);
throw normalizeToError(error, `Failed to fetch document: ${pageId}`); throw normalizeToError(error, `Failed to fetch document: ${pageId}`);
} }
}; };
@ -57,7 +57,7 @@ const storeDocument = async ({ context, state: pageBinaryData, documentName: pag
}; };
await service.updateDescriptionBinary(pageId, payload); await service.updateDescriptionBinary(pageId, payload);
} catch (error) { } catch (error) {
logger.error("Error in updating document:", error); logger.error("DATABASE_EXTENSION: Error in updating document:", error);
throw normalizeToError(error, `Failed to update document: ${pageId}`); throw normalizeToError(error, `Failed to update document: ${pageId}`);
} }
}; };

View file

@ -36,7 +36,7 @@ export const onAuthenticate = async ({
cookie = parsedToken.cookie; cookie = parsedToken.cookie;
} catch (error) { } catch (error) {
// If token parsing fails, fallback to request headers // If token parsing fails, fallback to request headers
logger.error("Token parsing failed, using request headers:", error); logger.error("AUTH: Token parsing failed, using request headers:", error);
} finally { } finally {
// If cookie is still not found, fallback to request headers // If cookie is still not found, fallback to request headers
if (!cookie) { if (!cookie) {
@ -76,7 +76,8 @@ export const handleAuthentication = async ({ cookie, userId }: { cookie: string;
name: user.display_name, name: user.display_name,
}, },
}; };
} catch (_error) { } catch (error) {
logger.error("AUTH: Token parsing failed, using request headers:", error);
throw Error("Authentication unsuccessful!"); throw Error("Authentication unsuccessful!");
} }
}; };

View file

@ -19,12 +19,12 @@ export class RedisManager {
public async initialize(): Promise<void> { public async initialize(): Promise<void> {
if (this.redisClient && this.isConnected) { if (this.redisClient && this.isConnected) {
logger.info("Redis client already initialized and connected"); logger.info("REDIS_MANAGER: client already initialized and connected");
return; return;
} }
if (this.connectionPromise) { if (this.connectionPromise) {
logger.info("Redis connection already in progress, waiting..."); logger.info("REDIS_MANAGER: Redis connection already in progress, waiting...");
await this.connectionPromise; await this.connectionPromise;
return; return;
} }
@ -54,7 +54,7 @@ export class RedisManager {
const redisUrl = this.getRedisUrl(); const redisUrl = this.getRedisUrl();
if (!redisUrl) { if (!redisUrl) {
logger.warn("No Redis URL provided, Redis functionality will be disabled"); logger.warn("REDIS_MANAGER: No Redis URL provided, Redis functionality will be disabled");
this.isConnected = false; this.isConnected = false;
return; return;
} }
@ -70,27 +70,27 @@ export class RedisManager {
// Set up event listeners // Set up event listeners
this.redisClient.on("connect", () => { this.redisClient.on("connect", () => {
logger.info("Redis client connected"); logger.info("REDIS_MANAGER: Redis client connected");
this.isConnected = true; this.isConnected = true;
}); });
this.redisClient.on("ready", () => { this.redisClient.on("ready", () => {
logger.info("Redis client ready"); logger.info("REDIS_MANAGER: Redis client ready");
this.isConnected = true; this.isConnected = true;
}); });
this.redisClient.on("error", (error) => { this.redisClient.on("error", (error) => {
logger.error("Redis client error:", error); logger.error("REDIS_MANAGER: Redis client error:", error);
this.isConnected = false; this.isConnected = false;
}); });
this.redisClient.on("close", () => { this.redisClient.on("close", () => {
logger.warn("Redis client connection closed"); logger.warn("REDIS_MANAGER: Redis client connection closed");
this.isConnected = false; this.isConnected = false;
}); });
this.redisClient.on("reconnecting", () => { this.redisClient.on("reconnecting", () => {
logger.info("Redis client reconnecting..."); logger.info("REDIS_MANAGER: Redis client reconnecting...");
this.isConnected = false; this.isConnected = false;
}); });
@ -99,9 +99,9 @@ export class RedisManager {
// Test the connection // Test the connection
await this.redisClient.ping(); await this.redisClient.ping();
logger.info("Redis connection test successful"); logger.info("REDIS_MANAGER: Redis connection test successful");
} catch (error) { } catch (error) {
logger.error("Failed to initialize Redis client:", error); logger.error("REDIS_MANAGER: Failed to initialize Redis client:", error);
this.isConnected = false; this.isConnected = false;
throw error; throw error;
} finally { } finally {
@ -111,7 +111,7 @@ export class RedisManager {
public getClient(): Redis | null { public getClient(): Redis | null {
if (!this.redisClient || !this.isConnected) { if (!this.redisClient || !this.isConnected) {
logger.warn("Redis client not available or not connected"); logger.warn("REDIS_MANAGER: Redis client not available or not connected");
return null; return null;
} }
return this.redisClient; return this.redisClient;
@ -125,9 +125,9 @@ export class RedisManager {
if (this.redisClient) { if (this.redisClient) {
try { try {
await this.redisClient.quit(); await this.redisClient.quit();
logger.info("Redis client disconnected gracefully"); logger.info("REDIS_MANAGER: Redis client disconnected gracefully");
} catch (error) { } catch (error) {
logger.error("Error disconnecting Redis client:", error); logger.error("REDIS_MANAGER: Error disconnecting Redis client:", error);
// Force disconnect if quit fails // Force disconnect if quit fails
this.redisClient.disconnect(); this.redisClient.disconnect();
} finally { } finally {
@ -150,7 +150,7 @@ export class RedisManager {
} }
return true; return true;
} catch (error) { } catch (error) {
logger.error(`Error setting Redis key ${key}:`, error); logger.error(`REDIS_MANAGER: Error setting Redis key ${key}:`, error);
return false; return false;
} }
} }
@ -162,7 +162,7 @@ export class RedisManager {
try { try {
return await client.get(key); return await client.get(key);
} catch (error) { } catch (error) {
logger.error(`Error getting Redis key ${key}:`, error); logger.error(`REDIS_MANAGER: Error getting Redis key ${key}:`, error);
return null; return null;
} }
} }
@ -175,7 +175,7 @@ export class RedisManager {
await client.del(key); await client.del(key);
return true; return true;
} catch (error) { } catch (error) {
logger.error(`Error deleting Redis key ${key}:`, error); logger.error(`REDIS_MANAGER: Error deleting Redis key ${key}:`, error);
return false; return false;
} }
} }
@ -188,7 +188,7 @@ export class RedisManager {
const result = await client.exists(key); const result = await client.exists(key);
return result === 1; return result === 1;
} catch (error) { } catch (error) {
logger.error(`Error checking Redis key ${key}:`, error); logger.error(`REDIS_MANAGER: Error checking Redis key ${key}:`, error);
return false; return false;
} }
} }
@ -201,7 +201,7 @@ export class RedisManager {
const result = await client.expire(key, ttl); const result = await client.expire(key, ttl);
return result === 1; return result === 1;
} catch (error) { } catch (error) {
logger.error(`Error setting expiry for Redis key ${key}:`, error); logger.error(`REDIS_MANAGER: Error setting expiry for Redis key ${key}:`, error);
return false; return false;
} }
} }

View file

@ -35,15 +35,15 @@ export class Server {
public async initialize(): Promise<void> { public async initialize(): Promise<void> {
try { try {
await redisManager.initialize(); await redisManager.initialize();
logger.info("Redis setup completed"); logger.info("SERVER: Redis setup completed");
const manager = HocusPocusServerManager.getInstance(); const manager = HocusPocusServerManager.getInstance();
this.hocuspocusServer = await manager.initialize(); this.hocuspocusServer = await manager.initialize();
logger.info("HocusPocus setup completed"); logger.info("SERVER: HocusPocus setup completed");
this.setupRoutes(this.hocuspocusServer); this.setupRoutes(this.hocuspocusServer);
this.setupNotFoundHandler(); this.setupNotFoundHandler();
} catch (error) { } catch (error) {
logger.error("Failed to initialize live server dependencies:", error); logger.error("SERVER: Failed to initialize live server dependencies:", error);
throw error; throw error;
} }
} }
@ -89,10 +89,10 @@ export class Server {
public listen() { public listen() {
this.httpServer = this.app this.httpServer = this.app
.listen(this.app.get("port"), () => { .listen(this.app.get("port"), () => {
logger.info(`Plane Live server has started at port ${this.app.get("port")}`); logger.info(`SERVER: Express server has started at port ${this.app.get("port")}`);
}) })
.on("error", (err) => { .on("error", (err) => {
logger.error("Failed to start server:", err); logger.error("SERVER: Failed to start server:", err);
throw err; throw err;
}); });
} }
@ -100,11 +100,11 @@ export class Server {
public async destroy() { public async destroy() {
if (this.hocuspocusServer) { if (this.hocuspocusServer) {
this.hocuspocusServer.closeConnections(); this.hocuspocusServer.closeConnections();
logger.info("HocusPocus connections closed gracefully."); logger.info("SERVER: HocusPocus connections closed gracefully.");
} }
await redisManager.disconnect(); await redisManager.disconnect();
logger.info("Redis connection closed gracefully."); logger.info("SERVER: Redis connection closed gracefully.");
if (this.httpServer) { if (this.httpServer) {
await new Promise<void>((resolve, reject) => { await new Promise<void>((resolve, reject) => {
@ -112,7 +112,7 @@ export class Server {
if (err) { if (err) {
reject(err); reject(err);
} else { } else {
logger.info("Express server closed gracefully."); logger.info("SERVER: Express server closed gracefully.");
resolve(); resolve();
} }
}); });

View file

@ -1,4 +1,5 @@
import axios, { AxiosInstance } from "axios"; import axios, { AxiosInstance } from "axios";
import { logger } from "@plane/logger";
import { env } from "@/env"; import { env } from "@/env";
export abstract class APIService { export abstract class APIService {
@ -13,6 +14,17 @@ export abstract class APIService {
withCredentials: true, withCredentials: true,
timeout: 20000, timeout: 20000,
}); });
this.setupInterceptors();
}
private setupInterceptors() {
this.axiosInstance.interceptors.response.use(
(response) => response,
(error) => {
logger.error("AXIOS_ERROR:", error);
return Promise.reject(error);
}
);
} }
setHeader(key: string, value: string) { setHeader(key: string, value: string) {