github.com/blend/go-sdk@v1.20240719.1/logger/context.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  	"time"
    13  )
    14  
    15  type loggerKey struct{}
    16  
    17  // WithLogger adds the logger to a context.
    18  func WithLogger(ctx context.Context, log Log) context.Context {
    19  	return context.WithValue(ctx, loggerKey{}, log)
    20  }
    21  
    22  // GetLogger gets a logger off a context.
    23  func GetLogger(ctx context.Context) Log {
    24  	if value := ctx.Value(loggerKey{}); value != nil {
    25  		if typed, ok := value.(Log); ok {
    26  			return typed
    27  		}
    28  	}
    29  	return nil
    30  }
    31  
    32  type triggerTimestampKey struct{}
    33  
    34  // WithTriggerTimestamp returns a new context with a given timestamp value.
    35  // It is used by the scope to connote when an event was triggered.
    36  func WithTriggerTimestamp(ctx context.Context, ts time.Time) context.Context {
    37  	return context.WithValue(ctx, triggerTimestampKey{}, ts)
    38  }
    39  
    40  // GetTriggerTimestamp gets when an event was triggered off a context.
    41  func GetTriggerTimestamp(ctx context.Context) time.Time {
    42  	if raw := ctx.Value(triggerTimestampKey{}); raw != nil {
    43  		if typed, ok := raw.(time.Time); ok {
    44  			return typed
    45  		}
    46  	}
    47  	return time.Time{}
    48  }
    49  
    50  type timestampKey struct{}
    51  
    52  // WithTimestamp returns a new context with a given timestamp value.
    53  func WithTimestamp(ctx context.Context, ts time.Time) context.Context {
    54  	return context.WithValue(ctx, timestampKey{}, ts)
    55  }
    56  
    57  // GetTimestamp gets a timestampoff a context.
    58  func GetTimestamp(ctx context.Context) time.Time {
    59  	if raw := ctx.Value(timestampKey{}); raw != nil {
    60  		if typed, ok := raw.(time.Time); ok {
    61  			return typed
    62  		}
    63  	}
    64  	return time.Time{}
    65  }
    66  
    67  type pathKey struct{}
    68  
    69  // WithPath returns a new context with a given path segment(s).
    70  //
    71  // NOTE: This overwrites any _existing_ path context values.
    72  //
    73  // If you want to _append_ path segments, use `logger.WithPathAppend(...)`.
    74  func WithPath(ctx context.Context, path ...string) context.Context {
    75  	return context.WithValue(ctx, pathKey{}, path)
    76  }
    77  
    78  // WithPathAppend appends a given path segment to a context.
    79  func WithPathAppend(ctx context.Context, path ...string) context.Context {
    80  	return context.WithValue(ctx, pathKey{}, append(GetPath(ctx), path...))
    81  }
    82  
    83  // GetPath gets a path off a context.
    84  func GetPath(ctx context.Context) []string {
    85  	if raw := ctx.Value(pathKey{}); raw != nil {
    86  		if typed, ok := raw.([]string); ok {
    87  			return typed
    88  		}
    89  	}
    90  	return nil
    91  }
    92  
    93  type labelsKey struct{}
    94  
    95  // WithSetLabels sets the labels on a context, overwriting any existing labels.
    96  func WithSetLabels(ctx context.Context, labels Labels) context.Context {
    97  	return context.WithValue(ctx, labelsKey{}, labels)
    98  }
    99  
   100  // WithLabels returns a new context with a given additional labels.
   101  //
   102  // Any labels already on the context will be added to the new set
   103  // with the provided set being layered on top of those.
   104  func WithLabels(ctx context.Context, labels Labels) context.Context {
   105  	// it is critical here that we copy the labels
   106  	// and not mutate the existing map
   107  	combinedLabels := GetLabels(ctx)
   108  	for key, value := range labels {
   109  		combinedLabels[key] = value
   110  	}
   111  	return context.WithValue(ctx, labelsKey{}, combinedLabels)
   112  }
   113  
   114  // WithLabel returns a new context with a given additional label.
   115  //
   116  // Any labels already on the context will be added to the new set
   117  // with the provided set being layered on top of those.
   118  func WithLabel(ctx context.Context, key, value string) context.Context {
   119  	newLabels := make(Labels)
   120  
   121  	// it is critical here that we copy the labels
   122  	// and not mutate the existing map.
   123  	existing := GetLabels(ctx)
   124  	for key, value := range existing {
   125  		newLabels[key] = value
   126  	}
   127  
   128  	// we assign after as the new value should overwrite
   129  	newLabels[key] = value
   130  	return context.WithValue(ctx, labelsKey{}, newLabels)
   131  }
   132  
   133  // GetLabels gets labels off a context.
   134  //
   135  // It will return a copy of the labels, preventing map races.
   136  func GetLabels(ctx context.Context) Labels {
   137  	if raw := ctx.Value(labelsKey{}); raw != nil {
   138  		if typed, ok := raw.(Labels); ok {
   139  			// create a copy
   140  			output := make(Labels)
   141  			for key, value := range typed {
   142  				output[key] = value
   143  			}
   144  			return output
   145  		}
   146  	}
   147  	return make(Labels)
   148  }
   149  
   150  // GetLabel gets a label off a context by key.
   151  func GetLabel(ctx context.Context, key string) (string, bool) {
   152  	if raw := ctx.Value(labelsKey{}); raw != nil {
   153  		if typed, ok := raw.(Labels); ok {
   154  			if value, found := typed[key]; found {
   155  				return value, true
   156  			}
   157  		}
   158  	}
   159  	return "", false
   160  }
   161  
   162  type annotationsKey struct{}
   163  
   164  // WithAnnotations returns a new context with a given additional annotations.
   165  func WithAnnotations(ctx context.Context, annotations Annotations) context.Context {
   166  	return context.WithValue(ctx, annotationsKey{}, annotations)
   167  }
   168  
   169  // WithAnnotation returns a new context with a given additional annotation.
   170  func WithAnnotation(ctx context.Context, key string, value interface{}) context.Context {
   171  	existing := GetAnnotations(ctx)
   172  	existing[key] = value
   173  	return context.WithValue(ctx, annotationsKey{}, existing)
   174  }
   175  
   176  // GetAnnotations gets annotations off a context.
   177  func GetAnnotations(ctx context.Context) Annotations {
   178  	if raw := ctx.Value(annotationsKey{}); raw != nil {
   179  		if typed, ok := raw.(Annotations); ok {
   180  			// create a copy
   181  			output := make(Annotations)
   182  			for key, value := range typed {
   183  				output[key] = value
   184  			}
   185  			return output
   186  		}
   187  	}
   188  	return make(Annotations)
   189  }
   190  
   191  type restrictedKey struct{}
   192  
   193  // WithRestricted returns a new context with the restricted value.
   194  func WithRestricted(ctx context.Context, restricted bool) context.Context {
   195  	return context.WithValue(ctx, restrictedKey{}, restricted)
   196  }
   197  
   198  // GetRestricted gets restricted value from a context.
   199  func GetRestricted(ctx context.Context) bool {
   200  	if raw := ctx.Value(restrictedKey{}); raw != nil {
   201  		if typed, ok := raw.(bool); ok {
   202  			return typed
   203  		}
   204  	}
   205  	return false
   206  }
   207  
   208  type skipTriggerKey struct{}
   209  
   210  type skipWriteKey struct{}
   211  
   212  // WithSkipTrigger sets the context to skip logger listener triggers.
   213  // The event will still be written unless you also use `WithSkipWrite`.
   214  func WithSkipTrigger(ctx context.Context, skipTrigger bool) context.Context {
   215  	return context.WithValue(ctx, skipTriggerKey{}, skipTrigger)
   216  }
   217  
   218  // WithSkipWrite sets the context to skip writing the event to the output stream.
   219  // The event will still trigger listeners unless you also use `WithSkipTrigger`.
   220  func WithSkipWrite(ctx context.Context, skipWrite bool) context.Context {
   221  	return context.WithValue(ctx, skipWriteKey{}, skipWrite)
   222  }
   223  
   224  // IsSkipTrigger returns if we should skip triggering logger listeners for a context.
   225  func IsSkipTrigger(ctx context.Context) bool {
   226  	if v := ctx.Value(skipTriggerKey{}); v != nil {
   227  		if typed, ok := v.(bool); ok {
   228  			return typed
   229  		}
   230  	}
   231  	return false
   232  }
   233  
   234  // IsSkipWrite returns if we should skip writing to the event stream for a context.
   235  func IsSkipWrite(ctx context.Context) bool {
   236  	if v := ctx.Value(skipWriteKey{}); v != nil {
   237  		if typed, ok := v.(bool); ok {
   238  			return typed
   239  		}
   240  	}
   241  	return false
   242  }