@gftdcojp/ksqldb-orm
Version:
ksqldb-orm - Server-Side TypeScript ORM for ksqlDB with enterprise security extensions
376 lines • 14.4 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.consoleLogger = exports.log = exports.Logger = void 0;
const winston = __importStar(require("winston"));
const path = __importStar(require("path"));
const fs = __importStar(require("fs"));
/**
* @fileoverview 統一されたログ機能
* @description プロジェクト全体で使用する統一されたログ機能を提供します
*/
// 環境変数の安全な取得(ブラウザ環境では undefined)
const getEnvVar = (key) => {
try {
return typeof process !== 'undefined' && process.env ? process.env[key] : undefined;
}
catch {
return undefined;
}
};
// ログディレクトリの絶対パス取得
const getLogDirectory = () => {
// 環境変数でログディレクトリを指定可能
const customLogDir = getEnvVar('GFTD_LOG_DIR') || getEnvVar('LOG_DIR');
if (customLogDir) {
return path.resolve(customLogDir);
}
// デフォルトでプロジェクトルートの logs ディレクトリを使用
// Next.js や他の環境でも動作するように process.cwd() を使用
const projectRoot = process.cwd();
return path.join(projectRoot, 'logs');
};
// ブラウザ環境かどうかチェック
const isBrowser = () => {
try {
return typeof window !== 'undefined' && typeof process === 'undefined';
}
catch {
return true; // エラーの場合はブラウザ環境と仮定
}
};
// サーバーレス環境の検出
const isServerlessEnvironment = () => {
try {
// 各種サーバーレス環境の検出
const awsLambda = getEnvVar('AWS_LAMBDA_FUNCTION_NAME');
const vercel = getEnvVar('VERCEL') || getEnvVar('NOW_REGION');
const netlify = getEnvVar('NETLIFY');
const cloudflare = getEnvVar('CF_PAGES');
const railway = getEnvVar('RAILWAY_ENVIRONMENT');
const render = getEnvVar('RENDER');
// AWS Lambda環境の特徴的なパスパターン
const cwd = process.cwd();
const isLambdaPath = cwd.includes('/var/task') || cwd.includes('/var/runtime');
// 読み取り専用ファイルシステムの検出
const isReadOnlyFs = process.env.LAMBDA_TASK_ROOT !== undefined;
return !!(awsLambda || vercel || netlify || cloudflare || railway || render || isLambdaPath || isReadOnlyFs);
}
catch {
return false;
}
};
// Node.js環境かつファイルシステムが利用可能かチェック
const isFileSystemAvailable = () => {
try {
// ブラウザ環境では無効
if (isBrowser()) {
return false;
}
// サーバーレス環境では無効
if (isServerlessEnvironment()) {
return false;
}
// テスト環境では無効(テストファイルの汚染防止)
if (getEnvVar('NODE_ENV') === 'test') {
return false;
}
// ファイルシステムAPIの存在確認
return typeof fs !== 'undefined' && typeof fs.mkdirSync === 'function';
}
catch {
return false;
}
};
// ログディレクトリの作成
const ensureLogDirectory = () => {
if (!isFileSystemAvailable()) {
// ファイルシステムが利用できない理由を詳細に記録
const reasons = [];
if (isBrowser()) {
reasons.push('browser environment');
}
if (isServerlessEnvironment()) {
reasons.push('serverless environment detected');
}
if (getEnvVar('NODE_ENV') === 'test') {
reasons.push('test environment');
}
const reasonText = reasons.length > 0 ? ` (${reasons.join(', ')})` : '';
console.warn(`File logging is disabled: Unable to create log directory or file system is not available${reasonText}`);
return null;
}
try {
const logDir = getLogDirectory();
// ディレクトリが存在しない場合は作成
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir, { recursive: true });
}
return logDir;
}
catch (error) {
// ディレクトリ作成に失敗した場合は詳細なエラー情報を出力
const errorMessage = error instanceof Error ? error.message : String(error);
console.warn(`Failed to create log directory: ${errorMessage}`);
console.warn('File logging is disabled: Unable to create log directory or file system is not available');
return null;
}
};
/**
* 機密情報をマスキングするパターン
*/
const SENSITIVE_PATTERNS = [
// JWT トークン
{ pattern: /eyJ[A-Za-z0-9_\/+-]*\.eyJ[A-Za-z0-9_\/+-]*\.[A-Za-z0-9_\/+-]*/g, replacement: '[JWT_TOKEN_REDACTED]' },
// Bearer トークン
{ pattern: /Bearer\s+[A-Za-z0-9_\/+-]+/gi, replacement: 'Bearer [TOKEN_REDACTED]' },
// API キー(様々な形式)
{ pattern: /(?:api_key|apikey|api-key)\s*[:=]\s*["']?[A-Za-z0-9_\/+-]{16,}["']?/gi, replacement: 'api_key=[API_KEY_REDACTED]' },
// パスワード
{ pattern: /(?:password|passwd|pwd)\s*[:=]\s*["']?[^\s"',;}{]+["']?/gi, replacement: 'password=[PASSWORD_REDACTED]' },
// Basic認証
{ pattern: /Basic\s+[A-Za-z0-9_\/+-]+=*/gi, replacement: 'Basic [BASIC_AUTH_REDACTED]' },
// 秘密鍵
{ pattern: /(?:secret|secret_key|secretkey)\s*[:=]\s*["']?[A-Za-z0-9_\/+-]{16,}["']?/gi, replacement: 'secret=[SECRET_REDACTED]' },
// クレジットカード番号
{ pattern: /\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b/g, replacement: '[CREDIT_CARD_REDACTED]' },
// 電話番号
{ pattern: /\b\d{3}-\d{4}-\d{4}\b/g, replacement: '[PHONE_REDACTED]' },
// メールアドレス(部分的にマスキング)
{ pattern: /([A-Za-z0-9._%+-]+)@([A-Za-z0-9.-]+\.[A-Za-z]{2,})/g, replacement: (_match, user, domain) => {
const maskedUser = user.length > 2 ? user.charAt(0) + '*'.repeat(user.length - 2) + user.charAt(user.length - 1) : user;
return `${maskedUser}@${domain}`;
} },
];
/**
* 機密情報をマスキングする関数
* @param data - マスキング対象のデータ
* @returns マスキング済みのデータ
*/
function sanitizeSensitiveData(data) {
if (typeof data === 'string') {
let sanitized = data;
for (const { pattern, replacement } of SENSITIVE_PATTERNS) {
if (typeof replacement === 'function') {
sanitized = sanitized.replace(pattern, replacement);
}
else {
sanitized = sanitized.replace(pattern, replacement);
}
}
return sanitized;
}
if (Array.isArray(data)) {
return data.map(item => sanitizeSensitiveData(item));
}
if (data && typeof data === 'object') {
const sanitized = {};
for (const [key, value] of Object.entries(data)) {
// キー名でも機密情報を判定
const lowerKey = key.toLowerCase();
if (lowerKey.includes('password') || lowerKey.includes('secret') ||
lowerKey.includes('token') || lowerKey.includes('key') ||
lowerKey.includes('auth') || lowerKey.includes('credential')) {
sanitized[key] = '[REDACTED]';
}
else {
sanitized[key] = sanitizeSensitiveData(value);
}
}
return sanitized;
}
return data;
}
// ログレベル設定
const logLevel = getEnvVar('GFTD_LOG_LEVEL') || getEnvVar('LOG_LEVEL') || 'info';
// 機密情報サニタイズフォーマット
const sanitizeFormat = winston.format((info) => {
// メッセージとメタデータの両方をサニタイズ
info.message = sanitizeSensitiveData(info.message);
// メタデータオブジェクトをサニタイズ
const { level, message, timestamp, stack, ...meta } = info;
const sanitizedMeta = sanitizeSensitiveData(meta);
return {
level,
message,
timestamp,
stack,
...sanitizedMeta
};
});
// ログフォーマット設定
const logFormat = winston.format.combine(winston.format.timestamp({
format: 'YYYY-MM-DD HH:mm:ss.SSS'
}), winston.format.errors({ stack: true }), sanitizeFormat(), // 機密情報サニタイズを追加
winston.format.colorize({ all: true }), winston.format.printf(({ level, message, timestamp, stack }) => {
if (stack) {
return `[${timestamp}] [${level}] ${message}\n${stack}`;
}
return `[${timestamp}] [${level}] ${message}`;
}));
// ログトランスポート設定
const transports = [
new winston.transports.Console({
format: logFormat,
level: logLevel
})
];
// ログディレクトリの準備
const logDirectory = ensureLogDirectory();
// ファイルログ設定(ファイルシステムが利用可能な場合のみ)
if (logDirectory) {
// エラーログファイル
transports.push(new winston.transports.File({
filename: path.join(logDirectory, 'error.log'),
level: 'error',
maxsize: 10 * 1024 * 1024, // 10MB
maxFiles: 5,
format: winston.format.combine(winston.format.timestamp(), sanitizeFormat(), // 機密情報サニタイズを追加
winston.format.json())
}));
// 統合ログファイル
transports.push(new winston.transports.File({
filename: path.join(logDirectory, 'combined.log'),
maxsize: 50 * 1024 * 1024, // 50MB
maxFiles: 10,
format: winston.format.combine(winston.format.timestamp(), sanitizeFormat(), // 機密情報サニタイズを追加
winston.format.json())
}));
// ファイルロギング有効時の確認メッセージ
console.info(`📄 File logging enabled: ${logDirectory}`);
}
// Winstonロガー作成
const winstonLogger = winston.createLogger({
level: logLevel,
format: winston.format.combine(winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.json()),
transports,
exitOnError: false
});
/**
* アプリケーション全体で使用する統一されたログ機能
*/
class Logger {
constructor() {
this.logger = winstonLogger;
}
/**
* シングルトンインスタンスを取得
*/
static getInstance() {
if (!Logger.instance) {
Logger.instance = new Logger();
}
return Logger.instance;
}
/**
* 現在のログ設定情報を取得
*/
getLogInfo() {
return {
logDirectory: logDirectory,
transports: transports.map(t => t.constructor.name),
level: logLevel
};
}
/**
* デバッグログ
* @param message ログメッセージ
* @param meta 追加メタデータ
*/
debug(message, meta) {
this.logger.debug(message, meta);
}
/**
* 情報ログ
* @param message ログメッセージ
* @param meta 追加メタデータ
*/
info(message, meta) {
this.logger.info(message, meta);
}
/**
* 警告ログ
* @param message ログメッセージ
* @param meta 追加メタデータ
*/
warn(message, meta) {
this.logger.warn(message, meta);
}
/**
* エラーログ
* @param message ログメッセージ
* @param error エラーオブジェクト
* @param meta 追加メタデータ
*/
error(message, error, meta) {
if (error instanceof Error) {
this.logger.error(message, { ...meta, error: error.message, stack: error.stack });
}
else {
this.logger.error(message, { ...meta, error });
}
}
/**
* 成功ログ (info level)
* @param message ログメッセージ
* @param meta 追加メタデータ
*/
success(message, meta) {
this.logger.info(`✅ ${message}`, meta);
}
/**
* 失敗ログ (error level)
* @param message ログメッセージ
* @param error エラーオブジェクト
* @param meta 追加メタデータ
*/
failure(message, error, meta) {
this.error(`❌ ${message}`, error, meta);
}
}
exports.Logger = Logger;
// デフォルトのログ機能をエクスポート
exports.log = Logger.getInstance();
// 従来のコンソールログ代替機能
exports.consoleLogger = {
debug: (message, ...args) => exports.log.debug(message, { args }),
info: (message, ...args) => exports.log.info(message, { args }),
warn: (message, ...args) => exports.log.warn(message, { args }),
error: (message, ...args) => exports.log.error(message, args[0], { args: args.slice(1) }),
log: (message, ...args) => exports.log.info(message, { args }),
success: (message, ...args) => exports.log.success(message, { args }),
failure: (message, error, ...args) => exports.log.failure(message, error, { args })
};
//# sourceMappingURL=logger.js.map