github.com/blend/go-sdk@v1.20240719.1/logger/scope.go (about) 1 /* 2 3 Copyright (c) 2024 - 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 - "Restricted" is a boolean value that controls potentially PII-containing errors that should be logged into the restricted logs. 40 */ 41 type Scope struct { 42 // Path is a series of descriptive labels that shows the origin of the scope. 43 Path []string 44 // Labels are descriptive string fields for the scope. 45 Labels 46 // Annotations are extra fields for the scope. 47 Annotations 48 // Restricted denotes a scope as PII-sensitive for the scope. 49 Restricted bool 50 // Logger is a parent reference to the root logger; this holds 51 // information around what flags are enabled and listeners for events. 52 Logger *Logger 53 } 54 55 // ScopeOption is a mutator for a scope. 56 type ScopeOption func(*Scope) 57 58 // OptScopePath sets the path on a scope. 59 func OptScopePath(path ...string) ScopeOption { 60 return func(s *Scope) { 61 s.Path = path 62 } 63 } 64 65 // OptScopeLabels sets the labels on a scope. 66 func OptScopeLabels(labels ...Labels) ScopeOption { 67 return func(s *Scope) { 68 s.Labels = CombineLabels(labels...) 69 } 70 } 71 72 // OptScopeAnnotations sets the annotations on a scope. 73 func OptScopeAnnotations(annotations ...Annotations) ScopeOption { 74 return func(s *Scope) { 75 s.Annotations = CombineAnnotations(annotations...) 76 } 77 } 78 79 // OptScopeRestriction sets the restricted value on a scope. 80 func OptScopeRestriction(restricted bool) ScopeOption { 81 return func(s *Scope) { 82 s.Restricted = restricted 83 } 84 } 85 86 // WithPath returns a new scope with a given additional path segment. 87 func (sc Scope) WithPath(paths ...string) Scope { 88 return NewScope(sc.Logger, 89 OptScopePath(append(sc.Path, paths...)...), 90 OptScopeLabels(sc.Labels), 91 OptScopeAnnotations(sc.Annotations), 92 OptScopeRestriction(sc.Restricted), 93 ) 94 } 95 96 // WithLabels returns a new scope with a given additional set of labels. 97 func (sc Scope) WithLabels(labels Labels) Scope { 98 return NewScope(sc.Logger, 99 OptScopePath(sc.Path...), 100 OptScopeLabels(sc.Labels, labels), 101 OptScopeAnnotations(sc.Annotations), 102 OptScopeRestriction(sc.Restricted), 103 ) 104 } 105 106 // WithAnnotations returns a new scope with a given additional set of annotations. 107 func (sc Scope) WithAnnotations(annotations Annotations) Scope { 108 return NewScope(sc.Logger, 109 OptScopePath(sc.Path...), 110 OptScopeLabels(sc.Labels), 111 OptScopeAnnotations(sc.Annotations, annotations), 112 OptScopeRestriction(sc.Restricted), 113 ) 114 } 115 116 // -------------------------------------------------------------------------------- 117 // Trigger event handler 118 // -------------------------------------------------------------------------------- 119 120 // Trigger triggers an event in the subcontext. 121 // The provided context is amended with fields from the scope. 122 // The provided context is also amended with a TriggerTimestamp, which can be retrieved with `GetTriggerTimestamp(ctx)` in listeners. 123 func (sc Scope) Trigger(event Event) { 124 sc.TriggerContext(context.Background(), event) 125 } 126 127 // TriggerContext triggers an event with a given context.. 128 func (sc Scope) TriggerContext(ctx context.Context, event Event) { 129 ctx = WithTriggerTimestamp(ctx, time.Now().UTC()) 130 sc.Logger.Dispatch(sc.ApplyContext(ctx), event) 131 } 132 133 // -------------------------------------------------------------------------------- 134 // Builtin Flag Handlers (infof, debugf etc.) 135 // -------------------------------------------------------------------------------- 136 137 // Info logs an informational message to the output stream. 138 func (sc Scope) Info(args ...interface{}) { 139 sc.Trigger(NewMessageEvent(Info, fmt.Sprint(args...), OptMessageRestricted(sc.Restricted))) 140 } 141 142 // InfoContext logs an informational message to the output stream in a given context. 143 func (sc Scope) InfoContext(ctx context.Context, args ...interface{}) { 144 sc.TriggerContext(ctx, NewMessageEvent(Info, fmt.Sprint(args...), OptMessageRestricted(sc.Restricted))) 145 } 146 147 // Infof logs an informational message to the output stream. 148 func (sc Scope) Infof(format string, args ...interface{}) { 149 sc.Trigger(NewMessageEvent(Info, fmt.Sprintf(format, args...), OptMessageRestricted(sc.Restricted))) 150 } 151 152 // InfofContext logs an informational message to the output stream in a given context. 153 func (sc Scope) InfofContext(ctx context.Context, format string, args ...interface{}) { 154 sc.TriggerContext(ctx, NewMessageEvent(Info, fmt.Sprintf(format, args...), OptMessageRestricted(sc.Restricted))) 155 } 156 157 // Debug logs a debug message to the output stream. 158 func (sc Scope) Debug(args ...interface{}) { 159 sc.Trigger(NewMessageEvent(Debug, fmt.Sprint(args...), OptMessageRestricted(sc.Restricted))) 160 } 161 162 // DebugContext logs a debug message to the output stream in a given context. 163 func (sc Scope) DebugContext(ctx context.Context, args ...interface{}) { 164 sc.TriggerContext(ctx, NewMessageEvent(Debug, fmt.Sprint(args...), OptMessageRestricted(sc.Restricted))) 165 } 166 167 // Debugf logs a debug message to the output stream. 168 func (sc Scope) Debugf(format string, args ...interface{}) { 169 sc.Trigger(NewMessageEvent(Debug, fmt.Sprintf(format, args...), OptMessageRestricted(sc.Restricted))) 170 } 171 172 // DebugfContext logs a debug message to the output stream. 173 func (sc Scope) DebugfContext(ctx context.Context, format string, args ...interface{}) { 174 sc.TriggerContext(ctx, NewMessageEvent(Debug, fmt.Sprintf(format, args...), OptMessageRestricted(sc.Restricted))) 175 } 176 177 // Warningf logs a warning message to the output stream. 178 func (sc Scope) Warningf(format string, args ...interface{}) { 179 sc.Trigger(NewErrorEvent(Warning, fmt.Errorf(format, args...), OptErrorEventRestricted(sc.Restricted))) 180 } 181 182 // WarningfContext logs a warning message to the output stream in a given context. 183 func (sc Scope) WarningfContext(ctx context.Context, format string, args ...interface{}) { 184 sc.TriggerContext(ctx, NewErrorEvent(Warning, fmt.Errorf(format, args...), OptErrorEventRestricted(sc.Restricted))) 185 } 186 187 // Errorf writes an event to the log and triggers event listeners. 188 func (sc Scope) Errorf(format string, args ...interface{}) { 189 sc.Trigger(NewErrorEvent(Error, fmt.Errorf(format, args...), OptErrorEventRestricted(sc.Restricted))) 190 } 191 192 // ErrorfContext writes an event to the log and triggers event listeners in a given context. 193 func (sc Scope) ErrorfContext(ctx context.Context, format string, args ...interface{}) { 194 sc.TriggerContext(ctx, NewErrorEvent(Error, fmt.Errorf(format, args...), OptErrorEventRestricted(sc.Restricted))) 195 } 196 197 // Fatalf writes an event to the log and triggers event listeners. 198 func (sc Scope) Fatalf(format string, args ...interface{}) { 199 sc.Trigger(NewErrorEvent(Fatal, fmt.Errorf(format, args...), OptErrorEventRestricted(sc.Restricted))) 200 } 201 202 // FatalfContext writes an event to the log and triggers event listeners in a given context. 203 func (sc Scope) FatalfContext(ctx context.Context, format string, args ...interface{}) { 204 sc.TriggerContext(ctx, NewErrorEvent(Fatal, fmt.Errorf(format, args...), OptErrorEventRestricted(sc.Restricted))) 205 } 206 207 // Warning logs a warning error to std err. 208 func (sc Scope) Warning(err error, opts ...ErrorEventOption) { 209 sc.Trigger(NewErrorEvent(Warning, err, opts...)) 210 } 211 212 // WarningContext logs a warning error to std err in a given context. 213 func (sc Scope) WarningContext(ctx context.Context, err error, opts ...ErrorEventOption) { 214 sc.TriggerContext(ctx, NewErrorEvent(Warning, err, opts...)) 215 } 216 217 // Error logs an error to std err. 218 func (sc Scope) Error(err error, opts ...ErrorEventOption) { 219 sc.Trigger(NewErrorEvent(Error, err, opts...)) 220 } 221 222 // ErrorContext logs an error to std err. 223 func (sc Scope) ErrorContext(ctx context.Context, err error, opts ...ErrorEventOption) { 224 sc.TriggerContext(ctx, NewErrorEvent(Error, err, opts...)) 225 } 226 227 // Fatal logs an error as fatal. 228 func (sc Scope) Fatal(err error, opts ...ErrorEventOption) { 229 sc.Trigger(NewErrorEvent(Fatal, err, opts...)) 230 } 231 232 // FatalContext logs an error as fatal. 233 func (sc Scope) FatalContext(ctx context.Context, err error, opts ...ErrorEventOption) { 234 sc.TriggerContext(ctx, NewErrorEvent(Fatal, err, opts...)) 235 } 236 237 // 238 // Context utilities 239 // 240 241 // FromContext returns a scope from a given context. 242 // It will read any relevant fields off the context (Path, Labels, Annotations) 243 // and append them to values already on the scope. 244 func (sc Scope) FromContext(ctx context.Context) Scope { 245 return NewScope(sc.Logger, 246 OptScopePath(append(GetPath(ctx), sc.Path...)...), 247 OptScopeLabels(sc.Labels, GetLabels(ctx)), 248 OptScopeAnnotations(sc.Annotations, GetAnnotations(ctx)), 249 OptScopeRestriction(sc.Restricted), 250 ) 251 } 252 253 // ApplyContext applies the scope fields to a given context. 254 func (sc Scope) ApplyContext(ctx context.Context) context.Context { 255 ctx = WithPath(ctx, append(GetPath(ctx), sc.Path...)...) 256 ctx = WithLabels(ctx, sc.Labels) // treated specially because maps are references 257 ctx = WithAnnotations(ctx, CombineAnnotations(sc.Annotations, GetAnnotations(ctx))) 258 ctx = WithRestricted(ctx, sc.Restricted) 259 return ctx 260 }