Skip to content

Commit 2469e77

Browse files
authored
feat(performance): AngularFire Performance Monitoring (#2064)
* 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
1 parent e32164d commit 2469e77

22 files changed

+473
-10
lines changed

CHANGELOG.md

+19
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1+
<a name="5.2.0"></a>
2+
# [5.2.0](https://github.com/angular/angularfire2/compare/5.1.3...5.2.0) (2019-05-24)
3+
4+
### Bug Fixes
5+
6+
* **firestore:** Fix for builds targeting Node ([#2079](https://github.com/angular/angularfire2/issues/2079)) ([8a33826](https://github.com/angular/angularfire2/commit/8a33826))
7+
* **storage:** Typo in updateMetadata method ([#2029](https://github.com/angular/angularfire2/issues/2029)) ([6133296](https://github.com/angular/angularfire2/commit/6133296))
8+
9+
### Features
10+
11+
* **performance:** AngularFire Performance Monitoring ([#2064](https://github.com/angular/angularfire2/issues/2064))
12+
* **auth-guard:** AngularFire Auth Guards ([#2016](https://github.com/angular/angularfire2/issues/2016)) ([e32164d](https://github.com/angular/angularfire2/commit/e32164d))
13+
* **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))
14+
* **firestore:** Support Firestore Collection Group Queries ([#2066](https://github.com/angular/angularfire2/issues/2066)) ([c34c0f3](https://github.com/angular/angularfire2/commit/c34c0f3))
15+
* **functions:** Allow configuration of Functions Emulator Origin ([#2017](https://github.com/angular/angularfire2/issues/2017)) ([d12b4c5](https://github.com/angular/angularfire2/commit/d12b4c5))
16+
* **schematics:** ng deploy schematic ([#2046](https://github.com/angular/angularfire2/issues/2046)) ([be0a1fb](https://github.com/angular/angularfire2/commit/be0a1fb))
17+
18+
19+
120
<a name="5.1.2"></a>
221
# [5.1.2](https://github.com/angular/angularfire2/compare/5.1.1...5.1.2) (2019-03-11)
322

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ Firebase offers two cloud-based, client-accessible database solutions that suppo
9898

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

101+
### Monitor your application performance in production
102+
103+
- [Getting started with Performance Monitoring](docs/performance/getting-started.md)
104+
101105
### Directly call Cloud Functions
102106

103107
- [Getting started with Callable Functions](docs/functions/functions.md)

cloudbuild.yaml

+9-7
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,26 @@
44
# @next `gcloud builds submit --substitutions=TAG_NAME="v1.2.3-rc.1"`
55
# @latest `gcloud builds submit --substitutions=TAG_NAME="v1.2.3"`
66
steps:
7-
- name: 'gcr.io/cloud-builders/yarn'
8-
entrypoint: 'bash'
7+
- name: node:lts
8+
entrypoint: bash
99
args: ["./tools/build.sh"]
1010
env:
1111
- 'TAG_NAME=$TAG_NAME'
1212
- 'SHORT_SHA=$SHORT_SHA'
1313

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

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

2424
secrets:
2525
- kmsKeyName: projects/angularfire/locations/global/keyRings/cloud-build/cryptoKeys/cloud-build
2626
secretEnv:
27-
NPM_TOKEN: CiQAwtE8WoPa1sNqAQJZ1WMODuJooVmO6zihz2hAZOfUmDsgogUSTQCq8yp8qgltY+8jWpAR9GuS1JaVhd+fTVRilqLtdi2yXSdiDPTzLhZ+30bMlAOcoc0PxhCBn3JOpn8H1xshX+mG8yK7xog2Uq+CLVx/
27+
NPM_TOKEN: CiQAwtE8WoPa1sNqAQJZ1WMODuJooVmO6zihz2hAZOfUmDsgogUSTQCq8yp8qgltY+8jWpAR9GuS1JaVhd+fTVRilqLtdi2yXSdiDPTzLhZ+30bMlAOcoc0PxhCBn3JOpn8H1xshX+mG8yK7xog2Uq+CLVx/
28+
29+
timeout: 1200s

docs/performance/getting-started.md

+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# Getting started with Performance Monitoring
2+
3+
## Automatic page load tracing
4+
5+
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.
6+
7+
```ts
8+
import { AngularFireModule } from '@angular/fire';
9+
import { AngularFirePerformanceModule } from '@angular/fire/performance';
10+
import { environment } from '../environments/environment';
11+
12+
@NgModule({
13+
imports: [
14+
BrowserModule,
15+
AngularFireModule.initializeApp(environment.firebase),
16+
AngularFirePerformanceModule,
17+
...
18+
],
19+
declarations: [ AppComponent ],
20+
bootstrap: [ AppComponent ]
21+
})
22+
export class AppModule {}
23+
```
24+
25+
The page load trace breaks down into the following default metrics:
26+
27+
* [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
28+
* [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
29+
* [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
30+
* [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
31+
* [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
32+
* [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
33+
* **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
34+
35+
## Manual traces
36+
37+
You can inject `AngularFirePerformance` to perform manual traces on Observables.
38+
39+
```ts
40+
constructor(private afp: AngularFirePerformance, private afs: AngularFirestore) {}
41+
42+
ngOnInit() {
43+
this.articles = afs.collection('articles')
44+
.collection('articles', ref => ref.orderBy('publishedAt', 'desc'))
45+
.snapshotChanges()
46+
.pipe(
47+
// measure the amount of time between the Observable being subscribed to and first emission (or completion)
48+
this.afp.trace('getArticles'),
49+
map(articles => ...)
50+
);
51+
}
52+
```
53+
54+
### `trace(name:string)`
55+
56+
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:
57+
58+
### `traceUntil(name:string, test: (T) => Boolean)`
59+
60+
Trace the observable until the first emission that passes the provided test.
61+
62+
### `traceWhile(name:string, test: (T) => Boolean)`
63+
64+
Trace the observable until the first emission that fails the provided test.
65+
66+
### `traceUntilLast(name:string)`
67+
68+
Trace the observable until completion.
69+
70+
### `traceUntilFirst(name: string)`
71+
72+
Traces the observable until the first emission.
73+
74+
## Advanced usage
75+
76+
### Configuration via Dependency Injection
77+
78+
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:
79+
80+
```ts
81+
import { NgModule } from '@angular/core';
82+
import { AngularFirePerformanceModule, AUTOMATICALLY_TRACE_CORE_NG_METRICS } from '@angular/fire/functions';
83+
84+
@NgModule({
85+
imports: [
86+
...
87+
AngularFirePerformanceModule,
88+
...
89+
],
90+
...
91+
providers: [
92+
{ provide: AUTOMATICALLY_TRACE_CORE_NG_METRICS, useValue: false }
93+
]
94+
})
95+
export class AppModule {}
96+
```
97+
98+
Similarly, setting `INSTRUMENTATION_ENABLED` or `DATA_COLLECTION_ENABLED` to false disable all automatic and custom traces respectively.
99+
100+
### Get at an observable form of trace
101+
102+
`trace$(name:string)` provides an observable version of `firebase/perf`'s `.trace` method; the basis for `AngularFirePerfomance`'s pipes.
103+
104+
`.subscribe()` is equivalent to calling `.start()`
105+
`.unsubscribe()` is equivalent to calling `.stop()`
106+
107+
### Using `TraceOptions` to collect additional metrics
108+
109+
`TraceOptions` can be provided to the aformentioned operators to collect custom metrics and attributes on your traces:
110+
111+
```ts
112+
type TraceOptions = {
113+
metrics?: { [key:string]: number },
114+
attributes?: { [key:string]: string },
115+
attribute$?: { [key:string]: Observable<string> },
116+
incrementMetric$: { [key:string]: Observable<number|void|null|undefined> },
117+
metric$?: { [key:string]: Observable<number> }
118+
};
119+
```
120+
121+
#### Usage:
122+
123+
```ts
124+
const articleLength$ = this.articles.pipe(
125+
map(actions => actions.length)
126+
);
127+
128+
const articleSize$ = this.articles.pipe(
129+
map(actions => actions.reduce((sum, a) => sum += JSON.stringify(a.payload.doc.data()).length))
130+
)
131+
132+
this.articles = afs.collection('articles')
133+
.collection('articles', ref => ref.orderBy('publishedAt', 'desc'))
134+
.snapshotChanges()
135+
.pipe(
136+
this.afp.trace('getArticles', {
137+
attributes: { gitSha: '1d277f823ad98dd739fb86e9a6c440aa8237ff3a' },
138+
metrics: { something: 42 },
139+
metrics$: { count: articleLength$, size: articleSize$ },
140+
attributes$: { user: this.afAuth.user },
141+
incrementMetric$: { buttonClicks: fromEvent(button, 'click') }
142+
}),
143+
share()
144+
);
145+
```

karma.conf.js

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ module.exports = function(config) {
3030
'node_modules/firebase/firebase-database.js',
3131
'node_modules/firebase/firebase-firestore.js',
3232
'node_modules/firebase/firebase-functions.js',
33+
'node_modules/firebase/firebase-performance.js',
3334
'node_modules/firebase/firebase-storage.js',
3435
'dist/packages-dist/bundles/core.umd.{js,map}',
3536
'dist/packages-dist/bundles/auth.umd.{js,map}',
@@ -38,6 +39,7 @@ module.exports = function(config) {
3839
'dist/packages-dist/bundles/firestore.umd.{js,map}',
3940
'dist/packages-dist/bundles/functions.umd.{js,map}',
4041
'dist/packages-dist/bundles/storage.umd.{js,map}',
42+
'dist/packages-dist/bundles/performance.umd.{js,map}',
4143
'dist/packages-dist/bundles/database-deprecated.umd.{js,map}',
4244
'dist/packages-dist/bundles/test.umd.{js,map}',
4345
],

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"name": "angularfire2",
2+
"name": "@angular/fire",
33
"version": "5.2.0",
44
"description": "The official library of Firebase and Angular.",
55
"private": true,
@@ -33,7 +33,7 @@
3333
},
3434
"homepage": "https://github.com/angular/angularfire2#readme",
3535
"dependencies": {
36-
"@angular-devkit/architect": "^0.800.0-rc.4",
36+
"@angular-devkit/architect": "^0.800.0-rc.4 || >=8.0.0 <9 || 9.0.0-0",
3737
"@angular-devkit/core": ">=6.0.0 <9 || 9.0.0-0",
3838
"@angular-devkit/schematics": ">=6.0.0 <9 || 9.0.0-0",
3939
"@angular/common": ">=6.0.0 <9 || 9.0.0-0",

src/core/firebase.app.module.ts

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export class FirebaseApp {
2525
auth: () => FirebaseAuth;
2626
database: (databaseURL?: string) => FirebaseDatabase;
2727
messaging: () => FirebaseMessaging;
28+
performance: () => any; // SEMVER: once >= 6 import performance.Performance
2829
storage: (storageBucket?: string) => FirebaseStorage;
2930
delete: () => Promise<void>;
3031
firestore: () => FirebaseFirestore;

src/performance/index.spec.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './performance.spec';

src/performance/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './public_api';

src/performance/package.json

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"name": "@angular/fire/performance",
3+
"version": "ANGULARFIRE2_VERSION",
4+
"description": "The performance monitoring module",
5+
"main": "../bundles/performance.umd.js",
6+
"module": "index.js",
7+
"es2015": "./es2015/index.js",
8+
"keywords": [
9+
"angular",
10+
"firebase",
11+
"rxjs"
12+
],
13+
"repository": {
14+
"type": "git",
15+
"url": "git+https://github.com/angular/angularfire2.git"
16+
},
17+
"author": "angular,firebase",
18+
"license": "MIT",
19+
"peerDependencies": {
20+
"@angular/fire": "ANGULARFIRE2_VERSION",
21+
"@angular/common": "ANGULAR_VERSION",
22+
"@angular/core": "ANGULAR_VERSION",
23+
"@angular/platform-browser": "ANGULAR_VERSION",
24+
"@angular/platform-browser-dynamic": "ANGULAR_VERSION",
25+
"firebase": "FIREBASE_VERSION",
26+
"rxjs": "RXJS_VERSION",
27+
"zone.js": "ZONEJS_VERSION"
28+
},
29+
"typings": "index.d.ts"
30+
}

src/performance/performance.module.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { NgModule } from '@angular/core';
2+
import { AngularFirePerformance } from './performance';
3+
4+
import 'firebase/performance';
5+
6+
@NgModule({
7+
providers: [ AngularFirePerformance ]
8+
})
9+
export class AngularFirePerformanceModule { }

src/performance/performance.spec.ts

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { TestBed, inject } from '@angular/core/testing';
2+
import { FirebaseApp, AngularFireModule } from '@angular/fire';
3+
import { AngularFirePerformance, AngularFirePerformanceModule } from '@angular/fire/performance';
4+
import { COMMON_CONFIG } from './test-config';
5+
6+
describe('AngularFirePerformance', () => {
7+
let app: FirebaseApp;
8+
let afp: AngularFirePerformance;
9+
10+
beforeEach(() => {
11+
TestBed.configureTestingModule({
12+
imports: [
13+
AngularFireModule.initializeApp(COMMON_CONFIG),
14+
AngularFirePerformanceModule
15+
]
16+
});
17+
inject([FirebaseApp, AngularFirePerformance], (app_: FirebaseApp, _perf: AngularFirePerformance) => {
18+
app = app_;
19+
afp = _perf;
20+
})();
21+
});
22+
23+
afterEach(done => {
24+
app.delete();
25+
done();
26+
});
27+
28+
it('should be exist', () => {
29+
expect(afp instanceof AngularFirePerformance).toBe(true);
30+
});
31+
32+
it('should have the Performance instance', () => {
33+
expect(afp.performance).toBeDefined();
34+
});
35+
36+
});

0 commit comments

Comments
 (0)