aboutsummaryrefslogtreecommitdiff
path: root/logger/log.go
blob: a47d27dece886e4396c842d432823f5490fcafc9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// SPDX-FileCopyrightText: 2020 Ethel Morgan
//
// SPDX-License-Identifier: MIT

package logger

import (
	"context"
	"fmt"
	"sync"

	log "github.com/sirupsen/logrus"
)

type (
	// log, ctx := logger.FromContext(ctx)
	// log.WithField("error", err).Warninging(...)
	// log.AddField("transport", udn)
	// log.Info(...)
	Logger interface {
		// AddField adds a field to the current logger.
		AddField(name string, value interface{})

		// WithField forks a logger, adding context.
		WithField(name string, value interface{}) Logger

		// WithError is a convenience method for one-off forks to log error messages under the key "error".
		WithError(err error) Logger

		// Fork returns a copy of the Logger and a fork of the context.Context to pass through.
		Fork(context.Context) (Logger, context.Context)

		Debug(string)
		Info(string)
		Warning(string)
		Error(string)
		Fatal(string)
	}

	logger struct {
		mu     sync.Mutex
		values map[string]interface{}
	}

	// contextKey is a separate type to prevent collisions with other packages.
	contextKey int
)

const (
	loggerKey contextKey = iota
)

func FromContext(ctx context.Context) (Logger, context.Context) {
	maybeLogger := ctx.Value(loggerKey)

	if maybeLogger == nil {
		l := Background()
		return l, context.WithValue(ctx, loggerKey, l)
	}

	if l, ok := maybeLogger.(*logger); ok {
		return l, ctx
	}

	panic(fmt.Sprintf("expected logger in context, found %+v", maybeLogger))
}
func Background() Logger {
	return &logger{
		values: map[string]interface{}{},
	}
}

func (l *logger) AddField(name string, value interface{}) {
	l.mu.Lock()
	defer l.mu.Unlock()

	// TODO: check it doesn't exist already?
	l.values[name] = value
}
func (l *logger) WithField(name string, value interface{}) Logger {
	clone, _ := l.Fork(context.Background())
	clone.AddField(name, value)
	return clone
}
func (l *logger) WithError(err error) Logger {
	return l.WithField("error", err)
}

func (l *logger) Fork(ctx context.Context) (Logger, context.Context) {
	l.mu.Lock()
	defer l.mu.Unlock()

	clone := &logger{
		values: map[string]interface{}{},
	}
	for k, v := range l.values {
		clone.values[k] = v
	}
	return clone, context.WithValue(ctx, loggerKey, clone)
}

func (l *logger) Debug(message string) {
	l.mu.Lock()
	defer l.mu.Unlock()

	log.WithFields(log.Fields(l.values)).Debug(message)
}
func (l *logger) Info(message string) {
	l.mu.Lock()
	defer l.mu.Unlock()

	log.WithFields(log.Fields(l.values)).Info(message)
}
func (l *logger) Warning(message string) {
	l.mu.Lock()
	defer l.mu.Unlock()

	log.WithFields(log.Fields(l.values)).Warning(message)
}
func (l *logger) Error(message string) {
	l.mu.Lock()
	defer l.mu.Unlock()

	log.WithFields(log.Fields(l.values)).Error(message)
}
func (l *logger) Fatal(message string) {
	l.mu.Lock()
	defer l.mu.Unlock()

	log.WithFields(log.Fields(l.values)).Fatal(message)
}