1

APEX Log 总结

 2 years ago
source link: https://liqiang.io/post/summary-for-apex-logger?lang=ZH_CN
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

APEX 的改进点

  • Simplified handlers, no formatter/hook distinction
  • log.Interface shares the Logger and Entry method set
  • Built-in handlers (text, json, cli, discard, logfmt, memory, kinesis, multi)
  • Tracing support

什么是 Handler/Formmater 和 Hook

  • A handler is the mechanism which processes log entries, without one apex/log isn’t very useful.
  1. [[email protected]]# cat handler.go
  2. type Handler interface {
  3. HandleLog(*Entry) error
  4. }

Traceing support 是怎么做的,做到什么程度,有必要?

看 demo 是只做了一个执行时间的计算,没看到更多的演示和介绍,等下看代码试试。

log.Entry 的编码和解码是怎么做到的

Apex log 中吐槽了 Logrus 的日志 json 解析很 tricky,那么他又是怎么做到不 tricky 的呢,吐槽点:

This might not sound like a big deal, but if you want to serialize, ship, and deserialize JSON logs with the same schema, then this sort of normalization is not very Go-friendly. To produce the same *logrus.Entry on the consumer-side you’d have to unmarshal to a map, assign the three primary fields, loop the rest and assign those back to the entry. It’s not a huge problem, but it’s not ideal.

它吐槽的是这样的格式:

  1. [[email protected]]# cat logrus.json
  2. {
  3. "time": ...,
  4. "msg": ...,
  5. "level": ...,
  6. <field>: ...
  7. }

槽点在于 field 是动态的字段,前面有 3 个固定的字段,然后动态的 field 字段需要构造一个 map 来解析;那么 apex/log 的解决方式虽然我直觉上觉得也没有很完美,但是,似乎也找不到什么反驳点来吐槽,至少,我能看到的一个优点是,在解析的时候,你确实不需要比较 map 里面的 key 是不是 3 个固定字段中的一个,而是一股脑地直接利用它的 key 和 value 就可以了:

  1. [[email protected]]# cat apexlog.go
  2. {
  3. "fields":{
  4. "file":"something.png",
  5. "type":"image/png",
  6. "user":"tobi"
  7. },
  8. "level":"info",
  9. "timestamp":"2021-07-26T11:59:04.639931+08:00",
  10. "message":"upload"
  11. }

Logger / Handler / Entry 的关系

  1. [[email protected]]# cat logger.go
  2. type Logger struct {
  3. Handler Handler
  4. Level Level
  5. }
  6. type Handler interface {
  7. HandleLog(*Entry) error
  8. }
  9. // WithFields returns a new entry with `fields` set.
  10. func (l *Logger) WithFields(fields Fielder) *Entry {
  11. return NewEntry(l).WithFields(fields.Fields())
  12. }
  13. // Info level message.
  14. func (l *Logger) Info(msg string) {
  15. NewEntry(l).Info(msg)
  16. }
  17. // log the message, invoking the handler. We clone the entry here
  18. // to bypass the overhead in Entry methods when the level is not
  19. // met.
  20. func (l *Logger) log(level Level, e *Entry, msg string) {
  21. if level < l.Level {
  22. return
  23. }
  24. if err := l.Handler.HandleLog(e.finalize(level, msg)); err != nil {
  25. stdlog.Printf("error logging: %s", err)
  26. }
  27. }
  28. // Entry represents a single log entry.
  29. type Entry struct {
  30. Logger *Logger `json:"-"`
  31. Fields Fields `json:"fields"`
  32. Level Level `json:"level"`
  33. Timestamp time.Time `json:"timestamp"`
  34. Message string `json:"message"`
  35. start time.Time
  36. fields []Fields
  37. }
  38. // Info level message.
  39. func (e *Entry) Info(msg string) {
  40. e.Logger.log(InfoLevel, e, msg)
  41. }

Multi Handler 实现

  1. [[email protected]]# cat handlers/multi/multi.go
  2. // New handler.
  3. func New(h ...log.Handler) *Handler {
  4. return &Handler{
  5. Handlers: h,
  6. }
  7. }
  8. // HandleLog implements log.Handler.
  9. func (h *Handler) HandleLog(e *log.Entry) error {
  10. for _, handler := range h.Handlers {
  11. // TODO(tj): maybe just write to stderr here, definitely not ideal
  12. // to miss out logging to a more critical handler if something
  13. // goes wrong
  14. if err := handler.HandleLog(e); err != nil {
  15. return err
  16. }
  17. }
  18. return nil
  19. }

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK