Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(745)

Side by Side Diff: packages/usage/lib/usage.dart

Issue 2989763002: Update charted to 0.4.8 and roll (Closed)
Patch Set: Removed Cutch from list of reviewers Created 3 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « packages/usage/lib/src/uuid.dart ('k') | packages/usage/lib/usage_html.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 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 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 /** 5 /**
6 * `usage` is a wrapper around Google Analytics for both command-line apps 6 * `usage` is a wrapper around Google Analytics for both command-line apps
7 * and web apps. 7 * and web apps.
8 * 8 *
9 * In order to use this library as a web app, import the `analytics_html.dart` 9 * In order to use this library as a web app, import the `analytics_html.dart`
10 * library and instantiate the [AnalyticsHtml] class. 10 * library and instantiate the [AnalyticsHtml] class.
(...skipping 11 matching lines...) Expand all
22 * For more information, please see the Google Analytics Measurement Protocol 22 * For more information, please see the Google Analytics Measurement Protocol
23 * [Policy](https://developers.google.com/analytics/devguides/collection/protoco l/policy). 23 * [Policy](https://developers.google.com/analytics/devguides/collection/protoco l/policy).
24 */ 24 */
25 library usage; 25 library usage;
26 26
27 import 'dart:async'; 27 import 'dart:async';
28 28
29 // Matches file:/, non-ws, /, non-ws, .dart 29 // Matches file:/, non-ws, /, non-ws, .dart
30 final RegExp _pathRegex = new RegExp(r'file:/\S+/(\S+\.dart)'); 30 final RegExp _pathRegex = new RegExp(r'file:/\S+/(\S+\.dart)');
31 31
32 // Match multiple tabs or spaces.
33 final RegExp _tabOrSpaceRegex = new RegExp(r'[\t ]+');
34
32 /** 35 /**
33 * An interface to a Google Analytics session. [AnalyticsHtml] and [AnalyticsIO] 36 * An interface to a Google Analytics session. [AnalyticsHtml] and [AnalyticsIO]
34 * are concrete implementations of this interface. [AnalyticsMock] can be used 37 * are concrete implementations of this interface. [AnalyticsMock] can be used
35 * for testing or for some varients of an opt-in workflow. 38 * for testing or for some variants of an opt-in workflow.
36 * 39 *
37 * The analytics information is sent on a best-effort basis. So, failures to 40 * The analytics information is sent on a best-effort basis. So, failures to
38 * send the GA information will not result in errors from the asynchronous 41 * send the GA information will not result in errors from the asynchronous
39 * `send` methods. 42 * `send` methods.
40 */ 43 */
41 abstract class Analytics { 44 abstract class Analytics {
42 /** 45 /**
43 * Tracking ID / Property ID. 46 * Tracking ID / Property ID.
44 */ 47 */
45 String get trackingId; 48 String get trackingId;
46 49
47 /** 50 /// The application name.
48 * Whether the user has opt-ed in to additional analytics. 51 String get applicationName;
49 */ 52
50 bool optIn; 53 /// The application version.
54 String get applicationVersion;
51 55
52 /** 56 /**
53 * Whether the [optIn] value has been explicitly set (either `true` or 57 * Is this the first time the tool has run?
54 * `false`).
55 */ 58 */
56 bool get hasSetOptIn; 59 bool get firstRun;
60
61 /**
62 * Whether the [Analytics] instance is configured in an opt-in or opt-out mann er.
63 */
64 AnalyticsOpt analyticsOpt = AnalyticsOpt.optOut;
65
66 /**
67 * Will analytics data be sent.
68 */
69 bool get enabled;
70
71 /**
72 * Enable or disable sending of analytics data.
73 */
74 set enabled(bool value);
75
76 /**
77 * Anonymous client ID in UUID v4 format.
78 *
79 * The value is randomly-generated and should be reasonably stable for the
80 * computer sending analytics data.
81 */
82 String get clientId;
57 83
58 /** 84 /**
59 * Sends a screen view hit to Google Analytics. 85 * Sends a screen view hit to Google Analytics.
86 *
87 * [parameters] can be any analytics key/value pair. Useful
88 * for custom dimensions, etc.
60 */ 89 */
61 Future sendScreenView(String viewName); 90 Future sendScreenView(String viewName, {Map<String, String> parameters});
62 91
63 /** 92 /**
64 * Sends an Event hit to Google Analytics. [label] specifies the event label. 93 * Sends an Event hit to Google Analytics. [label] specifies the event label.
65 * [value] specifies the event value. Values must be non-negative. 94 * [value] specifies the event value. Values must be non-negative.
95 *
96 * [parameters] can be any analytics key/value pair. Useful
97 * for custom dimensions, etc.
66 */ 98 */
67 Future sendEvent(String category, String action, {String label, int value}); 99 Future sendEvent(String category, String action,
100 {String label, int value, Map<String, String> parameters});
68 101
69 /** 102 /**
70 * Sends a Social hit to Google Analytics. [network] specifies the social 103 * Sends a Social hit to Google Analytics. [network] specifies the social
71 * network, for example Facebook or Google Plus. [action] specifies the social 104 * network, for example Facebook or Google Plus. [action] specifies the social
72 * interaction action. For example on Google Plus when a user clicks the +1 105 * interaction action. For example on Google Plus when a user clicks the +1
73 * button, the social action is 'plus'. [target] specifies the target of a 106 * button, the social action is 'plus'. [target] specifies the target of a
74 * social interaction. This value is typically a URL but can be any text. 107 * social interaction. This value is typically a URL but can be any text.
75 */ 108 */
76 Future sendSocial(String network, String action, String target); 109 Future sendSocial(String network, String action, String target);
77 110
78 /** 111 /**
79 * Sends a Timing hit to Google Analytics. [variableName] specifies the 112 * Sends a Timing hit to Google Analytics. [variableName] specifies the
80 * variable name of the timing. [time] specifies the user timing value (in 113 * variable name of the timing. [time] specifies the user timing value (in
81 * milliseconds). [category] specifies the category of the timing. [label] 114 * milliseconds). [category] specifies the category of the timing. [label]
82 * specifies the label of the timing. 115 * specifies the label of the timing.
83 */ 116 */
84 Future sendTiming(String variableName, int time, {String category, 117 Future sendTiming(String variableName, int time,
85 String label}); 118 {String category, String label});
86 119
87 /** 120 /**
88 * Start a timer. The time won't be calculated, and the analytics information 121 * Start a timer. The time won't be calculated, and the analytics information
89 * sent, until the [AnalyticsTimer.finish] method is called. 122 * sent, until the [AnalyticsTimer.finish] method is called.
90 */ 123 */
91 AnalyticsTimer startTimer(String variableName, 124 AnalyticsTimer startTimer(String variableName,
92 {String category, String label}); 125 {String category, String label});
93 126
94 /** 127 /**
95 * In order to avoid sending any personally identifying information, the 128 * In order to avoid sending any personally identifying information, the
96 * [description] field must not contain the exception message. In addition, 129 * [description] field must not contain the exception message. In addition,
97 * only the first 100 chars of the description will be sent. 130 * only the first 100 chars of the description will be sent.
98 */ 131 */
99 Future sendException(String description, {bool fatal}); 132 Future sendException(String description, {bool fatal});
100 133
101 /** 134 /**
135 * Gets a session variable value.
136 */
137 dynamic getSessionValue(String param);
138
139 /**
102 * Sets a session variable value. The value is persistent for the life of the 140 * Sets a session variable value. The value is persistent for the life of the
103 * [Analytics] instance. This variable will be sent in with every analytics 141 * [Analytics] instance. This variable will be sent in with every analytics
104 * hit. A list of valid variable names can be found here: 142 * hit. A list of valid variable names can be found here:
105 * https://developers.google.com/analytics/devguides/collection/protocol/v1/pa rameters. 143 * https://developers.google.com/analytics/devguides/collection/protocol/v1/pa rameters.
106 */ 144 */
107 void setSessionValue(String param, dynamic value); 145 void setSessionValue(String param, dynamic value);
108 146
109 /** 147 /**
148 * Fires events when the usage library sends any data over the network. This
149 * will not fire if analytics has been disabled or if the throttling algorithm
150 * has been engaged.
151 *
152 * This method is public to allow library clients to more easily test their
153 * analytics implementations.
154 */
155 Stream<Map<String, dynamic>> get onSend;
156
157 /**
110 * Wait for all of the outstanding analytics pings to complete. The returned 158 * Wait for all of the outstanding analytics pings to complete. The returned
111 * `Future` will always complete without errors. You can pass in an optional 159 * `Future` will always complete without errors. You can pass in an optional
112 * `Duration` to specify to only wait for a certain amount of time. 160 * `Duration` to specify to only wait for a certain amount of time.
113 * 161 *
114 * This method is particularly useful for command-line clients. Outstanding 162 * This method is particularly useful for command-line clients. Outstanding
115 * I/O requests will cause the VM to delay terminating the process. Generally, 163 * I/O requests will cause the VM to delay terminating the process. Generally,
116 * users won't want their CLI app to pause at the end of the process waiting 164 * users won't want their CLI app to pause at the end of the process waiting
117 * for Google analytics requests to complete. This method allows CLI apps to 165 * for Google analytics requests to complete. This method allows CLI apps to
118 * delay for a short time waiting for GA requests to complete, and then do 166 * delay for a short time waiting for GA requests to complete, and then do
119 * something like call `exit()` explicitly themselves. 167 * something like call `dart:io`'s `exit()` explicitly themselves (or the
168 * [close] method below).
120 */ 169 */
121 Future waitForLastPing({Duration timeout}); 170 Future waitForLastPing({Duration timeout});
171
172 /// Free any used resources.
173 ///
174 /// The [Analytics] instance should not be used after this call.
175 void close();
176 }
177
178 enum AnalyticsOpt {
179 /**
180 * Users must opt-in before any analytics data is sent.
181 */
182 optIn,
183
184 /**
185 * Users must opt-out for analytics data to not be sent.
186 */
187 optOut
122 } 188 }
123 189
124 /** 190 /**
125 * An object, returned by [Analytics.startTimer], that is used to measure an 191 * An object, returned by [Analytics.startTimer], that is used to measure an
126 * asynchronous process. 192 * asynchronous process.
127 */ 193 */
128 class AnalyticsTimer { 194 class AnalyticsTimer {
129 final Analytics analytics; 195 final Analytics analytics;
130 final String variableName; 196 final String variableName;
131 final String category; 197 final String category;
(...skipping 16 matching lines...) Expand all
148 } 214 }
149 215
150 /** 216 /**
151 * Finish the timer, calculate the elapsed time, and send the information to 217 * Finish the timer, calculate the elapsed time, and send the information to
152 * analytics. Once this is called, any future invocations are no-ops. 218 * analytics. Once this is called, any future invocations are no-ops.
153 */ 219 */
154 Future finish() { 220 Future finish() {
155 if (_endMillis != null) return new Future.value(); 221 if (_endMillis != null) return new Future.value();
156 222
157 _endMillis = new DateTime.now().millisecondsSinceEpoch; 223 _endMillis = new DateTime.now().millisecondsSinceEpoch;
158 return analytics.sendTiming( 224 return analytics.sendTiming(variableName, currentElapsedMillis,
159 variableName, currentElapsedMillis, category: category, label: label); 225 category: category, label: label);
160 } 226 }
161 } 227 }
162 228
163 /** 229 /**
164 * A no-op implementation of the [Analytics] class. This can be used as a 230 * A no-op implementation of the [Analytics] class. This can be used as a
165 * stand-in for that will never ping the GA server, or as a mock in test code. 231 * stand-in for that will never ping the GA server, or as a mock in test code.
166 */ 232 */
167 class AnalyticsMock implements Analytics { 233 class AnalyticsMock implements Analytics {
234 @override
168 String get trackingId => 'UA-0'; 235 String get trackingId => 'UA-0';
236 @override
237 String get applicationName => 'mock-app';
238 @override
239 String get applicationVersion => '1.0.0';
240
169 final bool logCalls; 241 final bool logCalls;
170 242
171 bool optIn = false; 243 /**
172 bool hasSetOptIn = true; 244 * Events are never added to this controller for the mock implementation.
245 */
246 StreamController<Map<String, dynamic>> _sendController =
247 new StreamController.broadcast();
173 248
174 /** 249 /**
175 * Create a new [AnalyticsMock]. If [logCalls] is true, all calls will be 250 * Create a new [AnalyticsMock]. If [logCalls] is true, all calls will be
176 * logged to stdout. 251 * logged to stdout.
177 */ 252 */
178 AnalyticsMock([this.logCalls = false]); 253 AnalyticsMock([this.logCalls = false]);
179 254
180 Future sendScreenView(String viewName) => 255 @override
181 _log('screenView', {'viewName': viewName}); 256 bool get firstRun => false;
182 257
183 Future sendEvent(String category, String action, {String label, int value}) { 258 @override
184 return _log('event', {'category': category, 'action': action, 259 AnalyticsOpt analyticsOpt = AnalyticsOpt.optOut;
185 'label': label, 'value': value}); 260
261 @override
262 bool enabled = true;
263
264 @override
265 String get clientId => '00000000-0000-4000-0000-000000000000';
266
267 @override
268 Future sendScreenView(String viewName, {Map<String, String> parameters}) {
269 parameters ??= <String, String>{};
270 parameters['viewName'] = viewName;
271 return _log('screenView', parameters);
186 } 272 }
187 273
274 @override
275 Future sendEvent(String category, String action,
276 {String label, int value, Map<String, String> parameters}) {
277 parameters ??= <String, String>{};
278 return _log(
279 'event',
280 {'category': category, 'action': action, 'label': label, 'value': value}
281 ..addAll(parameters));
282 }
283
284 @override
188 Future sendSocial(String network, String action, String target) => 285 Future sendSocial(String network, String action, String target) =>
189 _log('social', {'network': network, 'action': action, 'target': target}); 286 _log('social', {'network': network, 'action': action, 'target': target});
190 287
191 Future sendTiming(String variableName, int time, {String category, 288 @override
192 String label}) { 289 Future sendTiming(String variableName, int time,
193 return _log('timing', {'variableName': variableName, 'time': time, 290 {String category, String label}) {
194 'category': category, 'label': label}); 291 return _log('timing', {
292 'variableName': variableName,
293 'time': time,
294 'category': category,
295 'label': label
296 });
195 } 297 }
196 298
299 @override
197 AnalyticsTimer startTimer(String variableName, 300 AnalyticsTimer startTimer(String variableName,
198 {String category, String label}) { 301 {String category, String label}) {
199 return new AnalyticsTimer(this, 302 return new AnalyticsTimer(this, variableName,
200 variableName, category: category, label: label); 303 category: category, label: label);
201 } 304 }
202 305
306 @override
203 Future sendException(String description, {bool fatal}) => 307 Future sendException(String description, {bool fatal}) =>
204 _log('exception', {'description': description, 'fatal': fatal}); 308 _log('exception', {'description': description, 'fatal': fatal});
205 309
206 void setSessionValue(String param, dynamic value) { } 310 @override
311 dynamic getSessionValue(String param) => null;
207 312
313 @override
314 void setSessionValue(String param, dynamic value) {}
315
316 @override
317 Stream<Map<String, dynamic>> get onSend => _sendController.stream;
318
319 @override
208 Future waitForLastPing({Duration timeout}) => new Future.value(); 320 Future waitForLastPing({Duration timeout}) => new Future.value();
209 321
322 @override
323 void close() {}
324
210 Future _log(String hitType, Map m) { 325 Future _log(String hitType, Map m) {
211 if (logCalls) { 326 if (logCalls) {
212 print('analytics: ${hitType} ${m}'); 327 print('analytics: ${hitType} ${m}');
213 } 328 }
214 329
215 return new Future.value(); 330 return new Future.value();
216 } 331 }
217 } 332 }
218 333
219 /** 334 /**
220 * Sanitize a stacktrace. This will shorten file paths in order to remove any 335 * Sanitize a stacktrace. This will shorten file paths in order to remove any
221 * PII that may be contained in the full file path. For example, this will 336 * PII that may be contained in the full file path. For example, this will
222 * shorten `file:///Users/foobar/tmp/error.dart` to `error.dart`. 337 * shorten `file:///Users/foobar/tmp/error.dart` to `error.dart`.
223 * 338 *
224 * If [shorten] is `true`, this method will also attempt to compress the text 339 * If [shorten] is `true`, this method will also attempt to compress the text
225 * of the stacktrace. GA has a 100 char limit on the text that can be sent for 340 * of the stacktrace. GA has a 100 char limit on the text that can be sent for
226 * an exception. This will try and make those first 100 chars contain 341 * an exception. This will try and make those first 100 chars contain
227 * information useful to debugging the issue. 342 * information useful to debugging the issue.
228 */ 343 */
229 String sanitizeStacktrace(dynamic st, {bool shorten: true}) { 344 String sanitizeStacktrace(dynamic st, {bool shorten: true}) {
230 String str = '${st}'; 345 String str = '${st}';
231 346
232 Iterable<Match> iter = _pathRegex.allMatches(str); 347 Iterable<Match> iter = _pathRegex.allMatches(str);
233 iter = iter.toList().reversed; 348 iter = iter.toList().reversed;
234 349
235 for (Match match in iter) { 350 for (Match match in iter) {
236 String replacement = match.group(1); 351 String replacement = match.group(1);
237 str = str.substring(0, match.start) 352 str =
238 + replacement + str.substring(match.end); 353 str.substring(0, match.start) + replacement + str.substring(match.end);
239 } 354 }
240 355
241 if (shorten) { 356 if (shorten) {
242 // Shorten the stacktrace up a bit. 357 // Shorten the stacktrace up a bit.
243 str = str 358 str = str.replaceAll(_tabOrSpaceRegex, ' ');
244 .replaceAll('(package:', '(')
245 .replaceAll('(dart:', '(')
246 .replaceAll(new RegExp(r'\s+'), ' ');
247 } 359 }
248 360
249 return str; 361 return str;
250 } 362 }
OLDNEW
« no previous file with comments | « packages/usage/lib/src/uuid.dart ('k') | packages/usage/lib/usage_html.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698