log_hub.go 7.1 KB

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