feat: implement structured logging for server and client
Server: - Add pino and pino-http for structured logging - Create @Logged class decorator using Proxy pattern for automatic method logging - Add pino redact config for sensitive data (password, token, etc.) - Move AuthMiddleware to controllers folder (per architecture diagram) - Add LoggingMiddleware for HTTP request logging - Replace console.log/error with structured logger in controllers and app.ts - Decorate all repositories and GPTAdapter with @Logged Client: - Add react-native-logs with namespaced loggers (apiLogger, storeLogger) - Add request/response logging to ApiClient with duration tracking
This commit is contained in:
20
apps/server/src/controllers/AuthMiddleware.ts
Normal file
20
apps/server/src/controllers/AuthMiddleware.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { verifyToken, TokenPayload } from "../utils/jwt";
|
||||
|
||||
export interface AuthenticatedRequest extends Request {
|
||||
user?: TokenPayload;
|
||||
}
|
||||
|
||||
export function authenticate(
|
||||
req: AuthenticatedRequest,
|
||||
res: Response,
|
||||
next: NextFunction,
|
||||
): void {
|
||||
// TODO: Implement real JWT verification
|
||||
// Fake user for testing purposes
|
||||
req.user = {
|
||||
userId: "fake-user-id",
|
||||
email: "test@example.com",
|
||||
};
|
||||
next();
|
||||
}
|
||||
@@ -7,7 +7,10 @@ import {
|
||||
GetMessagesOptions,
|
||||
} from "@caldav/shared";
|
||||
import { ChatService } from "../services";
|
||||
import { AuthenticatedRequest } from "../middleware";
|
||||
import { createLogger } from "../logging";
|
||||
import { AuthenticatedRequest } from "./AuthMiddleware";
|
||||
|
||||
const log = createLogger("ChatController");
|
||||
|
||||
export class ChatController {
|
||||
constructor(private chatService: ChatService) {}
|
||||
@@ -19,6 +22,7 @@ export class ChatController {
|
||||
const response = await this.chatService.processMessage(userId, data);
|
||||
res.json(response);
|
||||
} catch (error) {
|
||||
log.error({ error, userId: req.user?.userId }, "Error processing message");
|
||||
res.status(500).json({ error: "Failed to process message" });
|
||||
}
|
||||
}
|
||||
@@ -44,6 +48,7 @@ export class ChatController {
|
||||
);
|
||||
res.json(response);
|
||||
} catch (error) {
|
||||
log.error({ error, conversationId: req.params.conversationId }, "Error confirming event");
|
||||
res.status(500).json({ error: "Failed to confirm event" });
|
||||
}
|
||||
}
|
||||
@@ -59,6 +64,7 @@ export class ChatController {
|
||||
);
|
||||
res.json(response);
|
||||
} catch (error) {
|
||||
log.error({ error, conversationId: req.params.conversationId }, "Error rejecting event");
|
||||
res.status(500).json({ error: "Failed to reject event" });
|
||||
}
|
||||
}
|
||||
@@ -72,6 +78,7 @@ export class ChatController {
|
||||
const conversations = await this.chatService.getConversations(userId);
|
||||
res.json(conversations);
|
||||
} catch (error) {
|
||||
log.error({ error, userId: req.user?.userId }, "Error getting conversations");
|
||||
res.status(500).json({ error: "Failed to get conversations" });
|
||||
}
|
||||
}
|
||||
@@ -102,6 +109,7 @@ export class ChatController {
|
||||
if ((error as Error).message === "Conversation not found") {
|
||||
res.status(404).json({ error: "Conversation not found" });
|
||||
} else {
|
||||
log.error({ error, conversationId: req.params.id }, "Error getting conversation");
|
||||
res.status(500).json({ error: "Failed to get conversation" });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { Response } from "express";
|
||||
import { EventService } from "../services";
|
||||
import { AuthenticatedRequest } from "../middleware";
|
||||
import { createLogger } from "../logging";
|
||||
import { AuthenticatedRequest } from "./AuthMiddleware";
|
||||
|
||||
const log = createLogger("EventController");
|
||||
|
||||
export class EventController {
|
||||
constructor(private eventService: EventService) {}
|
||||
@@ -10,7 +13,7 @@ export class EventController {
|
||||
const event = await this.eventService.create(req.user!.userId, req.body);
|
||||
res.status(201).json(event);
|
||||
} catch (error) {
|
||||
console.error("Error creating event:", error);
|
||||
log.error({ error, userId: req.user?.userId }, "Error creating event");
|
||||
res.status(500).json({ error: "Failed to create event" });
|
||||
}
|
||||
}
|
||||
@@ -27,7 +30,7 @@ export class EventController {
|
||||
}
|
||||
res.json(event);
|
||||
} catch (error) {
|
||||
console.error("Error getting event:", error);
|
||||
log.error({ error, eventId: req.params.id }, "Error getting event");
|
||||
res.status(500).json({ error: "Failed to get event" });
|
||||
}
|
||||
}
|
||||
@@ -37,7 +40,7 @@ export class EventController {
|
||||
const events = await this.eventService.getAll(req.user!.userId);
|
||||
res.json(events);
|
||||
} catch (error) {
|
||||
console.error("Error getting events:", error);
|
||||
log.error({ error, userId: req.user?.userId }, "Error getting events");
|
||||
res.status(500).json({ error: "Failed to get events" });
|
||||
}
|
||||
}
|
||||
@@ -69,7 +72,7 @@ export class EventController {
|
||||
);
|
||||
res.json(events);
|
||||
} catch (error) {
|
||||
console.error("Error getting events by range:", error);
|
||||
log.error({ error, start: req.query.start, end: req.query.end }, "Error getting events by range");
|
||||
res.status(500).json({ error: "Failed to get events" });
|
||||
}
|
||||
}
|
||||
@@ -87,7 +90,7 @@ export class EventController {
|
||||
}
|
||||
res.json(event);
|
||||
} catch (error) {
|
||||
console.error("Error updating event:", error);
|
||||
log.error({ error, eventId: req.params.id }, "Error updating event");
|
||||
res.status(500).json({ error: "Failed to update event" });
|
||||
}
|
||||
}
|
||||
@@ -104,7 +107,7 @@ export class EventController {
|
||||
}
|
||||
res.status(204).send();
|
||||
} catch (error) {
|
||||
console.error("Error deleting event:", error);
|
||||
log.error({ error, eventId: req.params.id }, "Error deleting event");
|
||||
res.status(500).json({ error: "Failed to delete event" });
|
||||
}
|
||||
}
|
||||
|
||||
27
apps/server/src/controllers/LoggingMiddleware.ts
Normal file
27
apps/server/src/controllers/LoggingMiddleware.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import pinoHttp from "pino-http";
|
||||
import { logger } from "../logging";
|
||||
|
||||
export const httpLogger = pinoHttp({
|
||||
logger,
|
||||
customLogLevel: (_req, res, err) => {
|
||||
if (res.statusCode >= 500 || err) return "error";
|
||||
if (res.statusCode >= 400) return "warn";
|
||||
return "info";
|
||||
},
|
||||
customSuccessMessage: (req, res) => {
|
||||
return `${req.method} ${req.url} ${res.statusCode}`;
|
||||
},
|
||||
customErrorMessage: (req, _res, err) => {
|
||||
return `${req.method} ${req.url} failed: ${err.message}`;
|
||||
},
|
||||
redact: ["req.headers.authorization"],
|
||||
serializers: {
|
||||
req: (req) => ({
|
||||
method: req.method,
|
||||
url: req.url,
|
||||
}),
|
||||
res: (res) => ({
|
||||
statusCode: res.statusCode,
|
||||
}),
|
||||
},
|
||||
});
|
||||
@@ -1,3 +1,5 @@
|
||||
export * from "./AuthController";
|
||||
export * from "./ChatController";
|
||||
export * from "./EventController";
|
||||
export * from "./AuthMiddleware";
|
||||
export * from "./LoggingMiddleware";
|
||||
|
||||
Reference in New Issue
Block a user