report

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Nov 24, 2020 License: MIT Imports: 10 Imported by: 4

README

Logging Utility for Go

A simple log & trace utility for Golang services.

Go Report Card

PkgGoDev

  • Info & Action logging methods to record information or actionable errors.
  • StartSpan & EndSpan methods to record trace information.
  • Runtime Golang stats are gathered via RuntimeEvery.
  • Flexible writer interface provided, with StdOut JSON & Honeycomb exporters included.
  • Log metrics aggregated and exposed via Count for log interface tests.

Documentation

Overview

Package report provides a simple log and tracing interface.

Info & Action logging methods to record information or actionable errors. StartSpan & EndSpan methods to record trace information. Runtime Golang stats are gathered via RuntimeEvery. Flexible writer interface provided, with StdOut JSON & Honeycomb exporters included. Log metrics aggregated and exposed via Count for log interface tests.

See Also

https://opentracing.io/docs/overview/spans/ https://docs.honeycomb.io/working-with-data/tracing/ https://docs.honeycomb.io/working-with-data/tracing/send-trace-data/

Example
package main

import (
	"fmt"

	"github.com/robtuley/report"
)

func main() {
	// setup logging output
	log := report.New("example")
	log.Baggage("timestamp", "2017-05-20T21:00:24.2+01:00") // to make output deterministic
	log.Export(report.StdOutJSON())
	defer log.Close()

	// ticker daemon execution
	log.Info("example.start", report.Data{})
	seq := 0
	for i := 0; i < 3; i++ {
		log.Info("example.tick", report.Data{"sequence": seq})
		seq = seq + 1
	}
	log.Info("example.stop", report.Data{})

	// validate
	if log.Count("example.tick") != 3 {
		// your own log validation...
		fmt.Print("Ooops! example.tick should be 3")
	}
	if err := log.Err(); err != nil {
		// your own log validation...
		fmt.Print(err)
	}

}
Output:

{"name":"example.start","service_name":"example","timestamp":"2017-05-20T21:00:24.2+01:00","type":"info"}
{"name":"example.tick","sequence":0,"service_name":"example","timestamp":"2017-05-20T21:00:24.2+01:00","type":"info"}
{"name":"example.tick","sequence":1,"service_name":"example","timestamp":"2017-05-20T21:00:24.2+01:00","type":"info"}
{"name":"example.tick","sequence":2,"service_name":"example","timestamp":"2017-05-20T21:00:24.2+01:00","type":"info"}
{"name":"example.stop","service_name":"example","timestamp":"2017-05-20T21:00:24.2+01:00","type":"info"}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Data

type Data map[string]interface{}

Data is a string-keyed map of unstructured data relevant to the event

type Exporter

type Exporter interface {
	Send(d Data) error
	Close()
}

Exporter exports events to an external service

func Honeycomb

func Honeycomb(key string, dataset string) Exporter

Honeycomb sends log events to HoneyComb

func JSON

func JSON(w io.Writer) Exporter

JSON writes JSON formatted logs

func StdOutJSON

func StdOutJSON() Exporter

StdOutJSON writes logs to StdOut as JSON

type Logger

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

Logger is the central logging agent on which to register events

func New

func New(name string) *Logger

New creates an instance of a logging agent

logger := report.New("myAppName")
logger.Export(report.StdOutJSON())
defer logger.Stop()

func (*Logger) Action

func (l *Logger) Action(event string, payload Data) <-chan int

Action events that need intervention or resolving.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/robtuley/report"
)

func main() {
	// setup logging output
	log := report.New("example")
	log.Baggage("timestamp", "2017-05-20T21:00:24.2+01:00") // to make output deterministic
	log.Export(report.StdOutJSON())
	defer log.Close()

	// for example we get an error...
	err := errors.New("Failed to parse JSON")

	// normal usage is simple to call log.Action
	log.Action("json.unparseable", report.Data{"error": err.Error()})

	// if you want to block until the logline is written, consume from the returned channel
	// (useful if you intend to shutdown as a result of this error)
	<-log.Action("json.unparseable", report.Data{"error": err.Error()})

	// LastError can be used to validate if an actionable event was logged
	if err := log.Err(); err != nil {
		fmt.Println(err.Error())
	}

}
Output:

{"error":"Failed to parse JSON","name":"json.unparseable","service_name":"example","timestamp":"2017-05-20T21:00:24.2+01:00","type":"action"}
{"error":"Failed to parse JSON","name":"json.unparseable","service_name":"example","timestamp":"2017-05-20T21:00:24.2+01:00","type":"action"}
Actionable event: json.unparseable

func (*Logger) Baggage

func (l *Logger) Baggage(key string, value interface{})

Baggage adds a key value pair that is included in every logged event

func (*Logger) Close

func (l *Logger) Close()

Close shuts down the logging agent

func (*Logger) Count

func (l *Logger) Count(event string) int

Count returns the number of log events of a particular type since startup

Example
package main

import (
	"fmt"

	"github.com/robtuley/report"
)

func main() {
	log := report.New("example")
	defer log.Close()

	log.Info("http.response.200", report.Data{})
	log.Info("http.response.404", report.Data{})
	log.Info("http.response.200", report.Data{})

	fmt.Printf("404 response count is %d", log.Count("http.response.404"))

	if err := log.Err(); err != nil {
		fmt.Print(err)
	}

}
Output:

404 response count is 1

func (*Logger) Err

func (l *Logger) Err() error

Err returns the last Actionable log event or encoding error if either occurred

Example
package main

import (
	"fmt"

	"github.com/robtuley/report"
)

func main() {
	log := report.New("example")
	log.Export(report.StdOutJSON())
	defer log.Close()

	// this log line has errored, but to prevent error handling clutter
	// the library interface requires you need to check error states
	// separately using Logger.LastError()
	<-log.Info("encoding.fail", report.Data{
		"unencodeable": make(chan int),
	})

	// check whether there has been any logging errors
	if err := log.Err(); err != nil {
		fmt.Println(err.Error())
	}

}
Output:

Error sending encoding.fail: json: unsupported type: chan int

func (*Logger) Export

func (l *Logger) Export(e Exporter)

Export configures an external service to receive log events

Example
package main

import (
	"bytes"
	"fmt"

	"github.com/robtuley/report"
)

func main() {
	// want to write to 2 logfiles, represented here as 2 buffers
	b1 := &bytes.Buffer{}
	b2 := &bytes.Buffer{}

	// setup logging output
	log := report.New("example")
	log.Baggage("timestamp", "2017-05-20T21:00:24.2+01:00") // to make output deterministic
	defer log.Close()

	// configure 2 writers
	log.Export(report.JSON(b1))
	log.Export(report.JSON(b2))

	// log something
	<-log.Info("http.response", report.Data{"status": 404, "request": "/nopage"})

	// output the 2 log files, note these have been written in parallel which
	// is why they are kept separate in this example until the end
	fmt.Print(b1)
	fmt.Print(b2)

	if err := log.Err(); err != nil {
		fmt.Print(err)
	}

}
Output:

{"name":"http.response","request":"/nopage","service_name":"example","status":404,"timestamp":"2017-05-20T21:00:24.2+01:00","type":"info"}
{"name":"http.response","request":"/nopage","service_name":"example","status":404,"timestamp":"2017-05-20T21:00:24.2+01:00","type":"info"}

func (*Logger) Info

func (l *Logger) Info(event string, payload Data) <-chan int

Info logs event that provide telemetry measures or context to any events requiring action.

Example
package main

import (
	"fmt"

	"github.com/robtuley/report"
)

func main() {
	// setup logging output
	log := report.New("example")
	log.Baggage("timestamp", "2017-05-20T21:00:24.2+01:00") // to make output deterministic
	log.Export(report.StdOutJSON())
	defer log.Close()

	// normal usage is simple to call log.Info
	log.Info("http.response", report.Data{"status": 200, "request": "/page1"})
	log.Info("http.response", report.Data{"status": 200, "request": "/page2"})

	// if you want to block until the logline is written, consume from the returned channel
	<-log.Info("http.response", report.Data{"status": 404, "request": "/nopage"})

	if err := log.Err(); err != nil {
		fmt.Print(err)
	}

}
Output:

{"name":"http.response","request":"/page1","service_name":"example","status":200,"timestamp":"2017-05-20T21:00:24.2+01:00","type":"info"}
{"name":"http.response","request":"/page2","service_name":"example","status":200,"timestamp":"2017-05-20T21:00:24.2+01:00","type":"info"}
{"name":"http.response","request":"/nopage","service_name":"example","status":404,"timestamp":"2017-05-20T21:00:24.2+01:00","type":"info"}

func (*Logger) RuntimeEvery

func (l *Logger) RuntimeEvery(duration time.Duration)

RuntimeEvery records runtime stats at the specified interval

log := report.New(report.StdOutJSON(), report.Data{"service": "myAppName"})
log.RuntimeEvery(time.Second*10)

func (*Logger) Send

func (l *Logger) Send(d Data) error

Send exports a raw data event to configured external services

func (*Logger) Trace

func (l *Logger) Trace(s Span) <-chan int

Trace writes the data from a completed trace span

type Span

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

Span represents a trace span

func StartSpan

func StartSpan(event string, opts ...SpanOption) Span

StartSpan creates a span

func (Span) Child

func (s Span) Child(child Span) Span

Child adds a child span

func (Span) End

func (s Span) End(errors ...error) Span

End finishes the span

func (Span) Err

func (s Span) Err() error

Err provides error if there was one within span

func (Span) Field

func (s Span) Field(k string, v interface{}) Span

Field associates key and value with the span

func (Span) FollowedBy

func (s Span) FollowedBy(next Span) Span

FollowedBy adds a followed by span

func (Span) SpanID

func (s Span) SpanID() string

SpanID provides the span ID

func (Span) TraceID

func (s Span) TraceID() string

TraceID provides the span trace ID

type SpanOption

type SpanOption func(s Span) Span

SpanOption is a option to modify a span

func ParentSpanID

func ParentSpanID(id string) SpanOption

ParentSpanID option specifies the parent span ID for a span

func TraceID

func TraceID(id string) SpanOption

TraceID option specifies the trace ID for a span

Jump to

Keyboard shortcuts

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