OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 library stack_trace.chain; | 5 library stack_trace.chain; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:collection'; | 8 import 'dart:collection'; |
| 9 import 'dart:math' as math; |
9 | 10 |
10 import 'frame.dart'; | 11 import 'frame.dart'; |
11 import 'stack_zone_specification.dart'; | 12 import 'stack_zone_specification.dart'; |
12 import 'trace.dart'; | 13 import 'trace.dart'; |
13 import 'utils.dart'; | 14 import 'utils.dart'; |
14 | 15 |
15 /// A function that handles errors in the zone wrapped by [Chain.capture]. | 16 /// A function that handles errors in the zone wrapped by [Chain.capture]. |
16 typedef void ChainHandler(error, Chain chain); | 17 typedef void ChainHandler(error, Chain chain); |
17 | 18 |
| 19 /// The line used in the string representation of stack chains to represent |
| 20 /// the gap between traces. |
| 21 const _gap = '===== asynchronous gap ===========================\n'; |
| 22 |
18 /// A chain of stack traces. | 23 /// A chain of stack traces. |
19 /// | 24 /// |
20 /// A stack chain is a collection of one or more stack traces that collectively | 25 /// A stack chain is a collection of one or more stack traces that collectively |
21 /// represent the path from [main] through nested function calls to a particular | 26 /// represent the path from [main] through nested function calls to a particular |
22 /// code location, usually where an error was thrown. Multiple stack traces are | 27 /// code location, usually where an error was thrown. Multiple stack traces are |
23 /// necessary when using asynchronous functions, since the program's stack is | 28 /// necessary when using asynchronous functions, since the program's stack is |
24 /// reset before each asynchronous callback is run. | 29 /// reset before each asynchronous callback is run. |
25 /// | 30 /// |
26 /// Stack chains can be automatically tracked using [Chain.capture]. This sets | 31 /// Stack chains can be automatically tracked using [Chain.capture]. This sets |
27 /// up a new [Zone] in which the current stack chain is tracked and can be | 32 /// up a new [Zone] in which the current stack chain is tracked and can be |
28 /// accessed using [new Chain.current]. Any errors that would be top-leveled in | 33 /// accessed using [new Chain.current]. Any errors that would be top-leveled in |
29 /// the zone can be handled, along with their associated chains, with the | 34 /// the zone can be handled, along with their associated chains, with the |
30 /// `onError` callback. For example: | 35 /// `onError` callback. For example: |
31 /// | 36 /// |
32 /// Chain.capture(() { | 37 /// Chain.capture(() { |
33 /// // ... | 38 /// // ... |
34 /// }, onError: (error, stackChain) { | 39 /// }, onError: (error, stackChain) { |
35 /// print("Caught error $error\n" | 40 /// print("Caught error $error\n" |
36 /// "$stackChain"); | 41 /// "$stackChain"); |
37 /// }); | 42 /// }); |
38 class Chain implements StackTrace { | 43 class Chain implements StackTrace { |
39 /// The line used in the string representation of stack chains to represent | |
40 /// the gap between traces. | |
41 static const _GAP = '===== asynchronous gap ===========================\n'; | |
42 | 44 |
43 /// The stack traces that make up this chain. | 45 /// The stack traces that make up this chain. |
44 /// | 46 /// |
45 /// Like the frames in a stack trace, the traces are ordered from most local | 47 /// Like the frames in a stack trace, the traces are ordered from most local |
46 /// to least local. The first one is the trace where the actual exception was | 48 /// to least local. The first one is the trace where the actual exception was |
47 /// raised, the second one is where that callback was scheduled, and so on. | 49 /// raised, the second one is where that callback was scheduled, and so on. |
48 final List<Trace> traces; | 50 final List<Trace> traces; |
49 | 51 |
50 /// The [StackZoneSpecification] for the current zone. | 52 /// The [StackZoneSpecification] for the current zone. |
51 static StackZoneSpecification get _currentSpec => | 53 static StackZoneSpecification get _currentSpec => |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
173 | 175 |
174 return new Chain(nonEmptyTraces); | 176 return new Chain(nonEmptyTraces); |
175 } | 177 } |
176 | 178 |
177 /// Converts [this] to a [Trace]. | 179 /// Converts [this] to a [Trace]. |
178 /// | 180 /// |
179 /// The trace version of a chain is just the concatenation of all the traces | 181 /// The trace version of a chain is just the concatenation of all the traces |
180 /// in the chain. | 182 /// in the chain. |
181 Trace toTrace() => new Trace(flatten(traces.map((trace) => trace.frames))); | 183 Trace toTrace() => new Trace(flatten(traces.map((trace) => trace.frames))); |
182 | 184 |
183 String toString() => traces.join(_GAP); | 185 String toString() { |
| 186 // Figure out the longest path so we know how much to pad. |
| 187 var longest = traces.map((trace) { |
| 188 return trace.frames.map((frame) => frame.location.length) |
| 189 .fold(0, math.max); |
| 190 }).fold(0, math.max); |
| 191 |
| 192 // Don't call out to [Trace.toString] here because that doesn't ensure that |
| 193 // padding is consistent across all traces. |
| 194 return traces.map((trace) { |
| 195 return trace.frames.map((frame) { |
| 196 return '${padRight(frame.location, longest)} ${frame.member}\n'; |
| 197 }).join(); |
| 198 }).join(_gap); |
| 199 } |
184 } | 200 } |
OLD | NEW |