//////////////////////////////////////////////////////////// // // 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; };