log_hub.go 6.1 KB

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