rubberneck

package module
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: Nov 21, 2017 License: MIT Imports: 4 Imported by: 24

README

Rubberneck

godoc here Go Report Card Travis build

Pretty(ish) print configuration output on application startup! Make it easy to see what those env vars actually told your app to do!

Rubberneck

Installation

It's recommended to use the gopkg.in service for this package since it means you'll always get a tagged release that maintains a stable API.

go get gopkg.in/relistan/rubberneck.v1

The Problem This Solves

Modern applications are often configured from environment variables, or from a combination of environment variables and command line flags, or config files. Environment variables are great and they're recommended by everthing from The Twelve-Factor App to modern container engines.

This is good stuff. But there are a couple of challenges:

  1. Env vars make it very easy to think you're configuring your app while accidentally leaving out critial settings, or misspelling a variable, or putting an extra _ where it wasn't needed. This can make debugging problematic. What config was it actually using when it ran out of memory?

  2. Discoverability is a challenge. One nice thing about config files is that they make it easy to see all the possible configuration settings in one place. You can ship your app with a default configuration with lots of comments in it and users can easily discover which settings to provide. If you configure your app with env vars, you have to put this all into the documentation (always a good idea, anyway), but you lose the direct tie between the code and the docs. That means it's easy to have settings that aren't in the docs.

A Solution

In Go, configuration is usually stored in a struct or nested struct. This makes using systems like envconfig or Kingpin really easy. Having the configuration collected all together presents a possible solution:

One possibility is to print out your application's configuration on startup. Once all the configuration has been generated, all CLI flags applied, dump the struct to the stdout of the application so that we can see not just what env vars were passed, but what the configuration was that was actually generated inside the application. You can also see what defaults got applied, which overrides were taken into account, and exactly what was in the struct at startup time.

You could do this in a lot of ways. This is for humans and humans don't read JSON easily, but you could still dump a TOML or YAML file, for example. But the output won't nest easily into your logging system, for example. So Rubberneck tries to solve that in a really simple way.

Installing

You may always import this repo from GitHub, which will get you whatever is on the master branch. But I recommend using the gopkg.in service, which provides versioned repo support for the go tools. To get this package, I recommend:

go get gopkg.in/relistan/rubberneck.v1

To import it:

import "gopkg.in/relistan/rubberneck.v1"

Usage

The most basic usage is:

	rubberneck.Print(myConfigStruct)

This will dump the output to stdout using the fmt package and will look something like:

Settings -----------------------------------------
  * AdvertiseIP: 192.168.168.168
  * ClusterIPs: [10.3.18.204 123.123.123.123]
  * ConfigFile: sidecar.toml
  * ClusterName: default
  * CpuProfile: false
  * Discover: [docker static]
  * HAproxyDisable: false
  * LoggingLevel:
  * Sidecar:
    * ExcludeIPs: [192.168.168.168]
    * StatsAddr:
    * PushPullInterval:
      * Duration: 20s
    * GossipMessages: 20
    * LoggingFormat: standard
    * LoggingLevel: debug
    * DefaultCheckEndpoint:
  * DockerDiscovery:
	* DockerURL: unix:///var/run/docker.sock
--------------------------------------------------

If you want to instead print to the logrus package you could do is like this:

    printer := rubberneck.NewPrinter(log.Infof, rubberneck.NoAddLineFeed)
    printer.Print(opts)

If you wanted to provide a custom label at the top, and to print out more than one struct you could do something like this:

    printer := rubberneck.NewPrinter(log.Infof, rubberneck.NoAddLineFeed)
    printer.PrintWithLabel("Sidecar starting", *opts, config)

This will result in the "Settings" block above being titled liked:

INFO[001] Sidecar starting ---------------------------------
INFO[001]   * AdvertiseIP: 192.168.168.168
INFO[001]   * ClusterIPs: [10.3.18.204 123.123.123.123]
INFO[001]   * ConfigFile: sidecar.toml

How to handle output is highly configurable because Rubberneck just takes a function as the output argument. The function must be of the following definition:

type printerFunc func(format string, v ...interface{})

The various logrus functions conform to this as do those of various other packages. If yours doesn't, or you want to do something else with the output, you can pass an anonymous function that does what you need.

Masking values

Printing configuration settings may also include some sensitive data that you might not want to see in your logs. This includes passwords, private keys etc. You can provide a masking function to filter out these values. For example:

    var redacted = "[REDACTED]"
    maskFunc := func(argument string) *string {
			if argument == "Password" {
				return &redacted
			}
			return nil
		}
    printer := rubberneck.NewPrinterWithKeyMasking(log.Printf, maskFunc, rubberneck.NoAddLineFeed)
    printer.Print(opts)

The above masking will then mask the password setting:

Settings -----------------------------------------
  * AdvertiseIP: 192.168.168.168
  * ClusterIPs: [10.3.18.204 123.123.123.123]
  * Password: [REDACTED]
--------------------------------------------------

Caveats

This is for printing configuration structs, usually derived from environment variable configurations or CLI flags, and therefore does not attempt to handle all possible structures. It assumes that you don't have anything but basic types inside of slices, for example. This is generally what you want. If it's not, then you might be better off dumping out YAML or something more complex.

Contributing

Contributions are more than welcome. Bug reports with specific reproduction steps are great. If you have a code contribution you'd like to make, open a pull request with suggested code.

Pull requests should:

  • Clearly state their intent in the title
  • Have a description that explains the need for the changes
  • Include tests!
  • Not break the public API

Ping us to let us know you're working on something interesting by opening a GitHub Issue on the project.

Documentation

Overview

Pretty(ish) print configuration output on application startup! Make it easy to see what those env vars actually told your app to do!

The most basic usage is:

rubberneck.Print(myConfigStruct)

Which will print out an output something like:

Settings -----------------------------------------
  * AdvertiseIP: 192.168.168.168
  * ClusterIPs: [10.3.18.204 123.123.123.123]
  * ConfigFile: sidecar.toml
  * ClusterName: default
  * CpuProfile: false
  * Discover: [docker static]
  * HAproxyDisable: false
  * LoggingLevel:
  * Sidecar:
    * ExcludeIPs: [192.168.168.168]
    * StatsAddr:
    * PushPullInterval:
      * Duration: 20s
    * GossipMessages: 20
    * LoggingFormat: standard
    * LoggingLevel: debug
    * DefaultCheckEndpoint:
  * DockerDiscovery:
	* DockerURL: unix:///var/run/docker.sock
--------------------------------------------------

You may configure a Printer for your specific needs if the default usage is not suited to your purposes.

Index

Constants

View Source
const (
	AddLineFeed   = iota
	NoAddLineFeed = iota
)

AddLineFeed will direct the Printer to add line feeds at the end of each output. NoAddLineFeed will not. This is useful for controlling the display of output when functions exhibit different behavior. For example, fmt.Printf wants line feeds added, whereas logrus.Infof does not.

Variables

This section is empty.

Functions

func Print

func Print(obj interface{})

Print configures a default printer to output to stdout and then prints the object.

Types

type MaskFunc added in v1.1.0

type MaskFunc func(argument string) *string

MaskFunc takes a config argument and returns a string that is used to mask config values. This is useful to redact passwords etc.

type Printer

type Printer struct {
	Show PrinterFunc
	Mask MaskFunc
}

Printer defines the signature of a function that can be used to display the configuration. This signature is used by fmt.Printf, log.Printf, various logging output levels from the logrus package, and others.

func NewDefaultPrinter

func NewDefaultPrinter() *Printer

NewDefaultPrinter returns a Printer configured to write to stdout.

func NewPrinter

func NewPrinter(fn PrinterFunc, lineFeed int) *Printer

NewPrinter returns a Printer configured to use the supplied function to output to the supplied function.

func NewPrinterWithKeyMasking added in v1.1.0

func NewPrinterWithKeyMasking(fn PrinterFunc, mk MaskFunc, lineFeed int) *Printer

NewPrinterWithKeyMasking returns a Printer configured to use printer function `fn` for output and the masking function `mk` for redacting certain keys.

func (*Printer) Print

func (p *Printer) Print(objs ...interface{})

Print attempts to pretty print the contents of each obj in a format suitable for displaying the configuration of an application on startup. It uses a default label of 'Settings' for the output.

func (*Printer) PrintWithLabel

func (p *Printer) PrintWithLabel(label string, objs ...interface{})

PrintWithLabel attempts to pretty print the contents of each obj in a format suitable for displaying the configuration of an application on startup. It takes a label argument which is a string to be printed into the title bar in the output.

type PrinterFunc

type PrinterFunc func(format string, v ...interface{})

Conforms to the signature used by fmt.Printf and log.Printf among many functions available in other packages.

Jump to

Keyboard shortcuts

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