OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 /// Message logging. |
| 6 library log; |
| 7 |
| 8 import 'dart:io'; |
| 9 import 'io.dart'; |
| 10 |
| 11 typedef LogFn(Level level, message); |
| 12 final Map<Level, LogFn> _loggers = new Map<Level, LogFn>(); |
| 13 |
| 14 /// The list of recorded log messages. Will only be recorded if |
| 15 /// [recordTranscript()] is called. |
| 16 List<Entry> _transcript; |
| 17 |
| 18 /// An enum type for defining the different logging levels. By default, [ERROR] |
| 19 /// and [WARNING] messages are printed to sterr. [MESSAGE] messages are printed |
| 20 /// to stdout, and others are ignored. |
| 21 class Level { |
| 22 /// An error occurred and an operation could not be completed. Usually shown |
| 23 /// to the user on stderr. |
| 24 static const ERROR = const Level._("ERR "); |
| 25 |
| 26 /// Something unexpected happened, but the program was able to continue, |
| 27 /// though possibly in a degraded fashion. |
| 28 static const WARNING = const Level._("WARN"); |
| 29 |
| 30 /// A message intended specifically to be shown to the user. |
| 31 static const MESSAGE = const Level._("MSG "); |
| 32 |
| 33 /// Some interaction with the external world occurred, such as a network |
| 34 /// operation, process spawning, or file IO. |
| 35 static const IO = const Level._("IO "); |
| 36 |
| 37 /// Fine-grained and verbose additional information. Can be used to provide |
| 38 /// program state context for other logs (such as what pub was doing when an |
| 39 /// IO operation occurred) or just more detail for an operation. |
| 40 static const FINE = const Level._("FINE"); |
| 41 |
| 42 const Level._(this.name); |
| 43 final String name; |
| 44 |
| 45 String toString() => name; |
| 46 int get hashCode => name.hashCode; |
| 47 } |
| 48 |
| 49 /// A single log entry. |
| 50 class Entry { |
| 51 final Level level; |
| 52 final String message; |
| 53 |
| 54 Entry(this.level, this.message); |
| 55 } |
| 56 |
| 57 /// Logs [message] at [Level.ERROR]. |
| 58 void error(message) => write(Level.ERROR, message); |
| 59 |
| 60 /// Logs [message] at [Level.WARNING]. |
| 61 void warning(message) => write(Level.WARNING, message); |
| 62 |
| 63 /// Logs [message] at [Level.MESSAGE]. |
| 64 void message(message) => write(Level.MESSAGE, message); |
| 65 |
| 66 /// Logs [message] at [Level.IO]. |
| 67 void io(message) => write(Level.IO, message); |
| 68 |
| 69 /// Logs [message] at [Level.FINE]. |
| 70 void fine(message) => write(Level.FINE, message); |
| 71 |
| 72 /// Logs [message] at [level]. |
| 73 void write(Level level, message) { |
| 74 if (_loggers.isEmpty) showNormal(); |
| 75 |
| 76 var logFn = _loggers[level]; |
| 77 if (logFn != null) logFn(level, message); |
| 78 |
| 79 if (_transcript != null) { |
| 80 _transcript.add(new Entry(level, '$message')); |
| 81 } |
| 82 } |
| 83 |
| 84 /// Logs an asynchronous IO operation. Logs [startMessage] before the operation |
| 85 /// starts, then when [operation] completes, invokes [endMessage] with the |
| 86 /// completion value and logs the result of that. Returns a future that |
| 87 /// completes after the logging is done. |
| 88 /// |
| 89 /// If [endMessage] is omitted, then logs "Begin [startMessage]" before the |
| 90 /// operation and "End [startMessage]" after it. |
| 91 Future ioAsync(String startMessage, Future operation, |
| 92 [String endMessage(value)]) { |
| 93 if (endMessage == null) { |
| 94 io("Begin $startMessage."); |
| 95 } else { |
| 96 io(startMessage); |
| 97 } |
| 98 |
| 99 return operation.transform((result) { |
| 100 if (endMessage == null) { |
| 101 io("End $startMessage."); |
| 102 } else { |
| 103 io(endMessage(result)); |
| 104 } |
| 105 return result; |
| 106 }); |
| 107 } |
| 108 |
| 109 /// Logs the spawning of an [executable] process with [arguments] at [IO] |
| 110 /// level. |
| 111 void process(String executable, List<String> arguments) { |
| 112 io("Spawning $executable ${Strings.join(arguments, ' ')}"); |
| 113 } |
| 114 |
| 115 /// Logs the results of running [executable]. |
| 116 void processResult(String executable, PubProcessResult result) { |
| 117 // Log it all as one message so that it shows up as a single unit in the logs. |
| 118 var buffer = new StringBuffer(); |
| 119 buffer.add("Finished $executable. Exit code ${result.exitCode}."); |
| 120 |
| 121 dumpOutput(String name, List<String> output) { |
| 122 if (output.length == 0) { |
| 123 buffer.add("Nothing output on $name."); |
| 124 } else { |
| 125 buffer.add("$name:"); |
| 126 var numLines = 0; |
| 127 for (var line in output) { |
| 128 if (++numLines > 1000) { |
| 129 buffer.add('[${output.length - 1000}] more lines of output truncated..
.]'); |
| 130 break; |
| 131 } |
| 132 |
| 133 buffer.add(line); |
| 134 } |
| 135 } |
| 136 } |
| 137 |
| 138 dumpOutput("stdout", result.stdout); |
| 139 dumpOutput("stderr", result.stderr); |
| 140 |
| 141 io(buffer.toString()); |
| 142 } |
| 143 |
| 144 /// Enables recording of log entries. |
| 145 void recordTranscript() { |
| 146 _transcript = <Entry>[]; |
| 147 } |
| 148 |
| 149 /// If [recordTranscript()] was called, then prints the previously recorded log |
| 150 /// transcript to stderr. |
| 151 void dumpTranscript() { |
| 152 if (_transcript == null) return; |
| 153 |
| 154 stderr.writeString('---- Log transcript ----\n'); |
| 155 for (var entry in _transcript) { |
| 156 _logToStderrWithLabel(entry.level, entry.message); |
| 157 } |
| 158 stderr.writeString('---- End log transcript ----\n'); |
| 159 } |
| 160 |
| 161 /// Sets the verbosity to "normal", which shows errors, warnings, and messages. |
| 162 void showNormal() { |
| 163 _loggers[Level.ERROR] = _logToStderr; |
| 164 _loggers[Level.WARNING] = _logToStderr; |
| 165 _loggers[Level.MESSAGE] = _logToStdout; |
| 166 _loggers[Level.IO] = null; |
| 167 _loggers[Level.FINE] = null; |
| 168 } |
| 169 |
| 170 /// Sets the verbosity to "io", which shows errors, warnings, messages, and IO |
| 171 /// event logs. |
| 172 void showIO() { |
| 173 _loggers[Level.ERROR] = _logToStderrWithLabel; |
| 174 _loggers[Level.WARNING] = _logToStderrWithLabel; |
| 175 _loggers[Level.MESSAGE] = _logToStdoutWithLabel; |
| 176 _loggers[Level.IO] = _logToStderrWithLabel; |
| 177 _loggers[Level.FINE] = null; |
| 178 } |
| 179 |
| 180 /// Sets the verbosity to "all", which logs ALL the things. |
| 181 void showAll() { |
| 182 _loggers[Level.ERROR] = _logToStderrWithLabel; |
| 183 _loggers[Level.WARNING] = _logToStderrWithLabel; |
| 184 _loggers[Level.MESSAGE] = _logToStdoutWithLabel; |
| 185 _loggers[Level.IO] = _logToStderrWithLabel; |
| 186 _loggers[Level.FINE] = _logToStderrWithLabel; |
| 187 } |
| 188 |
| 189 /// Log function that prints the message to stdout. |
| 190 void _logToStdout(Level level, message) { |
| 191 print('$message'); |
| 192 } |
| 193 |
| 194 /// Log function that prints the message to stdout with the level name. |
| 195 void _logToStdoutWithLabel(Level level, message) { |
| 196 print(_splitAndPrefix(level, message)); |
| 197 } |
| 198 |
| 199 /// Log function that prints the message to stderr. |
| 200 void _logToStderr(Level level, message) { |
| 201 stderr.writeString('$message\n'); |
| 202 } |
| 203 |
| 204 /// Log function that prints the message to stderr with the level name. |
| 205 void _logToStderrWithLabel(Level level, message) { |
| 206 stderr.writeString(_splitAndPrefix(level, message)); |
| 207 stderr.writeString('\n'); |
| 208 } |
| 209 |
| 210 /// Add the level prefix to the first line of [message] and prefix subsequent |
| 211 /// lines with "|". |
| 212 String _splitAndPrefix(Level level, message) { |
| 213 // TODO(rnystrom): We're doing lots of splitting and joining in here. If that |
| 214 // becomes a performance problem, we can optimize this to write directly to |
| 215 // stdout/stderr a line at a time. |
| 216 return "$level: ${Strings.join(message.toString().split('\n'), '\n | ')}"; |
| 217 } |
OLD | NEW |