Add basic support for log contexts.

This commit is contained in:
Menotdan
2024-03-26 21:09:18 -04:00
parent 4cd788cedc
commit ac36297e27
7 changed files with 180 additions and 14 deletions

View File

@@ -228,7 +228,7 @@ namespace FlaxEditor.Modules
new SearchResult { Name = item.ShortName, Type = assetItem.TypeName, Item = item } new SearchResult { Name = item.ShortName, Type = assetItem.TypeName, Item = item }
}; };
} }
var actor = FlaxEngine.Object.Find<Actor>(ref id); var actor = FlaxEngine.Object.Find<Actor>(ref id, true);
if (actor != null) if (actor != null)
{ {
return new List<SearchResult> return new List<SearchResult>
@@ -236,6 +236,16 @@ namespace FlaxEditor.Modules
new SearchResult { Name = actor.Name, Type = actor.TypeName, Item = actor } new SearchResult { Name = actor.Name, Type = actor.TypeName, Item = actor }
}; };
} }
var script = FlaxEngine.Object.Find<Script>(ref id, true);
if (script != null && script.Actor != null)
{
string actorPathStart = $"{script.Actor.Name}/";
return new List<SearchResult>
{
new SearchResult { Name = $"{actorPathStart}{script.TypeName}", Type = script.TypeName, Item = script }
};
}
} }
Profiler.BeginEvent("ContentFinding.Search"); Profiler.BeginEvent("ContentFinding.Search");
@@ -388,6 +398,13 @@ namespace FlaxEditor.Modules
Editor.Instance.SceneEditing.Select(actor); Editor.Instance.SceneEditing.Select(actor);
Editor.Instance.Windows.EditWin.Viewport.FocusSelection(); Editor.Instance.Windows.EditWin.Viewport.FocusSelection();
break; break;
case Script script:
if (script.Actor != null)
{
Editor.Instance.SceneEditing.Select(script.Actor);
Editor.Instance.Windows.EditWin.Viewport.FocusSelection();
}
break;
} }
} }

View File

@@ -0,0 +1,43 @@
#include "LogContexts.h"
#include "Engine/Core/Types/Guid.h"
#include "Engine/Core/Types/String.h"
#include "Engine/Core/Collections/Array.h"
ThreadLocal<LogContextStack> LogContexts::CurrentLogContext;
String LogContextFormatter::Format()
{
LogContextData context = LogContexts::Get();
if (context.ObjectID != Guid::Empty)
return String::Format(TEXT("(Loading source was {0})"), context.ObjectID);
return String("");
}
void LogContexts::Set(const Guid& id)
{
LogContextData context = LogContextData();
context.ObjectID = id;
LogContextStack contextStack = LogContexts::CurrentLogContext.Get();
contextStack.Stack.Push(context);
LogContexts::CurrentLogContext.Set(contextStack);
}
void LogContexts::Clear()
{
LogContextStack contextStack = LogContexts::CurrentLogContext.Get();
contextStack.Stack.Pop();
LogContexts::CurrentLogContext.Set(contextStack);
}
LogContextData LogContexts::Get()
{
LogContextStack contextStack = LogContexts::CurrentLogContext.Get();
if (contextStack.Stack.Count() == 0)
return LogContextData();
return contextStack.Stack.Last();
}

View File

@@ -0,0 +1,91 @@
#pragma once
#include "Engine/Core/Config.h"
#include "Engine/Scripting/ScriptingType.h"
#include "Engine/Threading/ThreadLocal.h"
class String;
struct Guid;
/// <summary>
/// Log context data structure. Contains different kinds of context data for different situtations.
/// </summary>
API_STRUCT() struct FLAXENGINE_API LogContextData
{
DECLARE_SCRIPTING_TYPE_STRUCTURE(LogContextData)
/// <summary>
/// A GUID for an object which this context applies to.
/// </summary>
API_FIELD() Guid ObjectID;
};
template<>
struct TIsPODType<LogContextData>
{
enum { Value = true };
};
/// <summary>
/// Stores a stack of log contexts.
/// </summary>
API_STRUCT() struct FLAXENGINE_API LogContextStack
{
DECLARE_SCRIPTING_TYPE_STRUCTURE(LogContextStack)
/// <summary>
/// Log context stack.
/// </summary>
Array<LogContextData> Stack;
};
template<>
struct TIsPODType<LogContextStack>
{
enum { Value = true };
};
/// <summary>
/// Log context interaction class. Methods are thread local, and as such, the context is as well.
/// This system is used to pass down important information to be logged through large callstacks
/// which don't have any reason to be passing down the information otherwise.
/// </summary>
API_CLASS(Static) class FLAXENGINE_API LogContexts
{
DECLARE_SCRIPTING_TYPE_MINIMAL(LogContexts)
/// <summary>
/// Adds a log context element to the stack to be displayed in warning and error logs.
/// </summary>
/// <param name="id">The GUID of the object this context applies to.</param>
API_FUNCTION() static void Set(const Guid& id);
/// <summary>
/// Pops a log context element off of the stack and discards it.
/// </summary>
API_FUNCTION() static void Clear();
/// <summary>
/// Gets the log context element off the top of stack.
/// </summary>
/// <returns>The log context element at the top of the stack.</returns>
API_FUNCTION() static LogContextData Get();
private:
static ThreadLocal<LogContextStack> CurrentLogContext;
};
/// <summary>
/// Formatting class which will provide a string to apply
/// the current log context to an error or warning.
/// </summary>
API_CLASS(Static) class FLAXENGINE_API LogContextFormatter
{
DECLARE_SCRIPTING_TYPE_MINIMAL(LogContextFormatter)
public:
/// <summary>
/// Returns a string which represents the current log context on the stack.
/// </summary>
/// <returns>The formatted string representing the current log context.</returns>
API_FUNCTION() static String Format();
};

View File

@@ -126,10 +126,11 @@ namespace FlaxEngine
/// </summary> /// </summary>
/// <param name="id">Unique ID of the object.</param> /// <param name="id">Unique ID of the object.</param>
/// <typeparam name="T">Type of the object.</typeparam> /// <typeparam name="T">Type of the object.</typeparam>
/// <param name="skipLog">Whether or not to log warnings when objects aren't found.</param>
/// <returns>Found object or null if missing.</returns> /// <returns>Found object or null if missing.</returns>
public static T Find<T>(ref Guid id) where T : Object public static T Find<T>(ref Guid id, bool skipLog = false) where T : Object
{ {
return Internal_FindObject(ref id, typeof(T)) as T; return Internal_FindObject(ref id, typeof(T), skipLog) as T;
} }
/// <summary> /// <summary>
@@ -137,10 +138,11 @@ namespace FlaxEngine
/// </summary> /// </summary>
/// <param name="id">Unique ID of the object.</param> /// <param name="id">Unique ID of the object.</param>
/// <param name="type">Type of the object.</param> /// <param name="type">Type of the object.</param>
/// <param name="skipLog">Whether or not to log warnings when objects aren't found.</param>
/// <returns>Found object or null if missing.</returns> /// <returns>Found object or null if missing.</returns>
public static Object Find(ref Guid id, Type type) public static Object Find(ref Guid id, Type type, bool skipLog = false)
{ {
return Internal_FindObject(ref id, type); return Internal_FindObject(ref id, type, skipLog);
} }
/// <summary> /// <summary>
@@ -335,7 +337,7 @@ namespace FlaxEngine
internal static partial string Internal_GetTypeName(IntPtr obj); internal static partial string Internal_GetTypeName(IntPtr obj);
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_FindObject", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))] [LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_FindObject", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
internal static partial Object Internal_FindObject(ref Guid id, [MarshalUsing(typeof(Interop.SystemTypeMarshaller))] Type type); internal static partial Object Internal_FindObject(ref Guid id, [MarshalUsing(typeof(Interop.SystemTypeMarshaller))] Type type, [MarshalAs(UnmanagedType.U1)] bool skipLog = false);
[LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_TryFindObject", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))] [LibraryImport("FlaxEngine", EntryPoint = "ObjectInternal_TryFindObject", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Interop.StringMarshaller))]
internal static partial Object Internal_TryFindObject(ref Guid id, [MarshalUsing(typeof(Interop.SystemTypeMarshaller))] Type type); internal static partial Object Internal_TryFindObject(ref Guid id, [MarshalUsing(typeof(Interop.SystemTypeMarshaller))] Type type);

View File

@@ -2,6 +2,7 @@
#include "Script.h" #include "Script.h"
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"
#include "Engine/Core/Types/LogContexts.h"
#if USE_EDITOR #if USE_EDITOR
#include "Internal/StdTypesContainer.h" #include "Internal/StdTypesContainer.h"
#include "ManagedCLR/MClass.h" #include "ManagedCLR/MClass.h"
@@ -330,6 +331,9 @@ void Script::Serialize(SerializeStream& stream, const void* otherObj)
void Script::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) void Script::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
{ {
// Store Logging Context
LogContexts::Set(GetID());
// Base // Base
SceneObject::Deserialize(stream, modifier); SceneObject::Deserialize(stream, modifier);
@@ -364,4 +368,6 @@ void Script::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier
} }
} }
} }
LogContexts::Clear();
} }

View File

@@ -24,6 +24,7 @@
#include "Engine/Core/ObjectsRemovalService.h" #include "Engine/Core/ObjectsRemovalService.h"
#include "Engine/Core/Types/TimeSpan.h" #include "Engine/Core/Types/TimeSpan.h"
#include "Engine/Core/Types/Stopwatch.h" #include "Engine/Core/Types/Stopwatch.h"
#include "Engine/Core/Types/LogContexts.h"
#include "Engine/Content/Asset.h" #include "Engine/Content/Asset.h"
#include "Engine/Content/Content.h" #include "Engine/Content/Content.h"
#include "Engine/Engine/EngineService.h" #include "Engine/Engine/EngineService.h"
@@ -891,7 +892,7 @@ ScriptingObject* Scripting::FindObject(Guid id, MClass* type)
// Check type // Check type
if (!type || result->Is(type)) if (!type || result->Is(type))
return result; return result;
LOG(Warning, "Found scripting object with ID={0} of type {1} that doesn't match type {2}.", id, String(result->GetType().Fullname), String(type->GetFullName())); LOG(Warning, "Found scripting object with ID={0} of type {1} that doesn't match type {2}. {3}", id, String(result->GetType().Fullname), String(type->GetFullName()), LogContextFormatter::Format());
return nullptr; return nullptr;
} }
@@ -910,7 +911,7 @@ ScriptingObject* Scripting::FindObject(Guid id, MClass* type)
return asset; return asset;
} }
LOG(Warning, "Unable to find scripting object with ID={0}. Required type {1}.", id, String(type->GetFullName())); LOG(Warning, "Unable to find scripting object with ID={0}. Required type {1}. {2}", id, String(type->GetFullName()), LogContextFormatter::Format());
return nullptr; return nullptr;
} }

View File

@@ -7,6 +7,7 @@
#include "Engine/Level/Actor.h" #include "Engine/Level/Actor.h"
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"
#include "Engine/Core/Types/Pair.h" #include "Engine/Core/Types/Pair.h"
#include "Engine/Core/Types/LogContexts.h"
#include "Engine/Utilities/StringConverter.h" #include "Engine/Utilities/StringConverter.h"
#include "Engine/Content/Asset.h" #include "Engine/Content/Asset.h"
#include "Engine/Content/Content.h" #include "Engine/Content/Content.h"
@@ -708,7 +709,7 @@ DEFINE_INTERNAL_CALL(MString*) ObjectInternal_GetTypeName(ScriptingObject* obj)
return MUtils::ToString(obj->GetType().Fullname); return MUtils::ToString(obj->GetType().Fullname);
} }
DEFINE_INTERNAL_CALL(MObject*) ObjectInternal_FindObject(Guid* id, MTypeObject* type) DEFINE_INTERNAL_CALL(MObject*) ObjectInternal_FindObject(Guid* id, MTypeObject* type, bool skipLog = false)
{ {
if (!id->IsValid()) if (!id->IsValid())
return nullptr; return nullptr;
@@ -725,15 +726,20 @@ DEFINE_INTERNAL_CALL(MObject*) ObjectInternal_FindObject(Guid* id, MTypeObject*
{ {
if (klass && !obj->Is(klass)) if (klass && !obj->Is(klass))
{ {
LOG(Warning, "Found scripting object with ID={0} of type {1} that doesn't match type {2}.", *id, String(obj->GetType().Fullname), String(klass->GetFullName())); if (!skipLog)
LOG(Warning, "Found scripting object with ID={0} of type {1} that doesn't match type {2}. {3}", *id, String(obj->GetType().Fullname), String(klass->GetFullName()), LogContextFormatter::Format());
return nullptr; return nullptr;
} }
return obj->GetOrCreateManagedInstance(); return obj->GetOrCreateManagedInstance();
} }
if (klass)
LOG(Warning, "Unable to find scripting object with ID={0}. Required type {1}.", *id, String(klass->GetFullName())); if (!skipLog)
else {
LOG(Warning, "Unable to find scripting object with ID={0}", *id); if (klass)
LOG(Warning, "Unable to find scripting object with ID={0}. Required type {1}. {2}", *id, String(klass->GetFullName()), LogContextFormatter::Format());
else
LOG(Warning, "Unable to find scripting object with ID={0}", *id);
}
return nullptr; return nullptr;
} }