Skip to content

Instantly share code, notes, and snippets.

@Solessfir
Last active April 12, 2025 22:29
Show Gist options
  • Save Solessfir/eb0df57297f8a61f0c598629b0a78865 to your computer and use it in GitHub Desktop.
Save Solessfir/eb0df57297f8a61f0c598629b0a78865 to your computer and use it in GitHub Desktop.
Single header UE5 Log library with automatic type deduction
/**
* Copyright (c) Solessfir under MIT license
*
* Usage:
*
* #define LOG_CATEGORY_NAME LogMyCustomCategory // Optional, defaulted to EasyLog if not defined
* #include "EasyLog.h"
*
* LOG_DISPLAY("This Actor name is: {0}, expecting {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);
*
* No need for GetName() or ToString(), etc. Type will be automatically deduced.
*
* 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__)
#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 to handle logging logic
#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)
// Log call location Class::Function::Line
#define GET_LOG_LOCATION (FString(__FUNCTION__) + "::" + FString::FromInt(__LINE__))
// Generate unique hash based on the function name and line for AddOnScreenDebugMessage()
#define UNIQUE_KEY_HASH static_cast<int32>((FCrc::MemCrc32(__FUNCTION__, sizeof(__FUNCTION__) - 1) ^ (static_cast<uint32>(__LINE__) << 15)) & 0x7FFFFFFF)
// Allows to pass LOG_CATEGORY_NAME to DEFINE_LOG_CATEGORY_STATIC
#define INTERNAL_LOG_CATEGORY_DEFINE(CategoryName) DEFINE_LOG_CATEGORY_STATIC(CategoryName, Log, All)
#ifndef LOG_CATEGORY_NAME
#define LOG_CATEGORY_NAME EasyLog
#endif
// Define Log Category
INTERNAL_LOG_CATEGORY_DEFINE(LOG_CATEGORY_NAME);
// Type traits for automatic type deduction
template<typename T, typename = int32>
struct THasToString : std::false_type {};
template<typename T>
struct THasToString<T, decltype(std::declval<T>().ToString(), 0)> : std::true_type {};
template<typename T, typename = int32>
struct THasActorNameOrLabel : std::false_type {};
template<typename T>
struct THasActorNameOrLabel<T, decltype(std::declval<T>()->GetActorNameOrLabel(), 0)> : std::true_type {};
template<typename T, typename = int32>
struct THasGetName : std::false_type {};
template<typename T>
struct THasGetName<T, decltype(std::declval<T>()->GetName(), 0)> : std::true_type {};
template<typename T, typename = int32>
struct THasLexToString : std::false_type {};
template<typename T>
struct THasLexToString<T, decltype(LexToString(std::declval<T>()), 0)> : std::true_type {};
template<typename T>
struct TIsUObjectPointer : std::false_type {};
template<typename T>
struct TIsUObjectPointer<T*> : std::is_base_of<UObject, T> {};
template<typename... T>
void FillArgs(FStringFormatOrderedArguments& Arguments, T&&... Parameters)
{
([&]<typename F>(F&& Argument)
{
if constexpr (std::is_constructible_v<FStringFormatArg, F>)
{
Arguments.Add(Forward<F>(Argument));
}
else if constexpr (THasToString<F>::value)
{
Arguments.Add(Argument.ToString());
}
else if constexpr (THasActorNameOrLabel<F>::value)
{
Arguments.Add(IsValid(Argument) ? Argument->GetActorNameOrLabel() : TEXT("None"));
}
else if constexpr (THasGetName<F>::value)
{
if constexpr (TIsUObjectPointer<F>::value)
{
Arguments.Add(IsValid(Argument) ? Argument->GetName() : TEXT("None"));
}
else
{
Arguments.Add(Argument ? Argument->GetName() : TEXT("None"));
}
}
else if constexpr (THasLexToString<F>::value)
{
Arguments.Add(LexToString(Forward<F>(Argument)));
}
else
{
static_assert(std::is_void_v<F> && false, "Unsupported type");
}
}(Forward<T>(Parameters)), ...);
}
#else
// Disable all logging in Shipping builds
#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