#include "RiderLogging.hpp" #include "BlueprintProvider.hpp" #include "IRiderLink.hpp" #include "Model/Library/UE4Library/LogMessageInfo.Pregenerated.h" #include "Model/Library/UE4Library/StringRange.Pregenerated.h" #include "Model/Library/UE4Library/UnrealLogEvent.Pregenerated.h" #include "Internationalization/Regex.h" #include "Misc/DateTime.h" #include "Modules/ModuleManager.h" #define LOCTEXT_NAMESPACE "RiderLogging" DEFINE_LOG_CATEGORY(FLogRiderLoggingModule); IMPLEMENT_MODULE(FRiderLoggingModule, RiderLogging); namespace LoggingExtensionImpl { static TArray> GetPathRanges( const FRegexPattern& Pattern, const FString& Str) { using JetBrains::EditorPlugin::StringRange; FRegexMatcher Matcher(Pattern, Str); TArray> Ranges; while (Matcher.FindNext()) { const int Start = Matcher.GetMatchBeginning(); const int End = Matcher.GetMatchEnding(); FString PathName = Str.Mid(Start, End - Start); if (BluePrintProvider::IsBlueprint(PathName)) Ranges.Emplace(StringRange(Start, End)); } return Ranges; } static TArray> GetMethodRanges( const FRegexPattern& Pattern, const FString& Str) { using JetBrains::EditorPlugin::StringRange; FRegexMatcher Matcher(Pattern, Str); TArray> Ranges; while (Matcher.FindNext()) { Ranges.Emplace(StringRange(Matcher.GetMatchBeginning(), Matcher.GetMatchEnding())); } return Ranges; } static bool SendMessageToRider(const JetBrains::EditorPlugin::LogMessageInfo& MessageInfo, const FString& Message) { static const FRegexPattern PathPattern = FRegexPattern(TEXT("(/[\\w\\.]+)+")); static const FRegexPattern MethodPattern = FRegexPattern(TEXT("[0-9a-z_A-Z]+::~?[0-9a-z_A-Z]+")); return IRiderLinkModule::Get().FireAsyncAction( [&MessageInfo, &Message] (JetBrains::EditorPlugin::RdEditorModel const& RdEditorModel) { rd::ISignal const& UnrealLog = RdEditorModel.get_unrealLog(); UnrealLog.fire({ MessageInfo, Message, GetPathRanges(PathPattern, Message), GetMethodRanges(MethodPattern, Message) }); }); } void SendMessageInChunks(FString* Msg, const JetBrains::EditorPlugin::LogMessageInfo& MessageInfo) { static int NUMBER_OF_CHUNKS = 1024; while (!Msg->IsEmpty()) { SendMessageToRider(MessageInfo, Msg->Left(NUMBER_OF_CHUNKS)); *Msg = Msg->RightChop(NUMBER_OF_CHUNKS); } } void ScheduledSendMessage(FString* Msg, const JetBrains::EditorPlugin::LogMessageInfo& MessageInfo) { FString ToSend; while (Msg->Split("\n", &ToSend, Msg)) { SendMessageInChunks(&ToSend, MessageInfo); } SendMessageInChunks(Msg, MessageInfo); } } void FRiderLoggingModule::StartupModule() { UE_LOG(FLogRiderLoggingModule, Verbose, TEXT("STARTUP START")); static const auto START_TIME = FDateTime::UtcNow().ToUnixTimestamp(); static const auto GetTimeNow = [](double Time) -> rd::DateTime { return rd::DateTime(START_TIME + static_cast(Time)); }; ModuleLifetimeDef = IRiderLinkModule::Get().CreateNestedLifetimeDefinition(); LoggingScheduler = MakeUnique(ModuleLifetimeDef.lifetime, "LoggingScheduler"); ModuleLifetimeDef.lifetime->bracket( [this]() { OutputDevice.Setup([this](const TCHAR* msg, ELogVerbosity::Type Type, const FName& Name, TOptional Time) { if (Type > ELogVerbosity::All) return; rd::optional DateTime; if (Time) { DateTime = GetTimeNow(Time.GetValue()); } const FString PlainName = Name.GetPlainNameString(); const JetBrains::EditorPlugin::LogMessageInfo MessageInfo{Type, PlainName, DateTime}; LoggingScheduler->queue([Msg = FString(msg), MessageInfo]() mutable { LoggingExtensionImpl::ScheduledSendMessage(&Msg, MessageInfo); }); }); }, [this]() { OutputDevice.TearDown(); }); UE_LOG(FLogRiderLoggingModule, Verbose, TEXT("STARTUP FINISH")); } void FRiderLoggingModule::ShutdownModule() { UE_LOG(FLogRiderLoggingModule, Verbose, TEXT("SHUTDOWN START")); ModuleLifetimeDef.terminate(); UE_LOG(FLogRiderLoggingModule, Verbose, TEXT("SHUTDOWN FINISH")); } #undef LOCTEXT_NAMESPACE