@juice-js/observability 1.0.1-alpha.14 → 1.0.1-alpha.17
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/LICENSE +21 -0
- package/README.md +2 -0
- package/fesm2022/{juice-js-observability-alert.page-TKwqJT2d.mjs → juice-js-observability-alert.page-nEy5Gc6j.mjs} +2 -2
- package/fesm2022/{juice-js-observability-alert.page-TKwqJT2d.mjs.map → juice-js-observability-alert.page-nEy5Gc6j.mjs.map} +1 -1
- package/fesm2022/{juice-js-observability-alerts.page-B-2uS9wr.mjs → juice-js-observability-alerts.page-DUXXBanH.mjs} +2 -2
- package/fesm2022/{juice-js-observability-alerts.page-B-2uS9wr.mjs.map → juice-js-observability-alerts.page-DUXXBanH.mjs.map} +1 -1
- package/fesm2022/{juice-js-observability-logs.page-CZNbTl2Q.mjs → juice-js-observability-logs.page-i76xbJLH.mjs} +2 -2
- package/fesm2022/{juice-js-observability-logs.page-CZNbTl2Q.mjs.map → juice-js-observability-logs.page-i76xbJLH.mjs.map} +1 -1
- package/fesm2022/{juice-js-observability-metrics.page-DoVED9MT.mjs → juice-js-observability-metrics.page-Cq5S0yGJ.mjs} +2 -2
- package/fesm2022/{juice-js-observability-metrics.page-DoVED9MT.mjs.map → juice-js-observability-metrics.page-Cq5S0yGJ.mjs.map} +1 -1
- package/fesm2022/{juice-js-observability-overview.page-Cod7-cIy.mjs → juice-js-observability-overview.page-DXZl_fUh.mjs} +2 -2
- package/fesm2022/{juice-js-observability-overview.page-Cod7-cIy.mjs.map → juice-js-observability-overview.page-DXZl_fUh.mjs.map} +1 -1
- package/fesm2022/{juice-js-observability-resource.page-C8RhUipF.mjs → juice-js-observability-resource.page-CauDE6kK.mjs} +2 -2
- package/fesm2022/{juice-js-observability-resource.page-C8RhUipF.mjs.map → juice-js-observability-resource.page-CauDE6kK.mjs.map} +1 -1
- package/fesm2022/{juice-js-observability-resources.page-KadGxEAh.mjs → juice-js-observability-resources.page-CukW8vVc.mjs} +2 -2
- package/fesm2022/{juice-js-observability-resources.page-KadGxEAh.mjs.map → juice-js-observability-resources.page-CukW8vVc.mjs.map} +1 -1
- package/fesm2022/{juice-js-observability-rules.page-DKWvNpu9.mjs → juice-js-observability-rules.page-Crb07uWm.mjs} +2 -2
- package/fesm2022/{juice-js-observability-rules.page-DKWvNpu9.mjs.map → juice-js-observability-rules.page-Crb07uWm.mjs.map} +1 -1
- package/fesm2022/{juice-js-observability-service.page-_yK0kVYS.mjs → juice-js-observability-service.page-Dt8bN2Ry.mjs} +2 -2
- package/fesm2022/{juice-js-observability-service.page-_yK0kVYS.mjs.map → juice-js-observability-service.page-Dt8bN2Ry.mjs.map} +1 -1
- package/fesm2022/{juice-js-observability-services.page-BtG8zyiI.mjs → juice-js-observability-services.page-B1huLe_a.mjs} +2 -2
- package/fesm2022/{juice-js-observability-services.page-BtG8zyiI.mjs.map → juice-js-observability-services.page-B1huLe_a.mjs.map} +1 -1
- package/fesm2022/{juice-js-observability-telemetry-B5jMJYfn.mjs → juice-js-observability-telemetry-BftWSOaQ.mjs} +235 -8
- package/fesm2022/juice-js-observability-telemetry-BftWSOaQ.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-topology.page-B7aXWOYV.mjs → juice-js-observability-topology.page-aMWEXyMB.mjs} +2 -2
- package/fesm2022/{juice-js-observability-topology.page-B7aXWOYV.mjs.map → juice-js-observability-topology.page-aMWEXyMB.mjs.map} +1 -1
- package/fesm2022/{juice-js-observability-trace.page-CkLNNnKf.mjs → juice-js-observability-trace.page-DUCWnZL5.mjs} +126 -3
- package/fesm2022/juice-js-observability-trace.page-DUCWnZL5.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-traces.page-DgvNixIc.mjs → juice-js-observability-traces.page-DXSDdeTH.mjs} +2 -2
- package/fesm2022/{juice-js-observability-traces.page-DgvNixIc.mjs.map → juice-js-observability-traces.page-DXSDdeTH.mjs.map} +1 -1
- package/fesm2022/juice-js-observability.mjs +13 -13
- package/package.json +1 -1
- package/styles/_observability.scss +2 -0
- package/types/juice-js-observability.d.ts +1 -0
- package/fesm2022/juice-js-observability-telemetry-B5jMJYfn.mjs.map +0 -1
- package/fesm2022/juice-js-observability-trace.page-CkLNNnKf.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"juice-js-observability-telemetry-BftWSOaQ.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 /** Moments to mark with a vertical line. */\n readonly marks = input<string[]>([]);\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, components.MarkLineComponent, 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.marks(); 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, hideOverlap: true }, 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, i) => ({\n name: s.name, type: 'line', showSymbol: false, connectNulls: false,\n data: s.points.map(p => [p.at, p.value]),\n ...(i === 0 && this.marks().length ? { markLine: {\n silent: true, symbol: 'none', label: { show: false }, lineStyle: { color: muted, type: 'dashed' as const },\n data: this.marks().map(at => ({ xAxis: at })),\n } } : {}),\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 // The app's own metrics first: host and runtime metrics say less about a service, and sort before most names.\n return names.find(m => m.name === this.initial())?.name\n ?? names.find(m => !/^(dotnet|process|system)./.test(m.name))?.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/**\n * One resource metric of one instance around a moment, for the trace page. A counter reads as a rate. The window is\n * fixed by the trace, not the context bar's range.\n */\n@Component({\n selector: 'jc-instance-metric',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [Chart],\n template: `<jc-chart [title]=\"title()\" [series]=\"[{ name: name(), points: points() }]\" [format]=\"format()\" [marks]=\"marks()\" />`,\n})\nexport class InstanceMetric {\n private readonly api = inject(ApiService);\n readonly title = input.required<string>();\n readonly name = input.required<string>();\n readonly instanceId = input.required<string>();\n readonly from = input.required<string>();\n readonly to = input.required<string>();\n readonly marks = input<string[]>([]);\n readonly format = input<(v: number) => string>(v => String(Math.round(v * 100) / 100));\n /** Reads the metric as a level, even if the host reports it as a counter. */\n readonly level = input(false);\n\n private readonly series = apiResource({\n params: () => ({ name: this.name(), instanceId: this.instanceId(), from: this.from(), to: this.to(), ...(this.level() ? { level: true } : {}) }),\n stream: ({ params }) => this.api.series(params),\n });\n protected readonly points = computed(() => {\n const s = this.series.value();\n return s ? withGaps(s.points, this.from(), this.to(), s.stepSeconds) : [];\n });\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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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ApiService, ContextService, LinkPipe, ObservabilityLinks } from "./juice-js-observability.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { S as apiResource, _ as PercentPipe, d as HealthBadge, g as DurationPipe, h as AgoPipe, l as ConfidenceBadge, m as themeColor, o as problemText, p as onThemeChange, u as ContextBar } from "./juice-js-observability-telemetry-BftWSOaQ.mjs";
|
|
3
3
|
import * as i0 from "@angular/core";
|
|
4
4
|
import { ChangeDetectionStrategy, Component, afterNextRender, computed, effect, inject, input, viewChild } from "@angular/core";
|
|
5
5
|
import { Router, RouterLink } from "@angular/router";
|
|
@@ -483,4 +483,4 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
483
483
|
});
|
|
484
484
|
export { TopologyPage };
|
|
485
485
|
|
|
486
|
-
//# sourceMappingURL=juice-js-observability-topology.page-
|
|
486
|
+
//# sourceMappingURL=juice-js-observability-topology.page-aMWEXyMB.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"juice-js-observability-topology.page-B7aXWOYV.mjs","names":[],"sources":["../../../../projects/juice-js/observability/src/lib/pages/topology.page.ts"],"sourcesContent":["import { LinkPipe, ObservabilityLinks } from '../core/options';\nimport {\n ChangeDetectionStrategy, Component, ElementRef, OnDestroy, afterNextRender, computed, effect, inject, input, viewChild,\n} from '@angular/core';\nimport { apiResource } from '../shared/resource';\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 type { Core, StylesheetJson } from 'cytoscape';\nimport { ApiService } from '../core/api.service';\nimport { ContextService } from '../core/context.service';\nimport { Topology } from '../core/models';\nimport { AgoPipe, DurationPipe, PercentPipe } from '../shared/format';\nimport { ConfidenceBadge, ContextBar, HealthBadge } from '../shared/ui';\nimport { problemText } from '../shared/telemetry';\nimport { onThemeChange, themeColor } from '../shared/theme';\n\n/** A service's health → its --jc-* colour from the app's theme (observability.colors). */\nconst HEALTH_TOKENS: Record<string, string> = {\n Healthy: 'ok', Degraded: 'warn', Unhealthy: 'bad', Critical: 'crit', Offline: 'off', Unknown: 'none',\n};\n\n/**\n * Dependency topology (Console requirements §9): services, databases and destinations; zoom, pan and selection.\n * Observed edges are solid, inferred ones dashed and labelled, so an inference never reads as a confirmed call. A\n * selected edge shows its evidence and representative traces; the selection is in the URL.\n */\n@Component({\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [LinkPipe, RouterLink, MatButtonModule, MatIconModule, MatProgressBarModule, ContextBar, HealthBadge, ConfidenceBadge,\n AgoPipe, DurationPipe, PercentPipe],\n template: `\n <jc-context-bar><h1>Dependencies</h1></jc-context-bar>\n @if (topology.isLoading()) { <mat-progress-bar mode=\"indeterminate\" /> }\n @if (topology.error()) { <p class=\"jc-error\">{{ error() }}</p> }\n <div class=\"jc-topology\">\n <div class=\"jc-graph-wrap\">\n <div #graph class=\"jc-graph\" aria-label=\"Dependency graph\"></div>\n <div class=\"jc-graph-legend\">\n <span><i class=\"jc-line\"></i> observed</span><span><i class=\"jc-line jc-dashed\"></i> inferred</span>\n <span>■ service ◆ database ⬣ destination</span>\n <button mat-button (click)=\"fit()\">Fit</button>\n </div>\n @if (topology.value()?.truncated) { <p class=\"jc-muted\">Showing the most recent edges only.</p> }\n @if (topology.hasValue() && topology.value()!.nodes.length === 0) { <p class=\"jc-muted\">No dependencies seen{{ ctx.environment() ? ' in ' + ctx.environment() : '' }}.</p> }\n </div>\n\n <aside class=\"jc-card jc-side\">\n @if (selectedEdge(); as e) {\n <h2>{{ name(e.source) }} → {{ name(e.target) }}</h2>\n <p>{{ e.relation }} <jc-confidence [confidence]=\"e.confidence\" /></p>\n @if (e.confidence.toLowerCase() !== 'observed') {\n <p class=\"jc-muted jc-small\">Inferred from span attributes (for example db.system.name or messaging.destination.name), not a call\n seen between two instrumented services.</p>\n }\n @if (evidence.isLoading()) { <mat-progress-bar mode=\"indeterminate\" /> }\n @if (evidence.value(); as ev) {\n <dl class=\"jc-facts\">\n <dt>Calls in window</dt><dd>{{ ev.count }}</dd>\n <dt>Error rate</dt><dd [class.jc-bad]=\"ev.errorRate >= 0.05\">{{ ev.count ? (ev.errorRate | percent2) : 'no data' }}</dd>\n <dt>p95</dt><dd>{{ ev.p95Ms | duration }}</dd>\n <dt>Last seen</dt><dd>{{ ev.lastSeen ?? e.lastSeen | ago }}</dd>\n </dl>\n <h3>Representative traces</h3>\n @for (t of ev.errorTraces; track t) { <div><a [routerLink]=\"['traces', t] | jcLink\" [queryParams]=\"ctx.keep()\" class=\"jc-mono\">{{ t.slice(0, 16) }}…</a> <span class=\"jc-badge jc-health-unhealthy\">error</span></div> }\n @for (t of ev.slowestTraces; track t) { <div><a [routerLink]=\"['traces', t] | jcLink\" [queryParams]=\"ctx.keep()\" class=\"jc-mono\">{{ t.slice(0, 16) }}…</a> <span class=\"jc-muted jc-small\">slow</span></div> }\n @if (!ev.errorTraces.length && !ev.slowestTraces.length) { <p class=\"jc-muted\">No calls in this window.</p> }\n <a mat-stroked-button [routerLink]=\"['traces'] | jcLink\" [queryParams]=\"ctx.keep({ dependency: e.id })\">All traces through this edge</a>\n }\n <p><a [routerLink]=\"link(e.source)\" [queryParams]=\"ctx.keep()\">Open {{ name(e.source) }}</a> ·\n <a [routerLink]=\"link(e.target)\" [queryParams]=\"ctx.keep()\">Open {{ name(e.target) }}</a></p>\n } @else if (selectedNode(); as n) {\n <h2>{{ n.displayName }}</h2>\n <p>{{ n.type }} <jc-health [state]=\"n.health\" /></p>\n <a mat-stroked-button [routerLink]=\"link(n.id)\" [queryParams]=\"ctx.keep()\">Open</a>\n } @else {\n <p class=\"jc-muted\">Select a node or an edge. Scroll to zoom, drag to pan.</p>\n }\n </aside>\n </div>`,\n})\nexport class TopologyPage implements OnDestroy {\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 edge = input<string>();\n readonly node = input<string>();\n\n private readonly graph = viewChild.required<ElementRef<HTMLDivElement>>('graph');\n private cy?: Core;\n private stopThemeWatch?: () => void;\n private destroyed = false;\n\n protected readonly topology = apiResource({ params: () => this.ctx.environment(), stream: ({ params }) => this.api.topology(params || null) });\n protected readonly error = computed(() => problemText(this.topology.error()));\n private readonly nodes = computed(() => new Map((this.topology.value()?.nodes ?? []).map(n => [n.id, n])));\n protected readonly selectedEdge = computed(() => this.topology.value()?.edges.find(e => e.id === this.edge()) ?? null);\n protected readonly selectedNode = computed(() => (this.node() ? this.nodes().get(this.node()!) : null) ?? null);\n protected readonly evidence = apiResource({\n // No edge selected: undefined params leave the resource idle.\n params: () => this.edge() ? { edge: this.edge()!, env: this.ctx.environment(), range: this.ctx.range(), abs: this.ctx.absolute() } : undefined,\n stream: ({ params }) => this.api.dependency(params.edge, { environment: params.env, ...this.ctx.window() }),\n });\n\n constructor() {\n afterNextRender(async () => {\n const cytoscape = (await import('cytoscape')).default;\n // Left the page while the graph library loaded: its container is detached (no size, so cytoscape's first layout\n // throws), and a graph made now would outlive ngOnDestroy.\n if (this.destroyed) return;\n this.cy = cytoscape({\n container: this.graph().nativeElement,\n wheelSensitivity: 0.3,\n style: this.graphStyle(),\n });\n // The app switched theme (light/dark): restyle and recolour the nodes.\n this.stopThemeWatch = onThemeChange(() => {\n this.cy?.style(this.graphStyle());\n this.draw(this.topology.value());\n });\n this.cy.on('tap', 'edge', e => this.choose({ edge: e.target.id(), node: null }));\n this.cy.on('tap', 'node', e => this.choose({ node: e.target.id(), edge: null }));\n this.cy.on('tap', e => { if (e.target === this.cy) this.choose({ node: null, edge: null }); });\n this.draw(this.topology.value());\n });\n effect(() => this.draw(this.topology.value()));\n effect(() => {\n const id = this.edge() ?? this.node();\n this.cy?.elements().unselect();\n if (id) this.cy?.getElementById(id).select();\n });\n }\n\n private draw(topology: Topology | undefined) {\n if (!this.cy || !topology) return;\n this.cy.elements().remove();\n this.cy.add([\n ...topology.nodes.map(n => ({ data: {\n id: n.id, label: n.displayName, type: n.type,\n color: this.color(n.type === 'service' ? HEALTH_TOKENS[n.health ?? 'Unknown'] ?? 'none' : 'muted') } })),\n ...topology.edges.filter(e => this.nodes().has(e.source) && this.nodes().has(e.target)).map(e => ({ data: {\n id: e.id, source: e.source, target: e.target, confidence: e.confidence.toLowerCase(),\n label: e.confidence.toLowerCase() === 'inferred' ? `${e.relation} (inferred)` : e.relation } })),\n ]);\n this.cy.layout({ name: 'breadthfirst', directed: true, padding: 30, spacingFactor: 1.2 }).run();\n const id = this.edge() ?? this.node();\n if (id) this.cy.getElementById(id).select();\n }\n\n private color(token: string) { return themeColor(token, this.graph().nativeElement); }\n\n /** The graph's look, in the app's theme colours. */\n private graphStyle() {\n const [text, muted, line, surface, selected] = [this.color('text'), this.color('muted'), this.color('border'),\n this.color('surface'), this.color('accent')];\n return [\n { selector: 'node', style: {\n label: 'data(label)', 'font-size': 11, color: text, 'text-valign': 'bottom', 'text-margin-y': 4,\n 'background-color': 'data(color)', width: 26, height: 26, 'border-width': 2, 'border-color': surface } },\n { selector: 'node[type = \"database\"]', style: { shape: 'diamond' } },\n { selector: 'node[type = \"messaging-destination\"]', style: { shape: 'hexagon' } },\n { selector: 'node[type = \"service\"]', style: { shape: 'round-rectangle' } },\n { selector: 'edge', style: {\n width: 2, 'curve-style': 'bezier', 'target-arrow-shape': 'triangle', 'line-color': muted,\n 'target-arrow-color': muted, label: 'data(label)', 'font-size': 9, color: muted, 'text-rotation': 'autorotate',\n 'text-background-color': surface, 'text-background-opacity': 0.8, 'text-background-padding': '1px' } },\n { selector: 'edge[confidence = \"inferred\"]', style: { 'line-style': 'dashed', 'line-color': line, 'target-arrow-color': line } },\n { selector: ':selected', style: { 'line-color': selected, 'target-arrow-color': selected, 'border-color': selected, 'border-width': 3 } },\n ] as StylesheetJson;\n }\n\n fit() { this.cy?.fit(undefined, 30); }\n\n private choose(selection: { edge?: string | null; node?: string | null }) {\n void this.router.navigate([], { queryParams: selection, queryParamsHandling: 'merge', replaceUrl: true });\n }\n\n name(id: string) { return this.nodes().get(id)?.displayName ?? id; }\n\n link(id: string) { return this.links.to(this.nodes().get(id)?.type === 'service' ? 'services' : 'resources', id); }\n\n ngOnDestroy() {\n this.destroyed = true;\n this.stopThemeWatch?.();\n this.cy?.destroy();\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"juice-js-observability-topology.page-aMWEXyMB.mjs","names":[],"sources":["../../../../projects/juice-js/observability/src/lib/pages/topology.page.ts"],"sourcesContent":["import { LinkPipe, ObservabilityLinks } from '../core/options';\nimport {\n ChangeDetectionStrategy, Component, ElementRef, OnDestroy, afterNextRender, computed, effect, inject, input, viewChild,\n} from '@angular/core';\nimport { apiResource } from '../shared/resource';\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 type { Core, StylesheetJson } from 'cytoscape';\nimport { ApiService } from '../core/api.service';\nimport { ContextService } from '../core/context.service';\nimport { Topology } from '../core/models';\nimport { AgoPipe, DurationPipe, PercentPipe } from '../shared/format';\nimport { ConfidenceBadge, ContextBar, HealthBadge } from '../shared/ui';\nimport { problemText } from '../shared/telemetry';\nimport { onThemeChange, themeColor } from '../shared/theme';\n\n/** A service's health → its --jc-* colour from the app's theme (observability.colors). */\nconst HEALTH_TOKENS: Record<string, string> = {\n Healthy: 'ok', Degraded: 'warn', Unhealthy: 'bad', Critical: 'crit', Offline: 'off', Unknown: 'none',\n};\n\n/**\n * Dependency topology (Console requirements §9): services, databases and destinations; zoom, pan and selection.\n * Observed edges are solid, inferred ones dashed and labelled, so an inference never reads as a confirmed call. A\n * selected edge shows its evidence and representative traces; the selection is in the URL.\n */\n@Component({\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [LinkPipe, RouterLink, MatButtonModule, MatIconModule, MatProgressBarModule, ContextBar, HealthBadge, ConfidenceBadge,\n AgoPipe, DurationPipe, PercentPipe],\n template: `\n <jc-context-bar><h1>Dependencies</h1></jc-context-bar>\n @if (topology.isLoading()) { <mat-progress-bar mode=\"indeterminate\" /> }\n @if (topology.error()) { <p class=\"jc-error\">{{ error() }}</p> }\n <div class=\"jc-topology\">\n <div class=\"jc-graph-wrap\">\n <div #graph class=\"jc-graph\" aria-label=\"Dependency graph\"></div>\n <div class=\"jc-graph-legend\">\n <span><i class=\"jc-line\"></i> observed</span><span><i class=\"jc-line jc-dashed\"></i> inferred</span>\n <span>■ service ◆ database ⬣ destination</span>\n <button mat-button (click)=\"fit()\">Fit</button>\n </div>\n @if (topology.value()?.truncated) { <p class=\"jc-muted\">Showing the most recent edges only.</p> }\n @if (topology.hasValue() && topology.value()!.nodes.length === 0) { <p class=\"jc-muted\">No dependencies seen{{ ctx.environment() ? ' in ' + ctx.environment() : '' }}.</p> }\n </div>\n\n <aside class=\"jc-card jc-side\">\n @if (selectedEdge(); as e) {\n <h2>{{ name(e.source) }} → {{ name(e.target) }}</h2>\n <p>{{ e.relation }} <jc-confidence [confidence]=\"e.confidence\" /></p>\n @if (e.confidence.toLowerCase() !== 'observed') {\n <p class=\"jc-muted jc-small\">Inferred from span attributes (for example db.system.name or messaging.destination.name), not a call\n seen between two instrumented services.</p>\n }\n @if (evidence.isLoading()) { <mat-progress-bar mode=\"indeterminate\" /> }\n @if (evidence.value(); as ev) {\n <dl class=\"jc-facts\">\n <dt>Calls in window</dt><dd>{{ ev.count }}</dd>\n <dt>Error rate</dt><dd [class.jc-bad]=\"ev.errorRate >= 0.05\">{{ ev.count ? (ev.errorRate | percent2) : 'no data' }}</dd>\n <dt>p95</dt><dd>{{ ev.p95Ms | duration }}</dd>\n <dt>Last seen</dt><dd>{{ ev.lastSeen ?? e.lastSeen | ago }}</dd>\n </dl>\n <h3>Representative traces</h3>\n @for (t of ev.errorTraces; track t) { <div><a [routerLink]=\"['traces', t] | jcLink\" [queryParams]=\"ctx.keep()\" class=\"jc-mono\">{{ t.slice(0, 16) }}…</a> <span class=\"jc-badge jc-health-unhealthy\">error</span></div> }\n @for (t of ev.slowestTraces; track t) { <div><a [routerLink]=\"['traces', t] | jcLink\" [queryParams]=\"ctx.keep()\" class=\"jc-mono\">{{ t.slice(0, 16) }}…</a> <span class=\"jc-muted jc-small\">slow</span></div> }\n @if (!ev.errorTraces.length && !ev.slowestTraces.length) { <p class=\"jc-muted\">No calls in this window.</p> }\n <a mat-stroked-button [routerLink]=\"['traces'] | jcLink\" [queryParams]=\"ctx.keep({ dependency: e.id })\">All traces through this edge</a>\n }\n <p><a [routerLink]=\"link(e.source)\" [queryParams]=\"ctx.keep()\">Open {{ name(e.source) }}</a> ·\n <a [routerLink]=\"link(e.target)\" [queryParams]=\"ctx.keep()\">Open {{ name(e.target) }}</a></p>\n } @else if (selectedNode(); as n) {\n <h2>{{ n.displayName }}</h2>\n <p>{{ n.type }} <jc-health [state]=\"n.health\" /></p>\n <a mat-stroked-button [routerLink]=\"link(n.id)\" [queryParams]=\"ctx.keep()\">Open</a>\n } @else {\n <p class=\"jc-muted\">Select a node or an edge. Scroll to zoom, drag to pan.</p>\n }\n </aside>\n </div>`,\n})\nexport class TopologyPage implements OnDestroy {\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 edge = input<string>();\n readonly node = input<string>();\n\n private readonly graph = viewChild.required<ElementRef<HTMLDivElement>>('graph');\n private cy?: Core;\n private stopThemeWatch?: () => void;\n private destroyed = false;\n\n protected readonly topology = apiResource({ params: () => this.ctx.environment(), stream: ({ params }) => this.api.topology(params || null) });\n protected readonly error = computed(() => problemText(this.topology.error()));\n private readonly nodes = computed(() => new Map((this.topology.value()?.nodes ?? []).map(n => [n.id, n])));\n protected readonly selectedEdge = computed(() => this.topology.value()?.edges.find(e => e.id === this.edge()) ?? null);\n protected readonly selectedNode = computed(() => (this.node() ? this.nodes().get(this.node()!) : null) ?? null);\n protected readonly evidence = apiResource({\n // No edge selected: undefined params leave the resource idle.\n params: () => this.edge() ? { edge: this.edge()!, env: this.ctx.environment(), range: this.ctx.range(), abs: this.ctx.absolute() } : undefined,\n stream: ({ params }) => this.api.dependency(params.edge, { environment: params.env, ...this.ctx.window() }),\n });\n\n constructor() {\n afterNextRender(async () => {\n const cytoscape = (await import('cytoscape')).default;\n // Left the page while the graph library loaded: its container is detached (no size, so cytoscape's first layout\n // throws), and a graph made now would outlive ngOnDestroy.\n if (this.destroyed) return;\n this.cy = cytoscape({\n container: this.graph().nativeElement,\n wheelSensitivity: 0.3,\n style: this.graphStyle(),\n });\n // The app switched theme (light/dark): restyle and recolour the nodes.\n this.stopThemeWatch = onThemeChange(() => {\n this.cy?.style(this.graphStyle());\n this.draw(this.topology.value());\n });\n this.cy.on('tap', 'edge', e => this.choose({ edge: e.target.id(), node: null }));\n this.cy.on('tap', 'node', e => this.choose({ node: e.target.id(), edge: null }));\n this.cy.on('tap', e => { if (e.target === this.cy) this.choose({ node: null, edge: null }); });\n this.draw(this.topology.value());\n });\n effect(() => this.draw(this.topology.value()));\n effect(() => {\n const id = this.edge() ?? this.node();\n this.cy?.elements().unselect();\n if (id) this.cy?.getElementById(id).select();\n });\n }\n\n private draw(topology: Topology | undefined) {\n if (!this.cy || !topology) return;\n this.cy.elements().remove();\n this.cy.add([\n ...topology.nodes.map(n => ({ data: {\n id: n.id, label: n.displayName, type: n.type,\n color: this.color(n.type === 'service' ? HEALTH_TOKENS[n.health ?? 'Unknown'] ?? 'none' : 'muted') } })),\n ...topology.edges.filter(e => this.nodes().has(e.source) && this.nodes().has(e.target)).map(e => ({ data: {\n id: e.id, source: e.source, target: e.target, confidence: e.confidence.toLowerCase(),\n label: e.confidence.toLowerCase() === 'inferred' ? `${e.relation} (inferred)` : e.relation } })),\n ]);\n this.cy.layout({ name: 'breadthfirst', directed: true, padding: 30, spacingFactor: 1.2 }).run();\n const id = this.edge() ?? this.node();\n if (id) this.cy.getElementById(id).select();\n }\n\n private color(token: string) { return themeColor(token, this.graph().nativeElement); }\n\n /** The graph's look, in the app's theme colours. */\n private graphStyle() {\n const [text, muted, line, surface, selected] = [this.color('text'), this.color('muted'), this.color('border'),\n this.color('surface'), this.color('accent')];\n return [\n { selector: 'node', style: {\n label: 'data(label)', 'font-size': 11, color: text, 'text-valign': 'bottom', 'text-margin-y': 4,\n 'background-color': 'data(color)', width: 26, height: 26, 'border-width': 2, 'border-color': surface } },\n { selector: 'node[type = \"database\"]', style: { shape: 'diamond' } },\n { selector: 'node[type = \"messaging-destination\"]', style: { shape: 'hexagon' } },\n { selector: 'node[type = \"service\"]', style: { shape: 'round-rectangle' } },\n { selector: 'edge', style: {\n width: 2, 'curve-style': 'bezier', 'target-arrow-shape': 'triangle', 'line-color': muted,\n 'target-arrow-color': muted, label: 'data(label)', 'font-size': 9, color: muted, 'text-rotation': 'autorotate',\n 'text-background-color': surface, 'text-background-opacity': 0.8, 'text-background-padding': '1px' } },\n { selector: 'edge[confidence = \"inferred\"]', style: { 'line-style': 'dashed', 'line-color': line, 'target-arrow-color': line } },\n { selector: ':selected', style: { 'line-color': selected, 'target-arrow-color': selected, 'border-color': selected, 'border-width': 3 } },\n ] as StylesheetJson;\n }\n\n fit() { this.cy?.fit(undefined, 30); }\n\n private choose(selection: { edge?: string | null; node?: string | null }) {\n void this.router.navigate([], { queryParams: selection, queryParamsHandling: 'merge', replaceUrl: true });\n }\n\n name(id: string) { return this.nodes().get(id)?.displayName ?? id; }\n\n link(id: string) { return this.links.to(this.nodes().get(id)?.type === 'service' ? 'services' : 'resources', id); }\n\n ngOnDestroy() {\n this.destroyed = true;\n this.stopThemeWatch?.();\n this.cy?.destroy();\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ApiService, ContextService, LinkPipe, ObservabilityLinks } from "./juice-js-observability.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { S as apiResource, b as plainTraceId, f as KeyValueTable, g as DurationPipe, m as themeColor, n as LogTable, o as problemText, t as InstanceMetric, u as ContextBar, x as preciseTime, y as formatDuration } from "./juice-js-observability-telemetry-BftWSOaQ.mjs";
|
|
3
3
|
import * as i0 from "@angular/core";
|
|
4
4
|
import { ChangeDetectionStrategy, Component, computed, inject, input, signal } from "@angular/core";
|
|
5
5
|
import { Router, RouterLink } from "@angular/router";
|
|
@@ -10,6 +10,34 @@ import * as i3 from "@angular/material/progress-bar";
|
|
|
10
10
|
import { MatProgressBarModule } from "@angular/material/progress-bar";
|
|
11
11
|
import * as i2 from "@angular/material/icon";
|
|
12
12
|
import { MatIconModule } from "@angular/material/icon";
|
|
13
|
+
const MaxUsageInstances = 8;
|
|
14
|
+
const UsageMarginMs = 12e4;
|
|
15
|
+
const UsageMetrics = [
|
|
16
|
+
{
|
|
17
|
+
title: "CPU",
|
|
18
|
+
name: "dotnet.process.cpu.time",
|
|
19
|
+
level: false,
|
|
20
|
+
format: (v) => `${Math.round(v * 100)} % of a core`
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
title: "Memory",
|
|
24
|
+
name: "dotnet.process.memory.working_set",
|
|
25
|
+
level: false,
|
|
26
|
+
format: (v) => `${Math.round(v / 1048576)} MiB`
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
title: "GC pause",
|
|
30
|
+
name: "dotnet.gc.pause.time",
|
|
31
|
+
level: false,
|
|
32
|
+
format: (v) => `${Math.round(v * 1e4) / 10} ms/s`
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
title: "Thread pool queue",
|
|
36
|
+
name: "dotnet.thread_pool.queue.length",
|
|
37
|
+
level: true,
|
|
38
|
+
format: (v) => String(Math.round(v * 10) / 10)
|
|
39
|
+
}
|
|
40
|
+
];
|
|
13
41
|
var TracePage = class TracePage {
|
|
14
42
|
ctx = inject(ContextService);
|
|
15
43
|
api = inject(ApiService);
|
|
@@ -17,6 +45,7 @@ var TracePage = class TracePage {
|
|
|
17
45
|
links = inject(ObservabilityLinks);
|
|
18
46
|
traceId = input.required(...ngDevMode ? [{ debugName: "traceId" }] : /* istanbul ignore next */ []);
|
|
19
47
|
span = input(...ngDevMode ? [void 0, { debugName: "span" }] : /* istanbul ignore next */ []);
|
|
48
|
+
usage = input(...ngDevMode ? [void 0, { debugName: "usage" }] : /* istanbul ignore next */ []);
|
|
20
49
|
id = computed(() => plainTraceId(this.traceId()), ...ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []);
|
|
21
50
|
trace = apiResource({
|
|
22
51
|
params: () => this.id(),
|
|
@@ -41,6 +70,24 @@ var TracePage = class TracePage {
|
|
|
41
70
|
...this.windowAround(s)
|
|
42
71
|
} : {};
|
|
43
72
|
}, ...ngDevMode ? [{ debugName: "spanLogQuery" }] : /* istanbul ignore next */ []);
|
|
73
|
+
showUsage = computed(() => this.usage() === "on", ...ngDevMode ? [{ debugName: "showUsage" }] : /* istanbul ignore next */ []);
|
|
74
|
+
allInstances = computed(() => {
|
|
75
|
+
const byId = /* @__PURE__ */ new Map();
|
|
76
|
+
for (const s of this.spans()) if (s.instanceId && !byId.has(s.instanceId)) byId.set(s.instanceId, {
|
|
77
|
+
id: s.instanceId,
|
|
78
|
+
name: s.instance ?? s.instanceId,
|
|
79
|
+
service: s.service
|
|
80
|
+
});
|
|
81
|
+
return [...byId.values()];
|
|
82
|
+
}, ...ngDevMode ? [{ debugName: "allInstances" }] : /* istanbul ignore next */ []);
|
|
83
|
+
instances = computed(() => this.allInstances().slice(0, MaxUsageInstances), ...ngDevMode ? [{ debugName: "instances" }] : /* istanbul ignore next */ []);
|
|
84
|
+
hiddenInstances = computed(() => this.allInstances().length - this.instances().length, ...ngDevMode ? [{ debugName: "hiddenInstances" }] : /* istanbul ignore next */ []);
|
|
85
|
+
usageWindow = computed(() => ({
|
|
86
|
+
from: (/* @__PURE__ */ new Date(this.start() - UsageMarginMs)).toISOString(),
|
|
87
|
+
to: new Date(this.start() + this.total() + UsageMarginMs).toISOString()
|
|
88
|
+
}), ...ngDevMode ? [{ debugName: "usageWindow" }] : /* istanbul ignore next */ []);
|
|
89
|
+
usageMarks = computed(() => [new Date(this.start()).toISOString(), new Date(this.start() + this.total()).toISOString()], ...ngDevMode ? [{ debugName: "usageMarks" }] : /* istanbul ignore next */ []);
|
|
90
|
+
usageMetrics = UsageMetrics;
|
|
44
91
|
rows = computed(() => {
|
|
45
92
|
const spans = this.spans();
|
|
46
93
|
const ids = new Set(spans.map((s) => s.spanId));
|
|
@@ -114,6 +161,13 @@ var TracePage = class TracePage {
|
|
|
114
161
|
if (!next.delete(spanId)) next.add(spanId);
|
|
115
162
|
this.collapsed.set(next);
|
|
116
163
|
}
|
|
164
|
+
toggleUsage() {
|
|
165
|
+
this.router.navigate([], {
|
|
166
|
+
queryParams: { usage: this.showUsage() ? null : "on" },
|
|
167
|
+
queryParamsHandling: "merge",
|
|
168
|
+
replaceUrl: true
|
|
169
|
+
});
|
|
170
|
+
}
|
|
117
171
|
select(spanId) {
|
|
118
172
|
this.router.navigate([], {
|
|
119
173
|
queryParams: { span: spanId },
|
|
@@ -162,6 +216,13 @@ var TracePage = class TracePage {
|
|
|
162
216
|
isSignal: true,
|
|
163
217
|
isRequired: false,
|
|
164
218
|
transformFunction: null
|
|
219
|
+
},
|
|
220
|
+
usage: {
|
|
221
|
+
classPropertyName: "usage",
|
|
222
|
+
publicName: "usage",
|
|
223
|
+
isSignal: true,
|
|
224
|
+
isRequired: false,
|
|
225
|
+
transformFunction: null
|
|
165
226
|
}
|
|
166
227
|
},
|
|
167
228
|
host: { classAttribute: "jc-fill-page" },
|
|
@@ -184,6 +245,7 @@ var TracePage = class TracePage {
|
|
|
184
245
|
@if (errors() > 0) { <span class="jc-badge jc-health-unhealthy">{{ errors() }} failed</span> }
|
|
185
246
|
<button mat-button (click)="showErrorsOnly.set(!showErrorsOnly())">{{ showErrorsOnly() ? 'Show all spans' : 'Error paths only' }}</button>
|
|
186
247
|
<a mat-button [routerLink]="['logs'] | jcLink" [queryParams]="ctx.keep({ trace: id() })">Logs of this trace</a>
|
|
248
|
+
<button mat-button [attr.aria-expanded]="showUsage()" (click)="toggleUsage()">Resource usage</button>
|
|
187
249
|
@if (auditLink(); as link) {
|
|
188
250
|
@if ('route' in link) { <a mat-button [routerLink]="link.route" data-testid="audit-link">Audit records ↗</a> }
|
|
189
251
|
@else { <a mat-button [href]="link.href" target="_blank" rel="noopener" data-testid="audit-link">Audit records ↗</a> }
|
|
@@ -192,6 +254,24 @@ var TracePage = class TracePage {
|
|
|
192
254
|
</div>
|
|
193
255
|
<div class="jc-legend">@for (s of services(); track s) { <span><i [style.background]="color(s)"></i>{{ s }}</span> }</div>
|
|
194
256
|
|
|
257
|
+
@if (showUsage()) {
|
|
258
|
+
<section class="jc-card jc-trace-usage" aria-label="Resource usage">
|
|
259
|
+
@for (i of instances(); track i.id) {
|
|
260
|
+
<h3>{{ i.service }} · {{ i.name }}</h3>
|
|
261
|
+
<div class="jc-usage-charts">
|
|
262
|
+
@for (m of usageMetrics; track m.name) {
|
|
263
|
+
<jc-instance-metric [title]="m.title" [name]="m.name" [format]="m.format" [level]="m.level" [instanceId]="i.id"
|
|
264
|
+
[from]="usageWindow().from" [to]="usageWindow().to" [marks]="usageMarks()" />
|
|
265
|
+
}
|
|
266
|
+
</div>
|
|
267
|
+
} @empty {
|
|
268
|
+
<p class="jc-muted">The spans of this trace name no known instance.</p>
|
|
269
|
+
}
|
|
270
|
+
@if (hiddenInstances() > 0) { <p class="jc-muted">And {{ hiddenInstances() }} more instances.</p> }
|
|
271
|
+
<p class="jc-muted jc-small">Dashed lines mark the trace. No data: the host does not collect the <code>System.Runtime</code> meter.</p>
|
|
272
|
+
</section>
|
|
273
|
+
}
|
|
274
|
+
|
|
195
275
|
<div class="jc-trace" [class.jc-with-detail]="selected()">
|
|
196
276
|
<div class="jc-waterfall" role="tree">
|
|
197
277
|
<div class="jc-wf-head"><span>Span</span><span class="jc-wf-scale">
|
|
@@ -358,6 +438,21 @@ var TracePage = class TracePage {
|
|
|
358
438
|
selector: "jc-log-table",
|
|
359
439
|
inputs: ["query", "compact"]
|
|
360
440
|
},
|
|
441
|
+
{
|
|
442
|
+
kind: "component",
|
|
443
|
+
type: InstanceMetric,
|
|
444
|
+
selector: "jc-instance-metric",
|
|
445
|
+
inputs: [
|
|
446
|
+
"title",
|
|
447
|
+
"name",
|
|
448
|
+
"instanceId",
|
|
449
|
+
"from",
|
|
450
|
+
"to",
|
|
451
|
+
"marks",
|
|
452
|
+
"format",
|
|
453
|
+
"level"
|
|
454
|
+
]
|
|
455
|
+
},
|
|
361
456
|
{
|
|
362
457
|
kind: "pipe",
|
|
363
458
|
type: LinkPipe,
|
|
@@ -397,7 +492,8 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
397
492
|
ContextBar,
|
|
398
493
|
KeyValueTable,
|
|
399
494
|
LogTable,
|
|
400
|
-
DurationPipe
|
|
495
|
+
DurationPipe,
|
|
496
|
+
InstanceMetric
|
|
401
497
|
],
|
|
402
498
|
template: `
|
|
403
499
|
<jc-context-bar>
|
|
@@ -417,6 +513,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
417
513
|
@if (errors() > 0) { <span class="jc-badge jc-health-unhealthy">{{ errors() }} failed</span> }
|
|
418
514
|
<button mat-button (click)="showErrorsOnly.set(!showErrorsOnly())">{{ showErrorsOnly() ? 'Show all spans' : 'Error paths only' }}</button>
|
|
419
515
|
<a mat-button [routerLink]="['logs'] | jcLink" [queryParams]="ctx.keep({ trace: id() })">Logs of this trace</a>
|
|
516
|
+
<button mat-button [attr.aria-expanded]="showUsage()" (click)="toggleUsage()">Resource usage</button>
|
|
420
517
|
@if (auditLink(); as link) {
|
|
421
518
|
@if ('route' in link) { <a mat-button [routerLink]="link.route" data-testid="audit-link">Audit records ↗</a> }
|
|
422
519
|
@else { <a mat-button [href]="link.href" target="_blank" rel="noopener" data-testid="audit-link">Audit records ↗</a> }
|
|
@@ -425,6 +522,24 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
425
522
|
</div>
|
|
426
523
|
<div class="jc-legend">@for (s of services(); track s) { <span><i [style.background]="color(s)"></i>{{ s }}</span> }</div>
|
|
427
524
|
|
|
525
|
+
@if (showUsage()) {
|
|
526
|
+
<section class="jc-card jc-trace-usage" aria-label="Resource usage">
|
|
527
|
+
@for (i of instances(); track i.id) {
|
|
528
|
+
<h3>{{ i.service }} · {{ i.name }}</h3>
|
|
529
|
+
<div class="jc-usage-charts">
|
|
530
|
+
@for (m of usageMetrics; track m.name) {
|
|
531
|
+
<jc-instance-metric [title]="m.title" [name]="m.name" [format]="m.format" [level]="m.level" [instanceId]="i.id"
|
|
532
|
+
[from]="usageWindow().from" [to]="usageWindow().to" [marks]="usageMarks()" />
|
|
533
|
+
}
|
|
534
|
+
</div>
|
|
535
|
+
} @empty {
|
|
536
|
+
<p class="jc-muted">The spans of this trace name no known instance.</p>
|
|
537
|
+
}
|
|
538
|
+
@if (hiddenInstances() > 0) { <p class="jc-muted">And {{ hiddenInstances() }} more instances.</p> }
|
|
539
|
+
<p class="jc-muted jc-small">Dashed lines mark the trace. No data: the host does not collect the <code>System.Runtime</code> meter.</p>
|
|
540
|
+
</section>
|
|
541
|
+
}
|
|
542
|
+
|
|
428
543
|
<div class="jc-trace" [class.jc-with-detail]="selected()">
|
|
429
544
|
<div class="jc-waterfall" role="tree">
|
|
430
545
|
<div class="jc-wf-head"><span>Span</span><span class="jc-wf-scale">
|
|
@@ -520,9 +635,17 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
520
635
|
alias: "span",
|
|
521
636
|
required: false
|
|
522
637
|
}]
|
|
638
|
+
}],
|
|
639
|
+
usage: [{
|
|
640
|
+
type: i0.Input,
|
|
641
|
+
args: [{
|
|
642
|
+
isSignal: true,
|
|
643
|
+
alias: "usage",
|
|
644
|
+
required: false
|
|
645
|
+
}]
|
|
523
646
|
}]
|
|
524
647
|
}
|
|
525
648
|
});
|
|
526
649
|
export { TracePage };
|
|
527
650
|
|
|
528
|
-
//# sourceMappingURL=juice-js-observability-trace.page-
|
|
651
|
+
//# sourceMappingURL=juice-js-observability-trace.page-DUCWnZL5.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"juice-js-observability-trace.page-DUCWnZL5.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 { InstanceMetric, 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\nconst MaxUsageInstances = 8;\nconst UsageMarginMs = 2 * 60_000;\n\n/**\n * .NET's built-in System.Runtime meter (.NET 9+). Counters arrive as a rate per second. The thread pool's queue length is\n * a level that .NET reports as a counter.\n */\nconst UsageMetrics = [\n { title: 'CPU', name: 'dotnet.process.cpu.time', level: false, format: (v: number) => `${Math.round(v * 100)} % of a core` },\n { title: 'Memory', name: 'dotnet.process.memory.working_set', level: false, format: (v: number) => `${Math.round(v / 1048576)} MiB` },\n { title: 'GC pause', name: 'dotnet.gc.pause.time', level: false, format: (v: number) => `${Math.round(v * 10000) / 10} ms/s` },\n { title: 'Thread pool queue', name: 'dotnet.thread_pool.queue.length', level: true, format: (v: number) => String(Math.round(v * 10) / 10) },\n];\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, InstanceMetric],\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 <button mat-button [attr.aria-expanded]=\"showUsage()\" (click)=\"toggleUsage()\">Resource usage</button>\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 @if (showUsage()) {\n <section class=\"jc-card jc-trace-usage\" aria-label=\"Resource usage\">\n @for (i of instances(); track i.id) {\n <h3>{{ i.service }} · {{ i.name }}</h3>\n <div class=\"jc-usage-charts\">\n @for (m of usageMetrics; track m.name) {\n <jc-instance-metric [title]=\"m.title\" [name]=\"m.name\" [format]=\"m.format\" [level]=\"m.level\" [instanceId]=\"i.id\"\n [from]=\"usageWindow().from\" [to]=\"usageWindow().to\" [marks]=\"usageMarks()\" />\n }\n </div>\n } @empty {\n <p class=\"jc-muted\">The spans of this trace name no known instance.</p>\n }\n @if (hiddenInstances() > 0) { <p class=\"jc-muted\">And {{ hiddenInstances() }} more instances.</p> }\n <p class=\"jc-muted jc-small\">Dashed lines mark the trace. No data: the host does not collect the <code>System.Runtime</code> meter.</p>\n </section>\n }\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 /** 'on' shows the resource usage strip; in the URL so a shared link keeps it. */\n readonly usage = 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 protected readonly showUsage = computed(() => this.usage() === 'on');\n /** The instances that served this trace, in the order they joined it. */\n private readonly allInstances = computed(() => {\n const byId = new Map<string, { id: string; name: string; service: string }>();\n for (const s of this.spans()) {\n if (s.instanceId && !byId.has(s.instanceId)) byId.set(s.instanceId, { id: s.instanceId, name: s.instance ?? s.instanceId, service: s.service });\n }\n return [...byId.values()];\n });\n protected readonly instances = computed(() => this.allInstances().slice(0, MaxUsageInstances));\n protected readonly hiddenInstances = computed(() => this.allInstances().length - this.instances().length);\n /** Metrics are exported every few seconds, so a short trace needs a margin to have any points beside it. */\n protected readonly usageWindow = computed(() => ({\n from: new Date(this.start() - UsageMarginMs).toISOString(),\n to: new Date(this.start() + this.total() + UsageMarginMs).toISOString(),\n }));\n protected readonly usageMarks = computed(() => [new Date(this.start()).toISOString(), new Date(this.start() + this.total()).toISOString()]);\n protected readonly usageMetrics = UsageMetrics;\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 toggleUsage() {\n void this.router.navigate([], { queryParams: { usage: this.showUsage() ? null : 'on' }, queryParamsHandling: 'merge', replaceUrl: true });\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":";;;;;;;;;;;;AAkBA,MAAM,oBAAoB;AAC1B,MAAM,gBAAgB;AAMtB,MAAM,eAAe;CACnB;EAAE,OAAO;EAAO,MAAM;EAA2B,OAAO;EAAO,SAAS,MAAc,GAAG,KAAK,MAAM,IAAI,GAAG,EAAC;CAAc;CAC1H;EAAE,OAAO;EAAU,MAAM;EAAqC,OAAO;EAAO,SAAS,MAAc,GAAG,KAAK,MAAM,IAAI,OAAO,EAAC;CAAM;CACnI;EAAE,OAAO;EAAY,MAAM;EAAwB,OAAO;EAAO,SAAS,MAAc,GAAG,KAAK,MAAM,IAAI,GAAK,IAAI,GAAE;CAAO;CAC5H;EAAE,OAAO;EAAqB,MAAM;EAAmC,OAAO;EAAM,SAAS,MAAc,OAAO,KAAK,MAAM,IAAI,EAAE,IAAI,EAAE;CAAC;AAC3I;AA0ID,IAAa,YAAb,MAAa,UAAS;CACD,MAAM,OAAO,cAAc;CAC7B,MAAM,OAAO,UAAU;CACvB,SAAS,OAAO,MAAM;CACtB,QAAQ,OAAO,kBAAkB;CAGzC,UAAU,MAAM,SAAkB,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CAClC,OAAO,MAAe,GAAA,YAAA,CAAA,QAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAEtB,QAAQ,MAAe,GAAA,YAAA,CAAA,QAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAEb,KAAK,eAAe,aAAa,KAAK,QAAO,CAAE,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,KAAA,CAAA,+BAAA,CAAA,CAAA;CACjD,QAAQ,YAAY;EAAE,cAAc,KAAK,GAAE;EAAI,SAAS,EAAE,aAAa,KAAK,IAAI,MAAM,MAAM;CAAC,CAAE;CAC/F,QAAQ,eAAe,YAAY,KAAK,MAAM,MAAK,CAAE,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAEvD,YAAY,eAAe,KAAK,MAAM,MAAM,KAAK,GAAE,CAAE,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CACvD,YAAY,uBAAO,IAAI,IAAG,GAAW,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CACrC,iBAAiB,OAAO,OAAM,GAAA,YAAA,CAAA,EAAA,WAAA,iBAAA,CAAA,+BAAA,CAAA,CAAA;CAC9B,aAAa,eAAe,KAAK,KAAI,KAAM,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CAElD,QAAQ,eAAe,KAAK,MAAM,MAAK,CAAE,EAAE,SAAS,CAAA,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAErD,QAAQ,eAAe,KAAK,IAAI,GAAG,KAAK,MAAK,CAAE,CAAC,KAAI,MAAK,YAAY,EAAE,SAAS,CAAC,CAAC,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CACpF,QAAQ,eACzB,KAAK,IAAI,MAAO,KAAK,IAAI,GAAG,KAAK,MAAK,CAAE,CAAC,KAAI,MAAK,YAAY,EAAE,SAAS,IAAI,EAAE,UAAU,CAAC,IAAI,KAAK,MAAK,CAAE,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAC3F,OAAO,eAAe,KAAK,MAAK,CAAE,CAAC,MAAK,MAAK,CAAC,EAAE,YAAY,KAAK,KAAK,MAAK,CAAE,CAAC,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CACjF,WAAW,eAAe,CAAC,GAAG,IAAI,IAAI,KAAK,MAAK,CAAE,CAAC,KAAI,MAAK,EAAE,OAAO,CAAC,CAAC,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CACzE,SAAS,eAAe,KAAK,MAAK,CAAE,CAAC,QAAO,MAAK,EAAE,WAAW,OAAO,CAAC,CAAC,QAAO,GAAA,YAAA,CAAA,EAAA,WAAA,SAAA,CAAA,+BAAA,CAAA,CAAA;CAC9E,WAAW,eAAe,KAAK,MAAK,CAAE,CAAC,MAAK,MAAK,EAAE,WAAW,KAAK,WAAU,CAAE,KAAK,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CACzF,eAAe,eAAc;EAC9C,MAAM,IAAI,KAAK,SAAQ;EACvB,OAAO,IAAI;GAAE,QAAQ,EAAE;GAAQ,GAAG,KAAK,aAAa,CAAC;EAAC,IAAK,CAAA;CAC7D,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CAEiB,YAAY,eAAe,KAAK,MAAK,MAAO,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAEnD,eAAe,eAAc;EAC5C,MAAM,uBAAO,IAAI,IAAG;EACpB,KAAK,MAAM,KAAK,KAAK,MAAK,GACxB,IAAI,EAAE,cAAc,CAAC,KAAK,IAAI,EAAE,UAAU,GAAG,KAAK,IAAI,EAAE,YAAY;GAAE,IAAI,EAAE;GAAY,MAAM,EAAE,YAAY,EAAE;GAAY,SAAS,EAAE;EAAO,CAAE;EAEhJ,OAAO,CAAC,GAAG,KAAK,OAAM,CAAE;CAC1B,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,eAAA,CAAA,+BAAA,CAAA,CAAA;CACiB,YAAY,eAAe,KAAK,aAAY,CAAE,CAAC,MAAM,GAAG,iBAAiB,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAC3E,kBAAkB,eAAe,KAAK,aAAY,CAAE,CAAC,SAAS,KAAK,UAAS,CAAE,CAAC,QAAO,GAAA,YAAA,CAAA,EAAA,WAAA,kBAAA,CAAA,+BAAA,CAAA,CAAA;CAEtF,cAAc,gBAAgB;EAC/C,uBAAM,IAAI,KAAK,KAAK,MAAK,IAAK,aAAa,EAAC,CAAC,YAAW;EACxD,IAAI,IAAI,KAAK,KAAK,MAAK,IAAK,KAAK,MAAK,IAAK,aAAa,CAAC,CAAC,YAAW;CACtE,IAAE,GAAA,YAAA,CAAA,EAAA,WAAA,cAAA,CAAA,+BAAA,CAAA,CAAA;CACgB,aAAa,eAAe,CAAC,IAAI,KAAK,KAAK,MAAK,CAAE,CAAC,CAAC,YAAW,GAAI,IAAI,KAAK,KAAK,MAAK,IAAK,KAAK,MAAK,CAAE,CAAC,CAAC,YAAW,CAAE,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,aAAA,CAAA,+BAAA,CAAA,CAAA;CACxH,eAAe;CAGjB,OAAO,eAAqB;EAC3C,MAAM,QAAQ,KAAK,MAAK;EACxB,MAAM,MAAM,IAAI,IAAI,MAAM,KAAI,MAAK,EAAE,MAAM,CAAC;EAC5C,MAAM,2BAAW,IAAI,IAAG;EACxB,KAAK,MAAM,KAAK,OAAO;GACrB,MAAM,SAAS,EAAE,gBAAgB,IAAI,IAAI,EAAE,YAAY,IAAI,EAAE,eAAe;GAC5E,SAAS,IAAI,QAAQ,CAAC,GAAI,SAAS,IAAI,MAAM,KAAK,CAAA,GAAK,CAAC,CAAC;EAC3D;EACA,MAAM,UAAU,IAAI,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,aAAa,EAAE,UAAU,CAAC,CAAC,MAAM,GAAG,KAAK,IAAI,GAAG,KAAK,KAAK,MAAM,SAAS,GAAI,CAAC,CAAC,CAAC,CAAC,KAAI,MAAK,EAAE,MAAM,CAAC;EACvJ,MAAM,OAAc,CAAA;EACpB,MAAM,QAAQ,QAAuB,UAAiB;GACpD,KAAK,MAAM,MAAM,SAAS,IAAI,MAAM,KAAK,CAAA,EAAA,CAAI,MAAM,GAAG,MAAM,EAAE,UAAU,cAAc,EAAE,SAAS,CAAC,GAAG;IACnG,KAAK,KAAK;KACR,MAAM;KAAG;KAAO,UAAU,SAAS,IAAI,EAAE,MAAM,CAAC,EAAE,UAAU;KAC5D,QAAQ,OAAO,YAAY,EAAE,SAAS,IAAI,KAAK,MAAK,KAAM,KAAK,MAAK;KACpE,WAAW,EAAE,WAAW;KAAS,SAAS,QAAQ,IAAI,EAAE,MAAM,KAAK,MAAM,SAAS;IACnF,CAAA;IACD,KAAK,EAAE,QAAQ,QAAQ,CAAC;GAC1B;EACF;EACA,KAAK,MAAM,CAAC;EACZ,OAAO;CACT,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAGiB,UAAU,eAAc;EACzC,MAAM,OAAO,KAAK,KAAI;EACtB,MAAM,WAAW,IAAI,IAAI,KAAK,MAAK,CAAE,CAAC,KAAI,MAAK,CAAC,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;EAC1E,IAAI,OAA2B;EAC/B,IAAI,KAAK,eAAc,GAAI;GACzB,uBAAO,IAAI,IAAG;GACd,KAAK,MAAM,KAAK,KAAK,QAAO,MAAK,EAAE,SAAS,GAC1C,KAAK,IAAI,KAAgC,EAAE,KAAK,QAAQ,IAAI,KAAK,SAAS,IAAI,EAAE,GAAG,KAAK,IAAI,EAAE;EAElG;EACA,MAAM,MAAa,CAAA;EACnB,IAAI,cAA6B;EACjC,KAAK,MAAM,KAAK,MAAM;GACpB,IAAI,gBAAgB,QAAQ,EAAE,QAAQ,aAAa;GACnD,cAAc;GACd,IAAI,QAAQ,CAAC,KAAK,IAAI,EAAE,KAAK,MAAM,GAAG;GACtC,IAAI,KAAK,CAAC;GACV,IAAI,KAAK,UAAS,CAAE,CAAC,IAAI,EAAE,KAAK,MAAM,GAAG,cAAc,EAAE;EAC3D;EACA,OAAO;CACT,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CAEiB,QAAQ,eAAe;EAAC;EAAG;EAAI;EAAI;CAAE,CAAC,CAAC,KAAI,QAAO;EAAE;EAAI,OAAO,eAAe,KAAK,MAAK,IAAK,KAAK,GAAG;CAAC,EAAG,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CAG7G,cAAc,KAAK,IAAI,GAAG,OAAO,WAAW,cAAc,CAAC,KAAK,CAAC;CAElF,MAAM,SAAe;EACnB,OAAO,mBAAmB,KAAK,SAAQ,CAAE,CAAC,QAAQ,OAAO,IAAI,KAAK,cAAc,EAAC;CACnF;CAEA,MAAM,MAAU;EACd,OAAO,KAAK,IAAI,IAAK,MAAM,KAAK,aAAa,KAAK,MAAK,CAAE;CAC3D;CAEA,SAAS,MAAU;EACjB,OAAO,YAAY,KAAK,SAAS,IAAI,KAAK,MAAK;CACjD;CAEA,YAAY,MAAY,IAAU;EAChC,OAAO,YAAY,EAAE,IAAI,YAAY,KAAK,SAAS;CACrD;CAEA,OAAO,QAAgB,OAAY;EACjC,MAAM,gBAAe;EACrB,MAAM,OAAO,IAAI,IAAI,KAAK,UAAS,CAAE;EACrC,IAAI,CAAC,KAAK,OAAO,MAAM,GAAG,KAAK,IAAI,MAAM;EACzC,KAAK,UAAU,IAAI,IAAI;CACzB;CAEA,cAAW;EACT,AAAK,KAAK,OAAO,SAAS,CAAA,GAAI;GAAE,aAAa,EAAE,OAAO,KAAK,UAAS,IAAK,OAAO,KAAI;GAAI,qBAAqB;GAAS,YAAY;EAAI,CAAE;CAC1I;CAEA,OAAO,QAAqB;EAC1B,AAAK,KAAK,OAAO,SAAS,CAAA,GAAI;GAAE,aAAa,EAAE,MAAM,OAAM;GAAI,qBAAqB;GAAS,YAAY;EAAI,CAAE;CACjH;CAEQ,aAAa,MAAU;EAC7B,MAAM,QAAQ,IAAI,KAAK,KAAK,SAAS,CAAC,CAAC,QAAO;EAC9C,OAAO;GAAE,uBAAM,IAAI,KAAK,QAAQ,GAAM,EAAC,CAAC,YAAW;GAAI,IAAI,IAAI,KAAK,QAAQ,KAAK,aAAa,GAAM,CAAC,CAAC,YAAW;EAAE;CACrH;CAGA,WAAW,MAAY,OAA6B;EAClD,OAAO;GAAE,GAAG,KAAK,IAAI,OAAO,KAAK,WAAW,KAAK,UAAU;GAAG,GAAG;EAAK;CACxE;CAlJW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAS,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAT,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAS,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,SAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,MAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,OAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,MAAA,EAAA,gBAAA,eAAA;EAAA,UAAA;EAAA,UA3HV;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyHN,UAAA;EAAA,cAAA;GAAA;IAAA,MAAA;IAAA,MA1HgB;IAAU,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAY;GAAe;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;IAAA,QAAA,CAAA,WAAA;IAAA,UAAA,CAAA,aAAA,WAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;IAAA,UAAA,CAAA,aAAA,WAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;GAAa;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;IAAA,UAAA,CAAA,SAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;GAAoB;GAAA;IAAA,MAAA;IAAA,MAAA,GAAA;IAAA,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;IAAA;IAAA,SAAA,CAAA,cAAA;IAAA,UAAA,CAAA,gBAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAU,UAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAa,UAAA;IAAA,QAAA,CAAA,UAAA,OAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAE;IAAQ,UAAA;IAAA,QAAA,CAAA,SAAA,SAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAgB;IAAc,UAAA;IAAA,QAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;KAAA;IAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAvJ;IAAQ,MAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAAc;IAAQ,MAAA;GAAA;GAAA;IAAA,MAAA;IAAA,MAA6F;IAAY,MAAA;GAAA;EAAA;EAAA,iBAAA,GAAA,wBAAA;CAAA,CAAA;;AA4HtI,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAS,YAAA,CAAA;EAhIrB,MAAA;EAAU,MAAA,CAAA;GACT,iBAAiB,wBAAwB;GAEzC,MAAM,EAAE,OAAO,eAAc;GAC7B,SAAS;IAAC;IAAU;IAAY;IAAU;IAAiB;IAAe;IAAsB;IAAY;IAAe;IAAU;IAAc;GAAc;GACjK,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0HX,CAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ApiService, ContextService } from "./juice-js-observability.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { S as apiResource, a as TraceTable, u as ContextBar } from "./juice-js-observability-telemetry-BftWSOaQ.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-DXSDdeTH.mjs.map
|