FE_Test / services /gradio-logger.ts
GitHub Actions
Deploy from GitHub Actions [test] - 2025-10-31 10:18:25
5f2aab6
/**
* Gradio関連のログ出力を統一的に管理するロガー
* JSON形式で構造化ログを出力
*/
/**
* ログ用の時刻フォーマットを生成
* @returns {string} YYYY/MM/DD HH:MM:SS形式の時刻文字列
*/
function getLogTimestamp(): string {
// JST (Asia/Tokyo) タイムゾーンで日時を取得
const now = new Date();
const jstDate = new Date(now.toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' }));
const year = jstDate.getFullYear();
const month = String(jstDate.getMonth() + 1).padStart(2, '0');
const day = String(jstDate.getDate()).padStart(2, '0');
const hours = String(jstDate.getHours()).padStart(2, '0');
const minutes = String(jstDate.getMinutes()).padStart(2, '0');
const seconds = String(jstDate.getSeconds()).padStart(2, '0');
return `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`;
}
interface BaseLogData {
timestamp: string;
component: 'Pool' | 'API';
event: string;
userIdentifier?: string;
backend?: string;
}
interface PoolLogData extends BaseLogData {
component: 'Pool';
}
interface APILogData extends BaseLogData {
component: 'API';
endpoint?: string;
apiName?: string;
userEmail?: string;
}
interface ErrorLogData extends BaseLogData {
errorType?: 'CONNECTION' | 'TRANSIENT' | 'PERMANENT';
error?: string;
}
type LogData = PoolLogData | APILogData | ErrorLogData;
/**
* [Pool]ログを出力
*/
function logPool(event: string, userIdentifier?: string, backend?: string): void {
const logData: PoolLogData = {
timestamp: getLogTimestamp(),
component: 'Pool',
event,
...(userIdentifier && { userIdentifier }),
...(backend && { backend }),
};
console.log(JSON.stringify(logData));
}
/**
* エラーオブジェクトを文字列に変換
* @param error - エラーオブジェクト
* @returns エラー情報を含む文字列
*/
function serializeError(error: unknown): string {
if (error instanceof Error) {
const details: Record<string, unknown> = {
message: error.message,
name: error.name,
};
// エラーコード(ECONNREFUSED等)を取得
if ('code' in error && error.code) {
details.code = error.code;
}
// ステータスコード(HTTP 403等)を取得
if ('status' in error && typeof error.status === 'number') {
details.status = error.status;
}
// その他の有用なプロパティを取得
if ('cause' in error && error.cause) {
details.cause = String(error.cause);
}
return JSON.stringify(details);
}
if (typeof error === 'object' && error !== null) {
try {
return JSON.stringify(error);
} catch {
return String(error);
}
}
return String(error);
}
/**
* [Pool]エラーログを出力
*/
function logPoolError(
event: string,
error: unknown,
errorType?: 'CONNECTION' | 'TRANSIENT' | 'PERMANENT',
userIdentifier?: string,
backend?: string,
): void {
const logData: ErrorLogData = {
timestamp: getLogTimestamp(),
component: 'Pool',
event,
error: serializeError(error),
...(errorType && { errorType }),
...(userIdentifier && { userIdentifier }),
...(backend && { backend }),
};
console.error(JSON.stringify(logData));
}
/**
* [API REQUEST]ログを出力
*/
function logAPIRequest(apiName: string, endpoint: string, userIdentifier: string, backend: string, userEmail?: string): void {
const logData: APILogData = {
timestamp: getLogTimestamp(),
component: 'API',
event: 'REQUEST',
apiName,
endpoint,
userIdentifier,
backend,
...(userEmail && { userEmail }),
};
console.log(JSON.stringify(logData));
}
/**
* [API RESPONSE]ログを出力(成功時)
*/
function logAPIResponse(
apiName: string,
endpoint: string,
userIdentifier: string,
backend: string,
durationMs: number,
responseSizeMB: number,
userEmail?: string,
): void {
const logData = {
timestamp: getLogTimestamp(),
component: 'API',
event: 'RESPONSE',
apiName,
endpoint,
userIdentifier,
backend,
durationSec: Math.round(durationMs / 1000),
responseSizeMB,
...(userEmail && { userEmail }),
...(responseSizeMB > 1 && { warning: 'Large response' }),
};
console.log(JSON.stringify(logData));
}
export const gradioLogger = {
logPool,
logPoolError,
logAPIRequest,
logAPIResponse,
};
// レガシーログ用にgetLogTimestampもエクスポート
export { getLogTimestamp };