feat: PHP 로그 메서드 추가 및 로그 추출 기능 개선

This commit is contained in:
2026-02-07 06:18:10 +00:00
parent 952c0d28c6
commit 15989e0436
+169 -11
View File
@@ -400,16 +400,95 @@ const findPhpAssignments = (text) => {
return assignments;
};
const PHP_LOG_METHODS = {
pushGeneralActionLog: { scope: 'GENERAL', category: 'ACTION', generalMethod: true },
pushGeneralHistoryLog: { scope: 'GENERAL', category: 'HISTORY', generalMethod: true },
pushNationalActionLog: { scope: 'NATION', category: 'ACTION', generalMethod: false },
pushNationalHistoryLog: { scope: 'NATION', category: 'HISTORY', generalMethod: false },
pushGlobalActionLog: { scope: 'SYSTEM', category: 'ACTION', generalMethod: false },
pushGlobalHistoryLog: { scope: 'SYSTEM', category: 'HISTORY', generalMethod: false },
};
const findPhpActorGeneralVars = (text) => {
const vars = new Set();
const regex = /\$([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\$this->generalObj\s*;/g;
while (true) {
const match = regex.exec(text);
if (!match) {
break;
}
vars.add(match[1]);
}
return vars;
};
const findPhpActorLoggerVars = (text, actorGeneralVars) => {
const vars = new Set(['logger']);
const regex =
/\$([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(\$this->generalObj|\$[A-Za-z_][A-Za-z0-9_]*)\s*->\s*getLogger\s*\(\s*\)\s*;/g;
while (true) {
const match = regex.exec(text);
if (!match) {
break;
}
const lhs = match[1];
const rhs = match[2];
if (rhs === '$this->generalObj') {
vars.add(lhs);
continue;
}
const generalVar = rhs.slice(1);
if (actorGeneralVars.has(generalVar)) {
vars.add(lhs);
}
}
return vars;
};
const isPhpActorLoggerExpr = (calleeExpr, actorGeneralVars, actorLoggerVars) => {
if (!calleeExpr) {
return false;
}
if (calleeExpr.includes('$this->generalObj->getLogger(')) {
return true;
}
for (const actorGeneralVar of actorGeneralVars) {
if (calleeExpr.includes(`$${actorGeneralVar}->getLogger(`)) {
return true;
}
}
const variableOnly = calleeExpr.match(/^\$([A-Za-z_][A-Za-z0-9_]*)$/);
if (variableOnly) {
return actorLoggerVars.has(variableOnly[1]);
}
return false;
};
const extractPhpLogCalls = (text, assignments) => {
const results = [];
const regex = /pushGeneralActionLog\s*\(/g;
const lineStarts = buildLineIndex(text);
const actorGeneralVars = findPhpActorGeneralVars(text);
const actorLoggerVars = findPhpActorLoggerVars(text, actorGeneralVars);
const regex =
/([$\w><:\-\(\)]+)\s*->\s*(pushGeneralActionLog|pushGeneralHistoryLog|pushNationalActionLog|pushNationalHistoryLog|pushGlobalActionLog|pushGlobalHistoryLog)\s*\(/g;
while (true) {
const match = regex.exec(text);
if (!match) {
break;
}
const calleeExpr = match[1]?.trim() ?? '';
const methodName = match[2];
const methodMeta = PHP_LOG_METHODS[methodName];
if (!methodMeta) {
continue;
}
const startIndex = match.index + match[0].length;
const { value, endIndex, endedBy } = scanToFirstArgumentEnd(text, startIndex);
let format = null;
@@ -422,13 +501,19 @@ const extractPhpLogCalls = (text, assignments) => {
format = formatMatch[1];
}
}
const parsed = parsePhpExprToTemplate(value, assignments, match.index);
const hasGeneralId = methodMeta.generalMethod && !isPhpActorLoggerExpr(calleeExpr, actorGeneralVars, actorLoggerVars);
results.push({
pos: match.index,
raw: value.trim(),
template: parsed.template,
line: getLineNumber(lineStarts, match.index),
format,
category: methodMeta.category,
scope: methodMeta.scope,
hasGeneralId,
});
}
@@ -574,7 +659,80 @@ const extractTsLogs = (filePath, text) => {
};
visit(sourceFile);
return results;
if (results.length > 0) {
return results;
}
// Wrapper 파일(event_*.ts)에서 팩토리 호출만 있는 경우 로그를 보완 추출한다.
const factoryResults = [];
const visitFactory = (node) => {
if (!ts.isCallExpression(node) || !ts.isIdentifier(node.expression)) {
ts.forEachChild(node, visitFactory);
return;
}
if (node.expression.text !== 'createEventResearchCommand') {
ts.forEachChild(node, visitFactory);
return;
}
const [arg] = node.arguments;
if (!arg || !ts.isObjectLiteralExpression(arg)) {
ts.forEachChild(node, visitFactory);
return;
}
const nameProp = arg.properties.find(
(prop) =>
ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === 'name'
);
if (!nameProp || !ts.isPropertyAssignment(nameProp)) {
ts.forEachChild(node, visitFactory);
return;
}
const nameExpr = nameProp.initializer;
if (!ts.isStringLiteral(nameExpr) && !ts.isNoSubstitutionTemplateLiteral(nameExpr)) {
ts.forEachChild(node, visitFactory);
return;
}
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
factoryResults.push(
{
kind: 'factory',
template: '<M>${}</> 완료',
raw: '`<M>${ACTION_NAME}</> 완료`',
line: line + 1,
category: 'ACTION',
scope: 'GENERAL',
format: 'MONTH',
hasGeneralId: false,
isProperty: false,
},
{
kind: 'factory',
template: '<M>${}</> 완료',
raw: '`<M>${ACTION_NAME}</> 완료`',
line: line + 1,
category: 'HISTORY',
scope: 'GENERAL',
format: 'YEAR_MONTH',
hasGeneralId: false,
isProperty: false,
},
{
kind: 'factory',
template: '<Y>${}</>${} <M>${}</> 완료',
raw: '`<Y>${generalName}</>${josaYi} <M>${ACTION_NAME}</> 완료`',
line: line + 1,
category: 'HISTORY',
scope: 'NATION',
format: 'YEAR_MONTH',
hasGeneralId: false,
isProperty: false,
}
);
ts.forEachChild(node, visitFactory);
};
visitFactory(sourceFile);
return factoryResults;
};
const normalizeTemplate = (text) => {
@@ -600,13 +758,7 @@ const isTargetLog = (entry) => {
if (!entry) {
return false;
}
if (entry.format === 'PLAIN') {
return true;
}
if (entry.hasGeneralId) {
return true;
}
return false;
return !!entry.hasGeneralId;
};
const loadIgnoreConfig = async () => {
@@ -666,7 +818,7 @@ const shouldIgnoreTemplate = (key, template, rules) => {
return false;
};
const shouldIncludeTsEntry = (entry) => {
const shouldIncludeEntryByMode = (entry) => {
if (mode === 'all') {
return true;
}
@@ -743,9 +895,15 @@ const loadPhpLogs = async () => {
line: log.line,
template: log.template,
raw: log.raw,
category: log.category,
scope: log.scope,
format: log.format,
hasGeneralId: log.hasGeneralId,
}));
const filtered = normalized.filter((entry) => {
if (!shouldIncludeEntryByMode(entry)) {
return false;
}
if (excludeGuards && isGuardLog(entry.template)) {
return false;
}
@@ -785,7 +943,7 @@ const loadTsLogs = async () => {
const text = await fs.readFile(file, 'utf-8');
const entries = extractTsLogs(file, text);
const filtered = entries
.filter((entry) => shouldIncludeTsEntry(entry))
.filter((entry) => shouldIncludeEntryByMode(entry))
.map((entry) => ({
file: path.relative(ROOT_DIR, file),
line: entry.line,