github.com/mendersoftware/go-lib-micro@v0.0.0-20240304135804-e8e39c59b148/accesslog/context.go (about) 1 // Copyright 2024 Northern.tech AS 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package accesslog 16 17 import ( 18 "context" 19 "strings" 20 "sync" 21 ) 22 23 const ( 24 DefaultMaxErrors = 5 25 ) 26 27 type AccessLogFormat string 28 29 type LogContext interface { 30 PushError(err error) bool 31 SetField(key string, value interface{}) 32 } 33 34 type logContext struct { 35 errors []error 36 mu sync.Mutex 37 maxErrors int 38 fields map[string]interface{} 39 } 40 41 func (c *logContext) SetField(key string, value interface{}) { 42 c.mu.Lock() 43 defer c.mu.Unlock() 44 if c.fields == nil { 45 c.fields = make(map[string]interface{}) 46 } 47 c.fields[key] = value 48 } 49 50 func (c *logContext) PushError(err error) bool { 51 if c.maxErrors > 0 { 52 c.mu.Lock() 53 defer c.mu.Unlock() 54 if len(c.errors) > c.maxErrors { 55 return false 56 } 57 } 58 c.errors = append(c.errors, err) 59 return true 60 } 61 62 func (c *logContext) addFields(fields map[string]interface{}) { 63 if c == nil { 64 return 65 } 66 switch len(c.errors) { 67 case 0: 68 case 1: 69 if c.errors[0] != nil { 70 fields["error"] = c.errors[0].Error() 71 } 72 default: 73 var s strings.Builder 74 for i, err := range c.errors { 75 if err != nil { 76 s.WriteString(err.Error()) 77 if i < len(c.errors)-1 { 78 s.WriteString("; ") 79 } 80 } 81 } 82 fields["error"] = s.String() 83 } 84 for key, value := range c.fields { 85 fields[key] = value 86 } 87 } 88 89 type logContextKey struct{} 90 91 func withContext(ctx context.Context, c *logContext) context.Context { 92 return context.WithValue(ctx, logContextKey{}, c) 93 } 94 95 func fromContext(ctx context.Context) *logContext { 96 if c, ok := ctx.Value(logContextKey{}).(*logContext); ok && c != nil { 97 return c 98 } 99 return nil 100 } 101 102 func GetContext(ctx context.Context) LogContext { 103 if lc := fromContext(ctx); lc != nil { 104 return lc 105 } 106 return nil 107 }