mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 22:32:20 +00:00
First cut of basic functionality
This CL addresses comments from the previous Pull Request https://github.com/lukechurch/dart-microlytics/pull/1 It can be used to reporting usage and performance data to Google Analytics. Known issues: -> unittest library is still used -> some comments are still not in full sentences R=ahe@google.com, danrubel@google.com Review URL: https://codereview.chromium.org//515993003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@39914 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
parent
700df9e917
commit
710bf6f401
26
pkg/microlytics/example/simple.dart
Normal file
26
pkg/microlytics/example/simple.dart
Normal file
|
@ -0,0 +1,26 @@
|
|||
// Copyright (c) 2014, 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 'package:microlytics/channels.dart';
|
||||
import 'package:microlytics/io_channels.dart';
|
||||
import 'package:microlytics/microlytics.dart';
|
||||
|
||||
void main(List<String> arguments) {
|
||||
// Create the channel that will be used to communicate to analytics.
|
||||
var channel = new RateLimitingBufferedChannel(
|
||||
new HttpClientChannel(), packetsPerSecond: 1.0);
|
||||
|
||||
if (arguments.length != 1) {
|
||||
print("usage: dart simple.dart GA-Client-ID");
|
||||
return;
|
||||
}
|
||||
final String clientID = arguments.single;
|
||||
|
||||
// Create the logger.
|
||||
var lg = new AnalyticsLogger(channel, "555", clientID, "test", "1.2");
|
||||
|
||||
// Send some messages.
|
||||
lg.logAnonymousEvent("hello", "world");
|
||||
lg.logAnonymousTiming("loader", "var", 42);
|
||||
}
|
55
pkg/microlytics/lib/channels.dart
Normal file
55
pkg/microlytics/lib/channels.dart
Normal file
|
@ -0,0 +1,55 @@
|
|||
// Copyright (c) 2014, 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.
|
||||
|
||||
library microlytics.channels;
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
const String ANALYTICS_URL = "https://ssl.google-analytics.com/collect";
|
||||
|
||||
abstract class Channel {
|
||||
void sendData(String data);
|
||||
void shutdown() {}
|
||||
}
|
||||
|
||||
/// [Channel] that implements a leaky bucket
|
||||
/// algorithm to provide rate limiting.
|
||||
/// See [http://en.wikipedia.org/wiki/Leaky_bucket].
|
||||
class RateLimitingBufferedChannel extends Channel {
|
||||
final List<String> _buffer = <String>[];
|
||||
final Channel _innerChannel;
|
||||
final int _bufferSizeLimit;
|
||||
Timer _timer;
|
||||
|
||||
RateLimitingBufferedChannel(
|
||||
this._innerChannel,
|
||||
{int bufferSizeLimit: 10,
|
||||
double packetsPerSecond: 1.0})
|
||||
: this._bufferSizeLimit = bufferSizeLimit {
|
||||
if (!(packetsPerSecond > 0)) {
|
||||
throw new ArgumentError("packetsPerSecond must be larger than zero.");
|
||||
}
|
||||
|
||||
int transmitDelay = (1000 / packetsPerSecond).floor();
|
||||
_timer = new Timer.periodic(
|
||||
new Duration(milliseconds: transmitDelay), _onTimerTick);
|
||||
}
|
||||
|
||||
void _onTimerTick(_) {
|
||||
if (_buffer.length > 0) {
|
||||
String item = _buffer.removeLast();
|
||||
_innerChannel.sendData(item);
|
||||
}
|
||||
}
|
||||
|
||||
void sendData(String data) {
|
||||
if (_buffer.length >= _bufferSizeLimit) return;
|
||||
_buffer.add(data);
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
_timer.cancel();
|
||||
_innerChannel.shutdown();
|
||||
}
|
||||
}
|
14
pkg/microlytics/lib/html_channels.dart
Normal file
14
pkg/microlytics/lib/html_channels.dart
Normal file
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) 2014, 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.
|
||||
|
||||
library microlytics.html_channels;
|
||||
|
||||
import 'dart:html';
|
||||
import 'channels.dart';
|
||||
|
||||
class HttpRequestChannel extends Channel {
|
||||
void sendData(String data) {
|
||||
HttpRequest.request(ANALYTICS_URL, method: "POST", sendData: data);
|
||||
}
|
||||
}
|
21
pkg/microlytics/lib/io_channels.dart
Normal file
21
pkg/microlytics/lib/io_channels.dart
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) 2014, 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.
|
||||
|
||||
library microlytics.io_channels;
|
||||
|
||||
import 'dart:io';
|
||||
import 'channels.dart';
|
||||
|
||||
class HttpClientChannel extends Channel {
|
||||
void sendData(String data) {
|
||||
HttpClient client = new HttpClient();
|
||||
client.postUrl(Uri.parse(ANALYTICS_URL)).then((HttpClientRequest req) {
|
||||
req.write(data);
|
||||
return req.close();
|
||||
}).then((HttpClientResponse response) {
|
||||
response.drain();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
61
pkg/microlytics/lib/microlytics.dart
Normal file
61
pkg/microlytics/lib/microlytics.dart
Normal file
|
@ -0,0 +1,61 @@
|
|||
// Copyright (c) 2014, 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.
|
||||
|
||||
library microlytics;
|
||||
|
||||
import 'channels.dart';
|
||||
|
||||
/// Very limited implementation of an API to report usage to Google Analytics.
|
||||
/// No Personally Identifiable Information must ever be passed to this class.
|
||||
class AnalyticsLogger {
|
||||
final Channel _channel;
|
||||
final String _clientID;
|
||||
final String _analyticsID;
|
||||
final String _appName;
|
||||
final String _appVersion;
|
||||
final String _messagePrefix; //Computed prefix for analytics messages
|
||||
|
||||
/// Create a new logger
|
||||
/// [channel] represents how this is going to be sent, this would typically
|
||||
/// be a [RateLimitingBufferedChannel] wrapping either a [HttpRequestChannel]
|
||||
/// or a [HttpClientChannel].
|
||||
/// [clientID] is a version 4 UUID associated with the site or app.
|
||||
/// [appName] is an application name.
|
||||
/// [appVersion] is a verion string.
|
||||
AnalyticsLogger(Channel channel, String clientID, String analyticsID,
|
||||
String appName, String appVersion)
|
||||
: this._channel = channel,
|
||||
this._clientID = clientID,
|
||||
this._analyticsID = analyticsID,
|
||||
this._appName = appName,
|
||||
this._appVersion = appVersion,
|
||||
this._messagePrefix =
|
||||
"v=1"
|
||||
"&tid=$analyticsID"
|
||||
"&cid=$clientID"
|
||||
"&an=$appName"
|
||||
"&av=$appVersion";
|
||||
|
||||
void logAnonymousTiming(String category, String variable, int ms) {
|
||||
category = Uri.encodeComponent(category);
|
||||
variable = Uri.encodeComponent(variable);
|
||||
_channel.sendData(
|
||||
"${this._messagePrefix}"
|
||||
"&t=timing"
|
||||
"&utc=$category"
|
||||
"&utv=$variable"
|
||||
"&utt=$ms");
|
||||
}
|
||||
|
||||
void logAnonymousEvent(String category, String event) {
|
||||
category = Uri.encodeComponent(category);
|
||||
event = Uri.encodeComponent(event);
|
||||
_channel.sendData(
|
||||
"${this._messagePrefix}"
|
||||
"&t=event"
|
||||
"&ec=$category"
|
||||
"&ea=$event");
|
||||
}
|
||||
}
|
||||
|
8
pkg/microlytics/pubspec.yaml
Normal file
8
pkg/microlytics/pubspec.yaml
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Copyright (c) 2014, 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.
|
||||
|
||||
name: microlytics
|
||||
description: A minimal implementation of the Analytics API in pure Dart
|
||||
dev_dependencies:
|
||||
unittest: any
|
120
pkg/microlytics/test/dart_microlytics_test.dart
Normal file
120
pkg/microlytics/test/dart_microlytics_test.dart
Normal file
|
@ -0,0 +1,120 @@
|
|||
// Copyright (c) 2014, 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.
|
||||
|
||||
library microlytics.test;
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
import 'package:microlytics/microlytics.dart';
|
||||
|
||||
import 'test_channel.dart';
|
||||
|
||||
void main() {
|
||||
testBasicEventRead();
|
||||
testBasicNegativeEventRead();
|
||||
testBasicTimingRead();
|
||||
testBasicTimingMultiread();
|
||||
}
|
||||
|
||||
void testBasicEventRead() {
|
||||
TestChannel c = new TestChannel();
|
||||
AnalyticsLogger logger = new AnalyticsLogger(
|
||||
c,
|
||||
"2cfac780-31e2-11e4-8c21-0800200c9a66",
|
||||
"UA-53895644-1",
|
||||
"TestApp",
|
||||
"0.42");
|
||||
logger.logAnonymousEvent("video", "play");
|
||||
Expect.isTrue(c.contains(
|
||||
"v=1"
|
||||
"&tid=UA-53895644-1"
|
||||
"&cid=2cfac780-31e2-11e4-8c21-0800200c9a66"
|
||||
"&an=TestApp"
|
||||
"&av=0.42"
|
||||
"&t=event"
|
||||
"&ec=video"
|
||||
"&ea=play"));
|
||||
}
|
||||
|
||||
void testBasicNegativeEventRead() {
|
||||
TestChannel c = new TestChannel();
|
||||
AnalyticsLogger logger = new AnalyticsLogger(
|
||||
c,
|
||||
"2cfac780-31e2-11e4-8c21-0800200c9a66",
|
||||
"UA-53895644-1",
|
||||
"TestApp",
|
||||
"0.42");
|
||||
logger.logAnonymousEvent("video", "play");
|
||||
Expect.isFalse(c.contains(
|
||||
"v=1"
|
||||
"&tid=UA-53895644-1"
|
||||
"&cid=2cfac780-31e2-11e4-8c21-0800200c9a66"
|
||||
"&an=TestApp"
|
||||
"&av=XXX"
|
||||
"&t=event"
|
||||
"&ec=video"
|
||||
"&ea=play"));
|
||||
}
|
||||
|
||||
void testBasicTimingRead() {
|
||||
TestChannel c = new TestChannel();
|
||||
AnalyticsLogger logger = new AnalyticsLogger(
|
||||
c,
|
||||
"2cfac780-31e2-11e4-8c21-0800200c9a66",
|
||||
"UA-53895644-1",
|
||||
"TestApp",
|
||||
"0.42");
|
||||
logger.logAnonymousTiming("video", "delay", 157);
|
||||
Expect.isTrue(c.contains(
|
||||
"v=1"
|
||||
"&tid=UA-53895644-1"
|
||||
"&cid=2cfac780-31e2-11e4-8c21-0800200c9a66"
|
||||
"&an=TestApp"
|
||||
"&av=0.42"
|
||||
"&t=timing"
|
||||
"&utc=video"
|
||||
"&utv=delay"
|
||||
"&utt=157"));
|
||||
}
|
||||
|
||||
void testBasicTimingMultiread() {
|
||||
TestChannel c = new TestChannel();
|
||||
AnalyticsLogger logger = new AnalyticsLogger(
|
||||
c,
|
||||
"2cfac780-31e2-11e4-8c21-0800200c9a66",
|
||||
"UA-53895644-1",
|
||||
"TestApp",
|
||||
"0.42");
|
||||
logger.logAnonymousTiming("video", "delay", 159);
|
||||
logger.logAnonymousTiming("video", "delay", 152);
|
||||
Expect.isTrue(c.contains(
|
||||
"v=1"
|
||||
"&tid=UA-53895644-1"
|
||||
"&cid=2cfac780-31e2-11e4-8c21-0800200c9a66"
|
||||
"&an=TestApp"
|
||||
"&av=0.42"
|
||||
"&t=timing"
|
||||
"&utc=video"
|
||||
"&utv=delay"
|
||||
"&utt=152"));
|
||||
Expect.isTrue(c.contains(
|
||||
"v=1"
|
||||
"&tid=UA-53895644-1"
|
||||
"&cid=2cfac780-31e2-11e4-8c21-0800200c9a66"
|
||||
"&an=TestApp"
|
||||
"&av=0.42"
|
||||
"&t=timing"
|
||||
"&utc=video"
|
||||
"&utv=delay"
|
||||
"&utt=159"));
|
||||
Expect.isFalse(c.contains(
|
||||
"v=1"
|
||||
"&tid=UA-53895644-1"
|
||||
"&cid=2cfac780-31e2-11e4-8c21-0800200c9a66"
|
||||
"&an=TestApp"
|
||||
"&av=0.42"
|
||||
"&t=timing"
|
||||
"&utc=video"
|
||||
"&utv=delay"
|
||||
"&utt=19"));
|
||||
}
|
19
pkg/microlytics/test/test_channel.dart
Normal file
19
pkg/microlytics/test/test_channel.dart
Normal file
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) 2014, 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.
|
||||
|
||||
library microlytics.test_channel;
|
||||
|
||||
import 'package:microlytics/channels.dart';
|
||||
|
||||
class TestChannel extends Channel {
|
||||
List<String> _channelLog = [];
|
||||
|
||||
void sendData(String data) {
|
||||
_channelLog.add(data);
|
||||
}
|
||||
|
||||
bool contains(String data) {
|
||||
return _channelLog.contains(data);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue