parse-log.ts 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import { registerTSPaths } from '../server/helpers/register-ts-paths'
  2. registerTSPaths()
  3. import * as program from 'commander'
  4. import { createReadStream, readdir } from 'fs-extra'
  5. import { join } from 'path'
  6. import { createInterface } from 'readline'
  7. import * as winston from 'winston'
  8. import { labelFormatter } from '../server/helpers/logger'
  9. import { CONFIG } from '../server/initializers/config'
  10. import { mtimeSortFilesDesc } from '../shared/core-utils/logs/logs'
  11. program
  12. .option('-l, --level [level]', 'Level log (debug/info/warn/error)')
  13. .option('-f, --files [file...]', 'Files to parse. If not provided, the script will parse the latest log file from config)')
  14. .parse(process.argv)
  15. const excludedKeys = {
  16. level: true,
  17. message: true,
  18. splat: true,
  19. timestamp: true,
  20. label: true
  21. }
  22. function keysExcluder (key, value) {
  23. return excludedKeys[key] === true ? undefined : value
  24. }
  25. const loggerFormat = winston.format.printf((info) => {
  26. let additionalInfos = JSON.stringify(info, keysExcluder, 2)
  27. if (additionalInfos === '{}') additionalInfos = ''
  28. else additionalInfos = ' ' + additionalInfos
  29. return `[${info.label}] ${toTimeFormat(info.timestamp)} ${info.level}: ${info.message}${additionalInfos}`
  30. })
  31. const logger = winston.createLogger({
  32. transports: [
  33. new winston.transports.Console({
  34. level: program['level'] || 'debug',
  35. stderrLevels: [],
  36. format: winston.format.combine(
  37. winston.format.splat(),
  38. labelFormatter(),
  39. winston.format.colorize(),
  40. loggerFormat
  41. )
  42. })
  43. ],
  44. exitOnError: true
  45. })
  46. const logLevels = {
  47. error: logger.error.bind(logger),
  48. warn: logger.warn.bind(logger),
  49. info: logger.info.bind(logger),
  50. debug: logger.debug.bind(logger)
  51. }
  52. run()
  53. .then(() => process.exit(0))
  54. .catch(err => console.error(err))
  55. function run () {
  56. return new Promise(async res => {
  57. const files = await getFiles()
  58. for (const file of files) {
  59. console.log('Opening %s.', file)
  60. const stream = createReadStream(file)
  61. const rl = createInterface({
  62. input: stream
  63. })
  64. rl.on('line', line => {
  65. try {
  66. const log = JSON.parse(line)
  67. // Don't know why but loggerFormat does not remove splat key
  68. Object.assign(log, { splat: undefined })
  69. logLevels[log.level](log)
  70. } catch (err) {
  71. console.error('Cannot parse line.', line)
  72. throw err
  73. }
  74. })
  75. stream.once('close', () => res())
  76. }
  77. })
  78. }
  79. // Thanks: https://stackoverflow.com/a/37014317
  80. async function getNewestFile (files: string[], basePath: string) {
  81. const sorted = await mtimeSortFilesDesc(files, basePath)
  82. return (sorted.length > 0) ? sorted[0].file : ''
  83. }
  84. async function getFiles () {
  85. if (program['files']) return program['files']
  86. const logFiles = await readdir(CONFIG.STORAGE.LOG_DIR)
  87. const filename = await getNewestFile(logFiles, CONFIG.STORAGE.LOG_DIR)
  88. return [ join(CONFIG.STORAGE.LOG_DIR, filename) ]
  89. }
  90. function toTimeFormat (time: string) {
  91. const timestamp = Date.parse(time)
  92. if (isNaN(timestamp) === true) return 'Unknown date'
  93. return new Date(timestamp).toISOString()
  94. }