Chromium Code Reviews| 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 /** | |
| 6 * Provides APIs for debugging and error logging. This library introduces | |
| 7 * abstractions similar to those used in other languages, such as the Closure JS | |
| 8 * Logger and java.util.logging.Logger. | |
| 9 */ | |
| 10 #library('logging'); | |
| 11 | |
| 12 /** | |
| 13 * Whether to allow fine-grain logging and configuration of loggers in a | |
| 14 * hierarchy. When false, all logging is merged in the root logger. | |
| 15 */ | |
| 16 bool hierarchicalLoggingEnabled = false; | |
| 17 | |
| 18 /** | |
| 19 * Level for the root-logger. This will be the level of all loggers if | |
| 20 * [hierarchicalLoggingEnabled] is false. | |
| 21 */ | |
| 22 Level _rootLevel = Level.INFO; | |
| 23 | |
| 24 | |
| 25 /** | |
| 26 * Use a [Logger] to log debug messages. [Logger]s are named using a | |
| 27 * hierarchical dot-separated name convention. | |
| 28 */ | |
| 29 class Logger { | |
| 30 /** Simple name of this logger. */ | |
| 31 final String name; | |
| 32 | |
| 33 /** The full name of this logger, which includes also the parent's names. */ | |
|
gram
2012/06/29 19:55:21
Remove 'also'
Siggi Cherem (dart-lang)
2012/06/29 20:16:52
Done.
| |
| 34 String get fullName() => | |
| 35 (parent == null || parent.name == '') ? name : '${parent.fullName}.$name'; | |
|
gram
2012/06/29 19:55:21
What is I have three levels A, B, C, and A is name
Siggi Cherem (dart-lang)
2012/06/29 20:16:52
as we discussed in person - this shouldn't be a pr
| |
| 36 | |
| 37 /** Parent of this logger in the hierarchy of loggers. */ | |
| 38 final Logger parent; | |
| 39 | |
| 40 /** Logging [Level] used for entries generated on this logger. */ | |
| 41 Level _level; | |
| 42 | |
| 43 /** Children in the hierarchy of loggers, indexed by their simple names. */ | |
| 44 Map<String, Logger> children; | |
| 45 | |
| 46 /** Handlers used to process log entries in this logger. */ | |
| 47 List<LoggerHandler> _handlers; | |
| 48 | |
| 49 /** | |
| 50 * Singleton constructor. Calling `new Logger(name)` will return the same | |
| 51 * actual instance whenever it is called with the same string name. | |
| 52 */ | |
| 53 factory Logger(String name) { | |
| 54 if (_loggers == null) _loggers = <Logger>{}; | |
| 55 if (_loggers.containsKey(name)) return _loggers[name]; | |
|
gram
2012/06/29 19:55:21
you could have an "else" here.
Siggi Cherem (dart-lang)
2012/06/29 20:16:52
true. as we briefly discussed :), left as is to ma
| |
| 56 | |
| 57 // Split hierarchical names (separated with '.'). | |
| 58 int dot = name.lastIndexOf('.'); | |
| 59 Logger parent = null; | |
| 60 String thisName; | |
| 61 if (dot == -1) { | |
| 62 if (name != '') parent = new Logger(''); | |
| 63 thisName = name; | |
| 64 } else { | |
| 65 parent = new Logger(name.substring(0, dot)); | |
| 66 thisName = name.substring(dot + 1); | |
| 67 } | |
| 68 final res = new Logger._internal(thisName, parent); | |
| 69 _loggers[name] = res; | |
| 70 return res; | |
| 71 } | |
| 72 | |
| 73 Logger._internal(this.name, this.parent) | |
| 74 : children = new Map<String, Logger>() { | |
| 75 if (parent != null) parent.children[name] = this; | |
| 76 } | |
| 77 | |
| 78 /** | |
| 79 * Effective level considering the levels established in this logger's parents | |
| 80 * (when [hierarchicalLoggingEnabled] is true). | |
| 81 */ | |
| 82 Level get level() { | |
| 83 if (hierarchicalLoggingEnabled) { | |
| 84 if (_level != null) return _level; | |
| 85 if (parent != null) return parent.level; | |
| 86 } | |
| 87 return _rootLevel; | |
| 88 } | |
| 89 | |
| 90 /** Override the level for this particular [Logger] and its children. */ | |
| 91 Level set level(value) { | |
| 92 if (hierarchicalLoggingEnabled && parent != null) { | |
| 93 _level = value; | |
| 94 } else { | |
| 95 if (parent != null) { | |
| 96 throw new UnsupportedOperationException( | |
| 97 'Please set "hierarchicalLoggingEnabled" to true if you want to ' | |
| 98 'change the level on a non-root logger.'); | |
| 99 } | |
| 100 _rootLevel = value; | |
| 101 } | |
| 102 } | |
| 103 | |
| 104 /** | |
| 105 * Returns an event manager for this [Logger]. You can listen for log messages | |
| 106 * by adding a [LoggerHandler] to an event from the event manager, for | |
| 107 * instance: | |
| 108 * logger.on.record.add((record) { ... }); | |
| 109 */ | |
| 110 LoggerEvents get on() => new LoggerEvents(this); | |
| 111 | |
| 112 /** Adds a handler to listen whenever a log record is added to this logger. */ | |
| 113 void _addHandler(LoggerHandler handler) { | |
| 114 if (hierarchicalLoggingEnabled || parent == null) { | |
| 115 if (_handlers == null) { | |
| 116 _handlers = new List<LoggerHandler>(); | |
| 117 } | |
| 118 _handlers.add(handler); | |
| 119 } else { | |
| 120 root._addHandler(handler); | |
| 121 } | |
| 122 } | |
| 123 | |
| 124 /** Remove a previously added handler. */ | |
| 125 void _removeHandler(LoggerHandler handler) { | |
| 126 if (hierarchicalLoggingEnabled || parent == null) { | |
| 127 if (_handlers == null) return; | |
| 128 int index = _handlers.indexOf(handler); | |
| 129 if (index != -1) _handlers.removeRange(index, 1); | |
| 130 } else { | |
| 131 root._removeHandler(handler); | |
| 132 } | |
| 133 } | |
| 134 | |
| 135 /** Removes all handlers previously added to this logger. */ | |
| 136 void _clearHandlers() { | |
| 137 if (hierarchicalLoggingEnabled || parent == null) { | |
| 138 _handlers = null; | |
| 139 } else { | |
| 140 root._clearHandlers(); | |
| 141 } | |
| 142 } | |
| 143 | |
| 144 /** Whether a message for [value]'s level is loggable in this logger. */ | |
| 145 bool isLoggable(Level value) => (value >= level); | |
| 146 | |
| 147 /** | |
| 148 * Adds a log record for a [message] at a particular [logLevel] if | |
| 149 * `isLoggable(logLevel)` is true. Use this method to create log entries for | |
| 150 * user-defined levels. To record a message at a predefined level (e.g. | |
| 151 * [Level.INFO], [Level.WARNING], etc) you can use their specialized methods | |
| 152 * instead (e.g. [info], [warning], etc). | |
| 153 */ | |
| 154 // TODO(sigmund): add support for logging exceptions. | |
| 155 void log(Level logLevel, String message) { | |
| 156 if (isLoggable(logLevel)) { | |
| 157 var record = new LogRecord(logLevel, message, fullName); | |
| 158 if (hierarchicalLoggingEnabled) { | |
| 159 var target = this; | |
| 160 while (target != null) { | |
| 161 target._publish(record); | |
| 162 target = target.parent; | |
| 163 } | |
| 164 } else { | |
| 165 root._publish(record); | |
| 166 } | |
| 167 } | |
| 168 } | |
| 169 | |
| 170 /** Log message at level [Level.FINEST]. */ | |
| 171 void finest(String message) => log(Level.FINEST, message); | |
| 172 | |
| 173 /** Log message at level [Level.FINER]. */ | |
| 174 void finer(String message) => log(Level.FINER, message); | |
| 175 | |
| 176 /** Log message at level [Level.FINE]. */ | |
| 177 void fine(String message) => log(Level.FINE, message); | |
| 178 | |
| 179 /** Log message at level [Level.CONFIG]. */ | |
| 180 void config(String message) => log(Level.CONFIG, message); | |
| 181 | |
| 182 /** Log message at level [Level.INFO]. */ | |
| 183 void info(String message) => log(Level.INFO, message); | |
| 184 | |
| 185 /** Log message at level [Level.WARNING]. */ | |
| 186 void warning(String message) => log(Level.WARNING, message); | |
| 187 | |
| 188 /** Log message at level [Level.SEVERE]. */ | |
| 189 void severe(String message) => log(Level.SEVERE, message); | |
| 190 | |
| 191 /** Log message at level [Level.SHOUT]. */ | |
| 192 void shout(String message) => log(Level.SHOUT, message); | |
| 193 | |
| 194 void _publish(LogRecord record) { | |
| 195 if (_handlers != null) { | |
| 196 _handlers.forEach((h) => h(record)); | |
| 197 } | |
| 198 } | |
| 199 | |
| 200 /** Top-level root [Logger]. */ | |
| 201 static get root() => new Logger(''); | |
| 202 | |
| 203 /** All [Logger]s in the system. */ | |
| 204 static Map<String, Logger> _loggers; | |
| 205 } | |
| 206 | |
| 207 | |
| 208 /** Handler callback to process log entries as they are added to a [Logger]. */ | |
| 209 typedef void LoggerHandler(LogRecord); | |
| 210 | |
| 211 | |
| 212 /** Event manager for a [Logger] (holds events that a [Logger] can fire). */ | |
| 213 class LoggerEvents { | |
| 214 final Logger _logger; | |
| 215 | |
| 216 LoggerEvents(this._logger); | |
| 217 | |
| 218 /** Event fired when a log record is added to a [Logger]. */ | |
| 219 LoggerHandlerList get record() => new LoggerHandlerList(_logger); | |
| 220 } | |
| 221 | |
| 222 | |
| 223 /** List of handlers that will be called on a logger event. */ | |
| 224 class LoggerHandlerList { | |
| 225 Logger _logger; | |
| 226 | |
| 227 LoggerHandlerList(this._logger); | |
| 228 | |
| 229 void add(LoggerHandler handler) => _logger._addHandler(handler); | |
| 230 void remove(LoggerHandler handler) => _logger._removeHandler(handler); | |
| 231 void clear() => _logger._clearHandlers(); | |
| 232 } | |
| 233 | |
| 234 | |
| 235 /** | |
| 236 * [Level]s to control logging output. Logging can be enabled to include all | |
| 237 * levels above certain [Level]. [Level]s are ordered using an integer | |
| 238 * value [Level.value]. The predefined [Level] constants below are sorted as | |
| 239 * follows (in descending order): [Level.SHOUT], [Level.SEVERE], | |
| 240 * [Level.WARNING], [Level.INFO], [Level.CONFIG], [Level.FINE], [Level.FINER], | |
| 241 * [Level.FINEST], and [Level.ALL]. | |
| 242 * | |
| 243 * We recommend using one of the predefined logging levels. If you define your | |
| 244 * own level, make sure you use a value between those used in [Level.ALL] and | |
| 245 * [Level.OFF]. | |
| 246 */ | |
| 247 class Level implements Comparable, Hashable { | |
| 248 | |
| 249 // TODO(sigmund): mark name/value as 'const' when the language supports it. | |
| 250 final String name; | |
| 251 | |
| 252 /** | |
| 253 * Unique value for this level. Used to order levels, so filtering can exclude | |
| 254 * messages whose level is under certain value. | |
| 255 */ | |
| 256 final int value; | |
| 257 | |
| 258 const Level(this.name, this.value); | |
| 259 | |
| 260 /** Special key to turn on logging for all levels ([value] = 0). */ | |
| 261 static final Level ALL = const Level('ALL', 0); | |
| 262 | |
| 263 /** Special key to turn off all logging ([value] = 2000). */ | |
| 264 static final Level OFF = const Level('OFF', 2000); | |
| 265 | |
| 266 /** Key for highly detailed tracing ([value] = 300). */ | |
| 267 static final Level FINEST = const Level('FINEST', 300); | |
| 268 | |
| 269 /** Key for fairly detailed tracing ([value] = 400). */ | |
| 270 static final Level FINER = const Level('FINER', 400); | |
| 271 | |
| 272 /** Key for tracing information ([value] = 500). */ | |
| 273 static final Level FINE = const Level('FINE', 500); | |
| 274 | |
| 275 /** Key for static configuration messages ([value] = 700). */ | |
| 276 static final Level CONFIG = const Level('CONFIG', 700); | |
| 277 | |
| 278 /** Key for informational messages ([value] = 800). */ | |
| 279 static final Level INFO = const Level('INFO', 800); | |
| 280 | |
| 281 /** Key for potential problems ([value] = 900). */ | |
| 282 static final Level WARNING = const Level('WARNING', 900); | |
| 283 | |
| 284 /** Key for serious failures ([value] = 1000). */ | |
| 285 static final Level SEVERE = const Level('SEVERE', 1000); | |
| 286 | |
| 287 /** Key for extra debugging loudness ([value] = 1200). */ | |
| 288 static final Level SHOUT = const Level('SHOUT', 1200); | |
| 289 | |
| 290 bool operator ==(Level other) => other != null && value == other.value; | |
| 291 bool operator <(Level other) => value < other.value; | |
| 292 bool operator <=(Level other) => value <= other.value; | |
| 293 bool operator >(Level other) => value > other.value; | |
| 294 bool operator >=(Level other) => value >= other.value; | |
| 295 int compareTo(Level other) => value - other.value; | |
| 296 int hashCode() => value; | |
| 297 String toString() => name; | |
| 298 } | |
| 299 | |
| 300 | |
| 301 /** | |
| 302 * A log entry representation used to propagate information from [Logger] to | |
| 303 * individual [Handler]s. | |
| 304 */ | |
| 305 class LogRecord { | |
| 306 final Level level; | |
| 307 final String message; | |
| 308 | |
| 309 /** Logger where this record is stored. */ | |
| 310 final String loggerName; | |
| 311 | |
| 312 /** Time when this record was created. */ | |
| 313 final Date time; | |
| 314 | |
| 315 /** Unique sequence number greater than all log records created before it. */ | |
| 316 final int sequenceNumber; | |
| 317 | |
| 318 static int _nextNumber = 0; | |
| 319 | |
| 320 /** Associated exception (if any) when recording errors messages. */ | |
| 321 Exception exception; | |
| 322 | |
| 323 /** Associated exception message (if any) when recording errors messages. */ | |
| 324 String exceptionText; | |
| 325 | |
| 326 LogRecord( | |
| 327 this.level, this.message, this.loggerName, | |
| 328 [time, this.exception, this.exceptionText]) : | |
| 329 this.time = (time == null) ? new Date.now() : time, | |
| 330 this.sequenceNumber = LogRecord._nextNumber++; | |
| 331 } | |
| OLD | NEW |