Skip to content

Commit ad003b7

Browse files
committed
chore: Make better logger
1 parent a31fee7 commit ad003b7

File tree

12 files changed

+569
-85
lines changed

12 files changed

+569
-85
lines changed
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
using System;
2+
using System.Diagnostics;
3+
using System.Diagnostics.CodeAnalysis;
4+
using System.Runtime.CompilerServices;
5+
using System.Text;
6+
using UnityEngine;
7+
using Debug = UnityEngine.Debug;
8+
using LogType = UnityEngine.LogType;
9+
10+
namespace Unity.Netcode
11+
{
12+
internal class ContextualLogger
13+
{
14+
private const string k_NetcodeHeader = "[Netcode] ";
15+
private bool m_UseCompatibilityMode;
16+
private readonly GameObject m_GameObject;
17+
private readonly ContextBuilder m_Builder = new();
18+
19+
private LogContextNetworkManager m_ManagerContext;
20+
private readonly GenericContext m_LoggerContext;
21+
22+
private const string k_CompilationCondition = "UNITY_ASSERTIONS";
23+
24+
public ContextualLogger(bool useCompatibilityMode = false)
25+
{
26+
m_UseCompatibilityMode = useCompatibilityMode;
27+
m_ManagerContext = new LogContextNetworkManager(true);
28+
m_GameObject = null;
29+
m_LoggerContext = GenericContext.Create();
30+
}
31+
32+
public ContextualLogger([NotNull] NetworkManager networkManager, GameObject gameObject)
33+
{
34+
m_ManagerContext = new LogContextNetworkManager(networkManager);
35+
m_GameObject = gameObject;
36+
m_LoggerContext = GenericContext.Create();
37+
}
38+
39+
[Conditional(k_CompilationCondition)]
40+
internal void UpdateNetworkManagerContext(NetworkManager manager)
41+
{
42+
m_ManagerContext.Dispose();
43+
m_ManagerContext = new LogContextNetworkManager(manager);
44+
}
45+
46+
[Conditional(k_CompilationCondition)]
47+
internal void PushContext(string key, object value)
48+
{
49+
m_LoggerContext.StoreInfo(key, value);
50+
}
51+
52+
[Conditional(k_CompilationCondition)]
53+
internal void PushContext(string key)
54+
{
55+
m_LoggerContext.StoreContext(key);
56+
}
57+
58+
[Conditional(k_CompilationCondition)]
59+
internal void PopContext(string key)
60+
{
61+
m_LoggerContext.ClearInfo(key);
62+
}
63+
64+
65+
[HideInCallstack]
66+
[Conditional(k_CompilationCondition)]
67+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
68+
public void CaptureFunctionCall([CallerMemberName] string memberName = "")
69+
{
70+
Log(LogType.Log, new Context(LogLevel.Developer, memberName, true));
71+
}
72+
73+
[HideInCallstack]
74+
[Conditional(k_CompilationCondition)]
75+
public void Info(Context context) => Log(LogType.Log, context);
76+
[HideInCallstack]
77+
[Conditional(k_CompilationCondition)]
78+
public void Warning(Context context) => Log(LogType.Warning, context);
79+
[HideInCallstack]
80+
[Conditional(k_CompilationCondition)]
81+
public void Error(Context context) => Log(LogType.Error, context);
82+
83+
[HideInCallstack]
84+
[Conditional(k_CompilationCondition)]
85+
public void InfoServer(Context context) => LogServer(LogType.Log, context);
86+
[HideInCallstack]
87+
[Conditional(k_CompilationCondition)]
88+
public void WarningServer(Context context) => LogServer(LogType.Warning, context);
89+
[HideInCallstack]
90+
[Conditional(k_CompilationCondition)]
91+
public void ErrorServer(Context context) => LogServer(LogType.Error, context);
92+
93+
[HideInCallstack]
94+
public void Exception(Exception exception)
95+
{
96+
Debug.unityLogger.LogException(exception, m_GameObject);
97+
}
98+
99+
[HideInCallstack]
100+
private void Log(LogType logType, Context context)
101+
{
102+
// Don't act if the LogLevel is higher than the level of this log
103+
if (m_ManagerContext.LogLevel > context.Level)
104+
{
105+
return;
106+
}
107+
108+
var message = BuildLog(context);
109+
Debug.unityLogger.Log(logType, (object)message, context.GameObjectOverride ?? m_GameObject);
110+
}
111+
112+
[HideInCallstack]
113+
private void LogServer(LogType logType, Context context)
114+
{
115+
// Don't act if the configured logging level is higher than the level of this log
116+
if (m_ManagerContext.LogLevel <= context.Level)
117+
{
118+
return;
119+
}
120+
121+
var message = BuildLog(context);
122+
Debug.unityLogger.Log(logType, (object)message, context.GameObjectOverride ?? m_GameObject);
123+
124+
m_ManagerContext.TrySendMessage(logType, message);
125+
}
126+
127+
private string BuildLog(Context context)
128+
{
129+
m_Builder.Reset();
130+
131+
// Add the Netcode prefix
132+
m_Builder.Append(k_NetcodeHeader);
133+
134+
if (m_UseCompatibilityMode)
135+
{
136+
m_Builder.Append(context.Message);
137+
}
138+
else
139+
{
140+
// Add the system context
141+
m_ManagerContext.AppendTo(m_Builder);
142+
m_LoggerContext.AppendTo(m_Builder);
143+
144+
// Add the context for this log
145+
context.AppendTo(m_Builder);
146+
}
147+
148+
return m_Builder.Build();
149+
}
150+
}
151+
152+
internal class ContextBuilder
153+
{
154+
private readonly StringBuilder m_Builder = new();
155+
private const string k_OpenBracket = "[";
156+
private const string k_CloseBracket = "]";
157+
private const string k_Separator = ":";
158+
159+
public void Reset()
160+
{
161+
m_Builder.Clear();
162+
}
163+
164+
public void AppendContext(string context)
165+
{
166+
m_Builder.Append(k_OpenBracket);
167+
m_Builder.Append(context);
168+
m_Builder.Append(k_CloseBracket);
169+
}
170+
171+
public void AppendContext(object key, object value)
172+
{
173+
m_Builder.Append(k_OpenBracket);
174+
m_Builder.Append(key);
175+
m_Builder.Append(k_Separator);
176+
m_Builder.Append(value);
177+
m_Builder.Append(k_CloseBracket);
178+
}
179+
180+
public void Append(string value) => m_Builder.Append(value);
181+
182+
public string Build() => m_Builder.ToString();
183+
}
184+
}

com.unity.netcode.gameobjects/Runtime/Logging/ContextualLogger.cs.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace Unity.Netcode
5+
{
6+
internal readonly struct GenericContext : ILogContext, IDisposable
7+
{
8+
private readonly List<string> m_Contexts;
9+
private readonly Dictionary<object, object> m_Info;
10+
11+
private GenericContext(List<string> contexts, Dictionary<object, object> info)
12+
{
13+
m_Contexts = contexts;
14+
m_Info = info;
15+
}
16+
17+
public readonly void AppendTo(ContextBuilder builder)
18+
{
19+
if (m_Contexts != null)
20+
{
21+
foreach (var ctx in m_Contexts)
22+
{
23+
builder.AppendContext(ctx);
24+
}
25+
}
26+
27+
if (m_Info != null)
28+
{
29+
foreach (var (key, value) in m_Info)
30+
{
31+
builder.AppendContext(key, value);
32+
}
33+
}
34+
}
35+
36+
public void StoreContext(string msg)
37+
{
38+
m_Contexts.Add(msg);
39+
}
40+
41+
public void StoreInfo(object key, object value)
42+
{
43+
m_Info.Add(key, value);
44+
}
45+
46+
public void ClearInfo(object key)
47+
{
48+
m_Info?.Remove(key);
49+
}
50+
51+
public void Dispose()
52+
{
53+
PreallocatedStore.Free(this);
54+
}
55+
56+
public static GenericContext Create()
57+
{
58+
return PreallocatedStore.GetPreallocated();
59+
}
60+
61+
private static class PreallocatedStore
62+
{
63+
private static readonly Queue<GenericContext> k_Preallocated = new();
64+
65+
internal static GenericContext GetPreallocated()
66+
{
67+
if (k_Preallocated.Count > 0)
68+
{
69+
k_Preallocated.Dequeue();
70+
}
71+
72+
var contexts = new List<string>();
73+
var info = new Dictionary<object, object>();
74+
return new GenericContext(contexts, info);
75+
}
76+
77+
internal static void Free(GenericContext ctx)
78+
{
79+
ctx.m_Contexts.Clear();
80+
ctx.m_Info.Clear();
81+
k_Preallocated.Enqueue(ctx);
82+
}
83+
}
84+
}
85+
86+
}

com.unity.netcode.gameobjects/Runtime/Logging/GenericContext.cs.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
using System.Runtime.CompilerServices;
2+
using UnityEngine;
3+
4+
namespace Unity.Netcode
5+
{
6+
internal interface ILogContext
7+
{
8+
public void AppendTo(ContextBuilder builder)
9+
{
10+
}
11+
}
12+
13+
internal struct Context : ILogContext
14+
{
15+
public readonly LogLevel Level;
16+
private readonly string m_CallingFunction;
17+
internal readonly string Message;
18+
public GameObject GameObjectOverride;
19+
20+
21+
private readonly GenericContext m_Other;
22+
23+
public Context(LogLevel level, string msg, [CallerMemberName] string memberName = "")
24+
{
25+
Level = level;
26+
Message = msg;
27+
m_CallingFunction = memberName;
28+
29+
m_Other = GenericContext.Create();
30+
GameObjectOverride = null;
31+
}
32+
33+
internal Context(LogLevel level, string msg, bool noCaller)
34+
{
35+
Level = level;
36+
Message = msg;
37+
m_CallingFunction = null;
38+
39+
m_Other = GenericContext.Create();
40+
GameObjectOverride = null;
41+
}
42+
43+
public void AppendTo(ContextBuilder builder)
44+
{
45+
// [CallingFunction]
46+
if (!string.IsNullOrEmpty(m_CallingFunction))
47+
{
48+
builder.AppendContext(m_CallingFunction);
49+
}
50+
51+
// [SomeContext][SomeName:SomeValue]
52+
m_Other.AppendTo(builder);
53+
54+
// Human-readable log message
55+
builder.Append(" ");
56+
builder.Append(Message);
57+
}
58+
59+
public Context With(object key, object value)
60+
{
61+
m_Other.StoreInfo(key, value);
62+
return this;
63+
}
64+
65+
public Context With(string msg)
66+
{
67+
m_Other.StoreContext(msg);
68+
return this;
69+
}
70+
71+
public Context ForNetworkPrefab(NetworkPrefab networkPrefab)
72+
{
73+
GameObjectOverride = networkPrefab.Prefab.gameObject;
74+
m_Other.StoreInfo(nameof(NetworkPrefab), networkPrefab.Prefab.name);
75+
return this;
76+
}
77+
}
78+
}

com.unity.netcode.gameobjects/Runtime/Logging/LogContext.cs.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)