# 로그 처리 계획 (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<LogSettingsResponse> 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<LogSettingsService> _logger;

    public LogSettingsService(IConfiguration config, ILogger<LogSettingsService> logger)
    {
        _config = config;
        _logger = logger;
    }

    public async Task<LogSettingsResponse> 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<ActionResult<LogSettingsResponse>> GetSettings()
    {
        var settings = await _service.GetSettingsAsync();
        return Ok(new
        {
            efCoreQueryLog = settings.EfCoreQueryLog,
            realtimeServiceLog = settings.RealtimeServiceLog,
            debugMode = settings.DebugMode
        });
    }

    [HttpPost]
    public async Task<ActionResult> 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 = `
        <h3 style="margin: 0 0 10px 0; font-size: 14px;">Log Settings</h3>
        <div style="display: flex; flex-direction: column; gap: 5px;">
            <label style="display: flex; align-items: center; gap: 5px; font-size: 12px;">
                <input type="checkbox" id="log-efcore"> EF Core Query
            </label>
            <label style="display: flex; align-items: center; gap: 5px; font-size: 12px;">
                <input type="checkbox" id="log-realtime"> Realtime Service
            </label>
            <label style="display: flex; align-items: center; gap: 5px; font-size: 12px;">
                <input type="checkbox" id="log-debug"> Debug Mode
            </label>
        </div>
    `;
    
    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<ILogSettingsService, LogSettingsService>();
```

### 사용 방법

1. 웹 UI에 로그 설정 패널이 오른쪽 상단에 표시됨
2. 체크박스를 켜고 끄면 실시간으로 로그 레벨이 변경됨
3. 설정은 세션 간 유지되지 않음 (개발 편의용)

### 장점
- 코드 수정 없이 로그 테스트 가능
- 실시간으로 로그 켜고 끌 수 있음
- UI에서 직관적으로 조작 가능

### 단점
- 설정이 메모리에만 저장됨 (재시작 시 초기화)
- 영구 설정을 원하면 appsettings.json 수정 필요

---
