output

package module
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: Aug 27, 2024 License: MIT Imports: 8 Imported by: 1

README

output

GoDoc Reference go.mod LICENSE

Release Code Coverage Report Go Report Card Build Status

output is a Go library that provides an easy way for command-line oriented programs to handle console writing, error writing, and logging (but agnostic as to the choice of logging framework). It also provides a simple way to verify what is written to those writers.

Installing

Execute this:

go get github.com/majohn-r/output

Basic Usage

In main, create a Bus implementation and a Logger implementation. Here is an example that uses the https://github.com/sirupsen/logrus library to implement logging:

func main() {
    // the Bus created by output.NewDefaultBus() neither knows nor cares about
    // how logging actually works - that's the purview of the Logger
    // implementation it uses.
    o := output.NewDefaultBus(ProductionLogger{})
    runProgramLogic(o, os.Args)
}

func runProgramLogic(o output.Bus, args []string) {
    // any functions called should have the Bus passed in if they, or any
    // function they call, needs to write output or do any logging
    o.ConsolePrintf("hello world: %v\n", args)
}

type ProductionLogger struct {}

// Trace outputs a trace log message
func (ProductionLogger) Trace(msg string, fields map[string]any) {
    logrus.WithFields(fields).Trace(msg)
}

// Debug outputs a debug log message
func (ProductionLogger) Debug(msg string, fields map[string]any) {
    logrus.WithFields(fields).Debug(msg)
}

// Info outputs an info log message
func (ProductionLogger) Info(msg string, fields map[string]any) {
    logrus.WithFields(fields).Info(msg)
}

// Warning outputs a warning log message
func (ProductionLogger) Warning(msg string, fields map[string]any) {
    logrus.WithFields(fields).Warning(msg)
}

// Error outputs an error log message
func (ProductionLogger) Error(msg string, fields map[string]any) {
    logrus.WithFields(fields).Error(msg)
}

// Panic outputs a panic log message and calls panic()
func (ProductionLogger) Panic(msg string, fields map[string]any) {
    logrus.WithFields(fields).Panic(msg)
}

// Fatal outputs a fatal log message and terminates the program
func (ProductionLogger) Fatal(msg string, fields map[string]any) {
    logrus.WithFields(fields).Fatal(msg)
}

In the test code, the output can be checked like this:

func Test_runProgramLogic {
    tests := map[string]struct {
        name string
        args []string
        output.WantedRecording
    }{
        "test case": {
            args: []string{"hi" "12" "true"},
            WantedRecording: output.WantedRecording{
                Console: "hello world: [hi 12 true]",
            },
        },
    }
    for name, tt := range tests {
        t.Run(name, func(t *testing.T) {
            o := NewRecorder()
            runProgramLogic(o, tt.args)
            if issues, ok := o.Verify(tt.WantedRecording); !ok {
                for _, issue := range issues {
                    t.Errorf("runProgramLogic() %s", issue)
                }
            }
        })
    }
}

Documentation

Documentation beyond this file can be obtained by running ./build.sh doc, or go here: https://pkg.go.dev/github.com/majohn-r/output

Contributing

Git
  1. Fork the repository (https://github.com/majohn-r/output/fork).
  2. Create a feature branch (git checkout -b my-new-feature).
  3. Commit your changes (git commit -am 'Add some feature').
  4. Push to the branch (git push origin my-new-feature).
  5. Create a new Pull Request.
Code Quality

These are the minimum standards:

  1. run [./build.sh preCommit] with no errors and 100% coverage on tests.
  2. update CHANGELOG.md with a brief description of the change(s).
Commit message

Reference an issue in the first line of the commit message:

(#1234) fix that nagging problem

In the example above, 1234 is the issue number this commit reference.

This library adheres to Semantic Versioning standards, so it will be very helpful if the details in the commit message make clear whether the changes require a minor or major release bump.

Documentation

Overview

Package output provides an easy way for command-line oriented programs to handle console and error writing and logging, as well as a simple way to verify what is written to those channels.

Installation

go get github.com/majohn-r/output

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Bus

type Bus interface {
	// Log logs a message and map of fields at a specified log level.
	Log(Level, string, map[string]any)
	// ConsolePrintf prints a message with arguments to the error channel
	ConsolePrintf(string, ...any)
	// ConsolePrintln prints a message to the error channel, terminated by a newline
	ConsolePrintln(string)
	// ErrorPrintf prints a message with arguments to the error channel
	ErrorPrintf(string, ...any)
	// ErrorPrintln prints a message to the error channel, terminated by a newline
	ErrorPrintln(string)
	// ConsoleWriter returns a writer for console output.
	ConsoleWriter() io.Writer
	// ErrorWriter returns a writer for error output.
	ErrorWriter() io.Writer
	// IsConsoleTTY returns whether the console writer is a TTY
	IsConsoleTTY() bool
	// IsErrorTTY returns whether the error writer is a TTY
	IsErrorTTY() bool
	// Tab returns the current tab setting (number of spaces)
	Tab() uint8
	// IncrementTab increases the current tab setting up to the max uint8 value
	IncrementTab(uint8)
	// DecrementTab decreases the current tab setting; will not go below 0
	DecrementTab(uint8)
	// BeginConsoleList initiates console listing
	BeginConsoleList(bool)
	// EndConsoleList terminates console listing
	EndConsoleList()
	// ConsoleListDecorator makes the console list decorator available
	ConsoleListDecorator() *ListDecorator
	// BeginErrorList initiates error listing
	BeginErrorList(bool)
	// EndErrorList terminates error listing
	EndErrorList()
	// ErrorListDecorator makes the error list decorator available
	ErrorListDecorator() *ListDecorator
}

Bus defines a set of functions for writing console messages and error messages, and for providing access to the console writer and the error writer, and a Logger instance; its primary use is to simplify how application code handles console, error, and logged output, and its secondary use is to make it easy to test output writing.

func NewCustomBus

func NewCustomBus(c, e io.Writer, l Logger) Bus

NewCustomBus returns an implementation of Bus that lets the caller specify the console and error writers and the Logger.

func NewDefaultBus

func NewDefaultBus(l Logger) Bus

NewDefaultBus returns an implementation of Bus that writes console messages to stdout and error messages to stderr.

func NewNilBus

func NewNilBus() Bus

NewNilBus returns an implementation of Bus that does nothing.

type Level

type Level uint32

Level is used to specify log levels for Bus.Log().

const (
	Fatal Level = iota
	Panic
	Error
	Warning
	Info
	Debug
	Trace
)

These are the different logging levels.

type ListDecorator added in v0.7.0

type ListDecorator struct {
	// contains filtered or unexported fields
}

ListDecorator contains the data needed for creating list decorations

func (*ListDecorator) Decorator added in v0.7.0

func (ld *ListDecorator) Decorator() string

Decorator generates the appropriate decoration for lists (and typically, this is the empty string)

type Logger

type Logger interface {
	Trace(msg string, fields map[string]any)
	Debug(msg string, fields map[string]any)
	Info(msg string, fields map[string]any)
	Warning(msg string, fields map[string]any)
	Error(msg string, fields map[string]any)
	Panic(msg string, fields map[string]any)
	Fatal(msg string, fields map[string]any)
}

Logger defines a set of functions for writing to a log at various log levels

type NilLogger

type NilLogger struct{}

NilLogger is a logger that does nothing at all; its intended use is for test code where the side effect of logging is of no interest whatsoever.

func (NilLogger) Debug

func (nl NilLogger) Debug(_ string, _ map[string]any)

Debug does nothing.

func (NilLogger) Error

func (nl NilLogger) Error(_ string, _ map[string]any)

Error does nothing.

func (NilLogger) Fatal

func (nl NilLogger) Fatal(_ string, _ map[string]any)

Fatal does nothing.

func (NilLogger) Info

func (nl NilLogger) Info(_ string, _ map[string]any)

Info does nothing.

func (NilLogger) Panic

func (nl NilLogger) Panic(_ string, _ map[string]any)

Panic does nothing.

func (NilLogger) Trace

func (nl NilLogger) Trace(_ string, _ map[string]any)

Trace does nothing.

func (NilLogger) Warning

func (nl NilLogger) Warning(_ string, _ map[string]any)

Warning does nothing.

type NilWriter

type NilWriter struct{}

NilWriter is a writer that does nothing at all; its intended use is for test code where the side effect of writing to the console or writing error output is of no interest whatsoever.

func (NilWriter) Write

func (nw NilWriter) Write(p []byte) (int, error)

Write does nothing except return the expected values

type Recorder

type Recorder struct {
	// contains filtered or unexported fields
}

Recorder is an implementation of Bus that simply records its inputs; it's intended for unit tests, where you can provide the code under test (that needs a Bus) with an instance of Recorder and then verify that the code produces the expected console, error, and log output.

func NewRecorder

func NewRecorder() *Recorder

NewRecorder returns a recording implementation of Bus.

func (*Recorder) BeginConsoleList added in v0.7.0

func (r *Recorder) BeginConsoleList(numeric bool)

BeginConsoleList initiates console listing

func (*Recorder) BeginErrorList added in v0.7.0

func (r *Recorder) BeginErrorList(numeric bool)

BeginErrorList initiates error listing

func (*Recorder) ConsoleListDecorator added in v0.7.0

func (r *Recorder) ConsoleListDecorator() *ListDecorator

ConsoleListDecorator makes the console list decorator available

func (*Recorder) ConsoleOutput

func (r *Recorder) ConsoleOutput() string

ConsoleOutput returns the data written as console output.

func (*Recorder) ConsolePrintf added in v0.8.0

func (r *Recorder) ConsolePrintf(format string, args ...any)

ConsolePrintf prints a message with arguments to the error channel

func (*Recorder) ConsolePrintln added in v0.8.0

func (r *Recorder) ConsolePrintln(msg string)

ConsolePrintln prints a message to the error channel, terminated by a newline

func (*Recorder) ConsoleWriter

func (r *Recorder) ConsoleWriter() io.Writer

ConsoleWriter returns the internal console writer.

func (*Recorder) DecrementTab added in v0.5.0

func (r *Recorder) DecrementTab(t uint8)

DecrementTab decrements the tab setting by the specified number of spaces

func (*Recorder) EndConsoleList added in v0.7.0

func (r *Recorder) EndConsoleList()

EndConsoleList terminates console listing

func (*Recorder) EndErrorList added in v0.7.0

func (r *Recorder) EndErrorList()

EndErrorList terminates error listing

func (*Recorder) ErrorListDecorator added in v0.7.0

func (r *Recorder) ErrorListDecorator() *ListDecorator

ErrorListDecorator makes the error list decorator available

func (*Recorder) ErrorOutput

func (r *Recorder) ErrorOutput() string

ErrorOutput returns the data written as error output.

func (*Recorder) ErrorPrintf added in v0.8.0

func (r *Recorder) ErrorPrintf(format string, args ...any)

ErrorPrintf prints a message with arguments to the error channel

func (*Recorder) ErrorPrintln added in v0.8.0

func (r *Recorder) ErrorPrintln(msg string)

ErrorPrintln prints a message to the error channel, terminated by a newline

func (*Recorder) ErrorWriter

func (r *Recorder) ErrorWriter() io.Writer

ErrorWriter returns the internal error writer.

func (*Recorder) IncrementTab added in v0.5.0

func (r *Recorder) IncrementTab(t uint8)

IncrementTab increments the tab setting by the specified number of spaces

func (*Recorder) IsConsoleTTY added in v0.3.0

func (r *Recorder) IsConsoleTTY() bool

IsConsoleTTY returns whether the console writer is a TTY

func (*Recorder) IsErrorTTY added in v0.3.0

func (r *Recorder) IsErrorTTY() bool

IsErrorTTY returns whether the error writer is a TTY

func (*Recorder) Log

func (r *Recorder) Log(l Level, msg string, fields map[string]any)

Log records a message and map of fields at a specified log level.

func (*Recorder) LogOutput

func (r *Recorder) LogOutput() string

LogOutput returns the data written to a log.

func (*Recorder) Report added in v0.4.0

func (r *Recorder) Report(t TestingReporter, header string, w WantedRecording)

Report handles the common use case for using a Recorder: detecting whether any differences were recorded, and reporting them if there were any differences.

func (*Recorder) Tab added in v0.5.0

func (r *Recorder) Tab() uint8

Tab returns the current tab setting

func (*Recorder) Verify

func (r *Recorder) Verify(w WantedRecording) (differences []string, verified bool)

Verify verifies the recorded output against the expected output and returns any differences found.

type RecordingLogger

type RecordingLogger struct {
	// contains filtered or unexported fields
}

RecordingLogger is a simple logger intended for use in unit tests; it records the output given to it.

Caveats:

Your production log may not actually do anything with some calls into it - for instance, many logging frameworks allow you to limit the severity of what is logged, e.g., only warnings or worse; RecordingLogger will record every call made into it.

The output recorded cannot be guaranteed to match exactly what your logging code records - but it will include the log level, the message, and all field-value pairs.

The RecordingLogger will probably behave differently to a logging mechanism that supports panic and fatal logs, in that a production logger will probably call panic in processing a panic log, and will probably exit the program on a fatal log. RecordingLogger does neither of those.

func NewRecordingLogger

func NewRecordingLogger() *RecordingLogger

NewRecordingLogger returns a recording implementation of Logger.

func (*RecordingLogger) Debug

func (rl *RecordingLogger) Debug(msg string, fields map[string]any)

Debug records a debug log message.

func (*RecordingLogger) Error

func (rl *RecordingLogger) Error(msg string, fields map[string]any)

Error records an error log message.

func (*RecordingLogger) Fatal

func (rl *RecordingLogger) Fatal(msg string, fields map[string]any)

Fatal records a fatal log message and does not terminate the program.

func (*RecordingLogger) Info

func (rl *RecordingLogger) Info(msg string, fields map[string]any)

Info records an info log message.

func (*RecordingLogger) Panic

func (rl *RecordingLogger) Panic(msg string, fields map[string]any)

Panic records a panic log message and does not call panic().

func (*RecordingLogger) String added in v0.5.3

func (rl *RecordingLogger) String() string

func (*RecordingLogger) Trace

func (rl *RecordingLogger) Trace(msg string, fields map[string]any)

Trace records a trace log message.

func (*RecordingLogger) Warning

func (rl *RecordingLogger) Warning(msg string, fields map[string]any)

Warning records a warning log message.

type TestingReporter added in v0.4.0

type TestingReporter interface {
	Errorf(string, ...any)
}

TestingReporter is an interface that requires the one *testing.T function that we care about: Errorf

type WantedRecording

type WantedRecording struct {
	Console string
	Error   string
	Log     string
}

WantedRecording is intended to be used in unit tests as part of the test structure; it allows the test writer to capture what the test wants the console, error, and log output to contain.

Directories

Path Synopsis
build module

Jump to

Keyboard shortcuts

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