Skip to content

Commit f415faa

Browse files
committed
Operator rename, more configuration, and docs
1 parent 1d277f8 commit f415faa

File tree

2 files changed

+135
-38
lines changed

2 files changed

+135
-38
lines changed

docs/performance/getting-started.md

+102-30
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,40 @@
11
# Getting started with Performance Monitoring
22

3-
## Basic usage
3+
## Automatic page load tracing
44

5-
**TBD** basic explainer
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.
638

739
```ts
840
constructor(private afp: AngularFirePerformance, private afs: AngularFirestore) {}
@@ -12,62 +44,102 @@ ngOnInit() {
1244
.collection('articles', ref => ref.orderBy('publishedAt', 'desc'))
1345
.snapshotChanges()
1446
.pipe(
47+
// measure the amount of time between the Observable being subscribed to and first emission (or completion)
1548
this.afp.trace('getArticles'),
1649
map(articles => ...)
1750
);
1851
}
1952
```
2053

21-
`trace(name:string)` will trace the time it takes for your observable to either complete or emit it's first value. Beyond the basic trace there are three other operators:
22-
23-
### `traceComplete(name:string)`
54+
### `trace(name:string)`
2455

25-
Traces the observable until the completion.
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:
2657

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

29-
Traces the observable until the first emission that passes the provided test.
60+
Trace the observable until the first emission that passes the provided test.
3061

31-
### `traceFirst(name: string)`
62+
### `traceWhile(name:string, test: (T) => Boolean)`
3263

33-
Traces the observable until the first emission or the first emission that matches the provided test.
64+
Trace the observable until the first emission that fails the provided test.
3465

35-
## Advanced usage
66+
### `traceUntilLast(name:string)`
3667

37-
### `trace$(...) => Observable<void>`
68+
Trace the observable until completion.
3869

39-
`(name:string)`
40-
`(name:string, options: TraceOptions)`
70+
### `traceUntilFirst(name: string)`
4171

42-
Observable version of `firebase/perf`'s `.trace` method; the basis for `AngularFirePerfomance`'s pipes.
72+
Traces the observable until the first emission.
4373

44-
`.subscribe()` is equivalent to calling `.start()`
45-
`.unsubscribe()` is equivalent to calling `.stop()`
74+
## Advanced usage
4675

47-
### `TraceOptions`
76+
### Configuration via Dependency Injection
4877

49-
**TBD explain how each option is used by `.trace$`**
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:
5079

5180
```ts
52-
export const gameUpdate = (state: State, input: Input): State => (
53-
afp.traceComplete('game', {
54-
pluckMetrics: ['score'],
55-
attribute$: { user: this.afAuth.user }
56-
}),
57-
whileNotGameOver(state, input),
58-
processInput(state, input),
59-
updateState(state)
60-
);
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 {}
6196
```
6297

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+
63111
```ts
64-
export type TraceOptions = {
65-
pluckMetrics?: [string],
66-
pluckAttributes?: [string],
112+
type TraceOptions = {
67113
metrics?: { [key:string]: number },
68114
attributes?: { [key:string]: string },
69115
attribute$?: { [key:string]: Observable<string> },
70116
incrementMetric$: { [key:string]: Observable<number|void|null|undefined> },
71117
metric$?: { [key:string]: Observable<number> }
72118
};
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+
);
73145
```

src/performance/performance.ts

+33-8
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import { first, tap } from 'rxjs/operators';
44
import { performance } from 'firebase/app';
55

66
export const AUTOMATICALLY_TRACE_CORE_NG_METRICS = new InjectionToken<boolean>('angularfire2.performance.auto_trace');
7+
export const INSTRUMENTATION_ENABLED = new InjectionToken<boolean>('angularfire2.performance.instrumentationEnabled');
8+
export const DATA_COLLECTION_ENABLED = new InjectionToken<boolean>('angularfire2.performance.dataCollectionEnabled');
79

810
export type TraceOptions = {
9-
metrics: {[key:string]: number},
10-
attributes?:{[key:string]:string},
11-
attribute$?:{[key:string]:Observable<string>},
12-
incrementMetric$:{[key:string]: Observable<number|void|null|undefined>},
13-
metric$?:{[key:string]: Observable<number>}
11+
metrics?: {[key:string]: number},
12+
attributes?: {[key:string]:string},
13+
attribute$?: {[key:string]:Observable<string>},
14+
incrementMetric$?: {[key:string]: Observable<number|void|null|undefined>},
15+
metric$?: {[key:string]: Observable<number>}
1416
};
1517

1618
@Injectable()
@@ -20,17 +22,22 @@ export class AngularFirePerformance {
2022

2123
constructor(
2224
@Optional() @Inject(AUTOMATICALLY_TRACE_CORE_NG_METRICS) automaticallyTraceCoreNgMetrics:boolean|null,
25+
@Optional() @Inject(INSTRUMENTATION_ENABLED) instrumentationEnabled:boolean|null,
26+
@Optional() @Inject(DATA_COLLECTION_ENABLED) dataCollectionEnabled:boolean|null,
2327
appRef: ApplicationRef,
2428
private zone: NgZone
2529
) {
2630

2731
this.performance = zone.runOutsideAngular(() => performance());
32+
33+
if (instrumentationEnabled == false) { this.performance.instrumentationEnabled = false }
34+
if (dataCollectionEnabled == false) { this.performance.dataCollectionEnabled = false }
2835

2936
if (automaticallyTraceCoreNgMetrics != false) {
3037

31-
// TODO detirmine more built in metrics
38+
// TODO determine more built in metrics
3239
appRef.isStable.pipe(
33-
this.traceComplete('isStable'),
40+
this.traceUntilLast('isStable'),
3441
first(it => it)
3542
).subscribe();
3643

@@ -73,7 +80,14 @@ export class AngularFirePerformance {
7380
)
7481
};
7582

76-
traceComplete = <T=any>(name:string, options?: TraceOptions) => (source$: Observable<T>) => {
83+
traceWhile = <T=any>(name:string, test: (a:T) => boolean, options?: TraceOptions) => (source$: Observable<T>) => {
84+
const traceSubscription = this.trace$(name, options).subscribe();
85+
return source$.pipe(
86+
tap(a => { if (!test(a)) { traceSubscription.unsubscribe() }})
87+
)
88+
};
89+
90+
traceUntilLast= <T=any>(name:string, options?: TraceOptions) => (source$: Observable<T>) => {
7791
const traceSubscription = this.trace$(name, options).subscribe();
7892
return source$.pipe(
7993
tap(
@@ -84,6 +98,17 @@ export class AngularFirePerformance {
8498
)
8599
};
86100

101+
traceUntilFirst = <T=any>(name:string, options?: TraceOptions) => (source$: Observable<T>) => {
102+
const traceSubscription = this.trace$(name, options).subscribe();
103+
return source$.pipe(
104+
tap(
105+
() => traceSubscription.unsubscribe(),
106+
() => {},
107+
() => {}
108+
)
109+
)
110+
};
111+
87112
trace = <T=any>(name:string, options?: TraceOptions) => (source$: Observable<T>) => {
88113
const traceSubscription = this.trace$(name, options).subscribe();
89114
return source$.pipe(

0 commit comments

Comments
 (0)