Logging

Printing logs via C++ at Runtime.

Updated about 1 month ago Edit Page Revisions

About Logging

Logging means keeping an ordered record of events, function calls, variable values at a certain time during runtime, etc. This is usually saved in the form of text in a log file. It is an invaluable tool for a software developer, especially when debugging, as it can provide detailed information on what the code is doing at any given moment. Good use cases include ensuring certain blocks of code are being executed, inspecting data values passed between functions, and reporting potential issues.

Logging in Unreal Engine is actually a fairly complex subject, and there are multiple ways to perform logging. In this page we will introduce several methods for logging and interacting with logs.


Writing Logs in Unreal Engine

Logging using UE_LOG

Here is an example of a simple log message:

UE_LOG(LogTemp, Warning, TEXT("Hello"));

If you're not getting any output from the logging statements, ensure you are compiling the project before you test the changes.

UE_LOG is a macro that outputs the log message into the log file. The first input parameter it takes is the name of the logging category. There are many of these categories already built into the engine, defined in CoreGlobals.h. You can also create your own custom logging categories. The process for doing so is described later on, click here to jump to that section.

The second parameter the UE_LOG macro takes is the verbosity level of the message. The final parameter in the example above is the text message itself; however, one could add more parameters if the string uses some printf format specifiers. See below for some formatting examples.

If you are looking for blueprint style logging that appears on the viewport, click here.

Formatting Examples Quick Reference

Logging an FString

UE_LOG(LogTemp, Warning, TEXT("The Actor's name is %s"), *YourActor->GetName());

Logging a Bool

UE_LOG(LogTemp, Warning, TEXT("The boolean value is %s"), ( bYourBool ? TEXT("true") : TEXT("false") ));

Logging an Integer

UE_LOG(LogTemp, Warning, TEXT("The integer value is: %d"), YourInteger);

Logging a Float

UE_LOG(LogTemp, Warning, TEXT("The float value is: %f"), YourFloat);

Logging an FVector

UE_LOG(LogTemp, Warning, TEXT("The vector value is: %s"), *YourVector.ToString());

Logging with Multiple Specifiers

UE_LOG(LogTemp, Warning, TEXT("Current values are: vector %s, float %f, and integer %d"), *YourVector.ToString(), YourFloat, YourInteger);

Logging using UE_LOGFMT

This macro is introduced with Unreal Engine 5.2 and may not be available in earlier revisions of the engine.
In order to utilize UE_LOGFMT you will need to #include "Logging/StructuredLog.h" either in your current file or in a linked file.

Experienced Unreal Engine developers will be familiar with the UE_LOG macro, and in particular some of its quirks.

UE_LOG is extremely verbose, requiring the developer to constantly wrap log text in the TEXT macro.
UE_LOG is also incapable of printing basic types, such as Booleans, or FStrings, Unreal's standard String type.
UE_LOG requires awareness of types when printing different variables such as float, integer, booleans, strings.

The new UE_LOGFMT macro helps to alleviate many of these issues. At its most simple, UE_LOGFMT can be utilized in the following way:

UE_LOGFMT(LogTemp, Log, "This message will print to my log");

When printing variables to log, UE_LOGFMT provides a cleaner and more consistent syntax. Like so:

FString Name("SomeName");
int32 Value = 999;
UE_LOGFMT(LogTemp, Log, "Printing my Name {0} with Value {1}", Name, Value);

UE_LOGFMT accepts parameters in two ways:

Positional Parameters

The field values must exactly match the fields referenced by the log statement.

UE_LOGFMT(LogCore, Warning, "Loading '{0}' failed with error {1}", Package->GetName(), ErrorCode);

In this example, the first variable encountered in the log statement ({0}) is replaced by the value of Package->GetName().
The second variable ({1}) is replaced by ErrorCode.

The ordering of Package->GetName and ErrorCode in this example is implicitly important to deducing the values replaced in the log statement.

Named Parameters

The fields must contain every field referenced by Format. Order is irrelevant and extra fields are permitted.

UE_LOGFMT(LogCore, Warning, "Loading '{Name}' failed with error {Error}", ("Error", ErrorCode), ("Name", Package->GetName()), ("Flags", LoadFlags));

In this example, the first variable encountered in the log statement ({Name}) is replaced by the value bundled with the ("Name", ...) identifier.
The second variable ({Error}) is replaced by the value bundled with the ("Error", ...) identifier.


Accessing Logs

There are a few different ways to access the logs when working with UE:

  • If your Play-In-Editor session has ended, you can find the full log of that session in YourProjectName\Saved\Logs folder. The logs from previous sessions will be there as well, in case you ever need to access them. If you do not know where your project is located, you can open your project library in the Epic Games launcher, right-click on the project in question, and then select Show in Folder option.

  • You can see the log output in real time while playing in Editor in the Output Log tab. If it is not open by default, you can find it under Window->Developer Tools->Output Log in UE4, and under Window->Output Log in UE5 . In addition to real-time logging, this tab will keep all log information from all play sessions that happened during the current Unreal Editor session.

  • If you have an executable, you can create a shortcut with -Log at the end of its name to open the log as you launch the executable.

  • You can open the console when your game is running by pressing the tilde (~) key and access the log by typing in the console command showlog and pressing the Enter key. Note that if you do this during a Play-In-Editor session, clicking x to close the log will close your Unreal Editor session.

Log Verbosity Levels

In certain cases, it is useful to see a less or more detailed log output. Log verbosity levels allow for easy control of the level of detail present in a given log. If a particular log statement is more verbose than the compile-time verbosity, it will not be compiled into the game code. Afterwards, the level of the whole log is set to default verbosity, which can be changed in the Engine.ini file. Runtime verbosity can be changed through the command line. Given a certain verbosity level of the log, only log messages with matching or lower verbosity level will be printed to it. Messages with a higher verbosity level will be ignored.

The following table lists all available verbosity levels, from lowest verbosity to highest:

Verbosity Level Printed in Console? Printed in Editor's Log? Notes
Fatal Yes N/A Crashes the session, even if logging is disabled
Error Yes Yes Log text is coloured red
Warning Yes Yes Log text is coloured yellow
Display Yes Yes Log text is coloured grey
Log No Yes Log text is coloured grey
Verbose No No
VeryVerbose No No

Each log statement declares which log category it belongs to as well as its verbosity level.

Changing Default Log Verbosity

Setting Log Verbosity via Engine.ini

Verbosity settings for each log category can be set in either Engine.ini or DefaultEngine.ini

[Core.Log]
Global=
=

Setting Log Verbosity via Commandline Parameter

Verbosity settings for each log category can also be set via commandline parameter passed when launching the game or editor. At the end of the commandline used to launch your executable, add the following flags.

-LogCmds="global Verbose, LogPython Verbose, LogAnimMontage off, LogDeepDriveAgent VeryVerbose"

Changing Log Verbosity at Runtime

Verbosity settings for each log category can also be set via console command at runtime.

While the game is running, open the console and run a command with the following format:

log

Custom Log Categories

Creating your own logging categories will make it much easier to parse your log files, as you can toggle the visibility of log categories inside the Output Log window. It's usually a good idea to make a new log category for every major system in your project, like a procedural level generator, or perhaps for your game mode.

You can define a new log category by putting these two snippets wherever you need to use this log category. You could put these inside their own file, and #include it wherever you need it. These should be at the top level of your code, not inside any functions or classes.

Header File

DECLARE_LOG_CATEGORY_EXTERN(LogCustom, Log, All);

Cpp File

DEFINE_LOG_CATEGORY(LogCustom);

The syntax for the header file snippet is DECLARE_LOG_CATEGORY_EXTERN(CategoryName, DefaultVerbosity, CompileTimeVerbosity). DefaultVerbosity is the verbosity level used when one is not specified in the ini files or on the command line. Anything more verbose than this will not be logged. CompileTimeVerbosity is the maximum verbosity to compile in the code. Anything more verbose than this will not be compiled.

The values in the snippet above is a good starting point for your log category.

It's common to see log categories prefixed with Log, but it's not required.
Following this convention will allow you to easily sort log categories alphabetically.

Using a different CompileTimeVerbosity than All may not print your logs in the Output Log.


Printing Messages to Screen

Strictly speaking, printing a message to the screen during runtime does not count as logging since the message does not get saved in a file. However, it is often more convenient than using a log message when developing and/or debugging as it allows you to see the message in the game window without needing to have a separate window open for the log. Anything from variable values in real time to function call order can be easily seen this way.

Here's an example of a simple string message:

GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::White, TEXT("This message will appear on the screen!"));

The first parameter is the key. If key is set to -1, each time this line of code executes, a new message will be added to the screen. For example, if one added this to the Tick()function, the screen would soon flood with a stream of these messages. If the key is a positive integer (the key's type is uint64), each new message replaces the previous message with the same integer as its key. For example, if the above function was called on Tick() with its key modified to 1, only one message would be seen on the screen in game as each new call would simply replace it.

The second parameter is time (in seconds) that the message will be displayed for. It is of type float.

The third parameter is an FColor that determines the color of the text. It is best to use colors that will be easy to read against the background of the game world. It is easiest to use predefined constants for colors, such as FColor::White. See the bottom of this official documentation page for the full list of available color constants.

The fourth parameter is the message itself. Note that the whole of the string must take up only one parameter, therefore, printing using printf parameters requires the use of FString::Printf():

GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("Some variable values: x = %f, y = %f"), x, y));

AddOnScreenDebugMessage() has two more optional parameters. The fifth parameter is a boolean value that determines whether new messages appear on the top (if true) or the bottom (if false). Note that this only applies in the case of the key value set to -1. The sixth parameter is a 2D vector that determines the scale of the text. This is useful if the messages printed to the screen are too small to be read easily or if they are too big and take up too much screen space.

Visual Studio may underline GEngine and claim that it is undefined. However, you do not need to explicitly include Engine.h or EngineGlobals.h to use it in any of your classes. It should compile and work fine despite the red underline.

Adding messages to Viewport Stats Subsystem

4.26 adds the Viewport Stats Subsystem, allowing to add messages to the current viewport such as "LIGHTING NEEDS TO BE REBUILT" and "BLUEPRINT COMPILE ERROR". Following code sample is copy-pasted directly from this class. Simply inspect this class to check all available options.

if (UViewportStatsSubsystem* ViewportSubsystem = GetWorld()->GetSubsystem())
{
	// Bind a member function delegate to the subsystem...
	FViewportDisplayCallback Callback;
	Callback.BindDynamic(this, &UCustomClass::DisplayViewportMessage);
	ViewportSubsystem->AddDisplayDelegate(Callback);
	
	// ... or use inline lambda functions
	ViewportSubsystem->AddDisplayDelegate([](FText& OutText, FLinearColor& OutColor)
	{
		// Some kind of state management
		OutText = NSLOCTEXT("FooNamespace", "Blarg", "Display message here");
		OutColor = FLinearColor::Red;
		return bShouldDisplay;
	});
}

Additional Reading