PART 10 - 강의 3/6

로깅 시스템

커스텀 로그 카테고리, UE_LOG 레벨별 사용법

01

UE_LOG 기초

언리얼 엔진의 기본 로깅 시스템

기본 사용법

Basic Logging // 기본 형식 UE_LOG(LogCategory, Verbosity, TEXT("Format string"), Args...); // 예제 UE_LOG(LogTemp, Log, TEXT("Simple message")); UE_LOG(LogTemp, Warning, TEXT("Player health: %f"), Health); UE_LOG(LogTemp, Error, TEXT("Failed to load: %s"), *AssetPath); // 조건부 로깅 UE_CLOG(bCondition, LogTemp, Warning, TEXT("Condition is true!"));

로그 Verbosity 레벨

Verbosity 설명 색상 사용 시점
Fatal 치명적 오류 (크래시 발생) 빨강 복구 불가능한 오류
Error 오류 빨강 기능 실패, 예외 상황
Warning 경고 노랑 잠재적 문제, 비정상 상황
Display 중요 정보 회색 항상 표시해야 할 정보
Log 일반 로그 회색 일반적인 정보
Verbose 상세 정보 회색 디버깅용 상세 정보
VeryVerbose 매우 상세 회색 극단적 디버깅
Verbosity 표시 설정

기본적으로 Verbose 이하는 출력되지 않습니다. 콘솔에서 설정:

Log CategoryName Verbose
02

커스텀 로그 카테고리

프로젝트에 맞는 전용 로그 카테고리 생성

로그 카테고리 선언

MyGame.h #pragma once #include "CoreMinimal.h" // 로그 카테고리 선언 (헤더에서) // DECLARE_LOG_CATEGORY_EXTERN(CategoryName, DefaultVerbosity, CompileTimeVerbosity) // 게임 전체 로그 DECLARE_LOG_CATEGORY_EXTERN(LogMyGame, Log, All); // 시스템별 로그 DECLARE_LOG_CATEGORY_EXTERN(LogCombat, Log, All); DECLARE_LOG_CATEGORY_EXTERN(LogInventory, Log, All); DECLARE_LOG_CATEGORY_EXTERN(LogQuest, Log, All); DECLARE_LOG_CATEGORY_EXTERN(LogAI, Log, All); DECLARE_LOG_CATEGORY_EXTERN(LogNetwork, Log, All); // 성능 프로파일링용 (기본 비활성) DECLARE_LOG_CATEGORY_EXTERN(LogPerformance, Verbose, All);

로그 카테고리 정의

MyGame.cpp #include "MyGame.h" // 로그 카테고리 정의 (소스 파일에서) DEFINE_LOG_CATEGORY(LogMyGame); DEFINE_LOG_CATEGORY(LogCombat); DEFINE_LOG_CATEGORY(LogInventory); DEFINE_LOG_CATEGORY(LogQuest); DEFINE_LOG_CATEGORY(LogAI); DEFINE_LOG_CATEGORY(LogNetwork); DEFINE_LOG_CATEGORY(LogPerformance);

사용 예제

Usage Examples // 전투 시스템 void UCombatComponent::ApplyDamage(float Damage, AActor* Source) { UE_LOG(LogCombat, Log, TEXT("Damage applied: %.1f from %s"), Damage, *GetNameSafe(Source)); if (Damage > MaxHealth) { UE_LOG(LogCombat, Warning, TEXT("Damage %.1f exceeds MaxHealth %.1f"), Damage, MaxHealth); } CurrentHealth -= Damage; if (CurrentHealth <= 0) { UE_LOG(LogCombat, Display, TEXT("%s has been defeated"), *GetOwner()->GetName()); } } // 인벤토리 시스템 bool UInventoryComponent::AddItem(const FItemData& Item) { UE_LOG(LogInventory, Verbose, TEXT("Attempting to add item: %s x%d"), *Item.ItemId.ToString(), Item.Quantity); if (IsFull()) { UE_LOG(LogInventory, Warning, TEXT("Inventory full, cannot add %s"), *Item.ItemId.ToString()); return false; } Items.Add(Item); UE_LOG(LogInventory, Log, TEXT("Added %s to inventory"), *Item.ItemId.ToString()); return true; } // 네트워크 void AMyGameMode::OnPlayerJoined(APlayerController* PC) { UE_LOG(LogNetwork, Display, TEXT("Player joined: %s"), *PC->GetPlayerState<APlayerState>()->GetPlayerName()); }
로그 카테고리 설계 가이드
  • 시스템별 분리: Combat, Inventory, Quest 등 기능 단위로 분리
  • 적절한 기본 레벨: 일반 로그는 Log, 상세 디버깅은 Verbose
  • 일관된 네이밍: Log 접두사 + 시스템명
  • 필터링 용이성: 콘솔에서 카테고리별 필터링 가능
03

로그 포맷팅과 고급 기능

효과적인 로그 메시지 작성법

포맷 스트링

Format Strings // 기본 타입 UE_LOG(LogTemp, Log, TEXT("Int: %d"), IntValue); UE_LOG(LogTemp, Log, TEXT("Float: %f (2dp: %.2f)"), FloatValue, FloatValue); UE_LOG(LogTemp, Log, TEXT("Bool: %s"), bValue ? TEXT("true") : TEXT("false")); // FString - * 연산자로 TCHAR* 변환 FString MyString = TEXT("Hello"); UE_LOG(LogTemp, Log, TEXT("String: %s"), *MyString); // FName FName MyName = FName(TEXT("ItemName")); UE_LOG(LogTemp, Log, TEXT("Name: %s"), *MyName.ToString()); // FVector FVector Location = FVector(100, 200, 300); UE_LOG(LogTemp, Log, TEXT("Location: %s"), *Location.ToString()); // 객체 이름 (null 안전) UE_LOG(LogTemp, Log, TEXT("Actor: %s"), *GetNameSafe(MyActor)); // 클래스 이름 UE_LOG(LogTemp, Log, TEXT("Class: %s"), *GetClass()->GetName()); // 전체 경로 UE_LOG(LogTemp, Log, TEXT("Full path: %s"), *GetPathName());

함수/파일 정보 포함

Context Information // 함수명 포함 UE_LOG(LogMyGame, Log, TEXT("[%s] Message"), ANSI_TO_TCHAR(__FUNCTION__)); // 함수명 + 라인 번호 UE_LOG(LogMyGame, Log, TEXT("[%s:%d] Message"), ANSI_TO_TCHAR(__FUNCTION__), __LINE__); // 커스텀 매크로 정의 #define LOG_FUNC() \ UE_LOG(LogMyGame, Log, TEXT("[%s]"), ANSI_TO_TCHAR(__FUNCTION__)) #define LOG_WARN(Format, ...) \ UE_LOG(LogMyGame, Warning, TEXT("[%s] ") TEXT(Format), \ ANSI_TO_TCHAR(__FUNCTION__), ##__VA_ARGS__) // 사용 void UMyClass::SomeFunction() { LOG_FUNC(); // "[UMyClass::SomeFunction]" LOG_WARN("Value is %d", Value); // "[UMyClass::SomeFunction] Value is 42" }

화면 출력 (디버그용)

On-Screen Debug // GEngine 화면 메시지 if (GEngine) { // AddOnScreenDebugMessage(Key, Duration, Color, Message) // Key: -1 = 새 메시지, 양수 = 같은 키 덮어쓰기 GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::White, TEXT("일반 메시지")); GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Green, FString::Printf(TEXT("Health: %.1f"), Health)); GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("경고!")); // 같은 키로 업데이트되는 메시지 (HUD 느낌) GEngine->AddOnScreenDebugMessage(1, 0.0f, FColor::Yellow, FString::Printf(TEXT("FPS: %.1f"), 1.0f / DeltaTime)); } // 커스텀 매크로 #define SCREEN_LOG(Format, ...) \ if (GEngine) { \ GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Cyan, \ FString::Printf(TEXT(Format), ##__VA_ARGS__)); \ } // 사용 SCREEN_LOG("Player took %d damage", DamageAmount);
04

로그 필터링과 출력 제어

콘솔 명령과 설정을 통한 로그 관리

콘솔 명령어

Console Commands // 특정 카테고리의 Verbosity 변경 Log LogCombat Verbose // Verbose까지 출력 Log LogCombat Warning // Warning 이상만 출력 Log LogCombat off // 로그 비활성화 Log LogCombat break // Error에서 중단점 // 현재 로그 설정 확인 Log List // 로그 파일 출력 Log LogCombat VeryVerbose OutputLogFile // 모든 로그 필터링 Log * Warning // 모든 카테고리 Warning만

DefaultEngine.ini 설정

DefaultEngine.ini [Core.Log] ; 전역 로그 레벨 Global=Warning ; 카테고리별 설정 LogCombat=Log LogInventory=Log LogQuest=Verbose LogPerformance=off ; 개발 빌드에서만 상세 로그 #if !UE_BUILD_SHIPPING LogAI=Verbose LogNetwork=Verbose #endif

코드에서 동적 제어

Dynamic Log Control // 런타임에 로그 레벨 변경 void UMyDebugSettings::EnableVerboseLogging(bool bEnable) { if (bEnable) { UE_SET_LOG_VERBOSITY(LogCombat, Verbose); UE_SET_LOG_VERBOSITY(LogInventory, Verbose); } else { UE_SET_LOG_VERBOSITY(LogCombat, Log); UE_SET_LOG_VERBOSITY(LogInventory, Log); } } // 콘솔 명령으로 노출 static FAutoConsoleCommand CmdEnableVerbose( TEXT("MyGame.EnableVerboseLog"), TEXT("Enable verbose logging for game systems"), FConsoleCommandDelegate::CreateLambda([]() { UE_SET_LOG_VERBOSITY(LogCombat, Verbose); UE_SET_LOG_VERBOSITY(LogInventory, Verbose); UE_LOG(LogMyGame, Display, TEXT("Verbose logging enabled")); }) ); // 조건부 컴파일 #if !UE_BUILD_SHIPPING // 개발 빌드에서만 상세 로그 UE_LOG(LogPerformance, Verbose, TEXT("Frame time: %f ms"), DeltaTime * 1000); #endif
05

로깅 베스트 프랙티스

효과적인 로그 작성 가이드라인

로그 레벨 선택 가이드

Fatal / Error

  • 복구 불가능한 상태
  • 필수 리소스 로드 실패
  • 크리티컬 시스템 오류

Warning

  • 예상치 못한 상황
  • 성능 저하 가능성
  • Fallback 사용 시

Display / Log

  • 중요 이벤트 기록
  • 상태 변화 추적
  • 시스템 초기화/종료

Verbose

  • 상세 디버깅 정보
  • 루프 내 상태
  • 성능 측정 데이터

좋은 로그 메시지 예시

Good vs Bad Logging // 나쁜 예 - 정보 부족 UE_LOG(LogTemp, Error, TEXT("Failed")); UE_LOG(LogTemp, Log, TEXT("Done")); UE_LOG(LogTemp, Warning, TEXT("Problem")); // 좋은 예 - 컨텍스트 포함 UE_LOG(LogInventory, Error, TEXT("Failed to add item %s: inventory full (current: %d, max: %d)"), *ItemId.ToString(), CurrentCount, MaxCapacity); UE_LOG(LogQuest, Log, TEXT("Quest '%s' completed by player %s in %.1f seconds"), *QuestName, *PlayerName, CompletionTime); UE_LOG(LogCombat, Warning, TEXT("Damage calculation overflow: raw=%.0f, clamped to %.0f for %s"), RawDamage, ClampedDamage, *GetNameSafe(Target)); // 구조화된 로깅 UE_LOG(LogAI, Verbose, TEXT("[AI:%s] State: %s -> %s | Target: %s | Distance: %.1f"), *GetOwner()->GetName(), *UEnum::GetValueAsString(PreviousState), *UEnum::GetValueAsString(CurrentState), *GetNameSafe(CurrentTarget), DistanceToTarget);

성능 고려사항

로깅 성능 주의사항
  • Tick 함수 내 과도한 로깅은 성능 저하 유발
  • FString::Printf 호출도 비용이 있음
  • Shipping 빌드에서 Verbose 로그 제거 고려
Performance-Aware Logging // 조건부 컴파일로 Shipping 빌드에서 제외 #if !UE_BUILD_SHIPPING UE_LOG(LogPerformance, Verbose, TEXT("Tick: %f ms"), DeltaTime * 1000); #endif // 또는 CLOG로 조건부 실행 static int32 FrameCounter = 0; UE_CLOG(++FrameCounter % 60 == 0, LogPerformance, Log, TEXT("60 frames processed")); // 로그 레벨 체크 후 문자열 생성 #if !NO_LOGGING if (UE_LOG_ACTIVE(LogAI, Verbose)) { // 비용이 큰 문자열 생성은 로그가 활성화될 때만 FString DetailedState = BuildDetailedStateString(); UE_LOG(LogAI, Verbose, TEXT("%s"), *DetailedState); } #endif
SUMMARY

핵심 요약

  • DECLARE_LOG_CATEGORY_EXTERNDEFINE_LOG_CATEGORY로 커스텀 로그 카테고리 생성
  • Verbosity 레벨: Fatal, Error, Warning, Display, Log, Verbose, VeryVerbose
  • UE_CLOG로 조건부 로깅, GEngine->AddOnScreenDebugMessage로 화면 출력
  • 콘솔 명령 Log CategoryName Level로 런타임 필터링
  • 컨텍스트 정보(함수명, 객체명, 값)를 포함한 의미있는 로그 메시지 작성
다음 강의 예고

다음 강의에서는 Visual Logger를 사용하여 AI와 게임플레이를 시각적으로 디버깅하는 방법을 학습합니다.

PRACTICE

도전 과제

배운 내용을 직접 실습해보세요

실습 1: 커스텀 로그 카테고리 정의

DECLARE_LOG_CATEGORY_EXTERN과 DEFINE_LOG_CATEGORY로 LogRPGCombat, LogRPGInventory, LogRPGQuest 카테고리를 만드세요. UE_LOG에서 카테고리별로 로그 레벨(Log, Warning, Error)을 구분하세요.

실습 2: 조건부 로깅 시스템

UE_CLOG로 조건부 로깅을 구현하고, Shipping 빌드에서 로그가 제거되는 것을 확인하세요. FMsg::Logf, GEngine->AddOnScreenDebugMessage도 활용하세요.

심화 과제: 로그 파싱 및 분석 도구

Output Log의 로그를 파싱하여 에러/경고를 자동 분류하는 스크립트를 작성하세요. 또한 커스텀 로그 카테고리별 출력 파일을 분리하여 디버깅 효율을 높이세요.