format.go 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. package log
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/json"
  6. "fmt"
  7. "net"
  8. "reflect"
  9. "time"
  10. )
  11. // TimeFormat is [time.RFC3339Nano] with nanoseconds padded using
  12. // zeros to ensure the formatted time is always the same number of
  13. // characters.
  14. // Based on RFC3339NanoFixed from github.com/containerd/log
  15. const TimeFormat = "2006-01-02T15:04:05.000000000Z07:00"
  16. func FormatTime(t time.Time) string {
  17. return t.Format(TimeFormat)
  18. }
  19. // DurationFormat formats a [time.Duration] log entry.
  20. //
  21. // A nil value signals an error with the formatting.
  22. type DurationFormat func(time.Duration) interface{}
  23. func DurationFormatString(d time.Duration) interface{} { return d.String() }
  24. func DurationFormatSeconds(d time.Duration) interface{} { return d.Seconds() }
  25. func DurationFormatMilliseconds(d time.Duration) interface{} { return d.Milliseconds() }
  26. // FormatIO formats net.Conn and other types that have an `Addr()` or `Name()`.
  27. //
  28. // See FormatEnabled for more information.
  29. func FormatIO(ctx context.Context, v interface{}) string {
  30. m := make(map[string]string)
  31. m["type"] = reflect.TypeOf(v).String()
  32. switch t := v.(type) {
  33. case net.Conn:
  34. m["localAddress"] = formatAddr(t.LocalAddr())
  35. m["remoteAddress"] = formatAddr(t.RemoteAddr())
  36. case interface{ Addr() net.Addr }:
  37. m["address"] = formatAddr(t.Addr())
  38. default:
  39. return Format(ctx, t)
  40. }
  41. return Format(ctx, m)
  42. }
  43. func formatAddr(a net.Addr) string {
  44. return a.Network() + "://" + a.String()
  45. }
  46. // Format formats an object into a JSON string, without any indendtation or
  47. // HTML escapes.
  48. // Context is used to output a log waring if the conversion fails.
  49. //
  50. // This is intended primarily for `trace.StringAttribute()`
  51. func Format(ctx context.Context, v interface{}) string {
  52. b, err := encode(v)
  53. if err != nil {
  54. G(ctx).WithError(err).Warning("could not format value")
  55. return ""
  56. }
  57. return string(b)
  58. }
  59. func encode(v interface{}) ([]byte, error) {
  60. return encodeBuffer(&bytes.Buffer{}, v)
  61. }
  62. func encodeBuffer(buf *bytes.Buffer, v interface{}) ([]byte, error) {
  63. enc := json.NewEncoder(buf)
  64. enc.SetEscapeHTML(false)
  65. enc.SetIndent("", "")
  66. if err := enc.Encode(v); err != nil {
  67. err = fmt.Errorf("could not marshall %T to JSON for logging: %w", v, err)
  68. return nil, err
  69. }
  70. // encoder.Encode appends a newline to the end
  71. return bytes.TrimSpace(buf.Bytes()), nil
  72. }