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  }