Skip to content

Commit 79a40ef

Browse files
committed
added child logger and logger context method go-aah/aah#111
1 parent d8d574e commit 79a40ef

File tree

5 files changed

+82
-66
lines changed

5 files changed

+82
-66
lines changed

default.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ func Panicln(v ...interface{}) {
117117
dl.Panicln(v...)
118118
}
119119

120+
// AddContext method to add context values into default logger.
121+
// These context values gets logged with each log entry.
122+
func AddContext(fields Fields) {
123+
dl.AddContext(fields)
124+
}
125+
120126
// WithFields method to add multiple key-value pairs into log.
121127
func WithFields(fields Fields) *Entry {
122128
return dl.WithFields(fields)
@@ -132,6 +138,11 @@ func Writer() io.Writer {
132138
return dl.receiver.Writer()
133139
}
134140

141+
// SetWriter method sets the given writer into logger instance.
142+
func SetWriter(w io.Writer) {
143+
dl.SetWriter(w)
144+
}
145+
135146
// ToGoLogger method wraps the current log writer into Go Logger instance.
136147
func ToGoLogger() *slog.Logger {
137148
return dl.ToGoLogger()

default_test.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ func TestDefaultContextLogging(t *testing.T) {
7070
_ = SetPattern("%utctime:2006-01-02 15:04:05.000 %level:-5 %longfile %line %custom:- %message %fields")
7171
_ = SetLevel("trace")
7272

73+
AddContext(Fields{"myname": "default logger"})
74+
7375
Trace("I would like to see this message, trace is more fine grained for dev")
7476
Tracef("I would like to see this message, trace is more fine grained for dev: %v", 4)
7577

@@ -79,7 +81,7 @@ func TestDefaultContextLogging(t *testing.T) {
7981
Info("Yes, I would love to see")
8082
Infof("Yes, I would love to %v", "see")
8183

82-
Warn("Yes, yes it's an warning")
84+
WithField("warnkey1", "warn value 1").WithField("warnkey2", "warn value 2").Warn("Yes, yes it's an warning")
8385
Warnf("Yes, yes it's an %v", "warning")
8486

8587
Error("Yes, yes, yes - finally an error")
@@ -92,12 +94,15 @@ func TestDefaultContextLogging(t *testing.T) {
9294
exit = os.Exit
9395

9496
// With Context
95-
ctx := WithField("ctx1", "ctx1 value").WithFields(Fields{"ctx2": "ctx 2 value"})
97+
ctx := dl.New(Fields{
98+
"key1": "key 1 value",
99+
"key2": "key 2 value",
100+
})
96101

97102
ctx.Trace("I would like to see this message, trace is more fine grained for dev")
98103
ctx.Tracef("I would like to see this message, trace is more fine grained for dev: %v", 4)
99104

100-
ctx.Debug("I would like to see this message, debug is useful for dev")
105+
ctx.WithField("key3", "key 3 value").Debug("I would like to see this message, debug is useful for dev")
101106
ctx.Debugf("I would like to see this message, debug is useful for %v", "dev")
102107

103108
ctx.Info("Yes, I would love to see")
@@ -119,6 +124,12 @@ func TestDefaultContextLogging(t *testing.T) {
119124
ctx2.Print("hi fields")
120125
}
121126

127+
func TestDefaultFieldsLogging(t *testing.T) {
128+
_ = SetPattern("%time:2006-01-02 15:04:05.000 %level:-5 %appname %reqid %principal %message %fields")
129+
WithField("appname", "value1").Info("Logging value 1")
130+
WithFields(Fields{"appname": "value1", "key1": "key1value"}).Info("Logging value 1")
131+
}
132+
122133
func testStdPanic(method, msg string) {
123134
defer func() {
124135
if r := recover(); r != nil {

entry.go

Lines changed: 20 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -192,25 +192,8 @@ func (e *Entry) Panicln(v ...interface{}) {
192192
// WithFields method to add multiple key-value pairs into log.
193193
func (e *Entry) WithFields(fields Fields) *Entry {
194194
ne := acquireEntry(e.logger)
195-
for k, v := range e.Fields {
196-
ne.Fields[k] = v
197-
}
198-
for k, v := range fields {
199-
if k == "appname" {
200-
ne.AppName = fmt.Sprint(v)
201-
continue
202-
}
203-
if k == "reqid" {
204-
ne.RequestID = fmt.Sprint(v)
205-
continue
206-
}
207-
if k == "principal" {
208-
ne.Principal = fmt.Sprint(v)
209-
continue
210-
}
211-
212-
ne.Fields[k] = v
213-
}
195+
ne.addFields(e.Fields)
196+
ne.addFields(fields)
214197
return ne
215198
}
216199

@@ -219,35 +202,6 @@ func (e *Entry) WithField(key string, value interface{}) *Entry {
219202
return e.WithFields(Fields{key: value})
220203
}
221204

222-
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
223-
// Entry level methods
224-
//___________________________________
225-
226-
// IsLevelInfo method returns true if log level is INFO otherwise false.
227-
func (e *Entry) IsLevelInfo() bool {
228-
return e.logger.level == LevelInfo
229-
}
230-
231-
// IsLevelError method returns true if log level is ERROR otherwise false.
232-
func (e *Entry) IsLevelError() bool {
233-
return e.logger.level == LevelError
234-
}
235-
236-
// IsLevelWarn method returns true if log level is WARN otherwise false.
237-
func (e *Entry) IsLevelWarn() bool {
238-
return e.logger.level == LevelWarn
239-
}
240-
241-
// IsLevelDebug method returns true if log level is DEBUG otherwise false.
242-
func (e *Entry) IsLevelDebug() bool {
243-
return e.logger.level == LevelDebug
244-
}
245-
246-
// IsLevelTrace method returns true if log level is TRACE otherwise false.
247-
func (e *Entry) IsLevelTrace() bool {
248-
return e.logger.level == LevelTrace
249-
}
250-
251205
// Reset method resets the `Entry` values for reuse.
252206
func (e *Entry) Reset() {
253207
e.AppName = ""
@@ -258,7 +212,7 @@ func (e *Entry) Reset() {
258212
e.Message = ""
259213
e.File = ""
260214
e.Line = 0
261-
e.Fields = make(map[string]interface{})
215+
e.Fields = make(Fields)
262216
e.logger = nil
263217
}
264218

@@ -270,12 +224,28 @@ func (e *Entry) output(lvl level, msg string) {
270224
e.Time = time.Now()
271225
e.Level = lvl
272226
e.Message = msg
227+
e.addFields(e.logger.ctx)
273228
e.logger.output(e)
274229
}
275230

231+
func (e *Entry) addFields(fields Fields) {
232+
for k, v := range fields {
233+
switch k {
234+
case "appname":
235+
e.AppName = fmt.Sprint(v)
236+
case "reqid":
237+
e.RequestID = fmt.Sprint(v)
238+
case "principal":
239+
e.Principal = fmt.Sprint(v)
240+
default:
241+
e.Fields[k] = v
242+
}
243+
}
244+
}
245+
276246
func newEntry() *Entry {
277247
return &Entry{
278-
Fields: make(map[string]interface{}),
248+
Fields: make(Fields),
279249
}
280250
}
281251

formatter.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ var (
5959
// Usage of flag order is up to format composition.
6060
// level - outputs INFO, DEBUG, ERROR, so on
6161
// appname - outputs Application Name (aka instance name)
62-
// reqid - outputs Request ID HTTP header value
62+
// reqid - outputs Request ID from HTTP header
6363
// principal - outputs Logged-In subject primary principal
6464
// level - outputs INFO, DEBUG, ERROR, so on
6565
// time - outputs local time as per format supplied
@@ -143,7 +143,7 @@ func textFormatter(flags []ess.FmtFlagPart, entry *Entry) []byte {
143143
buf.WriteString("fields[")
144144
for k, v := range entry.Fields {
145145
cnt--
146-
buf.WriteString(fmt.Sprintf("%v=%v", k, v))
146+
buf.WriteString(fmt.Sprintf("%v: %v", k, v))
147147
if cnt != 0 {
148148
buf.WriteString(", ")
149149
}

log.go

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,17 @@ var (
6161
)
6262

6363
type (
64+
// Logger is the object which logs the given message into recevier as per deifned
65+
// format flags. Logger can be used simultaneously from multiple goroutines;
66+
// it guarantees to serialize access to the Receivers.
67+
Logger struct {
68+
cfg *config.Config
69+
m *sync.Mutex
70+
level level
71+
receiver Receiver
72+
ctx Fields
73+
}
74+
6475
// Receiver is the interface for pluggable log receiver.
6576
// For e.g: Console, File
6677
Receiver interface {
@@ -72,16 +83,6 @@ type (
7283
Log(e *Entry)
7384
}
7485

75-
// Logger is the object which logs the given message into recevier as per deifned
76-
// format flags. Logger can be used simultaneously from multiple goroutines;
77-
// it guarantees to serialize access to the Receivers.
78-
Logger struct {
79-
cfg *config.Config
80-
m *sync.Mutex
81-
level level
82-
receiver Receiver
83-
}
84-
8586
// Logger and Entry log method compliance.
8687
loggerInterface interface {
8788
Error(v ...interface{})
@@ -140,13 +141,34 @@ func New(cfg *config.Config) (*Logger, error) {
140141
return nil, err
141142
}
142143

144+
logger.ctx = make(Fields)
145+
143146
return logger, nil
144147
}
145148

146149
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
147150
// Logger methods
148151
//___________________________________
149152

153+
// New method creates a child logger and adds structured context to it. Child
154+
// logger inherits parent logger context value on creation. Fields added
155+
// to the child don't affect the parent logger and vice versa. These context
156+
// values gets logged with each log entry.
157+
//
158+
// Also you can use method `AddContext` to context to the current logger.
159+
func (l *Logger) New(fields Fields) *Logger {
160+
nl := *l
161+
nl.AddContext(fields)
162+
return &nl
163+
}
164+
165+
// AddContext method to add context values into current logger.
166+
func (l *Logger) AddContext(fields Fields) {
167+
for k, v := range fields {
168+
l.ctx[k] = v
169+
}
170+
}
171+
150172
// Level method returns currently enabled logging level.
151173
func (l *Logger) Level() string {
152174
return levelToLevelName[l.level]
@@ -307,7 +329,9 @@ func (l *Logger) WithFields(fields Fields) *Entry {
307329

308330
// WithField method to add single key-value into log
309331
func (l *Logger) WithField(key string, value interface{}) *Entry {
310-
return l.WithFields(Fields{key: value})
332+
e := acquireEntry(l)
333+
defer releaseEntry(e)
334+
return e.WithField(key, value)
311335
}
312336

313337
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾

0 commit comments

Comments
 (0)