github.com/blend/go-sdk@v1.20220411.3/logger/scope.go (about) 1 /* 2 3 Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file. 5 6 */ 7 8 package logger 9 10 import ( 11 "context" 12 "fmt" 13 "time" 14 ) 15 16 var ( 17 _ Log = (*Scope)(nil) 18 ) 19 20 // NewScope returns a new scope for a logger with a given set of optional options. 21 func NewScope(log *Logger, options ...ScopeOption) Scope { 22 s := Scope{ 23 Logger: log, 24 Labels: make(Labels), 25 Annotations: make(Annotations), 26 } 27 for _, option := range options { 28 option(&s) 29 } 30 return s 31 } 32 33 // Scope is a set of re-usable parameters for triggering events. 34 /* 35 The key fields: 36 - "Path" is a set of names that denote a hierarchy or tree of calls. 37 - "Labels" are string pairs that will appear with written log messages for easier searching later. 38 - "Annoations" are string pairs that will not appear with written log messages, but can be used to add extra data to events. 39 */ 40 type Scope struct { 41 // Path is a series of descriptive labels that shows the origin of the scope. 42 Path []string 43 // Labels are descriptive string fields for the scope. 44 Labels 45 // Annotations are extra fields for the scope. 46 Annotations 47 48 // Logger is a parent reference to the root logger; this holds 49 // information around what flags are enabled and listeners for events. 50 Logger *Logger 51 } 52 53 // ScopeOption is a mutator for a scope. 54 type ScopeOption func(*Scope) 55 56 // OptScopePath sets the path on a scope. 57 func OptScopePath(path ...string) ScopeOption { 58 return func(s *Scope) { 59 s.Path = path 60 } 61 } 62 63 // OptScopeLabels sets the labels on a scope. 64 func OptScopeLabels(labels ...Labels) ScopeOption { 65 return func(s *Scope) { 66 s.Labels = CombineLabels(labels...) 67 } 68 } 69 70 // OptScopeAnnotations sets the annotations on a scope. 71 func OptScopeAnnotations(annotations ...Annotations) ScopeOption { 72 return func(s *Scope) { 73 s.Annotations = CombineAnnotations(annotations...) 74 } 75 } 76 77 // WithPath returns a new scope with a given additional path segment. 78 func (sc Scope) WithPath(paths ...string) Scope { 79 return NewScope(sc.Logger, 80 OptScopePath(append(sc.Path, paths...)...), 81 OptScopeLabels(sc.Labels), 82 OptScopeAnnotations(sc.Annotations), 83 ) 84 } 85 86 // WithLabels returns a new scope with a given additional set of labels. 87 func (sc Scope) WithLabels(labels Labels) Scope { 88 return NewScope(sc.Logger, 89 OptScopePath(sc.Path...), 90 OptScopeLabels(sc.Labels, labels), 91 OptScopeAnnotations(sc.Annotations), 92 ) 93 } 94 95 // WithAnnotations returns a new scope with a given additional set of annotations. 96 func (sc Scope) WithAnnotations(annotations Annotations) Scope { 97 return NewScope(sc.Logger, 98 OptScopePath(sc.Path...), 99 OptScopeLabels(sc.Labels), 100 OptScopeAnnotations(sc.Annotations, annotations), 101 ) 102 } 103 104 // -------------------------------------------------------------------------------- 105 // Trigger event handler 106 // -------------------------------------------------------------------------------- 107 108 // Trigger triggers an event in the subcontext. 109 // The provided context is amended with fields from the scope. 110 // The provided context is also amended with a TriggerTimestamp, which can be retrieved with `GetTriggerTimestamp(ctx)` in listeners. 111 func (sc Scope) Trigger(event Event) { 112 sc.TriggerContext(context.Background(), event) 113 } 114 115 // TriggerContext triggers an event with a given context.. 116 func (sc Scope) TriggerContext(ctx context.Context, event Event) { 117 ctx = WithTriggerTimestamp(ctx, time.Now().UTC()) 118 sc.Logger.Dispatch(sc.ApplyContext(ctx), event) 119 } 120 121 // -------------------------------------------------------------------------------- 122 // Builtin Flag Handlers (infof, debugf etc.) 123 // -------------------------------------------------------------------------------- 124 125 // Info logs an informational message to the output stream. 126 func (sc Scope) Info(args ...interface{}) { 127 sc.Trigger(NewMessageEvent(Info, fmt.Sprint(args...))) 128 } 129 130 // InfoContext logs an informational message to the output stream in a given context. 131 func (sc Scope) InfoContext(ctx context.Context, args ...interface{}) { 132 sc.TriggerContext(ctx, NewMessageEvent(Info, fmt.Sprint(args...))) 133 } 134 135 // Infof logs an informational message to the output stream. 136 func (sc Scope) Infof(format string, args ...interface{}) { 137 sc.Trigger(NewMessageEvent(Info, fmt.Sprintf(format, args...))) 138 } 139 140 // InfofContext logs an informational message to the output stream in a given context. 141 func (sc Scope) InfofContext(ctx context.Context, format string, args ...interface{}) { 142 sc.TriggerContext(ctx, NewMessageEvent(Info, fmt.Sprintf(format, args...))) 143 } 144 145 // Debug logs a debug message to the output stream. 146 func (sc Scope) Debug(args ...interface{}) { 147 sc.Trigger(NewMessageEvent(Debug, fmt.Sprint(args...))) 148 } 149 150 // DebugContext logs a debug message to the output stream in a given context. 151 func (sc Scope) DebugContext(ctx context.Context, args ...interface{}) { 152 sc.TriggerContext(ctx, NewMessageEvent(Debug, fmt.Sprint(args...))) 153 } 154 155 // Debugf logs a debug message to the output stream. 156 func (sc Scope) Debugf(format string, args ...interface{}) { 157 sc.Trigger(NewMessageEvent(Debug, fmt.Sprintf(format, args...))) 158 } 159 160 // DebugfContext logs a debug message to the output stream. 161 func (sc Scope) DebugfContext(ctx context.Context, format string, args ...interface{}) { 162 sc.TriggerContext(ctx, NewMessageEvent(Debug, fmt.Sprintf(format, args...))) 163 } 164 165 // Warningf logs a warning message to the output stream. 166 func (sc Scope) Warningf(format string, args ...interface{}) { 167 sc.Trigger(NewErrorEvent(Warning, fmt.Errorf(format, args...))) 168 } 169 170 // WarningfContext logs a warning message to the output stream in a given context. 171 func (sc Scope) WarningfContext(ctx context.Context, format string, args ...interface{}) { 172 sc.TriggerContext(ctx, NewErrorEvent(Warning, fmt.Errorf(format, args...))) 173 } 174 175 // Errorf writes an event to the log and triggers event listeners. 176 func (sc Scope) Errorf(format string, args ...interface{}) { 177 sc.Trigger(NewErrorEvent(Error, fmt.Errorf(format, args...))) 178 } 179 180 // ErrorfContext writes an event to the log and triggers event listeners in a given context. 181 func (sc Scope) ErrorfContext(ctx context.Context, format string, args ...interface{}) { 182 sc.TriggerContext(ctx, NewErrorEvent(Error, fmt.Errorf(format, args...))) 183 } 184 185 // Fatalf writes an event to the log and triggers event listeners. 186 func (sc Scope) Fatalf(format string, args ...interface{}) { 187 sc.Trigger(NewErrorEvent(Fatal, fmt.Errorf(format, args...))) 188 } 189 190 // FatalfContext writes an event to the log and triggers event listeners in a given context. 191 func (sc Scope) FatalfContext(ctx context.Context, format string, args ...interface{}) { 192 sc.TriggerContext(ctx, NewErrorEvent(Fatal, fmt.Errorf(format, args...))) 193 } 194 195 // Warning logs a warning error to std err. 196 func (sc Scope) Warning(err error, opts ...ErrorEventOption) { 197 sc.Trigger(NewErrorEvent(Warning, err, opts...)) 198 } 199 200 // WarningContext logs a warning error to std err in a given context. 201 func (sc Scope) WarningContext(ctx context.Context, err error, opts ...ErrorEventOption) { 202 sc.TriggerContext(ctx, NewErrorEvent(Warning, err, opts...)) 203 } 204 205 // Error logs an error to std err. 206 func (sc Scope) Error(err error, opts ...ErrorEventOption) { 207 sc.Trigger(NewErrorEvent(Error, err, opts...)) 208 } 209 210 // ErrorContext logs an error to std err. 211 func (sc Scope) ErrorContext(ctx context.Context, err error, opts ...ErrorEventOption) { 212 sc.TriggerContext(ctx, NewErrorEvent(Error, err, opts...)) 213 } 214 215 // Fatal logs an error as fatal. 216 func (sc Scope) Fatal(err error, opts ...ErrorEventOption) { 217 sc.Trigger(NewErrorEvent(Fatal, err, opts...)) 218 } 219 220 // FatalContext logs an error as fatal. 221 func (sc Scope) FatalContext(ctx context.Context, err error, opts ...ErrorEventOption) { 222 sc.TriggerContext(ctx, NewErrorEvent(Fatal, err, opts...)) 223 } 224 225 // 226 // Context utilities 227 // 228 229 // FromContext returns a scope from a given context. 230 // It will read any relevant fields off the context (Path, Labels, Annotations) 231 // and append them to values already on the scope. 232 func (sc Scope) FromContext(ctx context.Context) Scope { 233 return NewScope(sc.Logger, 234 OptScopePath(append(GetPath(ctx), sc.Path...)...), 235 OptScopeLabels(sc.Labels, GetLabels(ctx)), 236 OptScopeAnnotations(sc.Annotations, GetAnnotations(ctx)), 237 ) 238 } 239 240 // ApplyContext applies the scope fields to a given context. 241 func (sc Scope) ApplyContext(ctx context.Context) context.Context { 242 ctx = WithPath(ctx, append(GetPath(ctx), sc.Path...)...) 243 ctx = WithLabels(ctx, sc.Labels) // treated specially because maps are references 244 ctx = WithAnnotations(ctx, CombineAnnotations(sc.Annotations, GetAnnotations(ctx))) 245 return ctx 246 }