@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.
@@ -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
- .jc-trace { display: grid; grid-template-columns: 1fr; gap: 12px; }
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>;