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:
@@ -34,6 +34,7 @@
|
||||
"react-dom": "19.1.0",
|
||||
"react-native": "0.81.5",
|
||||
"react-native-gesture-handler": "~2.28.0",
|
||||
"react-native-logs": "^5.5.0",
|
||||
"react-native-reanimated": "~4.1.1",
|
||||
"react-native-safe-area-context": "5.6.0",
|
||||
"react-native-screens": "~4.16.0",
|
||||
|
||||
1
apps/client/src/logging/index.ts
Normal file
1
apps/client/src/logging/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { log, apiLogger, storeLogger } from "./logger";
|
||||
30
apps/client/src/logging/logger.ts
Normal file
30
apps/client/src/logging/logger.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { logger, consoleTransport } from "react-native-logs";
|
||||
|
||||
const log = logger.createLogger({
|
||||
levels: {
|
||||
debug: 0,
|
||||
info: 1,
|
||||
warn: 2,
|
||||
error: 3,
|
||||
},
|
||||
severity: __DEV__ ? "debug" : "warn",
|
||||
transport: consoleTransport,
|
||||
transportOptions: {
|
||||
colors: {
|
||||
debug: "white",
|
||||
info: "blueBright",
|
||||
warn: "yellowBright",
|
||||
error: "redBright",
|
||||
},
|
||||
},
|
||||
async: true,
|
||||
dateFormat: "time",
|
||||
printLevel: true,
|
||||
printDate: true,
|
||||
enabled: true,
|
||||
});
|
||||
|
||||
export const apiLogger = log.extend("API");
|
||||
export const storeLogger = log.extend("Store");
|
||||
|
||||
export { log };
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Platform } from "react-native";
|
||||
import { apiLogger } from "../logging";
|
||||
|
||||
const API_BASE_URL =
|
||||
process.env.EXPO_PUBLIC_API_URL ||
|
||||
@@ -19,20 +20,33 @@ async function request<T>(
|
||||
endpoint: string,
|
||||
options?: RequestOptions,
|
||||
): Promise<T> {
|
||||
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
|
||||
method,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...options?.headers,
|
||||
},
|
||||
body: options?.body ? JSON.stringify(options.body) : undefined,
|
||||
});
|
||||
const start = performance.now();
|
||||
apiLogger.debug(`${method} ${endpoint}`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
|
||||
method,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...options?.headers,
|
||||
},
|
||||
body: options?.body ? JSON.stringify(options.body) : undefined,
|
||||
});
|
||||
|
||||
const duration = Math.round(performance.now() - start);
|
||||
|
||||
if (!response.ok) {
|
||||
apiLogger.error(`${method} ${endpoint} - ${response.status} (${duration}ms)`);
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
}
|
||||
|
||||
apiLogger.debug(`${method} ${endpoint} - ${response.status} (${duration}ms)`);
|
||||
return response.json();
|
||||
} catch (error) {
|
||||
const duration = Math.round(performance.now() - start);
|
||||
apiLogger.error(`${method} ${endpoint} failed (${duration}ms): ${error}`);
|
||||
throw error;
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
export const ApiClient = {
|
||||
|
||||
Reference in New Issue
Block a user