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 |