Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import 'dart:async';

import 'package:flutter_driver/driver_extension.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:firebase_performance/firebase_performance.dart';

void main() {
final Completer<String> completer = Completer<String>();
enableFlutterDriverExtension(handler: (_) => completer.future);
tearDownAll(() => completer.complete(null));

group('firebase_performance test driver', () {
// TODO(bparrishMines): Rewrite integration tests when iOS portion is written.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reminder to remove this if you don't intend to land it.

/*
final FirebasePerformance performance = FirebasePerformance.instance;

setUp(() async {
Expand Down Expand Up @@ -37,5 +38,6 @@ void main() {
expect(trace.getAttribute('testAttribute2'), null);
expect(trace.getAttribute('testMetric'), null);
});
*/
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
library firebase_performance;

import 'dart:async';
import 'dart:collection';

import 'package:flutter/services.dart';
import 'package:flutter/foundation.dart';
Expand Down
65 changes: 44 additions & 21 deletions packages/firebase_performance/lib/src/firebase_performance.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,59 +11,82 @@ enum HttpMethod { Connect, Delete, Get, Head, Options, Patch, Post, Put, Trace }
///
/// You can get an instance by calling [FirebasePerformance.instance].
class FirebasePerformance {
FirebasePerformance._();
FirebasePerformance._(this._handle) {
channel.invokeMethod<bool>(
'FirebasePerformance#instance',
<String, dynamic>{'handle': _handle},
);
}

static int _nextHandle = 0;

static int _traceCount = 0;
static int _httpMetricCount = 0;
final int _handle;
Copy link
Contributor

@collinjackson collinjackson May 7, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this is just a matter of personal preference, are you sure you want to identify the singleton with a handle? There's just one on both the Dart and native side, and (AFAIK) _handle will always be zero. I guess an argument could be made for consistency with all the MethodChannel-using objects having a handle. It would be more obviously correct if FirebasePerformance had multiple app support.

I don't have a strong opinion here, just wanted to make sure you've thought about it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do remember thinking about this. My goal was to try and separate the responsibility of the Plugin (FirebasePerformancePlugin) class from the main Firebase class (FirebasePerformance). My thinking would be that the plugin class would only be responsible for calling constructors and maintaining the MethodCallHandlers.

Also, i figured this wouldn't be as weird if it wasn't a Firebase plugin. Most libraries don't have a central class that most of their objects are created from.

And this would also set us up to easily integrate multi-app support if the firebase team decides to add it!


@visibleForTesting
static const MethodChannel channel =
MethodChannel('plugins.flutter.io/firebase_performance');

/// Singleton of [FirebasePerformance].
static final FirebasePerformance instance = FirebasePerformance._();
static final FirebasePerformance instance =
FirebasePerformance._(_nextHandle++);

/// Determines whether performance monitoring is enabled or disabled.
///
/// True if performance monitoring is enabled and false if performance
/// monitoring is disabled. This is for dynamic enable/disable state. This
/// does not reflect whether instrumentation is enabled/disabled.
Future<bool> isPerformanceCollectionEnabled() async {
final bool isEnabled = await channel
// TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter.
// https://github.com/flutter/flutter/issues/26431
// ignore: strong_mode_implicit_dynamic_method
.invokeMethod('FirebasePerformance#isPerformanceCollectionEnabled');
return isEnabled;
Future<bool> isPerformanceCollectionEnabled() {
return channel.invokeMethod<bool>(
'$FirebasePerformance#isPerformanceCollectionEnabled',
<String, dynamic>{'handle': _handle},
);
}

/// Enables or disables performance monitoring.
///
/// This setting is persisted and applied on future invocations of your
/// application. By default, performance monitoring is enabled.
Future<void> setPerformanceCollectionEnabled(bool enable) async {
// TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter.
// https://github.com/flutter/flutter/issues/26431
// ignore: strong_mode_implicit_dynamic_method
await channel.invokeMethod(
'FirebasePerformance#setPerformanceCollectionEnabled', enable);
Future<void> setPerformanceCollectionEnabled(bool enable) {
return channel.invokeMethod<void>(
'$FirebasePerformance#setPerformanceCollectionEnabled',
<String, dynamic>{'handle': _handle, 'enable': enable},
);
}

/// Creates a [Trace] object with given [name].
///
/// The [name] requires no leading or trailing whitespace, no leading
/// underscore _ character, max length of [Trace.maxTraceNameLength]
/// underscore _ character, and max length of [Trace.maxTraceNameLength]
/// characters.
Trace newTrace(String name) {
return Trace._(_traceCount++, name);
final int handle = _nextHandle++;

FirebasePerformance.channel.invokeMethod<void>(
'$FirebasePerformance#newTrace',
<String, dynamic>{'handle': _handle, 'traceHandle': handle, 'name': name},
);

return Trace._(handle, name);
}

/// Creates [HttpMetric] for collecting performance for one request/response.
HttpMetric newHttpMetric(String url, HttpMethod httpMethod) {
return HttpMetric._(_httpMetricCount++, url, httpMethod);
final int handle = _nextHandle++;

FirebasePerformance.channel.invokeMethod<void>(
'$FirebasePerformance#newHttpMetric',
<String, dynamic>{
'handle': _handle,
'httpMetricHandle': handle,
'url': url,
'httpMethod': httpMethod.toString(),
},
);

return HttpMetric._(handle, url, httpMethod);
}

/// Creates a [Trace] object with given [name] and start the trace.
/// Creates a [Trace] object with given [name] and starts the trace.
///
/// The [name] requires no leading or trailing whitespace, no leading
/// underscore _ character, max length of [Trace.maxTraceNameLength]
Expand Down
171 changes: 107 additions & 64 deletions packages/firebase_performance/lib/src/http_metric.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,106 +6,149 @@ part of firebase_performance;

/// Metric used to collect data for network requests/responses.
///
/// It is possible to have more than one httpmetric running at a time.
/// It is possible to have more than one [HttpMetric] running at a time.
/// Attributes can also be added to help measure performance related events. A
/// httpmetric also measures the time between calling start() and stop().
/// [HttpMetric] also measures the time between calling `start()` and `stop()`.
///
/// Data collected is automatically sent to the associated Firebase console
/// after stop() is called.
///
/// You can confirm that Performance Monitoring results appear in the Firebase
/// console. Results should appear within 12 hours.
///
/// It is highly recommended that one always calls `start()` and `stop()` on
/// each created [HttpMetric] to avoid leaking on the platform side.
class HttpMetric extends PerformanceAttributes {
HttpMetric._(this._handle, this._url, this._httpMethod);
HttpMetric._(this._handle, this.url, this.httpMethod);

final int _handle;
final String _url;
final HttpMethod _httpMethod;
final String url;
final HttpMethod httpMethod;

@override
bool _hasStarted = false;

@override
bool _hasStopped = false;

int _httpResponseCode;
int _requestPayloadSize;
String _responseContentType;
int _responsePayloadSize;

@override
final int _handle;

/// HttpResponse code of the request.
int httpResponseCode;
int get httpResponseCode => _httpResponseCode;

/// Size of the request payload.
int requestPayloadSize;
int get requestPayloadSize => _requestPayloadSize;

/// Content type of the response such as text/html, application/json, etc...
String responseContentType;
String get responseContentType => _responseContentType;

/// Size of the response payload.
int responsePayloadSize;
int get responsePayloadSize => _responsePayloadSize;

/// Starts this httpmetric.
/// HttpResponse code of the request.
///
/// Can only be called once, otherwise assertion error is thrown.
/// If the [HttpMetric] has already been stopped, returns immediately without
/// taking action.
set httpResponseCode(int httpResponseCode) {
if (_hasStopped) return;

_httpResponseCode = httpResponseCode;
FirebasePerformance.channel.invokeMethod<void>(
'$HttpMetric#httpResponseCode',
<String, dynamic>{
'handle': _handle,
'httpResponseCode': httpResponseCode,
},
);
}

/// Size of the request payload.
///
/// Using ```await``` with this method is only necessary when accurate timing
/// is relevant.
Future<void> start() {
assert(!_hasStarted);
/// If the [HttpMetric] has already been stopped, returns immediately without
/// taking action.
set requestPayloadSize(int requestPayloadSize) {
if (_hasStopped) return;

_hasStarted = true;
return FirebasePerformance.channel
// TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter.
// https://github.com/flutter/flutter/issues/26431
// ignore: strong_mode_implicit_dynamic_method
.invokeMethod('HttpMetric#start', <String, dynamic>{
'handle': _handle,
'url': _url,
'httpMethod': _httpMethod.index,
});
_requestPayloadSize = requestPayloadSize;
FirebasePerformance.channel.invokeMethod<void>(
'$HttpMetric#requestPayloadSize',
<String, dynamic>{
'handle': _handle,
'requestPayloadSize': requestPayloadSize,
},
);
}

/// Stops this httpMetric.
/// Content type of the response such as text/html, application/json, etc...
///
/// Can only be called once and only after start(), otherwise assertion error
/// is thrown. Data collected is automatically sent to the associated
/// Firebase console after stop() is called.
/// If the [HttpMetric] has already been stopped, returns immediately without
/// taking action.
set responseContentType(String responseContentType) {
if (_hasStopped) return;

_responseContentType = responseContentType;
FirebasePerformance.channel.invokeMethod<void>(
'$HttpMetric#responseContentType',
<String, dynamic>{
'handle': _handle,
'responseContentType': responseContentType,
},
);
}

/// Size of the response payload.
///
/// Not necessary to use ```await``` with this method.
Future<void> stop() {
assert(!_hasStopped);
assert(_hasStarted);

final Map<String, dynamic> data = <String, dynamic>{
'handle': _handle,
'httpResponseCode': httpResponseCode,
'requestPayloadSize': requestPayloadSize,
'responseContentType': responseContentType,
'responsePayloadSize': responsePayloadSize,
'attributes': _attributes,
};
/// If the [HttpMetric] has already been stopped, returns immediately without
/// taking action.
set responsePayloadSize(int responsePayloadSize) {
if (_hasStopped) return;

_hasStopped = true;
// TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter.
// https://github.com/flutter/flutter/issues/26431
// ignore: strong_mode_implicit_dynamic_method
return FirebasePerformance.channel.invokeMethod('HttpMetric#stop', data);
_responsePayloadSize = responsePayloadSize;
FirebasePerformance.channel.invokeMethod<void>(
'$HttpMetric#responsePayloadSize',
<String, dynamic>{
'handle': _handle,
'responsePayloadSize': responsePayloadSize,
},
);
}

/// Sets a String [value] for the specified [attribute].
/// Starts this [HttpMetric].
///
/// If the httpmetric has been stopped, this method throws an assertion
/// error.
/// Can only be called once.
///
/// See [PerformanceAttributes.putAttribute].
@override
void putAttribute(String attribute, String value) {
assert(!_hasStopped);
super.putAttribute(attribute, value);
/// Using `await` with this method is only necessary when accurate timing
/// is relevant.
Future<void> start() {
if (_hasStopped) return Future<void>.value(null);

_hasStarted = true;
return FirebasePerformance.channel.invokeMethod<void>(
'$HttpMetric#start',
<String, dynamic>{'handle': _handle},
);
}

/// Removes an already added [attribute].
/// Stops this [HttpMetric].
///
/// If the httpmetric has been stopped, this method throws an assertion
/// error.
/// Can only be called once and only after start(), Data collected is
/// automatically sent to the associate Firebase console after stop() is
/// called. You can confirm that Performance Monitoring results appear in the
/// Firebase console. Results should appear within 12 hours.
///
/// See [PerformanceAttributes.removeAttribute].
@override
void removeAttribute(String attribute) {
assert(!_hasStopped);
super.removeAttribute(attribute);
/// Not necessary to use `await` with this method.
Future<void> stop() {
if (_hasStopped) return Future<void>.value(null);

_hasStopped = true;
return FirebasePerformance.channel.invokeMethod<void>(
'$HttpMetric#stop',
<String, dynamic>{'handle': _handle},
);
}
}
Loading