github.com/goravel/framework@v1.13.9/log/logrus_writer.go (about) 1 package log 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 8 "github.com/rotisserie/eris" 9 "github.com/sirupsen/logrus" 10 11 "github.com/goravel/framework/contracts/config" 12 "github.com/goravel/framework/contracts/http" 13 "github.com/goravel/framework/contracts/log" 14 "github.com/goravel/framework/log/formatter" 15 "github.com/goravel/framework/log/logger" 16 ) 17 18 type Writer struct { 19 code string 20 21 // context 22 context map[string]any 23 domain string 24 25 hint string 26 instance *logrus.Entry 27 message string 28 owner any 29 30 // http 31 request http.ContextRequest 32 response http.ContextResponse 33 34 // stacktrace 35 stackEnabled bool 36 stacktrace map[string]any 37 38 tags []string 39 trace string 40 41 // user 42 user any 43 } 44 45 func NewWriter(instance *logrus.Entry) log.Writer { 46 return &Writer{ 47 code: "", 48 49 // context 50 context: map[string]any{}, 51 domain: "", 52 53 hint: "", 54 instance: instance, 55 message: "", 56 owner: nil, 57 58 // http 59 request: nil, 60 response: nil, 61 62 // stacktrace 63 stackEnabled: false, 64 stacktrace: nil, 65 66 tags: []string{}, 67 trace: "", 68 69 // user 70 user: nil, 71 } 72 } 73 74 func (r *Writer) Debug(args ...any) { 75 r.instance.WithField("root", r.toMap()).Debug(args...) 76 } 77 78 func (r *Writer) Debugf(format string, args ...any) { 79 r.instance.WithField("root", r.toMap()).Debugf(format, args...) 80 } 81 82 func (r *Writer) Info(args ...any) { 83 r.instance.WithField("root", r.toMap()).Info(args...) 84 } 85 86 func (r *Writer) Infof(format string, args ...any) { 87 r.instance.WithField("root", r.toMap()).Infof(format, args...) 88 } 89 90 func (r *Writer) Warning(args ...any) { 91 r.instance.WithField("root", r.toMap()).Warning(args...) 92 } 93 94 func (r *Writer) Warningf(format string, args ...any) { 95 r.instance.WithField("root", r.toMap()).Warningf(format, args...) 96 } 97 98 func (r *Writer) Error(args ...any) { 99 r.withStackTrace(fmt.Sprint(args...)) 100 r.instance.WithField("root", r.toMap()).Error(args...) 101 } 102 103 func (r *Writer) Errorf(format string, args ...any) { 104 r.withStackTrace(fmt.Sprintf(format, args...)) 105 r.instance.WithField("root", r.toMap()).Errorf(format, args...) 106 } 107 108 func (r *Writer) Fatal(args ...any) { 109 r.withStackTrace(fmt.Sprint(args...)) 110 r.instance.WithField("root", r.toMap()).Fatal(args...) 111 } 112 113 func (r *Writer) Fatalf(format string, args ...any) { 114 r.withStackTrace(fmt.Sprintf(format, args...)) 115 r.instance.WithField("root", r.toMap()).Fatalf(format, args...) 116 } 117 118 func (r *Writer) Panic(args ...any) { 119 r.withStackTrace(fmt.Sprint(args...)) 120 r.instance.WithField("root", r.toMap()).Panic(args...) 121 } 122 123 func (r *Writer) Panicf(format string, args ...any) { 124 r.withStackTrace(fmt.Sprintf(format, args...)) 125 r.instance.WithField("root", r.toMap()).Panicf(format, args...) 126 } 127 128 // Code set a code or slug that describes the error. 129 // Error messages are intended to be read by humans, but such code is expected to 130 // be read by machines and even transported over different services. 131 func (r *Writer) Code(code string) log.Writer { 132 r.code = code 133 return r 134 } 135 136 // Hint set a hint for faster debugging. 137 func (r *Writer) Hint(hint string) log.Writer { 138 r.hint = hint 139 140 return r 141 } 142 143 // In sets the feature category or domain in which the log entry is relevant. 144 func (r *Writer) In(domain string) log.Writer { 145 r.domain = domain 146 147 return r 148 } 149 150 // Owner set the name/email of the colleague/team responsible for handling this error. 151 // Useful for alerting purpose. 152 func (r *Writer) Owner(owner any) log.Writer { 153 r.owner = owner 154 155 return r 156 } 157 158 // Request supplies a http.Request. 159 func (r *Writer) Request(req http.ContextRequest) log.Writer { 160 r.request = req 161 162 return r 163 } 164 165 // Response supplies a http.Response. 166 func (r *Writer) Response(res http.ContextResponse) log.Writer { 167 r.response = res 168 169 return r 170 } 171 172 // Tags add multiple tags, describing the feature returning an error. 173 func (r *Writer) Tags(tags ...string) log.Writer { 174 r.tags = append(r.tags, tags...) 175 176 return r 177 } 178 179 // User sets the user associated with the log entry. 180 func (r *Writer) User(user any) log.Writer { 181 r.user = user 182 return r 183 } 184 185 // With adds key-value pairs to the context of the log entry 186 func (r *Writer) With(data map[string]any) log.Writer { 187 for k, v := range data { 188 r.context[k] = v 189 } 190 191 return r 192 } 193 194 func (r *Writer) withStackTrace(message string) { 195 erisNew := eris.New(message) 196 r.message = erisNew.Error() 197 format := eris.NewDefaultJSONFormat(eris.FormatOptions{ 198 InvertOutput: true, 199 WithTrace: true, 200 InvertTrace: true, 201 }) 202 r.stacktrace = eris.ToCustomJSON(erisNew, format) 203 r.stackEnabled = true 204 } 205 206 // ToMap returns a map representation of the error. 207 func (r *Writer) toMap() map[string]any { 208 payload := map[string]any{} 209 210 if message := r.message; message != "" { 211 payload["message"] = message 212 } 213 214 if code := r.code; code != "" { 215 payload["code"] = code 216 } 217 218 if domain := r.domain; domain != "" { 219 payload["domain"] = domain 220 } 221 222 if tags := r.tags; len(tags) > 0 { 223 payload["tags"] = tags 224 } 225 226 if context := r.context; len(context) > 0 { 227 payload["context"] = context 228 } 229 230 if trace := r.trace; trace != "" { 231 payload["trace"] = trace 232 } 233 234 if hint := r.hint; hint != "" { 235 payload["hint"] = hint 236 } 237 238 if owner := r.owner; owner != nil { 239 payload["owner"] = owner 240 } 241 242 if r.user != nil { 243 payload["user"] = r.user 244 } 245 246 if req := r.request; req != nil { 247 payload["request"] = map[string]any{ 248 "method": req.Method(), 249 "uri": req.FullUrl(), 250 "header": req.Headers(), 251 "body": req.All(), 252 } 253 } 254 255 if res := r.response; res != nil { 256 payload["response"] = map[string]any{ 257 "status": res.Origin().Status(), 258 "header": res.Origin().Header(), 259 "body": res.Origin().Body(), 260 "size": res.Origin().Size(), 261 } 262 } 263 264 if stacktrace := r.stacktrace; stacktrace != nil || r.stackEnabled { 265 payload["stacktrace"] = stacktrace 266 } 267 268 return payload 269 } 270 271 func registerHook(config config.Config, instance *logrus.Logger, channel string) error { 272 channelPath := "logging.channels." + channel 273 driver := config.GetString(channelPath + ".driver") 274 275 var hook logrus.Hook 276 var err error 277 switch driver { 278 case log.StackDriver: 279 for _, stackChannel := range config.Get(channelPath + ".channels").([]string) { 280 if stackChannel == channel { 281 return errors.New("stack drive can't include self channel") 282 } 283 284 if err := registerHook(config, instance, stackChannel); err != nil { 285 return err 286 } 287 } 288 289 return nil 290 case log.SingleDriver: 291 if !config.GetBool(channelPath + ".print") { 292 instance.SetOutput(io.Discard) 293 } 294 295 logLogger := logger.NewSingle(config) 296 hook, err = logLogger.Handle(channelPath) 297 if err != nil { 298 return err 299 } 300 case log.DailyDriver: 301 if !config.GetBool(channelPath + ".print") { 302 instance.SetOutput(io.Discard) 303 } 304 305 logLogger := logger.NewDaily(config) 306 hook, err = logLogger.Handle(channelPath) 307 if err != nil { 308 return err 309 } 310 case log.CustomDriver: 311 logLogger := config.Get(channelPath + ".via").(log.Logger) 312 logHook, err := logLogger.Handle(channelPath) 313 if err != nil { 314 return err 315 } 316 317 hook = &Hook{logHook} 318 default: 319 return errors.New("Error logging channel: " + channel) 320 } 321 322 instance.SetFormatter(formatter.NewGeneral(config)) 323 324 instance.AddHook(hook) 325 326 return nil 327 } 328 329 type Hook struct { 330 instance log.Hook 331 } 332 333 func (h *Hook) Levels() []logrus.Level { 334 levels := h.instance.Levels() 335 var logrusLevels []logrus.Level 336 for _, item := range levels { 337 logrusLevels = append(logrusLevels, logrus.Level(item)) 338 } 339 340 return logrusLevels 341 } 342 343 func (h *Hook) Fire(entry *logrus.Entry) error { 344 return h.instance.Fire(&Entry{ 345 ctx: entry.Context, 346 level: log.Level(entry.Level), 347 time: entry.Time, 348 message: entry.Message, 349 }) 350 }