@juice-js/observability 1.0.0-alpha.1 → 1.0.0-alpha.11
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 +33 -1
- package/fesm2022/{juice-js-observability-alert.page-D2OL0zTT.mjs → juice-js-observability-alert.page-TKwqJT2d.mjs} +3 -4
- package/fesm2022/juice-js-observability-alert.page-TKwqJT2d.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-alerts.page-BLJvD_op.mjs → juice-js-observability-alerts.page-B-2uS9wr.mjs} +6 -3
- package/fesm2022/{juice-js-observability-alerts.page-BLJvD_op.mjs.map → juice-js-observability-alerts.page-B-2uS9wr.mjs.map} +1 -1
- package/fesm2022/{juice-js-observability-logs.page-BWFLleyX.mjs → juice-js-observability-logs.page-CZNbTl2Q.mjs} +3 -4
- package/fesm2022/juice-js-observability-logs.page-CZNbTl2Q.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-metrics.page-D1caPbQq.mjs → juice-js-observability-metrics.page-DoVED9MT.mjs} +4 -5
- package/fesm2022/juice-js-observability-metrics.page-DoVED9MT.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-overview.page-UBwHjlyq.mjs → juice-js-observability-overview.page-Cod7-cIy.mjs} +49 -13
- package/fesm2022/juice-js-observability-overview.page-Cod7-cIy.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-resource.page-BZwLK_vU.mjs → juice-js-observability-resource.page-C8RhUipF.mjs} +4 -5
- package/fesm2022/juice-js-observability-resource.page-C8RhUipF.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-resources.page-C0xWOb5I.mjs → juice-js-observability-resources.page-KadGxEAh.mjs} +3 -4
- package/fesm2022/juice-js-observability-resources.page-KadGxEAh.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-rules.page-Db_U5rRu.mjs → juice-js-observability-rules.page-DKWvNpu9.mjs} +3 -4
- package/fesm2022/juice-js-observability-rules.page-DKWvNpu9.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-service.page-B-SWJ9I5.mjs → juice-js-observability-service.page-_yK0kVYS.mjs} +7 -8
- package/fesm2022/juice-js-observability-service.page-_yK0kVYS.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-services.page-BmWgFnC8.mjs → juice-js-observability-services.page-BtG8zyiI.mjs} +3 -4
- package/fesm2022/juice-js-observability-services.page-BtG8zyiI.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-telemetry-VA4_3nzr.mjs → juice-js-observability-telemetry-B5jMJYfn.mjs} +73 -20
- package/fesm2022/juice-js-observability-telemetry-B5jMJYfn.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-topology.page-DEUNp10U.mjs → juice-js-observability-topology.page-B7aXWOYV.mjs} +97 -69
- package/fesm2022/juice-js-observability-topology.page-B7aXWOYV.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-trace.page-gBhMG2eQ.mjs → juice-js-observability-trace.page-CkLNNnKf.mjs} +16 -18
- package/fesm2022/juice-js-observability-trace.page-CkLNNnKf.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-traces.page-BCQg4V4o.mjs → juice-js-observability-traces.page-DgvNixIc.mjs} +7 -8
- package/fesm2022/juice-js-observability-traces.page-DgvNixIc.mjs.map +1 -0
- package/fesm2022/juice-js-observability.mjs +25 -16
- package/fesm2022/juice-js-observability.mjs.map +1 -1
- package/package.json +1 -1
- package/styles/_observability.scss +216 -129
- package/types/juice-js-observability.d.ts +14 -1
- 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
|
@@ -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-B5jMJYfn.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(),
|
|
@@ -251,7 +250,7 @@ var ServicePage = class ServicePage {
|
|
|
251
250
|
@if (tabIndex() === 1) {
|
|
252
251
|
@if (operations.isLoading()) { <mat-progress-bar mode="indeterminate" /> }
|
|
253
252
|
<table class="jc-table">
|
|
254
|
-
<thead><tr><th>Operation</th><th>Kind</th><th>Requests</th><th>
|
|
253
|
+
<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>
|
|
255
254
|
<tbody>
|
|
256
255
|
@for (o of operations.value()?.items ?? []; track o.name + o.kind) {
|
|
257
256
|
<tr [class.jc-selected]="o.name === operation()">
|
|
@@ -585,7 +584,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
585
584
|
@if (tabIndex() === 1) {
|
|
586
585
|
@if (operations.isLoading()) { <mat-progress-bar mode="indeterminate" /> }
|
|
587
586
|
<table class="jc-table">
|
|
588
|
-
<thead><tr><th>Operation</th><th>Kind</th><th>Requests</th><th>
|
|
587
|
+
<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>
|
|
589
588
|
<tbody>
|
|
590
589
|
@for (o of operations.value()?.items ?? []; track o.name + o.kind) {
|
|
591
590
|
<tr [class.jc-selected]="o.name === operation()">
|
|
@@ -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-_yK0kVYS.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"juice-js-observability-service.page-_yK0kVYS.mjs","names":[],"sources":["../../../../projects/juice-js/observability/src/lib/pages/service.page.ts"],"sourcesContent":["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';\n\nconst TABS = ['overview', 'operations', 'dependencies', 'traces', 'logs', 'metrics'] 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, 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> <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 </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 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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"}
|
|
@@ -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, s as screenPages, u as HealthBadge, x as apiResource } from "./juice-js-observability-telemetry-B5jMJYfn.mjs";
|
|
3
3
|
import * as i0 from "@angular/core";
|
|
4
4
|
import { ChangeDetectionStrategy, Component, computed, inject, input, signal } 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 i2 from "@angular/material/progress-bar";
|
|
9
8
|
import { MatProgressBarModule } from "@angular/material/progress-bar";
|
|
@@ -24,7 +23,7 @@ var ServicesPage = class ServicesPage {
|
|
|
24
23
|
filter = signal("", ...ngDevMode ? [{ debugName: "filter" }] : /* istanbul ignore next */ []);
|
|
25
24
|
reasonPipe = new ReasonPipe();
|
|
26
25
|
reason = (r) => this.reasonPipe.transform(r);
|
|
27
|
-
data =
|
|
26
|
+
data = apiResource({ stream: () => forkJoin({
|
|
28
27
|
services: this.api.resources("service"),
|
|
29
28
|
health: this.api.health({ subjectType: "service" })
|
|
30
29
|
}).pipe(map(({ services, health }) => ({
|
|
@@ -249,4 +248,4 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
249
248
|
});
|
|
250
249
|
export { ServicesPage };
|
|
251
250
|
|
|
252
|
-
//# sourceMappingURL=juice-js-observability-services.page-
|
|
251
|
+
//# sourceMappingURL=juice-js-observability-services.page-BtG8zyiI.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"juice-js-observability-services.page-BtG8zyiI.mjs","names":[],"sources":["../../../../projects/juice-js/observability/src/lib/pages/services.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 { forkJoin, map } from 'rxjs';\nimport { ApiService } from '../core/api.service';\nimport { ContextService } from '../core/context.service';\nimport { Health, HealthState } from '../core/models';\nimport { AgoPipe, ReasonPipe } from '../shared/format';\nimport { ContextBar, HealthBadge } from '../shared/ui';\nimport { problemText } from '../shared/telemetry';\nimport { screenPages } from '../shared/paging';\n\nconst SEVERITY: HealthState[] = ['Critical', 'Unhealthy', 'Degraded', 'Offline', 'Unknown', 'Healthy'];\n\n/**\n * The service catalog (Console requirements §8): one row per service, environment-independent, with its health in\n * each environment. The toolbar search box filters it.\n */\n@Component({\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [LinkPipe, RouterLink, MatPaginatorModule, MatProgressBarModule, ContextBar, HealthBadge, AgoPipe],\n template: `\n <jc-context-bar><h1>Services</h1>\n @if (state()) { <span class=\"jc-chip\">{{ state() }} <a [routerLink]=\"['services'] | jcLink\" [queryParams]=\"ctx.keep()\">×</a></span> }\n </jc-context-bar>\n @if (data.isLoading()) { <mat-progress-bar mode=\"indeterminate\" /> }\n @if (data.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>Service</th><th>Health by environment</th><th>Why</th><th>Last seen</th></tr></thead>\n <tbody>\n @for (s of pages.visible(); track s.id) {\n <tr>\n <td><a [routerLink]=\"['services', s.id] | jcLink\" [queryParams]=\"ctx.keep()\">{{ s.key }}</a></td>\n <td class=\"jc-envs\">\n @for (h of s.health; track h.environment) {\n <span><span class=\"jc-muted jc-small\">{{ h.environment ?? '—' }}</span> <jc-health [state]=\"h.state\" [stale]=\"h.stale\" /></span>\n } @empty { <jc-health [state]=\"null\" /> }\n </td>\n <td class=\"jc-small\">{{ s.worst?.reasons?.slice(0, 2)?.map(reason)?.join(' · ') }}</td>\n <td>{{ s.lastSeen | ago }}</td>\n </tr>\n } @empty { <tr><td colspan=\"4\" class=\"jc-muted\">No services{{ filter() ? ' match \"' + filter() + '\"' : '' }}.</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 ServicesPage implements SearchablePage {\n protected readonly ctx = inject(ContextService);\n private readonly api = inject(ApiService);\n readonly state = input<string>();\n protected readonly filter = signal('');\n private readonly reasonPipe = new ReasonPipe();\n protected readonly reason = (r: string) => this.reasonPipe.transform(r);\n\n protected readonly data = apiResource({\n stream: () => forkJoin({\n services: this.api.resources('service'),\n health: this.api.health({ subjectType: 'service' }),\n }).pipe(map(({ services, health }) => ({\n services: services.items, health: health.items, capped: services.nextCursor !== null || health.nextCursor !== null,\n }))),\n });\n protected readonly capped = computed(() => this.data.value()?.capped ?? false);\n protected readonly error = computed(() => problemText(this.data.error()));\n\n protected readonly rows = computed(() => {\n const data = this.data.value();\n if (!data) return [];\n const env = this.ctx.environment();\n const byService = new Map<string, Health[]>();\n for (const h of data.health) byService.set(h.subjectId, [...(byService.get(h.subjectId) ?? []), h]);\n const q = this.filter().toLowerCase();\n return data.services\n .map(s => {\n const health = (byService.get(s.id) ?? []).filter(h => !env || h.environment === env);\n const worst = [...health].sort((a, b) => SEVERITY.indexOf(a.state) - SEVERITY.indexOf(b.state))[0];\n return { ...s, health, worst };\n })\n // With an environment chosen, only the services seen in it.\n .filter(s => !env || s.health.length > 0)\n .filter(s => !q || s.key.toLowerCase().includes(q))\n .filter(s => !this.state() || s.health.some(h => h.state === this.state()))\n .sort((a, b) => SEVERITY.indexOf(a.worst?.state ?? 'Unknown') - SEVERITY.indexOf(b.worst?.state ?? 'Unknown') || a.key.localeCompare(b.key));\n });\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,6 +1,6 @@
|
|
|
1
1
|
import { ApiService, ContextService, LinkPipe, MAX_SEARCH_MS, ObservabilityLinks, RANGES } from "./juice-js-observability.mjs";
|
|
2
2
|
import * as i0 from "@angular/core";
|
|
3
|
-
import { ChangeDetectionStrategy, Component, DestroyRef, Pipe, afterNextRender, computed, effect, inject, input, linkedSignal, signal, viewChild } from "@angular/core";
|
|
3
|
+
import { ChangeDetectionStrategy, Component, DestroyRef, Pipe, afterNextRender, computed, effect, inject, input, isSignal, linkedSignal, signal, viewChild } from "@angular/core";
|
|
4
4
|
import { catchError, map, of } from "rxjs";
|
|
5
5
|
import { rxResource, takeUntilDestroyed, toSignal } from "@angular/core/rxjs-interop";
|
|
6
6
|
import { Router, RouterLink } from "@angular/router";
|
|
@@ -15,6 +15,15 @@ import * as i3 from "@angular/material/icon";
|
|
|
15
15
|
import { MatIconModule } from "@angular/material/icon";
|
|
16
16
|
import * as i2$1 from "@angular/material/select";
|
|
17
17
|
import { MatSelectModule } from "@angular/material/select";
|
|
18
|
+
function apiResource(options) {
|
|
19
|
+
const ref = rxResource(options);
|
|
20
|
+
const value = computed(() => ref.hasValue() ? ref.value() : void 0, ...ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []);
|
|
21
|
+
return new Proxy(ref, { get(target, prop) {
|
|
22
|
+
if (prop === "value") return value;
|
|
23
|
+
const member = Reflect.get(target, prop, target);
|
|
24
|
+
return typeof member === "function" && !isSignal(member) ? member.bind(target) : member;
|
|
25
|
+
} });
|
|
26
|
+
}
|
|
18
27
|
function preciseTime(iso) {
|
|
19
28
|
const fraction = /\.\d{3}(\d+)/.exec(iso)?.[1];
|
|
20
29
|
return Date.parse(iso) + (fraction ? Number(`0.${fraction}`) : 0);
|
|
@@ -170,6 +179,21 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
170
179
|
args: [{ name: "reason" }]
|
|
171
180
|
}]
|
|
172
181
|
});
|
|
182
|
+
function themeColor(name, element = document.body) {
|
|
183
|
+
return getComputedStyle(element).getPropertyValue(`--jc-${name}`).trim();
|
|
184
|
+
}
|
|
185
|
+
function seriesColors(element = document.body) {
|
|
186
|
+
const count = Number(themeColor("series-count", element)) || 0;
|
|
187
|
+
return Array.from({ length: count }, (_, i) => themeColor(`series-${i + 1}`, element)).filter(Boolean);
|
|
188
|
+
}
|
|
189
|
+
function onThemeChange(callback) {
|
|
190
|
+
const observer = new MutationObserver(callback);
|
|
191
|
+
for (const element of [document.documentElement, document.body]) observer.observe(element, {
|
|
192
|
+
attributes: true,
|
|
193
|
+
attributeFilter: ["class"]
|
|
194
|
+
});
|
|
195
|
+
return () => observer.disconnect();
|
|
196
|
+
}
|
|
173
197
|
var HealthBadge = class HealthBadge {
|
|
174
198
|
state = input(...ngDevMode ? [void 0, { debugName: "state" }] : /* istanbul ignore next */ []);
|
|
175
199
|
stale = input(false, ...ngDevMode ? [{ debugName: "stale" }] : /* istanbul ignore next */ []);
|
|
@@ -654,6 +678,7 @@ var Chart = class Chart {
|
|
|
654
678
|
empty = computed(() => this.series().every((s) => s.points.every((p) => p.value === null)), ...ngDevMode ? [{ debugName: "empty" }] : /* istanbul ignore next */ []);
|
|
655
679
|
chart;
|
|
656
680
|
observer;
|
|
681
|
+
stopThemeWatch;
|
|
657
682
|
constructor() {
|
|
658
683
|
afterNextRender(async () => {
|
|
659
684
|
const [echarts, charts, components, renderers] = await Promise.all([
|
|
@@ -672,6 +697,7 @@ var Chart = class Chart {
|
|
|
672
697
|
this.chart = echarts.init(this.host().nativeElement, void 0, { renderer: "svg" });
|
|
673
698
|
this.observer = new ResizeObserver(() => this.chart?.resize());
|
|
674
699
|
this.observer.observe(this.host().nativeElement);
|
|
700
|
+
this.stopThemeWatch = onThemeChange(() => this.render());
|
|
675
701
|
this.render();
|
|
676
702
|
});
|
|
677
703
|
effect(() => {
|
|
@@ -682,9 +708,16 @@ var Chart = class Chart {
|
|
|
682
708
|
render() {
|
|
683
709
|
if (!this.chart) return;
|
|
684
710
|
const format = this.format();
|
|
685
|
-
const
|
|
711
|
+
const host = this.host().nativeElement;
|
|
712
|
+
const [text, muted, border, colors] = [
|
|
713
|
+
themeColor("text", host),
|
|
714
|
+
themeColor("muted", host),
|
|
715
|
+
themeColor("border", host),
|
|
716
|
+
seriesColors(host)
|
|
717
|
+
];
|
|
686
718
|
const option = {
|
|
687
719
|
animation: false,
|
|
720
|
+
color: colors.length ? colors : void 0,
|
|
688
721
|
grid: {
|
|
689
722
|
left: 56,
|
|
690
723
|
right: 16,
|
|
@@ -697,19 +730,20 @@ var Chart = class Chart {
|
|
|
697
730
|
},
|
|
698
731
|
legend: this.series().length > 1 ? {
|
|
699
732
|
top: 0,
|
|
700
|
-
textStyle: { color:
|
|
733
|
+
textStyle: { color: text }
|
|
701
734
|
} : void 0,
|
|
702
735
|
xAxis: {
|
|
703
736
|
type: "time",
|
|
704
|
-
axisLabel: { color:
|
|
737
|
+
axisLabel: { color: muted },
|
|
738
|
+
axisLine: { lineStyle: { color: border } }
|
|
705
739
|
},
|
|
706
740
|
yAxis: {
|
|
707
741
|
type: "value",
|
|
708
742
|
axisLabel: {
|
|
709
|
-
color:
|
|
743
|
+
color: muted,
|
|
710
744
|
formatter: (v) => format(v)
|
|
711
745
|
},
|
|
712
|
-
splitLine: { lineStyle: { color:
|
|
746
|
+
splitLine: { lineStyle: { color: border } }
|
|
713
747
|
},
|
|
714
748
|
series: this.series().map((s) => ({
|
|
715
749
|
name: s.name,
|
|
@@ -723,6 +757,7 @@ var Chart = class Chart {
|
|
|
723
757
|
}
|
|
724
758
|
ngOnDestroy() {
|
|
725
759
|
this.observer?.disconnect();
|
|
760
|
+
this.stopThemeWatch?.();
|
|
726
761
|
this.chart?.dispose();
|
|
727
762
|
}
|
|
728
763
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
@@ -826,7 +861,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
826
861
|
});
|
|
827
862
|
function cursorList(params, query, fetch) {
|
|
828
863
|
const destroyRef = inject(DestroyRef);
|
|
829
|
-
const first =
|
|
864
|
+
const first = apiResource({
|
|
830
865
|
params,
|
|
831
866
|
stream: ({ params }) => {
|
|
832
867
|
const q = query(params);
|
|
@@ -926,6 +961,9 @@ var TraceTable = class TraceTable {
|
|
|
926
961
|
}), (q) => this.api.traces(q));
|
|
927
962
|
rows = this.traces.items;
|
|
928
963
|
longest = computed(() => Math.max(1, ...this.rows().map((t) => t.durationMs)), ...ngDevMode ? [{ debugName: "longest" }] : /* istanbul ignore next */ []);
|
|
964
|
+
barWidth(ms) {
|
|
965
|
+
return Math.max(2, 80 * ms / this.longest());
|
|
966
|
+
}
|
|
929
967
|
error = computed(() => problemText(this.traces.first.error()), ...ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []);
|
|
930
968
|
moreError = computed(() => problemText(this.traces.moreError()), ...ngDevMode ? [{ debugName: "moreError" }] : /* istanbul ignore next */ []);
|
|
931
969
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
@@ -973,7 +1011,7 @@ var TraceTable = class TraceTable {
|
|
|
973
1011
|
<td class="jc-nowrap">{{ t.startedAt | date: 'MMM d, HH:mm:ss.SSS' }}</td>
|
|
974
1012
|
<td><a [routerLink]="['traces', t.traceId] | jcLink" [queryParams]="ctx.keep()">{{ t.rootService }} · {{ t.rootOperation }}</a>
|
|
975
1013
|
@if (t.services.length > 1) { <div class="jc-muted jc-small">{{ t.services.join(' → ') }}</div> }</td>
|
|
976
|
-
<td class="jc-nowrap"><span class="jc-bar" [style.width
|
|
1014
|
+
<td class="jc-nowrap"><span class="jc-bar" [style.width.px]="barWidth(t.durationMs)"></span>{{ t.durationMs | duration }}</td>
|
|
977
1015
|
<td>{{ t.spanCount }}</td>
|
|
978
1016
|
<td>@if (t.errorCount > 0) { <span class="jc-badge jc-health-unhealthy">{{ t.errorCount }} error{{ t.errorCount > 1 ? 's' : '' }}</span> }
|
|
979
1017
|
@else { <span class="jc-badge jc-health-healthy">ok</span> }</td>
|
|
@@ -1091,7 +1129,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
1091
1129
|
<td class="jc-nowrap">{{ t.startedAt | date: 'MMM d, HH:mm:ss.SSS' }}</td>
|
|
1092
1130
|
<td><a [routerLink]="['traces', t.traceId] | jcLink" [queryParams]="ctx.keep()">{{ t.rootService }} · {{ t.rootOperation }}</a>
|
|
1093
1131
|
@if (t.services.length > 1) { <div class="jc-muted jc-small">{{ t.services.join(' → ') }}</div> }</td>
|
|
1094
|
-
<td class="jc-nowrap"><span class="jc-bar" [style.width
|
|
1132
|
+
<td class="jc-nowrap"><span class="jc-bar" [style.width.px]="barWidth(t.durationMs)"></span>{{ t.durationMs | duration }}</td>
|
|
1095
1133
|
<td>{{ t.spanCount }}</td>
|
|
1096
1134
|
<td>@if (t.errorCount > 0) { <span class="jc-badge jc-health-unhealthy">{{ t.errorCount }} error{{ t.errorCount > 1 ? 's' : '' }}</span> }
|
|
1097
1135
|
@else { <span class="jc-badge jc-health-healthy">ok</span> }</td>
|
|
@@ -1355,7 +1393,7 @@ var MetricExplorer = class MetricExplorer {
|
|
|
1355
1393
|
api = inject(ApiService);
|
|
1356
1394
|
scope = input({}, ...ngDevMode ? [{ debugName: "scope" }] : /* istanbul ignore next */ []);
|
|
1357
1395
|
initial = input(null, ...ngDevMode ? [{ debugName: "initial" }] : /* istanbul ignore next */ []);
|
|
1358
|
-
metrics =
|
|
1396
|
+
metrics = apiResource({
|
|
1359
1397
|
params: () => ({
|
|
1360
1398
|
scope: this.scope(),
|
|
1361
1399
|
env: this.ctx.environment(),
|
|
@@ -1373,7 +1411,7 @@ var MetricExplorer = class MetricExplorer {
|
|
|
1373
1411
|
const names = this.names();
|
|
1374
1412
|
return names.find((m) => m.name === this.initial())?.name ?? names[0]?.name ?? null;
|
|
1375
1413
|
}, ...ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []);
|
|
1376
|
-
series =
|
|
1414
|
+
series = apiResource({
|
|
1377
1415
|
params: () => this.selected() ? {
|
|
1378
1416
|
name: this.selected(),
|
|
1379
1417
|
scope: this.scope(),
|
|
@@ -1449,8 +1487,8 @@ var MetricExplorer = class MetricExplorer {
|
|
|
1449
1487
|
@for (m of names(); track m.name) { <mat-option [value]="m.name">{{ m.name }} <span class="jc-muted">({{ m.type }}{{ m.unit ? ', ' + m.unit : '' }})</span></mat-option> }
|
|
1450
1488
|
</mat-select>
|
|
1451
1489
|
</mat-form-field>
|
|
1452
|
-
@if (series.
|
|
1453
|
-
<jc-chart [title]="title()" [series]="[{ name:
|
|
1490
|
+
@if (series.value(); as s) {
|
|
1491
|
+
<jc-chart [title]="title()" [series]="[{ name: s.name, points: points() }]" />
|
|
1454
1492
|
}
|
|
1455
1493
|
}
|
|
1456
1494
|
}`,
|
|
@@ -1589,8 +1627,8 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
1589
1627
|
@for (m of names(); track m.name) { <mat-option [value]="m.name">{{ m.name }} <span class="jc-muted">({{ m.type }}{{ m.unit ? ', ' + m.unit : '' }})</span></mat-option> }
|
|
1590
1628
|
</mat-select>
|
|
1591
1629
|
</mat-form-field>
|
|
1592
|
-
@if (series.
|
|
1593
|
-
<jc-chart [title]="title()" [series]="[{ name:
|
|
1630
|
+
@if (series.value(); as s) {
|
|
1631
|
+
<jc-chart [title]="title()" [series]="[{ name: s.name, points: points() }]" />
|
|
1594
1632
|
}
|
|
1595
1633
|
}
|
|
1596
1634
|
}`
|
|
@@ -1625,7 +1663,7 @@ var ServiceStatsCharts = class ServiceStatsCharts {
|
|
|
1625
1663
|
};
|
|
1626
1664
|
percent = (v) => `${Math.round(v * 1e3) / 10} %`;
|
|
1627
1665
|
ms = (v) => `${Math.round(v)} ms`;
|
|
1628
|
-
stats =
|
|
1666
|
+
stats = apiResource({
|
|
1629
1667
|
params: () => ({
|
|
1630
1668
|
query: this.query(),
|
|
1631
1669
|
env: this.ctx.environment(),
|
|
@@ -1641,6 +1679,21 @@ var ServiceStatsCharts = class ServiceStatsCharts {
|
|
|
1641
1679
|
});
|
|
1642
1680
|
}
|
|
1643
1681
|
});
|
|
1682
|
+
rates() {
|
|
1683
|
+
const points = this.stats.value()?.points ?? [];
|
|
1684
|
+
const requests = {
|
|
1685
|
+
title: "Requests per second",
|
|
1686
|
+
name: "requests/s",
|
|
1687
|
+
points: this.pick("requestsPerSecond")
|
|
1688
|
+
};
|
|
1689
|
+
const messages = {
|
|
1690
|
+
title: "Messages per second",
|
|
1691
|
+
name: "messages/s",
|
|
1692
|
+
points: this.pick("messagesPerSecond")
|
|
1693
|
+
};
|
|
1694
|
+
if (!points.some((p) => p.messagesPerSecond > 0)) return [requests];
|
|
1695
|
+
return points.some((p) => p.requestsPerSecond > 0) ? [requests, messages] : [messages];
|
|
1696
|
+
}
|
|
1644
1697
|
pick(field) {
|
|
1645
1698
|
const s = this.stats.value();
|
|
1646
1699
|
return s ? withGaps(s.points.map((p) => ({
|
|
@@ -1674,7 +1727,7 @@ var ServiceStatsCharts = class ServiceStatsCharts {
|
|
|
1674
1727
|
@if (stats.isLoading()) { <mat-progress-bar mode="indeterminate" /> }
|
|
1675
1728
|
@if (stats.hasValue()) {
|
|
1676
1729
|
<div class="jc-grid-3">
|
|
1677
|
-
<jc-chart title="Requests per second" [series]="
|
|
1730
|
+
<jc-chart [title]="rates().length > 1 ? 'Requests and messages per second' : rates()[0].title" [series]="rates()" />
|
|
1678
1731
|
<jc-chart title="Error rate" [series]="[{ name: 'errors', points: pick('errorRate') }]" [format]="percent" />
|
|
1679
1732
|
<jc-chart title="p95 latency" [series]="[{ name: 'p95', points: pick('p95Ms') }]" [format]="ms" />
|
|
1680
1733
|
</div>
|
|
@@ -1727,7 +1780,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
1727
1780
|
@if (stats.isLoading()) { <mat-progress-bar mode="indeterminate" /> }
|
|
1728
1781
|
@if (stats.hasValue()) {
|
|
1729
1782
|
<div class="jc-grid-3">
|
|
1730
|
-
<jc-chart title="Requests per second" [series]="
|
|
1783
|
+
<jc-chart [title]="rates().length > 1 ? 'Requests and messages per second' : rates()[0].title" [series]="rates()" />
|
|
1731
1784
|
<jc-chart title="Error rate" [series]="[{ name: 'errors', points: pick('errorRate') }]" [format]="percent" />
|
|
1732
1785
|
<jc-chart title="p95 latency" [series]="[{ name: 'p95', points: pick('p95Ms') }]" [format]="ms" />
|
|
1733
1786
|
</div>
|
|
@@ -1743,6 +1796,6 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
1743
1796
|
}]
|
|
1744
1797
|
}] }
|
|
1745
1798
|
});
|
|
1746
|
-
export {
|
|
1799
|
+
export { ReasonPipe as _, problemText as a, preciseTime as b, ConfidenceBadge as c, KeyValueTable as d, onThemeChange as f, PercentPipe as g, DurationPipe as h, TraceTable as i, ContextBar as l, AgoPipe as m, MetricExplorer as n, cursorList as o, themeColor as p, ServiceStatsCharts as r, screenPages as s, LogTable as t, HealthBadge as u, formatDuration as v, apiResource as x, plainTraceId as y };
|
|
1747
1800
|
|
|
1748
|
-
//# sourceMappingURL=juice-js-observability-telemetry-
|
|
1801
|
+
//# sourceMappingURL=juice-js-observability-telemetry-B5jMJYfn.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"juice-js-observability-telemetry-B5jMJYfn.mjs","names":["i1","i2","i3"],"sources":["../../../../projects/juice-js/observability/src/lib/shared/resource.ts","../../../../projects/juice-js/observability/src/lib/shared/format.ts","../../../../projects/juice-js/observability/src/lib/shared/theme.ts","../../../../projects/juice-js/observability/src/lib/shared/ui.ts","../../../../projects/juice-js/observability/src/lib/shared/paging.ts","../../../../projects/juice-js/observability/src/lib/shared/telemetry.ts"],"sourcesContent":["import { ResourceRef, Signal, computed, isSignal } from '@angular/core';\nimport { RxResourceOptions, rxResource } from '@angular/core/rxjs-interop';\n\n/** A resource whose `value()` is `undefined` while its request failed, instead of throwing. */\nexport type ApiResource<T> = Omit<ResourceRef<T | undefined>, 'value'> & { readonly value: Signal<T | undefined> };\n\n/**\n * An `rxResource` for an API read. Angular's `value()` throws while the resource is in error, so a failed request (a\n * trace not found, a 500, the API unreachable) would break the page's rendering, and with it every overlay such as a\n * select's options. Here `value()` reads `undefined` then, and `error()` says why.\n */\nexport function apiResource<T, P>(options: RxResourceOptions<T, P>): ApiResource<T> {\n const ref = rxResource<T, P>(options);\n const value = computed(() => (ref.hasValue() ? ref.value() : undefined));\n return new Proxy(ref, {\n get(target, prop) {\n if (prop === 'value') return value;\n const member = Reflect.get(target, prop, target);\n return typeof member === 'function' && !isSignal(member) ? member.bind(target) : member;\n },\n }) as unknown as ApiResource<T>;\n}\n","import { Pipe, PipeTransform } from '@angular/core';\n\n/** Milliseconds since the epoch of an ISO 8601 time, keeping the sub-millisecond digits that Date.parse drops. */\nexport function preciseTime(iso: string): number {\n const fraction = /\\.\\d{3}(\\d+)/.exec(iso)?.[1];\n return Date.parse(iso) + (fraction ? Number(`0.${fraction}`) : 0);\n}\n\n/** The 32-hex trace id of a W3C traceparent (\"00-<trace id>-<span id>-01\"); any other text unchanged but trimmed and lower-cased. */\nexport function plainTraceId(text: string): string {\n const lower = text.trim().toLowerCase();\n return /^[0-9a-f]{2}-([0-9a-f]{32})-[0-9a-f]{16}-[0-9a-f]{2}$/.exec(lower)?.[1] ?? lower;\n}\n\n/** 0.8 ms, 120 ms, 1.42 s, 2 m 05 s. */\nexport function formatDuration(ms: number | null | undefined): string {\n if (ms === null || ms === undefined || Number.isNaN(ms)) return '—';\n if (ms < 1) return `${ms.toFixed(2)} ms`;\n if (ms < 1000) return `${Math.round(ms * 10) / 10} ms`;\n if (ms < 60_000) return `${(ms / 1000).toFixed(2)} s`;\n const s = Math.round(ms / 1000);\n return `${Math.floor(s / 60)} m ${String(s % 60).padStart(2, '0')} s`;\n}\n\nexport function formatPercent(rate: number | null | undefined): string {\n if (rate === null || rate === undefined) return '—';\n const p = rate * 100;\n return `${p === 0 ? '0' : p < 0.1 ? '<0.1' : p.toFixed(p < 10 ? 1 : 0)} %`;\n}\n\nexport function formatAgo(iso: string | null | undefined): string {\n if (!iso) return '—';\n const s = Math.round((Date.now() - new Date(iso).getTime()) / 1000);\n if (s < 60) return `${Math.max(s, 0)} s ago`;\n if (s < 3600) return `${Math.round(s / 60)} min ago`;\n if (s < 86_400) return `${Math.round(s / 3600)} h ago`;\n return `${Math.round(s / 86_400)} d ago`;\n}\n\n@Pipe({ name: 'duration' })\nexport class DurationPipe implements PipeTransform {\n transform(ms: number | null | undefined) { return formatDuration(ms); }\n}\n\n@Pipe({ name: 'percent2' })\nexport class PercentPipe implements PipeTransform {\n transform(rate: number | null | undefined) { return formatPercent(rate); }\n}\n\n@Pipe({ name: 'ago' })\nexport class AgoPipe implements PipeTransform {\n transform(iso: string | null | undefined) { return formatAgo(iso); }\n}\n\n/** A reason code such as `dependency_failed:shop/payments` as words. */\n@Pipe({ name: 'reason' })\nexport class ReasonPipe implements PipeTransform {\n transform(code: string) {\n const [what, subject] = code.split(/:(.*)/s);\n const text = what.replace(/_/g, ' ');\n return subject ? `${text}: ${subject}` : text;\n }\n}\n","/**\n * The app's colours, as `observability.colors($theme)` wrote them in `--jc-*` custom properties, for what CSS cannot\n * colour itself: the charts (ECharts) and the dependency graph (Cytoscape) draw on their own.\n */\nexport function themeColor(name: string, element: Element = document.body): string {\n return getComputedStyle(element).getPropertyValue(`--jc-${name}`).trim();\n}\n\n/** The categorical colours (`$series`): services in a trace, lines in a chart. */\nexport function seriesColors(element: Element = document.body): string[] {\n const count = Number(themeColor('series-count', element)) || 0;\n return Array.from({ length: count }, (_, i) => themeColor(`series-${i + 1}`, element)).filter(Boolean);\n}\n\n/**\n * Calls back when the app switches theme: a class change on `<html>` or `<body>`, as juice-layout's dark-mode toggle\n * makes. Returns the function that stops listening.\n */\nexport function onThemeChange(callback: () => void): () => void {\n const observer = new MutationObserver(callback);\n for (const element of [document.documentElement, document.body]) {\n observer.observe(element, { attributes: true, attributeFilter: ['class'] });\n }\n return () => observer.disconnect();\n}\n","import { ObservabilityLinks } from '../core/options';\nimport {\n ChangeDetectionStrategy, Component, ElementRef, OnDestroy, afterNextRender, computed, effect, inject, input, signal, viewChild,\n} from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { FormsModule } from '@angular/forms';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatIconModule } from '@angular/material/icon';\nimport { MatSelectModule } from '@angular/material/select';\nimport { Router } from '@angular/router';\nimport { catchError, map, of } from 'rxjs';\nimport type { ECharts, EChartsOption } from 'echarts';\nimport { ApiService } from '../core/api.service';\nimport { ContextService, RANGES } from '../core/context.service';\nimport { HealthState } from '../core/models';\nimport { onThemeChange, seriesColors, themeColor } from './theme';\n\n/** A health state, with \"no data\" for a missing one (Console requirements §10: missing data is not zero). */\n@Component({\n selector: 'jc-health',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `<span class=\"jc-badge\" [class]=\"'jc-health-' + (state() ?? 'none').toLowerCase()\"\n [attr.title]=\"stale() ? 'Evaluation has stopped: this state may be out of date' : null\">\n {{ state() ?? 'No data' }}@if (stale()) {<span class=\"jc-stale\"> · stale</span>}</span>`,\n})\nexport class HealthBadge {\n readonly state = input<HealthState | null | undefined>();\n readonly stale = input(false);\n}\n\n/** Observed or inferred dependency evidence (Console requirements §9): inferred is never shown as confirmed. */\n@Component({\n selector: 'jc-confidence',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `<span class=\"jc-badge\" [class.jc-observed]=\"observed()\" [class.jc-inferred]=\"!observed()\"\n [attr.title]=\"observed() ? 'Seen in traces: a call from one service to the other' : 'Inferred from span attributes, not a confirmed call'\">\n {{ observed() ? 'observed' : 'inferred' }}</span>`,\n})\nexport class ConfidenceBadge {\n readonly confidence = input.required<string>();\n protected readonly observed = computed(() => this.confidence().toLowerCase() === 'observed');\n}\n\n/** Attributes as a key/value table, sorted, values wrapped. */\n@Component({\n selector: 'jc-kv',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (entries().length === 0) { <p class=\"jc-muted\">{{ empty() }}</p> }\n @else {\n <table class=\"jc-kv\"><tbody>\n @for (e of entries(); track e[0]) { <tr><th>{{ e[0] }}</th><td>{{ e[1] }}</td></tr> }\n </tbody></table>\n }`,\n})\nexport class KeyValueTable {\n readonly values = input<Record<string, string | null | undefined>>({});\n readonly empty = input('None');\n protected readonly entries = computed(() =>\n Object.entries(this.values()).filter((e): e is [string, string] => e[1] !== null && e[1] !== undefined && e[1] !== '')\n .sort((a, b) => a[0].localeCompare(b[0])));\n}\n\n/**\n * The context bar on every page (Console requirements §13, §17): environment, time range, and a global lookup for a\n * trace id, a span id or a service name.\n */\n@Component({\n selector: 'jc-context-bar',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormsModule, MatSelectModule, MatIconModule, MatButtonModule],\n template: `\n <div class=\"jc-context\">\n <ng-content />\n <span class=\"jc-spacer\"></span>\n <mat-form-field subscriptSizing=\"dynamic\" class=\"jc-field-sm\">\n <mat-label>Environment</mat-label>\n <mat-select [value]=\"ctx.environment()\" (valueChange)=\"ctx.set({ env: $event || null })\">\n <mat-option value=\"\">All</mat-option>\n @for (e of environments(); track e.id) { <mat-option [value]=\"e.key\">{{ e.key }}</mat-option> }\n </mat-select>\n </mat-form-field>\n <mat-form-field subscriptSizing=\"dynamic\" class=\"jc-field-sm\">\n <mat-label>Time range</mat-label>\n <mat-select [value]=\"ctx.range()\" (valueChange)=\"ctx.set({ range: $event })\">\n @for (r of ranges; track r) { <mat-option [value]=\"r\">Last {{ r }}</mat-option> }\n @if (ctx.range() === 'custom') { <mat-option value=\"custom\" disabled>Custom window</mat-option> }\n </mat-select>\n </mat-form-field>\n <form class=\"jc-lookup\" (ngSubmit)=\"lookup()\">\n <mat-icon>search</mat-icon>\n <input name=\"q\" [(ngModel)]=\"query\" placeholder=\"Trace ID, span ID or service\" aria-label=\"Go to trace, span or service\">\n </form>\n </div>\n @if (message()) { <p class=\"jc-muted jc-lookup-message\">{{ message() }}</p> }`,\n})\nexport class ContextBar {\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 protected readonly ranges = Object.keys(RANGES);\n protected readonly environments = toSignal(this.api.environments().pipe(map(p => p.items), catchError(() => of([]))), { initialValue: [] });\n protected query = '';\n protected readonly message = signal('');\n\n lookup() {\n const q = this.query.trim();\n if (!q) return;\n this.message.set('Searching…');\n this.api.search(q).subscribe({\n next: page => {\n const hit = page.items[0];\n if (!hit) { this.message.set(`Nothing found for \"${q}\".`); return; }\n this.message.set(page.items.length > 1 ? `${page.items.length} matches; showing the first.` : '');\n const extra = hit.kind === 'span' ? { span: hit.id } : {};\n void (hit.kind === 'service'\n ? this.router.navigate(this.links.to('services', hit.id), { queryParams: this.ctx.keep() })\n : this.router.navigate(this.links.to('traces', hit.traceId), { queryParams: this.ctx.keep(extra) }));\n },\n error: () => this.message.set('Search failed.'),\n });\n }\n}\n\nexport interface ChartSeries { name: string; points: { at: string; value: number | null }[]; unit?: string; }\n\n/**\n * The API returns no point for a bucket without data; give each missing bucket a null so the chart shows a gap\n * instead of drawing a line across it (no data is not zero).\n */\nexport function withGaps(points: { at: string; value: number | null }[], from: string, to: string, stepSeconds: number) {\n const step = stepSeconds * 1000;\n const byTime = new Map(points.map(p => [new Date(p.at).getTime(), p.value]));\n const out: { at: string; value: number | null }[] = [];\n for (let t = Math.floor(new Date(from).getTime() / step) * step; t < new Date(to).getTime(); t += step) {\n out.push({ at: new Date(t).toISOString(), value: byTime.get(t) ?? null });\n }\n return out;\n}\n\n/**\n * A time series chart (Apache ECharts, loaded on first use). A missing bucket is a gap in the line, never zero.\n */\n@Component({\n selector: 'jc-chart',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `<div class=\"jc-chart-title\">{{ title() }}</div>\n @if (empty()) { <div class=\"jc-chart jc-chart-empty\">No data in this window</div> }\n <div #host class=\"jc-chart\" [hidden]=\"empty()\"></div>`,\n})\nexport class Chart implements OnDestroy {\n readonly title = input('');\n readonly series = input<ChartSeries[]>([]);\n readonly format = input<(v: number) => string>(v => String(Math.round(v * 100) / 100));\n private readonly host = viewChild.required<ElementRef<HTMLDivElement>>('host');\n protected readonly empty = computed(() => this.series().every(s => s.points.every(p => p.value === null)));\n private chart?: ECharts;\n private observer?: ResizeObserver;\n private stopThemeWatch?: () => void;\n\n constructor() {\n afterNextRender(async () => {\n // Only what a line chart needs: the full library is three times the size.\n const [echarts, charts, components, renderers] = await Promise.all([\n import('echarts/core'), import('echarts/charts'), import('echarts/components'), import('echarts/renderers')]);\n echarts.use([charts.LineChart, components.GridComponent, components.TooltipComponent, components.LegendComponent, renderers.SVGRenderer]);\n this.chart = echarts.init(this.host().nativeElement, undefined, { renderer: 'svg' });\n this.observer = new ResizeObserver(() => this.chart?.resize());\n this.observer.observe(this.host().nativeElement);\n this.stopThemeWatch = onThemeChange(() => this.render());\n this.render();\n });\n effect(() => { this.series(); this.render(); });\n }\n\n private render() {\n if (!this.chart) return;\n const format = this.format();\n // The app's theme colours (observability.colors), read at each render so a theme switch repaints.\n const host = this.host().nativeElement;\n const [text, muted, border, colors] = [themeColor('text', host), themeColor('muted', host), themeColor('border', host), seriesColors(host)];\n const option: EChartsOption = {\n animation: false,\n color: colors.length ? colors : undefined,\n grid: { left: 56, right: 16, top: 16, bottom: 28 },\n tooltip: { trigger: 'axis', valueFormatter: v => (typeof v === 'number' ? format(v) : '—') },\n legend: this.series().length > 1 ? { top: 0, textStyle: { color: text } } : undefined,\n xAxis: { type: 'time', axisLabel: { color: muted }, axisLine: { lineStyle: { color: border } } },\n yAxis: { type: 'value', axisLabel: { color: muted, formatter: (v: number) => format(v) },\n splitLine: { lineStyle: { color: border } } },\n series: this.series().map(s => ({\n name: s.name, type: 'line', showSymbol: false, connectNulls: false,\n data: s.points.map(p => [p.at, p.value]),\n })),\n };\n this.chart.setOption(option, true);\n }\n\n ngOnDestroy() {\n this.observer?.disconnect();\n this.stopThemeWatch?.();\n this.chart?.dispose();\n }\n}\n","import { DestroyRef, Signal, computed, inject, linkedSignal, signal } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { apiResource } from './resource';\nimport { PageEvent } from '@angular/material/paginator';\nimport { Observable, map } from 'rxjs';\nimport { Query } from '../core/api.service';\n\n/** A page from an API list that pages with a cursor: alerts, traces, logs. */\nexport interface CursorPage<T> { items: T[]; nextCursor?: string | null; }\n\n/**\n * A newest-first list read page by page with the API's cursor. The first page loads whenever `params` change; `more()`\n * appends the next page, asked with exactly the query of the first one (its time window included), so the list grows\n * without shifting, skipping or repeating rows. Call it in an injection context, such as a field initializer.\n */\nexport function cursorList<P, T>(params: () => P, query: (params: P) => Query, fetch: (query: Query) => Observable<CursorPage<T>>) {\n const destroyRef = inject(DestroyRef);\n const first = apiResource({\n params,\n stream: ({ params }) => {\n const q = query(params);\n return fetch(q).pipe(map(page => ({ page, query: q })));\n },\n });\n // Pages after the first; reset whenever the first page reloads.\n const rest = linkedSignal({ source: first.value, computation: () => ({ items: [] as T[], cursor: undefined as string | null | undefined }) });\n const loadingMore = signal(false);\n const moreError = signal<unknown>(null);\n const items = computed(() => [...(first.value()?.page.items ?? []), ...rest().items]);\n const cursor = computed(() => rest().cursor !== undefined ? rest().cursor! : first.value()?.page.nextCursor ?? null);\n\n function more() {\n const current = first.value();\n const next = cursor();\n if (!current || !next || loadingMore()) return;\n loadingMore.set(true);\n moreError.set(null);\n fetch({ ...current.query, cursor: next }).pipe(takeUntilDestroyed(destroyRef)).subscribe({\n next: page => rest.update(r => ({ items: [...r.items, ...page.items], cursor: page.nextCursor ?? null })),\n error: e => { moreError.set(e); loadingMore.set(false); },\n complete: () => loadingMore.set(false),\n });\n }\n\n return { first, items, hasMore: computed(() => cursor() !== null), loadingMore, moreError: moreError.asReadonly(), more };\n}\n\n/**\n * Pages a list already in memory for display (the inventory lists, which the Console sorts and filters itself). Goes\n * back to the first page whenever the rows change, for example when a filter narrows them.\n */\nexport function screenPages<T>(rows: Signal<T[]>, sizes = [50, 100, 200]) {\n const size = signal(sizes[0]);\n const index = linkedSignal({ source: rows, computation: () => 0 });\n return {\n sizes,\n size: size.asReadonly(),\n index: index.asReadonly(),\n visible: computed(() => rows().slice(index() * size(), (index() + 1) * size())),\n /** More rows than one page: show the paginator. */\n needed: computed(() => rows().length > sizes[0]),\n change(event: PageEvent) { size.set(event.pageSize); index.set(event.pageIndex); },\n };\n}\n","import { LinkPipe } from '../core/options';\nimport { ChangeDetectionStrategy, Component, computed, inject, input, linkedSignal } from '@angular/core';\nimport { apiResource } from './resource';\nimport { DatePipe } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatIconModule } from '@angular/material/icon';\nimport { MatProgressBarModule } from '@angular/material/progress-bar';\nimport { MatSelectModule } from '@angular/material/select';\nimport { RouterLink } from '@angular/router';\nimport { ApiService, Query } from '../core/api.service';\nimport { ContextService, MAX_SEARCH_MS } from '../core/context.service';\nimport { DurationPipe } from './format';\nimport { cursorList } from './paging';\nimport { Chart, withGaps } from './ui';\n\n/** Shows a request's failure as a short line instead of a broken page. */\nexport function problemText(error: unknown): string {\n const e = error as { status?: number; error?: { title?: string; detail?: string } } | undefined;\n if (!e) return '';\n if (e.status === 0) return 'The Juice API cannot be reached.';\n return [e.error?.title, e.error?.detail].filter(Boolean).join(': ') || `Request failed (${e.status}).`;\n}\n\n/**\n * Traces matching a query in the current context, newest first (Console requirements §5): enough per row to pick a\n * useful one, and one click to its waterfall.\n */\n@Component({\n selector: 'jc-trace-table',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [LinkPipe, RouterLink, DatePipe, DurationPipe, MatProgressBarModule, MatButtonModule],\n template: `\n @if (traces.first.isLoading()) { <mat-progress-bar mode=\"indeterminate\" /> }\n @if (traces.first.error()) { <p class=\"jc-error\">{{ error() }}</p> }\n @if (traces.first.hasValue()) {\n @if (rows().length === 0) { <p class=\"jc-muted\">No traces match in this window.</p> }\n @else {\n <table class=\"jc-table\">\n <thead><tr><th>Start</th><th>Root operation</th><th>Duration</th><th>Spans</th><th>Status</th><th>Instance</th></tr></thead>\n <tbody>\n @for (t of rows(); track t.traceId) {\n <tr>\n <td class=\"jc-nowrap\">{{ t.startedAt | date: 'MMM d, HH:mm:ss.SSS' }}</td>\n <td><a [routerLink]=\"['traces', t.traceId] | jcLink\" [queryParams]=\"ctx.keep()\">{{ t.rootService }} · {{ t.rootOperation }}</a>\n @if (t.services.length > 1) { <div class=\"jc-muted jc-small\">{{ t.services.join(' → ') }}</div> }</td>\n <td class=\"jc-nowrap\"><span class=\"jc-bar\" [style.width.px]=\"barWidth(t.durationMs)\"></span>{{ t.durationMs | duration }}</td>\n <td>{{ t.spanCount }}</td>\n <td>@if (t.errorCount > 0) { <span class=\"jc-badge jc-health-unhealthy\">{{ t.errorCount }} error{{ t.errorCount > 1 ? 's' : '' }}</span> }\n @else { <span class=\"jc-badge jc-health-healthy\">ok</span> }</td>\n <td class=\"jc-muted jc-small\">{{ t.rootInstance ?? t.rootHost ?? '—' }}</td>\n </tr>\n }\n </tbody>\n </table>\n <div class=\"jc-more\">\n <span class=\"jc-muted\">{{ rows().length }} trace{{ rows().length === 1 ? '' : 's' }}{{ traces.hasMore() ? ', newest first' : '' }}</span>\n @if (traces.hasMore()) {\n <button mat-button (click)=\"traces.more()\" [disabled]=\"traces.loadingMore()\">{{ traces.loadingMore() ? 'Loading…' : 'Older traces' }}</button>\n }\n @if (traces.moreError()) { <span class=\"jc-error\">{{ moreError() }}</span> }\n </div>\n }\n }`,\n})\nexport class TraceTable {\n protected readonly ctx = inject(ContextService);\n private readonly api = inject(ApiService);\n readonly query = input<Query>({});\n readonly limit = input(50);\n\n /** \"Older traces\" appends the next page of the same search, over the same window. */\n protected readonly traces = cursorList(\n () => ({ query: this.query(), env: this.ctx.environment(), range: this.ctx.range(), abs: this.ctx.absolute(), limit: this.limit() }),\n p => ({ environment: p.env, ...this.ctx.window(MAX_SEARCH_MS), limit: p.limit, ...p.query }),\n q => this.api.traces(q));\n protected readonly rows = this.traces.items;\n protected readonly longest = computed(() => Math.max(1, ...this.rows().map(t => t.durationMs)));\n /** Pixels, not a percentage: an auto-sized cell ignores a percentage width, so a full bar pushed the time into the next column. */\n protected barWidth(ms: number) { return Math.max(2, 80 * ms / this.longest()); }\n protected readonly error = computed(() => problemText(this.traces.first.error()));\n protected readonly moreError = computed(() => problemText(this.traces.moreError()));\n}\n\n/**\n * Log records for a query in the current context (Console requirements §7): each with its trace and span, when the\n * record carries them.\n */\n@Component({\n selector: 'jc-log-table',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [LinkPipe, RouterLink, DatePipe, MatProgressBarModule, MatButtonModule],\n template: `\n @if (logs.first.isLoading()) { <mat-progress-bar mode=\"indeterminate\" /> }\n @if (logs.first.error()) { <p class=\"jc-error\">{{ error() }}</p> }\n @if (logs.first.hasValue()) {\n @if (rows().length === 0) { <p class=\"jc-muted\">No log records match in this window.</p> }\n @else {\n <table class=\"jc-table jc-logs\">\n <thead><tr><th>Time</th><th>Severity</th>@if (!compact()) { <th>Service</th> }<th>Message</th>@if (!compact()) { <th>Trace</th> }</tr></thead>\n <tbody>\n @for (l of rows(); track $index) {\n <tr>\n <td class=\"jc-nowrap\">{{ l.timestamp | date: 'MMM d, HH:mm:ss.SSS' }}</td>\n <td><span class=\"jc-badge\" [class]=\"severityClass(l.severityNumber)\">{{ l.severity || l.severityNumber }}</span></td>\n @if (!compact()) { <td class=\"jc-nowrap\">{{ l.service }}@if (l.instance) {<div class=\"jc-muted jc-small\">{{ l.instance }}</div>}</td> }\n <td><pre class=\"jc-body\">{{ l.body }}@if (l.bodyTruncated) {…}</pre></td>\n @if (!compact()) { <td class=\"jc-nowrap\">\n @if (l.traceId) {\n <a [routerLink]=\"['traces', l.traceId] | jcLink\" [queryParams]=\"ctx.keep(l.spanId ? { span: l.spanId } : {})\">{{ l.spanId ? 'span' : 'trace' }} ↗</a>\n } @else { <span class=\"jc-muted\">—</span> }\n </td> }\n </tr>\n }\n </tbody>\n </table>\n <div class=\"jc-more\">\n <span class=\"jc-muted\">{{ rows().length }} record{{ rows().length === 1 ? '' : 's' }}{{ logs.hasMore() ? ', newest first' : '' }}</span>\n @if (logs.hasMore()) {\n <button mat-button (click)=\"logs.more()\" [disabled]=\"logs.loadingMore()\">{{ logs.loadingMore() ? 'Loading…' : 'Older logs' }}</button>\n }\n @if (logs.moreError()) { <span class=\"jc-error\">{{ moreError() }}</span> }\n </div>\n }\n }`,\n})\nexport class LogTable {\n protected readonly ctx = inject(ContextService);\n private readonly api = inject(ApiService);\n readonly query = input<Query>({});\n /** Inside a span: its service and trace are already on screen, so only time, severity and message. */\n readonly compact = input(false);\n\n /** \"Older logs\" appends the next page of the same search, over the same window. */\n protected readonly logs = cursorList(\n () => ({ query: this.query(), env: this.ctx.environment(), range: this.ctx.range(), abs: this.ctx.absolute() }),\n p => ({ environment: p.env, ...this.ctx.window(MAX_SEARCH_MS), limit: 200, ...p.query }),\n q => this.api.logs(q));\n protected readonly rows = this.logs.items;\n protected readonly error = computed(() => problemText(this.logs.first.error()));\n protected readonly moreError = computed(() => problemText(this.logs.moreError()));\n\n severityClass(n: number) {\n return n >= 17 ? 'jc-health-unhealthy' : n >= 13 ? 'jc-health-degraded' : n >= 9 ? 'jc-health-healthy' : 'jc-health-none';\n }\n}\n\n/**\n * The metrics reported for a scope (service, instance or host) and a chart of the one selected (Console requirements\n * §7): a counter as a rate, a gauge or histogram as an average.\n */\n@Component({\n selector: 'jc-metric-explorer',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormsModule, MatSelectModule, MatProgressBarModule, Chart],\n template: `\n @if (metrics.isLoading()) { <mat-progress-bar mode=\"indeterminate\" /> }\n @if (metrics.error()) { <p class=\"jc-error\">{{ metricsError() }}</p> }\n @if (metrics.hasValue()) {\n @if (names().length === 0) { <p class=\"jc-muted\">No metrics reported in this window.</p> }\n @else {\n <mat-form-field subscriptSizing=\"dynamic\" class=\"jc-field-wide\">\n <mat-label>Metric</mat-label>\n <mat-select [value]=\"selected()\" (valueChange)=\"selected.set($event)\">\n @for (m of names(); track m.name) { <mat-option [value]=\"m.name\">{{ m.name }} <span class=\"jc-muted\">({{ m.type }}{{ m.unit ? ', ' + m.unit : '' }})</span></mat-option> }\n </mat-select>\n </mat-form-field>\n @if (series.value(); as s) {\n <jc-chart [title]=\"title()\" [series]=\"[{ name: s.name, points: points() }]\" />\n }\n }\n }`,\n})\nexport class MetricExplorer {\n private readonly ctx = inject(ContextService);\n private readonly api = inject(ApiService);\n readonly scope = input<Query>({});\n readonly initial = input<string | null>(null);\n\n protected readonly metrics = apiResource({\n params: () => ({ scope: this.scope(), env: this.ctx.environment(), range: this.ctx.range(), abs: this.ctx.absolute() }),\n stream: ({ params }) => this.api.metrics({ environment: params.env, ...this.ctx.window(), ...params.scope }),\n });\n protected readonly names = computed(() => this.metrics.value()?.items ?? []);\n protected readonly selected = linkedSignal(() => {\n const names = this.names();\n return names.find(m => m.name === this.initial())?.name ?? names[0]?.name ?? null;\n });\n protected readonly series = apiResource({\n // Idle until a metric is chosen.\n params: () => this.selected() ? { name: this.selected()!, scope: this.scope(), env: this.ctx.environment(), range: this.ctx.range(), abs: this.ctx.absolute() } : undefined,\n stream: ({ params }) => {\n this.window = this.ctx.window();\n return this.api.series({ name: params.name, environment: params.env, ...this.window, ...params.scope });\n },\n });\n private window = { from: '', to: '' };\n protected readonly points = computed(() => {\n const s = this.series.value();\n return s ? withGaps(s.points, this.window.from, this.window.to, s.stepSeconds) : [];\n });\n protected readonly title = computed(() => {\n const s = this.series.value();\n if (!s) return '';\n const what = s.aggregation === 'rate' ? 'rate per second' : 'average';\n return `${s.name} — ${what}${s.unit ? ` (${s.unit}${s.aggregation === 'rate' ? '/s' : ''})` : ''}`;\n });\n protected readonly metricsError = computed(() => problemText(this.metrics.error()));\n}\n\n/** Requests, error rate and p95 of a service (or one operation) over the window. */\n@Component({\n selector: 'jc-service-stats',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [Chart, MatProgressBarModule],\n template: `\n @if (stats.isLoading()) { <mat-progress-bar mode=\"indeterminate\" /> }\n @if (stats.hasValue()) {\n <div class=\"jc-grid-3\">\n <jc-chart [title]=\"rates().length > 1 ? 'Requests and messages per second' : rates()[0].title\" [series]=\"rates()\" />\n <jc-chart title=\"Error rate\" [series]=\"[{ name: 'errors', points: pick('errorRate') }]\" [format]=\"percent\" />\n <jc-chart title=\"p95 latency\" [series]=\"[{ name: 'p95', points: pick('p95Ms') }]\" [format]=\"ms\" />\n </div>\n }`,\n})\nexport class ServiceStatsCharts {\n private readonly ctx = inject(ContextService);\n private readonly api = inject(ApiService);\n readonly query = input.required<Query>();\n private window = { from: '', to: '' };\n protected readonly percent = (v: number) => `${Math.round(v * 1000) / 10} %`;\n protected readonly ms = (v: number) => `${Math.round(v)} ms`;\n\n protected readonly stats = apiResource({\n params: () => ({ query: this.query(), env: this.ctx.environment(), range: this.ctx.range(), abs: this.ctx.absolute() }),\n stream: ({ params }) => {\n this.window = this.ctx.window();\n return this.api.serviceStats({ environment: params.env, ...this.window, ...params.query });\n },\n });\n\n /** Requests and messages are separate lines; a service with only one of them shows only that one. */\n rates() {\n const points = this.stats.value()?.points ?? [];\n const requests = { title: 'Requests per second', name: 'requests/s', points: this.pick('requestsPerSecond') };\n const messages = { title: 'Messages per second', name: 'messages/s', points: this.pick('messagesPerSecond') };\n if (!points.some(p => p.messagesPerSecond > 0)) return [requests];\n return points.some(p => p.requestsPerSecond > 0) ? [requests, messages] : [messages];\n }\n\n pick(field: 'requestsPerSecond' | 'messagesPerSecond' | 'errorRate' | 'p95Ms') {\n const s = this.stats.value();\n return s ? withGaps(s.points.map(p => ({ at: p.at, value: p[field] })), this.window.from, this.window.to, s.stepSeconds) : [];\n }\n}\n\n"],"mappings":"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"}
|