Documentation
Complete guide to the Miru Go debugger toolkit. Learn how to use structured logging, panic recovery, testing utilities, and the remote dashboard.
Table of Contents
Install
Get started with Miru by installing the package via go get:
go get github.com/denzeysenpai/miruQuick Start
Create a new debugger instance and configure it for your needs:
package main
import "github.com/denzeysenpai/miru"
func main() {
cfg := miru.DebugConfig{
OutputPath: "./Debug Output", // default
FolderBy: miru.Month, // or miru.Year, or miru.FolderNone
Colorful: true, // colored console output
WithContext: true, // include function:line in output
IncludeTests: false, // when true, Test() also logs to file
}
debug := miru.NewDebugger()
debug.Config(cfg)
SomeFunction(debug)
}
func SomeFunction(debug *miru.Debugger) {
debug.Func("SomeFunction")
defer func() {
if r := recover(); r != nil {
debug.Catch(r)
}
}()
// Console logging
debug.Out("Processing user data", userID)
// Memory statistics
debug.Mem()
// Group related tests
tg := debug.TestGroup("User Operations")
defer tg.Close()
tg.Test("user creation", createUser("test@example.com") != nil)
tg.Test("user validation", validateEmail("test@example.com"))
// Trace execution time
defer debug.Trace("database operation")()
// ... your code ...
} The example above demonstrates all core features: panic recovery with Catch, console logging with Out, memory statistics with Mem, test groups with TestGroup, and execution tracing with Trace.
Using Default: NewDebugger
You can also skip the config and just stick to the defaults:
func TestBasics() {
debug := miru.NewDebugger()
debug.Out("Hello There!")
}Config
Configure Miru with DebugConfig:
| Field | Type | Default | Description |
|---|---|---|---|
| OutputPath | string | "./Debug Output" | Directory for log files |
| FolderBy | FolderBy | FolderNone | miru.Month, miru.Year, or miru.FolderNone |
| Colorful | bool | false | Colored console output |
| WithContext | bool | true* | Include function name and line number |
| IncludeTests | bool | false | When true, Test() results are also written to log file |
| WalkDepth | int | 5 | Max depth for Walk pretty-print; -1 = no limit |
* Use miru.DefaultConfig() to get a config with all defaults (including WithContext: true).
Pretty-print: Walk
Inspect structs, slices, and maps with indented output. Depth is limited by WalkDepth in config (-1 = no limit).
type User struct{ Name string; Age int }
debug.Walk([]User{{"Alice", 30}, {"Bob", 25}})
debug.Walk(myMap)Output (first line uses same style as other Miru logs; rest is indented):
[Miru Walk]: <dateTime> main:42 -> slice (len 2)
[0]:
Name: Alice
Age: 30
[1]:
Name: Bob
Age: 25Panic recovery: Catch
debug.Catch(r) logs the recovered panic to the console and to the log file:
[Miru Catch]: <dateTime> SomeFunction:42 -> Caught: runtime error: ...- Red:
[Miru Catch]and the caught message (when Colorful is true) - Yellow:dateTime
Console logging: Out
Like console.log in JavaScript: any number of arguments, any types. Never writes to log files.
debug.Out("Hi I'm Mr. Meseeks!", 10, jsonData)Output (one line per argument):
[Miru Out]: <dateTime> SomeFunction:line -> Hi I'm Mr. Meseeks!
[Miru Out]: <dateTime> SomeFunction:line -> 10
[Miru Out]: <dateTime> SomeFunction:line -> {"key":"value"}- Red:
[Miru Out] - Yellow:dateTime
Structs, maps, and slices are serialized as JSON.
Tap
Pass a value through a function (e.g. to log it) and get the same value back. Like Ruby's tap. This way, you can log and get the value in the same line.
x := debug.Tap(compute(), func(v interface{}) { debug.Out(v) })
// x is the result of compute(); you also logged itTesting: Test
Run a function and compare its return value to the expected value. Works with or without arguments:
// no args
debug.Test("add", func() int { return 2 + 2 }, 4)
// with args: funcName, fn, expected, then args to pass to fn
debug.Test("add", func(a, b int) int { return a + b }, 7, 3, 4)
debug.Test("fail", func() int { return 1 }, 2)Output:
[Miru Test]: <dateTime> add -> PASSED (0.20ms)
[Miru Test]: <dateTime> fail -> FAILED (0.22ms)- Green:
[Miru Test]; PASSED is green, FAILED is red - Yellow:dateTime and duration
Test Groups: TestGroup
Group related tests together and get a summary of passed/failed tests:
tg := debug.TestGroup("User Authentication")
defer tg.Close()
tg.Test("valid login", authenticate("user", "pass") == nil)
tg.Test("invalid password", authenticate("user", "wrong") != nil)
tg.Test("empty username", authenticate("", "pass") != nil)Output:
[Miru TGroup Start]: <dateTime> User Authentication
[0] <dateTime> valid login -> PASSED
[1] <dateTime> invalid password -> PASSED
[2] <dateTime> empty username -> PASSED
[Miru TGroup Close]: <dateTime> (3 / 3)- Green:
[Miru TGroup Start]and[Miru TGroup Close]headers - PASSED tests are green, FAILED tests are red
- Final summary shows (passed / total) count
Remote Dashboard
Serve a small web UI that shows logs and traces live (SSE). Call once to start the server; all Catch, Out, Test, Trace, Walk, CheckStack, and Mem output is streamed to the page.
srv := debug.RemoteDashboard(8765) // port 0 or negative = 8765
// open http://localhost:8765
// when done: srv.Shutdown(ctx)No log file writing from the dashboard; it only streams what's already printed to the console.
Dashboard Features
Live Log Streaming
Real-time updates via Server-Sent Events (SSE). All log types displayed with timestamps and source context.
Log Type Filtering
Clickable count cards for each log type. Shows live counts with color-coded entries matching console output.
Search
Real-time search across all log entries. Searches in timestamps, log types, and message bodies.
Auto-Scroll Toggle
Enable/disable automatic scrolling to latest logs. Visual indicator shows active state.
Export Logs
Download filtered logs as timestamped text file. Exports only currently visible entries.
Keyboard Shortcuts
Ctrl+F for search, Ctrl+S for auto-scroll, Ctrl+E for export, Ctrl+C to clear, ? for help.
Keyboard Shortcuts
Stack Trace: CheckStack
Print the current goroutine's stack trace (console only, no file):
debug.CheckStack() Output: [Miru CheckStack] header plus indented stack lines. With Colorful, the goroutine line is yellow.
Tracing: Trace
Measure execution time with a deferred call:
defer debug.Trace("someFunc")()Output:
[Miru Trace]: <dateTime> someFunc -> 0.25ms- Green:
[Miru Trace] - Yellow:dateTime and duration
Memory Statistics: Mem
Display runtime memory statistics including allocation, heap usage, system memory, goroutine count, and GC cycles:
debug.Mem()Output:
[Miru Mem]: <dateTime> memory -> alloc=12MB heap=8MB sys=45MB goroutines=3 gc=15- Green:
[Miru Mem] - Yellow:dateTime
Shows alloc (current allocation), heap (heap allocation), sys (system memory), goroutines (active goroutines), and gc (GC cycles).
Error Flow: IfErr
IfErr provides a fluent error-handling helper that lets you react to errors without repetitive if err != nil blocks.
It supports chaining actions like:
Do()– run code when an error existsElse()– run code when no error existsPanic()– panic if an error existsRetry()– retry an operation multiple times
IfErr also logs the error automatically when it is not nil.
Basic Usage
err := doSomething()
debug.IfErr(err).
Do(func() {
debug.Out("operation failed")
}).
Else(func() {
debug.Out("operation succeeded")
})Do – Run Code on Error
Do executes a function only if an error occurred.
debug.IfErr(err).Do(func() {
debug.Out("error occurred")
})Else – Handle Success
Else executes a function only if no error occurred.
debug.IfErr(err).
Do(func() {
debug.Out("failed")
}).
Else(func() {
debug.Out("success")
})Panic on Error
Panic panics if an error exists.
debug.IfErr(err).Panic()Equivalent to:
if err != nil {
panic(err)
}Retry Operations
Retry repeatedly executes a function until it succeeds or the retry limit is reached.
err = debug.IfErr(err).Retry(3, func() error {
return reconnect()
})Behavior:
- Only runs if the original error is not nil
- Stops retrying once the function returns
nil - Logs each failed retry attempt
Output Sample:
[Miru Err]: <dateTime> main:32 -> connection failed
[Miru Out]: retry 1/3 failed: connection refused
[Miru Out]: retry 2/3 failed: connection refusedSummary
IfErr enables concise and readable error handling:
debug.IfErr(err).Do(...)
debug.IfErr(err).Do(...).Else(...)
debug.IfErr(err).Panic()
debug.IfErr(err).Retry(3, reconnect)License
Miru is released under the MIT License.