OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 /// Message logging. | 5 /// Message logging. |
6 library log; | 6 library log; |
7 | 7 |
8 import 'dart:io'; | 8 import 'dart:io'; |
9 import 'io.dart'; | 9 import 'io.dart'; |
10 | 10 |
11 typedef LogFn(Level level, message); | 11 typedef LogFn(Entry entry); |
12 final Map<Level, LogFn> _loggers = new Map<Level, LogFn>(); | 12 final Map<Level, LogFn> _loggers = new Map<Level, LogFn>(); |
13 | 13 |
14 /// The list of recorded log messages. Will only be recorded if | 14 /// The list of recorded log messages. Will only be recorded if |
15 /// [recordTranscript()] is called. | 15 /// [recordTranscript()] is called. |
16 List<Entry> _transcript; | 16 List<Entry> _transcript; |
17 | 17 |
18 /// An enum type for defining the different logging levels. By default, [ERROR] | 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 | 19 /// and [WARNING] messages are printed to sterr. [MESSAGE] messages are printed |
20 /// to stdout, and others are ignored. | 20 /// to stdout, and others are ignored. |
21 class Level { | 21 class Level { |
(...skipping 20 matching lines...) Expand all Loading... |
42 const Level._(this.name); | 42 const Level._(this.name); |
43 final String name; | 43 final String name; |
44 | 44 |
45 String toString() => name; | 45 String toString() => name; |
46 int get hashCode => name.hashCode; | 46 int get hashCode => name.hashCode; |
47 } | 47 } |
48 | 48 |
49 /// A single log entry. | 49 /// A single log entry. |
50 class Entry { | 50 class Entry { |
51 final Level level; | 51 final Level level; |
52 final String message; | 52 final List<String> lines; |
53 | 53 |
54 Entry(this.level, this.message); | 54 Entry(this.level, this.lines); |
55 } | 55 } |
56 | 56 |
57 /// Logs [message] at [Level.ERROR]. | 57 /// Logs [message] at [Level.ERROR]. |
58 void error(message) => write(Level.ERROR, message); | 58 void error(message) => write(Level.ERROR, message); |
59 | 59 |
60 /// Logs [message] at [Level.WARNING]. | 60 /// Logs [message] at [Level.WARNING]. |
61 void warning(message) => write(Level.WARNING, message); | 61 void warning(message) => write(Level.WARNING, message); |
62 | 62 |
63 /// Logs [message] at [Level.MESSAGE]. | 63 /// Logs [message] at [Level.MESSAGE]. |
64 void message(message) => write(Level.MESSAGE, message); | 64 void message(message) => write(Level.MESSAGE, message); |
65 | 65 |
66 /// Logs [message] at [Level.IO]. | 66 /// Logs [message] at [Level.IO]. |
67 void io(message) => write(Level.IO, message); | 67 void io(message) => write(Level.IO, message); |
68 | 68 |
69 /// Logs [message] at [Level.FINE]. | 69 /// Logs [message] at [Level.FINE]. |
70 void fine(message) => write(Level.FINE, message); | 70 void fine(message) => write(Level.FINE, message); |
71 | 71 |
72 /// Logs [message] at [level]. | 72 /// Logs [message] at [level]. |
73 void write(Level level, message) { | 73 void write(Level level, message) { |
74 if (_loggers.isEmpty) showNormal(); | 74 if (_loggers.isEmpty) showNormal(); |
75 | 75 |
| 76 var lines = message.toString().split(NEWLINE_PATTERN); |
| 77 var entry = new Entry(level, lines); |
| 78 |
76 var logFn = _loggers[level]; | 79 var logFn = _loggers[level]; |
77 if (logFn != null) logFn(level, message); | 80 if (logFn != null) logFn(entry); |
78 | 81 |
79 if (_transcript != null) { | 82 if (_transcript != null) _transcript.add(entry); |
80 _transcript.add(new Entry(level, '$message')); | |
81 } | |
82 } | 83 } |
83 | 84 |
84 /// Logs an asynchronous IO operation. Logs [startMessage] before the operation | 85 /// Logs an asynchronous IO operation. Logs [startMessage] before the operation |
85 /// starts, then when [operation] completes, invokes [endMessage] with the | 86 /// starts, then when [operation] completes, invokes [endMessage] with the |
86 /// completion value and logs the result of that. Returns a future that | 87 /// completion value and logs the result of that. Returns a future that |
87 /// completes after the logging is done. | 88 /// completes after the logging is done. |
88 /// | 89 /// |
89 /// If [endMessage] is omitted, then logs "Begin [startMessage]" before the | 90 /// If [endMessage] is omitted, then logs "Begin [startMessage]" before the |
90 /// operation and "End [startMessage]" after it. | 91 /// operation and "End [startMessage]" after it. |
91 Future ioAsync(String startMessage, Future operation, | 92 Future ioAsync(String startMessage, Future operation, |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
147 _transcript = <Entry>[]; | 148 _transcript = <Entry>[]; |
148 } | 149 } |
149 | 150 |
150 /// If [recordTranscript()] was called, then prints the previously recorded log | 151 /// If [recordTranscript()] was called, then prints the previously recorded log |
151 /// transcript to stderr. | 152 /// transcript to stderr. |
152 void dumpTranscript() { | 153 void dumpTranscript() { |
153 if (_transcript == null) return; | 154 if (_transcript == null) return; |
154 | 155 |
155 stderr.writeString('---- Log transcript ----\n'); | 156 stderr.writeString('---- Log transcript ----\n'); |
156 for (var entry in _transcript) { | 157 for (var entry in _transcript) { |
157 _logToStderrWithLabel(entry.level, entry.message); | 158 _logToStderrWithLabel(entry); |
158 } | 159 } |
159 stderr.writeString('---- End log transcript ----\n'); | 160 stderr.writeString('---- End log transcript ----\n'); |
160 } | 161 } |
161 | 162 |
162 /// Sets the verbosity to "normal", which shows errors, warnings, and messages. | 163 /// Sets the verbosity to "normal", which shows errors, warnings, and messages. |
163 void showNormal() { | 164 void showNormal() { |
164 _loggers[Level.ERROR] = _logToStderr; | 165 _loggers[Level.ERROR] = _logToStderr; |
165 _loggers[Level.WARNING] = _logToStderr; | 166 _loggers[Level.WARNING] = _logToStderr; |
166 _loggers[Level.MESSAGE] = _logToStdout; | 167 _loggers[Level.MESSAGE] = _logToStdout; |
167 _loggers[Level.IO] = null; | 168 _loggers[Level.IO] = null; |
(...skipping 13 matching lines...) Expand all Loading... |
181 /// Sets the verbosity to "all", which logs ALL the things. | 182 /// Sets the verbosity to "all", which logs ALL the things. |
182 void showAll() { | 183 void showAll() { |
183 _loggers[Level.ERROR] = _logToStderrWithLabel; | 184 _loggers[Level.ERROR] = _logToStderrWithLabel; |
184 _loggers[Level.WARNING] = _logToStderrWithLabel; | 185 _loggers[Level.WARNING] = _logToStderrWithLabel; |
185 _loggers[Level.MESSAGE] = _logToStdoutWithLabel; | 186 _loggers[Level.MESSAGE] = _logToStdoutWithLabel; |
186 _loggers[Level.IO] = _logToStderrWithLabel; | 187 _loggers[Level.IO] = _logToStderrWithLabel; |
187 _loggers[Level.FINE] = _logToStderrWithLabel; | 188 _loggers[Level.FINE] = _logToStderrWithLabel; |
188 } | 189 } |
189 | 190 |
190 /// Log function that prints the message to stdout. | 191 /// Log function that prints the message to stdout. |
191 void _logToStdout(Level level, message) { | 192 void _logToStdout(Entry entry) { |
192 print('$message'); | 193 _logToStream(stdout, entry, showLabel: false); |
193 } | 194 } |
194 | 195 |
195 /// Log function that prints the message to stdout with the level name. | 196 /// Log function that prints the message to stdout with the level name. |
196 void _logToStdoutWithLabel(Level level, message) { | 197 void _logToStdoutWithLabel(Entry entry) { |
197 print(_splitAndPrefix(level, message)); | 198 _logToStream(stdout, entry, showLabel: true); |
198 } | 199 } |
199 | 200 |
200 /// Log function that prints the message to stderr. | 201 /// Log function that prints the message to stderr. |
201 void _logToStderr(Level level, message) { | 202 void _logToStderr(Entry entry) { |
202 stderr.writeString('$message\n'); | 203 _logToStream(stderr, entry, showLabel: false); |
203 } | 204 } |
204 | 205 |
205 /// Log function that prints the message to stderr with the level name. | 206 /// Log function that prints the message to stderr with the level name. |
206 void _logToStderrWithLabel(Level level, message) { | 207 void _logToStderrWithLabel(Entry entry) { |
207 stderr.writeString(_splitAndPrefix(level, message)); | 208 _logToStream(stderr, entry, showLabel: true); |
208 stderr.writeString('\n'); | |
209 } | 209 } |
210 | 210 |
211 /// Add the level prefix to the first line of [message] and prefix subsequent | 211 void _logToStream(OutputStream stream, Entry entry, {bool showLabel}) { |
212 /// lines with "|". | 212 bool firstLine = true; |
213 String _splitAndPrefix(Level level, message) { | 213 for (var line in entry.lines) { |
214 // TODO(rnystrom): We're doing lots of splitting and joining in here. If that | 214 if (showLabel) { |
215 // becomes a performance problem, we can optimize this to write directly to | 215 if (firstLine) { |
216 // stdout/stderr a line at a time. | 216 stream.writeString(entry.level.name); |
217 return "$level: ${Strings.join(message.toString().split('\n'), '\n | ')}"; | 217 stream.writeString(': '); |
| 218 } else { |
| 219 stream.writeString(' | '); |
| 220 } |
| 221 } |
| 222 |
| 223 stream.writeString(line); |
| 224 stream.writeString('\n'); |
| 225 |
| 226 firstLine = false; |
| 227 } |
218 } | 228 } |
OLD | NEW |