# README
gobox
A collection of libraries that are useful for implementing Go services, libraries, and more.
Contributing
Please read the CONTRIBUTING.md document for guidelines on developing and contributing changes.
High-level Overview
Please see individual packages in the generated documentation for overviews on each.
Go idioms
Standard idioms
Please see Code review comments, go proverbs and Idiomatic Go.
Log errors with events.NewErrorInfo
When logging errors, use log.Debug(ctx, "some debug event", events.NewErrorInfo(err))
instead of using log.F{"error": err}
. NewErrorInfo logs errors using outreach naming conventions and logs stack traces.
Do not use context.WithValue
Context is often abused for thread-local state. There are very few legitimate uses for this (tracing is one of those).
Do not use fmt.PrintXXX or the standard log package
Prefer the gobox log package. This logs data in a structured format suitable for outreach Go services.
Do not use non-literal messages with log
Do not use the following pattern:
message := fmt.Sprintf("working on org: %s", model.Org.ShortName)
log.Info(ctx, message, modelInfo)
The first arg of log.XXX
calls should be a literal string so we can
quickly find out where a log message comes from. The rest of the args
can hold any structured data we want. The
events
package exposes a few common logging structures.
Use code generation for stringifying enums
See go generate:
For example, given this snippet,
package painkiller
//go:generate ./scripts/gobin.sh golang.org/x/tools/cmd/[email protected] -type=Pill
type Pill int
const (
Placebo Pill = iota
Aspirin
Ibuprofen
Paracetamol
Acetaminophen = Paracetamol
)
running go generate ./...
from the root of the repo will create the
file pill_string.go, in package painkiller, containing a definition of
func (Pill) String() string
which can be used to get the string
representation of the enum.
A suggested workflow is to run go generate ./...
from the root of the repo before sending PRs out.
Use events.Org for logging org tenancy info.
Org information can be logged with standard naming conventions using:
orgInfo := events.Org{Bento: xyz, DatabaseHost: ...}
log.Debug(ctx, "doing xyz", orgInfo)
In most cases, though you probably have some other model struct which has this info. In those cases, the preferred route is to make those model types themselves loggable:
type Model struct {...}
func (m *Model) MarshalLog(addField func(key string, value interface{}) {
m.Org().MarshalLog(addField)
... add any custom fields you want: addField("myCustomField", m.CustomInfo)...
}
func (m *Model) Org() events.Org {
return events.Org{...}
}
Now Model
can be used in logs like so:
var myModel m
log.Debug(ctx, "doing xyz", myModel)
Better still is to generate the MarshalLog function using struct tags