Files
feishin/src/renderer/api/api-controller-logger.ts
T
2025-09-07 12:24:20 -07:00

173 lines
5.9 KiB
TypeScript

import {
ApiController,
ApiControllerError,
ApiControllerFn,
} from '/@/shared/types/adapter/api-controller-types';
import { ServerListItem } from '/@/shared/types/domain/server-domain-types';
import { logger } from '/@/shared/utils/logger';
export interface LoggingOptions {
logErrors?: boolean;
logPerformance?: boolean;
logRequests?: boolean;
logResponses?: boolean;
maxRequestSize?: number;
maxResponseSize?: number;
}
type LoggedApiControllerFn<TRequest, TResponse> = (
request: TRequest,
server: ServerListItem,
options?: any,
) => Promise<[ApiControllerError, null] | [null, TResponse]>;
export function createLoggedApiController(
controller: ApiController,
options: LoggingOptions = {},
): ApiController {
const loggedController: any = {};
// Log utility functions
loggedController._utility = createLoggedUtility(controller._utility);
// Log all controller methods
for (const [sectionKey, section] of Object.entries(controller)) {
if (sectionKey === '_utility') continue;
loggedController[sectionKey] = {};
for (const [methodKey, method] of Object.entries(section as Record<string, any>)) {
if (typeof method === 'function') {
const functionName = `${sectionKey}.${methodKey}`;
if (methodKey === 'authenticate' || methodKey === 'getType') {
// Special handling for non-standard API functions
loggedController[sectionKey][methodKey] = (...args: any[]) => {
logger.info(`[API] ${functionName} called`, {
args: JSON.stringify(args, null, 2),
});
return method(...args);
};
} else {
loggedController[sectionKey][methodKey] = createLoggedFunction(
method as ApiControllerFn<any, any>,
functionName,
options,
);
}
} else {
loggedController[sectionKey][methodKey] = method;
}
}
}
return loggedController as ApiController;
}
function createLoggedFunction<TRequest, TResponse>(
originalFn: ApiControllerFn<TRequest, TResponse> | undefined,
functionName: string,
options: LoggingOptions = {},
): LoggedApiControllerFn<TRequest, TResponse> | undefined {
if (!originalFn) {
return undefined;
}
return async (request: TRequest, requestOptions?: any) => {
const startTime = Date.now();
const requestId = Math.random().toString(36).substring(2, 15);
const {
logErrors = true,
logPerformance = true,
logRequests = true,
logResponses = true,
maxRequestSize = 1000,
maxResponseSize = 1000,
} = options;
if (logRequests) {
const requestStr = JSON.stringify(request, null, 2);
const truncatedRequest =
requestStr.length > maxRequestSize
? requestStr.substring(0, maxRequestSize) + '...'
: requestStr;
logger.info(`[API] ${functionName} called`, {
request: truncatedRequest,
requestId,
});
}
try {
const result = await originalFn(request, requestOptions);
const duration = Date.now() - startTime;
if (result[0]) {
// Error response
if (logErrors) {
const error = result[0] as ApiControllerError;
logger.error(`[API] ${functionName} failed`, {
duration: logPerformance ? `${duration}ms` : undefined,
error: {
code: error.code,
message: error.message,
},
requestId,
});
}
} else {
// Success response
if (logResponses) {
const response = result[1];
const responseStr = JSON.stringify(response);
const truncatedResponse =
responseStr.length > maxResponseSize
? responseStr.substring(0, maxResponseSize) + '...'
: responseStr;
logger.info(`[API] ${functionName} succeeded`, {
duration: logPerformance ? `${duration}ms` : undefined,
requestId,
response: truncatedResponse,
responseSize: responseStr.length,
responseType: typeof response,
});
}
}
return result;
} catch (error) {
const duration = Date.now() - startTime;
if (logErrors) {
logger.error(`[API] ${functionName} threw exception`, {
duration: logPerformance ? `${duration}ms` : undefined,
error: error instanceof Error ? error.message : String(error),
requestId,
stack: error instanceof Error ? error.stack : undefined,
});
}
throw error;
}
};
}
function createLoggedUtility<T extends Record<string, any>>(utility: T): T {
const loggedUtility: any = {};
for (const [key, value] of Object.entries(utility)) {
if (typeof value === 'function') {
loggedUtility[key] = (...args: any[]) => {
logger.debug(`[API] _utility.${key} called`, {
args: JSON.stringify(args, null, 2),
});
return value(...args);
};
} else {
loggedUtility[key] = value;
}
}
return loggedUtility as T;
}