Structured Logging System
A category-aware, severity-filtered logging service that routes structured entries to console and file outputs through clean, Unity-free domain interfaces.
What the system is for
Every Unity project logs something, but most projects do it the same way: scattered Debug.Log calls, no category, no severity filter, and nothing written to disk when something goes wrong at runtime. When a crash happens in a build, the information is either missing or impossible to parse.
Serenity's Logging System replaces that with a structured service built on clean domain interfaces. Logs carry a category, a severity, optional tags and an optional exception. Each component gets its own scoped logger. Verbosity can be overridden per category at runtime. Output routes to Unity's console, to a file, or to both — independently configured.
The Unity problem
Debug.Log works fine in a small prototype. In a real project it does not scale. There is no way to silence a noisy subsystem without commenting out its calls. There is no file record for post-mortem analysis of builds. There is no consistent structure across log messages, so filtering in the console becomes guesswork. And when the project grows and multiple systems log at the same time, the output is an unreadable wall of text with no context about which system wrote what.
Patching that after the fact means touching every system that ever called Debug.Log. Doing it per scene or per prefab means inconsistent behavior and inevitable regressions. The problem needs a routing layer from the start, not a cleanup pass at the end.
How Serenity approaches it
The Logging System exposes ILogService as the central service. It writes entries through WriteLog, WriteVerboseLog, WriteInformationLog, WriteWarningLog, WriteErrorLog and WriteExceptionLog, each accepting a category and optional tags. Verbosity is controlled globally through the Verbosity property and per-category through SetCategoryVerbosity and ClearCategoryVerbosity. Calling For(category) returns an ILoggerComponent scoped to that category, which carries its own EffectiveVerbosity and exposes the same write methods without requiring the caller to pass the category on every call.
Output is shaped by ILogProfile, which maps installer types to ILogService instances through ILogRoute entries and provides a default logger via GetDefaultLogger. The profile definition is declared through ILogProfileDefinition. Each route points to a concrete ILogService — UnityConsoleLogService for Unity's console, UnityFileLogService for disk writes through IFileWriterService. Both are registered and wired by their own installers inside the initialization pipeline, so the domain layer never touches Unity APIs directly.
How it fits into Serenity
Logging lives in the Serenity.Logging namespace and follows the framework's layered structure. The Domain layer defines LogSeverity (Verbose, Info, Warning, Error, Exception), the LogEntry value object, and LogCategoryVerbosity. The Application layer defines ILogService, ILoggerComponent, ILogProfile, ILogRoute, ILogProfileDefinition and the factory interface ILogServiceFactory. The Installation layer registers everything through LogInstaller. The Business layer has zero dependency on Unity APIs — it can be tested without a Unity runtime.
The Infrastructure layer provides the Unity-specific implementations. ConsoleLogging contains UnityConsoleLogService and its factory. FileLogging contains UnityFileLogService, which writes through IFileWriterService for crash-recovery durability. Both are installed independently, so a project can enable console-only, file-only, or both routes without changing domain or application code.
Practical workflow
- Declare an ILogProfileDefinition that maps your installer types to logger keys and sets a default logger key.
- Add UnityConsoleLogInstaller, UnityFileLogInstaller, or both to your initialization pipeline depending on which output routes you need.
- Let LogInstaller wire ILogProfile and ILogService into the service locator through the standard installation pipeline.
- Inject ILogService into any system that needs to log, or call For(category) once at construction time to get an ILoggerComponent scoped to that system.
- Override verbosity for a noisy category at runtime with SetCategoryVerbosity without touching any other part of the project.
- Check IsEnabledFor(category, severity) before building expensive log messages to avoid allocation overhead when the entry would be filtered out anyway.
What you get
- Central logging service ILogService with global Verbosity and per-category overrides via SetCategoryVerbosity and ClearCategoryVerbosity
- Component-scoped logger ILoggerComponent returned by ILogService.For(category), with its own EffectiveVerbosity
- Structured LogEntry value object carrying UTC timestamp, LogSeverity, category, message, tags, and optional Exception
- LogSeverity enum with five levels: Verbose, Info, Warning, Error, and Exception
- Log profile ILogProfile and route map ILogRoute that direct each installer type to the correct ILogService instance
- Console output route via UnityConsoleLogService, registered through UnityConsoleLogInstaller
- File output route via UnityFileLogService writing through IFileWriterService, registered through UnityFileLogInstaller
- Zero Unity dependency in the Business layer — domain and application code is fully testable without a Unity runtime
When to use this
- Projects that need to capture logs to disk for post-mortem analysis of builds and crashes.
- Codebases with multiple subsystems where per-category verbosity control is needed to keep the console readable during development.
- Teams that want consistent log structure — severity, category, tags — across every system from day one.
- Any project that wants to replace scattered Debug.Log calls with a routable, filterable, testable logging layer.
Related systems
Use Serenity's Logging System when you want structured, filterable log output that works the same in the editor and in a shipped build, without coupling your domain code to Unity's console API.
English
Español
Català