@juice-js/observability 1.0.1-alpha.17 → 1.1.0-alpha.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/fesm2022/juice-js-observability-service.page-rGAvUK_k.mjs +1223 -0
- package/fesm2022/juice-js-observability-service.page-rGAvUK_k.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-topology.page-aMWEXyMB.mjs → juice-js-observability-topology.page-CUayiRVT.mjs} +55 -3
- package/fesm2022/juice-js-observability-topology.page-CUayiRVT.mjs.map +1 -0
- package/fesm2022/{juice-js-observability-trace.page-DUCWnZL5.mjs → juice-js-observability-trace.page-ByxGFLpE.mjs} +9 -7
- package/fesm2022/juice-js-observability-trace.page-ByxGFLpE.mjs.map +1 -0
- package/fesm2022/juice-js-observability.mjs +9 -3
- package/fesm2022/juice-js-observability.mjs.map +1 -1
- package/package.json +1 -1
- package/styles/_observability.scss +20 -3
- package/types/juice-js-observability.d.ts +49 -0
- package/fesm2022/juice-js-observability-service.page-Dt8bN2Ry.mjs +0 -726
- package/fesm2022/juice-js-observability-service.page-Dt8bN2Ry.mjs.map +0 -1
- package/fesm2022/juice-js-observability-topology.page-aMWEXyMB.mjs.map +0 -1
- package/fesm2022/juice-js-observability-trace.page-DUCWnZL5.mjs.map +0 -1
|
@@ -117,7 +117,11 @@
|
|
|
117
117
|
.jc-list-row { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; padding: 6px 0; border-bottom: 1px solid var(--jc-border); }
|
|
118
118
|
.jc-list-row:last-child { border-bottom: 0; }
|
|
119
119
|
.jc-summary { display: flex; flex-wrap: wrap; gap: 8px 16px; align-items: center; margin-bottom: 8px; }
|
|
120
|
-
.jc-actions { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0; }
|
|
120
|
+
.jc-actions { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0; min-width: 0; }
|
|
121
|
+
// A button label can carry a span name (a whole SQL statement, say): keep each button within the row and cut its label
|
|
122
|
+
// with an ellipsis; the full text is on the button's title.
|
|
123
|
+
.jc-actions > .mat-mdc-button-base { max-width: 100%; min-width: 0; }
|
|
124
|
+
.jc-actions .mdc-button__label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
121
125
|
// The filters, the context bar and every other field the pages size label each field. An app theme denser than -1 hides a fill field's label (and
|
|
122
126
|
// these fields have no placeholder to fall back on), so keep them at -1 whatever the app's density.
|
|
123
127
|
.jc-context, .jc-filter-form, mat-form-field[class*='jc-field-'] { @include mat.form-field-density(-1); }
|
|
@@ -155,12 +159,21 @@
|
|
|
155
159
|
.jc-health-card.jc-selected { border-color: var(--jc-accent); }
|
|
156
160
|
|
|
157
161
|
.jc-chart-title { font-size: 13px; color: var(--jc-muted); margin: 4px 0; }
|
|
162
|
+
.jc-capacity-inputs { display: flex; flex-wrap: wrap; gap: 12px; margin: 8px 0 16px; }
|
|
163
|
+
.jc-capacity-inputs mat-form-field { width: 170px; }
|
|
164
|
+
.jc-capacity-result { display: flex; flex-wrap: wrap; gap: 12px 32px; margin-bottom: 12px; }
|
|
165
|
+
.jc-capacity-result div { display: flex; flex-direction: column; }
|
|
166
|
+
.jc-capacity-result strong { font-size: 24px; font-weight: 500; line-height: 1.2; }
|
|
167
|
+
.jc-capacity-notes { margin: 12px 0 0; padding-left: 18px; font-size: 13px; }
|
|
168
|
+
.jc-capacity-measured th { width: 240px; }
|
|
169
|
+
|
|
158
170
|
.jc-chart { height: 180px; width: 100%; background: var(--jc-surface); border: 1px solid var(--jc-border); border-radius: 6px; }
|
|
159
171
|
.jc-chart-empty { display: flex; align-items: center; justify-content: center; color: var(--jc-muted); font-size: 13px; }
|
|
160
172
|
|
|
161
173
|
// ─── Waterfall ───
|
|
162
174
|
.jc-legend { display: flex; flex-wrap: wrap; gap: 12px; font-size: 12px; margin-bottom: 6px; i { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 4px; } }
|
|
163
|
-
|
|
175
|
+
// minmax(0, …), not a bare 1fr: a 1fr column grows to fit its longest unbreakable line (a span name in a button label) instead of letting it clip.
|
|
176
|
+
.jc-trace { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; }
|
|
164
177
|
.jc-trace.jc-with-detail { grid-template-columns: minmax(0, 1.6fr) minmax(340px, 1fr); }
|
|
165
178
|
.jc-waterfall { background: var(--jc-surface); border: 1px solid var(--jc-border); border-radius: 8px; font-size: 12px; overflow: hidden; }
|
|
166
179
|
.jc-wf-head, .jc-wf-row { display: grid; grid-template-columns: minmax(260px, 40%) 1fr; align-items: center; }
|
|
@@ -187,6 +200,7 @@
|
|
|
187
200
|
.jc-usage-charts { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px; }
|
|
188
201
|
.jc-fill-page > .jc-trace > .jc-waterfall, .jc-fill-page > .jc-trace > .jc-span-detail { overflow: auto; max-height: none; position: static; }
|
|
189
202
|
.jc-fill-page .jc-wf-head { position: sticky; top: 0; z-index: 1; background: var(--jc-surface); }
|
|
203
|
+
.jc-span-detail h2 { min-width: 0; overflow-wrap: anywhere; }
|
|
190
204
|
.jc-span-facts { display: flex; gap: 12px; align-items: center; margin-bottom: 6px; }
|
|
191
205
|
.jc-event { border-left: 3px solid var(--jc-border); padding-left: 8px; margin: 6px 0; }
|
|
192
206
|
.jc-event.jc-wf-error { border-left-color: var(--jc-bad); }
|
|
@@ -197,10 +211,13 @@
|
|
|
197
211
|
.jc-graph-legend { display: flex; gap: 16px; align-items: center; font-size: 12px; color: var(--jc-muted); padding: 4px; }
|
|
198
212
|
.jc-line { display: inline-block; width: 24px; border-top: 2px solid var(--jc-muted); vertical-align: middle; }
|
|
199
213
|
.jc-line.jc-dashed { border-top-style: dashed; }
|
|
214
|
+
.jc-line.jc-line-warn { border-top: 3px solid var(--jc-warn); }
|
|
215
|
+
.jc-line.jc-line-bad { border-top: 3px solid var(--jc-bad); }
|
|
216
|
+
.jc-line.jc-line-idle { opacity: 0.35; }
|
|
200
217
|
.jc-side { align-self: start; }
|
|
201
218
|
|
|
202
219
|
@media (max-width: 900px) {
|
|
203
|
-
.jc-trace.jc-with-detail, .jc-topology { grid-template-columns: 1fr; }
|
|
220
|
+
.jc-trace.jc-with-detail, .jc-topology { grid-template-columns: minmax(0, 1fr); }
|
|
204
221
|
// Stacked panes: the page scrolls as a whole again.
|
|
205
222
|
mat-sidenav-content > .jc-fill-page { height: auto; display: block; overflow: visible; }
|
|
206
223
|
.jc-fill-page > .jc-trace > .jc-waterfall, .jc-fill-page > .jc-trace > .jc-span-detail { overflow: visible; }
|
|
@@ -299,6 +299,53 @@ export interface ServiceStats {
|
|
|
299
299
|
stepSeconds: number;
|
|
300
300
|
points: ServiceStatsPoint[];
|
|
301
301
|
}
|
|
302
|
+
/** What a service was measured to need, per instance. A unit of work is a request or a message. */
|
|
303
|
+
export interface Capacity {
|
|
304
|
+
stepSeconds: number;
|
|
305
|
+
samples: number;
|
|
306
|
+
instances: number;
|
|
307
|
+
truncated: boolean;
|
|
308
|
+
requestSource: 'metrics' | 'spans' | 'none';
|
|
309
|
+
hosts: string[];
|
|
310
|
+
coresPerInstance: number | null;
|
|
311
|
+
peakPerSecond: number;
|
|
312
|
+
responseMs: number | null;
|
|
313
|
+
responseLowLoadMs: number | null;
|
|
314
|
+
responseHighLoadMs: number | null;
|
|
315
|
+
peakQueue: number | null;
|
|
316
|
+
/** `fitted` separates idle CPU from CPU per unit; `average` is CPU over work in the busiest samples, so it can read high. */
|
|
317
|
+
cpuModel: 'fitted' | 'average' | 'none';
|
|
318
|
+
cpuSecondsPerUnit: number | null;
|
|
319
|
+
idleCores: number | null;
|
|
320
|
+
peakCores: number | null;
|
|
321
|
+
/** `fitted` is idle plus growth per unit in flight; `peak` is only the highest seen. */
|
|
322
|
+
memoryModel: 'fitted' | 'peak' | 'none';
|
|
323
|
+
idleMemoryBytes: number | null;
|
|
324
|
+
memoryBytesPerInFlight: number | null;
|
|
325
|
+
peakMemoryBytes: number | null;
|
|
326
|
+
/** Near 1, a consumer was busy nearly all the time: if it handles one message at a time, it was at its limit whatever CPU says. */
|
|
327
|
+
peakMessagesPerSecond: number;
|
|
328
|
+
peakMessagesInFlight: number;
|
|
329
|
+
/** Null when the service created and delivered no outbox messages in the window. */
|
|
330
|
+
outbox: OutboxBacklog | null;
|
|
331
|
+
}
|
|
332
|
+
/** `undelivered` is created minus delivered in the window, so it includes rolled-back messages. The delay is the p95 from creating to delivering, in the newest minutes. */
|
|
333
|
+
export interface OutboxBacklog {
|
|
334
|
+
createdPerSecond: number;
|
|
335
|
+
deliveredPerSecond: number;
|
|
336
|
+
undelivered: number;
|
|
337
|
+
recentDelaySeconds: number | null;
|
|
338
|
+
fallingBehind: boolean;
|
|
339
|
+
}
|
|
340
|
+
export interface DependencyStats {
|
|
341
|
+
dependencyId: string;
|
|
342
|
+
count: number;
|
|
343
|
+
errorRate: number;
|
|
344
|
+
}
|
|
345
|
+
export interface DependencyStatsPage {
|
|
346
|
+
items: DependencyStats[];
|
|
347
|
+
truncated: boolean;
|
|
348
|
+
}
|
|
302
349
|
export interface DependencyEvidence {
|
|
303
350
|
dependencyId: string;
|
|
304
351
|
relation: string;
|
|
@@ -349,6 +396,8 @@ export declare class ApiService {
|
|
|
349
396
|
metrics(query: Query): Observable<TelemetryPage<MetricInfo>>;
|
|
350
397
|
series(query: Query): Observable<Series>;
|
|
351
398
|
serviceStats(query: Query): Observable<ServiceStats>;
|
|
399
|
+
capacity(query: Query): Observable<Capacity>;
|
|
400
|
+
dependencyStats(query: Query): Observable<DependencyStatsPage>;
|
|
352
401
|
dependency(id: string, query: Query): Observable<DependencyEvidence>;
|
|
353
402
|
search(q: string): Observable<TelemetryPage<SearchHit>>;
|
|
354
403
|
static ɵfac: i0.ɵɵFactoryDeclaration<ApiService, never>;
|