OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2017, 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 import 'dart:async'; |
| 6 import 'dart:io'; |
| 7 |
| 8 import 'package:http/http.dart' as http; |
| 9 import 'package:stack_trace/stack_trace.dart'; |
| 10 import 'package:usage/usage.dart'; |
| 11 |
| 12 /// Crash backend host. |
| 13 const String _crashServerHost = 'clients2.google.com'; |
| 14 |
| 15 /// Path to the crash servlet. |
| 16 const String _crashEndpointPath = '/cr/report'; // or, staging_report |
| 17 |
| 18 /// The field corresponding to the multipart/form-data file attachment where |
| 19 /// crash backend expects to find the Dart stack trace. |
| 20 const String _stackTraceFileField = 'DartError'; |
| 21 |
| 22 /// The name of the file attached as [stackTraceFileField]. |
| 23 /// |
| 24 /// The precise value is not important. It is ignored by the crash back end, but |
| 25 /// it must be supplied in the request. |
| 26 const String _stackTraceFilename = 'stacktrace_file'; |
| 27 |
| 28 /// Sends crash reports to Google. |
| 29 /// |
| 30 /// Clients shouldn't extend, mixin or implement this class. |
| 31 class CrashReportSender { |
| 32 static final Uri _baseUri = new Uri( |
| 33 scheme: 'https', host: _crashServerHost, path: _crashEndpointPath); |
| 34 |
| 35 final Analytics analytics; |
| 36 final http.Client _httpClient; |
| 37 |
| 38 /// Create a new [CrashReportSender], using the data from the given |
| 39 /// [Analytics] instance. |
| 40 CrashReportSender(this.analytics, {http.Client httpClient}) |
| 41 : _httpClient = httpClient ?? new http.Client(); |
| 42 |
| 43 /// Sends one crash report. |
| 44 /// |
| 45 /// The report is populated from data in [error] and [stackTrace]. |
| 46 Future sendReport(dynamic error, {StackTrace stackTrace}) async { |
| 47 if (!analytics.enabled) { |
| 48 return; |
| 49 } |
| 50 |
| 51 try { |
| 52 final Uri uri = _baseUri.replace( |
| 53 queryParameters: <String, String>{ |
| 54 'product': analytics.trackingId, |
| 55 'version': analytics.applicationVersion, |
| 56 }, |
| 57 ); |
| 58 |
| 59 final http.MultipartRequest req = new http.MultipartRequest('POST', uri); |
| 60 req.fields['uuid'] = analytics.clientId; |
| 61 req.fields['product'] = analytics.trackingId; |
| 62 req.fields['version'] = analytics.applicationVersion; |
| 63 req.fields['osName'] = Platform.operatingSystem; |
| 64 // TODO(devoncarew): Report the operating system version when we're able. |
| 65 //req.fields['osVersion'] = Platform.operatingSystemVersion; |
| 66 req.fields['type'] = 'DartError'; |
| 67 req.fields['error_runtime_type'] = '${error.runtimeType}'; |
| 68 |
| 69 final Chain chain = new Chain.parse(stackTrace.toString()); |
| 70 req.files.add(new http.MultipartFile.fromString( |
| 71 _stackTraceFileField, chain.terse.toString(), |
| 72 filename: _stackTraceFilename)); |
| 73 |
| 74 final http.StreamedResponse resp = await _httpClient.send(req); |
| 75 |
| 76 if (resp.statusCode != 200) { |
| 77 throw 'server responded with HTTP status code ${resp.statusCode}'; |
| 78 } |
| 79 } on SocketException catch (error) { |
| 80 throw 'network error while sending crash report: $error'; |
| 81 } catch (error, stackTrace) { |
| 82 // If the sender itself crashes, just print. |
| 83 throw 'exception while sending crash report: $error\n$stackTrace'; |
| 84 } |
| 85 } |
| 86 |
| 87 /// Closes the client and cleans up any resources associated with it. This |
| 88 /// will close the associated [http.Client]. |
| 89 void dispose() { |
| 90 _httpClient.close(); |
| 91 } |
| 92 } |
OLD | NEW |