|
| 1 | +import * as fs from 'fs'; |
| 2 | +import * as path from 'path'; |
| 3 | +import {Config} from './configParser'; |
| 4 | + |
| 5 | +// Will use chalk if chalk is available to add color to console logging |
| 6 | +let chalk: any; |
| 7 | +let printRed: Function; |
| 8 | +let printYellow: Function; |
| 9 | +let printGray: Function; |
| 10 | + |
| 11 | +try { |
| 12 | + chalk = require('chalk'); |
| 13 | + printRed = chalk.red; |
| 14 | + printYellow = chalk.yellow; |
| 15 | + printGray = chalk.gray; |
| 16 | +} catch (e) { |
| 17 | + printRed = printYellow = printGray = (msg: any) => { return msg; }; |
| 18 | +} |
| 19 | + |
| 20 | +export enum LogLevel { |
| 21 | + ERROR, |
| 22 | + WARN, |
| 23 | + INFO, |
| 24 | + DEBUG |
| 25 | +} |
| 26 | + |
| 27 | +export enum WriteTo { |
| 28 | + CONSOLE, |
| 29 | + FILE, |
| 30 | + BOTH, |
| 31 | + NONE |
| 32 | +} |
| 33 | + |
| 34 | +let logFile = 'protractor.log'; // the default log file name |
| 35 | + |
| 36 | +/** |
| 37 | + * Logger class adds timestamp output, log levels, and identifiers to help |
| 38 | + * when debugging. Also could write to console, file, both, or none. |
| 39 | + */ |
| 40 | +export class Logger { |
| 41 | + static logLevel: LogLevel = LogLevel.INFO; |
| 42 | + static showTimestamp: boolean = true; |
| 43 | + static showId: boolean = true; |
| 44 | + static writeTo: WriteTo = WriteTo.CONSOLE; |
| 45 | + static fd: any; |
| 46 | + static firstWrite: boolean = false; |
| 47 | + |
| 48 | + /** |
| 49 | + * Set up the logging configuration from the protractor configuration file. |
| 50 | + * @param config The protractor configuration |
| 51 | + */ |
| 52 | + static set(config: Config): void { |
| 53 | + if (config.troubleshoot) { |
| 54 | + Logger.logLevel = LogLevel.DEBUG; |
| 55 | + } |
| 56 | + } |
| 57 | + |
| 58 | + /** |
| 59 | + * Set up the write location. If writing to a file, get the file descriptor. |
| 60 | + * @param writeTo The enum for where to write the logs. |
| 61 | + * @param opt_logFile An optional parameter to override the log file location. |
| 62 | + */ |
| 63 | + static setWrite(writeTo: WriteTo, opt_logFile?: string): void { |
| 64 | + if (opt_logFile) { |
| 65 | + logFile = opt_logFile; |
| 66 | + } |
| 67 | + Logger.writeTo = writeTo; |
| 68 | + if (Logger.writeTo == WriteTo.FILE || Logger.writeTo == WriteTo.BOTH) { |
| 69 | + Logger.fd = fs.openSync(path.resolve(logFile), 'a'); |
| 70 | + Logger.firstWrite = false; |
| 71 | + } |
| 72 | + } |
| 73 | + |
| 74 | + /** |
| 75 | + * Creates a logger instance with an ID for the logger. |
| 76 | + * @constructor |
| 77 | + */ |
| 78 | + constructor(private id: string) {} |
| 79 | + |
| 80 | + /** |
| 81 | + * Log INFO |
| 82 | + * @param ...msgs multiple arguments to be logged. |
| 83 | + */ |
| 84 | + info(...msgs: any[]): void { this.log_(LogLevel.INFO, msgs); } |
| 85 | + |
| 86 | + /** |
| 87 | + * Log DEBUG |
| 88 | + * @param ...msgs multiple arguments to be logged. |
| 89 | + */ |
| 90 | + debug(...msgs: any[]): void { this.log_(LogLevel.DEBUG, msgs); } |
| 91 | + |
| 92 | + /** |
| 93 | + * Log WARN |
| 94 | + * @param ...msgs multiple arguments to be logged. |
| 95 | + */ |
| 96 | + warn(...msgs: any[]): void { this.log_(LogLevel.WARN, msgs); } |
| 97 | + |
| 98 | + /** |
| 99 | + * Log ERROR |
| 100 | + * @param ...msgs multiple arguments to be logged. |
| 101 | + */ |
| 102 | + error(...msgs: any[]): void { this.log_(LogLevel.ERROR, msgs); } |
| 103 | + |
| 104 | + /** |
| 105 | + * For the log level set, check to see if the messages should be logged. |
| 106 | + * @param logLevel The log level of the message. |
| 107 | + * @param msgs The messages to be logged |
| 108 | + */ |
| 109 | + log_(logLevel: LogLevel, msgs: any[]): void { |
| 110 | + switch (Logger.logLevel) { |
| 111 | + case LogLevel.ERROR: |
| 112 | + if (logLevel <= LogLevel.ERROR) { |
| 113 | + this.print_(logLevel, msgs); |
| 114 | + } |
| 115 | + break; |
| 116 | + case LogLevel.WARN: |
| 117 | + if (logLevel <= LogLevel.WARN) { |
| 118 | + this.print_(logLevel, msgs); |
| 119 | + } |
| 120 | + break; |
| 121 | + case LogLevel.INFO: |
| 122 | + if (logLevel <= LogLevel.INFO) { |
| 123 | + this.print_(logLevel, msgs); |
| 124 | + } |
| 125 | + break; |
| 126 | + case LogLevel.DEBUG: |
| 127 | + if (logLevel <= LogLevel.DEBUG) { |
| 128 | + this.print_(logLevel, msgs); |
| 129 | + } |
| 130 | + break; |
| 131 | + default: |
| 132 | + throw new Error('Log level undefined'); |
| 133 | + } |
| 134 | + } |
| 135 | + |
| 136 | + /** |
| 137 | + * Format with timestamp, log level, identifier, and message and log to |
| 138 | + * specified medium (console, file, both, none). |
| 139 | + * @param logLevel The log level of the message. |
| 140 | + * @param msgs The messages to be logged. |
| 141 | + */ |
| 142 | + print_(logLevel: LogLevel, msgs: any[]): void { |
| 143 | + let consoleLog: string = ''; |
| 144 | + let fileLog: string = ''; |
| 145 | + |
| 146 | + if (Logger.showTimestamp) { |
| 147 | + consoleLog += Logger.timestamp_(WriteTo.CONSOLE); |
| 148 | + fileLog += Logger.timestamp_(WriteTo.FILE); |
| 149 | + } |
| 150 | + consoleLog += Logger.level_(logLevel, this.id, WriteTo.CONSOLE); |
| 151 | + fileLog += Logger.level_(logLevel, this.id, WriteTo.FILE); |
| 152 | + if (Logger.showId) { |
| 153 | + consoleLog += Logger.id_(logLevel, this.id, WriteTo.CONSOLE); |
| 154 | + fileLog += Logger.id_(logLevel, this.id, WriteTo.FILE); |
| 155 | + } |
| 156 | + consoleLog += ' - '; |
| 157 | + fileLog += ' - '; |
| 158 | + |
| 159 | + switch (Logger.writeTo) { |
| 160 | + case WriteTo.CONSOLE: |
| 161 | + msgs.unshift(consoleLog); |
| 162 | + console.log.apply(console, msgs); |
| 163 | + break; |
| 164 | + case WriteTo.FILE: |
| 165 | + // for the first line written to the file, add a space |
| 166 | + if (!Logger.firstWrite) { |
| 167 | + fs.writeSync(Logger.fd, '\n'); |
| 168 | + Logger.firstWrite = true; |
| 169 | + } |
| 170 | + fileLog += ' ' + Logger.msgToFile_(msgs); |
| 171 | + fs.writeSync(Logger.fd, fileLog + '\n'); |
| 172 | + break; |
| 173 | + case WriteTo.BOTH: |
| 174 | + // for the first line written to the file, add a space |
| 175 | + if (!Logger.firstWrite) { |
| 176 | + fs.writeSync(Logger.fd, '\n'); |
| 177 | + Logger.firstWrite = true; |
| 178 | + } |
| 179 | + fileLog += ' ' + Logger.msgToFile_(msgs); |
| 180 | + fs.writeSync(Logger.fd, fileLog + '\n'); |
| 181 | + msgs.unshift(consoleLog); |
| 182 | + console.log.apply(console, msgs); |
| 183 | + break; |
| 184 | + case WriteTo.NONE: |
| 185 | + break; |
| 186 | + } |
| 187 | + } |
| 188 | + |
| 189 | + /** |
| 190 | + * Get a timestamp formatted with [hh:mm:ss] |
| 191 | + * @param writeTo The enum for where to write the logs. |
| 192 | + * @return The string of the formatted timestamp |
| 193 | + */ |
| 194 | + static timestamp_(writeTo: WriteTo): string { |
| 195 | + let d = new Date(); |
| 196 | + let ts = '['; |
| 197 | + let hours = d.getHours() < 10 ? '0' + d.getHours() : d.getHours(); |
| 198 | + let minutes = d.getMinutes() < 10 ? '0' + d.getMinutes() : d.getMinutes(); |
| 199 | + let seconds = d.getSeconds() < 10 ? '0' + d.getSeconds() : d.getSeconds(); |
| 200 | + if (writeTo == WriteTo.CONSOLE) { |
| 201 | + ts += printGray(hours + ':' + minutes + ':' + seconds) + ']'; |
| 202 | + } else { |
| 203 | + ts += hours + ':' + minutes + ':' + seconds + ']'; |
| 204 | + } |
| 205 | + ts += ' '; |
| 206 | + return ts; |
| 207 | + } |
| 208 | + |
| 209 | + /** |
| 210 | + * Get the identifier of the logger as '/<id>' |
| 211 | + * @param logLevel The log level of the message. |
| 212 | + * @param writeTo The enum for where to write the logs. |
| 213 | + * @return The string of the formatted id |
| 214 | + */ |
| 215 | + static id_(logLevel: LogLevel, id: string, writeTo: WriteTo): string { |
| 216 | + let level = LogLevel[logLevel].toString(); |
| 217 | + if (writeTo === WriteTo.FILE) { |
| 218 | + return '/' + id; |
| 219 | + } else if (logLevel === LogLevel.ERROR) { |
| 220 | + return printRed('/' + id); |
| 221 | + } else if (logLevel === LogLevel.WARN) { |
| 222 | + return printYellow('/' + id); |
| 223 | + } else { |
| 224 | + return '/' + id; |
| 225 | + } |
| 226 | + } |
| 227 | + |
| 228 | + /** |
| 229 | + * Get the log level formatted with the first letter. For info, it is I. |
| 230 | + * @param logLevel The log level of the message. |
| 231 | + * @param writeTo The enum for where to write the logs. |
| 232 | + * @return The string of the formatted log level |
| 233 | + */ |
| 234 | + static level_(logLevel: LogLevel, id: string, writeTo: WriteTo): string { |
| 235 | + let level = LogLevel[logLevel].toString(); |
| 236 | + if (writeTo === WriteTo.FILE) { |
| 237 | + return level[0]; |
| 238 | + } else if (logLevel === LogLevel.ERROR) { |
| 239 | + return printRed(level[0]); |
| 240 | + } else if (logLevel === LogLevel.WARN) { |
| 241 | + return printYellow(level[0]); |
| 242 | + } else { |
| 243 | + return level[0]; |
| 244 | + } |
| 245 | + } |
| 246 | + |
| 247 | + /** |
| 248 | + * Convert the list of messages to a single string message. |
| 249 | + * @param msgs The list of messages. |
| 250 | + * @return The string of the formatted messages |
| 251 | + */ |
| 252 | + static msgToFile_(msgs: any[]): string { |
| 253 | + let log = ''; |
| 254 | + for (let pos = 0; pos < msgs.length; pos++) { |
| 255 | + let msg = msgs[pos]; |
| 256 | + let ret: any; |
| 257 | + if (typeof msg === 'object') { |
| 258 | + ret = JSON.stringify(msg); |
| 259 | + } else { |
| 260 | + ret = msg; |
| 261 | + } |
| 262 | + if (pos !== msgs.length - 1) { |
| 263 | + ret += ' '; |
| 264 | + } |
| 265 | + log += ret; |
| 266 | + } |
| 267 | + return log; |
| 268 | + } |
| 269 | +} |
0 commit comments