1 package log 2 3 import ( 4 "context" 5 6 "github.com/sirupsen/logrus" 7 "go.opencensus.io/trace" 8 ) 9 10 type entryContextKeyType int 11 12 const _entryContextKey entryContextKeyType = iota 13 14 var ( 15 // L is the default, blank logging entry. WithField and co. all return a copy 16 // of the original entry, so this will not leak fields between calls. 17 // 18 // Do NOT modify fields directly, as that will corrupt state for all users and 19 // is not thread safe. 20 // Instead, use `L.With*` or `L.Dup()`. Or `G(context.Background())`. 21 L = logrus.NewEntry(logrus.StandardLogger()) 22 23 // G is an alias for GetEntry 24 G = GetEntry 25 26 // S is an alias for SetEntry 27 S = SetEntry 28 29 // U is an alias for UpdateContext 30 U = UpdateContext 31 ) 32 33 // GetEntry returns a `logrus.Entry` stored in the context, if one exists. 34 // Otherwise, it returns a default entry that points to the current context. 35 // 36 // Note: if the a new entry is returned, it will reference the passed in context. 37 // However, existing contexts may be stored in parent contexts and additionally reference 38 // earlier contexts. 39 // Use `UpdateContext` to update the entry and context. 40 func GetEntry(ctx context.Context) *logrus.Entry { 41 entry := fromContext(ctx) 42 43 if entry == nil { 44 entry = L.WithContext(ctx) 45 } 46 47 return entry 48 } 49 50 // SetEntry updates the log entry in the context with the provided fields, and 51 // returns both. It is equivalent to: 52 // 53 // entry := GetEntry(ctx).WithFields(fields) 54 // ctx = WithContext(ctx, entry) 55 // 56 // See WithContext for more information. 57 func SetEntry(ctx context.Context, fields logrus.Fields) (context.Context, *logrus.Entry) { 58 e := GetEntry(ctx) 59 if len(fields) > 0 { 60 e = e.WithFields(fields) 61 } 62 return WithContext(ctx, e) 63 } 64 65 // UpdateContext extracts the log entry from the context, and, if the entry's 66 // context points to a parent's of the current context, ands the entry 67 // to the most recent context. It is equivalent to: 68 // 69 // entry := GetEntry(ctx) 70 // ctx = WithContext(ctx, entry) 71 // 72 // This allows the entry to reference the most recent context and any new 73 // values (such as span contexts) added to it. 74 // 75 // See WithContext for more information. 76 func UpdateContext(ctx context.Context) context.Context { 77 // there is no way to check its ctx (and not one of its parents) that contains `e` 78 // so, at a slight cost, force add `e` to the context 79 ctx, _ = WithContext(ctx, GetEntry(ctx)) 80 return ctx 81 } 82 83 // WithContext returns a context that contains the provided log entry. 84 // The entry can be extracted with `GetEntry` (`G`) 85 // 86 // The entry in the context is a copy of `entry` (generated by `entry.WithContext`) 87 func WithContext(ctx context.Context, entry *logrus.Entry) (context.Context, *logrus.Entry) { 88 // regardless of the order, entry.Context != GetEntry(ctx) 89 // here, the returned entry will reference the supplied context 90 entry = entry.WithContext(ctx) 91 ctx = context.WithValue(ctx, _entryContextKey, entry) 92 93 return ctx, entry 94 } 95 96 // Copy extracts the tracing Span and logging entry from the src Context, if they 97 // exist, and adds them to the dst Context. 98 // 99 // This is useful to share tracing and logging between contexts, but not the 100 // cancellation. For example, if the src Context has been cancelled but cleanup 101 // operations triggered by the cancellation require a non-cancelled context to 102 // execute. 103 func Copy(dst context.Context, src context.Context) context.Context { 104 if s := trace.FromContext(src); s != nil { 105 dst = trace.NewContext(dst, s) 106 } 107 108 if e := fromContext(src); e != nil { 109 dst, _ = WithContext(dst, e) 110 } 111 112 return dst 113 } 114 115 func fromContext(ctx context.Context) *logrus.Entry { 116 e, _ := ctx.Value(_entryContextKey).(*logrus.Entry) 117 return e 118 } 119