@juice-js/observability 1.0.0-alpha.1 → 1.0.0-alpha.6
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.
- package/README.md +30 -1
- package/fesm2022/{juice-js-observability-alert.page-D2OL0zTT.mjs → juice-js-observability-alert.page-BHKAUvR-.mjs} +3 -4
- package/fesm2022/juice-js-observability-alert.page-BHKAUvR-.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-alerts.page-BLJvD_op.mjs → juice-js-observability-alerts.page-C2tXzLQl.mjs} +2 -2
- package/fesm2022/{juice-js-observability-alerts.page-BLJvD_op.mjs.map → juice-js-observability-alerts.page-C2tXzLQl.mjs.map} +1 -1
- package/fesm2022/{juice-js-observability-logs.page-BWFLleyX.mjs → juice-js-observability-logs.page--j6W1Ej-.mjs} +3 -4
- package/fesm2022/juice-js-observability-logs.page--j6W1Ej-.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-metrics.page-D1caPbQq.mjs → juice-js-observability-metrics.page-DR6qZtfl.mjs} +4 -5
- package/fesm2022/juice-js-observability-metrics.page-DR6qZtfl.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-overview.page-UBwHjlyq.mjs → juice-js-observability-overview.page-DU0GxSXQ.mjs} +43 -13
- package/fesm2022/juice-js-observability-overview.page-DU0GxSXQ.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-resource.page-BZwLK_vU.mjs → juice-js-observability-resource.page-D6qpvo_f.mjs} +4 -5
- package/fesm2022/juice-js-observability-resource.page-D6qpvo_f.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-resources.page-C0xWOb5I.mjs → juice-js-observability-resources.page-CR7-DFsh.mjs} +3 -4
- package/fesm2022/juice-js-observability-resources.page-CR7-DFsh.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-rules.page-Db_U5rRu.mjs → juice-js-observability-rules.page-fq-wO5I_.mjs} +3 -4
- package/fesm2022/juice-js-observability-rules.page-fq-wO5I_.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-service.page-B-SWJ9I5.mjs → juice-js-observability-service.page-Qc6FYChB.mjs} +5 -6
- package/fesm2022/juice-js-observability-service.page-Qc6FYChB.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-services.page-BmWgFnC8.mjs → juice-js-observability-services.page-CX6S7sUu.mjs} +3 -4
- package/fesm2022/juice-js-observability-services.page-CX6S7sUu.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-telemetry-VA4_3nzr.mjs → juice-js-observability-telemetry-D9EYxkdr.mjs} +51 -16
- package/fesm2022/juice-js-observability-telemetry-D9EYxkdr.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-topology.page-DEUNp10U.mjs → juice-js-observability-topology.page-CLz7J5pq.mjs} +94 -69
- package/fesm2022/juice-js-observability-topology.page-CLz7J5pq.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-trace.page-gBhMG2eQ.mjs → juice-js-observability-trace.page-DccvH1SV.mjs} +5 -17
- package/fesm2022/juice-js-observability-trace.page-DccvH1SV.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-traces.page-BCQg4V4o.mjs → juice-js-observability-traces.page-B1vYmz_K.mjs} +7 -8
- package/fesm2022/juice-js-observability-traces.page-B1vYmz_K.mjs.map +1 -0
- package/fesm2022/juice-js-observability.mjs +13 -13
- package/package.json +1 -1
- package/styles/_observability.scss +216 -129
- package/fesm2022/juice-js-observability-alert.page-D2OL0zTT.mjs.map +0 -1
- package/fesm2022/juice-js-observability-logs.page-BWFLleyX.mjs.map +0 -1
- package/fesm2022/juice-js-observability-metrics.page-D1caPbQq.mjs.map +0 -1
- package/fesm2022/juice-js-observability-overview.page-UBwHjlyq.mjs.map +0 -1
- package/fesm2022/juice-js-observability-resource.page-BZwLK_vU.mjs.map +0 -1
- package/fesm2022/juice-js-observability-resources.page-C0xWOb5I.mjs.map +0 -1
- package/fesm2022/juice-js-observability-rules.page-Db_U5rRu.mjs.map +0 -1
- package/fesm2022/juice-js-observability-service.page-B-SWJ9I5.mjs.map +0 -1
- package/fesm2022/juice-js-observability-services.page-BmWgFnC8.mjs.map +0 -1
- package/fesm2022/juice-js-observability-telemetry-VA4_3nzr.mjs.map +0 -1
- package/fesm2022/juice-js-observability-topology.page-DEUNp10U.mjs.map +0 -1
- package/fesm2022/juice-js-observability-trace.page-gBhMG2eQ.mjs.map +0 -1
- package/fesm2022/juice-js-observability-traces.page-BCQg4V4o.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -21,10 +21,39 @@ MenuModule.forRoot([...yourMenus, ...observabilityMenus('/observability', 100)])
|
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
```scss
|
|
24
|
-
// styles.scss
|
|
24
|
+
// styles.scss (the app's global styles, once): the pages' layout, in the Juice Console's teal theme, light and dark (.dark).
|
|
25
25
|
@use '@juice-js/observability';
|
|
26
|
+
|
|
27
|
+
// Optional: colours from your own Angular Material (M2) themes instead.
|
|
28
|
+
:root { @include observability.colors($light-theme); }
|
|
29
|
+
.dark { @include observability.colors($dark-theme); } // under your dark-mode selector
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The default colours sit under zero-specificity `:where()` selectors, so your `colors()` replaces them wherever it
|
|
33
|
+
comes in the file. `colors($theme, $status: (), $series: null)` sets the `--jc-*` custom properties the pages, charts
|
|
34
|
+
and dependency graph read:
|
|
35
|
+
|
|
36
|
+
| From your theme | Used for |
|
|
37
|
+
|---|---|
|
|
38
|
+
| background, card, divider, text, secondary text | page, cards and tables, borders, text, labels |
|
|
39
|
+
| primary | links, selection, the selected graph node or edge |
|
|
40
|
+
| warn | unhealthy and critical, errors |
|
|
41
|
+
|
|
42
|
+
A Material theme has no colour for healthy, degraded or offline, nor a categorical palette, so they have defaults
|
|
43
|
+
(Material green, deep orange and greys; ten Material hues for services in a trace and lines in a chart). Set your own:
|
|
44
|
+
|
|
45
|
+
```scss
|
|
46
|
+
:root {
|
|
47
|
+
@include observability.colors($light-theme,
|
|
48
|
+
$status: (ok: #1b5e20, warn: #e65100, bad: #c62828, crit: #8e0000, off: #546e7a, none: #90a4ae),
|
|
49
|
+
$series: (#1565c0, #6a1b9a, #00838f, #ef6c00));
|
|
50
|
+
}
|
|
26
51
|
```
|
|
27
52
|
|
|
53
|
+
The pages use the properties but don't style the page itself; give `body` your colours, for example
|
|
54
|
+
`background: var(--jc-bg); color: var(--jc-text);`. A theme switch at runtime (a class change on `<html>` or `<body>`,
|
|
55
|
+
like juice-layout's dark-mode toggle) repaints the charts and the graph.
|
|
56
|
+
|
|
28
57
|
- `apiBase`: the API origin; empty when the API serves the app.
|
|
29
58
|
- `basePath`: where the routes are mounted; every link between pages goes under it.
|
|
30
59
|
- The router needs `bindToComponentInputs: true`. The pages take their route and query parameters as inputs.
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { ApiService, ContextService, LinkPipe } from "./juice-js-observability.mjs";
|
|
2
|
-
import { a as problemText, d as KeyValueTable,
|
|
2
|
+
import { _ as ReasonPipe, a as problemText, d as KeyValueTable, l as ContextBar, m as AgoPipe, u as HealthBadge, x as apiResource } from "./juice-js-observability-telemetry-D9EYxkdr.mjs";
|
|
3
3
|
import * as i0 from "@angular/core";
|
|
4
4
|
import { ChangeDetectionStrategy, Component, computed, inject, input, signal } from "@angular/core";
|
|
5
|
-
import { rxResource } from "@angular/core/rxjs-interop";
|
|
6
5
|
import { RouterLink } from "@angular/router";
|
|
7
6
|
import { DatePipe } from "@angular/common";
|
|
8
7
|
import * as i1 from "@angular/forms";
|
|
@@ -22,7 +21,7 @@ var AlertPage = class AlertPage {
|
|
|
22
21
|
comment = "";
|
|
23
22
|
sending = signal(false, ...ngDevMode ? [{ debugName: "sending" }] : /* istanbul ignore next */ []);
|
|
24
23
|
ackError = signal("", ...ngDevMode ? [{ debugName: "ackError" }] : /* istanbul ignore next */ []);
|
|
25
|
-
detail =
|
|
24
|
+
detail = apiResource({
|
|
26
25
|
params: () => this.id(),
|
|
27
26
|
stream: ({ params }) => this.api.alert(params)
|
|
28
27
|
});
|
|
@@ -402,4 +401,4 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
402
401
|
});
|
|
403
402
|
export { AlertPage };
|
|
404
403
|
|
|
405
|
-
//# sourceMappingURL=juice-js-observability-alert.page-
|
|
404
|
+
//# sourceMappingURL=juice-js-observability-alert.page-BHKAUvR-.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"juice-js-observability-alert.page-BHKAUvR-.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,5 +1,5 @@
|
|
|
1
1
|
import { ApiService, ContextService, LinkPipe } from "./juice-js-observability.mjs";
|
|
2
|
-
import { a as problemText,
|
|
2
|
+
import { a as problemText, l as ContextBar, m as AgoPipe, o as cursorList } from "./juice-js-observability-telemetry-D9EYxkdr.mjs";
|
|
3
3
|
import * as i0 from "@angular/core";
|
|
4
4
|
import { ChangeDetectionStrategy, Component, computed, inject, input } from "@angular/core";
|
|
5
5
|
import { RouterLink } from "@angular/router";
|
|
@@ -205,4 +205,4 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
205
205
|
});
|
|
206
206
|
export { AlertsPage };
|
|
207
207
|
|
|
208
|
-
//# sourceMappingURL=juice-js-observability-alerts.page-
|
|
208
|
+
//# sourceMappingURL=juice-js-observability-alerts.page-C2tXzLQl.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"juice-js-observability-alerts.page-
|
|
1
|
+
{"version":3,"file":"juice-js-observability-alerts.page-C2tXzLQl.mjs","names":[],"sources":["../../../../projects/juice-js/observability/src/lib/pages/alerts.page.ts"],"sourcesContent":["import { LinkPipe } from '../core/options';\nimport { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';\nimport { MatButtonModule } from '@angular/material/button';\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 } from '../shared/format';\nimport { ContextBar } from '../shared/ui';\nimport { cursorList } from '../shared/paging';\nimport { problemText } from '../shared/telemetry';\n\n/** Alerts, newest first (Console requirements §14), filtered by state; 100 at a time, with the next on request. */\n@Component({\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [LinkPipe, RouterLink, MatButtonModule, MatProgressBarModule, ContextBar, AgoPipe],\n template: `\n <jc-context-bar><h1>Alerts</h1>\n @for (s of states; track s) {\n <a class=\"jc-chip\" [class.jc-selected]=\"s === (state() ?? 'firing')\" [routerLink]=\"['alerts'] | jcLink\" [queryParams]=\"ctx.keep({ state: s })\">{{ s }}</a>\n }\n </jc-context-bar>\n @if (alerts.first.isLoading()) { <mat-progress-bar mode=\"indeterminate\" /> }\n @if (alerts.first.error()) { <p class=\"jc-error\">{{ error() }}</p> }\n <table class=\"jc-table\">\n <thead><tr><th>Severity</th><th>Rule</th><th>Subject</th><th>State</th><th>Value</th><th>Fired</th><th>Acknowledged</th></tr></thead>\n <tbody>\n @for (a of alerts.items(); track a.id) {\n <tr>\n <td><span class=\"jc-badge\" [class.jc-health-unhealthy]=\"a.severity === 'Critical'\" [class.jc-health-degraded]=\"a.severity === 'Warning'\">{{ a.severity }}</span></td>\n <td><a [routerLink]=\"['alerts', a.id] | jcLink\" [queryParams]=\"ctx.keep()\">{{ a.rule }}</a></td>\n <td>{{ a.subjectKey }}</td>\n <td>{{ a.state }}@if (a.noData) { <span class=\"jc-muted\" title=\"The last evaluation had too few samples; the state is kept\"> · no data</span> }</td>\n <td>{{ a.lastValue ?? '—' }}</td>\n <td>{{ a.firedAt | ago }}</td>\n <td>{{ a.acknowledgedBy ?? '—' }}</td>\n </tr>\n } @empty { @if (alerts.first.hasValue()) { <tr><td colspan=\"7\" class=\"jc-muted\">No {{ state() ?? 'firing' }} alerts.</td></tr> } }\n </tbody>\n </table>\n @if (alerts.items().length > 0) {\n <div class=\"jc-more\">\n <span class=\"jc-muted\">{{ alerts.items().length }} {{ state() ?? 'firing' }} alert{{ alerts.items().length === 1 ? '' : 's' }}{{ alerts.hasMore() ? ', newest first' : '' }}</span>\n @if (alerts.hasMore()) {\n <button mat-button (click)=\"alerts.more()\" [disabled]=\"alerts.loadingMore()\">{{ alerts.loadingMore() ? 'Loading…' : 'Load more' }}</button>\n }\n @if (alerts.moreError()) { <span class=\"jc-error\">{{ moreError() }}</span> }\n </div>\n }`,\n})\nexport class AlertsPage {\n protected readonly ctx = inject(ContextService);\n private readonly api = inject(ApiService);\n protected readonly states = ['firing', 'pending', 'resolved'];\n readonly state = input<string>();\n protected readonly alerts = cursorList(() => this.state() ?? 'firing', state => ({ state }), q => this.api.alerts(q));\n protected readonly error = computed(() => problemText(this.alerts.first.error()));\n protected readonly moreError = computed(() => problemText(this.alerts.moreError()));\n}\n"],"mappings":";;;;;;;;;AAkDA,IAAa,aAAb,MAAa,WAAU;CACF,MAAM,OAAO,cAAc;CAC7B,MAAM,OAAO,UAAU;CACrB,SAAS;EAAC;EAAU;EAAW;CAAU;CACnD,QAAQ,MAAe,GAAA,YAAA,CAAA,QAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CACb,SAAS,iBAAiB,KAAK,MAAK,KAAM,WAAU,WAAU,EAAE,MAAK,KAAK,MAAK,KAAK,IAAI,OAAO,CAAC,CAAC;CACjG,QAAQ,eAAe,YAAY,KAAK,OAAO,MAAM,MAAK,CAAE,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAC9D,YAAY,eAAe,YAAY,KAAK,OAAO,UAAS,CAAE,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAPxE,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAU,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAV,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAU,cAAA;EAAA,UAAA;EAAA,QAAA,EAAA,OAAA;GAAA,mBAAA;GAAA,YAAA;GAAA,UAAA;GAAA,YAAA;GAAA,mBAAA;EAAA,EAAA;EAAA,UAAA;EAAA,UAlCX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgCN,UAAA;EAAA,cAAA;GAAA;IAAA,MAAA;IAAA,MAjCgB;IAAU,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;GAAe;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;IAAA,QAAA,CAAA,WAAA;IAAA,UAAA,CAAA,aAAA,WAAA;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,MAAvE;IAAQ,MAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAiE;IAAO,MAAA;GAAA;EAAA;EAAA,iBAAA,GAAA,wBAAA;CAAA,CAAA;;AAmC/E,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAU,YAAA,CAAA;EArCtB,MAAA;EAAU,MAAA,CAAA;GACT,iBAAiB,wBAAwB;GACzC,SAAS;IAAC;IAAU;IAAY;IAAiB;IAAsB;IAAY;GAAO;GAC1F,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiCX,CAAA"}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { ApiService, ContextService } from "./juice-js-observability.mjs";
|
|
2
|
-
import { l as ContextBar, t as LogTable } from "./juice-js-observability-telemetry-
|
|
2
|
+
import { l as ContextBar, t as LogTable, x as apiResource } from "./juice-js-observability-telemetry-D9EYxkdr.mjs";
|
|
3
3
|
import * as i0 from "@angular/core";
|
|
4
4
|
import { ChangeDetectionStrategy, Component, computed, inject, input } from "@angular/core";
|
|
5
5
|
import { map } from "rxjs";
|
|
6
|
-
import { rxResource } from "@angular/core/rxjs-interop";
|
|
7
6
|
import { Router } from "@angular/router";
|
|
8
7
|
import * as i1 from "@angular/forms";
|
|
9
8
|
import { FormsModule } from "@angular/forms";
|
|
@@ -25,7 +24,7 @@ var LogsPage = class LogsPage {
|
|
|
25
24
|
spanId = input(...ngDevMode ? [void 0, { debugName: "spanId" }] : /* istanbul ignore next */ []);
|
|
26
25
|
instance = input(...ngDevMode ? [void 0, { debugName: "instance" }] : /* istanbul ignore next */ []);
|
|
27
26
|
resource = input(...ngDevMode ? [void 0, { debugName: "resource" }] : /* istanbul ignore next */ []);
|
|
28
|
-
services =
|
|
27
|
+
services = apiResource({ stream: () => this.api.resources("service").pipe(map((p) => p.items)) });
|
|
29
28
|
query = computed(() => ({
|
|
30
29
|
serviceId: this.service(),
|
|
31
30
|
minSeverity: this.severity(),
|
|
@@ -411,4 +410,4 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
411
410
|
});
|
|
412
411
|
export { LogsPage };
|
|
413
412
|
|
|
414
|
-
//# sourceMappingURL=juice-js-observability-logs.page
|
|
413
|
+
//# sourceMappingURL=juice-js-observability-logs.page--j6W1Ej-.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"juice-js-observability-logs.page--j6W1Ej-.mjs","names":[],"sources":["../../../../projects/juice-js/observability/src/lib/pages/logs.page.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';\nimport { apiResource } from '../shared/resource';\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 { MatSelectModule } from '@angular/material/select';\nimport { Router } from '@angular/router';\nimport { map } from 'rxjs';\nimport { ApiService } from '../core/api.service';\nimport { ContextService } from '../core/context.service';\nimport { ContextBar } from '../shared/ui';\nimport { LogTable } from '../shared/telemetry';\n\n/** Log search (Console requirements §7): by service, severity, text, trace or span; each record links to its trace. */\n@Component({\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormsModule, MatButtonModule, MatFormFieldModule, MatInputModule, MatSelectModule, ContextBar, LogTable],\n template: `\n <jc-context-bar><h1>Logs</h1></jc-context-bar>\n <form class=\"jc-filter-form\" (ngSubmit)=\"apply(f.value)\" #f=\"ngForm\">\n <mat-form-field subscriptSizing=\"dynamic\">\n <mat-label>Service</mat-label>\n <mat-select name=\"service\" [ngModel]=\"service() ?? ''\">\n <mat-option value=\"\">Any</mat-option>\n @for (s of services.value() ?? []; track s.id) { <mat-option [value]=\"s.id\">{{ s.key }}</mat-option> }\n </mat-select>\n </mat-form-field>\n <mat-form-field subscriptSizing=\"dynamic\" class=\"jc-field-sm\">\n <mat-label>Severity at least</mat-label>\n <mat-select name=\"severity\" [ngModel]=\"severity() ?? ''\">\n <mat-option value=\"\">Any</mat-option><mat-option value=\"5\">Debug</mat-option><mat-option value=\"9\">Info</mat-option>\n <mat-option value=\"13\">Warn</mat-option><mat-option value=\"17\">Error</mat-option><mat-option value=\"21\">Fatal</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field subscriptSizing=\"dynamic\" class=\"jc-field-wide\"><mat-label>Message contains</mat-label><input matInput name=\"q\" [ngModel]=\"q() ?? ''\"></mat-form-field>\n <mat-form-field subscriptSizing=\"dynamic\"><mat-label>Trace ID</mat-label><input matInput name=\"trace\" [ngModel]=\"trace() ?? ''\"></mat-form-field>\n <mat-form-field subscriptSizing=\"dynamic\"><mat-label>Span ID</mat-label><input matInput name=\"spanId\" [ngModel]=\"spanId() ?? ''\"></mat-form-field>\n <button mat-flat-button type=\"submit\">Search</button>\n <button mat-button type=\"button\" (click)=\"clear()\">Clear</button>\n </form>\n <jc-log-table [query]=\"query()\" />`,\n})\nexport class LogsPage {\n protected readonly ctx = inject(ContextService);\n private readonly api = inject(ApiService);\n private readonly router = inject(Router);\n\n readonly service = input<string>();\n readonly severity = input<string>();\n readonly q = input<string>();\n readonly trace = input<string>();\n readonly spanId = input<string>();\n readonly instance = input<string>();\n readonly resource = input<string>();\n\n protected readonly services = apiResource({ stream: () => this.api.resources('service').pipe(map(p => p.items)) });\n protected readonly query = computed(() => ({\n serviceId: this.service(), minSeverity: this.severity(), q: this.q(), traceId: this.trace(), spanId: this.spanId(),\n instanceId: this.instance(), resourceId: this.resource(),\n }));\n\n apply(value: Record<string, unknown>) {\n const queryParams: Record<string, unknown> = {};\n for (const [key, v] of Object.entries(value)) queryParams[key] = v === '' || v === undefined ? null : v;\n void this.router.navigate([], { queryParams, queryParamsHandling: 'merge' });\n }\n\n clear() { void this.router.navigate([], { queryParams: this.ctx.keep() }); }\n}\n"],"mappings":";;;;;;;;;;;;;;;AA2CA,IAAa,WAAb,MAAa,SAAQ;CACA,MAAM,OAAO,cAAc;CAC7B,MAAM,OAAO,UAAU;CACvB,SAAS,OAAO,MAAM;CAE9B,UAAU,MAAe,GAAA,YAAA,CAAA,QAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CACzB,WAAW,MAAe,GAAA,YAAA,CAAA,QAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAC1B,IAAI,MAAe,GAAA,YAAA,CAAA,QAAA,EAAA,WAAA,IAAA,CAAA,+BAAA,CAAA,CAAA;CACnB,QAAQ,MAAe,GAAA,YAAA,CAAA,QAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CACvB,SAAS,MAAe,GAAA,YAAA,CAAA,QAAA,EAAA,WAAA,SAAA,CAAA,+BAAA,CAAA,CAAA;CACxB,WAAW,MAAe,GAAA,YAAA,CAAA,QAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAC1B,WAAW,MAAe,GAAA,YAAA,CAAA,QAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAEhB,WAAW,YAAY,EAAE,cAAc,KAAK,IAAI,UAAU,SAAS,CAAC,CAAC,KAAK,KAAI,MAAK,EAAE,KAAK,CAAC,EAAC,CAAE;CAC9F,QAAQ,gBAAgB;EACzC,WAAW,KAAK,QAAO;EAAI,aAAa,KAAK,SAAQ;EAAI,GAAG,KAAK,EAAC;EAAI,SAAS,KAAK,MAAK;EAAI,QAAQ,KAAK,OAAM;EAChH,YAAY,KAAK,SAAQ;EAAI,YAAY,KAAK,SAAQ;CACvD,IAAE,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAEH,MAAM,OAA8B;EAClC,MAAM,cAAuC,CAAA;EAC7C,KAAK,MAAM,CAAC,KAAK,MAAM,OAAO,QAAQ,KAAK,GAAG,YAAY,OAAO,MAAM,MAAM,MAAM,SAAY,OAAO;EACtG,AAAK,KAAK,OAAO,SAAS,CAAA,GAAI;GAAE;GAAa,qBAAqB;EAAO,CAAE;CAC7E;CAEA,QAAK;EAAK,AAAK,KAAK,OAAO,SAAS,CAAA,GAAI,EAAE,aAAa,KAAK,IAAI,KAAI,EAAE,CAAE;CAAG;CAzBhE,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAQ,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAR,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAQ,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,SAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,UAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,GAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,OAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,QAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,UAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,UAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,UAAA;EAAA,UAzBT;;;;;;;;;;;;;;;;;;;;;;;;EAuB2B,UAAA;EAAA,cAAA;GAAA;IAAA,MAAA;IAAA,MAxB3B;GAAW;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;IAAA;IAAA,SAAA,CAAA,eAAA;IAAA,UAAA,CAAA,SAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;IAAA,QAAA,CAAA,eAAA;IAAA,SAAA,CAAA,UAAA;IAAA,UAAA,CAAA,QAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;GAAe;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;IAAA,QAAA,CAAA,WAAA;IAAA,UAAA,CAAA,aAAA,WAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;GAAkB;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;IAAA,UAAA,CAAA,cAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;GAAc;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;IAAA,UAAA,CAAA,UAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;GAAe;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;IAAA,SAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;IAAA,UAAA,CAAA,WAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;IAAA;IAAA,SAAA,CAAA,mBAAA;IAAA,UAAA,CAAA,WAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAU,UAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAQ,UAAA;IAAA,QAAA,CAAA,SAAA,SAAA;GAAA;EAAA;EAAA,iBAAA,GAAA,wBAAA;CAAA,CAAA;;AA0BtG,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAQ,YAAA,CAAA;EA5BpB,MAAA;EAAU,MAAA,CAAA;GACT,iBAAiB,wBAAwB;GACzC,SAAS;IAAC;IAAa;IAAiB;IAAoB;IAAgB;IAAiB;IAAY;GAAQ;GACjH,UAAU;;;;;;;;;;;;;;;;;;;;;;;;EAwBX,CAAA"}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { ApiService, ContextService } from "./juice-js-observability.mjs";
|
|
2
|
-
import { l as ContextBar, n as MetricExplorer } from "./juice-js-observability-telemetry-
|
|
2
|
+
import { l as ContextBar, n as MetricExplorer, x as apiResource } from "./juice-js-observability-telemetry-D9EYxkdr.mjs";
|
|
3
3
|
import * as i0 from "@angular/core";
|
|
4
4
|
import { ChangeDetectionStrategy, Component, computed, inject, input } from "@angular/core";
|
|
5
5
|
import { map } from "rxjs";
|
|
6
|
-
import { rxResource } from "@angular/core/rxjs-interop";
|
|
7
6
|
import { Router } from "@angular/router";
|
|
8
7
|
import * as i1 from "@angular/forms";
|
|
9
8
|
import { FormsModule } from "@angular/forms";
|
|
@@ -17,8 +16,8 @@ var MetricsPage = class MetricsPage {
|
|
|
17
16
|
service = input(...ngDevMode ? [void 0, { debugName: "service" }] : /* istanbul ignore next */ []);
|
|
18
17
|
resource = input(...ngDevMode ? [void 0, { debugName: "resource" }] : /* istanbul ignore next */ []);
|
|
19
18
|
metric = input(...ngDevMode ? [void 0, { debugName: "metric" }] : /* istanbul ignore next */ []);
|
|
20
|
-
services =
|
|
21
|
-
hosts =
|
|
19
|
+
services = apiResource({ stream: () => this.api.resources("service").pipe(map((p) => p.items)) });
|
|
20
|
+
hosts = apiResource({ stream: () => this.api.resources("host").pipe(map((p) => p.items)) });
|
|
22
21
|
query = computed(() => ({
|
|
23
22
|
serviceId: this.service(),
|
|
24
23
|
resourceId: this.resource()
|
|
@@ -265,4 +264,4 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
265
264
|
});
|
|
266
265
|
export { MetricsPage };
|
|
267
266
|
|
|
268
|
-
//# sourceMappingURL=juice-js-observability-metrics.page-
|
|
267
|
+
//# sourceMappingURL=juice-js-observability-metrics.page-DR6qZtfl.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"juice-js-observability-metrics.page-DR6qZtfl.mjs","names":[],"sources":["../../../../projects/juice-js/observability/src/lib/pages/metrics.page.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';\nimport { apiResource } from '../shared/resource';\nimport { FormsModule } from '@angular/forms';\nimport { MatFormFieldModule } from '@angular/material/form-field';\nimport { MatSelectModule } from '@angular/material/select';\nimport { Router } from '@angular/router';\nimport { map } from 'rxjs';\nimport { ApiService } from '../core/api.service';\nimport { ContextService } from '../core/context.service';\nimport { ContextBar } from '../shared/ui';\nimport { MetricExplorer } from '../shared/telemetry';\n\n/** Metrics of a service or a host (Console requirements §7): pick a scope, then a metric. */\n@Component({\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormsModule, MatFormFieldModule, MatSelectModule, ContextBar, MetricExplorer],\n template: `\n <jc-context-bar><h1>Metrics</h1></jc-context-bar>\n <div class=\"jc-filter-form\">\n <mat-form-field subscriptSizing=\"dynamic\">\n <mat-label>Service</mat-label>\n <mat-select [ngModel]=\"service() ?? ''\" (ngModelChange)=\"scope({ service: $event || null, resource: null })\">\n <mat-option value=\"\">Any</mat-option>\n @for (s of services.value() ?? []; track s.id) { <mat-option [value]=\"s.id\">{{ s.key }}</mat-option> }\n </mat-select>\n </mat-form-field>\n <mat-form-field subscriptSizing=\"dynamic\">\n <mat-label>Host</mat-label>\n <mat-select [ngModel]=\"resource() ?? ''\" (ngModelChange)=\"scope({ resource: $event || null, service: null })\">\n <mat-option value=\"\">Any</mat-option>\n @for (h of hosts.value() ?? []; track h.id) { <mat-option [value]=\"h.id\">{{ h.displayName }}</mat-option> }\n </mat-select>\n </mat-form-field>\n </div>\n <jc-metric-explorer [scope]=\"query()\" [initial]=\"metric() ?? null\" />`,\n})\nexport class MetricsPage {\n protected readonly ctx = inject(ContextService);\n private readonly api = inject(ApiService);\n private readonly router = inject(Router);\n readonly service = input<string>();\n readonly resource = input<string>();\n readonly metric = input<string>();\n\n protected readonly services = apiResource({ stream: () => this.api.resources('service').pipe(map(p => p.items)) });\n protected readonly hosts = apiResource({ stream: () => this.api.resources('host').pipe(map(p => p.items)) });\n protected readonly query = computed(() => ({ serviceId: this.service(), resourceId: this.resource() }));\n\n scope(change: Record<string, string | null>) {\n void this.router.navigate([], { queryParams: change, queryParamsHandling: 'merge' });\n }\n}\n"],"mappings":";;;;;;;;;;;AAoCA,IAAa,cAAb,MAAa,YAAW;CACH,MAAM,OAAO,cAAc;CAC7B,MAAM,OAAO,UAAU;CACvB,SAAS,OAAO,MAAM;CAC9B,UAAU,MAAe,GAAA,YAAA,CAAA,QAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CACzB,WAAW,MAAe,GAAA,YAAA,CAAA,QAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAC1B,SAAS,MAAe,GAAA,YAAA,CAAA,QAAA,EAAA,WAAA,SAAA,CAAA,+BAAA,CAAA,CAAA;CAEd,WAAW,YAAY,EAAE,cAAc,KAAK,IAAI,UAAU,SAAS,CAAC,CAAC,KAAK,KAAI,MAAK,EAAE,KAAK,CAAC,EAAC,CAAE;CAC9F,QAAQ,YAAY,EAAE,cAAc,KAAK,IAAI,UAAU,MAAM,CAAC,CAAC,KAAK,KAAI,MAAK,EAAE,KAAK,CAAC,EAAC,CAAE;CACxF,QAAQ,gBAAgB;EAAE,WAAW,KAAK,QAAO;EAAI,YAAY,KAAK,SAAQ;CAAE,IAAI,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAEvG,MAAM,QAAqC;EACzC,AAAK,KAAK,OAAO,SAAS,CAAA,GAAI;GAAE,aAAa;GAAQ,qBAAqB;EAAO,CAAE;CACrF;CAdW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAW,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAX,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAW,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,SAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,UAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,QAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,UAAA;EAAA,UApBZ;;;;;;;;;;;;;;;;;;;EAkB8D,UAAA;EAAA,cAAA;GAAA;IAAA,MAAA;IAAA,MAnB9D;GAAW;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;IAAA;IAAA,SAAA,CAAA,eAAA;IAAA,UAAA,CAAA,SAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;GAAkB;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;IAAA,UAAA,CAAA,cAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;GAAe;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;IAAA,SAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;IAAA,UAAA,CAAA,WAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;IAAA;IAAA,SAAA,CAAA,mBAAA;IAAA,UAAA,CAAA,WAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAU,UAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAc,UAAA;IAAA,QAAA,CAAA,SAAA,SAAA;GAAA;EAAA;EAAA,iBAAA,GAAA,wBAAA;CAAA,CAAA;;AAqB3E,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAW,YAAA,CAAA;EAvBvB,MAAA;EAAU,MAAA,CAAA;GACT,iBAAiB,wBAAwB;GACzC,SAAS;IAAC;IAAa;IAAoB;IAAiB;IAAY;GAAc;GACtF,UAAU;;;;;;;;;;;;;;;;;;;EAmBX,CAAA"}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { ApiService, ContextService, LinkPipe } from "./juice-js-observability.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { _ as ReasonPipe, a as problemText, l as ContextBar, m as AgoPipe, u as HealthBadge, x as apiResource } from "./juice-js-observability-telemetry-D9EYxkdr.mjs";
|
|
3
3
|
import * as i0 from "@angular/core";
|
|
4
4
|
import { ChangeDetectionStrategy, Component, computed, inject } from "@angular/core";
|
|
5
5
|
import { map } from "rxjs";
|
|
6
|
-
import { rxResource } from "@angular/core/rxjs-interop";
|
|
7
6
|
import { RouterLink } from "@angular/router";
|
|
8
7
|
import * as i1 from "@angular/material/progress-bar";
|
|
9
8
|
import { MatProgressBarModule } from "@angular/material/progress-bar";
|
|
@@ -15,21 +14,26 @@ const ORDER = [
|
|
|
15
14
|
"Unknown",
|
|
16
15
|
"Healthy"
|
|
17
16
|
];
|
|
17
|
+
const SHOWN = 10;
|
|
18
18
|
var OverviewPage = class OverviewPage {
|
|
19
19
|
ctx = inject(ContextService);
|
|
20
20
|
api = inject(ApiService);
|
|
21
21
|
order = ORDER;
|
|
22
|
+
shown = SHOWN;
|
|
22
23
|
reasonPipe = new ReasonPipe();
|
|
23
24
|
reason = (r) => this.reasonPipe.transform(r);
|
|
24
|
-
health =
|
|
25
|
+
health = apiResource({
|
|
25
26
|
params: () => this.ctx.environment(),
|
|
26
27
|
stream: ({ params }) => this.api.health({
|
|
27
28
|
subjectType: "service",
|
|
28
29
|
environment: params
|
|
29
30
|
}).pipe(map((p) => p.items))
|
|
30
31
|
});
|
|
31
|
-
alerts =
|
|
32
|
+
alerts = apiResource({ stream: () => this.api.allAlerts({ state: "firing" }).pipe(map((p) => p.items)) });
|
|
32
33
|
error = computed(() => problemText(this.health.error()), ...ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []);
|
|
34
|
+
firing = computed(() => [...this.alerts.value() ?? []].sort((a, b) => Number(b.severity === "Critical") - Number(a.severity === "Critical") || (b.firedAt ?? "").localeCompare(a.firedAt ?? "")), ...ngDevMode ? [{ debugName: "firing" }] : /* istanbul ignore next */ []);
|
|
35
|
+
firingShown = computed(() => this.firing().slice(0, SHOWN), ...ngDevMode ? [{ debugName: "firingShown" }] : /* istanbul ignore next */ []);
|
|
36
|
+
criticalCount = computed(() => this.firing().filter((a) => a.severity === "Critical").length, ...ngDevMode ? [{ debugName: "criticalCount" }] : /* istanbul ignore next */ []);
|
|
33
37
|
counts = computed(() => {
|
|
34
38
|
const counts = {};
|
|
35
39
|
for (const h of this.health.value() ?? []) counts[h.state] = (counts[h.state] ?? 0) + 1;
|
|
@@ -65,8 +69,11 @@ var OverviewPage = class OverviewPage {
|
|
|
65
69
|
</div>
|
|
66
70
|
|
|
67
71
|
<section class="jc-card">
|
|
68
|
-
<
|
|
69
|
-
|
|
72
|
+
<div class="jc-row">
|
|
73
|
+
<h2>Firing alerts @if (firing().length) { <span class="jc-muted">({{ firing().length }}@if (criticalCount()) { · {{ criticalCount() }} critical})</span> }</h2>
|
|
74
|
+
@if (firing().length) { <a [routerLink]="['alerts'] | jcLink" [queryParams]="ctx.keep({ state: 'firing' })">All firing alerts</a> }
|
|
75
|
+
</div>
|
|
76
|
+
@for (a of firingShown(); track a.id) {
|
|
70
77
|
<div class="jc-list-row">
|
|
71
78
|
<span class="jc-badge" [class.jc-health-unhealthy]="a.severity === 'Critical'" [class.jc-health-degraded]="a.severity !== 'Critical'">{{ a.severity }}</span>
|
|
72
79
|
<a [routerLink]="['alerts', a.id] | jcLink" [queryParams]="ctx.keep()">{{ a.rule }}</a>
|
|
@@ -74,11 +81,15 @@ var OverviewPage = class OverviewPage {
|
|
|
74
81
|
<span class="jc-muted">fired {{ a.firedAt | ago }}@if (a.acknowledgedBy) { · acknowledged by {{ a.acknowledgedBy }} }</span>
|
|
75
82
|
</div>
|
|
76
83
|
} @empty { <p class="jc-muted">Nothing firing.</p> }
|
|
84
|
+
@if (firing().length > shown) {
|
|
85
|
+
<p class="jc-more"><span class="jc-muted">Critical first, then the newest; {{ firing().length - shown }} more.</span>
|
|
86
|
+
<a [routerLink]="['alerts'] | jcLink" [queryParams]="ctx.keep({ state: 'firing' })">See all {{ firing().length }}</a></p>
|
|
87
|
+
}
|
|
77
88
|
</section>
|
|
78
89
|
|
|
79
90
|
<section class="jc-card">
|
|
80
|
-
<h2>Services needing attention</h2>
|
|
81
|
-
@for (h of attention(); track h.subjectId + h.environment) {
|
|
91
|
+
<h2>Services needing attention @if (attention().length) { <span class="jc-muted">({{ attention().length }})</span> }</h2>
|
|
92
|
+
@for (h of attention().slice(0, shown); track h.subjectId + h.environment) {
|
|
82
93
|
<div class="jc-list-row">
|
|
83
94
|
<jc-health [state]="h.state" [stale]="h.stale" />
|
|
84
95
|
<a [routerLink]="['services', h.subjectId] | jcLink" [queryParams]="ctx.keep({ env: h.environment })">{{ h.subjectName }}</a>
|
|
@@ -87,6 +98,12 @@ var OverviewPage = class OverviewPage {
|
|
|
87
98
|
<span class="jc-muted">since {{ h.since | ago }}</span>
|
|
88
99
|
</div>
|
|
89
100
|
} @empty { <p class="jc-muted">Every evaluated service is healthy.</p> }
|
|
101
|
+
@if (attention().length > shown) {
|
|
102
|
+
<p class="jc-more"><span class="jc-muted">The worst first; {{ attention().length - shown }} more. The counts above open each state's list:</span>
|
|
103
|
+
@for (s of order; track s) { @if (s !== 'Healthy' && counts()[s]) {
|
|
104
|
+
<a [routerLink]="['services'] | jcLink" [queryParams]="ctx.keep({ state: s })">{{ counts()[s] }} {{ s.toLowerCase() }}</a>
|
|
105
|
+
} }</p>
|
|
106
|
+
}
|
|
90
107
|
</section>`,
|
|
91
108
|
isInline: true,
|
|
92
109
|
dependencies: [
|
|
@@ -182,8 +199,11 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
182
199
|
</div>
|
|
183
200
|
|
|
184
201
|
<section class="jc-card">
|
|
185
|
-
<
|
|
186
|
-
|
|
202
|
+
<div class="jc-row">
|
|
203
|
+
<h2>Firing alerts @if (firing().length) { <span class="jc-muted">({{ firing().length }}@if (criticalCount()) { · {{ criticalCount() }} critical})</span> }</h2>
|
|
204
|
+
@if (firing().length) { <a [routerLink]="['alerts'] | jcLink" [queryParams]="ctx.keep({ state: 'firing' })">All firing alerts</a> }
|
|
205
|
+
</div>
|
|
206
|
+
@for (a of firingShown(); track a.id) {
|
|
187
207
|
<div class="jc-list-row">
|
|
188
208
|
<span class="jc-badge" [class.jc-health-unhealthy]="a.severity === 'Critical'" [class.jc-health-degraded]="a.severity !== 'Critical'">{{ a.severity }}</span>
|
|
189
209
|
<a [routerLink]="['alerts', a.id] | jcLink" [queryParams]="ctx.keep()">{{ a.rule }}</a>
|
|
@@ -191,11 +211,15 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
191
211
|
<span class="jc-muted">fired {{ a.firedAt | ago }}@if (a.acknowledgedBy) { · acknowledged by {{ a.acknowledgedBy }} }</span>
|
|
192
212
|
</div>
|
|
193
213
|
} @empty { <p class="jc-muted">Nothing firing.</p> }
|
|
214
|
+
@if (firing().length > shown) {
|
|
215
|
+
<p class="jc-more"><span class="jc-muted">Critical first, then the newest; {{ firing().length - shown }} more.</span>
|
|
216
|
+
<a [routerLink]="['alerts'] | jcLink" [queryParams]="ctx.keep({ state: 'firing' })">See all {{ firing().length }}</a></p>
|
|
217
|
+
}
|
|
194
218
|
</section>
|
|
195
219
|
|
|
196
220
|
<section class="jc-card">
|
|
197
|
-
<h2>Services needing attention</h2>
|
|
198
|
-
@for (h of attention(); track h.subjectId + h.environment) {
|
|
221
|
+
<h2>Services needing attention @if (attention().length) { <span class="jc-muted">({{ attention().length }})</span> }</h2>
|
|
222
|
+
@for (h of attention().slice(0, shown); track h.subjectId + h.environment) {
|
|
199
223
|
<div class="jc-list-row">
|
|
200
224
|
<jc-health [state]="h.state" [stale]="h.stale" />
|
|
201
225
|
<a [routerLink]="['services', h.subjectId] | jcLink" [queryParams]="ctx.keep({ env: h.environment })">{{ h.subjectName }}</a>
|
|
@@ -204,10 +228,16 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
204
228
|
<span class="jc-muted">since {{ h.since | ago }}</span>
|
|
205
229
|
</div>
|
|
206
230
|
} @empty { <p class="jc-muted">Every evaluated service is healthy.</p> }
|
|
231
|
+
@if (attention().length > shown) {
|
|
232
|
+
<p class="jc-more"><span class="jc-muted">The worst first; {{ attention().length - shown }} more. The counts above open each state's list:</span>
|
|
233
|
+
@for (s of order; track s) { @if (s !== 'Healthy' && counts()[s]) {
|
|
234
|
+
<a [routerLink]="['services'] | jcLink" [queryParams]="ctx.keep({ state: s })">{{ counts()[s] }} {{ s.toLowerCase() }}</a>
|
|
235
|
+
} }</p>
|
|
236
|
+
}
|
|
207
237
|
</section>`
|
|
208
238
|
}]
|
|
209
239
|
}]
|
|
210
240
|
});
|
|
211
241
|
export { OverviewPage };
|
|
212
242
|
|
|
213
|
-
//# sourceMappingURL=juice-js-observability-overview.page-
|
|
243
|
+
//# sourceMappingURL=juice-js-observability-overview.page-DU0GxSXQ.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"juice-js-observability-overview.page-DU0GxSXQ.mjs","names":[],"sources":["../../../../projects/juice-js/observability/src/lib/pages/overview.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 { map } from 'rxjs';\nimport { ApiService } from '../core/api.service';\nimport { ContextService } from '../core/context.service';\nimport { HealthState } from '../core/models';\nimport { AgoPipe, ReasonPipe } from '../shared/format';\nimport { ContextBar, HealthBadge } from '../shared/ui';\nimport { problemText } from '../shared/telemetry';\n\nconst ORDER: HealthState[] = ['Critical', 'Unhealthy', 'Degraded', 'Offline', 'Unknown', 'Healthy'];\n\n/** How many rows each list shows: the overview orients, the alerts and services pages list everything. */\nconst SHOWN = 10;\n\n/**\n * Where an investigation starts (Console requirements §4, §21 step 1): what is firing and which services are not\n * healthy, each one click from its service page. Orientation, not a dashboard.\n */\n@Component({\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [LinkPipe, RouterLink, MatProgressBarModule, ContextBar, HealthBadge, AgoPipe],\n template: `\n <jc-context-bar><h1>Overview</h1></jc-context-bar>\n @if (health.isLoading() || alerts.isLoading()) { <mat-progress-bar mode=\"indeterminate\" /> }\n @if (health.error()) { <p class=\"jc-error\">{{ error() }}</p> }\n\n <div class=\"jc-tiles\">\n @for (s of order; track s) {\n <a class=\"jc-card jc-tile\" [routerLink]=\"['services'] | jcLink\" [queryParams]=\"ctx.keep({ state: s })\">\n <jc-health [state]=\"s\" /><strong>{{ counts()[s] ?? 0 }}</strong><span class=\"jc-muted\">services</span>\n </a>\n }\n </div>\n\n <section class=\"jc-card\">\n <div class=\"jc-row\">\n <h2>Firing alerts @if (firing().length) { <span class=\"jc-muted\">({{ firing().length }}@if (criticalCount()) { · {{ criticalCount() }} critical})</span> }</h2>\n @if (firing().length) { <a [routerLink]=\"['alerts'] | jcLink\" [queryParams]=\"ctx.keep({ state: 'firing' })\">All firing alerts</a> }\n </div>\n @for (a of firingShown(); track a.id) {\n <div class=\"jc-list-row\">\n <span class=\"jc-badge\" [class.jc-health-unhealthy]=\"a.severity === 'Critical'\" [class.jc-health-degraded]=\"a.severity !== 'Critical'\">{{ a.severity }}</span>\n <a [routerLink]=\"['alerts', a.id] | jcLink\" [queryParams]=\"ctx.keep()\">{{ a.rule }}</a>\n <span>{{ a.subjectKey }}</span>\n <span class=\"jc-muted\">fired {{ a.firedAt | ago }}@if (a.acknowledgedBy) { · acknowledged by {{ a.acknowledgedBy }} }</span>\n </div>\n } @empty { <p class=\"jc-muted\">Nothing firing.</p> }\n @if (firing().length > shown) {\n <p class=\"jc-more\"><span class=\"jc-muted\">Critical first, then the newest; {{ firing().length - shown }} more.</span>\n <a [routerLink]=\"['alerts'] | jcLink\" [queryParams]=\"ctx.keep({ state: 'firing' })\">See all {{ firing().length }}</a></p>\n }\n </section>\n\n <section class=\"jc-card\">\n <h2>Services needing attention @if (attention().length) { <span class=\"jc-muted\">({{ attention().length }})</span> }</h2>\n @for (h of attention().slice(0, shown); track h.subjectId + h.environment) {\n <div class=\"jc-list-row\">\n <jc-health [state]=\"h.state\" [stale]=\"h.stale\" />\n <a [routerLink]=\"['services', h.subjectId] | jcLink\" [queryParams]=\"ctx.keep({ env: h.environment })\">{{ h.subjectName }}</a>\n <span class=\"jc-muted\">{{ h.environment }}</span>\n <span>{{ h.reasons.slice(0, 3).map(reason).join(' · ') }}</span>\n <span class=\"jc-muted\">since {{ h.since | ago }}</span>\n </div>\n } @empty { <p class=\"jc-muted\">Every evaluated service is healthy.</p> }\n @if (attention().length > shown) {\n <p class=\"jc-more\"><span class=\"jc-muted\">The worst first; {{ attention().length - shown }} more. The counts above open each state's list:</span>\n @for (s of order; track s) { @if (s !== 'Healthy' && counts()[s]) {\n <a [routerLink]=\"['services'] | jcLink\" [queryParams]=\"ctx.keep({ state: s })\">{{ counts()[s] }} {{ s.toLowerCase() }}</a>\n } }</p>\n }\n </section>`,\n})\nexport class OverviewPage {\n protected readonly ctx = inject(ContextService);\n private readonly api = inject(ApiService);\n protected readonly order = ORDER;\n protected readonly shown = SHOWN;\n private readonly reasonPipe = new ReasonPipe();\n protected readonly reason = (r: string) => this.reasonPipe.transform(r);\n\n protected readonly health = apiResource({\n params: () => this.ctx.environment(),\n stream: ({ params }) => this.api.health({ subjectType: 'service', environment: params }).pipe(map(p => p.items)),\n });\n protected readonly alerts = apiResource({ stream: () => this.api.allAlerts({ state: 'firing' }).pipe(map(p => p.items)) });\n protected readonly error = computed(() => problemText(this.health.error()));\n /** Critical first, then the newest. */\n protected readonly firing = computed(() => [...(this.alerts.value() ?? [])].sort((a, b) =>\n Number(b.severity === 'Critical') - Number(a.severity === 'Critical')\n || (b.firedAt ?? '').localeCompare(a.firedAt ?? '')));\n protected readonly firingShown = computed(() => this.firing().slice(0, SHOWN));\n protected readonly criticalCount = computed(() => this.firing().filter(a => a.severity === 'Critical').length);\n protected readonly counts = computed(() => {\n const counts: Partial<Record<HealthState, number>> = {};\n for (const h of this.health.value() ?? []) counts[h.state] = (counts[h.state] ?? 0) + 1;\n return counts;\n });\n protected readonly attention = computed(() => (this.health.value() ?? [])\n .filter(h => h.state !== 'Healthy')\n .sort((a, b) => ORDER.indexOf(a.state) - ORDER.indexOf(b.state)));\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { ApiService, ContextService, LinkPipe } from "./juice-js-observability.mjs";
|
|
2
|
-
import { a as problemText, c as ConfidenceBadge, d as KeyValueTable,
|
|
2
|
+
import { _ as ReasonPipe, a as problemText, c as ConfidenceBadge, d as KeyValueTable, i as TraceTable, l as ContextBar, m as AgoPipe, n as MetricExplorer, t as LogTable, u as HealthBadge, x as apiResource } from "./juice-js-observability-telemetry-D9EYxkdr.mjs";
|
|
3
3
|
import * as i0 from "@angular/core";
|
|
4
4
|
import { ChangeDetectionStrategy, Component, computed, inject, input } from "@angular/core";
|
|
5
|
-
import { rxResource } from "@angular/core/rxjs-interop";
|
|
6
5
|
import { RouterLink } from "@angular/router";
|
|
7
6
|
import * as i1 from "@angular/material/progress-bar";
|
|
8
7
|
import { MatProgressBarModule } from "@angular/material/progress-bar";
|
|
@@ -10,11 +9,11 @@ var ResourcePage = class ResourcePage {
|
|
|
10
9
|
ctx = inject(ContextService);
|
|
11
10
|
api = inject(ApiService);
|
|
12
11
|
id = input.required(...ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []);
|
|
13
|
-
resource =
|
|
12
|
+
resource = apiResource({
|
|
14
13
|
params: () => this.id(),
|
|
15
14
|
stream: ({ params }) => this.api.resource(params)
|
|
16
15
|
});
|
|
17
|
-
topology =
|
|
16
|
+
topology = apiResource({
|
|
18
17
|
params: () => this.ctx.environment(),
|
|
19
18
|
stream: ({ params }) => this.api.topology(params || null)
|
|
20
19
|
});
|
|
@@ -272,4 +271,4 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
272
271
|
});
|
|
273
272
|
export { ResourcePage };
|
|
274
273
|
|
|
275
|
-
//# sourceMappingURL=juice-js-observability-resource.page-
|
|
274
|
+
//# sourceMappingURL=juice-js-observability-resource.page-D6qpvo_f.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"juice-js-observability-resource.page-D6qpvo_f.mjs","names":[],"sources":["../../../../projects/juice-js/observability/src/lib/pages/resource.page.ts"],"sourcesContent":["import { LinkPipe } 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 { RouterLink } from '@angular/router';\nimport { ApiService } from '../core/api.service';\nimport { ContextService } from '../core/context.service';\nimport { AgoPipe, ReasonPipe } from '../shared/format';\nimport { ConfidenceBadge, ContextBar, HealthBadge, KeyValueTable } from '../shared/ui';\nimport { LogTable, MetricExplorer, TraceTable, problemText } from '../shared/telemetry';\n\n/**\n * A host, container, pod, database or destination (Console requirements §11): what runs on it or who uses it, its\n * health, and its telemetry. A database or destination has traces (the calls to it) but no logs or metrics of its own.\n */\n@Component({\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [LinkPipe, RouterLink, MatProgressBarModule, ContextBar, HealthBadge, ConfidenceBadge, KeyValueTable, TraceTable, LogTable,\n MetricExplorer, AgoPipe],\n template: `\n <jc-context-bar>\n <a [routerLink]=\"['resources'] | jcLink\" [queryParams]=\"ctx.keep({ type: resource.value()?.type })\" class=\"jc-crumb\">Resources</a>\n <span class=\"jc-crumb-sep\">›</span>\n <h1>{{ resource.value()?.displayName ?? '…' }}</h1>\n <span class=\"jc-muted\">{{ resource.value()?.type }}</span>\n </jc-context-bar>\n @if (resource.isLoading()) { <mat-progress-bar mode=\"indeterminate\" /> }\n @if (resource.error()) { <p class=\"jc-error\">{{ error() }}</p> }\n @if (resource.value(); as r) {\n <div class=\"jc-grid-2\">\n <section class=\"jc-card\">\n <h2>Identity</h2>\n <jc-kv [values]=\"{ key: r.key, 'first seen': r.firstSeen, 'last seen': r.lastSeen }\" />\n <jc-kv [values]=\"r.attributes\" empty=\"\" />\n @for (h of r.health; track h.subjectId) {\n <p><jc-health [state]=\"h.state\" [stale]=\"h.stale\" /> {{ h.reasons.map(reason).join(' · ') }}</p>\n }\n </section>\n <section class=\"jc-card\">\n @if (isInfrastructure()) {\n <h2>Instances running here</h2>\n @for (i of r.instances; track i.id) {\n <div class=\"jc-list-row\"><jc-health [state]=\"i.health\" /><span>{{ i.instanceKey }}</span>\n <span class=\"jc-muted\">{{ i.environment }}</span><span class=\"jc-muted\">{{ i.lastSeen | ago }}</span></div>\n } @empty { <p class=\"jc-muted\">No service instances seen on it.</p> }\n } @else {\n <h2>Used by</h2>\n @for (e of users(); track e.id) {\n <div class=\"jc-list-row\"><a [routerLink]=\"['services', e.source] | jcLink\" [queryParams]=\"ctx.keep()\">{{ name(e.source) }}</a>\n <span>{{ e.relation }}</span><jc-confidence [confidence]=\"e.confidence\" />\n <a [routerLink]=\"['topology'] | jcLink\" [queryParams]=\"ctx.keep({ edge: e.id })\">evidence</a></div>\n } @empty { <p class=\"jc-muted\">No users seen{{ ctx.environment() ? ' in ' + ctx.environment() : '' }}.</p> }\n }\n </section>\n </div>\n @if (isInfrastructure()) {\n <h2>Metrics</h2>\n <jc-metric-explorer [scope]=\"{ resourceId: id() }\" />\n <h2>Logs</h2>\n <jc-log-table [query]=\"{ resourceId: id() }\" />\n } @else {\n <h2>Traces that reach it</h2>\n <jc-trace-table [query]=\"{ resourceId: id() }\" />\n }\n }`,\n})\nexport class ResourcePage {\n protected readonly ctx = inject(ContextService);\n private readonly api = inject(ApiService);\n readonly id = input.required<string>();\n\n protected readonly resource = 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 error = computed(() => problemText(this.resource.error()));\n protected readonly isInfrastructure = computed(() => ['host', 'container', 'k8s-pod'].includes(this.resource.value()?.type ?? ''));\n protected readonly users = computed(() => (this.topology.value()?.edges ?? []).filter(e => e.target === this.id()));\n private readonly reasonPipe = new ReasonPipe();\n protected readonly reason = (r: string) => this.reasonPipe.transform(r);\n\n name(id: string) { return this.topology.value()?.nodes.find(n => n.id === id)?.displayName ?? id; }\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { ApiService, ContextService, LinkPipe } from "./juice-js-observability.mjs";
|
|
2
|
-
import { a as problemText,
|
|
2
|
+
import { a as problemText, l as ContextBar, m as AgoPipe, s as screenPages, x as apiResource } from "./juice-js-observability-telemetry-D9EYxkdr.mjs";
|
|
3
3
|
import * as i0 from "@angular/core";
|
|
4
4
|
import { ChangeDetectionStrategy, Component, computed, inject, input, signal } from "@angular/core";
|
|
5
|
-
import { rxResource } from "@angular/core/rxjs-interop";
|
|
6
5
|
import { RouterLink } from "@angular/router";
|
|
7
6
|
import * as i2 from "@angular/material/progress-bar";
|
|
8
7
|
import { MatProgressBarModule } from "@angular/material/progress-bar";
|
|
@@ -21,7 +20,7 @@ var ResourcesPage = class ResourcesPage {
|
|
|
21
20
|
types = TYPES;
|
|
22
21
|
type = input(...ngDevMode ? [void 0, { debugName: "type" }] : /* istanbul ignore next */ []);
|
|
23
22
|
filter = signal("", ...ngDevMode ? [{ debugName: "filter" }] : /* istanbul ignore next */ []);
|
|
24
|
-
resources =
|
|
23
|
+
resources = apiResource({
|
|
25
24
|
params: () => this.type() ?? "host",
|
|
26
25
|
stream: ({ params }) => this.api.resources(params)
|
|
27
26
|
});
|
|
@@ -207,4 +206,4 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
207
206
|
});
|
|
208
207
|
export { ResourcesPage };
|
|
209
208
|
|
|
210
|
-
//# sourceMappingURL=juice-js-observability-resources.page-
|
|
209
|
+
//# sourceMappingURL=juice-js-observability-resources.page-CR7-DFsh.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"juice-js-observability-resources.page-CR7-DFsh.mjs","names":[],"sources":["../../../../projects/juice-js/observability/src/lib/pages/resources.page.ts"],"sourcesContent":["import { LinkPipe } from '../core/options';\nimport { ChangeDetectionStrategy, Component, computed, inject, input, signal } from '@angular/core';\nimport { apiResource } from '../shared/resource';\nimport { MatPaginatorModule } from '@angular/material/paginator';\nimport { MatProgressBarModule } from '@angular/material/progress-bar';\nimport { RouterLink } from '@angular/router';\nimport { SearchablePage } from '@juice-js/layout';\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';\nimport { screenPages } from '../shared/paging';\n\nconst TYPES = ['host', 'container', 'k8s-pod', 'database', 'messaging-destination'];\n\n/**\n * Where services run and what they use (Console requirements §11): hosts, containers, pods, databases and\n * destinations, as discovery reconciled them. Never deleted automatically; renames come only from configured aliases.\n */\n@Component({\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [LinkPipe, RouterLink, MatPaginatorModule, MatProgressBarModule, ContextBar, AgoPipe],\n template: `\n <jc-context-bar><h1>Resources</h1>\n @for (t of types; track t) {\n <a class=\"jc-chip\" [class.jc-selected]=\"t === (type() ?? 'host')\" [routerLink]=\"['resources'] | jcLink\" [queryParams]=\"ctx.keep({ type: t })\">{{ t }}</a>\n }\n </jc-context-bar>\n @if (resources.isLoading()) { <mat-progress-bar mode=\"indeterminate\" /> }\n @if (resources.error()) { <p class=\"jc-error\">{{ error() }}</p> }\n @if (capped()) { <p class=\"jc-notice\">Showing the first 5,000 only. Narrow the list with the search box or a filter.</p> }\n <table class=\"jc-table\">\n <thead><tr><th>Name</th><th>Key</th><th>First seen</th><th>Last seen</th></tr></thead>\n <tbody>\n @for (r of pages.visible(); track r.id) {\n <tr><td><a [routerLink]=\"['resources', r.id] | jcLink\" [queryParams]=\"ctx.keep()\">{{ r.displayName }}</a></td>\n <td class=\"jc-mono jc-small\">{{ r.key }}</td><td>{{ r.firstSeen | ago }}</td><td>{{ r.lastSeen | ago }}</td></tr>\n } @empty { <tr><td colspan=\"4\" class=\"jc-muted\">None seen.</td></tr> }\n </tbody>\n </table>\n @if (pages.needed()) {\n <mat-paginator [length]=\"rows().length\" [pageSize]=\"pages.size()\" [pageIndex]=\"pages.index()\" [pageSizeOptions]=\"pages.sizes\"\n (page)=\"pages.change($event)\" aria-label=\"Pages\" />\n }`,\n})\nexport class ResourcesPage implements SearchablePage {\n protected readonly ctx = inject(ContextService);\n private readonly api = inject(ApiService);\n protected readonly types = TYPES;\n readonly type = input<string>();\n private readonly filter = signal('');\n\n protected readonly resources = apiResource({\n params: () => this.type() ?? 'host',\n stream: ({ params }) => this.api.resources(params),\n });\n protected readonly error = computed(() => problemText(this.resources.error()));\n protected readonly capped = computed(() => (this.resources.value()?.nextCursor ?? null) !== null);\n protected readonly rows = computed(() => (this.resources.value()?.items ?? [])\n .filter(r => !this.filter() || `${r.displayName} ${r.key}`.toLowerCase().includes(this.filter().toLowerCase())));\n\n protected readonly pages = screenPages(this.rows);\n\n onSearch(query: string) { this.filter.set(query); }\n}\n"],"mappings":"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"}
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import { ApiService, ContextService, LinkPipe } from "./juice-js-observability.mjs";
|
|
2
|
-
import { a as problemText,
|
|
2
|
+
import { a as problemText, l as ContextBar, m as AgoPipe, x as apiResource } from "./juice-js-observability-telemetry-D9EYxkdr.mjs";
|
|
3
3
|
import * as i0 from "@angular/core";
|
|
4
4
|
import { ChangeDetectionStrategy, Component, computed, inject } from "@angular/core";
|
|
5
5
|
import { forkJoin, map } from "rxjs";
|
|
6
|
-
import { rxResource } from "@angular/core/rxjs-interop";
|
|
7
6
|
import { RouterLink } from "@angular/router";
|
|
8
7
|
import * as i1 from "@angular/material/progress-bar";
|
|
9
8
|
import { MatProgressBarModule } from "@angular/material/progress-bar";
|
|
10
9
|
var RulesPage = class RulesPage {
|
|
11
10
|
ctx = inject(ContextService);
|
|
12
11
|
api = inject(ApiService);
|
|
13
|
-
data =
|
|
12
|
+
data = apiResource({ stream: () => forkJoin({
|
|
14
13
|
rules: this.api.rules().pipe(map((p) => p.items)),
|
|
15
14
|
firing: this.api.allAlerts({ state: "firing" }).pipe(map((p) => p.items)),
|
|
16
15
|
pending: this.api.allAlerts({ state: "pending" }).pipe(map((p) => p.items))
|
|
@@ -173,4 +172,4 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
173
172
|
});
|
|
174
173
|
export { RulesPage };
|
|
175
174
|
|
|
176
|
-
//# sourceMappingURL=juice-js-observability-rules.page-
|
|
175
|
+
//# sourceMappingURL=juice-js-observability-rules.page-fq-wO5I_.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"juice-js-observability-rules.page-fq-wO5I_.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,8 +1,7 @@
|
|
|
1
1
|
import { ApiService, ContextService, LinkPipe, ObservabilityLinks } from "./juice-js-observability.mjs";
|
|
2
|
-
import { a as problemText, c as ConfidenceBadge,
|
|
2
|
+
import { _ as ReasonPipe, a as problemText, c as ConfidenceBadge, g as PercentPipe, h as DurationPipe, i as TraceTable, l as ContextBar, m as AgoPipe, n as MetricExplorer, r as ServiceStatsCharts, t as LogTable, u as HealthBadge, x as apiResource } from "./juice-js-observability-telemetry-D9EYxkdr.mjs";
|
|
3
3
|
import * as i0 from "@angular/core";
|
|
4
4
|
import { ChangeDetectionStrategy, Component, computed, inject, input } from "@angular/core";
|
|
5
|
-
import { rxResource } from "@angular/core/rxjs-interop";
|
|
6
5
|
import { Router, RouterLink } from "@angular/router";
|
|
7
6
|
import * as i3 from "@angular/material/button";
|
|
8
7
|
import { MatButtonModule } from "@angular/material/button";
|
|
@@ -33,15 +32,15 @@ var ServicePage = class ServicePage {
|
|
|
33
32
|
tabIndex = computed(() => Math.max(0, TABS.indexOf(this.tab() ?? "overview")), ...ngDevMode ? [{ debugName: "tabIndex" }] : /* istanbul ignore next */ []);
|
|
34
33
|
operation = computed(() => this.op() ?? null, ...ngDevMode ? [{ debugName: "operation" }] : /* istanbul ignore next */ []);
|
|
35
34
|
instanceFilter = computed(() => this.instance() ?? null, ...ngDevMode ? [{ debugName: "instanceFilter" }] : /* istanbul ignore next */ []);
|
|
36
|
-
service =
|
|
35
|
+
service = apiResource({
|
|
37
36
|
params: () => this.id(),
|
|
38
37
|
stream: ({ params }) => this.api.resource(params)
|
|
39
38
|
});
|
|
40
|
-
topology =
|
|
39
|
+
topology = apiResource({
|
|
41
40
|
params: () => this.ctx.environment(),
|
|
42
41
|
stream: ({ params }) => this.api.topology(params || null)
|
|
43
42
|
});
|
|
44
|
-
operations =
|
|
43
|
+
operations = apiResource({
|
|
45
44
|
params: () => ({
|
|
46
45
|
id: this.id(),
|
|
47
46
|
env: this.ctx.environment(),
|
|
@@ -724,4 +723,4 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
724
723
|
});
|
|
725
724
|
export { ServicePage };
|
|
726
725
|
|
|
727
|
-
//# sourceMappingURL=juice-js-observability-service.page-
|
|
726
|
+
//# sourceMappingURL=juice-js-observability-service.page-Qc6FYChB.mjs.map
|