UNPKG

5.42 kBPlain TextView Raw
1import getLogLevel, { rfcLogLevels, LevelInfo, LevelsMap } from 'rfc-log-levels'
2import getCurrentLine, { Offset, Location } from 'get-current-line'
3import { Transform } from './transform.js'
4
5/** The log entry that Caterpillar creates and forwards to its transforms */
6export interface LogEntry extends LevelInfo, Location {
7 /** the iso string of when the log occured */
8 date: string
9 /** all the arguments that were after the log level */
10 args: any[]
11}
12
13/** Configuration for the Caterpillar Logger */
14export interface LoggerOptions {
15 /** Use to override the default value of {@link Logger.lineOffset} */
16 lineOffset?: Offset
17
18 /** Use to override the default value of {@link Logger.levels} */
19 levels?: LevelsMap
20
21 /** Use to override the default value of {@link Logger.defaultLevelInfo} */
22 defaultLevel?: number | string
23
24 /** Use to override the default value of {@link Logger.lineLevel} */
25 lineLevel?: number
26}
27
28/**
29 * Logger.
30 * This is what we write to.
31 * @example Creation
32 * ``` javascript
33 * // Via class
34 * import { Logger } from 'caterpillar'
35 * const logger = new Logger()
36 * ```
37 */
38export class Logger extends Transform {
39 /**
40 * The configuration to use for the line offset.
41 * This defaults to any file path that includes `logger`, and any method that includes the word `log`.
42 */
43 public lineOffset: Offset = {
44 file: /logger/i,
45 method: /log/i,
46 }
47
48 /**
49 * The mapping of log level names to log level numbers.
50 * Defaults to the RFC Log Level configuration.
51 */
52 public levels: LevelsMap = rfcLogLevels
53
54 /**
55 * The log level information to use when the log level was unable to be determined.
56 * Defaults to the info log level.
57 */
58 public defaultLevelInfo!: LevelInfo
59
60 /** Set the default level info via a level number or name. */
61 public set defaultLevel(value: number | string) {
62 const levelInfo = this.getLogLevel(value)
63 if (levelInfo == null) {
64 throw new Error(
65 `caterpillar: the intended value of ${value} for the default log level not found in the configured levels`
66 )
67 }
68 this.defaultLevelInfo = levelInfo
69 }
70
71 /**
72 * Only fetch line information for entries that have a log level equal to, or below this number.
73 * You should only specify this if you need it, as fFetching line information for thousands of log entries, which is typical in large applications, will slow your application down dramatically.
74 * If not specified, defaults to `-Infinity` which effect is to ignore gathering line information for all log levels.
75 */
76 public lineLevel: number = -Infinity
77
78 /** Create our instance and apply our configuraiton options. */
79 constructor(opts?: LoggerOptions) {
80 super()
81
82 // options
83 if (opts?.lineOffset != null) this.lineOffset = opts.lineOffset
84 if (opts?.levels != null) this.levels = opts.levels
85 if (opts?.lineLevel != null) this.lineLevel = opts.lineLevel
86
87 // options: default level
88 this.defaultLevel = opts?.defaultLevel ?? 'info'
89
90 // dereference
91 this.levels = Object.assign({}, this.levels)
92 }
93
94 /** Alias for {@link getLogLevel} using the configured logger levels as reference. */
95 getLogLevel(value: number | string) {
96 return getLogLevel(value, this.levels)
97 }
98
99 /** Takes an arguments array and tranforms it into a log entry. */
100 format(args: any): LogEntry {
101 // fetch the level
102 const level = args.shift()
103 let levelInfo =
104 level === 'default' ? this.defaultLevelInfo : this.getLogLevel(level)
105 if (levelInfo == null) {
106 // fallback to the default log level
107 levelInfo = this.defaultLevelInfo
108 // as the level (first param) was not actually a level, put it back
109 args.unshift(level)
110 }
111
112 // fetch the date
113 const date = new Date().toISOString()
114
115 // fetch the line information
116 const lineInfo =
117 levelInfo.levelNumber <= this.lineLevel
118 ? getCurrentLine(this.lineOffset)
119 : {
120 line: -1,
121 char: -1,
122 method: '',
123 file: '',
124 }
125
126 // put it all together
127 return Object.assign({ date, args }, levelInfo, lineInfo)
128 }
129
130 /**
131 * Log the arguments into the logger stream as formatted data with debugging information.
132 * Such that our transformers can deal with it intelligently.
133 *
134 * @example Inputs
135 * ``` javascript
136 * logger.log('note', 'this is working swell')
137 * ```
138 * ``` javascript
139 * logger.log('this', 'worked', 'swell')
140 * ```
141 *
142 * @example Results
143 * ``` json
144 * {
145 * "args": ["this is working swell"],
146 * "date": "2013-04-25T10:18:25.722Z",
147 * "levelNumber": 5,
148 * "levelName": "notice",
149 * "line": "59",
150 * "method": "Object.<anonymous>",
151 * "file": "/Users/balupton/some-project/calling-file.js"
152 * }
153 * ```
154 * ``` json
155 * {
156 * "args": ["this", "worked", "well"],
157 * "date": "2013-04-25T10:18:26.539Z",
158 * "levelNumber": 6,
159 * "levelName": "info",
160 * "line": "60",
161 * "method": "Object.<anonymous>",
162 * "file": "/Users/balupton/some-project/calling-file.js"
163 * }
164 * ```
165 */
166 log(...args: any) {
167 this.write(args)
168 }
169
170 /** Alias for log which prefixes the error log level */
171 error(...args: any) {
172 this.write(['error', ...args])
173 }
174
175 /** Alias for log which prefixes the warn log level */
176 warn(...args: any) {
177 this.write(['warn', ...args])
178 }
179
180 /** Alias for log which prefixes the info log level */
181 info(...args: any) {
182 this.write(['info', ...args])
183 }
184
185 /** Alias for log which prefixes the debug log level */
186 debug(...args: any) {
187 this.write(['debug', ...args])
188 }
189}
190
191export default Logger