| 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 /** | 5 /** |
| 6 */ | 6 */ |
| 7 library logging; | 7 library logging; |
| 8 | 8 |
| 9 import 'dart:async'; | 9 import 'dart:async'; |
| 10 import 'dart:collection'; | 10 import 'dart:collection'; |
| 11 | 11 |
| 12 /** | 12 /// Whether to allow fine-grain logging and configuration of loggers in a |
| 13 * Whether to allow fine-grain logging and configuration of loggers in a | 13 /// hierarchy. |
| 14 * hierarchy. When false, all logging is merged in the root logger. | 14 /// |
| 15 */ | 15 /// When false, all logging is merged in the root logger. |
| 16 bool hierarchicalLoggingEnabled = false; | 16 bool hierarchicalLoggingEnabled = false; |
| 17 | 17 |
| 18 /** | 18 /// Automatically record stack traces for any message of this level or above. |
| 19 * Automatically record stack traces for any message of this level or above. | 19 /// |
| 20 * Because this is expensive, this is off by default. | 20 /// Because this is expensive, this is off by default. |
| 21 */ | |
| 22 Level recordStackTraceAtLevel = Level.OFF; | 21 Level recordStackTraceAtLevel = Level.OFF; |
| 23 | 22 |
| 24 /** | 23 /// Level for the root-logger. |
| 25 * Level for the root-logger. This will be the level of all loggers if | 24 /// |
| 26 * [hierarchicalLoggingEnabled] is false. | 25 /// This will be the level of all loggers if [hierarchicalLoggingEnabled] is |
| 27 */ | 26 /// false. |
| 28 Level _rootLevel = Level.INFO; | 27 Level _rootLevel = Level.INFO; |
| 29 | 28 |
| 30 /** | 29 /** |
| 31 * Use a [Logger] to log debug messages. [Logger]s are named using a | 30 * Use a [Logger] to log debug messages. |
| 32 * hierarchical dot-separated name convention. | 31 * |
| 32 * [Logger]s are named using a hierarchical dot-separated name convention. |
| 33 */ | 33 */ |
| 34 class Logger { | 34 class Logger { |
| 35 /** Simple name of this logger. */ | 35 /** Simple name of this logger. */ |
| 36 final String name; | 36 final String name; |
| 37 | 37 |
| 38 /** The full name of this logger, which includes the parent's full name. */ | 38 /** The full name of this logger, which includes the parent's full name. */ |
| 39 String get fullName => | 39 String get fullName => |
| 40 (parent == null || parent.name == '') ? name : '${parent.fullName}.$name'; | 40 (parent == null || parent.name == '') ? name : '${parent.fullName}.$name'; |
| 41 | 41 |
| 42 /** Parent of this logger in the hierarchy of loggers. */ | 42 /** Parent of this logger in the hierarchy of loggers. */ |
| (...skipping 15 matching lines...) Expand all Loading... |
| 58 * actual instance whenever it is called with the same string name. | 58 * actual instance whenever it is called with the same string name. |
| 59 */ | 59 */ |
| 60 factory Logger(String name) { | 60 factory Logger(String name) { |
| 61 return _loggers.putIfAbsent(name, () => new Logger._named(name)); | 61 return _loggers.putIfAbsent(name, () => new Logger._named(name)); |
| 62 } | 62 } |
| 63 | 63 |
| 64 /// Creates a new detached [Logger]. | 64 /// Creates a new detached [Logger]. |
| 65 /// | 65 /// |
| 66 /// Returns a new [Logger] instance (unlike `new Logger`, which returns a | 66 /// Returns a new [Logger] instance (unlike `new Logger`, which returns a |
| 67 /// [Logger] singleton), which doesn't have any parent or children, | 67 /// [Logger] singleton), which doesn't have any parent or children, |
| 68 /// and it's not a part of the global hierarchial loggers structure. | 68 /// and is not a part of the global hierarchical loggers structure. |
| 69 /// | 69 /// |
| 70 /// It can be useful when you just need a local short-living logger, | 70 /// It can be useful when you just need a local short-living logger, |
| 71 /// which you'd like to be garbage-collected later. | 71 /// which you'd like to be garbage-collected later. |
| 72 factory Logger.detached(String name) { | 72 factory Logger.detached(String name) { |
| 73 return new Logger._internal(name, null, new Map<String, Logger>()); | 73 return new Logger._internal(name, null, new Map<String, Logger>()); |
| 74 } | 74 } |
| 75 | 75 |
| 76 factory Logger._named(String name) { | 76 factory Logger._named(String name) { |
| 77 if (name.startsWith('.')) { | 77 if (name.startsWith('.')) { |
| 78 throw new ArgumentError("name shouldn't start with a '.'"); | 78 throw new ArgumentError("name shouldn't start with a '.'"); |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 116 } else { | 116 } else { |
| 117 if (parent != null) { | 117 if (parent != null) { |
| 118 throw new UnsupportedError( | 118 throw new UnsupportedError( |
| 119 'Please set "hierarchicalLoggingEnabled" to true if you want to ' | 119 'Please set "hierarchicalLoggingEnabled" to true if you want to ' |
| 120 'change the level on a non-root logger.'); | 120 'change the level on a non-root logger.'); |
| 121 } | 121 } |
| 122 _rootLevel = value; | 122 _rootLevel = value; |
| 123 } | 123 } |
| 124 } | 124 } |
| 125 | 125 |
| 126 /** | 126 /// Returns a stream of messages added to this [Logger]. |
| 127 * Returns an stream of messages added to this [Logger]. You can listen for | 127 /// |
| 128 * messages using the standard stream APIs, for instance: | 128 /// You can listen for messages using the standard stream APIs, for instance: |
| 129 * logger.onRecord.listen((record) { ... }); | 129 /// |
| 130 */ | 130 /// ```dart |
| 131 /// logger.onRecord.listen((record) { ... }); |
| 132 /// ``` |
| 131 Stream<LogRecord> get onRecord => _getStream(); | 133 Stream<LogRecord> get onRecord => _getStream(); |
| 132 | 134 |
| 133 void clearListeners() { | 135 void clearListeners() { |
| 134 if (hierarchicalLoggingEnabled || parent == null) { | 136 if (hierarchicalLoggingEnabled || parent == null) { |
| 135 if (_controller != null) { | 137 if (_controller != null) { |
| 136 _controller.close(); | 138 _controller.close(); |
| 137 _controller = null; | 139 _controller = null; |
| 138 } | 140 } |
| 139 } else { | 141 } else { |
| 140 root.clearListeners(); | 142 root.clearListeners(); |
| 141 } | 143 } |
| 142 } | 144 } |
| 143 | 145 |
| 144 /** Whether a message for [value]'s level is loggable in this logger. */ | 146 /** Whether a message for [value]'s level is loggable in this logger. */ |
| 145 bool isLoggable(Level value) => (value >= level); | 147 bool isLoggable(Level value) => (value >= level); |
| 146 | 148 |
| 147 /** | 149 /// Adds a log record for a [message] at a particular [logLevel] if |
| 148 * Adds a log record for a [message] at a particular [logLevel] if | 150 /// `isLoggable(logLevel)` is true. |
| 149 * `isLoggable(logLevel)` is true. | 151 /// |
| 150 * | 152 /// Use this method to create log entries for user-defined levels. To record a |
| 151 * Use this method to create log entries for user-defined levels. To record a | 153 /// message at a predefined level (e.g. [Level.INFO], [Level.WARNING], etc) |
| 152 * message at a predefined level (e.g. [Level.INFO], [Level.WARNING], etc) you | 154 /// you can use their specialized methods instead (e.g. [info], [warning], |
| 153 * can use their specialized methods instead (e.g. [info], [warning], etc). | 155 /// etc). |
| 154 * | 156 /// |
| 155 * If [message] is a [Function], it will be lazy evaluated. Additionally, if | 157 /// If [message] is a [Function], it will be lazy evaluated. Additionally, if |
| 156 * [message] or its evaluated value is not a [String], then 'toString()' will | 158 /// [message] or its evaluated value is not a [String], then 'toString()' will |
| 157 * be called on it and the result will be logged. | 159 /// be called on the object and the result will be logged. The log record will |
| 158 * | 160 /// contain a field holding the original object. |
| 159 * The log record will contain a field for the zone in which this call was | 161 /// |
| 160 * made. | 162 /// The log record will also contain a field for the zone in which this call |
| 161 * This can be advantagous if a log listener wants to handle records of | 163 /// was made. This can be advantageous if a log listener wants to handler |
| 162 * different zones differently (e.g. group log records by http-request if each | 164 /// records of different zones differently (e.g. group log records by HTTP |
| 163 * http-request handler runs in it's own zone). | 165 /// request if each HTTP request handler runs in it's own zone). |
| 164 */ | |
| 165 void log(Level logLevel, message, | 166 void log(Level logLevel, message, |
| 166 [Object error, StackTrace stackTrace, Zone zone]) { | 167 [Object error, StackTrace stackTrace, Zone zone]) { |
| 168 Object object; |
| 167 if (isLoggable(logLevel)) { | 169 if (isLoggable(logLevel)) { |
| 168 if (message is Function) message = message(); | 170 if (message is Function) message = message(); |
| 169 if (message is! String) message = message.toString(); | 171 if (message is! String) { |
| 172 object = message; |
| 173 message = message.toString(); |
| 174 } |
| 170 if (stackTrace == null && logLevel >= recordStackTraceAtLevel) { | 175 if (stackTrace == null && logLevel >= recordStackTraceAtLevel) { |
| 171 try { | 176 try { |
| 172 throw "autogenerated stack trace for $logLevel $message"; | 177 throw "autogenerated stack trace for $logLevel $message"; |
| 173 } catch (e, t) { | 178 } catch (e, t) { |
| 174 stackTrace = t; | 179 stackTrace = t; |
| 175 if (error == null) error = e; | 180 if (error == null) error = e; |
| 176 } | 181 } |
| 177 } | 182 } |
| 178 if (zone == null) zone = Zone.current; | 183 if (zone == null) zone = Zone.current; |
| 179 | 184 |
| 180 var record = | 185 var record = new LogRecord( |
| 181 new LogRecord(logLevel, message, fullName, error, stackTrace, zone); | 186 logLevel, message, fullName, error, stackTrace, zone, object); |
| 182 | 187 |
| 183 if (hierarchicalLoggingEnabled) { | 188 if (hierarchicalLoggingEnabled) { |
| 184 var target = this; | 189 var target = this; |
| 185 while (target != null) { | 190 while (target != null) { |
| 186 target._publish(record); | 191 target._publish(record); |
| 187 target = target.parent; | 192 target = target.parent; |
| 188 } | 193 } |
| 189 } else { | 194 } else { |
| 190 root._publish(record); | 195 root._publish(record); |
| 191 } | 196 } |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 328 } | 333 } |
| 329 | 334 |
| 330 /** | 335 /** |
| 331 * A log entry representation used to propagate information from [Logger] to | 336 * A log entry representation used to propagate information from [Logger] to |
| 332 * individual [Handler]s. | 337 * individual [Handler]s. |
| 333 */ | 338 */ |
| 334 class LogRecord { | 339 class LogRecord { |
| 335 final Level level; | 340 final Level level; |
| 336 final String message; | 341 final String message; |
| 337 | 342 |
| 343 /** Non-string message passed to Logger. */ |
| 344 final Object object; |
| 345 |
| 338 /** Logger where this record is stored. */ | 346 /** Logger where this record is stored. */ |
| 339 final String loggerName; | 347 final String loggerName; |
| 340 | 348 |
| 341 /** Time when this record was created. */ | 349 /** Time when this record was created. */ |
| 342 final DateTime time; | 350 final DateTime time; |
| 343 | 351 |
| 344 /** Unique sequence number greater than all log records created before it. */ | 352 /** Unique sequence number greater than all log records created before it. */ |
| 345 final int sequenceNumber; | 353 final int sequenceNumber; |
| 346 | 354 |
| 347 static int _nextNumber = 0; | 355 static int _nextNumber = 0; |
| 348 | 356 |
| 349 /** Associated error (if any) when recording errors messages. */ | 357 /** Associated error (if any) when recording errors messages. */ |
| 350 final Object error; | 358 final Object error; |
| 351 | 359 |
| 352 /** Associated stackTrace (if any) when recording errors messages. */ | 360 /** Associated stackTrace (if any) when recording errors messages. */ |
| 353 final StackTrace stackTrace; | 361 final StackTrace stackTrace; |
| 354 | 362 |
| 355 /** Zone of the calling code which resulted in this LogRecord. */ | 363 /** Zone of the calling code which resulted in this LogRecord. */ |
| 356 final Zone zone; | 364 final Zone zone; |
| 357 | 365 |
| 358 LogRecord(this.level, this.message, this.loggerName, | 366 LogRecord(this.level, this.message, this.loggerName, |
| 359 [this.error, this.stackTrace, this.zone]) | 367 [this.error, this.stackTrace, this.zone, this.object]) |
| 360 : time = new DateTime.now(), | 368 : time = new DateTime.now(), |
| 361 sequenceNumber = LogRecord._nextNumber++; | 369 sequenceNumber = LogRecord._nextNumber++; |
| 362 | 370 |
| 363 String toString() => '[${level.name}] $loggerName: $message'; | 371 String toString() => '[${level.name}] $loggerName: $message'; |
| 364 } | 372 } |
| OLD | NEW |