gologs

package module
v0.16.1 Latest Latest
Warning

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

Go to latest
Published: Apr 21, 2023 License: MIT Imports: 9 Imported by: 3

README

gologs

Why

Why yet another logging library?

  1. Create a log, split it into branches, give each branch different log prefixes, and set each branch to independent log level.

Goals

  1. This should work within the Go ecosystem. Specifically, it should emit logs to any io.Writer.

  2. This should be flexible enough to provide for use cases not originally envisioned, yet be easy enough to use to facilitate adoption. I should want to reach for this library for all my logging needs, for both command line and long running services.

  3. This should be lightweight. This should not spin up any go routines. This should only allocate when creating a new logger or a new log branch, or when the user specifically requires it by invoking the Format method. Events that do not get logged should not be formatted. This should not ask the OS for the system time if log format specification does not require it.

  4. This should be correct. It should never invoke Write more than once per logged event.

GoDoc

Compliments

A while ago I significantly altered the API of this library based on the amazing zerolog library. I hope the authors of that library have heard the expression that imitation is the most sincere form of flattery.

Usage Example

package main

import (
    "flag"
    "fmt"
    "os"

    "github.com/karrick/gologs"
)

func main() {
    optDebug := flag.Bool("debug", false, "Print debug output to stderr")
    optVerbose := flag.Bool("verbose", false, "Print verbose output to stderr")
    optQuiet := flag.Bool("quiet", false, "Print warning and error output to stderr")
    flag.Parse()

    // Initialize the global log variable, which will be used very much like the
    // log standard library would be used.
    log := gologs.New(os.Stderr)

    // Configure log level according to command line flags.
    if *optDebug {
        log.SetDebug()
    } else if *optVerbose {
        log.SetVerbose()
    } else if *optQuiet {
        log.SetError()
    } else {
        log.SetInfo()
    }

    // For sake of example, invoke printSize with a child logger that includes
    // the function name in the JSON properties of the log message.
    clog := log.With().String("function", "printSize").Logger()

    for _, arg := range flag.Args() {
        // NOTE: Sends event to parent logger.
        log.Verbose().String("arg", arg).Msg("")

        // NOTE: Sends events to child logger.
        if err := printSize(clog, arg); err != nil {
            log.Warning().Err(err).Msg("")
        }
    }
}

func printSize(log *gologs.Logger, pathname string) error {
    log.Debug().String("pathname", pathname).Msg("stat")
    stat, err := os.Stat(pathname)
    if err != nil {
        return err
    }

    size := stat.Size()
    log.Debug().String("pathname", pathname).Int64("size", size).Msg("")

    if (stat.Mode() & os.ModeType) == 0 {
        fmt.Printf("%s is %d bytes\n", pathname, size)
    }

    return nil
}

Description

Creating a Logger Instance

Everything written by this logger is formatted as a JSON event, given a trailing newline, and written to the underlying io.Writer. That io.Writer might be os.Stderr, or it might be a log rolling library, which in turn, is writting to a set of managed log files. The library provides a few time formatting functions, but the time is only included when the Logger is updated to either one of the provided time formatting functions or a user specified time formatter.

    log1 := gologs.New(os.Stderr)
    log1.Info().Msg("started program")
    // Output:
    // {"level":"info","message":"starting program"}

    log2 := gologs.New(os.Stderr).SetTimeFormatter(gologs.TimeUnix)
    log2.Info().Msg("started program")
    // Output:
    // {"time":1643776764,"level":"info","message":"starting program"}

    log3 := gologs.New(os.Stderr).SetTimeFormatter(gologs.TimeUnixNano)
    log3.Info().Msg("started program")
    // Output:
    // {"time":1643776794592630092,"level":"info","message":"starting program"}

    log4 := gologs.New(os.Stderr).SetTimeFormatter(gologs.TimeFormat(time.RFC3339))
    log4.Info().Msg("started program")
    // Output:
    // {"time":"2022-08-06T15:14:04-04:00","level":"info","message":"starting program"}

    log5 := gologs.New(os.Stderr).SetTimeFormatter(gologs.TimeFormat(time.Kitchen))
    log5.Info().Msg("started program")
    // Output:
    // {"time":"3:14PM","level":"info","message":"starting program"}
Log Levels

Like most logging libraries, the basic logger provides methods to change its log level, controling which events get logged and which get ignored.

    log.SetVerbose()
    log.Info().Msg("this event gets logged")
    log.Verbose().Msg("and so does this event")
    log.Debug().Msg("but this event gets ignored")

    log.SetLevel(gologs.Debug)
    log.Debug().Msg("this event does get logged")

When a logger is in Error mode, only Error events are logged. When a logger is in Warning mode, only Error and Warning events are logged. When a logger is in Info mode, only Error, Warning, and Info events are logged. When a logger is in Verbose mode, only Error, Warning, Info, and Verbose events are logged. When a logger is in Debug mode, all events are logged.

Note the logger mode for a newly created Logger is Warning, which I feel is in keeping with the UNIX philosophy to Avoid unnecessary output. Simple command line programs will not need to set the log level to prevent spewing too many events. While service application developers will need to spend a few minutes to build in the ability to configure the log level based on their service needs.

Perhaps more idiomatic of a command line program log configuration:

    if *optDebug {
        log.SetDebug()
    } else if *optVerbose {
        log.SetVerbose()
    } else if *optQuiet {
        log.SetError()
    } else {
        log.SetInfo()
    }
A Tree of Logs with Multiple Branches

In managing several real world services, I discovered the need for finer granularity in managing which events are logged in different parts of the same running program. Sometimes all events in one particular module of the service should be logged with great detail, while a different part of the program is deemed functional and the loggging of developer events would saturate the logs.

This library allows this workflow by allowing a developer to create a tree of logs with multiple branches, and each branch can have an independently controlled log level. These log branches are lightweight, require no go routines to facilitate, and can even be ephemeral, and demonstrated later in the Tracer Logging section. Creating logger branches do allocate by copying the branch byte slice from the parent to the child branch.

Base of the Tree

To be able to independently control log levels of different parts of the same program at runtime, this library provides for the creation of what I like to call a tree of logs. At the base of the tree, events are written to an underlying io.Writer. This allows a developer to create a log and have it write to standard error, standard output, a file handle, a log rolling library which writes to a file, or any other structure that implements the io.Writer interface.

Creating New Branches for the Log Tree

Different logging configurations can be effected by creating a logging tree, and while the tree may be arbitrarily complex, a simple tree is likely more developer friendly than a complex one. For instance, I have adopted the pattern of creating a very small tree, with a base logger for the entire application, and a logger branch for each major module of the program. Each of those branches can have a different log level, each of which can be controlled at runtime using various means, always by invoking one of its log level control methods. Additionally each branch can have a particular string prefix provided that will prefix the logged events.

This allows each branch to have an independently controlled log level, and the program can set one logger to run at Debug mode, while the other branches run at different levels. These log levels are also safe to asynchronously modify while other threads are actively logging events to them.

    // Foo is a module of the program with its own logger.
    type Foo struct {
        log *gologs.Logger
        // ...
    }

    // Bar is a module of the program with its own logger.
    type Bar struct {
        log *gologs.Logger
        // ...
    }

    func example1() {
        // log defined as in previous examples...
        foo := &Foo{
            log: log.With().String("module","FOO").Logger(),
        }
        go foo.run()

        bar := &Bar{
            log: log.With().String("module","BAR").Logger(),
        }
        go bar.run()
    }

In the above example both Foo and Bar are provided their own individual logger to use, and both Foo and Bar can independently control its own log level. It is important that they use that logger to log all of their events during their lifetime, in order to be effective.

Tracer Logging

I am sure I'm not the only person who wanted to figure out why a particular request or action was not working properly on a running service, decided to activate DEBUG log levels to watch the single request percolate through the service, to be reminded that the service is actually serving tens of thousands of requests per second, and now the additional slowdown that accompanies logging each and every single log event in the entire program not only slows it down, but makes it impossible to see the action or request in the maelstrom of log messages scrolling by the terminal.

For instance, let's say an administrator or developer wants to send a request through their running system, logging all events related to that request, regardless of the log level, but not necessarily see events for other requests.

For this example, remember that each module has a Logger it uses whenever logging any event. Let's say the Foo module receives requests to process. The Foo can create highly ephemeral Tracer Loggers to be assigned to the request instance itself, and provided that the request methods log using the provided logger, then those events will bypass any filters in place between where the log event was created to the base of the logging tree, and get written to the underlying io.Writer.

    type Request struct {
        log   *gologs.Logger
        query string
        // ...
    }

    func (f *Foo) NewRequest(query string) (*Request, error) {
        r := &Request{
            log:   f.log.With().String("request", query).Logger(),
            query: query,
        }
        if strings.HasSuffix(key, "*") {
            r.log.SetTracing(true)
        }
        // ...
    }

    func (r *Request) Process() error {
        r.log.Debug().Msg("beginning processing of request")
        // ...
    }

It is important to remember that events sent to a Logger configured for tracing will bypass all log level filters. So log, Foo, and Bar all might be set for Warning level, but you want to follow a particular request through the system, without changing the log levels, also causing the system to log every other request. Tracer logic is not meant to be added and removed while debugging a program, but rather left in place, run in production, but not used, unless some special developer or administrator requested token marks a particular event as one for which all events should be logged.

Here's an example of what Tracer Loggers are trying to eliminate, assuming a hypothetical Logging.Trace method existed:

    // Counter example: desired behavior sans tracer logic. Each log line
    // becomes a conditional, leading to code bloat.
    func (r *Request) Handler() {
        // It is inconvenient to branch log events each place you want to
        // emit a log event.
        if r.isSpecial {
            r.Log.Trace().Msg("handling request")
        } else {
            r.Log.Debug().Msg("handling request: %v", r)
        }

        // Do some work, then need to log more:
        if r.isSpecial {
            r.Log.Trace().Int("request-cycles", r.Cycles).Msg("")
        } else {
            r.Log.Debug().Int("request-cycles", r.Cycles).Msg("")
        }
    }

I propose something better, where the developer does not need to include conditional statements to branch based on whether the log should receive Tracer status or Verbose status for each log event. Yet, when Tracer status, still get written to the log when something requires it.

    func NewRequest(log *gologs.Logger, key string) (*Request, error) {
        log = log.With().
            String("key", key).
            Tracing(strings.HasSuffix(key, "*")).
            Logger()
        r := &R{
            log: log,
            Key: key,
        }
        return r, nil
    }

    func (r *Request) Handler() {
        r.Log.Debug().Msg("handling request")

        // Do some work, then need to log more:

        r.Log.Debug().Int("request-cycles", r.Cycles).Msg("")
    }

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func TimeUnix added in v0.7.0

func TimeUnix(buf []byte) []byte

TimeUnix appends the current Unix second time to buf as a JSON property name and value.

func TimeUnixMicro added in v0.7.0

func TimeUnixMicro(buf []byte) []byte

TimeUnixMicro appends the current Unix microsecond time to buf as a JSON property name and value.

func TimeUnixMilli added in v0.7.0

func TimeUnixMilli(buf []byte) []byte

TimeUnixMilli appends the current Unix millisecond time to buf as a JSON property name and value.

func TimeUnixNano added in v0.7.0

func TimeUnixNano(buf []byte) []byte

TimeUnixNano appends the current Unix nanosecond time to buf as a JSON property name and value.

Types

type Event added in v0.7.0

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

Event is an in progress log event being formatted before it is written upon calling its Msg() method. Callers never need to create an Event specifically, but rather receive an Event from calling Debug(), Verbose(), Info(), Warning(), or Error() methods of Logger instance.

func (*Event) Bool added in v0.7.0

func (event *Event) Bool(name string, value bool) *Event

Bool encodes a boolean property value to the Event using the specified name.

func (*Event) Err added in v0.9.0

func (event *Event) Err(err error) *Event

Err encodes a possibly nil error property value to the Event. When err is nil, the error value is represented as a JSON null.

func (*Event) Float added in v0.7.0

func (event *Event) Float(name string, value float64) *Event

Float encodes a float64 property value to the Event using the specified name.

func (*Event) Format added in v0.7.0

func (event *Event) Format(name, f string, args ...interface{}) *Event

Format encodes a string property value--formatting it with the provided arguments--to the Event using the specified name. This function will invoke fmt.Sprintf() function to format the formatting string with the provided arguments, allocating memory to do so. If no formatting is required, invoking Event.String(string, string) will be faster.

func (*Event) Int added in v0.7.0

func (event *Event) Int(name string, value int) *Event

Int encodes a int property value to the Event using the specified name.

func (*Event) Int64 added in v0.8.0

func (event *Event) Int64(name string, value int64) *Event

Int64 encodes a int64 property value to the Event using the specified name.

func (*Event) Msg added in v0.7.0

func (event *Event) Msg(s string) error

Msg adds the specified message to the Event for the message property, and writes the Event to Logger's io.Writer. The caller may provide an empty string, which will elide inclusion of the message property in the written log event. This method must be invoked to complete every Event. This method returns any error from attempting to write to the Logger's io.Writer.

func (*Event) String added in v0.7.0

func (event *Event) String(name, value string) *Event

String encodes a string property value to the Event using the specified name.

func (*Event) Stringer added in v0.16.0

func (event *Event) Stringer(name string, stringer interface{ String() string }) *Event

Stringer encodes the return value of a Stringer to the Event as a property value using the specified name. This method will result in allocation if and only if the Event will be logged.

To reduce program allocations, prefer this:

logger.Debug().Stringer("address", address).Msg("listening")

Rather than this:

logger.Debug().String("address", address.String()).Msg("listening")

func (*Event) Uint added in v0.10.0

func (event *Event) Uint(name string, value uint) *Event

Uint encodes a uint property value to the Event using the specified name.

func (*Event) Uint64 added in v0.10.0

func (event *Event) Uint64(name string, value uint64) *Event

Uint64 encodes a uint64 property value to the Event using the specified name.

type Intermediate added in v0.12.0

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

Intermediate is an intermediate Logger that is not capable of logging events, but used while creating a new Logger that always includes one or more properties in each logged event.

Logger.With() -> *Intermediate -> Bool() -> *Intermediate -> ... -> Logger() -> *Logger

func (*Intermediate) Bool added in v0.12.0

func (il *Intermediate) Bool(name string, value bool) *Intermediate

Bool returns a new Intermediate Logger that has the name property set to the JSON encoded bool value.

func (*Intermediate) Float added in v0.12.0

func (il *Intermediate) Float(name string, value float64) *Intermediate

Float returns a new Intermediate Logger that has the name property set to the JSON encoded float64 value.

func (*Intermediate) Format added in v0.12.0

func (il *Intermediate) Format(name, f string, args ...interface{}) *Intermediate

Format returns a new Intermediate Logger that has the name property set to the JSON encoded string value derived from the formatted string and its arguments. This function will invoke fmt.Sprintf() function to format the formatting string with the provided arguments, allocating memory to do so. If no formatting is required, invoking Intermediate.String(string, string) will be faster.

func (*Intermediate) Int added in v0.12.0

func (il *Intermediate) Int(name string, value int) *Intermediate

Int returns a new Intermediate Logger that has the name property set to the JSON encoded int value.

func (*Intermediate) Int64 added in v0.12.0

func (il *Intermediate) Int64(name string, value int64) *Intermediate

Int64 returns a new Intermediate Logger that has the name property set to the JSON encoded int64 value.

func (*Intermediate) Logger added in v0.12.0

func (il *Intermediate) Logger() *Logger

Logger converts the Intermediate Logger into a new Logger instance that includes the fields it was configured to contain.

func (*Intermediate) String added in v0.12.0

func (il *Intermediate) String(name, value string) *Intermediate

String returns a new Intermediate Logger that has the name property set to the JSON encoded string value.

func (*Intermediate) Tracing added in v0.13.0

func (il *Intermediate) Tracing(value bool) *Intermediate

Tracing returns a new Intermediate Logger that logs all events, regardless of the Logger level at the time an log event is created.

func (*Intermediate) Uint added in v0.12.0

func (il *Intermediate) Uint(name string, value uint) *Intermediate

Uint returns a new Intermediate Logger that has the name property set to the JSON encoded uint value.

func (*Intermediate) Uint64 added in v0.12.0

func (il *Intermediate) Uint64(name string, value uint64) *Intermediate

Uint64 returns a new Intermediate Logger that has the name property set to the JSON encoded uint64 value.

type Level

type Level uint32

Level type defines one of several possible log levels.

const (
	// Debug is for events that might help a person understand the cause of a
	// bug in a program.
	Debug Level = iota

	// Verbose is for events that might help a person understand the state of a
	// program.
	Verbose

	// Info is for events that annotate high level status of a program.
	Info

	// Warning is for events that indicate a possible problem with the
	// program. Warning events should be investigated and corrected soon.
	Warning

	// Error is for events that indicate a definite problem that might prevent
	// normal program execution. Error events should be corrected immediately.
	Error
)

func (Level) String

func (l Level) String() string

type Logger

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

Logger is a near zero allocation logging mechanism. Each log event is written using a single invocation of the Write method for the underlying io.Writer.

Example
package main

import (
	"fmt"
	"os"
	"strings"
)

func main() {
	// A Logger needs a io.Writer to which it will write all log messages.
	log := New(os.Stdout)

	// By default, a Logger has a log level of Warning, which is closer to the
	// UNIX philosophy of avoiding unnecessary output. This example is
	// intended to be more verbose for demonstrative purposes.
	log.SetVerbose()
	// log.SetDebug()

	log.Verbose().Msg("initializing program")

	log.Log().String("foo", "bar").Msg("")

	// When creating structure instances, consider sending the log instance to
	// the structure's constructor so it can prefix its log messages
	// accordingly. This is especially useful when the instantiated structure
	// might spin off goroutines to perform tasks.
	s := NewServer(log)

	s.run([]string{"one=1", "@two=2", "three=3", "@four=4"})

	// Create an io.Writer that conveys all writes it receives to the
	// underlying io.Writer as individual log events. NOTE: This logger will
	// not emit the first event, but inside the loop below the Writer's log
	// level is changed, such that follow on events are emitted.
	w := log.SetWarning().NewWriter(Info)

	for _, line := range []string{"line 1\n", "line 2\n", "line 3\n"} {
		n, err := w.Write([]byte(line))
		if got, want := n, len(line); got != want {
			log.Warning().Int("got", got).Int("want", want).Msg("bytes written mismatch")
		}
		if err != nil {
			log.Warning().Err(err).Msg("error during write")
		}
		w.SetInfo()
	}

}

type Server struct {
	log *Logger // log is used for all log output by Server
	// plus any other fields...
}

func NewServer(log *Logger) *Server {
	// Sometimes it is helpful to know when enter and leave a function.
	log.Verbose().Msg("Enter NewServer()")

	// However, when deferring a log entry, ensure to call it from a composed
	// function.
	defer func() { log.Debug().Msg("Leave NewServer()") }()

	// When creating a new runtime subsystem, create a new branch of the
	// provided log to be used by that component. Each branch of the log may
	// have potentially different log levels.
	log = log.With().String("structure", "Server").Logger()

	return &Server{log: log}
}

func (a *Server) run(args []string) {
	a.log.Verbose().Msg("Enter Server.run()")

	// Create a local logger instance for this method.
	log := a.log.With().String("method", "run").Logger()
	log.Verbose().Msg("starting loop")

	for _, arg := range args {
		if err := a.handleRequest(log, arg); err != nil {
			log.Warning().Err(err).Msg("cannot handle request")
		}
	}
}

func (a *Server) handleRequest(log *Logger, raw string) error {
	log.Debug().Msg("Enter Server.handleRequest()")
	defer func() { log.Debug().Msg("Leave Server.handleRequest()") }()

	request, err := NewRequest(log, raw)
	if err != nil {
		return fmt.Errorf("cannot create request: %w", err)
	}
	if err = request.Handle(); err != nil {
		return fmt.Errorf("cannot process request: %w", err)
	}
	return nil
}

// Request is a demonstration structure that has its own logger, which it uses
// to log all events relating to handling this request.
type Request struct {
	log   *Logger // Log is the logger for this particular request.
	Query string  // Query is the request payload.
}

func NewRequest(log *Logger, query string) (*Request, error) {
	fields := strings.Split(query, "=")
	if len(fields) != 2 {
		return nil, fmt.Errorf("cannot parse query: %q", query)
	}

	log = log.With().
		String("request", query).
		String("left", fields[0]).
		String("right", fields[1]).
		Logger()

	log.Debug().Msg("new request")
	return &Request{log: log, Query: query}, nil
}

func (r *Request) Handle() error {
	// Anywhere in the call flow for the request, if it wants to log
	// something, it should log to the Request's logger.
	log := r.log

	log.Debug().Msg("handling request")
	return nil
}
Output:

{"level":"verbose","message":"initializing program"}
{"foo":"bar"}
{"level":"verbose","message":"Enter NewServer()"}
{"level":"verbose","structure":"Server","message":"Enter Server.run()"}
{"level":"verbose","structure":"Server","method":"run","message":"starting loop"}
{"level":"info","message":"line 2\n"}
{"level":"info","message":"line 3\n"}

func New added in v0.1.0

func New(w io.Writer) *Logger

New returns a new Logger that writes log events to w.

By default, a Logger has a log level of Warning, which is closer to the UNIX philosophy of avoiding unnecessary output.

log := gologs.New(os.Stdout).SetTimeFormatter(gologs.TimeUnix)

func (*Logger) Debug

func (log *Logger) Debug() *Event

Debug returns an Event to be formatted and sent to the Logger's underlying io.Writer when the Logger's level is Debug. If the Logger's level is above Debug, this method returns without blocking.

func (*Logger) Error

func (log *Logger) Error() *Event

Error returns an Event to be formatted and sent to the Logger's underlying io.Writer.

func (*Logger) Info

func (log *Logger) Info() *Event

Info returns an Event to be formatted and sent to the Logger's underlying io.Writer when the Logger's level is Debug, Verbose, or Info. If the Logger's level is above Info, this method returns without blocking.

func (*Logger) Log added in v0.14.0

func (log *Logger) Log() *Event

Log returns an Event to be formatted and sent to the Logger's underlying io.Writer, regardless of the Logger's log level, and omitting the event log level in the output.

func (*Logger) NewWriter added in v0.13.0

func (log *Logger) NewWriter(level Level) *Writer

NewWriter creates an io.Writer that conveys all writes it receives to the underlying io.Writer as individual log events.

func main() {
    log := gologs.New(os.Stdout).SetTimeFormatter(gologs.TimeUnix)
    lw := log.NewWriter()
    scanner := bufio.NewScanner(os.Stdin)
    for scanner.Scan() {
        _, err := lw.Write(scanner.Bytes())
        if err != nil {
            fmt.Fprintf(os.Stderr, "%s\n", err)
            os.Exit(1)
        }
    }
    if err := scanner.Err(); err != nil {
        fmt.Fprintf(os.Stderr, "%s\n", err)
        os.Exit(1)
    }
}

func (*Logger) SetDebug added in v0.2.0

func (log *Logger) SetDebug() *Logger

SetDebug changes the Logger's level to Debug, which allows all events to be logged. The change is made without blocking.

func (*Logger) SetError added in v0.2.0

func (log *Logger) SetError() *Logger

SetError changes the Logger's level to Error, which causes all Debug, Verbose, Info, and Warning events to be ignored, and all Error events to be logged. The change is made without blocking.

func (*Logger) SetInfo added in v0.2.0

func (log *Logger) SetInfo() *Logger

SetInfo changes the Logger's level to Info, which causes all Debug and Verbose events to be ignored, and all Info, Warning, and Error events to be logged. The change is made without blocking. The change is made without blocking.

func (*Logger) SetLevel

func (log *Logger) SetLevel(level Level) *Logger

SetLevel changes the Logger's level to the specified Level without blocking.

func (*Logger) SetTimeFormatter added in v0.7.0

func (log *Logger) SetTimeFormatter(callback TimeFormatter) *Logger

SetTimeFormatter updates the time formatting callback function that is invoked for every log message while it is being formatted, potentially blocking until any in progress log event has been written.

func (*Logger) SetVerbose

func (log *Logger) SetVerbose() *Logger

SetVerbose changes the Logger's level to Verbose, which causes all Debug events to be ignored, and all Verbose, Info, Warning, and Error events to be logged. The change is made without blocking.

func (*Logger) SetWarning added in v0.2.0

func (log *Logger) SetWarning() *Logger

SetWarning changes the Logger's level to Warning, which causes all Debug, Verbose, and Info events to be ignored, and all Warning, and Error events to be logged. The change is made without blocking.

func (*Logger) SetWriter added in v0.7.0

func (log *Logger) SetWriter(w io.Writer) *Logger

SetWriter directs all future writes to w, potentially blocking until any in progress log event has been written.

func (*Logger) Verbose

func (log *Logger) Verbose() *Event

Verbose returns an Event to be formatted and sent to the Logger's underlying io.Writer when the Logger's level is Debug or Verbose. If the Logger's level is above Verbose, this method returns without blocking.

func (*Logger) Warning

func (log *Logger) Warning() *Event

Warning returns an Event to be formatted and sent to the Logger's underlying io.Writer when the Logger's level is Debug, Verbose, Info, or Warning. If the Logger's level is above Warning, this method returns without blocking.

func (*Logger) With added in v0.12.0

func (log *Logger) With() *Intermediate

With returns an Intermediate Logger instance that inherits from log, but can be modified to add one or more additional properties for every outgoing log event.

log = log.With().String("s", "value").Bool("b", true).Logger()

type TimeFormatter added in v0.13.0

type TimeFormatter func([]byte) []byte

func TimeFormat added in v0.12.0

func TimeFormat(format string) TimeFormatter

TimeFormat returns a time formatter that appends the current time to buf as a JSON property name and value using the specified string format.

type Writer added in v0.13.0

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

Writer is an io.Writer that conveys all writes it receives to the underlying io.Writer as individual log events.

func (*Writer) SetDebug added in v0.13.0

func (w *Writer) SetDebug() *Writer

SetDebug changes the Writer's level to Debug, which causes all writes to the Writer to be logged to the underlying Logger with a level of Debug. The change is made without blocking.

func (*Writer) SetError added in v0.13.0

func (w *Writer) SetError() *Writer

SetError changes the Writer's level to Error, which causes all writes to the Writer to be logged to the underlying Logger with a level of Error. The change is made without blocking.

func (*Writer) SetInfo added in v0.13.0

func (w *Writer) SetInfo() *Writer

SetInfo changes the Writer's level to Info, which causes all writes to the Writer to be logged to the underlying Logger with a level of Info. The change is made without blocking.

func (*Writer) SetLevel added in v0.13.0

func (w *Writer) SetLevel(level Level) *Writer

SetLevel changes the Writer's level to the specified Level without blocking. This causes all writes to the Writer to be logged with the specified Level.

func (*Writer) SetVerbose added in v0.13.0

func (w *Writer) SetVerbose() *Writer

SetVerbose changes the Writer's level to Verbose, which causes all writes to the Writer to be logged to the underlying Logger with a level of Verbose. The change is made without blocking.

func (*Writer) SetWarning added in v0.13.0

func (w *Writer) SetWarning() *Writer

SetWarning changes the Writer's level to Warning, which causes all writes to the Writer to be logged to the underlying Logger with a level of Warning. The change is made without blocking.

func (*Writer) Write added in v0.13.0

func (w *Writer) Write(buf []byte) (int, error)

Write creates and emits a log event with its message set to the text of buf and at the log level with which it was instantiated.

On success, it returns the length of buf and nil error. Note that the number of bytes it wrote to the underlying io.Writer will always be longer than the number of bytes it receives in buf.

On failure, it returns 0 for the number of bytes it wrote to the underlying io.Writer along with the write error.

Directories

Path Synopsis
examples
command Module
service Module

Jump to

Keyboard shortcuts

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