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:
2026-01-10 16:59:40 +01:00
parent 675785ec93
commit 71f84d1cc7
24 changed files with 576 additions and 37 deletions

View File

@@ -0,0 +1 @@
export { log, apiLogger, storeLogger } from "./logger";

View 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 };

View File

@@ -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 = {