...

Source file src/github.com/ory/x/logrusx/helper.go

Documentation: github.com/ory/x/logrusx

     1  package logrusx
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"net/http"
     8  	"reflect"
     9  	"strings"
    10  
    11  	"github.com/gobuffalo/pop/v5/logging"
    12  
    13  	"github.com/sirupsen/logrus"
    14  
    15  	"go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace"
    16  	"go.opentelemetry.io/otel/propagation"
    17  
    18  	"github.com/ory/x/errorsx"
    19  )
    20  
    21  type Logger struct {
    22  	*logrus.Entry
    23  	leakSensitive bool
    24  	opts          []Option
    25  	name          string
    26  	version       string
    27  }
    28  
    29  var opts = otelhttptrace.WithPropagators(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
    30  
    31  func (l *Logger) LeakSensitiveData() bool {
    32  	return l.leakSensitive
    33  }
    34  
    35  func (l *Logger) Logrus() *logrus.Logger {
    36  	return l.Entry.Logger
    37  }
    38  
    39  func (l *Logger) NewEntry() *Logger {
    40  	ll := *l
    41  	ll.Entry = logrus.NewEntry(l.Logger)
    42  	return &ll
    43  }
    44  
    45  func (l *Logger) WithContext(ctx context.Context) *Logger {
    46  	ll := *l
    47  	ll.Entry = l.Logger.WithContext(ctx)
    48  	return &ll
    49  }
    50  
    51  func (l *Logger) WithRequest(r *http.Request) *Logger {
    52  	headers := map[string]interface{}{}
    53  	if ua := r.UserAgent(); len(ua) > 0 {
    54  		headers["user-agent"] = ua
    55  	}
    56  
    57  	if cookie := l.maybeRedact(r.Header.Get("Cookie")); cookie != nil {
    58  		headers["cookie"] = cookie
    59  	}
    60  
    61  	if auth := l.maybeRedact(r.Header.Get("Authorization")); auth != nil {
    62  		headers["authorization"] = auth
    63  	}
    64  
    65  	for _, key := range []string{"Referer", "Origin", "Accept", "X-Request-ID", "If-None-Match",
    66  		"X-Forwarded-For", "X-Forwarded-Proto", "Cache-Control", "Accept-Encoding", "Accept-Language", "If-Modified-Since"} {
    67  		if value := r.Header.Get(key); len(value) > 0 {
    68  			headers[strings.ToLower(key)] = value
    69  		}
    70  	}
    71  
    72  	scheme := "https"
    73  	if r.TLS == nil {
    74  		scheme = "http"
    75  	}
    76  
    77  	ll := l.WithField("http_request", map[string]interface{}{
    78  		"remote":  r.RemoteAddr,
    79  		"method":  r.Method,
    80  		"path":    r.URL.EscapedPath(),
    81  		"query":   l.maybeRedact(r.URL.RawQuery),
    82  		"scheme":  scheme,
    83  		"host":    r.Host,
    84  		"headers": headers,
    85  	})
    86  
    87  	if _, _, spanCtx := otelhttptrace.Extract(r.Context(), r, opts); spanCtx.IsValid() {
    88  		traces := map[string]string{}
    89  		if spanCtx.HasTraceID() {
    90  			traces["trace_id"] = spanCtx.TraceID.String()
    91  		}
    92  		if spanCtx.HasSpanID() {
    93  			traces["span_id"] = spanCtx.SpanID.String()
    94  		}
    95  		ll = ll.WithField("otel", traces)
    96  	}
    97  
    98  	return ll
    99  }
   100  
   101  func (l *Logger) WithFields(f logrus.Fields) *Logger {
   102  	ll := *l
   103  	ll.Entry = l.Entry.WithFields(f)
   104  	return &ll
   105  }
   106  
   107  func (l *Logger) WithField(key string, value interface{}) *Logger {
   108  	ll := *l
   109  	ll.Entry = l.Entry.WithField(key, value)
   110  	return &ll
   111  }
   112  
   113  func (l *Logger) maybeRedact(value interface{}) interface{} {
   114  	if fmt.Sprintf("%v", value) == "" || value == nil {
   115  		return nil
   116  	}
   117  	if !l.leakSensitive {
   118  		return `Value is sensitive and has been redacted. To see the value set config key "log.leak_sensitive_values = true" or environment variable "LOG_LEAK_SENSITIVE_VALUES=true".`
   119  	}
   120  	return value
   121  }
   122  
   123  func (l *Logger) WithSensitiveField(key string, value interface{}) *Logger {
   124  	return l.WithField(key, l.maybeRedact(value))
   125  }
   126  
   127  func (l *Logger) WithError(err error) *Logger {
   128  	ctx := map[string]interface{}{"message": err.Error()}
   129  	if l.Entry.Logger.IsLevelEnabled(logrus.TraceLevel) {
   130  		if e, ok := err.(errorsx.StackTracer); ok {
   131  			ctx["trace"] = fmt.Sprintf("%+v", e.StackTrace())
   132  		} else {
   133  			ctx["trace"] = fmt.Sprintf("stack trace could not be recovered from error type %s", reflect.TypeOf(err))
   134  		}
   135  	}
   136  	if c := errorsx.ReasonCarrier(nil); errors.As(err, &c) {
   137  		ctx["reason"] = c.Reason()
   138  	}
   139  	if c := errorsx.RequestIDCarrier(nil); errors.As(err, &c) && c.RequestID() != "" {
   140  		ctx["request_id"] = c.RequestID()
   141  	}
   142  	if c := errorsx.DetailsCarrier(nil); errors.As(err, &c) && c.Details() != nil {
   143  		ctx["details"] = c.Details()
   144  	}
   145  	if c := errorsx.StatusCarrier(nil); errors.As(err, &c) && c.Status() != "" {
   146  		ctx["status"] = c.Status()
   147  	}
   148  	if c := errorsx.StatusCodeCarrier(nil); errors.As(err, &c) && c.StatusCode() != 0 {
   149  		ctx["status_code"] = c.StatusCode()
   150  	}
   151  	if c := errorsx.DebugCarrier(nil); errors.As(err, &c) {
   152  		ctx["debug"] = c.Debug()
   153  	}
   154  
   155  	return l.WithField("error", ctx)
   156  }
   157  
   158  var popLevelTranslations = map[logging.Level]logrus.Level{
   159  	// logging.SQL:   logrus.TraceLevel, we never want to log SQL statements, see https://github.com/ory/keto/issues/454
   160  	logging.Debug: logrus.DebugLevel,
   161  	logging.Info:  logrus.InfoLevel,
   162  	logging.Warn:  logrus.WarnLevel,
   163  	logging.Error: logrus.ErrorLevel,
   164  }
   165  
   166  func (l *Logger) PopLogger(lvl logging.Level, s string, args ...interface{}) {
   167  	level, ok := popLevelTranslations[lvl]
   168  	if ok {
   169  		l.WithField("source", "pop").Logf(level, s, args...)
   170  	}
   171  }
   172  

View as plain text