package module
Version: v0.8.0 Latest Latest

This package is not in the latest version of its module.

Go to latest
Published: Apr 21, 2022 License: MIT Imports: 9 Imported by: 0



⚙️ A set of tools for logging, debugging, and measuring of Go code.

package main

import ""

func main() {
	// Motor functions via three three basic output modes.
	// 1. log() is used for ordinary logs.
	// 2. debug() is used when "probing" and debugging.
	// 3. trace() is invoked only tracing / benchmarking purposes.
	log, debug, trace := motor.New()

	// V(2) forces debug mode.

	log("Hello, world!")

	if debug() {
		debug("In debug mode.")

		n, m := 10, 0
		for i := 1; i <= n; i++ {
			trace("m (%d) += %d", m, i)
			m += i

		debug("m =", m)
		debug("m expected =", n*(n+1)/2)
package main

//go:generate go install
import (


var log, debug, _ = motor.New()

// $ probe tinything [n]
func TestTinything(t *testing.T) {
	// if not in probe, quit early
	if !debug() {
		// in general order, by now
		// this test would have performed nothing.


	// in probe, simply use args to augment the test.
	n, _ := strconv.Atoi(motor.Args[0])
	log("n = %d", n)

This repository contains a small Go library, an interface, and two simple command–line tools, probe and speed.


go get -u

" optional
go install
go install


Here's the deal.

I grew accustomed to a certain minimalistic programming style,

Suddenly, everything made sense. No more worries about logging and metrics. I've finally managed to incoroporate unit testing into my development workflow, to the point motor programming should probably look reminiscent of so–called test–driven development, which I learned to hate over the years.

There's nothing worse than assert(2+2, 4) and you know it.

I've come to realise that at some point my code becomes aware of its surroundings, be it planned or not. Motor programming model exploits this: there are three output modes, progressively more and more verbose. In normal conditions, my code is expected to run in log(1), debug(2), and sometimes, trace(3) modes.

Now, quantitative analysis of code, benchmarks.

Go can be very progressive in some major respects. For instance, it supports first–class benchmarks via Benchmark* functions and go test. Unfortunately, this is simply not enough. Benchmarking becomes overly counter–intuitive to apply in your development workflow the moment you walk out of the some abstract algorithm in some abstract context realm. And it's a good thing. There's no point desperately trying to fit your all–round code into a very square hole that is a benchmark. Quite a lot of it has to do with data locality. In real–life, code components are far from decoupled. It's not so much of a trouble, but inconvenience—to test them as such, without constantly having to fall back and refer to them as the whole. Maybe we're better off performing at least some of the measturements while the code is still running normally? In any case, that's exactly what speed does. There are two things you can measure, ∆t and ∆d/∆t. This may not seem like much, but in fact it's all you need. You get time and differential, latency and throughput. Go take some measurements! Good code is traceable, so you should report them over trace(). If the program runs in speed, it will trace automatically. The tool will then collect the printed measurements and produce a CSV (comma–separated values) output.


Motor is a no bullshit piece of software.

Program probe

Use program to run an individual probe of a test.

In vim, list all availablte tests with




:!probe method01

=== RUN   TestMethod01
probe complete
--- PASS: TestMethod01 (0.00s)
ok      test    0.083s

To run a tracing probe,

:!probe method02...

=== RUN   TestMethod01
tracing on
% n=1 m=7.083
% n=2 m=9.112
probe complete
--- PASS: TestMethod02 (0.00s)
ok      test    0.180s
Program speed







This section is empty.


This section is empty.


This section is empty.


type Adapter added in v0.7.7

type Adapter interface {
	// Begin is called whenever a procedure is started.

	// Write manages the log transformation.
	// This function transforms the provided chunk into
	// the intermediate batching buffer in charge of the
	// flush valve.
	Write(brr *Brr, chunk Chunk, w io.Writer)

	// End is called just before the final flush.

	// "Real" destination of the log stream.
	// The log writes are flushed here at the most suitable
	// time to minimize memory use and I/O pressure due to
	// irregular writes typical for logging.
	Device() io.Writer

	// Max allowed time-to-flush for any given write.
	// Returns negative if unlimited.
	TTF() time.Duration

Adapter is how motor interacts with the destination I/O.

type Brr added in v0.7.7

type Brr struct {
	// (Not unique) one of a kind name (ie. /endpoint)
	Namekind string

	// (Unique) context (request) identifier.
	Id string

	// Start time
	T0 time.Time
	// contains filtered or unexported fields

Brr is the procedural log context.

If constructed from Func or Gofunc, it will attempt to predict both the average log size and time to flush in order to utilize memory most efficiently.

func (*Brr) Debug added in v0.7.7

func (brr *Brr) Debug(mode ...bool) bool

If set to true, Debugf/ln writes will come through.

func (*Brr) Debugf added in v0.7.7

func (brr *Brr) Debugf(format string, a ...interface{})

Debugf is the debug mode Printf counterpart.

If the context is not debugging, this function will return false immediately without ever consulting the log buffer.

func (*Brr) Debugln added in v0.7.7

func (brr *Brr) Debugln(a ...interface{})

Debugln is the debug mode Println counterpart.

If the context is not debugging, this function will return false immediately without ever consulting the log buffer.

func (*Brr) Flush added in v0.7.7

func (brr *Brr) Flush()

Flush orders the final flush of the log buffer.

This function is called when there's no more work to be done per the existing context. All further writes will be ignored.

func (*Brr) Func added in v0.7.7

func (brr *Brr) Func(namekind, id string) *Brr

Func consructs a new motorised log.

This function will allocate a new log buffer according to the estimate of procedure demands known at the time of its creation.

Use consistent procedure names.

func (*Brr) Gofunc added in v0.7.7

func (brr *Brr) Gofunc(namekind, id string, f func(*Brr))

Gofunc constructs a new asynchronous context.

func (*Brr) Printf added in v0.7.7

func (brr *Brr) Printf(format string, a ...interface{})

Printf puts a fragment of a message into the log buffer.

func (*Brr) Println added in v0.7.7

func (brr *Brr) Println(a ...interface{})

Println puts a new message into the log buffer.

type Chunk added in v0.7.7

type Chunk struct {
	// Printf format string, if provided.
	Format string

	Args []interface{}
	// Named printf arguments (tags) list.
	Tags  []Tag
	Flags []Flag

	// True if the chunk is a debug write in itself.
	Debug bool

Chunk represents a single log write to the log buffer.

func (Chunk) Autowrite added in v0.7.7

func (c Chunk) Autowrite(w io.Writer)

Autowrite prints (and formats) chunk arguments.

func (Chunk) Flag added in v0.7.7

func (c Chunk) Flag(f Flag) bool

Flag returns true if chunk contains flag.

type Config added in v0.7.7

type Config struct {
	// If true, motor will let debug writes through.
	Debug bool

	// Motor can write into multiple devices simultaneously.
	// For example, you can have human-readable formatted
	// messages end up in stdout, structured JSON log in
	// the dedicated log file, and have a seperate exhaust
	// reserved for metrics only.
	Sinks []Adapter

Config is a set of preferences required to start it.

type Example added in v0.7.8

type Example struct {

Example is a basic adapter with terminal formatting.

func (*Example) Begin added in v0.7.8

func (adp *Example) Begin(brr *Brr)

func (*Example) Device added in v0.7.8

func (adp *Example) Device() io.Writer

func (*Example) End added in v0.7.8

func (adp *Example) End(brr *Brr)

func (*Example) MaxLatency added in v0.7.8

func (adp *Example) MaxLatency() time.Duration

func (*Example) Write added in v0.7.8

func (adp *Example) Write(brr *Brr, c Chunk, w io.Writer)

type Flag added in v0.7.7

type Flag int

Flag allows for customization of chunks.

const (
	Error Flag = iota
	// Pretty yellow warning thing :-)

type Motor added in v0.7.7

type Motor struct {
	// contains filtered or unexported fields

Motor is the global logger context.

All writes to it are performed immediately, as opposed to contextual writes which may be held up in a buffer until further flushes.

func New

func New(config Config) *Motor

New returns a new motor.

type Tag added in v0.7.7

type Tag struct {
	Pos   int
	Label string

Tag is a structured log element.

They are extracted from named format string operands supported by out printf implementation.

%(label)s, %(label)#+v

See: Brr.Printf()


Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL