@masuidrive/procman 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/cli/commands/help.d.ts.map +1 -1
- package/dist/src/cli/commands/help.js +3 -1
- package/dist/src/cli/commands/help.js.map +1 -1
- package/dist/src/cli/index.js +6 -2
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/cli/parser.d.ts.map +1 -1
- package/dist/src/cli/parser.js +20 -0
- package/dist/src/cli/parser.js.map +1 -1
- package/dist/src/cli/utils/find-package-json.d.ts +4 -0
- package/dist/src/cli/utils/find-package-json.d.ts.map +1 -0
- package/dist/src/cli/utils/find-package-json.js +15 -0
- package/dist/src/cli/utils/find-package-json.js.map +1 -0
- package/dist/src/config/config-loader.d.ts +1 -0
- package/dist/src/config/config-loader.d.ts.map +1 -1
- package/dist/src/config/config-loader.js +16 -6
- package/dist/src/config/config-loader.js.map +1 -1
- package/dist/src/config/config-validator.d.ts +18 -49
- package/dist/src/config/config-validator.d.ts.map +1 -1
- package/dist/src/config/config-validator.js +64 -172
- package/dist/src/config/config-validator.js.map +1 -1
- package/dist/src/config/path-security-validator.d.ts +39 -0
- package/dist/src/config/path-security-validator.d.ts.map +1 -0
- package/dist/src/config/path-security-validator.js +134 -0
- package/dist/src/config/path-security-validator.js.map +1 -0
- package/dist/src/config/secure-config-loader.d.ts.map +1 -1
- package/dist/src/config/secure-config-loader.js +10 -2
- package/dist/src/config/secure-config-loader.js.map +1 -1
- package/dist/src/config/validation-suggestions.d.ts +13 -0
- package/dist/src/config/validation-suggestions.d.ts.map +1 -0
- package/dist/src/config/validation-suggestions.js +40 -0
- package/dist/src/config/validation-suggestions.js.map +1 -0
- package/dist/src/config/validation-types.d.ts +38 -0
- package/dist/src/config/validation-types.d.ts.map +1 -0
- package/dist/src/config/validation-types.js +7 -0
- package/dist/src/config/validation-types.js.map +1 -0
- package/dist/src/daemon/component-command-wiring.d.ts +19 -0
- package/dist/src/daemon/component-command-wiring.d.ts.map +1 -0
- package/dist/src/daemon/component-command-wiring.js +27 -0
- package/dist/src/daemon/component-command-wiring.js.map +1 -0
- package/dist/src/daemon/component-daemon-interface.d.ts +38 -0
- package/dist/src/daemon/component-daemon-interface.d.ts.map +1 -0
- package/dist/src/daemon/component-daemon-interface.js +72 -0
- package/dist/src/daemon/component-daemon-interface.js.map +1 -0
- package/dist/src/daemon/component-health.d.ts +38 -0
- package/dist/src/daemon/component-health.d.ts.map +1 -0
- package/dist/src/daemon/component-health.js +146 -0
- package/dist/src/daemon/component-health.js.map +1 -0
- package/dist/src/daemon/component-manager-errors.d.ts +20 -0
- package/dist/src/daemon/component-manager-errors.d.ts.map +1 -0
- package/dist/src/daemon/component-manager-errors.js +30 -0
- package/dist/src/daemon/component-manager-errors.js.map +1 -0
- package/dist/src/daemon/component-manager-types.d.ts +23 -0
- package/dist/src/daemon/component-manager-types.d.ts.map +1 -0
- package/dist/src/daemon/component-manager-types.js +5 -0
- package/dist/src/daemon/component-manager-types.js.map +1 -0
- package/dist/src/daemon/component-manager.d.ts +16 -64
- package/dist/src/daemon/component-manager.d.ts.map +1 -1
- package/dist/src/daemon/component-manager.js +72 -509
- package/dist/src/daemon/component-manager.js.map +1 -1
- package/dist/src/daemon/component-registration.d.ts +14 -0
- package/dist/src/daemon/component-registration.d.ts.map +1 -0
- package/dist/src/daemon/component-registration.js +92 -0
- package/dist/src/daemon/component-registration.js.map +1 -0
- package/dist/src/daemon/component-streaming.d.ts +36 -0
- package/dist/src/daemon/component-streaming.d.ts.map +1 -0
- package/dist/src/daemon/component-streaming.js +112 -0
- package/dist/src/daemon/component-streaming.js.map +1 -0
- package/dist/src/daemon/daemon-lifecycle.d.ts +37 -0
- package/dist/src/daemon/daemon-lifecycle.d.ts.map +1 -0
- package/dist/src/daemon/daemon-lifecycle.js +362 -0
- package/dist/src/daemon/daemon-lifecycle.js.map +1 -0
- package/dist/src/daemon/daemon-queries.d.ts +53 -0
- package/dist/src/daemon/daemon-queries.d.ts.map +1 -0
- package/dist/src/daemon/daemon-queries.js +113 -0
- package/dist/src/daemon/daemon-queries.js.map +1 -0
- package/dist/src/daemon/daemon-types.d.ts +30 -0
- package/dist/src/daemon/daemon-types.d.ts.map +1 -0
- package/dist/src/daemon/daemon-types.js +8 -0
- package/dist/src/daemon/daemon-types.js.map +1 -0
- package/dist/src/daemon/disposable-base.d.ts +49 -0
- package/dist/src/daemon/disposable-base.d.ts.map +1 -0
- package/dist/src/daemon/disposable-base.js +70 -0
- package/dist/src/daemon/disposable-base.js.map +1 -0
- package/dist/src/daemon/disposal-guard.d.ts +27 -0
- package/dist/src/daemon/disposal-guard.d.ts.map +1 -0
- package/dist/src/daemon/disposal-guard.js +58 -0
- package/dist/src/daemon/disposal-guard.js.map +1 -0
- package/dist/src/daemon/ipc-command-handler.d.ts +8 -34
- package/dist/src/daemon/ipc-command-handler.d.ts.map +1 -1
- package/dist/src/daemon/ipc-command-handler.js +14 -362
- package/dist/src/daemon/ipc-command-handler.js.map +1 -1
- package/dist/src/daemon/log-command-handlers.d.ts +19 -0
- package/dist/src/daemon/log-command-handlers.d.ts.map +1 -0
- package/dist/src/daemon/log-command-handlers.js +114 -0
- package/dist/src/daemon/log-command-handlers.js.map +1 -0
- package/dist/src/daemon/process-command-handlers.d.ts +32 -0
- package/dist/src/daemon/process-command-handlers.d.ts.map +1 -0
- package/dist/src/daemon/process-command-handlers.js +223 -0
- package/dist/src/daemon/process-command-handlers.js.map +1 -0
- package/dist/src/daemon/procman-daemon.d.ts +22 -98
- package/dist/src/daemon/procman-daemon.d.ts.map +1 -1
- package/dist/src/daemon/procman-daemon.js +107 -604
- package/dist/src/daemon/procman-daemon.js.map +1 -1
- package/dist/src/daemon/resource-manager.d.ts +6 -124
- package/dist/src/daemon/resource-manager.d.ts.map +1 -1
- package/dist/src/daemon/resource-manager.js +5 -231
- package/dist/src/daemon/resource-manager.js.map +1 -1
- package/dist/src/daemon/resource-types.d.ts +23 -0
- package/dist/src/daemon/resource-types.d.ts.map +1 -0
- package/dist/src/daemon/resource-types.js +7 -0
- package/dist/src/daemon/resource-types.js.map +1 -0
- package/dist/src/daemon/shutdown-orchestrator.d.ts +32 -0
- package/dist/src/daemon/shutdown-orchestrator.d.ts.map +1 -0
- package/dist/src/daemon/shutdown-orchestrator.js +123 -0
- package/dist/src/daemon/shutdown-orchestrator.js.map +1 -0
- package/dist/src/daemon/tracked-resources.d.ts +60 -0
- package/dist/src/daemon/tracked-resources.d.ts.map +1 -0
- package/dist/src/daemon/tracked-resources.js +124 -0
- package/dist/src/daemon/tracked-resources.js.map +1 -0
- package/dist/src/process-manager/index.d.ts +5 -1
- package/dist/src/process-manager/index.d.ts.map +1 -1
- package/dist/src/process-manager/index.js +3 -0
- package/dist/src/process-manager/index.js.map +1 -1
- package/dist/src/process-manager/interfaces/process-group.d.ts +6 -0
- package/dist/src/process-manager/interfaces/process-group.d.ts.map +1 -1
- package/dist/src/process-manager/managed-process-events.d.ts +22 -0
- package/dist/src/process-manager/managed-process-events.d.ts.map +1 -0
- package/dist/src/process-manager/managed-process-events.js +5 -0
- package/dist/src/process-manager/managed-process-events.js.map +1 -0
- package/dist/src/process-manager/managed-process-info.d.ts +11 -53
- package/dist/src/process-manager/managed-process-info.d.ts.map +1 -1
- package/dist/src/process-manager/managed-process-info.js +46 -133
- package/dist/src/process-manager/managed-process-info.js.map +1 -1
- package/dist/src/process-manager/process-batch-operations.d.ts +62 -0
- package/dist/src/process-manager/process-batch-operations.d.ts.map +1 -0
- package/dist/src/process-manager/process-batch-operations.js +80 -0
- package/dist/src/process-manager/process-batch-operations.js.map +1 -0
- package/dist/src/process-manager/process-config.d.ts +49 -0
- package/dist/src/process-manager/process-config.d.ts.map +1 -0
- package/dist/src/process-manager/process-config.js +68 -0
- package/dist/src/process-manager/process-config.js.map +1 -0
- package/dist/src/process-manager/process-event-forwarding.d.ts +18 -0
- package/dist/src/process-manager/process-event-forwarding.d.ts.map +1 -0
- package/dist/src/process-manager/process-event-forwarding.js +105 -0
- package/dist/src/process-manager/process-event-forwarding.js.map +1 -0
- package/dist/src/process-manager/process-group-manager.d.ts.map +1 -1
- package/dist/src/process-manager/process-group-manager.js +0 -1
- package/dist/src/process-manager/process-group-manager.js.map +1 -1
- package/dist/src/process-manager/process-info-queries.d.ts +48 -0
- package/dist/src/process-manager/process-info-queries.d.ts.map +1 -0
- package/dist/src/process-manager/process-info-queries.js +92 -0
- package/dist/src/process-manager/process-info-queries.js.map +1 -0
- package/dist/src/process-manager/process-manager.d.ts +5 -174
- package/dist/src/process-manager/process-manager.d.ts.map +1 -1
- package/dist/src/process-manager/process-manager.js +58 -398
- package/dist/src/process-manager/process-manager.js.map +1 -1
- package/dist/src/process-manager/process-monitoring.d.ts +80 -0
- package/dist/src/process-manager/process-monitoring.d.ts.map +1 -0
- package/dist/src/process-manager/process-monitoring.js +142 -0
- package/dist/src/process-manager/process-monitoring.js.map +1 -0
- package/dist/src/process-manager/process-statistics.d.ts +37 -0
- package/dist/src/process-manager/process-statistics.d.ts.map +1 -0
- package/dist/src/process-manager/process-statistics.js +24 -0
- package/dist/src/process-manager/process-statistics.js.map +1 -0
- package/dist/src/process-manager/restart-manager.d.ts +82 -0
- package/dist/src/process-manager/restart-manager.d.ts.map +1 -0
- package/dist/src/process-manager/restart-manager.js +150 -0
- package/dist/src/process-manager/restart-manager.js.map +1 -0
- package/dist/src/services/app-logger.d.ts +144 -0
- package/dist/src/services/app-logger.d.ts.map +1 -0
- package/dist/src/services/app-logger.js +489 -0
- package/dist/src/services/app-logger.js.map +1 -0
- package/dist/src/services/log-buffer.d.ts +35 -0
- package/dist/src/services/log-buffer.d.ts.map +1 -0
- package/dist/src/services/log-buffer.js +87 -0
- package/dist/src/services/log-buffer.js.map +1 -0
- package/dist/src/services/log-event-manager.d.ts +32 -0
- package/dist/src/services/log-event-manager.d.ts.map +1 -0
- package/dist/src/services/log-event-manager.js +31 -0
- package/dist/src/services/log-event-manager.js.map +1 -0
- package/dist/src/services/log-file-manager.d.ts +46 -0
- package/dist/src/services/log-file-manager.d.ts.map +1 -0
- package/dist/src/services/log-file-manager.js +156 -0
- package/dist/src/services/log-file-manager.js.map +1 -0
- package/dist/src/services/log-level-strategy.d.ts +19 -0
- package/dist/src/services/log-level-strategy.d.ts.map +1 -0
- package/dist/src/services/log-level-strategy.js +40 -0
- package/dist/src/services/log-level-strategy.js.map +1 -0
- package/dist/src/services/log-manager-types.d.ts +117 -0
- package/dist/src/services/log-manager-types.d.ts.map +1 -0
- package/dist/src/services/log-manager-types.js +12 -0
- package/dist/src/services/log-manager-types.js.map +1 -0
- package/dist/src/services/log-manager.d.ts +8 -35
- package/dist/src/services/log-manager.d.ts.map +1 -1
- package/dist/src/services/log-manager.js +10 -870
- package/dist/src/services/log-manager.js.map +1 -1
- package/dist/src/services/log-preprocessor.d.ts +30 -0
- package/dist/src/services/log-preprocessor.d.ts.map +1 -0
- package/dist/src/services/log-preprocessor.js +65 -0
- package/dist/src/services/log-preprocessor.js.map +1 -0
- package/dist/src/services/process-log-integration.d.ts +7 -2
- package/dist/src/services/process-log-integration.d.ts.map +1 -1
- package/dist/src/services/process-log-integration.js.map +1 -1
- package/package.json +5 -7
|
@@ -5,883 +5,23 @@
|
|
|
5
5
|
* リアルタイムログストリーミング機能とファイル管理を提供する。
|
|
6
6
|
* ログストリーミングはEventEmitterパターンで実装し、new-log イベントを発行する。
|
|
7
7
|
*/
|
|
8
|
-
/* global NodeJS */
|
|
9
8
|
import { EventEmitter } from 'events';
|
|
10
|
-
import * as fs from 'fs';
|
|
11
|
-
import { promises as fsPromises } from 'fs';
|
|
12
9
|
import * as path from 'path';
|
|
13
10
|
import * as os from 'os';
|
|
14
11
|
import { DEFAULT_LOG_MANAGER_CONFIG, } from './log-manager-config.js';
|
|
15
12
|
import { LogPathValidator, SecureLogPathBuilder, } from './log-path-validator.js';
|
|
16
13
|
import { LOG_STREAM_EVENTS } from '../shared/constants-streaming.js';
|
|
17
14
|
import { EventCleanupHelper } from '../utils/event-cleanup.js';
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
* FileManager - ファイル操作の専門クラス(単一責任原則)
|
|
29
|
-
*/
|
|
30
|
-
class FileManager {
|
|
31
|
-
config;
|
|
32
|
-
constructor(config = DEFAULT_LOG_MANAGER_CONFIG) {
|
|
33
|
-
this.config = config;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* ファイルの存在確認・作成
|
|
37
|
-
*/
|
|
38
|
-
ensureFileExists(filePath) {
|
|
39
|
-
try {
|
|
40
|
-
// ディレクトリの確認・作成
|
|
41
|
-
const dir = path.dirname(filePath);
|
|
42
|
-
if (!fs.existsSync(dir)) {
|
|
43
|
-
fs.mkdirSync(dir, {
|
|
44
|
-
recursive: true,
|
|
45
|
-
mode: this.config.file.directoryMode,
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
// ファイルが存在しない場合は空ファイルを作成
|
|
49
|
-
if (!fs.existsSync(filePath)) {
|
|
50
|
-
fs.writeFileSync(filePath, '', { mode: this.config.file.fileMode });
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
catch (error) {
|
|
54
|
-
console.warn(`[FileManager] Failed to ensure file exists ${filePath}: ${error.message}`);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* ログディレクトリの確認・作成
|
|
59
|
-
*/
|
|
60
|
-
ensureLogDirectory(logDir) {
|
|
61
|
-
if (!fs.existsSync(logDir)) {
|
|
62
|
-
fs.mkdirSync(logDir, {
|
|
63
|
-
recursive: true,
|
|
64
|
-
mode: this.config.file.directoryMode,
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* ファイルの末尾から指定行数を効率的に読み込み
|
|
70
|
-
*/
|
|
71
|
-
async readTailLines(filePath, maxLines) {
|
|
72
|
-
const stat = fs.statSync(filePath);
|
|
73
|
-
const fileSize = stat.size;
|
|
74
|
-
if (fileSize === 0) {
|
|
75
|
-
return [];
|
|
76
|
-
}
|
|
77
|
-
// 小さなファイルの場合は全体を読み込み
|
|
78
|
-
if (fileSize < this.config.performance.smallFileThreshold) {
|
|
79
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
80
|
-
return content
|
|
81
|
-
.split('\n')
|
|
82
|
-
.filter((line) => line.trim())
|
|
83
|
-
.slice(-maxLines);
|
|
84
|
-
}
|
|
85
|
-
// 大きなファイルの場合は末尾から効率的に読み込み
|
|
86
|
-
return this.readTailLinesFromLargeFile(filePath, maxLines, fileSize);
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* 大きなファイルから末尾行数を読み込み(関数分割)
|
|
90
|
-
*/
|
|
91
|
-
readTailLinesFromLargeFile(filePath, maxLines, fileSize) {
|
|
92
|
-
const fd = fs.openSync(filePath, 'r');
|
|
93
|
-
try {
|
|
94
|
-
return this.readLinesBackward(fd, fileSize, maxLines);
|
|
95
|
-
}
|
|
96
|
-
finally {
|
|
97
|
-
fs.closeSync(fd);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* ファイルを後方から読み込んで行を取得(10行以下に分割)
|
|
102
|
-
*/
|
|
103
|
-
readLinesBackward(fd, fileSize, maxLines) {
|
|
104
|
-
let position = fileSize;
|
|
105
|
-
let lines = [];
|
|
106
|
-
let buffer = '';
|
|
107
|
-
while (position > 0 && lines.length < maxLines) {
|
|
108
|
-
const chunkResult = this.readChunkBackward(fd, position);
|
|
109
|
-
position = chunkResult.newPosition;
|
|
110
|
-
buffer = chunkResult.chunk + buffer;
|
|
111
|
-
lines = this.processBufferLines(buffer, lines, maxLines);
|
|
112
|
-
buffer = this.extractPartialLine(buffer);
|
|
113
|
-
}
|
|
114
|
-
return lines.slice(-maxLines);
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* 後方からチャンクを読み込み(10行以下)
|
|
118
|
-
*/
|
|
119
|
-
readChunkBackward(fd, position) {
|
|
120
|
-
const chunkSize = this.config.file.chunkSize;
|
|
121
|
-
const readSize = Math.min(chunkSize, position);
|
|
122
|
-
const newPosition = position - readSize;
|
|
123
|
-
const chunk = Buffer.alloc(readSize);
|
|
124
|
-
fs.readSync(fd, chunk, 0, readSize, newPosition);
|
|
125
|
-
return {
|
|
126
|
-
chunk: chunk.toString('utf8'),
|
|
127
|
-
newPosition,
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* バッファの行を処理(10行以下)
|
|
132
|
-
*/
|
|
133
|
-
processBufferLines(buffer, currentLines, maxLines) {
|
|
134
|
-
const lineArray = buffer.split('\n');
|
|
135
|
-
const lines = [...currentLines];
|
|
136
|
-
// 完全な行を先頭に追加(逆順)
|
|
137
|
-
for (let i = lineArray.length - 2; i >= 0; i--) {
|
|
138
|
-
const line = lineArray[i].trim();
|
|
139
|
-
if (line && lines.length < maxLines) {
|
|
140
|
-
lines.unshift(line);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
return lines;
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* 部分的な行を抽出(10行以下)
|
|
147
|
-
*/
|
|
148
|
-
extractPartialLine(buffer) {
|
|
149
|
-
const lineArray = buffer.split('\n');
|
|
150
|
-
return lineArray.shift() || '';
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* リトライ機能付きファイル書き込み
|
|
154
|
-
*/
|
|
155
|
-
async writeToFileWithRetry(filePath, content, maxRetries) {
|
|
156
|
-
let lastError = null;
|
|
157
|
-
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
158
|
-
try {
|
|
159
|
-
await fsPromises.appendFile(filePath, content, { encoding: 'utf8' });
|
|
160
|
-
return; // 成功した場合はリターン
|
|
161
|
-
}
|
|
162
|
-
catch (error) {
|
|
163
|
-
lastError = error;
|
|
164
|
-
if (attempt === maxRetries) {
|
|
165
|
-
// 最後の試行で失敗した場合はエラーログを出力
|
|
166
|
-
console.error(`[FileManager] Failed to write to log file ${filePath} after ${maxRetries} attempts: ${lastError.message}`);
|
|
167
|
-
// エラーを伝播(上位でハンドリング可能にする)
|
|
168
|
-
throw lastError;
|
|
169
|
-
}
|
|
170
|
-
else {
|
|
171
|
-
// リトライ前の短い待機(設定値 * 試行回数)
|
|
172
|
-
await new Promise((resolve) => setTimeout(resolve, this.config.file.retryBaseDelay * attempt));
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
// =============================================================================
|
|
179
|
-
// LogPreprocessor Class
|
|
180
|
-
// =============================================================================
|
|
181
|
-
/**
|
|
182
|
-
* LogPreprocessor - ログ前処理の専門クラス(単一責任原則)
|
|
183
|
-
*/
|
|
184
|
-
class LogPreprocessor {
|
|
185
|
-
config;
|
|
186
|
-
constructor(config = DEFAULT_LOG_MANAGER_CONFIG) {
|
|
187
|
-
this.config = config;
|
|
188
|
-
}
|
|
189
|
-
/**
|
|
190
|
-
* ログメッセージの前処理
|
|
191
|
-
*/
|
|
192
|
-
preprocessMessage(message) {
|
|
193
|
-
let processed = message;
|
|
194
|
-
// サイズ制限の適用
|
|
195
|
-
processed = this.applySizeLimit(processed);
|
|
196
|
-
// 特殊文字のエスケープ
|
|
197
|
-
processed = this.escapeSpecialChars(processed);
|
|
198
|
-
// エンコーディング問題の修正
|
|
199
|
-
processed = this.fixEncodingIssues(processed);
|
|
200
|
-
// 改行文字の正規化とトリム
|
|
201
|
-
processed = this.normalizeAndTrim(processed);
|
|
202
|
-
return processed;
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* サイズ制限の適用
|
|
206
|
-
*/
|
|
207
|
-
applySizeLimit(message) {
|
|
208
|
-
if (message.length > this.config.maxLogMessageSize) {
|
|
209
|
-
return message.substring(0, this.config.maxLogMessageSize - 3) + '...';
|
|
210
|
-
}
|
|
211
|
-
return message;
|
|
212
|
-
}
|
|
213
|
-
/**
|
|
214
|
-
* 特殊文字のエスケープ(制御文字の削除)
|
|
215
|
-
*/
|
|
216
|
-
escapeSpecialChars(message) {
|
|
217
|
-
return message.replace(ESCAPE_CHARS, '');
|
|
218
|
-
}
|
|
219
|
-
/**
|
|
220
|
-
* エンコーディング問題の修正
|
|
221
|
-
*/
|
|
222
|
-
fixEncodingIssues(message) {
|
|
223
|
-
try {
|
|
224
|
-
// Buffer経由でUTF-8として解釈し直す
|
|
225
|
-
const buffer = Buffer.from(message, 'utf8');
|
|
226
|
-
return buffer.toString('utf8');
|
|
227
|
-
}
|
|
228
|
-
catch {
|
|
229
|
-
// エラーが発生した場合はASCII文字のみを保持
|
|
230
|
-
return message.replace(/[^\x20-\x7E]/g, '?');
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* 改行文字の正規化とトリム
|
|
235
|
-
*/
|
|
236
|
-
normalizeAndTrim(message) {
|
|
237
|
-
// 改行文字の正規化
|
|
238
|
-
const normalized = message.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
239
|
-
// トリム(前後の空白を削除)
|
|
240
|
-
return normalized.trim();
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
// =============================================================================
|
|
244
|
-
// LogLevelStrategy Classes
|
|
245
|
-
// =============================================================================
|
|
246
|
-
/**
|
|
247
|
-
* DefaultLogLevelStrategy - デフォルトのログレベル判定戦略(Strategy パターン)
|
|
248
|
-
*/
|
|
249
|
-
class DefaultLogLevelStrategy {
|
|
250
|
-
/**
|
|
251
|
-
* ログレベルの判定
|
|
252
|
-
*/
|
|
253
|
-
determineLevel(type, message) {
|
|
254
|
-
// stderr は基本的に warn 以上
|
|
255
|
-
if (type === 'stderr') {
|
|
256
|
-
return this.determineStderrLevel(message);
|
|
257
|
-
}
|
|
258
|
-
// stdout でもエラー関連のキーワードがあればエラー扱い
|
|
259
|
-
return this.determineStdoutLevel(message);
|
|
260
|
-
}
|
|
261
|
-
/**
|
|
262
|
-
* stderr のログレベル判定
|
|
263
|
-
*/
|
|
264
|
-
determineStderrLevel(message) {
|
|
265
|
-
const lowerMessage = message.toLowerCase();
|
|
266
|
-
if (lowerMessage.includes('error') || lowerMessage.includes('fatal')) {
|
|
267
|
-
return 'error';
|
|
268
|
-
}
|
|
269
|
-
return 'warn';
|
|
270
|
-
}
|
|
271
|
-
/**
|
|
272
|
-
* stdout のログレベル判定
|
|
273
|
-
*/
|
|
274
|
-
determineStdoutLevel(message) {
|
|
275
|
-
const lowerMessage = message.toLowerCase();
|
|
276
|
-
if (lowerMessage.includes('error') || lowerMessage.includes('fatal')) {
|
|
277
|
-
return 'error';
|
|
278
|
-
}
|
|
279
|
-
if (lowerMessage.includes('warn') || lowerMessage.includes('warning')) {
|
|
280
|
-
return 'warn';
|
|
281
|
-
}
|
|
282
|
-
return 'info';
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
// =============================================================================
|
|
286
|
-
// EventManager Class
|
|
287
|
-
// =============================================================================
|
|
288
|
-
/**
|
|
289
|
-
* EventManager - イベント発行の専門クラス(単一責任原則)
|
|
290
|
-
*/
|
|
291
|
-
class EventManager {
|
|
292
|
-
eventEmitter;
|
|
293
|
-
constructor(eventEmitter) {
|
|
294
|
-
this.eventEmitter = eventEmitter;
|
|
295
|
-
}
|
|
296
|
-
/**
|
|
297
|
-
* ログイベントの発行
|
|
298
|
-
*/
|
|
299
|
-
emitLogEvent(logEntry) {
|
|
300
|
-
this.eventEmitter.emit('log', logEntry);
|
|
301
|
-
// ストリーミング用の新しいログイベントも発行
|
|
302
|
-
this.eventEmitter.emit(LOG_STREAM_EVENTS.NEW_LOG, logEntry);
|
|
303
|
-
}
|
|
304
|
-
/**
|
|
305
|
-
* ファイル変更イベントの発行
|
|
306
|
-
*/
|
|
307
|
-
emitFileChangeEvent(data) {
|
|
308
|
-
this.eventEmitter.emit('fileChange', data);
|
|
309
|
-
}
|
|
310
|
-
/**
|
|
311
|
-
* ディスク容量エラーイベントの発行
|
|
312
|
-
*/
|
|
313
|
-
emitDiskSpaceErrorEvent(data) {
|
|
314
|
-
this.eventEmitter.emit('diskSpaceError', data);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
// =============================================================================
|
|
318
|
-
// LogBuffer Class
|
|
319
|
-
// =============================================================================
|
|
320
|
-
/**
|
|
321
|
-
* LogBuffer - 高頻度書き込み時のバッファリング管理
|
|
322
|
-
*/
|
|
323
|
-
class LogBuffer {
|
|
324
|
-
onFlush;
|
|
325
|
-
config;
|
|
326
|
-
buffer = [];
|
|
327
|
-
bufferSize = 0;
|
|
328
|
-
flushTimer = null;
|
|
329
|
-
isBackpressured = false;
|
|
330
|
-
constructor(onFlush, config = DEFAULT_LOG_MANAGER_CONFIG) {
|
|
331
|
-
this.onFlush = onFlush;
|
|
332
|
-
this.config = config;
|
|
333
|
-
}
|
|
334
|
-
/**
|
|
335
|
-
* ログエントリをバッファに追加
|
|
336
|
-
*/
|
|
337
|
-
addEntry(entry) {
|
|
338
|
-
// バックプレッシャー制御
|
|
339
|
-
if (this.isBackpressured) {
|
|
340
|
-
return false;
|
|
341
|
-
}
|
|
342
|
-
const entrySize = JSON.stringify(entry.logEntry).length;
|
|
343
|
-
// バッファサイズ制限チェック
|
|
344
|
-
if (this.bufferSize + entrySize > this.config.buffer.maxSize) {
|
|
345
|
-
this.flush();
|
|
346
|
-
}
|
|
347
|
-
this.buffer.push(entry);
|
|
348
|
-
this.bufferSize += entrySize;
|
|
349
|
-
// バックプレッシャー制御
|
|
350
|
-
if (this.bufferSize > this.config.buffer.backpressureThreshold) {
|
|
351
|
-
this.isBackpressured = true;
|
|
352
|
-
}
|
|
353
|
-
// 定期フラッシュタイマー設定
|
|
354
|
-
if (!this.flushTimer) {
|
|
355
|
-
this.flushTimer = setTimeout(async () => {
|
|
356
|
-
await this.flush();
|
|
357
|
-
}, this.config.buffer.flushInterval);
|
|
358
|
-
}
|
|
359
|
-
return true;
|
|
360
|
-
}
|
|
361
|
-
/**
|
|
362
|
-
* バッファを強制フラッシュ
|
|
363
|
-
*/
|
|
364
|
-
async flush() {
|
|
365
|
-
if (this.buffer.length === 0) {
|
|
366
|
-
return;
|
|
367
|
-
}
|
|
368
|
-
const entriesToFlush = [...this.buffer];
|
|
369
|
-
this.buffer = [];
|
|
370
|
-
this.bufferSize = 0;
|
|
371
|
-
this.isBackpressured = false;
|
|
372
|
-
if (this.flushTimer) {
|
|
373
|
-
clearTimeout(this.flushTimer);
|
|
374
|
-
this.flushTimer = null;
|
|
375
|
-
}
|
|
376
|
-
try {
|
|
377
|
-
await this.onFlush(entriesToFlush);
|
|
378
|
-
}
|
|
379
|
-
catch (error) {
|
|
380
|
-
console.error(`[LogBuffer] Failed to flush entries: ${error.message}`);
|
|
381
|
-
// 重要:失敗したエントリは失われるが、バッファが詰まるのを防ぐ
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
/**
|
|
385
|
-
* バッファクリーンアップ
|
|
386
|
-
*/
|
|
387
|
-
async cleanup() {
|
|
388
|
-
await this.flush();
|
|
389
|
-
if (this.flushTimer) {
|
|
390
|
-
clearTimeout(this.flushTimer);
|
|
391
|
-
this.flushTimer = null;
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
/**
|
|
395
|
-
* バッファ統計情報
|
|
396
|
-
*/
|
|
397
|
-
getStats() {
|
|
398
|
-
return {
|
|
399
|
-
entryCount: this.buffer.length,
|
|
400
|
-
bufferSize: this.bufferSize,
|
|
401
|
-
isBackpressured: this.isBackpressured,
|
|
402
|
-
};
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
/**
|
|
406
|
-
* AppLogger - 個別アプリケーションのログ管理(リファクタリング後)
|
|
407
|
-
*/
|
|
408
|
-
class AppLogger {
|
|
409
|
-
logDir;
|
|
410
|
-
eventEmitter;
|
|
411
|
-
managerConfig;
|
|
412
|
-
logInfo;
|
|
413
|
-
watchers = [];
|
|
414
|
-
logBuffer;
|
|
415
|
-
partialLines = new Map();
|
|
416
|
-
// 依存性注入されたサービスクラス
|
|
417
|
-
fileManager;
|
|
418
|
-
logPreprocessor;
|
|
419
|
-
logLevelStrategy;
|
|
420
|
-
eventManager;
|
|
421
|
-
constructor(appName, config, logDir, eventEmitter, managerConfig = DEFAULT_LOG_MANAGER_CONFIG,
|
|
422
|
-
// 依存性注入(テスト時にはモックを注入可能)
|
|
423
|
-
fileManager, logPreprocessor, logLevelStrategy, eventManager) {
|
|
424
|
-
this.logDir = logDir;
|
|
425
|
-
this.eventEmitter = eventEmitter;
|
|
426
|
-
this.managerConfig = managerConfig;
|
|
427
|
-
this.logInfo = this.initializeLogInfo(appName, config);
|
|
428
|
-
// 依存性注入の実装(DI コンテナパターン)
|
|
429
|
-
this.fileManager = fileManager || new FileManager(this.managerConfig);
|
|
430
|
-
this.logPreprocessor =
|
|
431
|
-
logPreprocessor || new LogPreprocessor(this.managerConfig);
|
|
432
|
-
this.logLevelStrategy = logLevelStrategy || new DefaultLogLevelStrategy();
|
|
433
|
-
this.eventManager = eventManager || new EventManager(this.eventEmitter);
|
|
434
|
-
this.ensureLogFiles();
|
|
435
|
-
this.logBuffer = new LogBuffer(async (entries) => await this.flushBufferedEntries(entries), this.managerConfig);
|
|
436
|
-
}
|
|
437
|
-
/**
|
|
438
|
-
* ログ情報の初期化
|
|
439
|
-
*/
|
|
440
|
-
initializeLogInfo(appName, config) {
|
|
441
|
-
const namespace = config.namespace || 'default';
|
|
442
|
-
// ログファイルパスの決定(仕様に従った優先順位)
|
|
443
|
-
let logFile = null;
|
|
444
|
-
let outFile = null;
|
|
445
|
-
let errorFile = null;
|
|
446
|
-
if (config.outFile && config.errorFile) {
|
|
447
|
-
// 1. out_file + error_file が指定時 → これらを使用、log_fileは無視
|
|
448
|
-
outFile = config.outFile;
|
|
449
|
-
errorFile = config.errorFile;
|
|
450
|
-
}
|
|
451
|
-
else if (config.logFile) {
|
|
452
|
-
// 2. log_file のみ指定時 → stdout/stderrを同一ファイルに出力
|
|
453
|
-
logFile = config.logFile;
|
|
454
|
-
}
|
|
455
|
-
else {
|
|
456
|
-
// 3. 未指定時 → デフォルトパス
|
|
457
|
-
logFile = path.join(this.logDir, `${appName}.jsonl`);
|
|
458
|
-
}
|
|
459
|
-
return {
|
|
460
|
-
appName,
|
|
461
|
-
namespace,
|
|
462
|
-
logFile,
|
|
463
|
-
outFile,
|
|
464
|
-
errorFile,
|
|
465
|
-
streams: {},
|
|
466
|
-
};
|
|
467
|
-
}
|
|
468
|
-
/**
|
|
469
|
-
* ログファイルの事前作成(FileManagerに委譲)
|
|
470
|
-
*/
|
|
471
|
-
ensureLogFiles() {
|
|
472
|
-
// 統合ログファイル
|
|
473
|
-
if (this.logInfo.logFile) {
|
|
474
|
-
this.fileManager.ensureFileExists(this.logInfo.logFile);
|
|
475
|
-
}
|
|
476
|
-
// stdout専用ファイル
|
|
477
|
-
if (this.logInfo.outFile) {
|
|
478
|
-
this.fileManager.ensureFileExists(this.logInfo.outFile);
|
|
479
|
-
}
|
|
480
|
-
// stderr専用ファイル
|
|
481
|
-
if (this.logInfo.errorFile) {
|
|
482
|
-
this.fileManager.ensureFileExists(this.logInfo.errorFile);
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
/**
|
|
486
|
-
* プロセス出力のキャプチャとログ記録
|
|
487
|
-
* ProcessLifecycleManagerからの stdout/stderr データを処理
|
|
488
|
-
*/
|
|
489
|
-
captureProcessOutput(type, data) {
|
|
490
|
-
// 既存の部分的な行を取得
|
|
491
|
-
const existingPartial = this.partialLines.get(type) || '';
|
|
492
|
-
const fullData = existingPartial + data;
|
|
493
|
-
// 改行で分割
|
|
494
|
-
const lines = fullData.split('\n');
|
|
495
|
-
// 最後の要素は部分的な行か空文字列
|
|
496
|
-
const partialLine = lines.pop() || '';
|
|
497
|
-
this.partialLines.set(type, partialLine);
|
|
498
|
-
// 完全な行を処理
|
|
499
|
-
for (const line of lines) {
|
|
500
|
-
if (line.length > 0) {
|
|
501
|
-
this.writeLogWithPreprocessing(type, line);
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
/**
|
|
506
|
-
* ログエントリの前処理を含む書き込み(10行以下に分割)
|
|
507
|
-
*/
|
|
508
|
-
writeLogWithPreprocessing(type, message) {
|
|
509
|
-
const processedMessage = this.logPreprocessor.preprocessMessage(message);
|
|
510
|
-
const logEntry = this.createLogEntry(type, processedMessage);
|
|
511
|
-
const targetFiles = this.determineTargetFiles(type);
|
|
512
|
-
this.addToBufferAndEmitEvent(logEntry, targetFiles);
|
|
513
|
-
}
|
|
514
|
-
/**
|
|
515
|
-
* ログエントリの作成(10行以下)
|
|
516
|
-
*/
|
|
517
|
-
createLogEntry(type, processedMessage) {
|
|
518
|
-
return {
|
|
519
|
-
timestamp: Date.now(),
|
|
520
|
-
level: this.logLevelStrategy.determineLevel(type, processedMessage),
|
|
521
|
-
message: processedMessage,
|
|
522
|
-
app: this.logInfo.appName,
|
|
523
|
-
namespace: this.logInfo.namespace,
|
|
524
|
-
type,
|
|
525
|
-
};
|
|
526
|
-
}
|
|
527
|
-
/**
|
|
528
|
-
* バッファ追加とイベント発行(10行以下)
|
|
529
|
-
*/
|
|
530
|
-
addToBufferAndEmitEvent(logEntry, targetFiles) {
|
|
531
|
-
const bufferedEntry = {
|
|
532
|
-
logEntry,
|
|
533
|
-
targetFiles,
|
|
534
|
-
timestamp: Date.now(),
|
|
535
|
-
};
|
|
536
|
-
const added = this.logBuffer.addEntry(bufferedEntry);
|
|
537
|
-
this.handleBufferBackpressure(added);
|
|
538
|
-
this.eventManager.emitLogEvent(logEntry);
|
|
539
|
-
}
|
|
540
|
-
/**
|
|
541
|
-
* バッファバックプレッシャーの処理(10行以下)
|
|
542
|
-
*/
|
|
543
|
-
handleBufferBackpressure(added) {
|
|
544
|
-
if (!added) {
|
|
545
|
-
const warningMessage = `Log buffer backpressure active for app: ${this.logInfo.appName}`;
|
|
546
|
-
console.warn(`[AppLogger] ${warningMessage}`);
|
|
547
|
-
// バックプレッシャーイベントを発行
|
|
548
|
-
this.eventEmitter.emit('bufferWarning', {
|
|
549
|
-
message: warningMessage,
|
|
550
|
-
appName: this.logInfo.appName,
|
|
551
|
-
timestamp: Date.now(),
|
|
552
|
-
});
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
/**
|
|
556
|
-
* 書き込み対象ファイルの決定(関数分割)
|
|
557
|
-
*/
|
|
558
|
-
determineTargetFiles(type) {
|
|
559
|
-
const targetFiles = [];
|
|
560
|
-
if (this.logInfo.logFile) {
|
|
561
|
-
targetFiles.push(this.logInfo.logFile);
|
|
562
|
-
}
|
|
563
|
-
if (type === 'stdout' && this.logInfo.outFile) {
|
|
564
|
-
targetFiles.push(this.logInfo.outFile);
|
|
565
|
-
}
|
|
566
|
-
else if (type === 'stderr' && this.logInfo.errorFile) {
|
|
567
|
-
targetFiles.push(this.logInfo.errorFile);
|
|
568
|
-
}
|
|
569
|
-
return targetFiles;
|
|
570
|
-
}
|
|
571
|
-
/**
|
|
572
|
-
* ログエントリの書き込み(互換性のため維持)
|
|
573
|
-
*/
|
|
574
|
-
writeLog(type, message) {
|
|
575
|
-
this.writeLogWithPreprocessing(type, message);
|
|
576
|
-
}
|
|
577
|
-
// preprocessLogMessage は LogPreprocessor に移動済み
|
|
578
|
-
/**
|
|
579
|
-
* バッファされたエントリをファイルに書き込み(20行以下に分割)
|
|
580
|
-
*/
|
|
581
|
-
async flushBufferedEntries(entries) {
|
|
582
|
-
// ファイル別にエントリをグループ化
|
|
583
|
-
const fileEntries = this.groupEntriesByFile(entries);
|
|
584
|
-
// ファイル別に並列書き込み
|
|
585
|
-
const writePromises = this.createWritePromises(fileEntries);
|
|
586
|
-
// 全ての書き込みの完了を待機
|
|
587
|
-
await this.executeParallelWrites(writePromises);
|
|
588
|
-
}
|
|
589
|
-
/**
|
|
590
|
-
* エントリをファイル別にグループ化(関数分割)
|
|
591
|
-
*/
|
|
592
|
-
groupEntriesByFile(entries) {
|
|
593
|
-
const fileEntries = new Map();
|
|
594
|
-
for (const bufferedEntry of entries) {
|
|
595
|
-
for (const filePath of bufferedEntry.targetFiles) {
|
|
596
|
-
if (!fileEntries.has(filePath)) {
|
|
597
|
-
fileEntries.set(filePath, []);
|
|
598
|
-
}
|
|
599
|
-
fileEntries.get(filePath).push(bufferedEntry.logEntry);
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
return fileEntries;
|
|
603
|
-
}
|
|
604
|
-
/**
|
|
605
|
-
* 書き込みPromise配列を作成(関数分割)
|
|
606
|
-
*/
|
|
607
|
-
createWritePromises(fileEntries) {
|
|
608
|
-
const writePromises = [];
|
|
609
|
-
for (const [filePath, logEntries] of fileEntries) {
|
|
610
|
-
const logLines = logEntries
|
|
611
|
-
.map((entry) => JSON.stringify(entry) + '\n')
|
|
612
|
-
.join('');
|
|
613
|
-
const writePromise = this.writeToFileWithRetryAndHandleError(filePath, logLines);
|
|
614
|
-
writePromises.push(writePromise);
|
|
615
|
-
}
|
|
616
|
-
return writePromises;
|
|
617
|
-
}
|
|
618
|
-
/**
|
|
619
|
-
* 並列書き込みを実行(関数分割)
|
|
620
|
-
*/
|
|
621
|
-
async executeParallelWrites(writePromises) {
|
|
622
|
-
try {
|
|
623
|
-
await Promise.allSettled(writePromises);
|
|
624
|
-
}
|
|
625
|
-
catch (error) {
|
|
626
|
-
// Promise.allSettledは例外を投げないが、念のため
|
|
627
|
-
console.error(`[AppLogger] Unexpected error during batch write: ${error.message}`);
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
/**
|
|
631
|
-
* リトライ機能付きファイル書き込みとエラーハンドリング
|
|
632
|
-
*/
|
|
633
|
-
async writeToFileWithRetryAndHandleError(filePath, content) {
|
|
634
|
-
try {
|
|
635
|
-
// FileManager に委譲
|
|
636
|
-
await this.fileManager.writeToFileWithRetry(filePath, content, this.managerConfig.file.maxRetries);
|
|
637
|
-
}
|
|
638
|
-
catch (error) {
|
|
639
|
-
const lastError = error;
|
|
640
|
-
// ディスク容量不足の場合は特別な処理
|
|
641
|
-
if (lastError.message.includes('ENOSPC')) {
|
|
642
|
-
this.handleDiskSpaceError(filePath);
|
|
643
|
-
}
|
|
644
|
-
else {
|
|
645
|
-
// その他のエラー(権限エラーなど)もイベント発行
|
|
646
|
-
this.eventEmitter.emit('error', {
|
|
647
|
-
message: lastError.message,
|
|
648
|
-
filePath,
|
|
649
|
-
appName: this.logInfo.appName,
|
|
650
|
-
timestamp: Date.now(),
|
|
651
|
-
error: lastError,
|
|
652
|
-
});
|
|
653
|
-
}
|
|
654
|
-
// エラーを再スロー(上位でハンドリングできるように)
|
|
655
|
-
// throw lastError;
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
/**
|
|
659
|
-
* ディスク容量不足エラーの処理(EventManagerに委譲)
|
|
660
|
-
*/
|
|
661
|
-
handleDiskSpaceError(filePath) {
|
|
662
|
-
const errorMessage = `Disk space error detected for ${filePath}. Consider implementing log rotation or cleanup.`;
|
|
663
|
-
console.error(`[AppLogger] ${errorMessage}`);
|
|
664
|
-
// イベントとして通知(EventManagerに委譲)
|
|
665
|
-
this.eventManager.emitDiskSpaceErrorEvent({
|
|
666
|
-
filePath,
|
|
667
|
-
appName: this.logInfo.appName,
|
|
668
|
-
timestamp: Date.now(),
|
|
669
|
-
message: errorMessage,
|
|
670
|
-
});
|
|
671
|
-
}
|
|
672
|
-
// determineLogLevel は LogLevelStrategy に移動済み
|
|
673
|
-
/**
|
|
674
|
-
* ファイル監視の開始
|
|
675
|
-
*/
|
|
676
|
-
async startWatching() {
|
|
677
|
-
// 既存の監視を停止
|
|
678
|
-
this.stopWatching();
|
|
679
|
-
const filesToWatch = [];
|
|
680
|
-
if (this.logInfo.logFile) {
|
|
681
|
-
filesToWatch.push({ file: this.logInfo.logFile, type: 'combined' });
|
|
682
|
-
}
|
|
683
|
-
if (this.logInfo.outFile) {
|
|
684
|
-
filesToWatch.push({ file: this.logInfo.outFile, type: 'stdout' });
|
|
685
|
-
}
|
|
686
|
-
if (this.logInfo.errorFile) {
|
|
687
|
-
filesToWatch.push({ file: this.logInfo.errorFile, type: 'stderr' });
|
|
688
|
-
}
|
|
689
|
-
for (const { file, type } of filesToWatch) {
|
|
690
|
-
if (fs.existsSync(file)) {
|
|
691
|
-
try {
|
|
692
|
-
const watcher = fs.watch(file, (eventType) => {
|
|
693
|
-
if (eventType === 'change') {
|
|
694
|
-
// EventManager に委譲
|
|
695
|
-
this.eventManager.emitFileChangeEvent({
|
|
696
|
-
appName: this.logInfo.appName,
|
|
697
|
-
file,
|
|
698
|
-
type,
|
|
699
|
-
});
|
|
700
|
-
}
|
|
701
|
-
});
|
|
702
|
-
this.watchers.push(watcher);
|
|
703
|
-
}
|
|
704
|
-
catch (error) {
|
|
705
|
-
console.warn(`[AppLogger] Failed to watch ${file}: ${error.message}`);
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
/**
|
|
711
|
-
* ファイル監視の停止
|
|
712
|
-
*/
|
|
713
|
-
stopWatching() {
|
|
714
|
-
for (const watcher of this.watchers) {
|
|
715
|
-
watcher.close();
|
|
716
|
-
}
|
|
717
|
-
this.watchers = [];
|
|
718
|
-
}
|
|
719
|
-
/**
|
|
720
|
-
* ログの読み込み(20行以下に分割)
|
|
721
|
-
*/
|
|
722
|
-
async readLogs(options = {}) {
|
|
723
|
-
const { lines = 100 } = options;
|
|
724
|
-
// ログファイルの選択
|
|
725
|
-
const logFile = this.selectLogFile(options.type);
|
|
726
|
-
if (!logFile || !fs.existsSync(logFile)) {
|
|
727
|
-
return [];
|
|
728
|
-
}
|
|
729
|
-
// ファイルからログエントリを読み込み・解析
|
|
730
|
-
return this.readAndParseLogEntries(logFile, lines, options);
|
|
731
|
-
}
|
|
732
|
-
/**
|
|
733
|
-
* ログファイルの選択(関数分割)
|
|
734
|
-
*/
|
|
735
|
-
selectLogFile(type) {
|
|
736
|
-
if (type && type.length === 1) {
|
|
737
|
-
switch (type[0]) {
|
|
738
|
-
case 'stdout':
|
|
739
|
-
return this.logInfo.outFile || this.logInfo.logFile;
|
|
740
|
-
case 'stderr':
|
|
741
|
-
return this.logInfo.errorFile || this.logInfo.logFile;
|
|
742
|
-
default:
|
|
743
|
-
return this.logInfo.logFile;
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
return this.logInfo.logFile;
|
|
747
|
-
}
|
|
748
|
-
/**
|
|
749
|
-
* ログエントリの読み込みと解析(関数分割)
|
|
750
|
-
*/
|
|
751
|
-
async readAndParseLogEntries(logFile, lines, options) {
|
|
752
|
-
try {
|
|
753
|
-
// FileManager に委譲
|
|
754
|
-
const recentLines = await this.fileManager.readTailLines(logFile, lines);
|
|
755
|
-
return this.parseLogLines(recentLines, options);
|
|
756
|
-
}
|
|
757
|
-
catch (error) {
|
|
758
|
-
console.error(`[AppLogger] Failed to read log file ${logFile}: ${error.message}`);
|
|
759
|
-
return [];
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
/**
|
|
763
|
-
* ログ行の解析とフィルタリング(関数分割)
|
|
764
|
-
*/
|
|
765
|
-
parseLogLines(lines, options) {
|
|
766
|
-
const logEntries = [];
|
|
767
|
-
for (const line of lines) {
|
|
768
|
-
try {
|
|
769
|
-
const entry = JSON.parse(line);
|
|
770
|
-
// フィルタリング適用
|
|
771
|
-
if (this.matchesFilter(entry, options)) {
|
|
772
|
-
logEntries.push(entry);
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
catch {
|
|
776
|
-
console.warn(`[AppLogger] Failed to parse log line: ${line}`);
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
return logEntries;
|
|
780
|
-
}
|
|
781
|
-
// readTailLines は FileManager に移動済み
|
|
782
|
-
/**
|
|
783
|
-
* ログエントリがフィルタ条件に一致するかチェック
|
|
784
|
-
*/
|
|
785
|
-
matchesFilter(entry, options) {
|
|
786
|
-
// レベルフィルタ
|
|
787
|
-
if (options.level && !options.level.includes(entry.level)) {
|
|
788
|
-
return false;
|
|
789
|
-
}
|
|
790
|
-
// タイプフィルタ
|
|
791
|
-
if (options.type && !options.type.includes(entry.type)) {
|
|
792
|
-
return false;
|
|
793
|
-
}
|
|
794
|
-
// 時間範囲フィルタ
|
|
795
|
-
if (options.since && entry.timestamp < options.since) {
|
|
796
|
-
return false;
|
|
797
|
-
}
|
|
798
|
-
if (options.until && entry.timestamp > options.until) {
|
|
799
|
-
return false;
|
|
800
|
-
}
|
|
801
|
-
return true;
|
|
802
|
-
}
|
|
803
|
-
/**
|
|
804
|
-
* ログのクリア
|
|
805
|
-
*/
|
|
806
|
-
async clearLogs() {
|
|
807
|
-
const filesToClear = [];
|
|
808
|
-
if (this.logInfo.logFile) {
|
|
809
|
-
filesToClear.push(this.logInfo.logFile);
|
|
810
|
-
}
|
|
811
|
-
if (this.logInfo.outFile) {
|
|
812
|
-
filesToClear.push(this.logInfo.outFile);
|
|
813
|
-
}
|
|
814
|
-
if (this.logInfo.errorFile) {
|
|
815
|
-
filesToClear.push(this.logInfo.errorFile);
|
|
816
|
-
}
|
|
817
|
-
for (const file of filesToClear) {
|
|
818
|
-
if (fs.existsSync(file)) {
|
|
819
|
-
fs.writeFileSync(file, '');
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
/**
|
|
824
|
-
* 残りの部分的な行を強制フラッシュ
|
|
825
|
-
*/
|
|
826
|
-
flushPartialLines() {
|
|
827
|
-
for (const [type, partialLine] of this.partialLines) {
|
|
828
|
-
if (partialLine.trim().length > 0) {
|
|
829
|
-
this.writeLogWithPreprocessing(type, partialLine);
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
this.partialLines.clear();
|
|
833
|
-
}
|
|
834
|
-
/**
|
|
835
|
-
* バッファ統計情報の取得
|
|
836
|
-
*/
|
|
837
|
-
getBufferStats() {
|
|
838
|
-
return this.logBuffer.getStats();
|
|
839
|
-
}
|
|
840
|
-
/**
|
|
841
|
-
* バッファを強制フラッシュ
|
|
842
|
-
*/
|
|
843
|
-
async flushBuffer() {
|
|
844
|
-
await this.logBuffer.flush();
|
|
845
|
-
}
|
|
846
|
-
/**
|
|
847
|
-
* リソースのクリーンアップ
|
|
848
|
-
*/
|
|
849
|
-
async close() {
|
|
850
|
-
// 残りの部分的な行をフラッシュ
|
|
851
|
-
this.flushPartialLines();
|
|
852
|
-
// バッファをクリーンアップ(フラッシュ含む)
|
|
853
|
-
await this.logBuffer.cleanup();
|
|
854
|
-
// ファイル監視停止
|
|
855
|
-
this.stopWatching();
|
|
856
|
-
// ストリーム管理は不要
|
|
857
|
-
this.logInfo.streams = {};
|
|
858
|
-
}
|
|
859
|
-
/**
|
|
860
|
-
* ログ統計情報の取得
|
|
861
|
-
*/
|
|
862
|
-
getLogStats() {
|
|
863
|
-
const stats = {};
|
|
864
|
-
try {
|
|
865
|
-
if (this.logInfo.logFile && fs.existsSync(this.logInfo.logFile)) {
|
|
866
|
-
const stat = fs.statSync(this.logInfo.logFile);
|
|
867
|
-
stats.logFileSize = stat.size;
|
|
868
|
-
stats.lastModified = stat.mtime.getTime();
|
|
869
|
-
}
|
|
870
|
-
if (this.logInfo.outFile && fs.existsSync(this.logInfo.outFile)) {
|
|
871
|
-
const stat = fs.statSync(this.logInfo.outFile);
|
|
872
|
-
stats.outFileSize = stat.size;
|
|
873
|
-
}
|
|
874
|
-
if (this.logInfo.errorFile && fs.existsSync(this.logInfo.errorFile)) {
|
|
875
|
-
const stat = fs.statSync(this.logInfo.errorFile);
|
|
876
|
-
stats.errorFileSize = stat.size;
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
catch (error) {
|
|
880
|
-
console.warn(`[AppLogger] Failed to get stats: ${error.message}`);
|
|
881
|
-
}
|
|
882
|
-
return stats;
|
|
883
|
-
}
|
|
884
|
-
}
|
|
15
|
+
import { FileManager } from './log-file-manager.js';
|
|
16
|
+
import { AppLogger } from './app-logger.js';
|
|
17
|
+
// Re-export everything from split modules for backward compatibility
|
|
18
|
+
export { ESCAPE_CHARS, } from './log-manager-types.js';
|
|
19
|
+
export { FileManager } from './log-file-manager.js';
|
|
20
|
+
export { LogPreprocessor } from './log-preprocessor.js';
|
|
21
|
+
export { DefaultLogLevelStrategy } from './log-level-strategy.js';
|
|
22
|
+
export { EventManager } from './log-event-manager.js';
|
|
23
|
+
export { LogBuffer } from './log-buffer.js';
|
|
24
|
+
export { AppLogger } from './app-logger.js';
|
|
885
25
|
// =============================================================================
|
|
886
26
|
// LogManager Class
|
|
887
27
|
// =============================================================================
|