Chromium Code Reviews| Index: pkg/telemetry/lib/crash_reporting.dart |
| diff --git a/pkg/telemetry/lib/crash_reporting.dart b/pkg/telemetry/lib/crash_reporting.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..d892a17286a0aca2b85d99d546f6c50442315e1f |
| --- /dev/null |
| +++ b/pkg/telemetry/lib/crash_reporting.dart |
| @@ -0,0 +1,93 @@ |
| +// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
| +// for details. All rights reserved. Use of this source code is governed by a |
| +// BSD-style license that can be found in the LICENSE file. |
| + |
| +import 'dart:async'; |
| +import 'dart:io'; |
| + |
| +import 'package:http/http.dart' as http; |
| +import 'package:stack_trace/stack_trace.dart'; |
| +import 'package:usage/usage.dart'; |
| + |
| +/// Tells crash backend that this is a Dart error as opposed to, say, Java. |
| +const String _dartTypeId = 'DartError'; |
| + |
| +/// Crash backend host. |
| +const String _crashServerHost = 'clients2.google.com'; |
| + |
| +/// Path to the crash servlet. |
| +const String _crashEndpointPath = '/cr/report'; // or, staging_report |
| + |
| +/// The field corresponding to the multipart/form-data file attachment where |
| +/// crash backend expects to find the Dart stack trace. |
| +const String _stackTraceFileField = 'DartError'; |
| + |
| +/// The name of the file attached as [stackTraceFileField]. |
| +/// |
| +/// The precise value is not important. It is ignored by the crash back end, but |
| +/// it must be supplied in the request. |
| +const String _stackTraceFilename = 'stacktrace_file'; |
| + |
| +/// Sends crash reports to Google. |
|
Brian Wilkerson
2017/06/23 21:41:09
If we might publish this as a package someday, con
devoncarew
2017/06/26 15:53:46
Done.
|
| +class CrashReportSender { |
|
kevmoo
2017/06/23 21:01:04
Should likely have a way to close/dispose this – a
devoncarew
2017/06/26 15:53:46
Good point! Added a dispose() method, doc'd it, an
|
| + static final Uri _baseUri = new Uri( |
| + scheme: 'https', |
| + host: _crashServerHost, |
| + port: 443, |
|
kevmoo
2017/06/23 21:01:04
A bit redundant w/ https?
devoncarew
2017/06/26 15:53:46
removed
|
| + path: _crashEndpointPath); |
| + |
| + final Analytics analytics; |
| + |
| + http.Client _httpClient; |
|
kevmoo
2017/06/23 21:01:05
make final?
Brian Wilkerson
2017/06/23 21:41:09
It can't be because of the assignment on line 46.
kevmoo
2017/06/23 21:43:31
That can be done in the initializer. No worries.
devoncarew
2017/06/26 15:53:46
refactored to allow final
|
| + |
| + /// Create a new [CrashReportSender], using the data from the given |
| + /// [Analytics] instance. |
| + CrashReportSender(this.analytics, {http.Client httpClient}) { |
| + _httpClient = httpClient ?? new http.Client(); |
| + } |
| + |
| + /// Sends one crash report. |
| + /// |
| + /// The report is populated from data in [error] and [stackTrace]. |
| + Future sendReport(dynamic error, [StackTrace stackTrace]) async { |
|
Brian Wilkerson
2017/06/23 21:41:09
Consider making stackTrace a named parameter. Addi
devoncarew
2017/06/26 15:53:46
Makes sense, will do.
|
| + if (!analytics.enabled) { |
| + return; |
| + } |
| + |
| + try { |
| + final Uri uri = _baseUri.replace( |
| + queryParameters: <String, String>{ |
| + 'product': analytics.trackingId, |
| + 'version': analytics.applicationVersion, |
| + }, |
| + ); |
| + |
| + final http.MultipartRequest req = new http.MultipartRequest('POST', uri); |
| + req.fields['uuid'] = analytics.clientId; |
| + req.fields['product'] = analytics.trackingId; |
| + req.fields['version'] = analytics.applicationVersion; |
| + req.fields['osName'] = Platform.operatingSystem; |
| + req.fields['osVersion'] = Platform.operatingSystem; // includes the vesion |
|
Brian Wilkerson
2017/06/23 21:41:09
"vesion" --> "version"
Should we parse out the na
devoncarew
2017/06/26 15:53:45
On investigation, this field does not contain the
|
| + req.fields['type'] = _dartTypeId; |
| + req.fields['error_runtime_type'] = '${error.runtimeType}'; |
| + |
| + final Chain chain = new Chain.parse(stackTrace.toString()); |
| + req.files.add(new http.MultipartFile.fromString( |
| + _stackTraceFileField, chain.terse.toString(), |
| + filename: _stackTraceFilename)); |
| + |
| + final http.StreamedResponse resp = await _httpClient.send(req); |
| + |
| + if (resp.statusCode != 200) { |
| + throw 'server responded with HTTP status code ${resp.statusCode}'; |
| + } |
| + } catch (sendError, sendStackTrace) { |
|
kevmoo
2017/06/23 21:01:04
A tiny more conventional to use `on SocketExceptio
devoncarew
2017/06/26 15:53:45
Done.
|
| + if (sendError is SocketException) { |
| + throw 'network error while sending crash report: $sendError'; |
| + } else { |
| + // If the sender itself crashes, just print. |
| + throw 'exception while sending crash report: $sendError\n$sendStackTrace'; |
|
kevmoo
2017/06/23 21:01:05
long line
devoncarew
2017/06/26 15:53:46
Done.
|
| + } |
| + } |
| + } |
| +} |