A lightweight logging container for Unity that keeps your debug output out of the Unity Console and inside dedicated, per-context windows you control.
Instead of dumping every message into one shared console, you create named console instances β one per system, feature, or subsystem β and view them in a purpose-built editor window. Each message keeps its timestamp, source and full call stack, and every stack frame is clickable straight into your IDE, exactly like the built-in Unity Console.
- No clutter. Your gameplay/tooling logs live in their own window, so the Unity Console stays reserved for engine warnings, exceptions and thirdβparty noise.
- Split by context. Give each system its own instance (
"Networking","AI","Save System", β¦) and debug one context at a time without filtering through everything else. - See everything, in order. The All Instances view merges every instance's messages into a single chronological stream, so you never lose the ordering between systems.
- Jump to code. Selecting a message lists its call stack as buttons; each one opens the file at the exact line in your external editor.
- Hide it in builds β or don't. By default nothing reaches a player build.
An optional settings asset lets you forward messages to
Debug.Login builds, with an independent toggle per message type. - Thread-safe. Log from jobs, background threads or async code without worrying about where the call comes from.
Package Manager (git URL)
-
Window βΈ Package Manager -
+⸠Add package from git URL⦠-
Enter:
https://github.com/reromanlee/ConsoleContainer.git
Or edit Packages/manifest.json directly
{
"dependencies": {
"com.reromanlee.consolecontainer": "https://github.com/reromanlee/ConsoleContainer.git"
}
}Or install locally by cloning the repository into your project's Packages/
folder.
Requires Unity 6000.0 (Unity 6) or newer.
using reromanlee.ConsoleContainer;
public class NetworkService
{
// Name the instance so it shows up in the viewer's dropdown.
private readonly IConsoleInstance console = new ConsoleInstance("Networking");
public void Connect(string host)
{
console.CreateText(this, "Connecting to", host);
console.CreateWarning(this, "Latency high:", "180ms");
console.CreateError("Socket", "Connection refused by", host);
}
}Open the window from Tools βΈ Console Viewer.
In the Package Manager, select ConsoleContainer and import the Console
Container Demo sample. Drop ConsoleContainerDemo on a GameObject, press
Play, open the viewer, and switch the dropdown between the running instances
(one of which logs from a background thread).
ConsoleInstance implements IConsoleInstance:
void CreateText (object source, params string[] messageContent);
void CreateText (string source, params string[] messageContent);
void CreateWarning(object source, params string[] messageContent);
void CreateWarning(string source, params string[] messageContent);
void CreateError (object source, params string[] messageContent);
void CreateError (string source, params string[] messageContent);Every message is composed the same way β think of it like console.log in the
browser, where the first argument names the source:
-
Time β rendered as
[HH:mm:ss]. -
Source β the
stringyou pass, orsource.GetType().Namefor theobjectoverload. -
Content β every
messageContentelement joined with a single space. -
Label β the full line shown in the list and details pane:
{source}: {content}
So console.CreateText(this, "Loaded", "42", "assets") from a SaveSystem
produces:
[14:03:11] SaveSystem: Loaded 42 assets
new ConsoleInstance(); // auto-named "Instance N"
new ConsoleInstance("My Context"); // named for the dropdown
instance.Name; // the display name
instance.Clear(); // remove this instance's messages
instance.Dispose();// clear and detach from the viewer| Feature | Behaviour |
|---|---|
| Instance dropdown | Pick a single instance, or All Instances to see every message merged in chronological order. |
| Zebra striping | Alternating rows are subtly highlighted for readability; selection and hover always take priority. |
| Selection | Click a message to show its full {source}: {content} text in the details pane. |
| Copy | Copies the selected message to the system clipboard. |
| Call stack | Each frame becomes a button β top button is the log call site, going down the chain β that opens the file at its line in your IDE. |
| Clear | Clears the currently selected instance, or all of them when All Instances is selected. |
In the Unity Editor, messages go only to the Console Viewer window. They
never touch the Unity Console β no Debug.Log, no doubled-up output β as long as
you log through a ConsoleInstance (calling Debug.Log or throwing exceptions
yourself still behaves normally).
In a player build, there is no viewer, so messages are optionally forwarded to Unity's log based on an optional settings asset:
-
Assets βΈ Create βΈ ConsoleContainer βΈ Settings. -
Move the created
ConsoleContainerSettingsasset into anyResourcesfolder so it ships with the build. -
Toggle logging per message type:
Message type Build output Text Debug.Log("{source}: {content}")Warning Debug.LogWarning("{source}: {content}")Error Debug.LogError("{source}: {content}")
If no settings asset exists in the build, all ConsoleContainer messages stay hidden. This lets you keep verbose instrumentation in your code and decide, per project, exactly what (if anything) surfaces in shipped logs.
- Editor-only cost. Message storage and call-stack capture happen only under
UNITY_EDITOR; player builds do nothing beyond the optionalDebugforward. - Incremental rendering. The viewer appends only new rows each editor frame using a globally monotonic sequence number β it does not rebuild the whole list on every message. A full rebuild happens only when instances change or a clear occurs.
- Off-thread friendly. Logging never blocks on the UI; the window marshals all rendering to the editor's main-thread update loop, so background threads just append under a short lock.
- Chronological merge is free. Because sequence numbers are assigned atomically at creation, the All Instances view stays ordered without sorting the entire history.
Best suited to typical debugging volumes. For sustained, extremely high-rate
logging, prefer a dedicated instance you can Clear() periodically.
MIT β see LICENSE.md.
