github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/pkg/log/configure.go (about) 1 /* 2 * Copyright 2020 The Compass Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package log 18 19 import ( 20 "context" 21 "fmt" 22 "io" 23 "os" 24 "path/filepath" 25 "strings" 26 "sync" 27 28 "github.com/onrik/logrus/filename" 29 30 "github.com/sirupsen/logrus" 31 ) 32 33 type logKey struct{} 34 35 const ( 36 // FieldRequestID missing godoc 37 FieldRequestID = "x-request-id" 38 fieldComponentName = "component" 39 jsonFormatterKey = "json" 40 textFormatterKey = "text" 41 ) 42 43 var ( 44 defaultEntry = logrus.NewEntry(logrus.StandardLogger()) 45 46 supportedFormatters = map[string]logrus.Formatter{ 47 jsonFormatterKey: &logrus.JSONFormatter{}, 48 textFormatterKey: &logrus.TextFormatter{}, 49 } 50 51 supportedOutputs = map[string]io.Writer{ 52 os.Stdout.Name(): os.Stdout, 53 os.Stderr.Name(): os.Stderr, 54 } 55 mutex = sync.RWMutex{} 56 currentSettings = DefaultConfig() 57 58 // C missing godoc 59 C = LoggerFromContext 60 // D missing godoc 61 D = DefaultLogger 62 ) 63 64 func init() { 65 // Configure default logger in init so we can log even before actual logging settings are loaded 66 hook := prepareLogSourceHook() 67 AddHook(hook) 68 AddHook(&ErrorLocationHook{}) 69 defaultEntry = defaultEntry.WithField(FieldRequestID, currentSettings.BootstrapCorrelationID) 70 71 _, err := Configure(context.Background(), currentSettings) 72 if err != nil { 73 panic(err) 74 } 75 } 76 77 // Configure creates a new context with a logger using the provided settings. 78 func Configure(ctx context.Context, config *Config) (context.Context, error) { 79 mutex.Lock() 80 defer mutex.Unlock() 81 82 if err := config.Validate(); err != nil { 83 return nil, err 84 } 85 86 level, err := logrus.ParseLevel(config.Level) 87 if err != nil { 88 return nil, err 89 } 90 formatter := supportedFormatters[config.Format] 91 output := supportedOutputs[config.Output] 92 93 currentSettings = config 94 95 entry := ctx.Value(logKey{}) 96 if entry == nil { 97 entry = defaultEntry 98 } else { 99 defaultEntry.Logger.SetOutput(output) 100 defaultEntry.Logger.SetFormatter(formatter) 101 defaultEntry.Logger.SetLevel(level) 102 } 103 e := copyEntry(entry.(*logrus.Entry)) 104 e.Logger.SetLevel(level) 105 e.Logger.SetFormatter(formatter) 106 e.Logger.SetOutput(output) 107 108 return ContextWithLogger(ctx, e), nil 109 } 110 111 // Configuration returns the logger settings 112 func Configuration() Config { 113 mutex.RLock() 114 defer mutex.RUnlock() 115 116 return *currentSettings 117 } 118 119 // ContextWithLogger returns a new context with the provided logger. 120 func ContextWithLogger(ctx context.Context, entry *logrus.Entry) context.Context { 121 return context.WithValue(ctx, logKey{}, entry) 122 } 123 124 // LoggerFromContext retrieves the current logger from the context. 125 func LoggerFromContext(ctx context.Context) *logrus.Entry { 126 mutex.RLock() 127 defer mutex.RUnlock() 128 entry := ctx.Value(logKey{}) 129 if entry == nil { 130 entry = defaultEntry 131 } 132 return copyEntry(entry.(*logrus.Entry)) 133 } 134 135 // DefaultLogger returns the default logger 136 func DefaultLogger() *logrus.Entry { 137 return LoggerFromContext(context.Background()) 138 } 139 140 // RegisterFormatter registers a new logrus Formatter with the given name. 141 // Returns an error if there is a formatter with the same name. 142 func RegisterFormatter(name string, formatter logrus.Formatter) error { 143 if _, exists := supportedFormatters[name]; exists { 144 return fmt.Errorf("formatter with name %s is already registered", name) 145 } 146 supportedFormatters[name] = formatter 147 return nil 148 } 149 150 // AddHook adds a hook to all loggers 151 func AddHook(hook logrus.Hook) { 152 defaultEntry.Logger.AddHook(hook) 153 } 154 155 func copyEntry(entry *logrus.Entry) *logrus.Entry { 156 entryData := make(logrus.Fields, len(entry.Data)) 157 for k, v := range entry.Data { 158 entryData[k] = v 159 } 160 161 newEntry := logrus.NewEntry(entry.Logger) 162 newEntry.Level = entry.Level 163 newEntry.Data = entryData 164 newEntry.Time = entry.Time 165 newEntry.Message = entry.Message 166 newEntry.Buffer = entry.Buffer 167 168 return newEntry 169 } 170 171 func prepareLogSourceHook() *filename.Hook { 172 hook := filename.NewHook() 173 hook.Field = fieldComponentName 174 hook.SkipPrefixes = append(hook.SkipPrefixes, "log/") 175 hook.Formatter = func(file, function string, line int) string { 176 split := strings.Split(function, string(filepath.Separator)) 177 return fmt.Sprintf("%s:%d:%s", file, line, split[len(split)-1]) 178 } 179 180 return hook 181 }