// Copyright Epic Games, Inc. All Rights Reserved. #include "LogMessage.h" #include "Misc/OutputDeviceHelper.h" #include "Misc/ScopeLock.h" // TraceServices #include "TraceServices/Model/Log.h" // TraceInsightsCore #include "InsightsCore/Common/TimeUtils.h" // TraceInsights #include "Insights/Log.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FLogMessageRecord //////////////////////////////////////////////////////////////////////////////////////////////////// FLogMessageRecord::FLogMessageRecord(const TraceServices::FLogMessageInfo& TraceLogMessage) : Time(TraceLogMessage.Time) //, Category(FText::FromString(TraceLogMessage.Category)) //, Message(FText::FromString(TraceLogMessage.Message)) , File(FText::FromString(TraceLogMessage.File)) , Line(TraceLogMessage.Line) , Index(static_cast(TraceLogMessage.Index)) , Verbosity(TraceLogMessage.Verbosity) { // Strip the "Log" prefix. FString CategoryStr(TraceLogMessage.Category->Name); if (CategoryStr.StartsWith(TEXT("Log"))) { CategoryStr.RightChopInline(3, EAllowShrinking::No); } Category = FText::FromString(MoveTemp(CategoryStr)); // Strip the trailing whitespaces (ex. some messages ends with "\n" and we do not want the LogView rows to have an unnecessary increased height). FString MessageStr(TraceLogMessage.Message); MessageStr.TrimEndInline(); Message = FText::FromString(MoveTemp(MessageStr)); } //////////////////////////////////////////////////////////////////////////////////////////////////// FText FLogMessageRecord::GetIndexAsText() const { return FText::AsNumber(Index); } //////////////////////////////////////////////////////////////////////////////////////////////////// FText FLogMessageRecord::GetTimeAsText() const { return FText::FromString(UE::Insights::FormatTimeHMS(Time, UE::Insights::FTimeValue::Microsecond)); } //////////////////////////////////////////////////////////////////////////////////////////////////// FText FLogMessageRecord::GetVerbosityAsText() const { return FText::FromString(FString(ToString(Verbosity))); } //////////////////////////////////////////////////////////////////////////////////////////////////// FText FLogMessageRecord::GetLineAsText() const { return FText::AsNumber(Line); } //////////////////////////////////////////////////////////////////////////////////////////////////// FText FLogMessageRecord::ToDisplayString() const { FTextBuilder TextBuilder; TextBuilder.AppendLine(GetTimeAsText()); TextBuilder.AppendLineFormat(NSLOCTEXT("SLogView", "CategoryLine", "Category: {0}"), Category); TextBuilder.AppendLineFormat(NSLOCTEXT("SLogView", "MessageLine", "Message: {0}"), Message); return TextBuilder.ToText(); } //////////////////////////////////////////////////////////////////////////////////////////////////// // FLogMessageCache //////////////////////////////////////////////////////////////////////////////////////////////////// FLogMessageCache::FLogMessageCache() { } //////////////////////////////////////////////////////////////////////////////////////////////////// void FLogMessageCache::SetSession(TSharedPtr InSession) { if (Session != InSession) { Reset(); Session = InSession; } } //////////////////////////////////////////////////////////////////////////////////////////////////// void FLogMessageCache::Reset() { FScopeLock Lock(&CriticalSection); Map.Reset(); } //////////////////////////////////////////////////////////////////////////////////////////////////// FLogMessageRecord& FLogMessageCache::Get(uint64 Index) { { FScopeLock Lock(&CriticalSection); if (Map.Contains(Index)) { return Map[Index]; } // Just an arbitrary limit. Will purge the cache after this limit, to avoid using too much memory. if (Map.Num() > 10000) { Map.Reset(); } } if (Session.IsValid()) { TraceServices::FAnalysisSessionReadScope SessionReadScope(*Session.Get()); const TraceServices::ILogProvider& LogProvider = TraceServices::ReadLogProvider(*Session.Get()); LogProvider.ReadMessage(Index, [this, Index](const TraceServices::FLogMessageInfo& Message) { LLM_SCOPE_BYTAG(Insights); FScopeLock Lock(&CriticalSection); FLogMessageRecord Entry(Message); Map.Add(Index, MoveTemp(Entry)); }); } { FScopeLock Lock(&CriticalSection); if (Map.Contains(Index)) { return Map[Index]; } } return InvalidEntry; } //////////////////////////////////////////////////////////////////////////////////////////////////// TSharedPtr FLogMessageCache::GetUncached(uint64 Index) const { TSharedPtr EntryPtr; if (Session.IsValid()) { TraceServices::FAnalysisSessionReadScope SessionReadScope(*Session.Get()); const TraceServices::ILogProvider& LogProvider = TraceServices::ReadLogProvider(*Session.Get()); LogProvider.ReadMessage(Index, [&EntryPtr](const TraceServices::FLogMessageInfo& Message) { EntryPtr = MakeShared(Message); }); } return EntryPtr; } //////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////