@juice-js/observability 1.0.0-alpha.8 → 1.0.0-alpha.9
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/fesm2022/{juice-js-observability-alert.page-D4_SWIUp.mjs → juice-js-observability-alert.page-TKwqJT2d.mjs} +2 -2
- package/fesm2022/{juice-js-observability-alert.page-D4_SWIUp.mjs.map → juice-js-observability-alert.page-TKwqJT2d.mjs.map} +1 -1
- package/fesm2022/{juice-js-observability-alerts.page-JKYZvFNx.mjs → juice-js-observability-alerts.page-B-2uS9wr.mjs} +6 -3
- package/fesm2022/{juice-js-observability-alerts.page-JKYZvFNx.mjs.map → juice-js-observability-alerts.page-B-2uS9wr.mjs.map} +1 -1
- package/fesm2022/{juice-js-observability-logs.page-CDxYz7V0.mjs → juice-js-observability-logs.page-CZNbTl2Q.mjs} +2 -2
- package/fesm2022/{juice-js-observability-logs.page-CDxYz7V0.mjs.map → juice-js-observability-logs.page-CZNbTl2Q.mjs.map} +1 -1
- package/fesm2022/{juice-js-observability-metrics.page-B9bNEGFX.mjs → juice-js-observability-metrics.page-DoVED9MT.mjs} +2 -2
- package/fesm2022/{juice-js-observability-metrics.page-B9bNEGFX.mjs.map → juice-js-observability-metrics.page-DoVED9MT.mjs.map} +1 -1
- package/fesm2022/{juice-js-observability-overview.page-DRsqxm7f.mjs → juice-js-observability-overview.page-Cod7-cIy.mjs} +9 -3
- package/fesm2022/{juice-js-observability-overview.page-DRsqxm7f.mjs.map → juice-js-observability-overview.page-Cod7-cIy.mjs.map} +1 -1
- package/fesm2022/{juice-js-observability-resource.page-BwnjrE7Z.mjs → juice-js-observability-resource.page-C8RhUipF.mjs} +2 -2
- package/fesm2022/{juice-js-observability-resource.page-BwnjrE7Z.mjs.map → juice-js-observability-resource.page-C8RhUipF.mjs.map} +1 -1
- package/fesm2022/{juice-js-observability-resources.page-BoEADhVE.mjs → juice-js-observability-resources.page-KadGxEAh.mjs} +2 -2
- package/fesm2022/{juice-js-observability-resources.page-BoEADhVE.mjs.map → juice-js-observability-resources.page-KadGxEAh.mjs.map} +1 -1
- package/fesm2022/{juice-js-observability-rules.page-BGdNA_XR.mjs → juice-js-observability-rules.page-DKWvNpu9.mjs} +2 -2
- package/fesm2022/{juice-js-observability-rules.page-BGdNA_XR.mjs.map → juice-js-observability-rules.page-DKWvNpu9.mjs.map} +1 -1
- package/fesm2022/{juice-js-observability-service.page-JstbjLbF.mjs → juice-js-observability-service.page-_yK0kVYS.mjs} +4 -4
- package/fesm2022/juice-js-observability-service.page-_yK0kVYS.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-services.page-RdDusSXl.mjs → juice-js-observability-services.page-BtG8zyiI.mjs} +2 -2
- package/fesm2022/{juice-js-observability-services.page-RdDusSXl.mjs.map → juice-js-observability-services.page-BtG8zyiI.mjs.map} +1 -1
- package/fesm2022/{juice-js-observability-telemetry-gYN3UogH.mjs → juice-js-observability-telemetry-B5jMJYfn.mjs} +18 -3
- package/fesm2022/{juice-js-observability-telemetry-gYN3UogH.mjs.map → juice-js-observability-telemetry-B5jMJYfn.mjs.map} +1 -1
- package/fesm2022/{juice-js-observability-topology.page-CChcM0n-.mjs → juice-js-observability-topology.page-M_kitXna.mjs} +2 -2
- package/fesm2022/{juice-js-observability-topology.page-CChcM0n-.mjs.map → juice-js-observability-topology.page-M_kitXna.mjs.map} +1 -1
- package/fesm2022/{juice-js-observability-trace.page-DE84DHal.mjs → juice-js-observability-trace.page-CkLNNnKf.mjs} +2 -2
- package/fesm2022/{juice-js-observability-trace.page-DE84DHal.mjs.map → juice-js-observability-trace.page-CkLNNnKf.mjs.map} +1 -1
- package/fesm2022/{juice-js-observability-traces.page-C-i5ZJqJ.mjs → juice-js-observability-traces.page-DgvNixIc.mjs} +2 -2
- package/fesm2022/{juice-js-observability-traces.page-C-i5ZJqJ.mjs.map → juice-js-observability-traces.page-DgvNixIc.mjs.map} +1 -1
- package/fesm2022/juice-js-observability.mjs +13 -13
- package/package.json +1 -1
- package/types/juice-js-observability.d.ts +1 -0
- package/fesm2022/juice-js-observability-service.page-JstbjLbF.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"juice-js-observability-trace.page-DE84DHal.mjs","names":[],"sources":["../../../../projects/juice-js/observability/src/lib/pages/trace.page.ts"],"sourcesContent":["import { LinkPipe, ObservabilityLinks } from '../core/options';\nimport { ChangeDetectionStrategy, Component, computed, inject, input, signal } from '@angular/core';\nimport { apiResource } from '../shared/resource';\nimport { DatePipe } from '@angular/common';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatIconModule } from '@angular/material/icon';\nimport { MatProgressBarModule } from '@angular/material/progress-bar';\nimport { Router, RouterLink } from '@angular/router';\nimport { ApiService } from '../core/api.service';\nimport { ContextService } from '../core/context.service';\nimport { Span } from '../core/models';\nimport { DurationPipe, formatDuration, plainTraceId, preciseTime } from '../shared/format';\nimport { ContextBar, KeyValueTable } from '../shared/ui';\nimport { LogTable, problemText } from '../shared/telemetry';\nimport { themeColor } from '../shared/theme';\n\ninterface Row { span: Span; depth: number; children: number; offset: number; selfError: boolean; slowest: boolean; }\n\n/** A stable colour per service, from a small palette that reads in both themes. */\n\n/**\n * A trace as a waterfall (Console requirements §6): the span hierarchy with timing relative to the trace start, so\n * parallel calls show side by side; errors and the slowest spans marked; subtrees collapsible. The selected span is\n * in the URL (`span`), with its details, its logs, and links to its service, its dependency, and the metrics and logs\n * around it (§7).\n */\n@Component({\n changeDetection: ChangeDetectionStrategy.OnPush,\n // Fills the window; the waterfall and the span scroll on their own (styles.scss).\n host: { class: 'jc-fill-page' },\n imports: [LinkPipe, RouterLink, DatePipe, MatButtonModule, MatIconModule, MatProgressBarModule, ContextBar, KeyValueTable, LogTable, DurationPipe],\n template: `\n <jc-context-bar>\n <a [routerLink]=\"['traces'] | jcLink\" [queryParams]=\"ctx.keep()\" class=\"jc-crumb\">Traces</a>\n <span class=\"jc-crumb-sep\">›</span>\n <h1 class=\"jc-mono\">{{ id() }}</h1>\n </jc-context-bar>\n @if (trace.isLoading()) { <mat-progress-bar mode=\"indeterminate\" /> }\n @if (trace.error()) { <p class=\"jc-error\">{{ error() }}</p> }\n\n @if (trace.value(); as t) {\n <div class=\"jc-summary\">\n <span><strong>{{ root()?.service }}</strong> · {{ root()?.name }}</span>\n <span>{{ start() | date: 'MMM d, HH:mm:ss.SSS' }}</span>\n <span>{{ total() | duration }}</span>\n <span>{{ t.spans.length }} spans · {{ services().length }} services</span>\n @if (errors() > 0) { <span class=\"jc-badge jc-health-unhealthy\">{{ errors() }} failed</span> }\n <button mat-button (click)=\"showErrorsOnly.set(!showErrorsOnly())\">{{ showErrorsOnly() ? 'Show all spans' : 'Error paths only' }}</button>\n <a mat-button [routerLink]=\"['logs'] | jcLink\" [queryParams]=\"ctx.keep({ trace: id() })\">Logs of this trace</a>\n @if (auditLink(); as link) {\n @if ('route' in link) { <a mat-button [routerLink]=\"link.route\" data-testid=\"audit-link\">Audit records ↗</a> }\n @else { <a mat-button [href]=\"link.href\" target=\"_blank\" rel=\"noopener\" data-testid=\"audit-link\">Audit records ↗</a> }\n }\n @if (t.truncated) { <span class=\"jc-muted\">Showing the first {{ t.spans.length }} spans.</span> }\n </div>\n <div class=\"jc-legend\">@for (s of services(); track s) { <span><i [style.background]=\"color(s)\"></i>{{ s }}</span> }</div>\n\n <div class=\"jc-trace\" [class.jc-with-detail]=\"selected()\">\n <div class=\"jc-waterfall\" role=\"tree\">\n <div class=\"jc-wf-head\"><span>Span</span><span class=\"jc-wf-scale\">\n @for (tick of ticks(); track tick.at) { <span [style.left.%]=\"tick.at\">{{ tick.label }}</span> }</span></div>\n @for (r of visible(); track r.span.spanId) {\n <div class=\"jc-wf-row\" role=\"treeitem\" [class.jc-selected]=\"r.span.spanId === selectedId()\"\n [class.jc-wf-error]=\"r.selfError\" (click)=\"select(r.span.spanId)\" tabindex=\"0\" (keydown.enter)=\"select(r.span.spanId)\">\n <span class=\"jc-wf-name\" [style.padding-left.px]=\"r.depth * 14\">\n @if (r.children > 0) {\n <button class=\"jc-wf-toggle\" (click)=\"toggle(r.span.spanId, $event)\" [attr.aria-label]=\"collapsed().has(r.span.spanId) ? 'Expand' : 'Collapse'\">\n {{ collapsed().has(r.span.spanId) ? '▸' : '▾' }}</button>\n } @else { <span class=\"jc-wf-toggle\"></span> }\n <i class=\"jc-dot\" [style.background]=\"color(r.span.service)\"></i>\n <span class=\"jc-wf-service\">{{ r.span.service }}</span> <span>{{ r.span.name }}</span>\n @if (r.selfError) { <mat-icon class=\"jc-wf-icon jc-bad\" title=\"Failed\">error</mat-icon> }\n @if (r.slowest) { <mat-icon class=\"jc-wf-icon\" title=\"Among the slowest spans\">schedule</mat-icon> }\n @if (collapsed().has(r.span.spanId)) { <span class=\"jc-muted jc-small\">+{{ r.children }}</span> }\n </span>\n <span class=\"jc-wf-track\">\n <span class=\"jc-wf-bar\" [style.left.%]=\"r.offset\" [style.width.%]=\"width(r.span)\" [style.background]=\"color(r.span.service)\"></span>\n <span class=\"jc-wf-time\" [style.left.%]=\"r.offset\">{{ r.span.durationMs | duration }}</span>\n </span>\n </div>\n }\n </div>\n\n @if (selected(); as s) {\n <aside class=\"jc-card jc-span-detail\">\n <div class=\"jc-row\"><h2>{{ s.name }}</h2><button mat-icon-button (click)=\"select(null)\" aria-label=\"Close\"><mat-icon>close</mat-icon></button></div>\n <div class=\"jc-span-facts\">\n <span class=\"jc-badge\" [class.jc-health-unhealthy]=\"s.status === 'Error'\" [class.jc-health-healthy]=\"s.status === 'Ok'\">{{ s.status }}</span>\n <span>{{ s.kind }}</span><span>{{ s.durationMs | duration }}</span>\n <span>at +{{ offsetMs(s) | duration }}</span>\n </div>\n @if (s.statusMessage) { <p class=\"jc-error\">{{ s.statusMessage }}</p> }\n\n <nav class=\"jc-actions\">\n @if (s.serviceId) { <a mat-stroked-button [routerLink]=\"['services', s.serviceId] | jcLink\" [queryParams]=\"ctx.keep()\">Service {{ s.service }}</a> }\n @if (s.target; as target) {\n <a mat-stroked-button [routerLink]=\"(target.kind === 'service' ? ['services', target.id] : ['resources', target.id]) | jcLink\" [queryParams]=\"ctx.keep()\">\n {{ target.kind === 'service' ? 'Calls' : 'Uses' }} {{ target.name }}</a>\n }\n @if (s.serviceId) {\n <a mat-stroked-button [routerLink]=\"['services', s.serviceId] | jcLink\" [queryParams]=\"aroundSpan(s, { tab: 'metrics' })\">Metrics around</a>\n <a mat-stroked-button [routerLink]=\"['services', s.serviceId] | jcLink\" [queryParams]=\"aroundSpan(s, { tab: 'logs' })\">Service logs around</a>\n <a mat-stroked-button [routerLink]=\"['services', s.serviceId] | jcLink\" [queryParams]=\"ctx.keep({ tab: 'traces', op: s.name })\">Other traces of {{ s.name }}</a>\n }\n </nav>\n\n <h3>Logs of this span</h3>\n <jc-log-table [query]=\"spanLogQuery()\" [compact]=\"true\" />\n\n <h3>Span</h3>\n <jc-kv [values]=\"{ 'service': s.service, 'environment': s.environment, 'instance': s.instance, 'host': s.host,\n 'trace id': id(), 'span id': s.spanId, 'parent span id': s.parentSpanId, 'started': s.startedAt,\n 'instrumentation scope': s.scope ? s.scope + (s.scopeVersion ? ' ' + s.scopeVersion : '') : null }\" />\n <h3>Attributes</h3>\n <jc-kv [values]=\"s.attributes\" empty=\"No attributes.\" />\n @if (s.events.length) {\n <h3>Events</h3>\n @for (e of s.events; track $index) {\n <div class=\"jc-event\" [class.jc-wf-error]=\"e.name === 'exception'\">\n <strong>{{ e.name }}</strong> <span class=\"jc-muted\">+{{ eventOffset(s, e.at) | duration }}</span>\n <jc-kv [values]=\"e.attributes\" empty=\"\" />\n </div>\n }\n }\n @if (s.links.length) {\n <h3>Links</h3>\n @for (l of s.links; track l.spanId) {\n <a [routerLink]=\"['traces', l.traceId] | jcLink\" [queryParams]=\"ctx.keep({ span: l.spanId })\" class=\"jc-mono\">{{ l.traceId }} / {{ l.spanId }}</a>\n }\n }\n </aside>\n }\n </div>\n }`,\n})\nexport class TracePage {\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 /** A trace id, or a W3C traceparent such as a Juice.Audit record before 10.4 holds; links from either open the trace. */\n readonly traceId = input.required<string>();\n readonly span = input<string>();\n\n protected readonly id = computed(() => plainTraceId(this.traceId()));\n protected readonly trace = apiResource({ params: () => this.id(), stream: ({ params }) => this.api.trace(params) });\n protected readonly error = computed(() => problemText(this.trace.error()));\n /** The host's audit records of this trace (`auditLink`): the requests it served and the data they changed. */\n protected readonly auditLink = computed(() => this.links.audit(this.id()));\n protected readonly collapsed = signal(new Set<string>());\n protected readonly showErrorsOnly = signal(false);\n protected readonly selectedId = computed(() => this.span() ?? null);\n\n private readonly spans = computed(() => this.trace.value()?.spans ?? []);\n // Sub-millisecond precision: spans of a fast in-process flow start microseconds apart.\n protected readonly start = computed(() => Math.min(...this.spans().map(s => preciseTime(s.startedAt))));\n protected readonly total = computed(() =>\n Math.max(0.001, Math.max(...this.spans().map(s => preciseTime(s.startedAt) + s.durationMs)) - this.start()));\n protected readonly root = computed(() => this.spans().find(s => !s.parentSpanId) ?? this.spans()[0]);\n protected readonly services = computed(() => [...new Set(this.spans().map(s => s.service))]);\n protected readonly errors = computed(() => this.spans().filter(s => s.status === 'Error').length);\n protected readonly selected = computed(() => this.spans().find(s => s.spanId === this.selectedId()) ?? null);\n protected readonly spanLogQuery = computed(() => {\n const s = this.selected();\n return s ? { spanId: s.spanId, ...this.windowAround(s) } : {};\n });\n\n /** Depth-first order with depth; spans whose parent is missing are roots. Children start-ordered. */\n private readonly rows = computed<Row[]>(() => {\n const spans = this.spans();\n const ids = new Set(spans.map(s => s.spanId));\n const byParent = new Map<string | null, Span[]>();\n for (const s of spans) {\n const parent = s.parentSpanId && ids.has(s.parentSpanId) ? s.parentSpanId : null;\n byParent.set(parent, [...(byParent.get(parent) ?? []), s]);\n }\n const slowest = new Set([...spans].sort((a, b) => b.durationMs - a.durationMs).slice(0, Math.max(1, Math.ceil(spans.length * 0.05))).map(s => s.spanId));\n const rows: Row[] = [];\n const walk = (parent: string | null, depth: number) => {\n for (const s of (byParent.get(parent) ?? []).sort((a, b) => a.startedAt.localeCompare(b.startedAt))) {\n rows.push({\n span: s, depth, children: byParent.get(s.spanId)?.length ?? 0,\n offset: 100 * (preciseTime(s.startedAt) - this.start()) / this.total(),\n selfError: s.status === 'Error', slowest: slowest.has(s.spanId) && spans.length > 3,\n });\n walk(s.spanId, depth + 1);\n }\n };\n walk(null, 0);\n return rows;\n });\n\n /** Hides collapsed subtrees; \"error paths only\" keeps failed spans and their ancestors. */\n protected readonly visible = computed(() => {\n const rows = this.rows();\n const parentOf = new Map(this.spans().map(s => [s.spanId, s.parentSpanId]));\n let keep: Set<string> | null = null;\n if (this.showErrorsOnly()) {\n keep = new Set<string>();\n for (const r of rows.filter(r => r.selfError)) {\n for (let id: string | null | undefined = r.span.spanId; id; id = parentOf.get(id)) keep.add(id);\n }\n }\n const out: Row[] = [];\n let hiddenBelow: number | null = null;\n for (const r of rows) {\n if (hiddenBelow !== null && r.depth > hiddenBelow) continue;\n hiddenBelow = null;\n if (keep && !keep.has(r.span.spanId)) continue;\n out.push(r);\n if (this.collapsed().has(r.span.spanId)) hiddenBelow = r.depth;\n }\n return out;\n });\n\n protected readonly ticks = computed(() => [0, 25, 50, 75].map(at => ({ at, label: formatDuration(this.total() * at / 100) })));\n\n /** The app's categorical colours (observability.colors $series), as CSS variables: a theme switch repaints them. */\n private readonly seriesCount = Math.max(1, Number(themeColor('series-count')) || 1);\n\n color(service: string) {\n return `var(--jc-series-${this.services().indexOf(service) % this.seriesCount + 1})`;\n }\n\n width(span: Span) {\n return Math.max(0.3, 100 * span.durationMs / this.total());\n }\n\n offsetMs(span: Span) {\n return preciseTime(span.startedAt) - this.start();\n }\n\n eventOffset(span: Span, at: string) {\n return preciseTime(at) - preciseTime(span.startedAt);\n }\n\n toggle(spanId: string, event: Event) {\n event.stopPropagation();\n const next = new Set(this.collapsed());\n if (!next.delete(spanId)) next.add(spanId);\n this.collapsed.set(next);\n }\n\n select(spanId: string | null) {\n void this.router.navigate([], { queryParams: { span: spanId }, queryParamsHandling: 'merge', replaceUrl: true });\n }\n\n private windowAround(span: Span) {\n const start = new Date(span.startedAt).getTime();\n return { from: new Date(start - 60_000).toISOString(), to: new Date(start + span.durationMs + 60_000).toISOString() };\n }\n\n /** The service page over a window around this span, so its metrics and logs line up with it. */\n aroundSpan(span: Span, extra: Record<string, string>) {\n return { ...this.ctx.around(span.startedAt, span.durationMs), ...extra };\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"juice-js-observability-trace.page-CkLNNnKf.mjs","names":[],"sources":["../../../../projects/juice-js/observability/src/lib/pages/trace.page.ts"],"sourcesContent":["import { LinkPipe, ObservabilityLinks } from '../core/options';\nimport { ChangeDetectionStrategy, Component, computed, inject, input, signal } from '@angular/core';\nimport { apiResource } from '../shared/resource';\nimport { DatePipe } from '@angular/common';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatIconModule } from '@angular/material/icon';\nimport { MatProgressBarModule } from '@angular/material/progress-bar';\nimport { Router, RouterLink } from '@angular/router';\nimport { ApiService } from '../core/api.service';\nimport { ContextService } from '../core/context.service';\nimport { Span } from '../core/models';\nimport { DurationPipe, formatDuration, plainTraceId, preciseTime } from '../shared/format';\nimport { ContextBar, KeyValueTable } from '../shared/ui';\nimport { LogTable, problemText } from '../shared/telemetry';\nimport { themeColor } from '../shared/theme';\n\ninterface Row { span: Span; depth: number; children: number; offset: number; selfError: boolean; slowest: boolean; }\n\n/** A stable colour per service, from a small palette that reads in both themes. */\n\n/**\n * A trace as a waterfall (Console requirements §6): the span hierarchy with timing relative to the trace start, so\n * parallel calls show side by side; errors and the slowest spans marked; subtrees collapsible. The selected span is\n * in the URL (`span`), with its details, its logs, and links to its service, its dependency, and the metrics and logs\n * around it (§7).\n */\n@Component({\n changeDetection: ChangeDetectionStrategy.OnPush,\n // Fills the window; the waterfall and the span scroll on their own (styles.scss).\n host: { class: 'jc-fill-page' },\n imports: [LinkPipe, RouterLink, DatePipe, MatButtonModule, MatIconModule, MatProgressBarModule, ContextBar, KeyValueTable, LogTable, DurationPipe],\n template: `\n <jc-context-bar>\n <a [routerLink]=\"['traces'] | jcLink\" [queryParams]=\"ctx.keep()\" class=\"jc-crumb\">Traces</a>\n <span class=\"jc-crumb-sep\">›</span>\n <h1 class=\"jc-mono\">{{ id() }}</h1>\n </jc-context-bar>\n @if (trace.isLoading()) { <mat-progress-bar mode=\"indeterminate\" /> }\n @if (trace.error()) { <p class=\"jc-error\">{{ error() }}</p> }\n\n @if (trace.value(); as t) {\n <div class=\"jc-summary\">\n <span><strong>{{ root()?.service }}</strong> · {{ root()?.name }}</span>\n <span>{{ start() | date: 'MMM d, HH:mm:ss.SSS' }}</span>\n <span>{{ total() | duration }}</span>\n <span>{{ t.spans.length }} spans · {{ services().length }} services</span>\n @if (errors() > 0) { <span class=\"jc-badge jc-health-unhealthy\">{{ errors() }} failed</span> }\n <button mat-button (click)=\"showErrorsOnly.set(!showErrorsOnly())\">{{ showErrorsOnly() ? 'Show all spans' : 'Error paths only' }}</button>\n <a mat-button [routerLink]=\"['logs'] | jcLink\" [queryParams]=\"ctx.keep({ trace: id() })\">Logs of this trace</a>\n @if (auditLink(); as link) {\n @if ('route' in link) { <a mat-button [routerLink]=\"link.route\" data-testid=\"audit-link\">Audit records ↗</a> }\n @else { <a mat-button [href]=\"link.href\" target=\"_blank\" rel=\"noopener\" data-testid=\"audit-link\">Audit records ↗</a> }\n }\n @if (t.truncated) { <span class=\"jc-muted\">Showing the first {{ t.spans.length }} spans.</span> }\n </div>\n <div class=\"jc-legend\">@for (s of services(); track s) { <span><i [style.background]=\"color(s)\"></i>{{ s }}</span> }</div>\n\n <div class=\"jc-trace\" [class.jc-with-detail]=\"selected()\">\n <div class=\"jc-waterfall\" role=\"tree\">\n <div class=\"jc-wf-head\"><span>Span</span><span class=\"jc-wf-scale\">\n @for (tick of ticks(); track tick.at) { <span [style.left.%]=\"tick.at\">{{ tick.label }}</span> }</span></div>\n @for (r of visible(); track r.span.spanId) {\n <div class=\"jc-wf-row\" role=\"treeitem\" [class.jc-selected]=\"r.span.spanId === selectedId()\"\n [class.jc-wf-error]=\"r.selfError\" (click)=\"select(r.span.spanId)\" tabindex=\"0\" (keydown.enter)=\"select(r.span.spanId)\">\n <span class=\"jc-wf-name\" [style.padding-left.px]=\"r.depth * 14\">\n @if (r.children > 0) {\n <button class=\"jc-wf-toggle\" (click)=\"toggle(r.span.spanId, $event)\" [attr.aria-label]=\"collapsed().has(r.span.spanId) ? 'Expand' : 'Collapse'\">\n {{ collapsed().has(r.span.spanId) ? '▸' : '▾' }}</button>\n } @else { <span class=\"jc-wf-toggle\"></span> }\n <i class=\"jc-dot\" [style.background]=\"color(r.span.service)\"></i>\n <span class=\"jc-wf-service\">{{ r.span.service }}</span> <span>{{ r.span.name }}</span>\n @if (r.selfError) { <mat-icon class=\"jc-wf-icon jc-bad\" title=\"Failed\">error</mat-icon> }\n @if (r.slowest) { <mat-icon class=\"jc-wf-icon\" title=\"Among the slowest spans\">schedule</mat-icon> }\n @if (collapsed().has(r.span.spanId)) { <span class=\"jc-muted jc-small\">+{{ r.children }}</span> }\n </span>\n <span class=\"jc-wf-track\">\n <span class=\"jc-wf-bar\" [style.left.%]=\"r.offset\" [style.width.%]=\"width(r.span)\" [style.background]=\"color(r.span.service)\"></span>\n <span class=\"jc-wf-time\" [style.left.%]=\"r.offset\">{{ r.span.durationMs | duration }}</span>\n </span>\n </div>\n }\n </div>\n\n @if (selected(); as s) {\n <aside class=\"jc-card jc-span-detail\">\n <div class=\"jc-row\"><h2>{{ s.name }}</h2><button mat-icon-button (click)=\"select(null)\" aria-label=\"Close\"><mat-icon>close</mat-icon></button></div>\n <div class=\"jc-span-facts\">\n <span class=\"jc-badge\" [class.jc-health-unhealthy]=\"s.status === 'Error'\" [class.jc-health-healthy]=\"s.status === 'Ok'\">{{ s.status }}</span>\n <span>{{ s.kind }}</span><span>{{ s.durationMs | duration }}</span>\n <span>at +{{ offsetMs(s) | duration }}</span>\n </div>\n @if (s.statusMessage) { <p class=\"jc-error\">{{ s.statusMessage }}</p> }\n\n <nav class=\"jc-actions\">\n @if (s.serviceId) { <a mat-stroked-button [routerLink]=\"['services', s.serviceId] | jcLink\" [queryParams]=\"ctx.keep()\">Service {{ s.service }}</a> }\n @if (s.target; as target) {\n <a mat-stroked-button [routerLink]=\"(target.kind === 'service' ? ['services', target.id] : ['resources', target.id]) | jcLink\" [queryParams]=\"ctx.keep()\">\n {{ target.kind === 'service' ? 'Calls' : 'Uses' }} {{ target.name }}</a>\n }\n @if (s.serviceId) {\n <a mat-stroked-button [routerLink]=\"['services', s.serviceId] | jcLink\" [queryParams]=\"aroundSpan(s, { tab: 'metrics' })\">Metrics around</a>\n <a mat-stroked-button [routerLink]=\"['services', s.serviceId] | jcLink\" [queryParams]=\"aroundSpan(s, { tab: 'logs' })\">Service logs around</a>\n <a mat-stroked-button [routerLink]=\"['services', s.serviceId] | jcLink\" [queryParams]=\"ctx.keep({ tab: 'traces', op: s.name })\">Other traces of {{ s.name }}</a>\n }\n </nav>\n\n <h3>Logs of this span</h3>\n <jc-log-table [query]=\"spanLogQuery()\" [compact]=\"true\" />\n\n <h3>Span</h3>\n <jc-kv [values]=\"{ 'service': s.service, 'environment': s.environment, 'instance': s.instance, 'host': s.host,\n 'trace id': id(), 'span id': s.spanId, 'parent span id': s.parentSpanId, 'started': s.startedAt,\n 'instrumentation scope': s.scope ? s.scope + (s.scopeVersion ? ' ' + s.scopeVersion : '') : null }\" />\n <h3>Attributes</h3>\n <jc-kv [values]=\"s.attributes\" empty=\"No attributes.\" />\n @if (s.events.length) {\n <h3>Events</h3>\n @for (e of s.events; track $index) {\n <div class=\"jc-event\" [class.jc-wf-error]=\"e.name === 'exception'\">\n <strong>{{ e.name }}</strong> <span class=\"jc-muted\">+{{ eventOffset(s, e.at) | duration }}</span>\n <jc-kv [values]=\"e.attributes\" empty=\"\" />\n </div>\n }\n }\n @if (s.links.length) {\n <h3>Links</h3>\n @for (l of s.links; track l.spanId) {\n <a [routerLink]=\"['traces', l.traceId] | jcLink\" [queryParams]=\"ctx.keep({ span: l.spanId })\" class=\"jc-mono\">{{ l.traceId }} / {{ l.spanId }}</a>\n }\n }\n </aside>\n }\n </div>\n }`,\n})\nexport class TracePage {\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 /** A trace id, or a W3C traceparent such as a Juice.Audit record before 10.4 holds; links from either open the trace. */\n readonly traceId = input.required<string>();\n readonly span = input<string>();\n\n protected readonly id = computed(() => plainTraceId(this.traceId()));\n protected readonly trace = apiResource({ params: () => this.id(), stream: ({ params }) => this.api.trace(params) });\n protected readonly error = computed(() => problemText(this.trace.error()));\n /** The host's audit records of this trace (`auditLink`): the requests it served and the data they changed. */\n protected readonly auditLink = computed(() => this.links.audit(this.id()));\n protected readonly collapsed = signal(new Set<string>());\n protected readonly showErrorsOnly = signal(false);\n protected readonly selectedId = computed(() => this.span() ?? null);\n\n private readonly spans = computed(() => this.trace.value()?.spans ?? []);\n // Sub-millisecond precision: spans of a fast in-process flow start microseconds apart.\n protected readonly start = computed(() => Math.min(...this.spans().map(s => preciseTime(s.startedAt))));\n protected readonly total = computed(() =>\n Math.max(0.001, Math.max(...this.spans().map(s => preciseTime(s.startedAt) + s.durationMs)) - this.start()));\n protected readonly root = computed(() => this.spans().find(s => !s.parentSpanId) ?? this.spans()[0]);\n protected readonly services = computed(() => [...new Set(this.spans().map(s => s.service))]);\n protected readonly errors = computed(() => this.spans().filter(s => s.status === 'Error').length);\n protected readonly selected = computed(() => this.spans().find(s => s.spanId === this.selectedId()) ?? null);\n protected readonly spanLogQuery = computed(() => {\n const s = this.selected();\n return s ? { spanId: s.spanId, ...this.windowAround(s) } : {};\n });\n\n /** Depth-first order with depth; spans whose parent is missing are roots. Children start-ordered. */\n private readonly rows = computed<Row[]>(() => {\n const spans = this.spans();\n const ids = new Set(spans.map(s => s.spanId));\n const byParent = new Map<string | null, Span[]>();\n for (const s of spans) {\n const parent = s.parentSpanId && ids.has(s.parentSpanId) ? s.parentSpanId : null;\n byParent.set(parent, [...(byParent.get(parent) ?? []), s]);\n }\n const slowest = new Set([...spans].sort((a, b) => b.durationMs - a.durationMs).slice(0, Math.max(1, Math.ceil(spans.length * 0.05))).map(s => s.spanId));\n const rows: Row[] = [];\n const walk = (parent: string | null, depth: number) => {\n for (const s of (byParent.get(parent) ?? []).sort((a, b) => a.startedAt.localeCompare(b.startedAt))) {\n rows.push({\n span: s, depth, children: byParent.get(s.spanId)?.length ?? 0,\n offset: 100 * (preciseTime(s.startedAt) - this.start()) / this.total(),\n selfError: s.status === 'Error', slowest: slowest.has(s.spanId) && spans.length > 3,\n });\n walk(s.spanId, depth + 1);\n }\n };\n walk(null, 0);\n return rows;\n });\n\n /** Hides collapsed subtrees; \"error paths only\" keeps failed spans and their ancestors. */\n protected readonly visible = computed(() => {\n const rows = this.rows();\n const parentOf = new Map(this.spans().map(s => [s.spanId, s.parentSpanId]));\n let keep: Set<string> | null = null;\n if (this.showErrorsOnly()) {\n keep = new Set<string>();\n for (const r of rows.filter(r => r.selfError)) {\n for (let id: string | null | undefined = r.span.spanId; id; id = parentOf.get(id)) keep.add(id);\n }\n }\n const out: Row[] = [];\n let hiddenBelow: number | null = null;\n for (const r of rows) {\n if (hiddenBelow !== null && r.depth > hiddenBelow) continue;\n hiddenBelow = null;\n if (keep && !keep.has(r.span.spanId)) continue;\n out.push(r);\n if (this.collapsed().has(r.span.spanId)) hiddenBelow = r.depth;\n }\n return out;\n });\n\n protected readonly ticks = computed(() => [0, 25, 50, 75].map(at => ({ at, label: formatDuration(this.total() * at / 100) })));\n\n /** The app's categorical colours (observability.colors $series), as CSS variables: a theme switch repaints them. */\n private readonly seriesCount = Math.max(1, Number(themeColor('series-count')) || 1);\n\n color(service: string) {\n return `var(--jc-series-${this.services().indexOf(service) % this.seriesCount + 1})`;\n }\n\n width(span: Span) {\n return Math.max(0.3, 100 * span.durationMs / this.total());\n }\n\n offsetMs(span: Span) {\n return preciseTime(span.startedAt) - this.start();\n }\n\n eventOffset(span: Span, at: string) {\n return preciseTime(at) - preciseTime(span.startedAt);\n }\n\n toggle(spanId: string, event: Event) {\n event.stopPropagation();\n const next = new Set(this.collapsed());\n if (!next.delete(spanId)) next.add(spanId);\n this.collapsed.set(next);\n }\n\n select(spanId: string | null) {\n void this.router.navigate([], { queryParams: { span: spanId }, queryParamsHandling: 'merge', replaceUrl: true });\n }\n\n private windowAround(span: Span) {\n const start = new Date(span.startedAt).getTime();\n return { from: new Date(start - 60_000).toISOString(), to: new Date(start + span.durationMs + 60_000).toISOString() };\n }\n\n /** The service page over a window around this span, so its metrics and logs line up with it. */\n aroundSpan(span: Span, extra: Record<string, string>) {\n return { ...this.ctx.around(span.startedAt, span.durationMs), ...extra };\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ApiService, ContextService } from "./juice-js-observability.mjs";
|
|
2
|
-
import { i as TraceTable, l as ContextBar, x as apiResource } from "./juice-js-observability-telemetry-
|
|
2
|
+
import { i as TraceTable, l as ContextBar, 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
5
|
import { map } from "rxjs";
|
|
@@ -517,4 +517,4 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
517
517
|
});
|
|
518
518
|
export { TracesPage };
|
|
519
519
|
|
|
520
|
-
//# sourceMappingURL=juice-js-observability-traces.page-
|
|
520
|
+
//# sourceMappingURL=juice-js-observability-traces.page-DgvNixIc.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"juice-js-observability-traces.page-C-i5ZJqJ.mjs","names":[],"sources":["../../../../projects/juice-js/observability/src/lib/pages/traces.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 { TraceTable } from '../shared/telemetry';\n\n/**\n * Trace search (Console requirements §5): narrow from a service and time window to one trace by operation, status,\n * duration, instance, resource, dependency, trace or span id, and span attributes such as `http.route=/orders` or\n * `messaging.destination.name=orders`. Every filter is a URL parameter, so a search is a shareable link.\n */\n@Component({\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormsModule, MatButtonModule, MatFormFieldModule, MatInputModule, MatSelectModule, ContextBar, TraceTable],\n template: `\n <jc-context-bar><h1>Traces</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\"><mat-label>Operation</mat-label><input matInput name=\"op\" [ngModel]=\"op() ?? ''\"></mat-form-field>\n <mat-form-field subscriptSizing=\"dynamic\" class=\"jc-field-sm\">\n <mat-label>Status</mat-label>\n <mat-select name=\"status\" [ngModel]=\"status() ?? ''\">\n <mat-option value=\"\">Any</mat-option><mat-option value=\"error\">Error</mat-option><mat-option value=\"ok\">No error</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field subscriptSizing=\"dynamic\" class=\"jc-field-md\"><mat-label>Min duration (ms)</mat-label><input matInput type=\"number\" min=\"0\" name=\"min\" [ngModel]=\"min() ?? ''\"></mat-form-field>\n <mat-form-field subscriptSizing=\"dynamic\" class=\"jc-field-md\"><mat-label>Max duration (ms)</mat-label><input matInput type=\"number\" min=\"0\" name=\"max\" [ngModel]=\"max() ?? ''\"></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 <mat-form-field subscriptSizing=\"dynamic\" class=\"jc-field-wide\">\n <mat-label>Attributes</mat-label>\n <input matInput name=\"attr\" [ngModel]=\"attributes().join(', ')\" placeholder=\"http.route=/orders, messaging.destination.name=orders\">\n </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 @if (instance() || resource() || dependency()) {\n <p class=\"jc-muted\">Also filtered by {{ scopeText() }}.\n <a href=\"\" (click)=\"$event.preventDefault(); apply({ instance: null, resource: null, dependency: null })\">Remove</a></p>\n }\n <jc-trace-table [query]=\"query()\" [limit]=\"100\" />`,\n})\nexport class TracesPage {\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 op = input<string>();\n readonly status = input<string>();\n readonly min = input<string>();\n readonly max = input<string>();\n readonly trace = input<string>();\n readonly spanId = input<string>();\n readonly attr = input<string | string[]>();\n readonly instance = input<string>();\n readonly resource = input<string>();\n readonly dependency = input<string>();\n\n protected readonly services = apiResource({ stream: () => this.api.resources('service').pipe(map(p => p.items)) });\n protected readonly scopeText = computed(() =>\n [this.instance() && 'an instance', this.resource() && 'a resource', this.dependency() && 'a dependency'].filter(x => !!x).join(', '));\n protected readonly attributes = computed(() => {\n const a = this.attr();\n return a === undefined ? [] : Array.isArray(a) ? a : [a];\n });\n protected readonly query = computed(() => ({\n serviceId: this.service(), operation: this.op(), status: this.status(), minDurationMs: this.min(), maxDurationMs: this.max(),\n traceId: this.trace(), spanId: this.spanId(), attribute: this.attributes(), instanceId: this.instance(),\n resourceId: this.resource(), dependencyId: this.dependency(),\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 if (typeof value['attr'] === 'string') {\n queryParams['attr'] = (value['attr'] as string).split(',').map(a => a.trim()).filter(Boolean);\n }\n void this.router.navigate([], { queryParams, queryParamsHandling: 'merge' });\n }\n\n clear() {\n void this.router.navigate([], { queryParams: this.ctx.keep() });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"juice-js-observability-traces.page-DgvNixIc.mjs","names":[],"sources":["../../../../projects/juice-js/observability/src/lib/pages/traces.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 { TraceTable } from '../shared/telemetry';\n\n/**\n * Trace search (Console requirements §5): narrow from a service and time window to one trace by operation, status,\n * duration, instance, resource, dependency, trace or span id, and span attributes such as `http.route=/orders` or\n * `messaging.destination.name=orders`. Every filter is a URL parameter, so a search is a shareable link.\n */\n@Component({\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FormsModule, MatButtonModule, MatFormFieldModule, MatInputModule, MatSelectModule, ContextBar, TraceTable],\n template: `\n <jc-context-bar><h1>Traces</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\"><mat-label>Operation</mat-label><input matInput name=\"op\" [ngModel]=\"op() ?? ''\"></mat-form-field>\n <mat-form-field subscriptSizing=\"dynamic\" class=\"jc-field-sm\">\n <mat-label>Status</mat-label>\n <mat-select name=\"status\" [ngModel]=\"status() ?? ''\">\n <mat-option value=\"\">Any</mat-option><mat-option value=\"error\">Error</mat-option><mat-option value=\"ok\">No error</mat-option>\n </mat-select>\n </mat-form-field>\n <mat-form-field subscriptSizing=\"dynamic\" class=\"jc-field-md\"><mat-label>Min duration (ms)</mat-label><input matInput type=\"number\" min=\"0\" name=\"min\" [ngModel]=\"min() ?? ''\"></mat-form-field>\n <mat-form-field subscriptSizing=\"dynamic\" class=\"jc-field-md\"><mat-label>Max duration (ms)</mat-label><input matInput type=\"number\" min=\"0\" name=\"max\" [ngModel]=\"max() ?? ''\"></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 <mat-form-field subscriptSizing=\"dynamic\" class=\"jc-field-wide\">\n <mat-label>Attributes</mat-label>\n <input matInput name=\"attr\" [ngModel]=\"attributes().join(', ')\" placeholder=\"http.route=/orders, messaging.destination.name=orders\">\n </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 @if (instance() || resource() || dependency()) {\n <p class=\"jc-muted\">Also filtered by {{ scopeText() }}.\n <a href=\"\" (click)=\"$event.preventDefault(); apply({ instance: null, resource: null, dependency: null })\">Remove</a></p>\n }\n <jc-trace-table [query]=\"query()\" [limit]=\"100\" />`,\n})\nexport class TracesPage {\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 op = input<string>();\n readonly status = input<string>();\n readonly min = input<string>();\n readonly max = input<string>();\n readonly trace = input<string>();\n readonly spanId = input<string>();\n readonly attr = input<string | string[]>();\n readonly instance = input<string>();\n readonly resource = input<string>();\n readonly dependency = input<string>();\n\n protected readonly services = apiResource({ stream: () => this.api.resources('service').pipe(map(p => p.items)) });\n protected readonly scopeText = computed(() =>\n [this.instance() && 'an instance', this.resource() && 'a resource', this.dependency() && 'a dependency'].filter(x => !!x).join(', '));\n protected readonly attributes = computed(() => {\n const a = this.attr();\n return a === undefined ? [] : Array.isArray(a) ? a : [a];\n });\n protected readonly query = computed(() => ({\n serviceId: this.service(), operation: this.op(), status: this.status(), minDurationMs: this.min(), maxDurationMs: this.max(),\n traceId: this.trace(), spanId: this.spanId(), attribute: this.attributes(), instanceId: this.instance(),\n resourceId: this.resource(), dependencyId: this.dependency(),\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 if (typeof value['attr'] === 'string') {\n queryParams['attr'] = (value['attr'] as string).split(',').map(a => a.trim()).filter(Boolean);\n }\n void this.router.navigate([], { queryParams, queryParamsHandling: 'merge' });\n }\n\n clear() {\n void this.router.navigate([], { queryParams: this.ctx.keep() });\n }\n}\n"],"mappings":"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"}
|
|
@@ -323,19 +323,19 @@ function page(path, title, load) {
|
|
|
323
323
|
};
|
|
324
324
|
}
|
|
325
325
|
const OBSERVABILITY_ROUTES = [
|
|
326
|
-
page("", "Overview", () => import("./juice-js-observability-overview.page-
|
|
327
|
-
page("services", "Services", () => import("./juice-js-observability-services.page-
|
|
328
|
-
page("services/:id", "Service", () => import("./juice-js-observability-service.page-
|
|
329
|
-
page("traces", "Traces", () => import("./juice-js-observability-traces.page-
|
|
330
|
-
page("traces/:traceId", "Trace", () => import("./juice-js-observability-trace.page-
|
|
331
|
-
page("topology", "Dependencies", () => import("./juice-js-observability-topology.page-
|
|
332
|
-
page("logs", "Logs", () => import("./juice-js-observability-logs.page-
|
|
333
|
-
page("metrics", "Metrics", () => import("./juice-js-observability-metrics.page-
|
|
334
|
-
page("resources", "Resources", () => import("./juice-js-observability-resources.page-
|
|
335
|
-
page("resources/:id", "Resource", () => import("./juice-js-observability-resource.page-
|
|
336
|
-
page("alerts", "Alerts", () => import("./juice-js-observability-alerts.page-
|
|
337
|
-
page("alerts/:id", "Alert", () => import("./juice-js-observability-alert.page-
|
|
338
|
-
page("rules", "Rules", () => import("./juice-js-observability-rules.page-
|
|
326
|
+
page("", "Overview", () => import("./juice-js-observability-overview.page-Cod7-cIy.mjs").then((m) => m.OverviewPage)),
|
|
327
|
+
page("services", "Services", () => import("./juice-js-observability-services.page-BtG8zyiI.mjs").then((m) => m.ServicesPage)),
|
|
328
|
+
page("services/:id", "Service", () => import("./juice-js-observability-service.page-_yK0kVYS.mjs").then((m) => m.ServicePage)),
|
|
329
|
+
page("traces", "Traces", () => import("./juice-js-observability-traces.page-DgvNixIc.mjs").then((m) => m.TracesPage)),
|
|
330
|
+
page("traces/:traceId", "Trace", () => import("./juice-js-observability-trace.page-CkLNNnKf.mjs").then((m) => m.TracePage)),
|
|
331
|
+
page("topology", "Dependencies", () => import("./juice-js-observability-topology.page-M_kitXna.mjs").then((m) => m.TopologyPage)),
|
|
332
|
+
page("logs", "Logs", () => import("./juice-js-observability-logs.page-CZNbTl2Q.mjs").then((m) => m.LogsPage)),
|
|
333
|
+
page("metrics", "Metrics", () => import("./juice-js-observability-metrics.page-DoVED9MT.mjs").then((m) => m.MetricsPage)),
|
|
334
|
+
page("resources", "Resources", () => import("./juice-js-observability-resources.page-KadGxEAh.mjs").then((m) => m.ResourcesPage)),
|
|
335
|
+
page("resources/:id", "Resource", () => import("./juice-js-observability-resource.page-C8RhUipF.mjs").then((m) => m.ResourcePage)),
|
|
336
|
+
page("alerts", "Alerts", () => import("./juice-js-observability-alerts.page-B-2uS9wr.mjs").then((m) => m.AlertsPage)),
|
|
337
|
+
page("alerts/:id", "Alert", () => import("./juice-js-observability-alert.page-TKwqJT2d.mjs").then((m) => m.AlertPage)),
|
|
338
|
+
page("rules", "Rules", () => import("./juice-js-observability-rules.page-DKWvNpu9.mjs").then((m) => m.RulesPage))
|
|
339
339
|
];
|
|
340
340
|
function observabilityMenus(basePath = "/", order = 1) {
|
|
341
341
|
const base = normalizeBasePath(basePath);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juice-js/observability",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.9",
|
|
4
4
|
"description": "Juice Observability pages (services, traces, topology, logs, metrics, resources, alerts, rules) for an Angular app on juice-layout",
|
|
5
5
|
"author": "jack@creatorflow.io",
|
|
6
6
|
"license": "MIT",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"juice-js-observability-service.page-JstbjLbF.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>Per second</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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"}
|