shared.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import { Context, Exporter, Formatter, Logger, Message } from 'cordis'
  2. import { Time } from 'cosmokit'
  3. import z from 'schemastery'
  4. /** Terminal color support level compatible with supports-color. */
  5. export type ColorSupportLevel = 0 | 1 | 2 | 3
  6. /** Formatting options for the logger name label. */
  7. export interface LabelStyle {
  8. width?: number
  9. margin?: number
  10. align?: 'left' | 'right'
  11. }
  12. /** Config namespace for console logger exporters. */
  13. export namespace ConsoleExporter {
  14. export interface Config {
  15. colors?: false | ColorSupportLevel
  16. maxLength?: number
  17. levels?: Record<string, number>
  18. showDiff?: boolean
  19. showTime?: string
  20. label?: LabelStyle
  21. }
  22. }
  23. /** Shared console log exporter implementation used by Node and browser builds. */
  24. export class ConsoleExporter implements Exporter {
  25. static readonly name = 'logger-console'
  26. static readonly Config: z<ConsoleExporter.Config> = z.object({
  27. colors: z.union([z.const(false), z.number()]),
  28. maxLength: z.number(),
  29. levels: z.dict(z.number()),
  30. showDiff: z.boolean().default(false),
  31. showTime: z.string().default('yyyy-MM-dd hh:mm:ss '),
  32. label: z.object({
  33. width: z.number(),
  34. margin: z.number(),
  35. align: z.union(['left', 'right']),
  36. }),
  37. }) as z<ConsoleExporter.Config>
  38. colors!: false | ColorSupportLevel
  39. maxLength?: number
  40. levels?: Record<string, number>
  41. showDiff!: boolean
  42. showTime!: string
  43. label?: LabelStyle
  44. timestamp: number
  45. formatters: Record<string, Formatter> = {}
  46. constructor(public ctx: Context, config: ConsoleExporter.Config = {}) {
  47. Object.assign(this, this.getDefaults(), config)
  48. this.timestamp = Date.now()
  49. ctx.logger.exporter(this)
  50. }
  51. getDefaults() {
  52. return {
  53. colors: false as false | ColorSupportLevel,
  54. showTime: 'yyyy-MM-dd hh:mm:ss ',
  55. showDiff: false,
  56. }
  57. }
  58. export(message: Message) {
  59. // eslint-disable-next-line no-console
  60. console.log(this.render(message))
  61. }
  62. render(message: Message) {
  63. const prefix = `[${message.type[0].toUpperCase()}]`
  64. const space = ' '.repeat(this.label?.margin ?? 1)
  65. let indent = 3 + space.length, output = ''
  66. if (this.showTime) {
  67. indent += this.showTime.length
  68. output += Logger.color(this, 8, Time.template(this.showTime))
  69. }
  70. const code = Logger.code(message.name, this.colors)
  71. const label = Logger.color(this, code, message.name, ';1')
  72. const padLength = (this.label?.width ?? 0) + label.length - message.name.length
  73. if (this.label?.align === 'right') {
  74. output += label.padStart(padLength) + space + prefix + space
  75. indent += (this.label.width ?? 0) + space.length
  76. } else {
  77. output += prefix + space + label.padEnd(padLength) + space
  78. }
  79. output += Logger.format(this, message).replace(/\n/g, '\n' + ' '.repeat(indent))
  80. if (this.showDiff && this.timestamp) {
  81. const diff = message.ts - this.timestamp
  82. output += Logger.color(this, code, ' +' + Time.format(diff))
  83. }
  84. this.timestamp = message.ts
  85. return output
  86. }
  87. }
  88. export default ConsoleExporter