log_hub.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. package log_helper
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "strings"
  7. "sync"
  8. "time"
  9. "github.com/ChineseSubFinder/ChineseSubFinder/pkg"
  10. "github.com/ChineseSubFinder/ChineseSubFinder/pkg/types/common"
  11. "github.com/ChineseSubFinder/ChineseSubFinder/pkg/types/log_hub"
  12. "github.com/huandu/go-clone"
  13. "github.com/sirupsen/logrus"
  14. easy "github.com/t-tomalak/logrus-easy-formatter"
  15. )
  16. /*
  17. 这里独立出来一个按扫描次为单位的日志模块,暂时没有打算替换原有的日志模块
  18. 仅仅是为了更好地获取、展示日志,也方便后续的问题反馈提交
  19. 考虑到这里,暂定这里的日志都需要存储到硬盘中,且有两种方式获取:
  20. 1. 从 http 接口查询完成的多次的日志
  21. 2. 从 ws 接口中获取当前正在进行扫描的日志
  22. 既然是没有替换的打算,那么就使用 logrus 的 hook 接口去完成额外日志的记录即可,也就是在“每次”扫描的开始和结束进行标记,然后拆分成多次的日志好了
  23. */
  24. type LoggerHub struct {
  25. onceLogger *logrus.Logger // 一次扫描日志的实例
  26. onceStart bool
  27. }
  28. func NewLoggerHub() *LoggerHub {
  29. return &LoggerHub{}
  30. }
  31. func (lh *LoggerHub) Levels() []logrus.Level {
  32. // 记录全级别
  33. return []logrus.Level{
  34. logrus.TraceLevel,
  35. logrus.DebugLevel,
  36. logrus.InfoLevel,
  37. logrus.WarnLevel,
  38. logrus.ErrorLevel,
  39. logrus.FatalLevel,
  40. logrus.PanicLevel,
  41. }
  42. }
  43. func (lh *LoggerHub) Fire(entry *logrus.Entry) error {
  44. // 如果是一次扫描的开始
  45. if strings.HasPrefix(entry.Message, OnceSubsScanStart) == true {
  46. // 收到日志的标志位,需要新开一个
  47. if lh.onceStart == false {
  48. // 这个日志的前缀是 OnceSubsScanStart ,然后通过 # 进行分割,得到任务的 ID
  49. names := strings.Split(entry.Message, "#")
  50. if len(names) > 1 {
  51. lh.onceLogger = newOnceLogger(names[1])
  52. } else {
  53. lh.onceLogger = newOnceLogger(fmt.Sprintf("%v", time.Now().Unix()))
  54. }
  55. lh.onceStart = true
  56. // 既然新的一次开始,就实例化新的实例出来使用
  57. onceLogsLock.Lock()
  58. onceLog4Running = log_hub.NewOnceLog(0)
  59. onceLogsLock.Unlock()
  60. }
  61. return nil
  62. } else if entry.Message == OnceSubsScanEnd {
  63. // “一次”扫描的结束标志位
  64. lh.onceStart = false
  65. // 注意这个函数的调用时机
  66. CleanAndLoadOnceLogs()
  67. return nil
  68. }
  69. if lh.onceStart == false {
  70. // 如果没有发现开启一次扫描的记录标志位,那么就不进行日志的写入
  71. return nil
  72. }
  73. switch entry.Level {
  74. case logrus.TraceLevel:
  75. lh.onceLogger.Traceln(entry.Message)
  76. case logrus.DebugLevel:
  77. lh.onceLogger.Debugln(entry.Message)
  78. case logrus.InfoLevel:
  79. lh.onceLogger.Infoln(entry.Message)
  80. case logrus.WarnLevel:
  81. lh.onceLogger.Warningln(entry.Message)
  82. case logrus.ErrorLevel:
  83. lh.onceLogger.Errorln(entry.Message)
  84. case logrus.FatalLevel:
  85. lh.onceLogger.Fatalln(entry.Message)
  86. case logrus.PanicLevel:
  87. lh.onceLogger.Panicln(entry.Message)
  88. }
  89. onceLogsLock.Lock()
  90. onceLog4Running.LogLines = append(onceLog4Running.LogLines, *log_hub.NewOneLine(
  91. entry.Level.String(),
  92. entry.Time.Format("2006-01-02 15:04:05"),
  93. entry.Message))
  94. onceLogsLock.Unlock()
  95. return nil
  96. }
  97. // GetOnceLog4Running 当前正在运行任务的日志
  98. func GetOnceLog4Running() *log_hub.OnceLog {
  99. var nowOnceRunningLog *log_hub.OnceLog
  100. onceLogsLock.Lock()
  101. nowOnceRunningLog = clone.Clone(onceLog4Running).(*log_hub.OnceLog)
  102. onceLogsLock.Unlock()
  103. return nowOnceRunningLog
  104. }
  105. // GetSpiltOnceLog 拆分到一行一个,没有锁,所以需要考虑并发问题
  106. func GetSpiltOnceLog(log *log_hub.OnceLog) []*log_hub.OnceLog {
  107. if log == nil {
  108. return nil
  109. }
  110. var outList = make([]*log_hub.OnceLog, len(log.LogLines))
  111. for i := 0; i < len(log.LogLines); i++ {
  112. outList[i] = &log_hub.OnceLog{
  113. LogLines: []log_hub.OneLine{log.LogLines[i]},
  114. }
  115. }
  116. return outList
  117. }
  118. func newOnceLogger(logFileName string) *logrus.Logger {
  119. var err error
  120. Logger := logrus.New()
  121. Logger.Formatter = &easy.Formatter{
  122. TimestampFormat: "2006-01-02 15:04:05",
  123. LogFormat: "[%lvl%]: %time% - %msg%\n",
  124. }
  125. pathRoot := filepath.Join(pkg.ConfigRootDirFPath(), "Logs")
  126. fileName := fmt.Sprintf(common.OnceLogPrefix+"%v.log", logFileName)
  127. fileAbsPath := filepath.Join(pathRoot, fileName)
  128. // 注意这个函数的调用时机
  129. CleanAndLoadOnceLogs()
  130. onceLoggerFile, err = os.OpenFile(fileAbsPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModePerm)
  131. if err != nil {
  132. panic(err)
  133. }
  134. Logger.SetOutput(onceLoggerFile)
  135. return Logger
  136. }
  137. // CleanAndLoadOnceLogs 调用的时机,一定是要在新开一个日志前,且把上一个日志的文件流关闭的时候
  138. func CleanAndLoadOnceLogs() {
  139. defer func() {
  140. onceLogsLock.Unlock()
  141. }()
  142. if onceLoggerFile != nil {
  143. _ = onceLoggerFile.Close()
  144. onceLoggerFile = nil
  145. }
  146. onceLogsLock.Lock()
  147. pathRoot := filepath.Join(pkg.ConfigRootDirFPath(), "Logs")
  148. // 扫描当前日志存储目录下有多少个符合要求的 Once- 日志
  149. // 确保有且仅有最近的 20 次扫描日志记录存在即可
  150. matches, err := filepath.Glob(filepath.Join(pathRoot, common.OnceLogPrefix+"*.log"))
  151. if err != nil {
  152. panic(err)
  153. }
  154. if len(matches) > onceLogMaxCount {
  155. // 需要清理多余的
  156. // 保存的文件名是 Once-unixTime.log 做为前提
  157. // 这里假定查询出来的都是正序排序
  158. for i := 0; i <= len(matches)-1-onceLogMaxCount; i++ {
  159. _, err := os.Stat(matches[i])
  160. if err != nil {
  161. continue
  162. }
  163. _ = os.Remove(matches[i])
  164. }
  165. // 将有存在价值的“单次”日志缓存到内存中,供 Web API 查询
  166. matches, err = filepath.Glob(filepath.Join(pathRoot, common.OnceLogPrefix+"*.log"))
  167. if err != nil {
  168. panic(err)
  169. }
  170. }
  171. }
  172. var (
  173. onceLoggerFile *os.File // 单次扫描保存 Log 文件的实例
  174. onceLogsLock sync.Mutex // 对应的锁
  175. onceLog4Running = log_hub.NewOnceLog(0) // 当前正在扫描时候日志的日志内容实例,注意,开启任务不代表就在扫描
  176. )
  177. const (
  178. onceLogMaxCount = 10000
  179. OnceSubsScanStart = "OneTimeSubtitleScanStart"
  180. OnceSubsScanEnd = "OneTimeSubtitleScanEnd"
  181. )