c# - Serilog 访问传递给日志记录语句的 LogEvent 中的原始对象
问题描述
我在 Unity3D 中使用 Serilog。我有一个简单的接收器,可以将 Serilog 日志记录语句写入Debug.LogFormat
Unity:
public class UnityLogEventSink : ILogEventSink
{
public void Emit(LogEvent logEvent)
{
// QUESTION: How to pass a UnityEngine.Object to Serilog logging statement such that it is available here?
UnityEngine.Object contextObject = null;
using (StringWriter stringBuffer = new StringWriter())
{
GetTextFormatter().Format(logEvent, stringBuffer);
LogType logType = GetUnityLogType(logEvent);
string logString = stringBuffer.ToString().Trim();
Debug.LogFormat(logType, LogOption.NoStacktrace, contextObject, logString);
}
}
// GetTextFormatter, GetUnityLogType etc. are defined here ...
}
现在我想将一个游戏对象传递给 Serilog 日志记录语句,这样我就可以在我的接收器中访问这个游戏对象。(Debug.LogFormat
当单击日志消息时,使用 GameObject 调用将在 Unity 编辑器中突出显示该对象。我想要那个。)
// Example what I have in mind (not working):
logger.ForContext("unityObject", gameObject).Information("This is an info with context");
我尝试将 GameObject 包装在 ScalarValue 和自定义 LogEventPropertyValue 中,但 GameObject 仍被转换为字符串(发生在 Serilog 的 PropertyValueConverter.cs 中)。
我需要 Debug.LogFormat 的原始 GameObject 实例。有没有办法保留 GameObject 引用,以便我可以在我的接收器中使用它?
作为一种解决方法,我可以将引用存储在静态映射中,并使用映射的键记录一个字符串属性。这样我可以稍后在接收器中从该地图中获取实例。但这是围绕 Serilog 工作的。有没有更好的利用 Serilog 的解决方案?
解决方案
尼克在https://github.com/serilog/serilog/issues/1124中为我回答了这个问题
public class ScalarValueEnricher : ILogEventEnricher
{
protected readonly LogEventProperty _prop;
public ScalarValueEnricher(string name, object value)
{
_prop = new LogEventProperty(name, new ScalarValue(value));
}
public void Enrich(LogEvent evt, ILogEventPropertyFactory _) =>
evt.AddPropertyIfAbsent(_prop);
}
也可以创建一个 Unity 特定的子类:
public class UnityObjectEnricher : ScalarValueEnricher
{
public static readonly string unityObjectPropertyName = "unityObject";
public UnityObjectEnricher(UnityEngine.Object value)
: base(unityObjectPropertyName, value)
{
}
}
然后可以在接收器中访问此属性:
private UnityEngine.Object GetUnityEngineContextObject(LogEvent logEvent)
{
if (logEvent.Properties.TryGetValue(UnityObjectEnricher.unityObjectPropertyName, out LogEventPropertyValue logEventPropertyValue))
{
if (logEventPropertyValue is ScalarValue scalarValue)
return scalarValue.Value as UnityEngine.Object;
}
return null;
}
像这样使用它:
// Note: using LogContext requires Serilog configuration ".Enrich.FromLogContext()"
using (LogContext.Push(new UnityObjectEnricher(gameObject)))
{
logger.Information("This is an info with context");
}
或者:
logger.ForContext(new UnityObjectEnricher(gameObject)).Information("This is another info with context");
推荐阅读
- c# - 有没有更优雅的方法来指定多个具有相同性质的一对多关系?
- sql - SQL 多个 SELECT 运行速度非常慢
- javascript - 当电子邮件地址输入到该行时,如何让 Google 表格通过电子邮件发送该行?
- python - spacy-udpipe 与 pytextrank 从非英文文本中提取关键字
- error-handling - Ren'py 中的错误处理
- python-2.x - 未来的absolute_import可以清除__package__吗?
- vb.net - VB.NET 如何使用 savefiledialog 保存列表视图
- javascript - 画布外菜单动画
- mysql - MySQL:如何在子查询 WHERE 条件中使用主查询变量
- dynamics-crm - 是否可以在 D365 的可编辑网格视图中隐藏列?