svengreb / nib

A “stylish“, log-level based line printer for human-facing Go CLI applications.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

A “stylish“, log-level based line printer for human-facing Go CLI applications.

Features

The pencil package provides pencil.Pencil that implements nib.Nib with

  • …custom prefixes
  • …configurable verbosity level icons

The inkpen package composes pencil.Pencil and additionally provides…

  • …colored output
  • …automatic TTY and cross-platform terminal color support detection

Please note that this package has mainly been created for my personal use in mind to avoid copying source code between my CLI based projects that require a line printer for human-facing messages. The default configurations might not fit your needs, but the pencil.Pencil and inkpen.Inkpen implementations of the nib.Nib interface have been designed so that they can be flexibly adapted to different use cases and environments.

API

The nib package provides the currently latest API v0. The nib.Nib interface consists of six functions that allow to print a formatted message with different verbosity levels:

  • Compile(v Verbosity, format string, args ...interface{}) string — compiles a message for the verbosity level using the given format and arguments..
  • Debugf(format string, args ...interface{}) — writes a message with debug verbosity level for the given format and arguments.
  • Errorf(format string, args ...interface{}) — writes a message with error verbosity level for the given format and arguments.
  • Fatalf(format string, args ...interface{}) — writes a message with fatal verbosity level for the given format and arguments.
  • Infof(format string, args ...interface{}) — writes a message with info verbosity level for the given format and arguments.
  • Successf(format string, args ...interface{}) — writes a message with success verbosity level for the given format and arguments.
  • Warnf(format string, args ...interface{}) — writes a message with warn verbosity level for the given format and arguments.
  • Writer() io.Writer — returns the underlying io.Writer.

The pencil package implements this interface including features like custom prefixes and verbosity level icons. The inkpen package composes pencil.Pencil and additionally comes with additional features like colored output including automatic TTY and cross-platform terminal color support detection.

For more details about the API, available packages and types please see the GoDoc reference documentation.

Usage

In addition to the possibility of implementing the nib.Nib interface yourself the pencil.Pencil type can be used:

package main

import (
	"github.com/svengreb/nib/inkpen"
	"github.com/svengreb/nib/pencil"
)

func main() {
	// Create a new pencil
	pen := pencil.New()
	// ... or inkpen with default configurations where nib.InfoVerbosity is the default verbosity level.
	ink := inkpen.New()

	// Print a message with "info" level.
	fruit := "coconut"
	pen.Infof("My favorite fruits are %ss", fruit)
	ink.Infof("My favorite fruits are %ss", fruit)
}

Configuration

Both types pencil.Pencil and inkpen.Inkpen were designed so that they can be flexibly adapted to different use cases and environments.

To customize a pencil.Pencil the New function accepts one or more pencil.Option. A inkpen.Inkpen can also be customized with one or more pencil.Option by passing them to the inkpen.WithPencilOptions(pencilOpts ...pencil.Option) Option function via the New function.

Verbosity

The default verbosity level of pencil.Pencil is nib.InfoVerbosity that prints messages with “info“ scope. You can adjust it to any other level through the pencil.WithVerbosity(v nib.Verbosity) Option function:

package main

import (
	"github.com/svengreb/nib"
	"github.com/svengreb/nib/inkpen"
	"github.com/svengreb/nib/pencil"
)

func main() {
	// Create a new pencil...
	pen := pencil.New(pencil.WithVerbosity(nib.DebugVerbosity))
	// ...or inkpen with default configurations and set the verbosity level from "info" to "debug" scope.
	ink := inkpen.New(inkpen.WithPencilOptions(pencil.WithVerbosity(nib.DebugVerbosity)))

	pen.Debugf("Raspberries are my second favorite fruit")
	ink.Debugf("Raspberries are my second favorite fruit")
}

Note that nib.Verbosity implements encoding.TextUnmarshaler to allow to unmarshal a textual representation of the level or get the nib.Verbosity based on the level scope name:

package main

import (
	"fmt"
	"os"

	"github.com/svengreb/nib"
)

func main() {
	// Get the textual representation of the verbosity level.
	fmt.Println(nib.InfoVerbosity.String()) // "info"

	// Get the nib.Verbosity from the given verbosity level scope name.
	// The function returns an non-nil error when the given name is not valid.
	v, err := nib.ParseVerbosity("error")
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	fmt.Printf("Verbosity: %s\n", v.String()) // "error"
}

To check whether the verbosity level is enabled, the *pencil.Pencil.Enabled(v nib.Verbosity) bool or *inkpen.Inkpen.Enabled(v nib.Verbosity) bool methods can be used:

package main

import (
	"fmt"

	"github.com/svengreb/nib"
	"github.com/svengreb/nib/inkpen"
	"github.com/svengreb/nib/pencil"
)

func main() {
	// Create a new pencil
	pen := pencil.New()
	// ... or inkpen...
	ink := inkpen.New()

	// ...and check whether the verbosity level is enabled.
	fmt.Println(pen.Enabled(nib.DebugVerbosity)) // false
	fmt.Println(pen.Enabled(nib.ErrorVerbosity)) // true
	fmt.Println(ink.Enabled(nib.FatalVerbosity)) // true
}

Icons

The default icons are Unicode characters which are supported by almost all terminals nowadays, but there might be cases where you want to use simple ASCII characters instead. You can change any verbosity level icon through the pencil.WithIcons(icons map[nib.Verbosity]rune) Option function:

package main

import (
	"github.com/svengreb/nib"
	"github.com/svengreb/nib/inkpen"
	"github.com/svengreb/nib/pencil"
)

func main() {
	// Create a new pencil...
	pen := pencil.New(pencil.WithIcons(map[nib.Verbosity]rune{nib.InfoVerbosity: '>'}))
	// ...or inkpen, that uses Unicode characters as verbosity level icons by default, and change the icon for the "info"
	// verbosity level scope.
	ink := inkpen.New(inkpen.WithPencilOptions(
		pencil.WithIcons(map[nib.Verbosity]rune{nib.InfoVerbosity: '>'}),
	))

	pen.Infof("Cashew nuts can be combined very well with yogurt")
	ink.Infof("Cashew nuts can be combined very well with yogurt")
}

inkpen.Inkpen allows to customize the color of icons through the inkpen.WithIconColorFuncs(iconColorFuncs map[nib.Verbosity]IconColorFunc) Option function:

package main

import (
	"github.com/fatih/color"

	"github.com/svengreb/nib"
	"github.com/svengreb/nib/inkpen"
)

func main() {
	// Create a new inkpen and change the color function for the "info" verbosity level scope to use green instead of
	// blue as foreground color.
	ink := inkpen.New(inkpen.WithIconColorFuncs(map[nib.Verbosity]inkpen.IconColorFunc{
		nib.InfoVerbosity: color.New(color.FgGreen).Sprintf,
	}))

	ink.Infof("Almonds taste very good with vanilla")
}

To disable verbosity level icons to be printed at all use the pencil.UseIcons(useIcons bool) Option function:

package main

import (
	"github.com/svengreb/nib/inkpen"
	"github.com/svengreb/nib/pencil"
)

func main() {
	// Create a new pencil...
	pen := pencil.New(pencil.UseIcons(false))
	// ...or inkpen and disable all verbosity level icons.
	ink := inkpen.New(inkpen.WithPencilOptions(pencil.UseIcons(false)))

	pen.Errorf("Cane sugar is not necessary for a delicious yogurt")
	ink.Errorf("Cane sugar is not necessary for a delicious yogurt")
}

Note that the provided map of icons gets merged with the default map in order to ensure there are no missing icons. See the pencil package to get an overview of the icon characters that are used by default.

To check if verbosity level icons are enabled, the *pencil.Pencil.IconsEnabled() bool or *inkpen.Inkpen.IconsEnabled() bool methods can be used:

package main

import (
	"fmt"

	"github.com/svengreb/nib/pencil"
)

func main() {
	// Create a new pencil...
	pen := pencil.New()
	// ...and check whether verbosity level icons are enabled.
	fmt.Println(pen.IconsEnabled()) // true
}

Prefixes

By default only the verbosity level icons are printed as prefix before the given message format and arguments. You can add any amount of custom prefixes through the pencil.WithPrefixes(prefixes ...string) Option function:

package main

import (
	"github.com/svengreb/nib/inkpen"
	"github.com/svengreb/nib/pencil"
)

func main() {
	// Create a new pencil...
	pen := pencil.New(pencil.WithPrefixes("[fruit mixer]"))
	// ... or inkpen and add a custom prefix that gets placed before the actual message.
	ink := inkpen.New(inkpen.WithPencilOptions(pencil.WithPrefixes("[fruit mixer]")))

	pen.Infof("Strawberries are also a very tasty ingredient for low-fat quark")
	ink.Infof("Strawberries are also a very tasty ingredient for low-fat quark")
}

Writer

By default pencil.Pencil uses os.Stderr while inkpen.Inkpen uses color.Output which in turn is a exported variable that makes use of github.com/mattn/go-colorable, a package for colored TTY output on multiple platforms. You can use any io.Writer through the pencil.WithWriter(writer io.Writer) Option function:

package main

import (
	"os"

	"github.com/svengreb/nib/inkpen"
	"github.com/svengreb/nib/pencil"
)

func main() {
	// Create a new pencil...
	pen := pencil.New(pencil.WithWriter(os.Stderr))
	// ... or inkpen and change the output writer to use the OS error stream.
	ink := inkpen.New(inkpen.WithPencilOptions(pencil.WithWriter(os.Stderr)))

	pen.Errorf("Blueberries mixed with raspberries and yoghurt are a delicious dream")
	ink.Errorf("Blueberries mixed with raspberries and yoghurt are a delicious dream")
}

Writer

Both types pencil.Pencil and inkpen.Inkpen use recommended io.Writer by default to provide optimal compatibility for their specific features like colored output. To allow to either reuse the default or configured io.Writer the Writer() io.Writer method of the nib.Nib API interface can be used:

package main

import (
	"os"

	"github.com/svengreb/nib/inkpen"
	"github.com/svengreb/nib/pencil"
)

func main() {
	// Create a new pencil...
	pen := pencil.New(pencil.WithWriter(os.Stderr))
	// ... or inkpen.
	ink := inkpen.New()

	_, _ = fmt.Fprintln(pen.Writer(), "Brazil nuts are also a delicious and healthy snack between meals")
	_, _ = fmt.Fprintln(ink.Writer(), "Brazil nuts are also a delicious and healthy snack between meals")
}

Contributing

nib is an open source project and contributions are always welcome!

There are many ways to contribute, from writing- and improving documentation and tutorials, reporting bugs, submitting enhancement suggestions that can be added to nib by submitting pull requests.

Please take a moment to read the contributing guide to learn about the development process, the styleguides to which this project adheres as well as the branch organization and versioning model.

The guide also includes information about minimal, complete, and verifiable examples and other ways to contribute to the project like improving existing issues and giving feedback on issues and pull requests.

Copyright © 2019-present Sven Greb

About

A “stylish“, log-level based line printer for human-facing Go CLI applications.

License:MIT License


Languages

Language:Go 96.8%Language:JavaScript 2.4%Language:Shell 0.8%