API Reference

Documentation

Complete guide to the Miru Go debugger toolkit. Learn how to use structured logging, panic recovery, testing utilities, and the remote dashboard.

Install

Get started with Miru by installing the package via go get:

bash
go get github.com/denzeysenpai/miru

Quick Start

Create a new debugger instance and configure it for your needs:

go
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:

go
func TestBasics() {
	debug := miru.NewDebugger()

	debug.Out("Hello There!")
}

Config

Configure Miru with DebugConfig:

FieldTypeDefaultDescription
OutputPathstring"./Debug Output"Directory for log files
FolderByFolderByFolderNonemiru.Month, miru.Year, or miru.FolderNone
ColorfulboolfalseColored console output
WithContextbooltrue*Include function name and line number
IncludeTestsboolfalseWhen true, Test() results are also written to log file
WalkDepthint5Max 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).

go
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):

text
[Miru Walk]:	<dateTime>	main:42	->	slice (len 2)
  [0]:
    Name: Alice
    Age: 30
  [1]:
    Name: Bob
    Age: 25

Panic recovery: Catch

debug.Catch(r) logs the recovered panic to the console and to the log file:

text
[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.

go
debug.Out("Hi I'm Mr. Meseeks!", 10, jsonData)

Output (one line per argument):

text
[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.

go
x := debug.Tap(compute(), func(v interface{}) { debug.Out(v) })
// x is the result of compute(); you also logged it

Testing: Test

Run a function and compare its return value to the expected value. Works with or without arguments:

go
// 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:

text
[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:

go
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:

text
[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.

go
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

Ctrl+FFocus search box
Ctrl+SToggle auto-scroll
Ctrl+EExport logs
Ctrl+CClear logs
EscClear search
?Toggle shortcuts help panel

Stack Trace: CheckStack

Print the current goroutine's stack trace (console only, no file):

go
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:

go
defer debug.Trace("someFunc")()

Output:

text
[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:

go
debug.Mem()

Output:

text
[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 exists
  • Else() – run code when no error exists
  • Panic() – panic if an error exists
  • Retry() – retry an operation multiple times

IfErr also logs the error automatically when it is not nil.

Basic Usage

go
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.

go
debug.IfErr(err).Do(func() {
    debug.Out("error occurred")
})

Else – Handle Success

Else executes a function only if no error occurred.

go
debug.IfErr(err).
    Do(func() {
        debug.Out("failed")
    }).
    Else(func() {
        debug.Out("success")
    })

Panic on Error

Panic panics if an error exists.

go
debug.IfErr(err).Panic()

Equivalent to:

go
if err != nil {
    panic(err)
}

Retry Operations

Retry repeatedly executes a function until it succeeds or the retry limit is reached.

go
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:

text
[Miru Err]: <dateTime> main:32 -> connection failed
[Miru Out]: retry 1/3 failed: connection refused
[Miru Out]: retry 2/3 failed: connection refused

Summary

IfErr enables concise and readable error handling:

go
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.