Unity.Extensions.Serilog
April 8, 2025 · View on GitHub
Overview
This package contains Unity-specific logging extension methods for Serilog.
If you find this package useful, consider supporting its development!
Unity® and the Unity logo are trademarks of Unity Technologies.
Installation
This package must be installed from a Git URL.
Simply follow the instructions at that link, using the following URL:
https://github.com/DerploidEntertainment/UnityUtil.git?path=/UnityUtil/Assets/Unity.Extensions.Serilog#<branch>
Replace <branch> with one of the branch names described in the UnityUtil installing docs (e.g., unity6).
You can ignore the steps about installing Odin Inspector in those docs.
This package has only been tested on Unity 6, but it should work with earlier Unity versions as well.
Usage
Note
Consider installing the following packages as well to get the best developer experience with Serilog in Unity projects:
- Serilog.Enrichers.Unity to enrich log events with additional Unity data
- Serilog.Sinks.Unity or Serilog.Sinks.Unity3D to add Unity as a Serilog sink
- Unity.Extensions.Logging for some other useful Microsoft.Extensions.Logging extension methods for Unity
Set SelfLog
As a general best practice, consider enabling Serilog's SelfLog to write to Unity's Console like so:
SelfLog.Enable(UnityEngine.Debug.LogError); // Or .LogWarning()
This will cause any exceptions within the Serilog pipeline to be shown as warnings or errors in the Unity Console, as these exceptions almost always indicate a configuration error that require immediate developer attention.
Set MinimumLevel from Unity
Use code like the following to have your Serilog configuration's MinimumLevel match the current Unity ILogger.filterLogType:
var logger = new LoggerConfiguration()
.MinimumLevel.IsUnityFilterLogType()
// Configure other Serilog enrichers, sinks, etc.
.CreateLogger()
By default, this package uses Debug.unityLogger.filterLogType,
but you can also provide a specific ILogger instance:
ILogger myLogger;
var logger = new LoggerConfiguration()
.MinimumLevel.IsUnityFilterLogType(myLogger)
// Configure other Serilog enrichers, sinks, etc.
.CreateLogger()
Destructure Unity objects for context
Unity log messages can optionally include a context argument.
You can pass a GameObject or Component-derived instance to the message and then,
when you click on the message in the Unity Console, Unity will highlight that object in the Hierarchy window.
This feature is very powerful when debugging in the Editor.
See the Debug.Log() manual page for more info.
The Unity sinks provided by Serilog.Sinks.Unity and Serilog.Sinks.Unity3D can both set the context to the value of a log property; however, setting this log property is quite tricky.
Serilog tries to serialize objects of unknown type (including UnityEngine.Object-derived types) by calling their ToString() method;
therefore, you cannot simply pass a UnityEngine.Object instance to logger.Information() or the related Serilog log methods.
Sinks would ignore the log property's serialized string value and your log messages would not have the desired context
(clicking them in the Console would not highlight anything).
The same is true if you try to push a UnityEngine.Object instance to the Serilog LogContext.
Instead, you must use a custom enricher
or destructuring policy
to preserve the original, unserialized object instance.
If that all sounds daunting, you can just use the UnityLogContextDestructuringPolicy from this package.
Simply set up your Serilog logging configuration like so:
var logger = new LoggerConfiguration()
.Destructure.UnityObjectContext()
// Configure other Serilog enrichers, sinks, etc.
.CreateLogger()
Then you can include a Unity object instance as context wherever you emit logs, like so:
class MyLoggingType : UnityEngine.Object // MonoBehaviour, ScriptableObject, etc.
{
private ILogger _logger;
public void SomeMethod() {
using (LogContext.PushProperty("@UnityLogContext", ValueTuple.Create(this)))
_logger.Information("Hello, world!");
}
}
The value of the context scope property must be a ValueTuple<T>, not the raw Unity object instance,
and the name of the property must be prefixed with the structure capturing operator, '@'.
This allows Serilog to "destructure" the property and extract the object instance wrapped in the ValueTuple,
without changing how all UnityEngine.Object instances are destructured/formatted in your app.
Warning
If your sink does not correctly consume this log property and/or remove it from the log event,
then you will likely get Maximum destructuring depth reached exceptions from Unity through Serilog's SelfLog
(assuming you have enabled it).
You can use any log property name with Serilog.Sinks.Unity, as long as that sink is configured to expect that log property
(it can also be configured to remove that log property, since it's value is presumably not needed other than for context).
For the older Serilog.Sinks.Unity3D sink, the log property must be named %_DO_NOT_USE_UNITY_ID_DO_NOT_USE%, and it will not be removed from the log event.
Also consider using Serilog behind Microsoft.Extensions.Logging.Abstractions;
if you do then you can reference Unity.Extensions.Logging,
which provides a UnityObjectLogger that automatically adds the context log property,
making your logging code less verbose (no extra using statements):
class MyLoggingType : UnityEngine.Object // MonoBehaviour, ScriptableObject, etc.
{
private ILogger<MyLoggingType> _logger;
private void Awake() { // Or Start(), or wherever your type sets up its logger
_logger = _loggerFactory.CreateLogger(this);
}
public void SomeMethod() {
_logger.LogInformation("Hello, world!"); // Automatically adds log property for `this`, to be used as `context` in sinks
}
}
For completeness, if you are using the Microsoft Extensions library but do not want to use UnityObjectLogger,
then you can still add the Unity object for context like so:
class MyLoggingType : UnityEngine.Object // MonoBehaviour, ScriptableObject, etc.
{
private ILogger<MyLoggingType> _logger;
private void Awake() { // Or Start(), or wherever your type sets up its logger
_logger = _loggerFactory.CreateLogger<MyLoggingType>();
}
public void SomeMethod() {
using (_logger.BeginScope(new Dictionary<string, object> { { "@UnityLogContext", ValueTuple.Create(this) } }))
_logger.LogInformation("Hello, world!");
// Or, when using Serilog.Extensions.Logging 9.0.0+ ...
using (_logger.BeginScope(("@UnityLogContext", ValueTuple.Create(this))))
_logger.LogInformation("Hello, world!");
}
}
Legal
Copyright © 2025 - present Derploid® Entertainment, LLC. All rights reserved.
Unity® and the Unity logo are trademarks of Unity Technologies.
