Skip to content

Commit

Permalink
feat(performance): AngularFire Performance Monitoring (#2064)
Browse files Browse the repository at this point in the history
* AngularFirePerformance!
* Build with node:lts and longer timeout
* Don't blow up when ng v8 or 9-rc drop
* Changelog for 5.2
* Change package.json to angular/fire for new github design
  • Loading branch information
jamesdaniels committed May 24, 2019
1 parent e32164d commit 2469e77
Show file tree
Hide file tree
Showing 22 changed files with 473 additions and 10 deletions.
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
<a name="5.2.0"></a>
# [5.2.0](https://github.com/angular/angularfire2/compare/5.1.3...5.2.0) (2019-05-24)

### Bug Fixes

* **firestore:** Fix for builds targeting Node ([#2079](https://github.com/angular/angularfire2/issues/2079)) ([8a33826](https://github.com/angular/angularfire2/commit/8a33826))
* **storage:** Typo in updateMetadata method ([#2029](https://github.com/angular/angularfire2/issues/2029)) ([6133296](https://github.com/angular/angularfire2/commit/6133296))

### Features

* **performance:** AngularFire Performance Monitoring ([#2064](https://github.com/angular/angularfire2/issues/2064))
* **auth-guard:** AngularFire Auth Guards ([#2016](https://github.com/angular/angularfire2/issues/2016)) ([e32164d](https://github.com/angular/angularfire2/commit/e32164d))
* **firestore:** Added option to include document IDs on valueChanges() ([#1976](https://github.com/angular/angularfire2/issues/1976)) ([7108875](https://github.com/angular/angularfire2/commit/7108875))
* **firestore:** Support Firestore Collection Group Queries ([#2066](https://github.com/angular/angularfire2/issues/2066)) ([c34c0f3](https://github.com/angular/angularfire2/commit/c34c0f3))
* **functions:** Allow configuration of Functions Emulator Origin ([#2017](https://github.com/angular/angularfire2/issues/2017)) ([d12b4c5](https://github.com/angular/angularfire2/commit/d12b4c5))
* **schematics:** ng deploy schematic ([#2046](https://github.com/angular/angularfire2/issues/2046)) ([be0a1fb](https://github.com/angular/angularfire2/commit/be0a1fb))



<a name="5.1.2"></a>
# [5.1.2](https://github.com/angular/angularfire2/compare/5.1.1...5.1.2) (2019-03-11)

Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ Firebase offers two cloud-based, client-accessible database solutions that suppo

- [Getting started with Firebase Messaging](docs/messaging/messaging.md)

### Monitor your application performance in production

- [Getting started with Performance Monitoring](docs/performance/getting-started.md)

### Directly call Cloud Functions

- [Getting started with Callable Functions](docs/functions/functions.md)
Expand Down
16 changes: 9 additions & 7 deletions cloudbuild.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,26 @@
# @next `gcloud builds submit --substitutions=TAG_NAME="v1.2.3-rc.1"`
# @latest `gcloud builds submit --substitutions=TAG_NAME="v1.2.3"`
steps:
- name: 'gcr.io/cloud-builders/yarn'
entrypoint: 'bash'
- name: node:lts
entrypoint: bash
args: ["./tools/build.sh"]
env:
- 'TAG_NAME=$TAG_NAME'
- 'SHORT_SHA=$SHORT_SHA'

- name: 'gcr.io/cloud-builders/yarn'
entrypoint: 'bash'
- name: node:lts
entrypoint: bash
args: ["./tools/test.sh"]

- name: 'gcr.io/cloud-builders/npm'
entrypoint: 'bash'
- name: node:lts
entrypoint: bash
env: ['TAG_NAME=$TAG_NAME']
args: ["./tools/release.sh"]
secretEnv: ['NPM_TOKEN']

secrets:
- kmsKeyName: projects/angularfire/locations/global/keyRings/cloud-build/cryptoKeys/cloud-build
secretEnv:
NPM_TOKEN: CiQAwtE8WoPa1sNqAQJZ1WMODuJooVmO6zihz2hAZOfUmDsgogUSTQCq8yp8qgltY+8jWpAR9GuS1JaVhd+fTVRilqLtdi2yXSdiDPTzLhZ+30bMlAOcoc0PxhCBn3JOpn8H1xshX+mG8yK7xog2Uq+CLVx/
NPM_TOKEN: CiQAwtE8WoPa1sNqAQJZ1WMODuJooVmO6zihz2hAZOfUmDsgogUSTQCq8yp8qgltY+8jWpAR9GuS1JaVhd+fTVRilqLtdi2yXSdiDPTzLhZ+30bMlAOcoc0PxhCBn3JOpn8H1xshX+mG8yK7xog2Uq+CLVx/

timeout: 1200s
145 changes: 145 additions & 0 deletions docs/performance/getting-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# Getting started with Performance Monitoring

## Automatic page load tracing

Understand your Angular application's real-world performance with [Firebase Performance Monitoring](https://firebase.google.com/docs/perf-mon). Performance Monitoring automatically provides a trace for **page load** when you add `AngularFirePerformanceModule` into your App Module's imports.

```ts
import { AngularFireModule } from '@angular/fire';
import { AngularFirePerformanceModule } from '@angular/fire/performance';
import { environment } from '../environments/environment';

@NgModule({
imports: [
BrowserModule,
AngularFireModule.initializeApp(environment.firebase),
AngularFirePerformanceModule,
...
],
declarations: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule {}
```

The page load trace breaks down into the following default metrics:

* [first paint traces](https://firebase.google.com/docs/perf-mon/automatic-web#first-paint) — measure the time between when the user navigates to a page and when any visual change happens
* [first contentful paint traces](https://firebase.google.com/docs/perf-mon/automatic-web#contentful-paint) — measure the time between when a user navigates to a page and when meaningful content displays, like an image or text
* [domInteractive traces](https://firebase.google.com/docs/perf-mon/automatic-web#domInteractive) — measure the time between when the user navigates to a page and when the page is considered interactive for the user
* [domContentLoadedEventEnd traces](https://firebase.google.com/docs/perf-mon/automatic-web#domContentLoaded) — measure the time between when the user navigates to a page and when the initial HTML document is completely loaded and parsed
* [loadEventEnd traces](https://firebase.google.com/docs/perf-mon/automatic-web#loadEventEnd) — measure the time between when the user navigates to the page and when the current document's load event completes
* [first input delay traces](https://firebase.google.com/docs/perf-mon/automatic-web#input-delay) — measure the time between when the user interacts with a page and when the browser is able to respond to that input
* **Angular specific traces** - measure the time needed for `ApplicationRef.isStable` to be true, an important metric to track if you're concerned about solving Zone.js issues for proper functionality of NGSW and Server Side Rendering

## Manual traces

You can inject `AngularFirePerformance` to perform manual traces on Observables.

```ts
constructor(private afp: AngularFirePerformance, private afs: AngularFirestore) {}

ngOnInit() {
this.articles = afs.collection('articles')
.collection('articles', ref => ref.orderBy('publishedAt', 'desc'))
.snapshotChanges()
.pipe(
// measure the amount of time between the Observable being subscribed to and first emission (or completion)
this.afp.trace('getArticles'),
map(articles => ...)
);
}
```

### `trace(name:string)`

The most basic operator, `trace` will measure the amount of time it takes for your observable to either complete or emit its first value. Beyond the basic trace there are several other operators:

### `traceUntil(name:string, test: (T) => Boolean)`

Trace the observable until the first emission that passes the provided test.

### `traceWhile(name:string, test: (T) => Boolean)`

Trace the observable until the first emission that fails the provided test.

### `traceUntilLast(name:string)`

Trace the observable until completion.

### `traceUntilFirst(name: string)`

Traces the observable until the first emission.

## Advanced usage

### Configuration via Dependency Injection

By default, `AngularFirePerformanceModule` traces your Angular application's time to `ApplicationRef.isStable`. `isStable` is an important metric to track if you're concerned about proper functionality of NGSW and Server Side Rendering. If you want to opt-out of the tracing of this metric use the `AUTOMATICALLY_TRACE_CORE_NG_METRICS` injection token:

```ts
import { NgModule } from '@angular/core';
import { AngularFirePerformanceModule, AUTOMATICALLY_TRACE_CORE_NG_METRICS } from '@angular/fire/functions';

@NgModule({
imports: [
...
AngularFirePerformanceModule,
...
],
...
providers: [
{ provide: AUTOMATICALLY_TRACE_CORE_NG_METRICS, useValue: false }
]
})
export class AppModule {}
```

Similarly, setting `INSTRUMENTATION_ENABLED` or `DATA_COLLECTION_ENABLED` to false disable all automatic and custom traces respectively.

### Get at an observable form of trace

`trace$(name:string)` provides an observable version of `firebase/perf`'s `.trace` method; the basis for `AngularFirePerfomance`'s pipes.

`.subscribe()` is equivalent to calling `.start()`
`.unsubscribe()` is equivalent to calling `.stop()`

### Using `TraceOptions` to collect additional metrics

`TraceOptions` can be provided to the aformentioned operators to collect custom metrics and attributes on your traces:

```ts
type TraceOptions = {
metrics?: { [key:string]: number },
attributes?: { [key:string]: string },
attribute$?: { [key:string]: Observable<string> },
incrementMetric$: { [key:string]: Observable<number|void|null|undefined> },
metric$?: { [key:string]: Observable<number> }
};
```

#### Usage:

```ts
const articleLength$ = this.articles.pipe(
map(actions => actions.length)
);

const articleSize$ = this.articles.pipe(
map(actions => actions.reduce((sum, a) => sum += JSON.stringify(a.payload.doc.data()).length))
)

this.articles = afs.collection('articles')
.collection('articles', ref => ref.orderBy('publishedAt', 'desc'))
.snapshotChanges()
.pipe(
this.afp.trace('getArticles', {
attributes: { gitSha: '1d277f823ad98dd739fb86e9a6c440aa8237ff3a' },
metrics: { something: 42 },
metrics$: { count: articleLength$, size: articleSize$ },
attributes$: { user: this.afAuth.user },
incrementMetric$: { buttonClicks: fromEvent(button, 'click') }
}),
share()
);
```
2 changes: 2 additions & 0 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ module.exports = function(config) {
'node_modules/firebase/firebase-database.js',
'node_modules/firebase/firebase-firestore.js',
'node_modules/firebase/firebase-functions.js',
'node_modules/firebase/firebase-performance.js',
'node_modules/firebase/firebase-storage.js',
'dist/packages-dist/bundles/core.umd.{js,map}',
'dist/packages-dist/bundles/auth.umd.{js,map}',
Expand All @@ -38,6 +39,7 @@ module.exports = function(config) {
'dist/packages-dist/bundles/firestore.umd.{js,map}',
'dist/packages-dist/bundles/functions.umd.{js,map}',
'dist/packages-dist/bundles/storage.umd.{js,map}',
'dist/packages-dist/bundles/performance.umd.{js,map}',
'dist/packages-dist/bundles/database-deprecated.umd.{js,map}',
'dist/packages-dist/bundles/test.umd.{js,map}',
],
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "angularfire2",
"name": "@angular/fire",
"version": "5.2.0",
"description": "The official library of Firebase and Angular.",
"private": true,
Expand Down Expand Up @@ -33,7 +33,7 @@
},
"homepage": "https://github.com/angular/angularfire2#readme",
"dependencies": {
"@angular-devkit/architect": "^0.800.0-rc.4",
"@angular-devkit/architect": "^0.800.0-rc.4 || >=8.0.0 <9 || 9.0.0-0",
"@angular-devkit/core": ">=6.0.0 <9 || 9.0.0-0",
"@angular-devkit/schematics": ">=6.0.0 <9 || 9.0.0-0",
"@angular/common": ">=6.0.0 <9 || 9.0.0-0",
Expand Down
1 change: 1 addition & 0 deletions src/core/firebase.app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export class FirebaseApp {
auth: () => FirebaseAuth;
database: (databaseURL?: string) => FirebaseDatabase;
messaging: () => FirebaseMessaging;
performance: () => any; // SEMVER: once >= 6 import performance.Performance
storage: (storageBucket?: string) => FirebaseStorage;
delete: () => Promise<void>;
firestore: () => FirebaseFirestore;
Expand Down
1 change: 1 addition & 0 deletions src/performance/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './performance.spec';
1 change: 1 addition & 0 deletions src/performance/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './public_api';
30 changes: 30 additions & 0 deletions src/performance/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "@angular/fire/performance",
"version": "ANGULARFIRE2_VERSION",
"description": "The performance monitoring module",
"main": "../bundles/performance.umd.js",
"module": "index.js",
"es2015": "./es2015/index.js",
"keywords": [
"angular",
"firebase",
"rxjs"
],
"repository": {
"type": "git",
"url": "git+https://github.com/angular/angularfire2.git"
},
"author": "angular,firebase",
"license": "MIT",
"peerDependencies": {
"@angular/fire": "ANGULARFIRE2_VERSION",
"@angular/common": "ANGULAR_VERSION",
"@angular/core": "ANGULAR_VERSION",
"@angular/platform-browser": "ANGULAR_VERSION",
"@angular/platform-browser-dynamic": "ANGULAR_VERSION",
"firebase": "FIREBASE_VERSION",
"rxjs": "RXJS_VERSION",
"zone.js": "ZONEJS_VERSION"
},
"typings": "index.d.ts"
}
9 changes: 9 additions & 0 deletions src/performance/performance.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { NgModule } from '@angular/core';
import { AngularFirePerformance } from './performance';

import 'firebase/performance';

@NgModule({
providers: [ AngularFirePerformance ]
})
export class AngularFirePerformanceModule { }
36 changes: 36 additions & 0 deletions src/performance/performance.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { TestBed, inject } from '@angular/core/testing';
import { FirebaseApp, AngularFireModule } from '@angular/fire';
import { AngularFirePerformance, AngularFirePerformanceModule } from '@angular/fire/performance';
import { COMMON_CONFIG } from './test-config';

describe('AngularFirePerformance', () => {
let app: FirebaseApp;
let afp: AngularFirePerformance;

beforeEach(() => {
TestBed.configureTestingModule({
imports: [
AngularFireModule.initializeApp(COMMON_CONFIG),
AngularFirePerformanceModule
]
});
inject([FirebaseApp, AngularFirePerformance], (app_: FirebaseApp, _perf: AngularFirePerformance) => {
app = app_;
afp = _perf;
})();
});

afterEach(done => {
app.delete();
done();
});

it('should be exist', () => {
expect(afp instanceof AngularFirePerformance).toBe(true);
});

it('should have the Performance instance', () => {
expect(afp.performance).toBeDefined();
});

});
Loading

0 comments on commit 2469e77

Please sign in to comment.