Skip to content

Latest commit

 

History

History
206 lines (146 loc) · 9.3 KB

File metadata and controls

206 lines (146 loc) · 9.3 KB

Unity.Extensions.Logging

Overview

This package contains Unity-specific extension methods for logging with Microsoft.Extensions.Logging.

If you find this package useful, consider supporting its development!

Buy Me A Coffee

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.Logging#<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

This package makes it easy to associate your log messages with info about the logging UnityEngine.Object-derived instances (MonoBehaviours, ScriptableObjects, etc.), which logging providers can use when logging to the Unity Console.

All of the examples below assume that your logging code has access to an ILoggerFactory instance, via dependency injection or a static singleton, e.g.

Unity context

This package's most common use case is for UnityEngine.Object-derived types that want to include their instance in the log. Logging providers can then use this instance as the context parameter when writing to the Unity Console, ensuring that when you click on log messages in the Unity Console, the logging object is highlighted in the Editor. See Unity's Debug.Log() manual page for more explanation of the context parameter.

The following example creates an ILogger instance that will include the logging type instance as a scope property on all logs:

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 attaches `this` to the log message
    }
}

The name of the scope property is UnityLogContext by default. If you want a different property name then you can change it with a settings object, like so:

_logger = _loggerFactory.CreateLogger(this, new UnityObjectLoggerSettings {
    UnityContextLogProperty = "Context",
});

You might change the property name, e.g., to avoid collisions with property names added by other enrichers, or if you wanted to save a few bytes with shorter property names in your production logs.

If you don't want to include the scope property at all, then you can disable it like so:

_logger = _loggerFactory.CreateLogger(this, new UnityObjectLoggerSettings {
    AddUnityContext = false,
});

Note

The value of the context scope property is technically a ValueTuple<UnityEngine.Object>, and the name of the property is technically prefixed with the structure capturing operator, '@'. This allows logging providers that follow the Message Templates schema (which includes most modern .NET logging libraries, including Serilog and NLog) to "destructure" the property and extract the object instance wrapped in the ValueTuple, without changing how all UnityEngine.Object instances are destructured/formatted.

Unity hierarchy name

In addition to (or instead of) including an object instance in log messages, you might want to include an object's "hierarchy name".

For GameObject and Component-derived instances, the hierarchy name is the name of the object's transform and all parent transforms, separated by a delimiter. For all other UnityEngine.Object instances, the hierarchy name is simply the value of the object's name property. This information is useful in logs from built Unity players where you can't click on a log in the Editor, but still want to know which instance of a type generated a log.

As an example, suppose that you have the following GameObject hierarchy:

Example Unity object hierarcy: "object" under "parent" under "grandparent"

object's default hierarchy name is then grandparent>parent>object.

The following example creates an ILogger instance that will include the logging instance's hierarchy name as a scope property on all logs:

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, new UnityObjectLoggerSettings {
            AddHierarchyName = true,
        });
    }

    public void SomeMethod() {
        _logger.LogInformation("Hello, world!");    // Automatically attaches heirarchy name of `this` to the log message
    }
}

The name of the scope property is UnityHierarchyName by default. You might want to change the property name, e.g., to avoid collisions with property names added by other libraries, or if you wanted to save a few bytes with shorter property names in your production logs. You can change the property name like so:

_logger = _loggerFactory.CreateLogger(this, new UnityObjectLoggerSettings {
    AddHierarchyName = true,
    HierarchyNameLogProperty = "Name",
});

To change the separator between transform names to something other than >, set the ParentNameSeparator property:

_logger = _loggerFactory.CreateLogger(this, new UnityObjectLoggerSettings {
    AddHierarchyName = true,
    ParentNameSeparator = "-",
});

Note that computing the hierarchy name requires walking the logging object's transform hierarchy on every log event, which could get expensive for deeply childed objects. That is why you must "opt in" to including the hierarchy name by setting AddHierarchyName to true.

As an additional optimization, this package assumes that object hierarches do not change, so that the hierarchy name can be cached. If this is not true, and object's parent(s) can change, then indicate that its hierarchy is not static with code like this:

_logger = _loggerFactory.CreateLogger(this, new UnityObjectLoggerSettings {
    AddHierarchyName = true,
    HasStaticHierarchy = false,
});

UnityDebugLogger(Factory)

This package also contains default implementations of the Microsoft.Extensions.Logging ("MEL") interfaces:

Such code makes of two types:

  1. UnityDebugLogger, which implements MEL.ILogger such that Log() just logs directly to the Unity Console
  2. UnityDebugLoggerFactory, which implements MEL.ILoggerFactory such that CreateLogger() returns a UnityDebugLogger instance

You can use these types like so:

using Unity.Extensions.Logging;

ILogger<T> logger = new UnityDebugLoggerFactory().CreateLogger<T>();
logger.LogInformation("Hello, world!");

These types are useful anywhere that setting up a full logging pipeline is impossible or unnecessary, and you just want a quick one-liner to set up Unity logging behind a MEL ILogger instance, such as:

  1. In automated tests using the Unity Test Framework where you just want log messages to show in the Console for debugging
  2. In Unity Editor scripts that may not have access to your usual runtime ILoggerFactorys
  3. In methods tagged with Unity's [MenuItem] attribute or Odin's [Button] attribute
  4. In static types or types tagged with [ExecuteInEditMode]
  5. In ScriptableObject lifecycle methods like Awake() and OnValidate()

Tip

If you really don't care where log messages end up (particularly in automated tests) then consider using MEL's built-in NullLoggerFactory type.

Notes for specific logging providers

Serilog

When using the Serilog logging provider, consider installing the following packages as well to get the best developer experience in Unity projects:

Legal

Copyright © 2025 - present Derploid® Entertainment, LLC. All rights reserved.

Unity® and the Unity logo are trademarks of Unity Technologies.