log.go 3.94 KB
Newer Older
1
package eventlog
2 3

import (
4 5 6
	"fmt"
	"time"

7
	"github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
Brian Tiger Chow's avatar
Brian Tiger Chow committed
8
	"github.com/jbenet/go-ipfs/util" // TODO remove IPFS dependency
9 10
)

Brian Tiger Chow's avatar
Brian Tiger Chow committed
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
// StandardLogger provides API compatibility with standard printf loggers
// eg. go-logging
type StandardLogger interface {
	Critical(args ...interface{})
	Criticalf(format string, args ...interface{})
	Debug(args ...interface{})
	Debugf(format string, args ...interface{})
	Error(args ...interface{})
	Errorf(format string, args ...interface{})
	Fatal(args ...interface{})
	Fatalf(format string, args ...interface{})
	Info(args ...interface{})
	Infof(format string, args ...interface{})
	Notice(args ...interface{})
	Noticef(format string, args ...interface{})
	Panic(args ...interface{})
	Panicf(format string, args ...interface{})
	Warning(args ...interface{})
	Warningf(format string, args ...interface{})
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
32 33
// EventLogger extends the StandardLogger interface to allow for log items
// containing structured metadata
34
type EventLogger interface {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
35
	StandardLogger
Brian Tiger Chow's avatar
Brian Tiger Chow committed
36 37 38 39 40 41 42 43 44 45 46 47 48 49

	// Event merges structured data from the provided inputs into a single
	// machine-readable log event.
	//
	// If the context contains metadata, a copy of this is used as the base
	// metadata accumulator.
	//
	// If one or more loggable objects are provided, these are deep-merged into base blob.
	//
	// Next, the event name is added to the blob under the key "event". If
	// the key "event" already exists, it will be over-written.
	//
	// Finally the timestamp and package name are added to the accumulator and
	// the metadata is logged.
50
	Event(ctx context.Context, event string, m ...Loggable)
51

52
	EventBegin(ctx context.Context, event string, m ...Loggable) *EventInProgress
53 54
}

55
// Logger retrieves an event logger by name
56
func Logger(system string) EventLogger {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
57 58 59

	// TODO if we would like to adjust log levels at run-time. Store this event
	// logger in a map (just like the util.Logger impl)
Brian Tiger Chow's avatar
Brian Tiger Chow committed
60
	return &eventLogger{system: system, StandardLogger: util.Logger(system)}
61 62 63 64
}

// eventLogger implements the EventLogger and wraps a go-logging Logger
type eventLogger struct {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
65
	StandardLogger
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
66

67
	system string
Brian Tiger Chow's avatar
Brian Tiger Chow committed
68
	// TODO add log-level
69 70
}

71
func (el *eventLogger) EventBegin(ctx context.Context, event string, metadata ...Loggable) *EventInProgress {
72
	start := time.Now()
Brian Tiger Chow's avatar
Brian Tiger Chow committed
73
	el.Event(ctx, fmt.Sprintf("%sBegin", event), metadata...)
74

75
	eip := &EventInProgress{}
76 77 78 79
	eip.doneFunc = func(additional []Loggable) {

		metadata = append(metadata, additional...)                      // anything added during the operation
		metadata = append(metadata, LoggableMap(map[string]interface{}{ // finally, duration of event
80
			"duration": time.Now().Sub(start),
81 82 83 84 85
		}))

		el.Event(ctx, event, metadata...)
	}
	return eip
86 87
}

88
func (el *eventLogger) Event(ctx context.Context, event string, metadata ...Loggable) {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
89

Brian Tiger Chow's avatar
Brian Tiger Chow committed
90 91 92
	// Collect loggables for later logging
	var loggables []Loggable

Brian Tiger Chow's avatar
Brian Tiger Chow committed
93
	// get any existing metadata from the context
94 95 96 97
	existing, err := MetadataFromContext(ctx)
	if err != nil {
		existing = Metadata{}
	}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
98
	loggables = append(loggables, existing)
Brian Tiger Chow's avatar
Brian Tiger Chow committed
99

100
	for _, datum := range metadata {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
101 102 103 104 105 106 107 108 109 110 111
		loggables = append(loggables, datum)
	}

	e := entry{
		loggables: loggables,
		system:    el.system,
		event:     event,
	}

	e.Log() // TODO replace this when leveled-logs have been implemented
}
112

113 114 115 116 117 118
type EventInProgress struct {
	loggables []Loggable
	doneFunc  func([]Loggable)
}

// Append adds loggables to be included in the call to Done
119
func (eip *EventInProgress) Append(l Loggable) {
120 121
	eip.loggables = append(eip.loggables, l)
}
122

123
// SetError includes the provided error
124
func (eip *EventInProgress) SetError(err error) {
125 126 127 128 129
	eip.loggables = append(eip.loggables, LoggableMap{
		"error": err.Error(),
	})
}

130 131
// Done creates a new Event entry that includes the duration and appended
// loggables.
132
func (eip *EventInProgress) Done() {
133
	eip.doneFunc(eip.loggables) // create final event with extra data
134 135
}

136
// Close is an alias for done
137
func (eip *EventInProgress) Close() error {
138
	eip.Done()
139 140
	return nil
}