@juice-js/observability 1.1.0-alpha.21 → 1.1.0-alpha.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/fesm2022/{juice-js-observability-alert.page-nEy5Gc6j.mjs → juice-js-observability-alert.page-DOAX9WWa.mjs} +154 -2
  2. package/fesm2022/juice-js-observability-alert.page-DOAX9WWa.mjs.map +1 -0
  3. package/fesm2022/juice-js-observability-incident.page-CCHEWXou.mjs +400 -0
  4. package/fesm2022/juice-js-observability-incident.page-CCHEWXou.mjs.map +1 -0
  5. package/fesm2022/juice-js-observability-incidents.page-CpNqYp2W.mjs +438 -0
  6. package/fesm2022/juice-js-observability-incidents.page-CpNqYp2W.mjs.map +1 -0
  7. package/fesm2022/juice-js-observability-notifications.page-BDqb10C6.mjs +725 -0
  8. package/fesm2022/juice-js-observability-notifications.page-BDqb10C6.mjs.map +1 -0
  9. package/fesm2022/juice-js-observability-rules.page-Df2uDVwz.mjs +580 -0
  10. package/fesm2022/juice-js-observability-rules.page-Df2uDVwz.mjs.map +1 -0
  11. package/fesm2022/{juice-js-observability-service.page-BrcID0Bs.mjs → juice-js-observability-service.page-BFXUe4mX.mjs} +3 -1
  12. package/fesm2022/juice-js-observability-service.page-BFXUe4mX.mjs.map +1 -0
  13. package/fesm2022/juice-js-observability-silences.page-Cg53dJAy.mjs +523 -0
  14. package/fesm2022/juice-js-observability-silences.page-Cg53dJAy.mjs.map +1 -0
  15. package/fesm2022/juice-js-observability-timeline.page-CtU2EoDN.mjs +213 -0
  16. package/fesm2022/juice-js-observability-timeline.page-CtU2EoDN.mjs.map +1 -0
  17. package/fesm2022/juice-js-observability.mjs +128 -3
  18. package/fesm2022/juice-js-observability.mjs.map +1 -1
  19. package/package.json +1 -1
  20. package/styles/_observability.scss +4 -0
  21. package/types/juice-js-observability.d.ts +197 -0
  22. package/fesm2022/juice-js-observability-alert.page-nEy5Gc6j.mjs.map +0 -1
  23. package/fesm2022/juice-js-observability-rules.page-Crb07uWm.mjs +0 -175
  24. package/fesm2022/juice-js-observability-rules.page-Crb07uWm.mjs.map +0 -1
  25. package/fesm2022/juice-js-observability-service.page-BrcID0Bs.mjs.map +0 -1
@@ -192,6 +192,136 @@ export interface Rule {
192
192
  enabled: boolean;
193
193
  definition: Record<string, unknown>;
194
194
  loadedAt: string;
195
+ managedBy: string;
196
+ etag: string;
197
+ }
198
+ export interface Channel {
199
+ id: string;
200
+ key: string;
201
+ kind: string;
202
+ destination: string;
203
+ events: string[];
204
+ ruleKeys: string[];
205
+ environments: string[];
206
+ recipients: string[] | null;
207
+ secretRef: string | null;
208
+ groupWindowSeconds: number;
209
+ minimumSeverity: string;
210
+ enabled: boolean;
211
+ managedBy: string;
212
+ loadedAt: string;
213
+ recentFailures: number;
214
+ waiting: number;
215
+ }
216
+ export interface DeliveryAttempt {
217
+ number: number;
218
+ at: string;
219
+ statusCode: number | null;
220
+ error: string | null;
221
+ durationMs: number;
222
+ }
223
+ export interface Delivery {
224
+ id: string;
225
+ alertId: string;
226
+ event: string;
227
+ status: string;
228
+ attempts: number;
229
+ createdAt: string;
230
+ firstAttemptAt: string | null;
231
+ deliveredAt: string | null;
232
+ nextAttemptAt: string | null;
233
+ lastStatusCode: number | null;
234
+ lastError: string | null;
235
+ history: DeliveryAttempt[];
236
+ suppressedBy: string | null;
237
+ groupedInto: string | null;
238
+ escalationStep: number;
239
+ }
240
+ export interface AllowlistEntry {
241
+ destination: string;
242
+ ports: number[];
243
+ managedBy: string;
244
+ loadedAt: string;
245
+ }
246
+ export interface EscalationStep {
247
+ afterMinutes: number;
248
+ endpointKeys: string[];
249
+ }
250
+ export interface EscalationPolicy {
251
+ id: string;
252
+ key: string;
253
+ name: string;
254
+ enabled: boolean;
255
+ ruleKeys: string[];
256
+ environments: string[];
257
+ minimumSeverity: string;
258
+ stopOnAcknowledge: boolean;
259
+ steps: EscalationStep[];
260
+ updatedAt: string;
261
+ }
262
+ export interface Silence {
263
+ id: string;
264
+ kind: string;
265
+ comment: string;
266
+ ruleKeys: string[];
267
+ service: string | null;
268
+ environment: string | null;
269
+ host: string | null;
270
+ maximumSeverity: string | null;
271
+ allAlerts: boolean;
272
+ startsAt: string;
273
+ endsAt: string;
274
+ repeatWeekly: boolean;
275
+ active: boolean;
276
+ createdBy: string;
277
+ createdAt: string;
278
+ cancelledAt: string | null;
279
+ cancelledBy: string | null;
280
+ held: number;
281
+ }
282
+ export interface IncidentSummary {
283
+ id: string;
284
+ title: string;
285
+ severity: string;
286
+ state: string;
287
+ createdBy: string;
288
+ createdAt: string;
289
+ updatedAt: string;
290
+ resolvedAt: string | null;
291
+ }
292
+ export interface IncidentEvent {
293
+ at: string;
294
+ actor: string;
295
+ type: string;
296
+ from: string | null;
297
+ to: string | null;
298
+ text: string | null;
299
+ alertId: string | null;
300
+ }
301
+ export interface IncidentDetail {
302
+ incident: IncidentSummary;
303
+ alerts: Alert[];
304
+ events: IncidentEvent[];
305
+ openAlerts: number;
306
+ }
307
+ export interface AlertNotifications {
308
+ deliveries: {
309
+ endpointKey: string;
310
+ kind: string;
311
+ delivery: Delivery;
312
+ }[];
313
+ escalations: {
314
+ policy: string;
315
+ name: string;
316
+ state: string;
317
+ stepsSent: number;
318
+ steps: number;
319
+ nextAt: string | null;
320
+ stopReason: string | null;
321
+ endedAt: string | null;
322
+ }[];
323
+ silences: Silence[];
324
+ incidents: IncidentSummary[];
195
325
  }
196
326
  export interface TraceSummary {
197
327
  traceId: string;
@@ -337,6 +467,45 @@ export interface OutboxBacklog {
337
467
  recentDelaySeconds: number | null;
338
468
  fallingBehind: boolean;
339
469
  }
470
+ export interface TimelineEntry {
471
+ at: string;
472
+ type: 'change' | 'health' | 'alert';
473
+ title: string;
474
+ detail: string | null;
475
+ state: string | null;
476
+ id: string | null;
477
+ serviceId: string | null;
478
+ environment: string | null;
479
+ }
480
+ export interface Timeline {
481
+ from: string;
482
+ to: string;
483
+ entries: TimelineEntry[];
484
+ truncated: boolean;
485
+ }
486
+ export interface CorrelationReason {
487
+ code: string;
488
+ text: string;
489
+ points: number;
490
+ }
491
+ export interface CorrelatedItem {
492
+ type: string;
493
+ id: string;
494
+ at: string;
495
+ title: string;
496
+ detail: string | null;
497
+ kind: string | null;
498
+ scope: string;
499
+ score: number;
500
+ reasons: CorrelationReason[];
501
+ }
502
+ /** What happened near an alert: correlation, never a proven cause. */
503
+ export interface Related {
504
+ relation: string;
505
+ note: string;
506
+ breachStartedAt: string;
507
+ items: CorrelatedItem[];
508
+ }
340
509
  export interface LoadRun {
341
510
  id: string;
342
511
  name: string;
@@ -417,6 +586,9 @@ export declare class ApiService {
417
586
  /** The caller's tenant, fetched once. */
418
587
  readonly tenant$: Observable<Tenant>;
419
588
  private get;
589
+ /** A change. `Idempotency-Key` on what can be retried, so a repeated click or a lost response doesn't do it twice. */
590
+ private send;
591
+ private once;
420
592
  /** Every page of a cursor-paged list (bounded: at most `maxPages` requests). */
421
593
  private all;
422
594
  environments(): Observable<Page<DeploymentEnvironment>>;
@@ -426,6 +598,29 @@ export declare class ApiService {
426
598
  health(query: Query): Observable<Page<Health>>;
427
599
  topology(environment?: string | null): Observable<Topology>;
428
600
  rules(): Observable<Page<Rule>>;
601
+ createRule(definition: unknown): Observable<Rule>;
602
+ /** With the rule's etag, so a change made since it was read isn't overwritten. */
603
+ updateRule(key: string, definition: unknown, etag: string): Observable<Rule>;
604
+ channels(): Observable<Page<Channel>>;
605
+ createChannel(body: unknown): Observable<Channel>;
606
+ updateChannel(id: string, body: unknown): Observable<Channel>;
607
+ deliveries(id: string, query?: Query): Observable<Page<Delivery>>;
608
+ allowlist(): Observable<Page<AllowlistEntry>>;
609
+ putAllowlist(destination: string, ports: number[]): Observable<AllowlistEntry>;
610
+ removeAllowlist(destination: string): Observable<void>;
611
+ escalationPolicies(): Observable<Page<EscalationPolicy>>;
612
+ createEscalationPolicy(body: unknown): Observable<EscalationPolicy>;
613
+ updateEscalationPolicy(key: string, body: unknown): Observable<EscalationPolicy>;
614
+ silences(query?: Query): Observable<Page<Silence>>;
615
+ createSilence(body: unknown): Observable<Silence>;
616
+ cancelSilence(id: string): Observable<void>;
617
+ incidents(query?: Query): Observable<Page<IncidentSummary>>;
618
+ incident(id: string): Observable<IncidentDetail>;
619
+ createIncident(body: unknown): Observable<IncidentSummary>;
620
+ moveIncident(id: string, to: string, comment: string): Observable<IncidentSummary>;
621
+ addIncidentNote(id: string, text: string): Observable<IncidentEvent>;
622
+ removeIncidentAlert(id: string, alertId: string): Observable<void>;
623
+ alertNotifications(id: string): Observable<AlertNotifications>;
429
624
  /** One page of alerts, newest first; pass the previous page's `nextCursor` as `cursor` for the next. */
430
625
  alerts(query: Query): Observable<Page<Alert>>;
431
626
  /** Every alert matching the query (bounded, like the other complete lists). */
@@ -440,6 +635,8 @@ export declare class ApiService {
440
635
  series(query: Query): Observable<Series>;
441
636
  serviceStats(query: Query): Observable<ServiceStats>;
442
637
  capacity(query: Query): Observable<Capacity>;
638
+ timeline(query: Query): Observable<Timeline>;
639
+ relatedToAlert(id: string): Observable<Related>;
443
640
  loadRuns(query: Query): Observable<Page<LoadRun>>;
444
641
  loadRunResults(id: string): Observable<LoadRunResults>;
445
642
  dependencyStats(query: Query): Observable<DependencyStatsPage>;
@@ -1 +0,0 @@
1
- {"version":3,"file":"juice-js-observability-alert.page-nEy5Gc6j.mjs","names":[],"sources":["../../../../projects/juice-js/observability/src/lib/pages/alert.page.ts"],"sourcesContent":["import { LinkPipe } from '../core/options';\nimport { ChangeDetectionStrategy, Component, computed, inject, input, signal } from '@angular/core';\nimport { apiResource } from '../shared/resource';\nimport { DatePipe } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatFormFieldModule } from '@angular/material/form-field';\nimport { MatInputModule } from '@angular/material/input';\nimport { MatProgressBarModule } from '@angular/material/progress-bar';\nimport { RouterLink } from '@angular/router';\nimport { ApiService } from '../core/api.service';\nimport { ContextService } from '../core/context.service';\nimport { AgoPipe, ReasonPipe } from '../shared/format';\nimport { ContextBar, HealthBadge, KeyValueTable } from '../shared/ui';\nimport { problemText } from '../shared/telemetry';\n\n/**\n * One alert (Console requirements §14): evidence, lifecycle history, the subject's health and reasons, the way into\n * the investigation, and acknowledgement for operators.\n */\n@Component({\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [LinkPipe, RouterLink, DatePipe, FormsModule, MatButtonModule, MatFormFieldModule, MatInputModule, MatProgressBarModule,\n ContextBar, HealthBadge, KeyValueTable, AgoPipe, ReasonPipe],\n template: `\n <jc-context-bar>\n <a [routerLink]=\"['alerts'] | jcLink\" [queryParams]=\"ctx.keep()\" class=\"jc-crumb\">Alerts</a><span class=\"jc-crumb-sep\">›</span>\n <h1>{{ detail.value()?.alert?.rule ?? '…' }}</h1>\n </jc-context-bar>\n @if (detail.isLoading()) { <mat-progress-bar mode=\"indeterminate\" /> }\n @if (detail.error()) { <p class=\"jc-error\">{{ error() }}</p> }\n @if (detail.value(); as d) {\n <div class=\"jc-summary\">\n <span class=\"jc-badge\" [class.jc-health-unhealthy]=\"d.alert.severity === 'Critical'\" [class.jc-health-degraded]=\"d.alert.severity === 'Warning'\">{{ d.alert.severity }}</span>\n <strong>{{ d.alert.state }}</strong>\n <span>{{ d.alert.subjectKey }}</span>\n @if (d.alert.noData) { <span class=\"jc-muted\">no data at the last evaluation: state kept</span> }\n @if (d.alert.subjectType === 'service' && d.alert.subjectId) {\n <a mat-flat-button [routerLink]=\"['services', d.alert.subjectId] | jcLink\" [queryParams]=\"investigate(d.alert.environment)\">Investigate</a>\n <a mat-stroked-button [routerLink]=\"['services', d.alert.subjectId] | jcLink\" [queryParams]=\"investigate(d.alert.environment, { tab: 'traces', status: 'error' })\">Error traces</a>\n }\n @for (l of d.links; track l.url) { <a mat-button [href]=\"l.url\" target=\"_blank\" rel=\"noopener\">{{ l.title }} ↗</a> }\n </div>\n <div class=\"jc-grid-2\">\n <section class=\"jc-card\"><h2>Evidence</h2><jc-kv [values]=\"d.evidence\" /></section>\n <section class=\"jc-card\">\n <h2>Subject health</h2>\n @for (h of d.subjectHealth; track h.subjectId + h.environment) {\n <p><jc-health [state]=\"h.state\" [stale]=\"h.stale\" /> {{ h.environment }}</p>\n <ul class=\"jc-reasons\">@for (r of h.reasons; track r) { <li>{{ r | reason }}</li> }</ul>\n } @empty { <p class=\"jc-muted\">No health for this subject.</p> }\n </section>\n </div>\n <section class=\"jc-card\">\n <h2>History</h2>\n <table class=\"jc-table\"><tbody>\n @for (t of d.history; track t.at + t.to) {\n <tr><td>{{ t.at | date: 'MMM d, HH:mm:ss' }}</td><td>{{ t.from ?? '—' }} → <strong>{{ t.to }}</strong></td><td>{{ t.reason | reason }}</td><td>{{ t.value ?? '—' }}</td></tr>\n }\n </tbody></table>\n </section>\n <section class=\"jc-card\">\n <h2>Acknowledgement</h2>\n @if (d.alert.acknowledgedAt) {\n <p>By <strong>{{ d.alert.acknowledgedBy }}</strong> {{ d.alert.acknowledgedAt | ago }}@if (d.acknowledgeComment) {: “{{ d.acknowledgeComment }}”}</p>\n } @else if (d.alert.state !== 'Resolved') {\n <form (ngSubmit)=\"acknowledge()\" class=\"jc-filter-form\">\n <mat-form-field subscriptSizing=\"dynamic\" class=\"jc-field-wide\"><mat-label>Comment</mat-label><input matInput name=\"comment\" [(ngModel)]=\"comment\" maxlength=\"1000\"></mat-form-field>\n <button mat-flat-button type=\"submit\" [disabled]=\"sending()\">Acknowledge</button>\n </form>\n @if (ackError()) { <p class=\"jc-error\">{{ ackError() }}</p> }\n } @else { <p class=\"jc-muted\">Resolved without acknowledgement.</p> }\n </section>\n }`,\n})\nexport class AlertPage {\n protected readonly ctx = inject(ContextService);\n private readonly api = inject(ApiService);\n readonly id = input.required<string>();\n protected comment = '';\n protected readonly sending = signal(false);\n protected readonly ackError = signal('');\n\n protected readonly detail = apiResource({ params: () => this.id(), stream: ({ params }) => this.api.alert(params) });\n protected readonly error = computed(() => problemText(this.detail.error()));\n\n /** The service page over the alert's environment, from shortly before the breach. */\n investigate(environment: string | null, extra: Record<string, string> = {}) {\n const alert = this.detail.value()?.alert;\n const start = alert?.breachStartedAt ? new Date(alert.breachStartedAt).getTime() - 15 * 60_000 : null;\n const end = alert?.resolvedAt ? new Date(alert.resolvedAt).getTime() + 5 * 60_000 : Date.now();\n const window = start && end - start <= 24 * 3_600_000\n ? { from: new Date(start).toISOString(), to: new Date(end).toISOString(), range: null } : {};\n return this.ctx.keep({ env: environment, ...window, ...extra });\n }\n\n acknowledge() {\n this.sending.set(true);\n this.ackError.set('');\n this.api.acknowledge(this.id(), this.comment).subscribe({\n next: () => { this.sending.set(false); this.detail.reload(); },\n error: e => {\n this.sending.set(false);\n this.ackError.set((e as { status?: number }).status === 403 ? 'Acknowledging needs the operator or admin role.' : problemText(e));\n },\n });\n }\n}\n"],"mappings":"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"}
@@ -1,175 +0,0 @@
1
- import { ApiService, ContextService, LinkPipe } from "./juice-js-observability.mjs";
2
- import { S as apiResource, h as AgoPipe, o as problemText, u as ContextBar } from "./juice-js-observability-telemetry-BftWSOaQ.mjs";
3
- import * as i0 from "@angular/core";
4
- import { ChangeDetectionStrategy, Component, computed, inject } from "@angular/core";
5
- import { forkJoin, map } from "rxjs";
6
- import { RouterLink } from "@angular/router";
7
- import * as i1 from "@angular/material/progress-bar";
8
- import { MatProgressBarModule } from "@angular/material/progress-bar";
9
- var RulesPage = class RulesPage {
10
- ctx = inject(ContextService);
11
- api = inject(ApiService);
12
- data = apiResource({ stream: () => forkJoin({
13
- rules: this.api.rules().pipe(map((p) => p.items)),
14
- firing: this.api.allAlerts({ state: "firing" }).pipe(map((p) => p.items)),
15
- pending: this.api.allAlerts({ state: "pending" }).pipe(map((p) => p.items))
16
- }) });
17
- error = computed(() => problemText(this.data.error()), ...ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []);
18
- rows = computed(() => {
19
- const d = this.data.value();
20
- if (!d) return [];
21
- const open = [...d.firing, ...d.pending];
22
- return d.rules.map((r) => {
23
- const mine = open.filter((a) => a.rule === r.key);
24
- const last = mine.map((a) => a.lastEvaluatedAt).sort().at(-1) ?? null;
25
- return {
26
- ...r,
27
- open: mine,
28
- lastEvaluatedAt: last
29
- };
30
- });
31
- }, ...ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []);
32
- static ɵfac = i0.ɵɵngDeclareFactory({
33
- minVersion: "12.0.0",
34
- version: "22.2.4",
35
- ngImport: i0,
36
- type: RulesPage,
37
- deps: [],
38
- target: i0.ɵɵFactoryTarget.Component
39
- });
40
- static ɵcmp = i0.ɵɵngDeclareComponent({
41
- minVersion: "17.0.0",
42
- version: "22.2.4",
43
- type: RulesPage,
44
- isStandalone: true,
45
- selector: "ng-component",
46
- ngImport: i0,
47
- template: `
48
- <jc-context-bar><h1>Rules</h1><span class="jc-muted">From deployment configuration (rules.json); read-only.</span></jc-context-bar>
49
- @if (data.isLoading()) { <mat-progress-bar mode="indeterminate" /> }
50
- @if (data.error()) { <p class="jc-error">{{ error() }}</p> }
51
- <table class="jc-table">
52
- <thead><tr><th>Rule</th><th>Template</th><th>Severity</th><th>Threshold</th><th>Recovery</th><th>Wait</th><th>Open alerts</th><th>Last evaluation</th></tr></thead>
53
- <tbody>
54
- @for (r of rows(); track r.key) {
55
- <tr [class.jc-muted]="!r.enabled">
56
- <td><strong>{{ r.name }}</strong><div class="jc-muted jc-small">{{ r.key }}@if (!r.enabled) { · disabled }</div></td>
57
- <td>{{ r.template }}</td>
58
- <td>{{ r.severity }}</td>
59
- <td>{{ r.definition['threshold'] ?? '—' }}</td>
60
- <td>{{ r.definition['recoveryThreshold'] ?? '—' }}</td>
61
- <td>{{ r.definition['forDuration'] ?? '—' }}</td>
62
- <td>
63
- @for (a of r.open; track a.id) {
64
- <div><a [routerLink]="['alerts', a.id] | jcLink" [queryParams]="ctx.keep()">{{ a.state }}</a> {{ a.subjectKey }} = {{ a.lastValue ?? 'no data' }}</div>
65
- } @empty { <span class="jc-muted">none</span> }
66
- </td>
67
- <td>{{ r.lastEvaluatedAt | ago }}</td>
68
- </tr>
69
- }
70
- </tbody>
71
- </table>`,
72
- isInline: true,
73
- dependencies: [
74
- {
75
- kind: "directive",
76
- type: RouterLink,
77
- selector: "[routerLink]",
78
- inputs: [
79
- "target",
80
- "queryParams",
81
- "fragment",
82
- "queryParamsHandling",
83
- "state",
84
- "info",
85
- "relativeTo",
86
- "preserveFragment",
87
- "skipLocationChange",
88
- "replaceUrl",
89
- "browserUrl",
90
- "routerLink"
91
- ]
92
- },
93
- {
94
- kind: "ngmodule",
95
- type: MatProgressBarModule
96
- },
97
- {
98
- kind: "component",
99
- type: i1.MatProgressBar,
100
- selector: "mat-progress-bar",
101
- inputs: [
102
- "color",
103
- "value",
104
- "bufferValue",
105
- "mode"
106
- ],
107
- outputs: ["animationEnd"],
108
- exportAs: ["matProgressBar"]
109
- },
110
- {
111
- kind: "component",
112
- type: ContextBar,
113
- selector: "jc-context-bar"
114
- },
115
- {
116
- kind: "pipe",
117
- type: LinkPipe,
118
- name: "jcLink"
119
- },
120
- {
121
- kind: "pipe",
122
- type: AgoPipe,
123
- name: "ago"
124
- }
125
- ],
126
- changeDetection: i0.ChangeDetectionStrategy.OnPush
127
- });
128
- };
129
- i0.ɵɵngDeclareClassMetadata({
130
- minVersion: "12.0.0",
131
- version: "22.2.4",
132
- ngImport: i0,
133
- type: RulesPage,
134
- decorators: [{
135
- type: Component,
136
- args: [{
137
- changeDetection: ChangeDetectionStrategy.OnPush,
138
- imports: [
139
- LinkPipe,
140
- RouterLink,
141
- MatProgressBarModule,
142
- ContextBar,
143
- AgoPipe
144
- ],
145
- template: `
146
- <jc-context-bar><h1>Rules</h1><span class="jc-muted">From deployment configuration (rules.json); read-only.</span></jc-context-bar>
147
- @if (data.isLoading()) { <mat-progress-bar mode="indeterminate" /> }
148
- @if (data.error()) { <p class="jc-error">{{ error() }}</p> }
149
- <table class="jc-table">
150
- <thead><tr><th>Rule</th><th>Template</th><th>Severity</th><th>Threshold</th><th>Recovery</th><th>Wait</th><th>Open alerts</th><th>Last evaluation</th></tr></thead>
151
- <tbody>
152
- @for (r of rows(); track r.key) {
153
- <tr [class.jc-muted]="!r.enabled">
154
- <td><strong>{{ r.name }}</strong><div class="jc-muted jc-small">{{ r.key }}@if (!r.enabled) { · disabled }</div></td>
155
- <td>{{ r.template }}</td>
156
- <td>{{ r.severity }}</td>
157
- <td>{{ r.definition['threshold'] ?? '—' }}</td>
158
- <td>{{ r.definition['recoveryThreshold'] ?? '—' }}</td>
159
- <td>{{ r.definition['forDuration'] ?? '—' }}</td>
160
- <td>
161
- @for (a of r.open; track a.id) {
162
- <div><a [routerLink]="['alerts', a.id] | jcLink" [queryParams]="ctx.keep()">{{ a.state }}</a> {{ a.subjectKey }} = {{ a.lastValue ?? 'no data' }}</div>
163
- } @empty { <span class="jc-muted">none</span> }
164
- </td>
165
- <td>{{ r.lastEvaluatedAt | ago }}</td>
166
- </tr>
167
- }
168
- </tbody>
169
- </table>`
170
- }]
171
- }]
172
- });
173
- export { RulesPage };
174
-
175
- //# sourceMappingURL=juice-js-observability-rules.page-Crb07uWm.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"juice-js-observability-rules.page-Crb07uWm.mjs","names":[],"sources":["../../../../projects/juice-js/observability/src/lib/pages/rules.page.ts"],"sourcesContent":["import { LinkPipe } from '../core/options';\nimport { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core';\nimport { apiResource } from '../shared/resource';\nimport { MatProgressBarModule } from '@angular/material/progress-bar';\nimport { RouterLink } from '@angular/router';\nimport { forkJoin, map } from 'rxjs';\nimport { ApiService } from '../core/api.service';\nimport { ContextService } from '../core/context.service';\nimport { AgoPipe } from '../shared/format';\nimport { ContextBar } from '../shared/ui';\nimport { problemText } from '../shared/telemetry';\n\n/**\n * Rules and their current state (Console requirements §14). Rules are typed templates in deployment configuration\n * (D5), so this page reads them; it never edits them and never takes a query.\n */\n@Component({\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [LinkPipe, RouterLink, MatProgressBarModule, ContextBar, AgoPipe],\n template: `\n <jc-context-bar><h1>Rules</h1><span class=\"jc-muted\">From deployment configuration (rules.json); read-only.</span></jc-context-bar>\n @if (data.isLoading()) { <mat-progress-bar mode=\"indeterminate\" /> }\n @if (data.error()) { <p class=\"jc-error\">{{ error() }}</p> }\n <table class=\"jc-table\">\n <thead><tr><th>Rule</th><th>Template</th><th>Severity</th><th>Threshold</th><th>Recovery</th><th>Wait</th><th>Open alerts</th><th>Last evaluation</th></tr></thead>\n <tbody>\n @for (r of rows(); track r.key) {\n <tr [class.jc-muted]=\"!r.enabled\">\n <td><strong>{{ r.name }}</strong><div class=\"jc-muted jc-small\">{{ r.key }}@if (!r.enabled) { · disabled }</div></td>\n <td>{{ r.template }}</td>\n <td>{{ r.severity }}</td>\n <td>{{ r.definition['threshold'] ?? '—' }}</td>\n <td>{{ r.definition['recoveryThreshold'] ?? '—' }}</td>\n <td>{{ r.definition['forDuration'] ?? '—' }}</td>\n <td>\n @for (a of r.open; track a.id) {\n <div><a [routerLink]=\"['alerts', a.id] | jcLink\" [queryParams]=\"ctx.keep()\">{{ a.state }}</a> {{ a.subjectKey }} = {{ a.lastValue ?? 'no data' }}</div>\n } @empty { <span class=\"jc-muted\">none</span> }\n </td>\n <td>{{ r.lastEvaluatedAt | ago }}</td>\n </tr>\n }\n </tbody>\n </table>`,\n})\nexport class RulesPage {\n protected readonly ctx = inject(ContextService);\n private readonly api = inject(ApiService);\n protected readonly data = apiResource({\n stream: () => forkJoin({\n rules: this.api.rules().pipe(map(p => p.items)),\n firing: this.api.allAlerts({ state: 'firing' }).pipe(map(p => p.items)),\n pending: this.api.allAlerts({ state: 'pending' }).pipe(map(p => p.items)),\n }),\n });\n protected readonly error = computed(() => problemText(this.data.error()));\n protected readonly rows = computed(() => {\n const d = this.data.value();\n if (!d) return [];\n const open = [...d.firing, ...d.pending];\n return d.rules.map(r => {\n const mine = open.filter(a => a.rule === r.key);\n const last = mine.map(a => a.lastEvaluatedAt).sort().at(-1) ?? null;\n return { ...r, open: mine, lastEvaluatedAt: last };\n });\n });\n}\n"],"mappings":";;;;;;;;AA6CA,IAAa,YAAb,MAAa,UAAS;CACD,MAAM,OAAO,cAAc;CAC7B,MAAM,OAAO,UAAU;CACrB,OAAO,YAAY,EACpC,cAAc,SAAS;EACrB,OAAO,KAAK,IAAI,MAAK,CAAE,CAAC,KAAK,KAAI,MAAK,EAAE,KAAK,CAAC;EAC9C,QAAQ,KAAK,IAAI,UAAU,EAAE,OAAO,SAAQ,CAAE,CAAC,CAAC,KAAK,KAAI,MAAK,EAAE,KAAK,CAAC;EACtE,SAAS,KAAK,IAAI,UAAU,EAAE,OAAO,UAAS,CAAE,CAAC,CAAC,KAAK,KAAI,MAAK,EAAE,KAAK,CAAC;CACzE,CAAA,EACF,CAAA;CACkB,QAAQ,eAAe,YAAY,KAAK,KAAK,MAAK,CAAE,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CACtD,OAAO,eAAc;EACtC,MAAM,IAAI,KAAK,KAAK,MAAK;EACzB,IAAI,CAAC,GAAG,OAAO,CAAA;EACf,MAAM,OAAO,CAAC,GAAG,EAAE,QAAQ,GAAG,EAAE,OAAO;EACvC,OAAO,EAAE,MAAM,KAAI,MAAI;GACrB,MAAM,OAAO,KAAK,QAAO,MAAK,EAAE,SAAS,EAAE,GAAG;GAC9C,MAAM,OAAO,KAAK,KAAI,MAAK,EAAE,eAAe,CAAC,CAAC,KAAI,CAAE,CAAC,GAAG,EAAE,KAAK;GAC/D,OAAO;IAAE,GAAG;IAAG,MAAM;IAAM,iBAAiB;GAAI;EAClD,CAAC;CACH,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CApBS,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAS,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAT,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAS,cAAA;EAAA,UAAA;EAAA,UAAA;EAAA,UA1BV;;;;;;;;;;;;;;;;;;;;;;;;;EAwBC,UAAA;EAAA,cAAA;GAAA;IAAA,MAAA;IAAA,MAzBS;IAAU,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;GAAoB;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;IAAA;IAAA,SAAA,CAAA,cAAA;IAAA,UAAA,CAAA,gBAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAU,UAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAtD;IAAQ,MAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAgD;IAAO,MAAA;GAAA;EAAA;EAAA,iBAAA,GAAA,wBAAA;CAAA,CAAA;;AA2B9D,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAS,YAAA,CAAA;EA7BrB,MAAA;EAAU,MAAA,CAAA;GACT,iBAAiB,wBAAwB;GACzC,SAAS;IAAC;IAAU;IAAY;IAAsB;IAAY;GAAO;GACzE,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;EAyBX,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"juice-js-observability-service.page-BrcID0Bs.mjs","names":["i1","i3","i2"],"sources":["../../../../projects/juice-js/observability/src/lib/shared/capacity.ts","../../../../projects/juice-js/observability/src/lib/pages/service.page.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, inject, input, linkedSignal, signal } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { MatFormFieldModule } from '@angular/material/form-field';\nimport { MatInputModule } from '@angular/material/input';\nimport { MatProgressBarModule } from '@angular/material/progress-bar';\nimport { RouterLink } from '@angular/router';\nimport { ApiService } from '../core/api.service';\nimport { ContextService } from '../core/context.service';\nimport { LinkPipe } from '../core/options';\nimport { Capacity } from '../core/models';\nimport { apiResource } from './resource';\nimport { problemText } from './telemetry';\n\nexport interface CapacityInputs {\n users: number;\n /** Seconds between one user's actions. */\n thinkSeconds: number;\n /** Requests or messages one user action causes in this service. */\n perAction: number;\n /** The CPU use to stay under, from 0 to 1. */\n targetCpu: number;\n coresPerInstance: number;\n}\n\nexport interface CapacityPlan {\n perSecond: number;\n instances: number;\n cores: number;\n /** Per instance; null without memory data. */\n memoryBytes: number | null;\n /** The load each instance would carry. */\n perInstance: number;\n /** That load over the highest measured: above 1 is beyond what was measured. */\n beyondMeasured: number | null;\n /** What set the number of instances: CPU, or a consumer that was already busy all the time. */\n limitedBy: 'cpu' | 'messages';\n}\n\n/**\n * The calculator: `null` when the inputs or the measurements can't give an answer, such as a target that idle CPU alone\n * already exceeds. Each instance can carry (target × cores − idle) ÷ CPU per unit, unless a consumer was seen busy all the\n * time: more CPU doesn't help one that handles a message at a time, so an instance carries only the rate it reached.\n */\nexport function planCapacity(m: Capacity, i: CapacityInputs): CapacityPlan | null {\n if (!m.cpuSecondsPerUnit || m.cpuSecondsPerUnit <= 0) return null;\n if (!(i.users > 0 && i.perAction > 0 && i.targetCpu > 0 && i.targetCpu <= 1 && i.coresPerInstance > 0 && i.thinkSeconds >= 0)) return null;\n const response = (m.responseMs ?? 0) / 1000;\n if (i.thinkSeconds + response <= 0) return null;\n\n const byCpu = (i.targetCpu * i.coresPerInstance - (m.idleCores ?? 0)) / m.cpuSecondsPerUnit;\n if (byCpu <= 0) return null;\n const limitedBy = consumerBusy(m) && m.peakMessagesPerSecond < byCpu ? 'messages' : 'cpu';\n const carried = limitedBy === 'messages' ? m.peakMessagesPerSecond : byCpu;\n const perSecond = i.users * i.perAction / (i.thinkSeconds + response);\n const instances = Math.max(1, Math.ceil(perSecond / carried));\n const perInstance = perSecond / instances;\n const memoryBytes = m.memoryModel === 'fitted'\n ? m.idleMemoryBytes! + m.memoryBytesPerInFlight! * perInstance * response\n : m.peakMemoryBytes;\n // A consumer held back by message handling never used its cores: count what it used, at the target, not the machine's.\n const coresEach = limitedBy === 'messages' && m.peakCores !== null\n ? Math.min(i.coresPerInstance, Math.ceil(m.peakCores / i.targetCpu))\n : i.coresPerInstance;\n return {\n perSecond, instances, cores: instances * coresEach, memoryBytes, perInstance,\n beyondMeasured: m.peakPerSecond > 0 ? perInstance / m.peakPerSecond : null,\n limitedBy,\n };\n}\n\n/** A consumer counts as busy all the time from this much message processing per second of time. */\nconst BUSY_CONSUMER = 0.9;\nfunction consumerBusy(m: Capacity): boolean { return m.peakMessagesInFlight >= BUSY_CONSUMER && m.peakMessagesPerSecond > 0; }\n\n/** A thread pool queue of a few items comes and goes on a healthy host; from this many, work was really waiting. */\nconst NOTABLE_QUEUE = 10;\n\n/** Response time counts as rising with load from this ratio between the busiest and quietest samples. */\nconst RISING_RESPONSE = 1.5;\n\nfunction bytes(value: number | null): string {\n if (value === null) return '—';\n return value >= 1024 ** 3 ? `${(value / 1024 ** 3).toFixed(1)} GB` : `${Math.round(value / 1024 ** 2)} MB`;\n}\nfunction rate(value: number): string { return value >= 10 ? value.toFixed(0) : value.toFixed(2); }\n\n/**\n * A service's Capacity tab: what it was measured to need in the selected window, and a calculator from users to instances,\n * cores and memory. The estimate says where it comes from and where it stops holding: scaling isn't linear, and the\n * service's own databases and brokers are not in it.\n */\n@Component({\n selector: 'jc-service-capacity',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormsModule, MatFormFieldModule, MatInputModule, MatProgressBarModule, RouterLink, LinkPipe],\n template: `\n @if (capacity.isLoading()) { <mat-progress-bar mode=\"indeterminate\" /> }\n @if (capacity.error()) { <p class=\"jc-error\">{{ error() }}</p> }\n @if (mixedEnvironments()) {\n <p class=\"jc-notice\">This service runs in more than one environment, and their hardware may differ. Choose an environment for an estimate that means something.</p>\n }\n @if (capacity.value(); as m) {\n @if (m.samples === 0) {\n <p class=\"jc-muted\">No telemetry from this service in this window.</p>\n } @else {\n <section class=\"jc-card\" aria-labelledby=\"jc-capacity-estimate\">\n <h3 id=\"jc-capacity-estimate\">Estimate</h3>\n <div class=\"jc-capacity-inputs\">\n <mat-form-field subscriptSizing=\"dynamic\"><mat-label>Concurrent users</mat-label>\n <input matInput type=\"number\" min=\"1\" name=\"users\" [ngModel]=\"users()\" (ngModelChange)=\"users.set($event)\" /></mat-form-field>\n <mat-form-field subscriptSizing=\"dynamic\"><mat-label>Seconds between actions</mat-label>\n <input matInput type=\"number\" min=\"0\" name=\"thinkSeconds\" [ngModel]=\"thinkSeconds()\" (ngModelChange)=\"thinkSeconds.set($event)\" /></mat-form-field>\n <mat-form-field subscriptSizing=\"dynamic\"><mat-label>Requests per action</mat-label>\n <input matInput type=\"number\" min=\"0\" name=\"perAction\" [ngModel]=\"perAction()\" (ngModelChange)=\"perAction.set($event)\" /></mat-form-field>\n <mat-form-field subscriptSizing=\"dynamic\"><mat-label>Target CPU use (%)</mat-label>\n <input matInput type=\"number\" min=\"1\" max=\"100\" name=\"targetCpu\" [ngModel]=\"targetCpu()\" (ngModelChange)=\"targetCpu.set($event)\" /></mat-form-field>\n <mat-form-field subscriptSizing=\"dynamic\"><mat-label>Cores per instance</mat-label>\n <input matInput type=\"number\" min=\"1\" name=\"coresPerInstance\" [ngModel]=\"coresPerInstance()\" (ngModelChange)=\"coresPerInstance.set($event)\" /></mat-form-field>\n </div>\n @if (plan(); as p) {\n <div class=\"jc-capacity-result\" data-testid=\"capacity-plan\">\n <div><strong>{{ p.instances }}</strong><span class=\"jc-muted\">instance{{ p.instances === 1 ? '' : 's' }}</span></div>\n <div><strong>{{ p.cores }}</strong><span class=\"jc-muted\">cores in total</span></div>\n <div><strong>{{ memory(p.memoryBytes) }}</strong><span class=\"jc-muted\">memory per instance{{ m.memoryModel === 'peak' ? ' (highest seen)' : '' }}</span></div>\n <div><strong>{{ perSecond(p.perSecond) }}</strong><span class=\"jc-muted\">requests and messages/s</span></div>\n </div>\n @if (p.beyondMeasured !== null && p.beyondMeasured > 1) {\n <p class=\"jc-notice\" data-testid=\"capacity-beyond\">Each instance would carry {{ perSecond(p.perInstance) }}/s, {{ p.beyondMeasured.toFixed(1) }}× the highest measured ({{ perSecond(m.peakPerSecond) }}/s). This is an extrapolation, and only a load test can confirm it.</p>\n }\n } @else if (m.cpuModel === 'none') {\n <p class=\"jc-notice\">No CPU data, so no estimate. CPU needs two readings at least a minute apart: choose a longer window, or wait. If it stays empty, add <code>AddMeter(\"System.Runtime\")</code> to the host's metrics (.NET 9+).</p>\n } @else {\n <p class=\"jc-notice\">No estimate for these values. Check them: idle CPU alone may already exceed the target.</p>\n }\n <ul class=\"jc-capacity-notes\">\n @if (responseRising(); as r) {\n <li class=\"jc-bad\">Response time already rises with load: {{ r.low }} ms when quiet, {{ r.high }} ms when busy. Something may be near its limit, so the real capacity may be lower.</li>\n }\n @if (busyConsumer()) {\n <li class=\"jc-bad\" data-testid=\"capacity-busy-consumer\">Message processing was busy nearly all the time, at about {{ perSecond(m.peakMessagesPerSecond) }} messages/s per instance. If it handles one message at a time, that is its limit whatever CPU says, and its queue may be growing. The estimate uses that rate, but a saturated consumer's rate can fall, so it may not hold. See which step takes the time in a trace.</li>\n }\n @if (m.outbox?.fallingBehind) {\n <li class=\"jc-bad\" data-testid=\"capacity-outbox-behind\">Outbox delivery is falling behind: {{ count(m.outbox!.undelivered) }} messages created in this window weren't delivered. CPU and requests can look healthy while this grows, and the estimate doesn't include it. If another service delivers this outbox, this doesn't apply.</li>\n }\n @if (m.peakQueue !== null && m.peakQueue >= notableQueue) {\n <li class=\"jc-bad\">The thread pool queue reached {{ m.peakQueue }}: work was waiting for a thread.</li>\n }\n @if (m.cpuModel === 'average') {\n <li data-testid=\"capacity-load-hint\">Load didn't vary enough here to separate idle CPU from the cost of each request, so this is rough. A <a [routerLink]=\"['load-runs'] | jcLink\" [queryParams]=\"ctx.keep()\">load test</a> gives a better estimate.</li>\n }\n <li>Assumes cost grows in a straight line with load. A database, lock or connection limit can be reached long before CPU runs out.</li>\n <li>Covers this service only, not its databases, brokers or the services it calls.</li>\n </ul>\n </section>\n\n <section class=\"jc-card\" aria-labelledby=\"jc-capacity-measured\">\n <h3 id=\"jc-capacity-measured\">Measured in this window</h3>\n <table class=\"jc-table jc-capacity-measured\">\n <tbody>\n <tr><th>CPU per request or message</th>\n <td>\n @if (m.cpuSecondsPerUnit !== null) { {{ (m.cpuSecondsPerUnit * 1000).toFixed(1) }} ms }\n @else { <span class=\"jc-muted\">No CPU data yet</span> }\n @if (m.cpuModel === 'average') {\n <div class=\"jc-muted jc-small\">Measured in the busiest samples and includes idle CPU, so it can read high. Load didn't vary enough, or CPU didn't follow it in a straight line, to separate the two.</div>\n }\n </td></tr>\n <tr><th>Idle CPU</th><td>@if (m.idleCores !== null) { {{ m.idleCores.toFixed(2) }} cores } @else { <span class=\"jc-muted\">Not separated</span> }</td></tr>\n <tr><th>Idle memory</th><td>@if (m.idleMemoryBytes !== null) { {{ memory(m.idleMemoryBytes) }} } @else { <span class=\"jc-muted\">Not separated</span> }</td></tr>\n <tr><th>Memory growth under load</th><td>@if (m.memoryBytesPerInFlight !== null) { {{ memory(m.memoryBytesPerInFlight) }} per request in flight } @else { <span class=\"jc-muted\">Not separated</span> }</td></tr>\n <tr><th>Highest load, one instance</th><td>{{ perSecond(m.peakPerSecond) }} requests and messages/s</td></tr>\n <tr><th>Highest use, one instance</th><td>\n {{ m.peakCores !== null ? m.peakCores.toFixed(2) + ' cores' : 'CPU unknown' }} · {{ m.peakMemoryBytes !== null ? memory(m.peakMemoryBytes) : 'memory unknown' }}</td></tr>\n <tr><th>Average response</th><td>{{ m.responseMs !== null ? m.responseMs.toFixed(0) + ' ms' : '—' }}</td></tr>\n <tr><th>Measured on</th><td>\n {{ m.instances }} instance{{ m.instances === 1 ? '' : 's' }}@if (m.coresPerInstance) {, {{ m.coresPerInstance }} cores each}@if (m.hosts.length) {, on {{ m.hosts.join(', ') }}}.\n <div class=\"jc-muted jc-small\">Figures from other hardware carry over only roughly.</div></td></tr>\n <tr><th>Based on</th><td>\n {{ m.samples }} samples of {{ m.stepSeconds }} s.\n <div class=\"jc-muted jc-small\">{{ sourceNote() }}</div>\n @if (m.truncated) { <div class=\"jc-bad jc-small\">Too many samples in this window, so only the newest were used. Choose a shorter one.</div> }</td></tr>\n </tbody>\n </table>\n </section>\n\n @if (m.outbox; as o) {\n <section class=\"jc-card\" aria-labelledby=\"jc-capacity-outbox\" data-testid=\"capacity-outbox\">\n <h3 id=\"jc-capacity-outbox\">Outbox delivery</h3>\n <table class=\"jc-table jc-capacity-measured\">\n <tbody>\n <tr><th>Messages created</th><td>{{ perSecond(o.createdPerSecond) }}/s</td></tr>\n <tr><th>Messages delivered</th><td>{{ perSecond(o.deliveredPerSecond) }}/s</td></tr>\n <tr><th>Created but not delivered</th><td [class.jc-bad]=\"o.fallingBehind\">{{ count(o.undelivered) }} in this window\n <div class=\"jc-muted jc-small\">Includes messages of rolled-back transactions, which are never delivered.</div></td></tr>\n <tr><th>Time to delivery, p95</th><td [class.jc-bad]=\"o.fallingBehind\">@if (o.recentDelaySeconds !== null) { {{ o.recentDelaySeconds.toFixed(1) }} s in the last 5 minutes } @else { <span class=\"jc-muted\">Nothing delivered in the last 5 minutes</span> }</td></tr>\n </tbody>\n </table>\n </section>\n }\n }\n }`,\n})\nexport class ServiceCapacity {\n protected readonly ctx = inject(ContextService);\n private readonly api = inject(ApiService);\n readonly serviceId = input.required<string>();\n /** The environments the service runs in: figures across several mix their hardware. */\n readonly environments = input<string[]>([]);\n\n protected readonly capacity = apiResource({\n params: () => ({ id: this.serviceId(), env: this.ctx.environment(), range: this.ctx.range(), abs: this.ctx.absolute() }),\n stream: ({ params }) => this.api.capacity({ serviceId: params.id, environment: params.env, ...this.ctx.window() }),\n });\n protected readonly users = signal(1000);\n protected readonly thinkSeconds = signal(10);\n protected readonly perAction = signal(1);\n protected readonly targetCpu = signal(60);\n protected readonly coresPerInstance = linkedSignal(() => this.capacity.value()?.coresPerInstance ?? 2);\n\n protected readonly error = computed(() => problemText(this.capacity.error()));\n protected readonly mixedEnvironments = computed(() => !this.ctx.environment() && new Set(this.environments()).size > 1);\n protected readonly plan = computed(() => {\n const m = this.capacity.value();\n return m ? planCapacity(m, {\n users: this.users(), thinkSeconds: this.thinkSeconds(), perAction: this.perAction(), targetCpu: this.targetCpu() / 100, coresPerInstance: this.coresPerInstance(),\n }) : null;\n });\n protected readonly busyConsumer = computed(() => { const m = this.capacity.value(); return !!m && consumerBusy(m); });\n protected readonly responseRising = computed(() => {\n const m = this.capacity.value();\n return m?.responseLowLoadMs && m.responseHighLoadMs && m.responseHighLoadMs >= RISING_RESPONSE * m.responseLowLoadMs\n ? { low: Math.round(m.responseLowLoadMs), high: Math.round(m.responseHighLoadMs) } : null;\n });\n protected readonly sourceNote = computed(() => this.capacity.value()?.requestSource === 'metrics'\n ? 'Requests are counted from the http.server.request.duration metric, messages from spans.'\n : 'Requests and messages are counted from spans, which undercount if traces are sampled. That makes the cost per request read high. Exporting http.server.request.duration fixes the request count.');\n\n protected readonly notableQueue = NOTABLE_QUEUE;\n protected readonly memory = bytes;\n protected readonly perSecond = rate;\n protected readonly count = (value: number) => Math.round(value).toLocaleString('en-US');\n}\n","import { LinkPipe, ObservabilityLinks } from '../core/options';\nimport { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';\nimport { apiResource } from '../shared/resource';\nimport { MatProgressBarModule } from '@angular/material/progress-bar';\nimport { MatTabsModule } from '@angular/material/tabs';\nimport { MatButtonModule } from '@angular/material/button';\nimport { Router, RouterLink } from '@angular/router';\nimport { ApiService } from '../core/api.service';\nimport { ContextService } from '../core/context.service';\nimport { AgoPipe, DurationPipe, PercentPipe, ReasonPipe } from '../shared/format';\nimport { ConfidenceBadge, ContextBar, HealthBadge } from '../shared/ui';\nimport { LogTable, MetricExplorer, ServiceStatsCharts, TraceTable, problemText } from '../shared/telemetry';\nimport { ServiceCapacity } from '../shared/capacity';\n\nconst TABS = ['overview', 'operations', 'dependencies', 'traces', 'logs', 'metrics', 'capacity'] as const;\n\n/**\n * One service across its environments (Console requirements §8): health with reasons, instances, operations,\n * dependencies, traces, logs and metrics, without leaving the service. The tab and the selected operation are in the\n * URL, so the investigation can be shared and the back button returns to it.\n */\n@Component({\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [LinkPipe, RouterLink, MatTabsModule, MatProgressBarModule, MatButtonModule, ContextBar, HealthBadge, ConfidenceBadge,\n TraceTable, LogTable, MetricExplorer, ServiceStatsCharts, ServiceCapacity, AgoPipe, DurationPipe, PercentPipe, ReasonPipe],\n template: `\n <jc-context-bar>\n <a [routerLink]=\"['services'] | jcLink\" [queryParams]=\"ctx.keep()\" class=\"jc-crumb\">Services</a>\n <span class=\"jc-crumb-sep\">›</span>\n <h1>{{ service.value()?.key ?? '…' }}</h1>\n @for (l of service.value()?.links ?? []; track l.url) { <a mat-button [href]=\"l.url\" target=\"_blank\" rel=\"noopener\">{{ l.title }} ↗</a> }\n </jc-context-bar>\n @if (service.isLoading()) { <mat-progress-bar mode=\"indeterminate\" /> }\n @if (service.error()) { <p class=\"jc-error\">{{ error() }}</p> }\n\n @if (service.value(); as s) {\n <div class=\"jc-health-strip\">\n @for (h of health(); track h.environment) {\n <div class=\"jc-card jc-health-card\" [class.jc-selected]=\"h.environment === ctx.environment()\">\n <div class=\"jc-row\"><strong>{{ h.environment ?? 'no environment' }}</strong><jc-health [state]=\"h.state\" [stale]=\"h.stale\" /></div>\n <ul class=\"jc-reasons\">\n @for (r of h.reasons; track r) {\n <li>\n @if (dependencyOf(r); as dep) {\n {{ r | reason }} — <a [routerLink]=\"['services', dep.id] | jcLink\" [queryParams]=\"ctx.keep()\">open</a>\n } @else { {{ r | reason }} }\n </li>\n }\n </ul>\n <div class=\"jc-muted jc-small\">since {{ h.since | ago }} · evaluated {{ h.evaluatedAt | ago }}</div>\n @if (h.environment && h.environment !== ctx.environment()) {\n <button mat-button (click)=\"ctx.set({ env: h.environment })\">Investigate {{ h.environment }}</button>\n }\n </div>\n } @empty { <p class=\"jc-muted\">No health evaluated yet: no data.</p> }\n </div>\n\n <mat-tab-group [selectedIndex]=\"tabIndex()\" (selectedIndexChange)=\"openTab($event)\" animationDuration=\"0ms\" mat-stretch-tabs=\"false\">\n <mat-tab label=\"Overview\">\n @if (tabIndex() === 0) {\n <jc-service-stats [query]=\"{ serviceId: id() }\" />\n <h3>Instances</h3>\n <table class=\"jc-table\">\n <thead><tr><th>Instance</th><th>Environment</th><th>Health</th><th>Version</th><th>State</th><th>Last seen</th><th></th></tr></thead>\n <tbody>\n @for (i of instances(); track i.id) {\n <tr>\n <td>{{ i.instanceKey }}@if (i.keySource !== 'Observed') { <span class=\"jc-muted jc-small\" title=\"No service.instance.id: derived from host and process\"> (derived)</span> }</td>\n <td>{{ i.environment }}</td>\n <td><jc-health [state]=\"i.health\" /></td>\n <td>{{ i.serviceVersion ?? '—' }}</td>\n <td>{{ i.state }}</td>\n <td>{{ i.lastSeen | ago }}</td>\n <td class=\"jc-nowrap\">\n <a [routerLink]=\"[]\" [queryParams]=\"ctx.keep({ tab: 'traces', instance: i.id })\">traces</a> ·\n <a [routerLink]=\"[]\" [queryParams]=\"ctx.keep({ tab: 'logs', instance: i.id })\">logs</a>\n @if (i.hostResourceId) { · <a [routerLink]=\"['resources', i.hostResourceId] | jcLink\" [queryParams]=\"ctx.keep()\">host</a> }\n </td>\n </tr>\n } @empty { <tr><td colspan=\"7\" class=\"jc-muted\">No instances.</td></tr> }\n </tbody>\n </table>\n }\n </mat-tab>\n\n <mat-tab label=\"Operations\">\n @if (tabIndex() === 1) {\n @if (operations.isLoading()) { <mat-progress-bar mode=\"indeterminate\" /> }\n <table class=\"jc-table\">\n <thead><tr><th>Operation</th><th>Kind</th><th>Requests</th><th title=\"Average over the selected time range. The chart shows each moment, so a burst reads higher there.\">Avg/s</th><th>Errors</th><th>p50</th><th>p95</th><th>p99</th><th>Last seen</th><th></th></tr></thead>\n <tbody>\n @for (o of operations.value()?.items ?? []; track o.name + o.kind) {\n <tr [class.jc-selected]=\"o.name === operation()\">\n <td><a [routerLink]=\"[]\" [queryParams]=\"ctx.keep({ tab: 'operations', op: o.name })\">{{ o.name }}</a></td>\n <td>{{ o.kind }}</td>\n <td>{{ o.count }}</td>\n <td>{{ o.requestsPerSecond.toFixed(2) }}</td>\n <td [class.jc-bad]=\"o.errorRate >= 0.05\">{{ o.errorRate | percent2 }}</td>\n <td>{{ o.p50Ms | duration }}</td><td>{{ o.p95Ms | duration }}</td><td>{{ o.p99Ms | duration }}</td>\n <td>{{ o.lastSeen | ago }}</td>\n <td class=\"jc-nowrap\">\n <a [routerLink]=\"[]\" [queryParams]=\"ctx.keep({ tab: 'traces', op: o.name, status: 'error' })\">error traces</a> ·\n <a [routerLink]=\"[]\" [queryParams]=\"ctx.keep({ tab: 'traces', op: o.name, slow: o.p95Ms })\">slow traces</a>\n </td>\n </tr>\n } @empty { <tr><td colspan=\"10\" class=\"jc-muted\">No operations in this window.</td></tr> }\n </tbody>\n </table>\n @if (operation(); as op) {\n <h3>{{ op }}</h3>\n <jc-service-stats [query]=\"{ serviceId: id(), operation: op }\" />\n }\n }\n </mat-tab>\n\n <mat-tab label=\"Dependencies\">\n @if (tabIndex() === 2) {\n <h3>Calls and uses</h3>\n <table class=\"jc-table\">\n <thead><tr><th>Target</th><th>Relation</th><th>Evidence</th><th>Spans seen</th><th>Last seen</th><th></th></tr></thead>\n <tbody>\n @for (d of downstream(); track d.edge.id) {\n <tr>\n <td><a [routerLink]=\"d.link\" [queryParams]=\"ctx.keep()\">{{ d.name }}</a>&nbsp;<span class=\"jc-muted jc-small\">{{ d.type }}</span></td>\n <td>{{ d.edge.relation }}</td>\n <td><jc-confidence [confidence]=\"d.edge.confidence\" /></td>\n <td>{{ d.edge.spanCount }}</td>\n <td>{{ d.edge.lastSeen | ago }}</td>\n <td><a [routerLink]=\"['topology'] | jcLink\" [queryParams]=\"ctx.keep({ edge: d.edge.id })\">evidence</a> ·\n <a [routerLink]=\"[]\" [queryParams]=\"ctx.keep({ tab: 'traces', dependency: d.edge.id })\">traces</a></td>\n </tr>\n } @empty { <tr><td colspan=\"6\" class=\"jc-muted\">No downstream dependencies seen.</td></tr> }\n </tbody>\n </table>\n <h3>Called by</h3>\n <table class=\"jc-table\">\n <tbody>\n @for (d of upstream(); track d.edge.id) {\n <tr><td><a [routerLink]=\"d.link\" [queryParams]=\"ctx.keep()\">{{ d.name }}</a></td><td>{{ d.edge.relation }}</td>\n <td><jc-confidence [confidence]=\"d.edge.confidence\" /></td><td>{{ d.edge.lastSeen | ago }}</td>\n <td><a [routerLink]=\"['topology'] | jcLink\" [queryParams]=\"ctx.keep({ edge: d.edge.id })\">evidence</a></td></tr>\n } @empty { <tr><td class=\"jc-muted\">No callers seen.</td></tr> }\n </tbody>\n </table>\n }\n </mat-tab>\n\n <mat-tab label=\"Traces\">\n @if (tabIndex() === 3) {\n <div class=\"jc-filters\">\n @for (f of traceFilters(); track f.label) { <span class=\"jc-chip\">{{ f.label }} <a [routerLink]=\"[]\" [queryParams]=\"f.clear\" queryParamsHandling=\"merge\" [attr.aria-label]=\"'Remove ' + f.label\">×</a></span> }\n </div>\n <jc-trace-table [query]=\"traceQuery()\" />\n }\n </mat-tab>\n\n <mat-tab label=\"Logs\">\n @if (tabIndex() === 4) { <jc-log-table [query]=\"{ serviceId: id(), instanceId: instanceFilter() }\" /> }\n </mat-tab>\n\n <mat-tab label=\"Metrics\">\n @if (tabIndex() === 5) { <jc-metric-explorer [scope]=\"{ serviceId: id(), instanceId: instanceFilter() }\" /> }\n </mat-tab>\n\n <mat-tab label=\"Capacity\">\n @if (tabIndex() === 6) { <jc-service-capacity [serviceId]=\"id()\" [environments]=\"environments()\" /> }\n </mat-tab>\n </mat-tab-group>\n }`,\n})\nexport class ServicePage {\n protected readonly ctx = inject(ContextService);\n private readonly api = inject(ApiService);\n private readonly router = inject(Router);\n private readonly links = inject(ObservabilityLinks);\n\n readonly id = input.required<string>();\n readonly tab = input<string>();\n readonly op = input<string>();\n readonly status = input<string>();\n readonly slow = input<string>();\n readonly instance = input<string>();\n readonly dependency = input<string>();\n\n protected readonly tabIndex = computed(() => Math.max(0, TABS.indexOf((this.tab() ?? 'overview') as typeof TABS[number])));\n protected readonly operation = computed(() => this.op() ?? null);\n protected readonly instanceFilter = computed(() => this.instance() ?? null);\n\n protected readonly service = apiResource({ params: () => this.id(), stream: ({ params }) => this.api.resource(params) });\n protected readonly topology = apiResource({ params: () => this.ctx.environment(), stream: ({ params }) => this.api.topology(params || null) });\n protected readonly operations = apiResource({\n params: () => ({ id: this.id(), env: this.ctx.environment(), range: this.ctx.range(), abs: this.ctx.absolute(), on: this.tabIndex() === 1 }),\n stream: ({ params }) => this.api.operations({ serviceId: params.id, environment: params.env, ...this.ctx.window(), limit: 200 }),\n });\n\n protected readonly error = computed(() => problemText(this.service.error()));\n protected readonly health = computed(() => (this.service.value()?.health ?? []).filter(h => h.subjectType === 'service'));\n protected readonly instances = computed(() => {\n const env = this.ctx.environment();\n return (this.service.value()?.instances ?? []).filter(i => !env || i.environment === env);\n });\n\n protected readonly environments = computed(() => (this.service.value()?.instances ?? []).map(i => i.environment));\n\n private readonly nodes = computed(() => new Map((this.topology.value()?.nodes ?? []).map(n => [n.id, n])));\n private edgesWhere(match: 'source' | 'target') {\n const other = match === 'source' ? 'target' : 'source';\n return (this.topology.value()?.edges ?? []).filter(e => e[match] === this.id()).map(edge => {\n const node = this.nodes().get(edge[other]);\n return {\n edge, name: node?.displayName ?? edge[other], type: node?.type ?? '',\n link: this.links.to(node?.type === 'service' ? 'services' : 'resources', edge[other]),\n };\n });\n }\n protected readonly downstream = computed(() => this.edgesWhere('source'));\n protected readonly upstream = computed(() => this.edgesWhere('target'));\n\n /** `dependency_failed:shop/payments` links to that service when it is in the topology. */\n dependencyOf(reason: string) {\n const name = reason.match(/^(?:critical_)?dependency_(?:failed|unknown):(.+)$/)?.[1];\n if (!name) return null;\n const node = [...this.nodes().values()].find(n => n.type === 'service' && (n.displayName === name || name.endsWith(`/${n.displayName}`)));\n return node ? { id: node.id } : null;\n }\n\n protected readonly traceQuery = computed(() => ({\n serviceId: this.id(), operation: this.op(), status: this.status(), minDurationMs: this.slow(),\n instanceId: this.instance(), dependencyId: this.dependency(),\n }));\n /** The trace filters in effect, each removable on its own. */\n protected readonly traceFilters = computed(() => {\n const filters: { label: string; clear: Record<string, null> }[] = [];\n if (this.op()) filters.push({ label: `operation: ${this.op()}`, clear: { op: null } });\n if (this.status()) filters.push({ label: `status: ${this.status()}`, clear: { status: null } });\n if (this.slow()) filters.push({ label: `slower than ${Math.round(Number(this.slow()))} ms`, clear: { slow: null } });\n if (this.instance()) filters.push({ label: 'one instance', clear: { instance: null } });\n if (this.dependency()) filters.push({ label: 'through one dependency', clear: { dependency: null } });\n return filters;\n });\n\n openTab(index: number) {\n void this.router.navigate([], { queryParams: { tab: TABS[index] }, queryParamsHandling: 'merge' });\n }\n}\n"],"mappings":"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"}