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.
|
+ } |
+ } |
+ } |
+} |