logs.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. package logs
  2. import (
  3. "bufio"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "os"
  8. "strconv"
  9. "sync"
  10. "github.com/sirupsen/logrus"
  11. )
  12. var (
  13. configureMutex = sync.Mutex{}
  14. // loggingConfigured will be set once logging has been configured via invoking `ConfigureLogging`.
  15. // Subsequent invocations of `ConfigureLogging` would be no-op
  16. loggingConfigured = false
  17. )
  18. type Config struct {
  19. LogLevel logrus.Level
  20. LogFormat string
  21. LogFilePath string
  22. LogPipeFd string
  23. }
  24. func ForwardLogs(logPipe io.Reader) {
  25. lineReader := bufio.NewReader(logPipe)
  26. for {
  27. line, err := lineReader.ReadBytes('\n')
  28. if len(line) > 0 {
  29. processEntry(line)
  30. }
  31. if err == io.EOF {
  32. logrus.Debugf("log pipe has been closed: %+v", err)
  33. return
  34. }
  35. if err != nil {
  36. logrus.Errorf("log pipe read error: %+v", err)
  37. }
  38. }
  39. }
  40. func processEntry(text []byte) {
  41. type jsonLog struct {
  42. Level string `json:"level"`
  43. Msg string `json:"msg"`
  44. }
  45. var jl jsonLog
  46. if err := json.Unmarshal(text, &jl); err != nil {
  47. logrus.Errorf("failed to decode %q to json: %+v", text, err)
  48. return
  49. }
  50. lvl, err := logrus.ParseLevel(jl.Level)
  51. if err != nil {
  52. logrus.Errorf("failed to parse log level %q: %v\n", jl.Level, err)
  53. return
  54. }
  55. logrus.StandardLogger().Logf(lvl, jl.Msg)
  56. }
  57. func ConfigureLogging(config Config) error {
  58. configureMutex.Lock()
  59. defer configureMutex.Unlock()
  60. if loggingConfigured {
  61. logrus.Debug("logging has already been configured")
  62. return nil
  63. }
  64. logrus.SetLevel(config.LogLevel)
  65. if config.LogPipeFd != "" {
  66. logPipeFdInt, err := strconv.Atoi(config.LogPipeFd)
  67. if err != nil {
  68. return fmt.Errorf("failed to convert _LIBCONTAINER_LOGPIPE environment variable value %q to int: %v", config.LogPipeFd, err)
  69. }
  70. logrus.SetOutput(os.NewFile(uintptr(logPipeFdInt), "logpipe"))
  71. } else if config.LogFilePath != "" {
  72. f, err := os.OpenFile(config.LogFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0644)
  73. if err != nil {
  74. return err
  75. }
  76. logrus.SetOutput(f)
  77. }
  78. switch config.LogFormat {
  79. case "text":
  80. // retain logrus's default.
  81. case "json":
  82. logrus.SetFormatter(new(logrus.JSONFormatter))
  83. default:
  84. return fmt.Errorf("unknown log-format %q", config.LogFormat)
  85. }
  86. loggingConfigured = true
  87. return nil
  88. }