Useful logging macros

From now on, everything below will be shown as if it is used inside this function’s scope:

void AMyActor::TestFunction(AActor* UselessArgument)

Logging automatically generated function names

Underneath are what certain macros (or compiler-generated fields) automatically expand to inside TestFunction's scope:

__func__ TestFunction
__FUNCTION__ AMyActor::TestFunction
__FUNCTIONW__ AMyActor::TestFunction
__FUNCSIG__ void AMyActor::TestFunction(AActor* UselessArgument)
__PRETTY_FUNCTION__ void AMyActor::TestFunction(AActor* UselessArgument)

Usage example

void AMyActor::TestFunction(AActor* UselessArgument)
{
    // TestFunction
    UE_LOG(LogTemp, Warning, TEXT("%s"), ANSI_TO_TCHAR(__func__));

    // AMyActor::TestFunction
    UE_LOG(LogTemp, Warning, TEXT("%s"), TEXT(__FUNCTION__));

    // AMyActor::TestFunction
    UE_LOG(LogTemp, Warning, TEXT("%s"), __FUNCTIONW__);

    // void AMyActor::TestFunction(AActor* UselessArgument)
    UE_LOG(LogTemp, Warning, TEXT("%s"), TEXT(__FUNCSIG__));

    // May not compile. Use __FUNCSIG__
    // void AMyActor::TestFunction(AActor* UselessArgument)
    UE_LOG(LogTemp, Warning, TEXT("%s"), TEXT(__PRETTY_FUNCTION__));
}

Important notes:

  1. __PRETTY_FUNCTION__ may not compile. Use __FUNCSIG__ instead.
  2. To display additional text along with these macros, simply do something like this:
    // void AMyActor::TestFunction(AActor* UselessArgument) My Message
    UE_LOG(LogTemp, Warning, TEXT("%s My Message"), TEXT(__FUNCSIG__));

    Or

    // void AMyActor::TestFunction(AActor* UselessArgument) My Message
    UE_LOG(LogTemp, Warning, TEXT("%s") TEXT(" My Message"), TEXT(__FUNCSIG__));

    C++ appends string literals at compile-time when put after each other. This feature is particularly useful when used in macros. It gives more flexibility.

  3. If you’re using __FUNCSIG__ and get this:

    void __cdecl AInteractableActor::BeginPlay(void)

    There is a hack to remove the “__cdecl” part:
    {
    FString Funcsig = __FUNCSIG__;
    Funcsig.RemoveAt(Funcsig.Find("__cdecl"), 8);
    UE_LOG(LogTemp, Warning, TEXT("%s"), *Funcsig);
    }

What I suggest

Use your project alias as log macro prefix. It’s usually between 2 and 6 characters.

Fortnite uses 'Fort' as alias. My current project uses 'Rage'.

Have a header file dedicated for log macros, which is preferably consistent in terms of naming with the engine’s log header file. If the engine uses LogMacros.h, yours could be YourAliasLogMacros.h

Here’s how my header file dedicated for log macros looks like:

DECLARE_LOG_CATEGORY_EXTERN(Rage, Log, All)
// .cpp DEFINE_LOG_CATEGORY(Rage)

// Log - Prints a message to a log file (does not print to console)
#define RAGE_LOG(Message, ...) \
UE_LOG(Rage, Log, TEXT("[%s] ") TEXT(Message), __FUNCTIONW__, __VA_ARGS__)

// Warning - Prints a warning to console (and log file)
#define RAGE_LOG_WARNING(Message, ...) \
UE_LOG(Rage, Warning, TEXT("[%s] ") TEXT(Message), __FUNCTIONW__, __VA_ARGS__)

// Error - Prints an error to console (and log file)
#define RAGE_LOG_ERROR(Message, ...) \
UE_LOG(Rage, Error, TEXT("[%s] ") TEXT(Message), __FUNCTIONW__, __VA_ARGS__)

// Fatal - Always prints a fatal error to console (and log file) and crashes (even if logging is disabled) 
#define RAGE_LOG_FATAL(Message, ...) \
UE_LOG(Rage, Fatal, TEXT("[%s] ") TEXT(Message), __FUNCTIONW__, __VA_ARGS__)

You could have the verbosity as macro argument, but just baking it inside the macro itself is cleaner in my opinion. It's up to personal preference.

Now, every time you use one of these log macros, you'll see from which member function it was invoked. I found this to be incredibly useful.

If you like a more verbose description, you might want to use __FUNCSIG__. It'll look like this:

#define SOMETHING(Message, ...) \
{ \
    FString Funcsig = __FUNCSIG__; \
    Funcsig.RemoveAt(Funcsig.Find("__cdecl"), 8); \
    UE_LOG(Rage, Warning, TEXT("[%s] ") TEXT(Message), *Funcsig, __VA_ARGS__); \
}

What about GEngine->AddOnScreenDebugMessage?

/** 
 * Calls GEngine::AddOnScreenDebugMessage
 * 'Message' is being formatted with FString::Printf. It supports '%s', '%d', etc. similar to UE_LOG.
 */
#define YOURALIAS_LOG_ONSCREEN(TimeToDisplay, DisplayColor, Message, ...) \
if (GEngine) GEngine->AddOnScreenDebugMessage(-1, TimeToDisplay, DisplayColor, \
FString::Printf(TEXT("[") __FUNCTIONW__ TEXT("] ") TEXT(Message), __VA_ARGS__));

Usage example

void AMyActor::TestFunction(AActor* UselessArgument)
{
    int32 Integer = 42;
    FString String = "Cake";
    
    // [AMyActor::TestFunction] The Cake is 42
    YOURALIAS_LOG_ONSCREEN(100.f, FColor::Orange, "The %s is %d", *String, Integer)

    FString Name = "David";
    FVector Position(0.f);

    // [AMyActor::TestFunction] User "David" is at X=0.000 Y=0.000 Z=0.000
    YOURALIAS_LOG_ONSCREEN(100.f, FColor::Emerald, "User \"%s\" is at %s", *Name, *Position.ToString())
}
gengine->addonscreendebugmessage example result in the Unreal Engine viewport
Result

What if we want to parametrize the verbosity of the function being appended at the start?

There are probably times when you don't need to see the function in your debug string, or would prefer a less - or more - verbose description of the function. I personally find this approach somewhat not necessary, but still worth mentioning.

I'll be using this simple enum, defined somewhere inside the header file dedicated for log macros:

enum class EFunctionLogVerbosity
{
    /** */
    None,
    /** FunctionName */
    Simple,
    /** AMyActor::FunctionName */
    Normal,
    /** void AMyActor::FunctionName(int) */
    Full
};

1. On screen:

Let's add an _IMPL version of YOURALIAS_LOG_ONSCREEN.
YOURALIAS_LOG_ONSCREEN_IMPL should be capable of specifying how verbose the function description - being appended at the start - should compile.

/**
 * Calls GEngine::AddOnScreenDebugMessage
 * 'Message' is being formatted with FString::Printf. It supports '%s', '%d', etc. similar to UE_LOG
 */
#define YOURALIAS_LOG_ONSCREEN(TimeToDisplay, DisplayColor, Message, ...) \
YOURALIAS_LOG_ONSCREEN_IMPL(EFunctionNameLogVerbosity::Normal, TimeToDisplay, DisplayColor, Message, __VA_ARGS__)


/**
 * Calls GEngine::AddOnScreenDebugMessage
 * 'Message' is being formatted with FString::Printf. It supports '%s', '%d', etc. similar to UE_LOG
 */
#define YOURALIAS_LOG_ONSCREEN_IMPL(FunctionNameLogVerbosity, TimeToDisplay, DisplayColor, Message, ...) \
static_assert(std::is_same_v<decltype(FunctionNameLogVerbosity), EFunctionNameLogVerbosity>, \
"Make sure 'FunctionNameLogVerbosity' is of type EFunctionNameLogVerbosity"); \
if (GEngine) \
{ \
    using V = EFunctionNameLogVerbosity; \
    if constexpr (FunctionNameLogVerbosity == V::None) \
    { \
        GEngine->AddOnScreenDebugMessage(-1, TimeToDisplay, DisplayColor, \
        FString::Printf(TEXT(Message), __VA_ARGS__)); \
    } \
    else if constexpr (FunctionNameLogVerbosity == V::Simple) \
    { \
        GEngine->AddOnScreenDebugMessage(-1, TimeToDisplay, DisplayColor, \
        FString::Printf(TEXT("[%s] ") TEXT(Message), ANSI_TO_TCHAR(__func__), __VA_ARGS__)); \
    } \
    else if constexpr (FunctionNameLogVerbosity == V::Normal) \
    { \
        GEngine->AddOnScreenDebugMessage(-1, TimeToDisplay, DisplayColor, \
        FString::Printf(TEXT("[") __FUNCTIONW__ TEXT("] ") TEXT(Message), __VA_ARGS__)); \
    } \
    else if constexpr (FunctionNameLogVerbosity == V::Full) \
    { \
        FString Funcsig = __FUNCSIG__; \
        Funcsig.RemoveAt(Funcsig.Find("__cdecl"), 8); \
        GEngine->AddOnScreenDebugMessage(-1, TimeToDisplay, DisplayColor, \
        FString::Printf(TEXT("[%s] ") TEXT(Message), *Funcsig, __VA_ARGS__)); \
    } \
}

Let's static_assert because we're nice people.

Usage example

void AMyActor::TestFunction(AActor* UselessArgument)
{
    FString String = "Test";

    // Hey, "Test"
    YOURALIAS_LOG_ONSCREEN_IMPL(EFunctionNameLogVerbosity::None, 100.f, FColor::Blue, "Hey, \"%s\"", *String);

    // [AMyActor::TestFunction] Hey, "Test"
    YOURALIAS_LOG_ONSCREEN(100.f, FColor::Emerald, "Hey, \"%s\"", *String);

    // [void AMyActor::TestFunction(class AActor *] Hey, "Test"
    YOURALIAS_LOG_ONSCREEN_IMPL(EFunctionNameLogVerbosity::Full, 100.f, FColor::Purple, "Hey, \"%s\"", *String);
}
Viewport output of the parametrized function verbosity usage example code snippet.
Result

2. Just logs:

Let’s again add an _IMPL version of YOURALIAS_LOG.
YOURALIAS_LOG_IMPL should be capable of specifying how verbose the function description – being appended at the start – should compile, while also specifying the log verbosity.

#define YOURALIAS_LOG_IMPL(FunctionNameLogVerbosity, Verbosity, Message, ...) \
static_assert(std::is_same_v<decltype(FunctionNameLogVerbosity), EFunctionNameLogVerbosity>, \
"Make sure 'FunctionNameLogVerbosity' is of type EFunctionNameLogVerbosity"); \
if (GEngine) \
{ \
    using V = EFunctionNameLogVerbosity; \
    if constexpr (FunctionNameLogVerbosity == V::None) \
    { \
        UE_LOG(LogTemp, Verbosity, TEXT(Message), __VA_ARGS__) \
    } \
    else if constexpr (FunctionNameLogVerbosity == V::Simple) \
    { \
        UE_LOG(LogTemp, Verbosity, TEXT("[%s] ") TEXT(Message), ANSI_TO_TCHAR(__func__), __VA_ARGS__) \
    } \
    else if constexpr (FunctionNameLogVerbosity == V::Normal) \
    { \
        UE_LOG(LogTemp, Verbosity, TEXT("[") __FUNCTIONW__ TEXT("] ") TEXT(Message), __VA_ARGS__) \
    } \
    else if constexpr (FunctionNameLogVerbosity == V::Full) \
    { \
        FString Funcsig = __FUNCSIG__; \
        Funcsig.RemoveAt(Funcsig.Find("__cdecl"), 8); \
        UE_LOG(LogTemp, Verbosity, TEXT("[%s] ") TEXT(Message), *Funcsig, __VA_ARGS__) \
    } \
}

Along with YOURALIAS, replace LogTemp with your own category.

If you liked the suggestion of baking the log verbosity in the macro itself, you could consider this:

#define YOURALIAS_LOG(Message, ...) \
YOURALIAS_LOG_IMPL(EFunctionNameLogVerbosity::Normal, Log, Message, __VA_ARGS__)

#define YOURALIAS_LOG_WARNING(Message, ...) \
YOURALIAS_LOG_IMPL(EFunctionNameLogVerbosity::Normal, Warning, Message, __VA_ARGS__)

#define YOURALIAS_LOG_ERROR(Message, ...) \
YOURALIAS_LOG_IMPL(EFunctionNameLogVerbosity::Normal, Error, Message, __VA_ARGS__)

#define YOURALIAS_LOG_FATAL(Message, ...) \
YOURALIAS_LOG_IMPL(EFunctionNameLogVerbosity::Normal, Fatal, Message, __VA_ARGS__)

Usage example

void AMyActor::TestFunction(AActor* UselessArgument)
{
    FString String = "Games";
    
    YOURALIAS_LOG_IMPL(EFunctionNameLogVerbosity::None, Log, "Epic %s", *String)
    YOURALIAS_LOG_IMPL(EFunctionNameLogVerbosity::Simple, Warning, "Epic %s", *String)
    YOURALIAS_LOG_IMPL(EFunctionNameLogVerbosity::Normal, Error, "Epic %s", *String)
    YOURALIAS_LOG_IMPL(EFunctionNameLogVerbosity::Full, Log, "Epic %s", *String)

    YOURALIAS_LOG("Epic %s", *String)
    YOURALIAS_LOG_WARNING("Epic %s", *String)
    YOURALIAS_LOG_ERROR("Epic %s", *String)
}
The log result of running the code snippet
Result