# Issue #07: ロギング初期化

**Phase:** 1a
**優先度:** 中
**依存関係:** #06
**見積もり:** 0.5日

---

## 概要

loguruを使った構造化ロギングの初期化を実装する。
設定ファイルに基づいてログハンドラを設定する。

---

## TDD実装手順

### Step 1: Red（テストを書く）

```python
# tests/unit/core/test_logging.py
"""Tests for logging initialization."""
import pytest
from unittest.mock import patch, MagicMock
import tempfile
from pathlib import Path


class TestLoggingInit:
    """Test logging initialization."""

    def test_init_logging_removes_default(self):
        """Should remove default loguru handler."""
        from railway.core.logging import init_logging
        from railway.core.settings import LoggingSettings

        with patch("railway.core.logging.logger") as mock_logger:
            settings = LoggingSettings(level="INFO")
            init_logging(settings)

            mock_logger.remove.assert_called()

    def test_init_logging_adds_console_handler(self):
        """Should add console handler when configured."""
        from railway.core.logging import init_logging
        from railway.core.settings import LoggingSettings, LoggingHandlerSettings

        with patch("railway.core.logging.logger") as mock_logger:
            settings = LoggingSettings(
                level="DEBUG",
                handlers=[
                    LoggingHandlerSettings(type="console", level="DEBUG")
                ]
            )
            init_logging(settings)

            # Should call logger.add for console handler
            mock_logger.add.assert_called()

    def test_init_logging_adds_file_handler(self):
        """Should add file handler when configured."""
        from railway.core.logging import init_logging
        from railway.core.settings import LoggingSettings, LoggingHandlerSettings

        with tempfile.TemporaryDirectory() as tmpdir:
            log_path = Path(tmpdir) / "app.log"

            with patch("railway.core.logging.logger") as mock_logger:
                settings = LoggingSettings(
                    level="INFO",
                    handlers=[
                        LoggingHandlerSettings(
                            type="file",
                            path=str(log_path),
                            level="INFO",
                            rotation="1 day",
                            retention="7 days"
                        )
                    ]
                )
                init_logging(settings)

                mock_logger.add.assert_called()

    def test_init_logging_applies_format(self):
        """Should apply custom log format."""
        from railway.core.logging import init_logging
        from railway.core.settings import LoggingSettings, LoggingHandlerSettings

        custom_format = "{time:YYYY-MM-DD} | {level} | {message}"

        with patch("railway.core.logging.logger") as mock_logger:
            settings = LoggingSettings(
                level="INFO",
                format=custom_format,
                handlers=[
                    LoggingHandlerSettings(type="console", level="INFO")
                ]
            )
            init_logging(settings)

            # Check that format was passed to logger.add
            call_kwargs = mock_logger.add.call_args_list[0][1]
            assert call_kwargs.get("format") == custom_format


class TestLoggingWithSettings:
    """Test logging with Settings integration."""

    def test_settings_initializes_logging(self):
        """Settings should initialize logging on creation."""
        from railway.core.settings import Settings

        yaml_content = """
logging:
  level: DEBUG
  handlers:
    - type: console
      level: DEBUG
"""
        with tempfile.TemporaryDirectory() as tmpdir:
            config_dir = Path(tmpdir) / "config"
            config_dir.mkdir()
            (config_dir / "development.yaml").write_text(yaml_content)

            with patch("railway.core.settings.init_logging") as mock_init:
                settings = Settings(_config_dir=str(config_dir))

                # Logging should be initialized
                # (May or may not be called depending on implementation)


class TestLoggingHelpers:
    """Test logging helper functions."""

    def test_get_logger(self):
        """Should return loguru logger."""
        from railway.core.logging import get_logger

        log = get_logger()

        # Should be loguru logger
        assert hasattr(log, "info")
        assert hasattr(log, "debug")
        assert hasattr(log, "error")
        assert hasattr(log, "success")

    def test_get_logger_with_context(self):
        """Should return logger with context."""
        from railway.core.logging import get_logger

        log = get_logger("my_module")

        # Should support binding context
        assert log is not None
```

```bash
# 実行して失敗を確認
pytest tests/unit/core/test_logging.py -v
# Expected: FAILED (ImportError)
```

### Step 2: Green（最小限の実装）

```python
# railway/core/logging.py
"""
Logging initialization for Railway Framework.
"""
import sys
from loguru import logger
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from railway.core.settings import LoggingSettings


def init_logging(settings: "LoggingSettings") -> None:
    """
    Initialize loguru based on configuration.

    Steps:
    1. Remove default handler
    2. Add configured handlers (console, file)
    3. Apply log level and format

    Args:
        settings: LoggingSettings instance
    """
    # Remove default handler
    logger.remove()

    # Add handlers from config
    for handler_config in settings.handlers:
        if handler_config.type == "console":
            logger.add(
                sys.stderr,
                level=handler_config.level,
                format=settings.format,
                colorize=True,
            )
        elif handler_config.type == "file":
            if handler_config.path:
                logger.add(
                    handler_config.path,
                    level=handler_config.level,
                    format=settings.format,
                    rotation=handler_config.rotation,
                    retention=handler_config.retention,
                    encoding="utf-8",
                )

    # If no handlers configured, add default console handler
    if not settings.handlers:
        logger.add(
            sys.stderr,
            level=settings.level,
            format=settings.format,
            colorize=True,
        )

    logger.debug(f"Logging initialized (level={settings.level})")


def get_logger(name: str | None = None) -> "logger":
    """
    Get loguru logger instance.

    Args:
        name: Optional context name (for future use)

    Returns:
        loguru.logger instance
    """
    if name:
        return logger.bind(context=name)
    return logger


# Export logger for convenience
__all__ = ["init_logging", "get_logger", "logger"]
```

```bash
# 実行して成功を確認
pytest tests/unit/core/test_logging.py -v
# Expected: PASSED
```

### Step 3: Refactor

- エラーハンドリングの追加
- ログディレクトリの自動作成

---

## 完了条件

- [ ] デフォルトハンドラが削除される
- [ ] consoleハンドラが追加される
- [ ] fileハンドラが追加される（設定時）
- [ ] カスタムフォーマットが適用される
- [ ] rotation/retentionが設定される
- [ ] `get_logger()`が動作する
- [ ] テストカバレッジ90%以上

---

## 次のIssue

- #08: railway init コマンド
