APEX 的改进点

问题

什么是 Handler/Formmater 和 Hook

  1. [root@liqiang.io]# 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. [root@liqiang.io]# 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. [root@liqiang.io]# 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. [root@liqiang.io]# 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. [root@liqiang.io]# 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. }

Ref