# 로그 처리 계획 (Log Handling Plan) ## 문제 상황 터미널에 너무 많은 로그가 출력되어 정신없음 ### 발생 로그 예시 ``` info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (2ms) [Parameters=[@__u_Timestamp_2='?' (DbType = DateTime), @__u_Value_1='?', @__u_NodeId_0='?'], CommandType='Text', CommandTimeout='30'] UPDATE realtime_table AS r SET timestamp = @__u_Timestamp_2, livevalue = @__u_Value_1 WHERE r.node_id = @__u_NodeId_0 info: ExperionCrawler.Infrastructure.OpcUa.ExperionRealtimeService[0] Update realtime info. ``` ### 원인 분석 1. **EF Core DB Command 로그** (`Microsoft.EntityFrameworkCore.Database.Command`) - `appsettings.json`에서 `Microsoft.EntityFrameworkCore.Database.Command`가 `"Information"`으로 설정 - 매 500ms마다 `UPDATE realtime_table` 쿼리 실행 시 로그 출력 2. **ExperionRealtimeService 로그** - `FlushPendingAsync()`에서 `_logger.LogDebug()`로 배치 업데이트 로그 남김 - `ExperionCrawler.Infrastructure.OpcUa.ExperionRealtimeService` 카테고리가 `"Information"`으로 설정 --- ## 해결 방안 ### 제안 1: appsettings.json 수정 (가장 간단) ```json { "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning", "Microsoft.EntityFrameworkCore": "Information", "Microsoft.EntityFrameworkCore.Database.Command": "Warning", "ExperionCrawler.Infrastructure.OpcUa.ExperionRealtimeService": "Warning" } } } ``` **효과:** - `UPDATE realtime_table` 쿼리 로그 숨김 - `[Realtime] 배치 업데이트: X/Y건` 로그 숨김 - 경고/오류만 표시 --- ### 제안 2: 개발/배포 환경 분리 (추천) **appsettings.json** (배포용 - 로그 적게) ```json { "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning", "Microsoft.EntityFrameworkCore": "Warning", "Microsoft.EntityFrameworkCore.Database.Command": "Warning", "ExperionCrawler": "Information" } } } ``` **appsettings.Development.json** (개발용 - 로그 많게) ```json { "Logging": { "LogLevel": { "Default": "Debug", "Microsoft.EntityFrameworkCore": "Debug", "Microsoft.EntityFrameworkCore.Database.Command": "Information", "ExperionCrawler": "Debug" } } } ``` **사용 방법:** - 개발 모드: `dotnet run` → 로그 자세히 보임 - 배포 모드: `dotnet run --configuration Release` → 로그 적게 보임 --- ### 제안 3: 코드에서 로그 레벨 조정 **Program.cs**에서 로그 필터 추가: ```csharp builder.Logging.AddFilter("Microsoft.EntityFrameworkCore.Database.Command", LogLevel.Warning); builder.Logging.AddFilter("ExperionCrawler", LogLevel.Information); ``` **ExperionRealtimeService.cs**의 `FlushPendingAsync()`에서: - `_logger.LogDebug()` → `_logger.LogTrace()` (가장 자세한 로그는 기본적으로 안 보임) --- ### 제안 4: 주석 처리 + 나중에 복구용 **appsettings.json**에 주석으로 메모: ```json { "Logging": { "LogLevel": { // 개발 시 주석 해제: 로그 자세히 보기 // "Microsoft.EntityFrameworkCore.Database.Command": "Information", // "ExperionCrawler": "Debug", // 배포 시: 로그 적게 (기본) "Microsoft.EntityFrameworkCore.Database.Command": "Warning", "ExperionCrawler": "Information" } } } ``` --- ## 검수 체크리스트 - [ ] 로그 레벨 조정 후 터미널이 깔끔한지 확인 - [ ] 필요한 로그가 사라지지 않았는지 확인 - [ ] 개발 모드에서 로그를 다시 볼 수 있는지 확인 --- ## 참고: 로그 레벨 순서 ``` Trace (가장 자세함 - 기본적으로 안 보임) Debug (개발 시 유용 - 기본적으로 안 보임) Information (일반 로그 - 기본 표시) Warning (경고 - 기본 표시) Error (오류 - 기본 표시) Critical (치명적 오류 - 기본 표시) ``` --- ## 제안 5: UI에서 동적 로그 레벨 설정 (추천 + 개발자 친화) ### 개요 웹 UI에서 실시간으로 로그 레벨을 조정할 수 있도록 API를 추가하여, 개발 중에 코드 수정 없이 로그를 켜고 끌 수 있게 함. ### 아키텍처 ``` ┌─────────────────────────────────────────────────────────────┐ │ Web UI (app.js) │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ 로그 레벨 설정 UI (Toggle Switch) │ │ │ │ - EF Core Query Log (ON/OFF) │ │ │ │ - Realtime Service Log (ON/OFF) │ │ │ │ - Debug Mode (ON/OFF) │ │ │ └──────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ LogSettingsController (API) │ │ - GET /api/log/settings → 현재 설정 조회 │ │ - POST /api/log/settings → 설정 업데이트 │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ LogSettingsService (비즈니스 로직) │ │ - 로그 레벨 설정 저장 (appsettings.json) │ │ - 로거 리로드 (새로운 설정 적용) │ └─────────────────────────────────────────────────────────────┘ ``` ### 구현 계획 #### 1. DTO 정의 (`src/Core/Application/DTOs/LogSettingsDtos.cs`) ```csharp namespace ExperionCrawler.Core.Application.DTOs; public class LogSettingsRequest { public bool? EnableEfCoreQueryLog { get; set; } public bool? EnableRealtimeServiceLog { get; set; } public bool? EnableDebugMode { get; set; } } public class LogSettingsResponse { public bool EfCoreQueryLog { get; set; } public bool RealtimeServiceLog { get; set; } public bool DebugMode { get; set; } } ``` #### 2. 서비스 인터페이스 (`src/Core/Application/Interfaces/ILogSettingsService.cs`) ```csharp namespace ExperionCrawler.Core.Application.Interfaces; public interface ILogSettingsService { Task GetSettingsAsync(); Task UpdateSettingsAsync(LogSettingsRequest request); } ``` #### 3. 서비스 구현 (`src/Core/Application/Services/LogSettingsService.cs`) ```csharp namespace ExperionCrawler.Core.Application.Services; public class LogSettingsService : ILogSettingsService { private readonly IConfiguration _config; private readonly ILogger _logger; public LogSettingsService(IConfiguration config, ILogger logger) { _config = config; _logger = logger; } public async Task GetSettingsAsync() { var loggingSection = _config.GetSection("Logging"); return new LogSettingsResponse { EfCoreQueryLog = IsLogLevelEnabled(loggingSection, "Microsoft.EntityFrameworkCore.Database.Command", "Information"), RealtimeServiceLog = IsLogLevelEnabled(loggingSection, "ExperionCrawler", "Information"), DebugMode = IsLogLevelEnabled(loggingSection, "Default", "Debug") }; } public async Task UpdateSettingsAsync(LogSettingsRequest request) { // appsettings.json 읽기 var configPath = "appsettings.json"; var json = await File.ReadAllTextAsync(configPath); var doc = JsonDocument.Parse(json); // 로그 레벨 업데이트 // (구현은 간단히 appsettings.Development.json 사용하는 방식으로) _logger.LogInformation("[LogSettings] 설정 업데이트: {Request}", request); } private bool IsLogLevelEnabled(IConfigurationSection section, string category, string level) { var logLevelSection = section.GetSection("LogLevel"); var value = logLevelSection[category]; return !string.IsNullOrEmpty(value) && value == level; } } ``` #### 4. 컨트롤러 (`src/Web/Controllers/LogSettingsController.cs`) ```csharp namespace ExperionCrawler.Web.Controllers; [ApiController] [Route("api/[controller]")] public class LogSettingsController : ControllerBase { private readonly ILogSettingsService _service; public LogSettingsController(ILogSettingsService service) { _service = service; } [HttpGet] public async Task> GetSettings() { var settings = await _service.GetSettingsAsync(); return Ok(new { efCoreQueryLog = settings.EfCoreQueryLog, realtimeServiceLog = settings.RealtimeServiceLog, debugMode = settings.DebugMode }); } [HttpPost] public async Task UpdateSettings([FromBody] LogSettingsRequest request) { await _service.UpdateSettingsAsync(request); return Ok(); } } ``` #### 5. UI 변경 (`src/Web/wwwroot/js/app.js`) ```javascript // 로그 설정 UI 추가 function createLogSettingsUI() { const container = document.createElement('div'); container.style.cssText = ` position: fixed; top: 10px; right: 10px; background: rgba(0,0,0,0.8); padding: 15px; border-radius: 8px; z-index: 1000; color: white; font-family: monospace; `; container.innerHTML = `

Log Settings

`; document.body.appendChild(container); // 초기 설정 로드 loadLogSettings(); // 체크박스 이벤트 document.getElementById('log-efcore').addEventListener('change', (e) => updateLogSetting('efCoreQueryLog', e.target.checked)); document.getElementById('log-realtime').addEventListener('change', (e) => updateLogSetting('realtimeServiceLog', e.target.checked)); document.getElementById('log-debug').addEventListener('change', (e) => updateLogSetting('debugMode', e.target.checked)); } async function loadLogSettings() { try { const res = await fetch('/api/logsettings'); const data = await res.json(); document.getElementById('log-efcore').checked = data.efCoreQueryLog; document.getElementById('log-realtime').checked = data.realtimeServiceLog; document.getElementById('log-debug').checked = data.debugMode; } catch (e) { console.error('Failed to load log settings:', e); } } async function updateLogSetting(key, value) { try { await fetch('/api/logsettings', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ [key]: value }) }); } catch (e) { console.error('Failed to update log setting:', e); } } ``` #### 6. Program.cs 등록 ```csharp // LogSettingsService 등록 builder.Services.AddScoped(); ``` ### 사용 방법 1. 웹 UI에 로그 설정 패널이 오른쪽 상단에 표시됨 2. 체크박스를 켜고 끄면 실시간으로 로그 레벨이 변경됨 3. 설정은 세션 간 유지되지 않음 (개발 편의용) ### 장점 - 코드 수정 없이 로그 테스트 가능 - 실시간으로 로그 켜고 끌 수 있음 - UI에서 직관적으로 조작 가능 ### 단점 - 설정이 메모리에만 저장됨 (재시작 시 초기화) - 영구 설정을 원하면 appsettings.json 수정 필요 ---