Lots of refactors related to BreakToDebugger and FormatLogMessage

This commit is contained in:
2026-02-14 01:25:04 -05:00
parent 96256d7836
commit dd159b064d
10 changed files with 221 additions and 162 deletions

View File

@@ -0,0 +1,113 @@
////////////////////////////////////////////////////////////
//
// BreakToDebugger
//
// When an error message gets written to UE_LOG, we can
// optionally trigger the blueprint debugger.
//
// This only affects UE_LOG messages that are generated
// during blueprint execution. Log messages from other
// threads do not trigger the debugger.
//
// The following explains how we trigger the blueprint
// debugger on UE_LOG messages. Log messages are sent to a
// long list of output devices, including: the visual studio
// output window, the unreal editor output window, the log
// file, and so forth. We add another pseudo output device.
// This output device doesn't actually send the log message
// anywhere, instead, it just activates the blueprint
// debugger.
//
// UE_LOG messages can be generated from any thread. The
// pseudo output device checks what thread it is running in,
// and if it's not the blueprint thread, it does nothing at
// all. If it is the blueprint thread, it's safe to trigger
// the blueprint debugger.
//
// One annoying limitation of this design is that our output
// device ends up early in the list, so the debugger runs
// *before* the message shows up in visual studio or the
// unreal editor. As a result, when you are in the
// debugger, the message won't be in your output window.
// Pressing 'single step' always reveals the message.
//
// The blueprint node "Format Log Message" uses UE_LOG
// internally, so therefore, that too can trigger the
// blueprint debugger.
//
////////////////////////////////////////////////////////////
#pragma once
#include "Containers/Array.h"
#include "CoreMinimal.h"
#include "HAL/Platform.h"
#include "Misc/OutputDeviceError.h"
#include "UObject/NameTypes.h"
#include "UObject/ObjectMacros.h"
#include "UObject/UObjectGlobals.h"
#include "BreakToDebugger.generated.h"
// ElxBreakToDebuggerThreshold:
//
// Controls the sensitivity level at which UE_LOG messages
// trigger the blueprint debugger.
//
UENUM(BlueprintType)
enum class ElxBreakToDebuggerThreshold : uint8 {
/* Break on errors. */
Error,
/* Break on warnings and above. */
Warning,
/* Break on display messages and above. */
Display,
/* Break on log messages and above. */
Log,
/* Break on verbose messages and above. */
Verbose,
/* Break on all messages. */
VeryVerbose,
/* Break on fatal errors only (ie, never break -- the process crashes instead). */
Fatal,
};
struct FlxBreakToDebuggerOutputDevice : public FOutputDevice
{
public:
// The constructor and destructor automatically register
// this output device with GLog.
//
// This struct doesn't store the sensitivity threshold.
// It relies on the LuprexGameMode class to do that, so
// that the threshold can be easily edited with the
// blueprint editor. This struct must be initialized
// with a reference to the threshold variable.
//
FlxBreakToDebuggerOutputDevice(const ElxBreakToDebuggerThreshold &SensitivityRef);
~FlxBreakToDebuggerOutputDevice();
// Inspect a log message.
//
INTEGRATION_API virtual void Serialize(const TCHAR* V, ELogVerbosity::Type Verbosity, const FName& Category) override;
// If the device is marked 'CanBeUsedOnMultipleThreads,'
// then UE_LOG will call Serialize from the current
// thread, otherwise, it will call Serialize from the
// logging thread. Using the logging thread would
// defeat the purpose of this device, so it's imperative
// that we set this flag.
//
INTEGRATION_API virtual bool CanBeUsedOnMultipleThreads() const override { return true; }
private:
static ELogVerbosity::Type ConvertThreshold(ElxBreakToDebuggerThreshold Verbosity);
const ElxBreakToDebuggerThreshold &Sensitivity;
};