logging.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. package logging
  2. // Custom logging hooks for powering our logs API.
  3. // Modeled after https://github.com/sirupsen/logrus/blob/master/hooks/test/test.go
  4. import (
  5. "math"
  6. "os"
  7. "path/filepath"
  8. "sync"
  9. "time"
  10. rotatelogs "github.com/lestrrat-go/file-rotatelogs"
  11. "github.com/owncast/owncast/utils"
  12. "github.com/rifflock/lfshook"
  13. "github.com/sirupsen/logrus"
  14. logger "github.com/sirupsen/logrus"
  15. )
  16. const maxLogEntries = 500
  17. // OCLogger represents the owncast internal logging.
  18. type OCLogger struct {
  19. Entries []logrus.Entry
  20. Warnings []logrus.Entry
  21. mu sync.RWMutex
  22. }
  23. // Logger is the shared instance of the internal logger.
  24. var Logger *OCLogger
  25. // Setup configures our custom logging destinations.
  26. func Setup(enableDebugOptions bool, enableVerboseLogging bool) {
  27. // Create the logging directory if needed
  28. loggingDirectory := filepath.Dir(getLogFilePath())
  29. if !utils.DoesFileExists(loggingDirectory) {
  30. if err := os.Mkdir(loggingDirectory, 0700); err != nil {
  31. logger.Errorln("unable to create logs directory", loggingDirectory, err)
  32. }
  33. }
  34. // Write logs to a file
  35. path := getLogFilePath()
  36. writer, _ := rotatelogs.New(
  37. path+".%Y%m%d%H%M",
  38. rotatelogs.WithLinkName(path),
  39. rotatelogs.WithMaxAge(time.Duration(86400)*time.Second),
  40. rotatelogs.WithRotationTime(time.Duration(604800)*time.Second),
  41. )
  42. logMapping := lfshook.WriterMap{
  43. logrus.InfoLevel: writer,
  44. logrus.DebugLevel: writer,
  45. logrus.TraceLevel: writer,
  46. logrus.WarnLevel: writer,
  47. logrus.ErrorLevel: writer,
  48. logrus.FatalLevel: writer,
  49. }
  50. logger.AddHook(lfshook.NewHook(
  51. logMapping,
  52. &logger.TextFormatter{},
  53. ))
  54. if enableVerboseLogging {
  55. logrus.SetLevel(logrus.TraceLevel)
  56. } else {
  57. logrus.SetLevel(logrus.InfoLevel)
  58. }
  59. // Write to stdout console
  60. logger.SetOutput(os.Stdout)
  61. // Write to our custom logging hook for the log API
  62. _logger := new(OCLogger)
  63. logger.AddHook(_logger)
  64. if enableDebugOptions {
  65. logrus.SetReportCaller(true)
  66. }
  67. Logger = _logger
  68. }
  69. // Fire runs for every logging request.
  70. func (l *OCLogger) Fire(e *logger.Entry) error {
  71. // Store all log messages to return back in the logging API
  72. l.mu.Lock()
  73. defer l.mu.Unlock()
  74. // Append to log entries
  75. if len(l.Entries) > maxLogEntries {
  76. l.Entries = l.Entries[1:]
  77. }
  78. l.Entries = append(l.Entries, *e)
  79. if e.Level <= logger.WarnLevel {
  80. if len(l.Warnings) > maxLogEntries {
  81. l.Warnings = l.Warnings[1:]
  82. }
  83. l.Warnings = append(l.Warnings, *e)
  84. }
  85. return nil
  86. }
  87. // Levels specifies what log levels we care about.
  88. func (l *OCLogger) Levels() []logrus.Level {
  89. return logrus.AllLevels
  90. }
  91. // AllEntries returns all entries that were logged.
  92. func (l *OCLogger) AllEntries() []*logrus.Entry {
  93. l.mu.RLock()
  94. defer l.mu.RUnlock()
  95. // Make a copy so the returned value won't race with future log requests
  96. logCount := int(math.Min(float64(len(l.Entries)), maxLogEntries))
  97. entries := make([]*logrus.Entry, logCount)
  98. for i := 0; i < len(entries); i++ {
  99. // Make a copy, for safety
  100. entries[len(entries)-logCount:][i] = &l.Entries[i]
  101. }
  102. return entries
  103. }
  104. // WarningEntries returns all warning or greater that were logged.
  105. func (l *OCLogger) WarningEntries() []*logrus.Entry {
  106. l.mu.RLock()
  107. defer l.mu.RUnlock()
  108. // Make a copy so the returned value won't race with future log requests
  109. logCount := int(math.Min(float64(len(l.Warnings)), maxLogEntries))
  110. entries := make([]*logrus.Entry, logCount)
  111. for i := 0; i < len(entries); i++ {
  112. // Make a copy, for safety
  113. entries[len(entries)-logCount:][i] = &l.Warnings[i]
  114. }
  115. return entries
  116. }