Last active
September 8, 2025 19:58
-
-
Save Solessfir/eb0df57297f8a61f0c598629b0a78865 to your computer and use it in GitHub Desktop.
Single header UE5 Log library with automatic type deduction
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Copyright (c) Solessfir under MIT license | |
| * | |
| * Usage: | |
| * | |
| * #define LOG_CATEGORY_NAME LogMyCustomCategory // Optional, defaults to EasyLog | |
| * #include "EasyLog.h" | |
| * | |
| * LOG_DISPLAY("Actor name: {0}, expected {1}", this, "Foo"); | |
| * LOG_WARNING("Vector is: {0}", FVector(1.f, 2.f, 3.f)); | |
| * LOG_ERROR("Error with object: {0}", SomeObject); // "None" if nullptr | |
| * LOG_WARNING_EX(-1, 10.f, "Updating value: {0}", SomeValue); | |
| * | |
| * UENUM Support (fallback to numeric values for raw enums) | |
| * LOG_WARNING("UENUM name is: {0}", UEnum::GetValueAsString(MyEnum)); | |
| * | |
| * No need for GetName() or ToString(), etc. Type is deduced automatically. | |
| * | |
| * Reference: | |
| * Laura's Unreal Blog | |
| * https://landelare.github.io/2022/04/28/better-ue_log.html | |
| * https://github.com/landelare/llog | |
| */ | |
| #pragma once | |
| #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) | |
| // Public logging macros | |
| #define LOG_DISPLAY(Format, ...) INTERNAL_LOG(Display, UNIQUE_KEY_HASH, 10.f, TEXT(Format), ##__VA_ARGS__) | |
| #define LOG_WARNING(Format, ...) INTERNAL_LOG(Warning, UNIQUE_KEY_HASH, 10.f, TEXT(Format), ##__VA_ARGS__) | |
| #define LOG_ERROR(Format, ...) INTERNAL_LOG(Error, UNIQUE_KEY_HASH, 10.f, TEXT(Format), ##__VA_ARGS__) | |
| // Extended versions with exposed Key and Duration | |
| #define LOG_DISPLAY_EX(Key, Duration, Format, ...) INTERNAL_LOG(Display, Key, Duration, TEXT(Format), ##__VA_ARGS__) | |
| #define LOG_WARNING_EX(Key, Duration, Format, ...) INTERNAL_LOG(Warning, Key, Duration, TEXT(Format), ##__VA_ARGS__) | |
| #define LOG_ERROR_EX(Key, Duration, Format, ...) INTERNAL_LOG(Error, Key, Duration, TEXT(Format), ##__VA_ARGS__) | |
| // Internal macro for unified logging | |
| #define INTERNAL_LOG(Verbosity, Key, Duration, Fmt, ...) \ | |
| do { \ | |
| FStringFormatOrderedArguments OrderedArguments; \ | |
| FillArgs(OrderedArguments, ##__VA_ARGS__); \ | |
| const FString Message = FString::Format(Fmt, MoveTemp(OrderedArguments)); \ | |
| const FString FullMessage = FString::Printf(TEXT("%s | %s"), *Message, *GET_LOG_LOCATION); \ | |
| if (GEngine && GAreScreenMessagesEnabled && Duration > 0.f) { \ | |
| FColor Color = ELogVerbosity::Verbosity == ELogVerbosity::Error ? FColor::Red : \ | |
| ELogVerbosity::Verbosity == ELogVerbosity::Warning ? FColor::Orange : FColor::White; \ | |
| GEngine->AddOnScreenDebugMessage(Key, Duration, Color, FullMessage); \ | |
| } \ | |
| UE_LOG(LOG_CATEGORY_NAME, Verbosity, TEXT("%s"), *FullMessage); \ | |
| } while (false) | |
| // Call location Class::Function::Line | |
| #define GET_LOG_LOCATION (FString(__FUNCTION__) + "::" + FString::FromInt(__LINE__)) | |
| // Hash for AddOnScreenDebugMessage() | |
| #define UNIQUE_KEY_HASH static_cast<int32>((FCrc::MemCrc32(__FUNCTION__, sizeof(__FUNCTION__) - 1) ^ (static_cast<uint32>(__LINE__) << 15)) & 0x7FFFFFFF) | |
| // Allow overriding log category | |
| #ifndef LOG_CATEGORY_NAME | |
| #define LOG_CATEGORY_NAME EasyLog | |
| #endif | |
| // Define log category once | |
| DEFINE_LOG_CATEGORY_STATIC(LOG_CATEGORY_NAME, Log, All); | |
| inline void FillArgs(FStringFormatOrderedArguments&) {} | |
| template<typename F, typename... Rest> | |
| void FillArgs(FStringFormatOrderedArguments& Args, F&& First, Rest&&... More) | |
| { | |
| if constexpr (requires { FStringFormatArg(Forward<F>(First)); }) | |
| { | |
| Args.Add(Forward<F>(First)); | |
| } | |
| else if constexpr (requires { First.ToString(); }) | |
| { | |
| Args.Add(First.ToString()); | |
| } | |
| else if constexpr (requires { First->GetActorNameOrLabel(); }) | |
| { | |
| Args.Add(IsValid(First) ? First->GetActorNameOrLabel() : TEXT("None")); | |
| } | |
| else if constexpr (requires { First->GetName(); }) | |
| { | |
| if constexpr (std::is_pointer_v<F> && std::is_base_of_v<UObject, std::remove_pointer_t<F>>) | |
| Args.Add(IsValid(First) ? First->GetName() : TEXT("None")); | |
| else | |
| Args.Add(First ? First->GetName() : TEXT("None")); | |
| } | |
| else if constexpr (requires { LexToString(Forward<F>(First)); }) | |
| { | |
| Args.Add(LexToString(Forward<F>(First))); | |
| } | |
| else if constexpr (std::is_enum_v<std::remove_cvref_t<F>>) | |
| { | |
| using Underlying = std::underlying_type_t<std::remove_cvref_t<F>>; | |
| Args.Add(LexToString(static_cast<Underlying>(First))); | |
| } | |
| else | |
| { | |
| static_assert([]{return false;}(), "Unsupported type passed to FillArgs()"); | |
| } | |
| FillArgs(Args, Forward<Rest>(More)...); | |
| } | |
| #else | |
| // Disable logging in Shipping/Test | |
| #define LOG_DISPLAY(Format, ...) | |
| #define LOG_WARNING(Format, ...) | |
| #define LOG_ERROR(Format, ...) | |
| #define LOG_DISPLAY_EX(Key, Duration, Format, ...) | |
| #define LOG_WARNING_EX(Key, Duration, Format, ...) | |
| #define LOG_ERROR_EX(Key, Duration, Format, ...) | |
| #define UNIQUE_KEY_HASH | |
| #endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment