OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2014, 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 library instrumentation; |
| 6 |
| 7 import 'dart:convert'; |
| 8 |
| 9 import 'package:analyzer/task/model.dart'; |
| 10 |
| 11 /** |
| 12 * A container with analysis performance constants. |
| 13 */ |
| 14 class AnalysisPerformanceKind { |
| 15 static const String FULL = 'analysis_full'; |
| 16 static const String INCREMENTAL = 'analysis_incremental'; |
| 17 } |
| 18 |
| 19 /** |
| 20 * The interface used by client code to communicate with an instrumentation |
| 21 * server. |
| 22 */ |
| 23 abstract class InstrumentationServer { |
| 24 /** |
| 25 * Pass the given [message] to the instrumentation server so that it will be |
| 26 * logged with other messages. |
| 27 * |
| 28 * This method should be used for most logging. |
| 29 */ |
| 30 void log(String message); |
| 31 |
| 32 /** |
| 33 * Pass the given [message] to the instrumentation server so that it will be |
| 34 * logged with other messages. |
| 35 * |
| 36 * This method should only be used for logging high priority messages, such as |
| 37 * exceptions that cause the server to shutdown. |
| 38 */ |
| 39 void logWithPriority(String message); |
| 40 |
| 41 /** |
| 42 * Signal that the client is done communicating with the instrumentation |
| 43 * server. This method should be invoked exactly one time and no other methods |
| 44 * should be invoked on this instance after this method has been invoked. |
| 45 */ |
| 46 void shutdown(); |
| 47 } |
| 48 |
| 49 /** |
| 50 * The interface used by client code to communicate with an instrumentation |
| 51 * server by wrapping an [InstrumentationServer]. |
| 52 */ |
| 53 class InstrumentationService { |
| 54 /** |
| 55 * An instrumentation service that will not log any instrumentation data. |
| 56 */ |
| 57 static final InstrumentationService NULL_SERVICE = |
| 58 new InstrumentationService(null); |
| 59 |
| 60 static const String TAG_ANALYSIS_TASK = 'Task'; |
| 61 static const String TAG_ERROR = 'Err'; |
| 62 static const String TAG_EXCEPTION = 'Ex'; |
| 63 static const String TAG_FILE_READ = 'Read'; |
| 64 static const String TAG_LOG_ENTRY = 'Log'; |
| 65 static const String TAG_NOTIFICATION = 'Noti'; |
| 66 static const String TAG_PERFORMANCE = 'Perf'; |
| 67 static const String TAG_REQUEST = 'Req'; |
| 68 static const String TAG_RESPONSE = 'Res'; |
| 69 static const String TAG_SUBPROCESS_START = 'SPStart'; |
| 70 static const String TAG_SUBPROCESS_RESULT = 'SPResult'; |
| 71 static const String TAG_VERSION = 'Ver'; |
| 72 static const String TAG_WATCH_EVENT = 'Watch'; |
| 73 |
| 74 /** |
| 75 * The instrumentation server used to communicate with the server, or `null` |
| 76 * if instrumentation data should not be logged. |
| 77 */ |
| 78 InstrumentationServer _instrumentationServer; |
| 79 |
| 80 /** |
| 81 * Counter used to generate unique ID's for [logSubprocessStart]. |
| 82 */ |
| 83 int _subprocessCounter = 0; |
| 84 |
| 85 /** |
| 86 * Initialize a newly created instrumentation service to comunicate with the |
| 87 * given [instrumentationServer]. |
| 88 */ |
| 89 InstrumentationService(this._instrumentationServer); |
| 90 |
| 91 /** |
| 92 * Return `true` if this [InstrumentationService] was initialized with a |
| 93 * non-`null` server (and hence instrumentation is active). |
| 94 */ |
| 95 bool get isActive => _instrumentationServer != null; |
| 96 |
| 97 /** |
| 98 * The current time, expressed as a decimal encoded number of milliseconds. |
| 99 */ |
| 100 String get _timestamp => new DateTime.now().millisecondsSinceEpoch.toString(); |
| 101 |
| 102 /** |
| 103 * Log that the given analysis [task] is being performed in the given |
| 104 * [context]. |
| 105 */ |
| 106 void logAnalysisTask(String context, dynamic task) { |
| 107 // TODO(brianwilkerson) When the old task model is removed, change the |
| 108 // parameter type to AnalysisTask. |
| 109 if (_instrumentationServer != null) { |
| 110 String description = |
| 111 (task is AnalysisTask) ? task.description : task.toString(); |
| 112 _instrumentationServer |
| 113 .log(_join([TAG_ANALYSIS_TASK, context, description])); |
| 114 } |
| 115 } |
| 116 |
| 117 /** |
| 118 * Log the fact that an error, described by the given [message], has occurred. |
| 119 */ |
| 120 void logError(String message) { |
| 121 _log(TAG_ERROR, message); |
| 122 } |
| 123 |
| 124 /** |
| 125 * Log that the given non-priority [exception] was thrown, with the given |
| 126 * [stackTrace]. |
| 127 */ |
| 128 void logException(dynamic exception, StackTrace stackTrace) { |
| 129 if (_instrumentationServer != null) { |
| 130 String message = _toString(exception); |
| 131 String trace = _toString(stackTrace); |
| 132 _instrumentationServer.log(_join([TAG_EXCEPTION, message, trace])); |
| 133 } |
| 134 } |
| 135 |
| 136 /** |
| 137 * Log that the contents of the file with the given [path] were read. The file |
| 138 * had the given [content] and [modificationTime]. |
| 139 */ |
| 140 void logFileRead(String path, int modificationTime, String content) { |
| 141 if (_instrumentationServer != null) { |
| 142 String timeStamp = _toString(modificationTime); |
| 143 _instrumentationServer |
| 144 .log(_join([TAG_FILE_READ, path, timeStamp, content])); |
| 145 } |
| 146 } |
| 147 |
| 148 /** |
| 149 * Log that a log entry that was written to the analysis engine's log. The log |
| 150 * entry has the given [level] and [message], and was created at the given |
| 151 * [time]. |
| 152 */ |
| 153 void logLogEntry(String level, DateTime time, String message, Object exception
, StackTrace stackTrace) { |
| 154 if (_instrumentationServer != null) { |
| 155 String timeStamp = |
| 156 time == null ? 'null' : time.millisecondsSinceEpoch.toString(); |
| 157 String exceptionText = exception.toString(); |
| 158 String stackTraceText = stackTrace.toString(); |
| 159 _instrumentationServer |
| 160 .log(_join([TAG_LOG_ENTRY, level, timeStamp, message, exceptionText, s
tackTraceText])); |
| 161 } |
| 162 } |
| 163 |
| 164 /** |
| 165 * Log that a notification has been sent to the client. |
| 166 */ |
| 167 void logNotification(String notification) { |
| 168 _log(TAG_NOTIFICATION, notification); |
| 169 } |
| 170 |
| 171 /** |
| 172 * Log the given performance fact. |
| 173 */ |
| 174 void logPerformance(String kind, Stopwatch sw, String message) { |
| 175 sw.stop(); |
| 176 String elapsed = sw.elapsedMilliseconds.toString(); |
| 177 if (_instrumentationServer != null) { |
| 178 _instrumentationServer |
| 179 .log(_join([TAG_PERFORMANCE, kind, elapsed, message])); |
| 180 } |
| 181 } |
| 182 |
| 183 /** |
| 184 * Log that the given priority [exception] was thrown, with the given |
| 185 * [stackTrace]. |
| 186 */ |
| 187 void logPriorityException(dynamic exception, StackTrace stackTrace) { |
| 188 if (_instrumentationServer != null) { |
| 189 String message = _toString(exception); |
| 190 String trace = _toString(stackTrace); |
| 191 _instrumentationServer |
| 192 .logWithPriority(_join([TAG_EXCEPTION, message, trace])); |
| 193 } |
| 194 } |
| 195 |
| 196 /** |
| 197 * Log that a request has been sent to the client. |
| 198 */ |
| 199 void logRequest(String request) { |
| 200 _log(TAG_REQUEST, request); |
| 201 } |
| 202 |
| 203 /** |
| 204 * Log that a response has been sent to the client. |
| 205 */ |
| 206 void logResponse(String response) { |
| 207 _log(TAG_RESPONSE, response); |
| 208 } |
| 209 |
| 210 /** |
| 211 * Log the result of executing a subprocess. [subprocessId] should be the |
| 212 * unique IDreturned by [logSubprocessStart]. |
| 213 */ |
| 214 void logSubprocessResult( |
| 215 int subprocessId, int exitCode, String stdout, String stderr) { |
| 216 if (_instrumentationServer != null) { |
| 217 _instrumentationServer.log(_join([ |
| 218 TAG_SUBPROCESS_RESULT, |
| 219 subprocessId.toString(), |
| 220 exitCode.toString(), |
| 221 JSON.encode(stdout), |
| 222 JSON.encode(stderr) |
| 223 ])); |
| 224 } |
| 225 } |
| 226 |
| 227 /** |
| 228 * Log that the given subprocess is about to be executed. Returns a unique |
| 229 * identifier that can be used to identify the subprocess for later log |
| 230 * entries. |
| 231 */ |
| 232 int logSubprocessStart( |
| 233 String executablePath, List<String> arguments, String workingDirectory) { |
| 234 int subprocessId = _subprocessCounter++; |
| 235 if (_instrumentationServer != null) { |
| 236 _instrumentationServer.log(_join([ |
| 237 TAG_SUBPROCESS_START, |
| 238 subprocessId.toString(), |
| 239 executablePath, |
| 240 workingDirectory, |
| 241 JSON.encode(arguments) |
| 242 ])); |
| 243 } |
| 244 return subprocessId; |
| 245 } |
| 246 |
| 247 /** |
| 248 * Signal that the client has started analysis server. |
| 249 * This method should be invoked exactly one time. |
| 250 */ |
| 251 void logVersion(String uuid, String clientId, String clientVersion, |
| 252 String serverVersion, String sdkVersion) { |
| 253 String normalize(String value) => |
| 254 value != null && value.length > 0 ? value : 'unknown'; |
| 255 |
| 256 if (_instrumentationServer != null) { |
| 257 _instrumentationServer.logWithPriority(_join([ |
| 258 TAG_VERSION, |
| 259 uuid, |
| 260 normalize(clientId), |
| 261 normalize(clientVersion), |
| 262 serverVersion, |
| 263 sdkVersion |
| 264 ])); |
| 265 } |
| 266 } |
| 267 |
| 268 /** |
| 269 * Log that the file system watcher sent an event. The [folderPath] is the |
| 270 * path to the folder containing the changed file, the [filePath] is the path |
| 271 * of the file that changed, and the [changeType] indicates what kind of |
| 272 * change occurred. |
| 273 */ |
| 274 void logWatchEvent(String folderPath, String filePath, String changeType) { |
| 275 if (_instrumentationServer != null) { |
| 276 _instrumentationServer |
| 277 .log(_join([TAG_WATCH_EVENT, folderPath, filePath, changeType])); |
| 278 } |
| 279 } |
| 280 |
| 281 /** |
| 282 * Signal that the client is done communicating with the instrumentation |
| 283 * server. This method should be invoked exactly one time and no other methods |
| 284 * should be invoked on this instance after this method has been invoked. |
| 285 */ |
| 286 void shutdown() { |
| 287 if (_instrumentationServer != null) { |
| 288 _instrumentationServer.shutdown(); |
| 289 _instrumentationServer = null; |
| 290 } |
| 291 } |
| 292 |
| 293 /** |
| 294 * Write an escaped version of the given [field] to the given [buffer]. |
| 295 */ |
| 296 void _escape(StringBuffer buffer, String field) { |
| 297 int index = field.indexOf(':'); |
| 298 if (index < 0) { |
| 299 buffer.write(field); |
| 300 return; |
| 301 } |
| 302 int start = 0; |
| 303 while (index >= 0) { |
| 304 buffer.write(field.substring(start, index)); |
| 305 buffer.write('::'); |
| 306 start = index + 1; |
| 307 index = field.indexOf(':', start); |
| 308 } |
| 309 buffer.write(field.substring(start)); |
| 310 } |
| 311 |
| 312 /** |
| 313 * Return the result of joining the values of the given fields, escaping the |
| 314 * separator character by doubling it. |
| 315 */ |
| 316 String _join(List<String> fields) { |
| 317 StringBuffer buffer = new StringBuffer(); |
| 318 buffer.write(_timestamp); |
| 319 for (String field in fields) { |
| 320 buffer.write(':'); |
| 321 _escape(buffer, field); |
| 322 } |
| 323 return buffer.toString(); |
| 324 } |
| 325 |
| 326 /** |
| 327 * Log the given message with the given tag. |
| 328 */ |
| 329 void _log(String tag, String message) { |
| 330 if (_instrumentationServer != null) { |
| 331 _instrumentationServer.log(_join([tag, message])); |
| 332 } |
| 333 } |
| 334 |
| 335 /** |
| 336 * Convert the given [object] to a string. |
| 337 */ |
| 338 String _toString(Object object) { |
| 339 if (object == null) { |
| 340 return 'null'; |
| 341 } |
| 342 return object.toString(); |
| 343 } |
| 344 } |
| 345 |
| 346 /** |
| 347 * An [InstrumentationServer] that sends messages to multiple instances. |
| 348 */ |
| 349 class MulticastInstrumentationServer implements InstrumentationServer { |
| 350 final List<InstrumentationServer> _servers; |
| 351 |
| 352 MulticastInstrumentationServer(this._servers); |
| 353 |
| 354 @override |
| 355 void log(String message) { |
| 356 for (InstrumentationServer server in _servers) { |
| 357 server.log(message); |
| 358 } |
| 359 } |
| 360 |
| 361 @override |
| 362 void logWithPriority(String message) { |
| 363 for (InstrumentationServer server in _servers) { |
| 364 server.logWithPriority(message); |
| 365 } |
| 366 } |
| 367 |
| 368 @override |
| 369 void shutdown() { |
| 370 for (InstrumentationServer server in _servers) { |
| 371 server.shutdown(); |
| 372 } |
| 373 } |
| 374 } |
OLD | NEW |