h2o.jsonnet (23598B)
1 // H2O exposes every metric as its own metric name with only a `version` label, 2 // so metric families (status-errors.*, http2-errors.*, quic.num-frames-*, ...) 3 // are grouped here with {__name__=~"..."} selectors + label_replace. 4 5 local g = import 'github.com/grafana/grafonnet/gen/grafonnet-latest/main.libsonnet'; 6 7 local dashboard = g.dashboard; 8 local var = dashboard.variable; 9 local ts = g.panel.timeSeries; 10 local stat = g.panel.stat; 11 local gauge = g.panel.gauge; 12 local barGauge = g.panel.barGauge; 13 local row = g.panel.row; 14 local prometheus = g.query.prometheus; 15 16 // --------------------------------------------------------------------------- 17 // Shared selectors 18 // --------------------------------------------------------------------------- 19 local dsRef = { type: 'prometheus', uid: '${datasource}' }; 20 local selInner = 'job=~"$job",instance=~"$instance"'; 21 local sel = '{%s}' % selInner; 22 23 // rate() of a metric family by __name__ prefix, relabelled to a short legend. 24 // `keep_metric_names` is required because rate() otherwise drops __name__, 25 // collapsing every family member to the same labelset (a "duplicate output 26 // timeseries" error) and leaving label_replace nothing to match on. 27 local groupRate(prefix, label) = 28 'label_replace(rate({__name__=~"%s.+",%s}[$__rate_interval]) keep_metric_names, "%s", "$1", "__name__", "%s(.+)")' 29 % [prefix, selInner, label, prefix]; 30 31 // rate() of a single counter. 32 local rate(metric) = 'rate(%s%s[$__rate_interval])' % [metric, sel]; 33 34 // --------------------------------------------------------------------------- 35 // Threshold step sets 36 // --------------------------------------------------------------------------- 37 local greenOnly = [{ color: 'green', value: null }]; 38 local usedSteps = [ 39 { color: 'green', value: null }, 40 { color: 'yellow', value: 75 }, 41 { color: 'orange', value: 85 }, 42 { color: 'red', value: 90 }, 43 ]; 44 local errSteps = [ 45 { color: 'green', value: null }, 46 { color: 'yellow', value: 1 }, 47 { color: 'red', value: 10 }, 48 ]; 49 local upDownMap = [{ 50 type: 'value', 51 options: { 52 '0': { text: 'DOWN', color: 'red', index: 0 }, 53 '1': { text: 'UP', color: 'green', index: 1 }, 54 }, 55 }]; 56 57 // --------------------------------------------------------------------------- 58 // Query helper 59 // --------------------------------------------------------------------------- 60 local prom(expr, legend=null, refId='A', instant=false) = 61 prometheus.new('${datasource}', expr) 62 + prometheus.withRefId(refId) 63 + prometheus.withEditorMode('code') 64 + (if legend != null then prometheus.withLegendFormat(legend) else {}) 65 + ( 66 if instant then 67 prometheus.withInstant(true) 68 + prometheus.withFormat('table') 69 + prometheus.withRange(false) 70 else prometheus.withRange(true) 71 ); 72 73 // --------------------------------------------------------------------------- 74 // Panel helpers 75 // --------------------------------------------------------------------------- 76 local tsPanel(title, gp, targets, unit='short', steps=greenOnly, min=null, max=null, decimals=null, desc=null, fill=10, stack='none', interp='smooth', threshStyle='off', calcs=['lastNotNull', 'max', 'mean']) = 77 ts.new(title) 78 + ts.panelOptions.withGridPos(gp.h, gp.w, gp.x, gp.y) 79 + { datasource: dsRef } 80 + ts.queryOptions.withTargets(targets) 81 + ts.standardOptions.withUnit(unit) 82 + ts.standardOptions.color.withMode('palette-classic') 83 + ts.standardOptions.thresholds.withMode('absolute') 84 + ts.standardOptions.thresholds.withSteps(steps) 85 + ts.fieldConfig.defaults.custom.withDrawStyle('line') 86 + ts.fieldConfig.defaults.custom.withFillOpacity(fill) 87 + ts.fieldConfig.defaults.custom.withGradientMode('opacity') 88 + ts.fieldConfig.defaults.custom.withLineWidth(2) 89 + ts.fieldConfig.defaults.custom.withLineInterpolation(interp) 90 + ts.fieldConfig.defaults.custom.withShowPoints('never') 91 + ts.fieldConfig.defaults.custom.withSpanNulls(false) 92 + ts.fieldConfig.defaults.custom.withStacking({ mode: stack, group: 'A' }) 93 + ts.fieldConfig.defaults.custom.thresholdsStyle.withMode(threshStyle) 94 + ts.options.legend.withDisplayMode('table') 95 + ts.options.legend.withPlacement('bottom') 96 + ts.options.legend.withShowLegend(true) 97 + ts.options.legend.withCalcs(calcs) 98 + ts.options.tooltip.withMode('multi') 99 + ts.options.tooltip.withSort('desc') 100 + (if min != null then ts.standardOptions.withMin(min) else {}) 101 + (if max != null then ts.standardOptions.withMax(max) else {}) 102 + (if decimals != null then ts.standardOptions.withDecimals(decimals) else {}) 103 + (if desc != null then ts.panelOptions.withDescription(desc) else {}); 104 105 local statPanel(title, gp, targets, unit='short', mappings=[], steps=greenOnly, colorMode='value', textMode='auto', graphMode='area', decimals=null, calc='lastNotNull', desc=null) = 106 stat.new(title) 107 + stat.panelOptions.withGridPos(gp.h, gp.w, gp.x, gp.y) 108 + { datasource: dsRef } 109 + stat.queryOptions.withTargets(targets) 110 + stat.standardOptions.withUnit(unit) 111 + stat.standardOptions.withMappings(mappings) 112 + stat.standardOptions.color.withMode('thresholds') 113 + stat.standardOptions.thresholds.withMode('absolute') 114 + stat.standardOptions.thresholds.withSteps(steps) 115 + stat.options.withColorMode(colorMode) 116 + stat.options.withGraphMode(graphMode) 117 + stat.options.withTextMode(textMode) 118 + stat.options.withJustifyMode('auto') 119 + stat.options.withOrientation('auto') 120 + stat.options.reduceOptions.withCalcs([calc]) 121 + stat.options.reduceOptions.withFields('') 122 + stat.options.reduceOptions.withValues(false) 123 + (if decimals != null then stat.standardOptions.withDecimals(decimals) else {}) 124 + (if desc != null then stat.panelOptions.withDescription(desc) else {}); 125 126 local gaugePanel(title, gp, targets, unit='percent', steps=usedSteps, min=0, max=100, desc=null) = 127 gauge.new(title) 128 + gauge.panelOptions.withGridPos(gp.h, gp.w, gp.x, gp.y) 129 + { datasource: dsRef } 130 + gauge.queryOptions.withTargets(targets) 131 + gauge.standardOptions.withUnit(unit) 132 + gauge.standardOptions.withMin(min) 133 + gauge.standardOptions.withMax(max) 134 + gauge.standardOptions.color.withMode('thresholds') 135 + gauge.standardOptions.thresholds.withMode('absolute') 136 + gauge.standardOptions.thresholds.withSteps(steps) 137 + gauge.options.withShowThresholdLabels(false) 138 + gauge.options.withShowThresholdMarkers(true) 139 + gauge.options.withOrientation('auto') 140 + gauge.options.reduceOptions.withCalcs(['lastNotNull']) 141 + gauge.options.reduceOptions.withFields('') 142 + gauge.options.reduceOptions.withValues(false) 143 + (if desc != null then gauge.panelOptions.withDescription(desc) else {}); 144 145 local barGaugePanel(title, gp, targets, unit='short', colorMode='continuous-GrYlRd', steps=greenOnly, display='gradient', orientation='horizontal', decimals=null, desc=null) = 146 barGauge.new(title) 147 + barGauge.panelOptions.withGridPos(gp.h, gp.w, gp.x, gp.y) 148 + { datasource: dsRef } 149 + barGauge.queryOptions.withTargets(targets) 150 + barGauge.standardOptions.withUnit(unit) 151 + barGauge.standardOptions.color.withMode(colorMode) 152 + barGauge.standardOptions.thresholds.withMode('absolute') 153 + barGauge.standardOptions.thresholds.withSteps(steps) 154 + barGauge.options.withDisplayMode(display) 155 + barGauge.options.withOrientation(orientation) 156 + barGauge.options.withShowUnfilled(true) 157 + barGauge.options.withValueMode('color') 158 + barGauge.options.reduceOptions.withCalcs(['lastNotNull']) 159 + barGauge.options.reduceOptions.withFields('') 160 + barGauge.options.reduceOptions.withValues(false) 161 + (if decimals != null then barGauge.standardOptions.withDecimals(decimals) else {}) 162 + (if desc != null then barGauge.panelOptions.withDescription(desc) else {}); 163 164 local rowPanel(title, y) = 165 row.new(title) 166 + row.withGridPos(y) 167 + row.withCollapsed(false); 168 169 // percentile series for a latency phase (h2o_<phase>_time_{0,25,50,75,99}). 170 local pctTargets(metric) = [ 171 prom('%s_0%s' % [metric, sel], legend='p0 (min)', refId='A'), 172 prom('%s_25%s' % [metric, sel], legend='p25', refId='B'), 173 prom('%s_50%s' % [metric, sel], legend='p50', refId='C'), 174 prom('%s_75%s' % [metric, sel], legend='p75', refId='D'), 175 prom('%s_99%s' % [metric, sel], legend='p99', refId='E'), 176 ]; 177 178 // --------------------------------------------------------------------------- 179 // Variables 180 // --------------------------------------------------------------------------- 181 local datasourceVar = 182 var.datasource.new('datasource', 'prometheus') 183 + var.datasource.generalOptions.withLabel('Data source') 184 + var.datasource.withRegex('') 185 + { current: { selected: false, text: 'VictoriaMetrics', value: 'DS_PROMETHEUS' } }; 186 187 local queryVar(name, label, expr, valueLabel) = 188 var.query.new(name) 189 + var.query.withDatasourceFromVariable(datasourceVar) 190 + var.query.queryTypes.withLabelValues(valueLabel, expr) 191 + var.query.generalOptions.withLabel(label) 192 + var.query.selectionOptions.withMulti(true) 193 + var.query.selectionOptions.withIncludeAll(true) 194 + var.query.withRefresh('time') 195 + var.query.withSort(1); 196 197 local jobVar = queryVar('job', 'Job', 'h2o_uptime', 'job'); 198 local instanceVar = queryVar('instance', 'Instance', 'h2o_uptime{job=~"$job"}', 'instance'); 199 200 // --------------------------------------------------------------------------- 201 // Panels 202 // --------------------------------------------------------------------------- 203 local panels = [ 204 //=== Overview ============================================================ 205 rowPanel('Overview', 0), 206 207 statPanel('Version', { h: 4, w: 3, x: 0, y: 1 }, 208 [prom('h2o_uptime%s' % sel, legend='{{version}}')], 209 unit='short', graphMode='none', textMode='name', colorMode='none', 210 desc='Running H2O version.'), 211 212 statPanel('Uptime', { h: 4, w: 3, x: 3, y: 1 }, 213 [prom('h2o_uptime%s' % sel)], 214 unit='s', graphMode='none', 215 desc='Seconds since the current worker process started.'), 216 217 statPanel('Connections', { h: 4, w: 3, x: 6, y: 1 }, 218 [prom('h2o_connections%s' % sel)], 219 unit='short'), 220 221 statPanel('Sessions', { h: 4, w: 3, x: 9, y: 1 }, 222 [prom('h2o_num_sessions%s' % sel)], 223 unit='short'), 224 225 statPanel('Generation', { h: 4, w: 3, x: 12, y: 1 }, 226 [prom('h2o_generation%s' % sel)], 227 unit='short', graphMode='none', 228 desc='Configuration generation — increments on each graceful reload.'), 229 230 statPanel('Worker Threads', { h: 4, w: 3, x: 15, y: 1 }, 231 [prom('h2o_worker_threads%s' % sel)], 232 unit='short', graphMode='none'), 233 234 statPanel('Listeners', { h: 4, w: 3, x: 18, y: 1 }, 235 [prom('h2o_listeners%s' % sel)], 236 unit='short', graphMode='none'), 237 238 gaugePanel('Connection Utilization', { h: 4, w: 3, x: 21, y: 1 }, 239 [prom('h2o_connections%s / h2o_max_connections%s * 100' % [sel, sel])], 240 desc='Current connections as a percentage of max-connections.'), 241 242 //=== Connections ========================================================= 243 rowPanel('Connections', 5), 244 245 tsPanel('Connections & Sessions', { h: 8, w: 12, x: 0, y: 6 }, 246 [ 247 prom('h2o_connections%s' % sel, legend='connections', refId='A'), 248 prom('h2o_num_sessions%s' % sel, legend='sessions', refId='B'), 249 prom('h2o_connections_active%s' % sel, legend='active', refId='C'), 250 prom('h2o_connections_idle%s' % sel, legend='idle', refId='D'), 251 ], 252 unit='short', 253 desc='Live connection and session counts.'), 254 255 tsPanel('Connection Churn', { h: 8, w: 12, x: 12, y: 6 }, 256 [ 257 prom(rate('h2o_connections_idle_closed'), legend='idle-closed', refId='A'), 258 prom(rate('h2o_connections_shutdown'), legend='shutdown', refId='B'), 259 ], 260 unit='cps', 261 desc='Rate of connections closed for being idle, and connections shut down.'), 262 263 //=== Request Latency ===================================================== 264 rowPanel('Request Latency (per H2O status durations, ms)', 14), 265 266 tsPanel('Request Total Time — percentiles', { h: 8, w: 12, x: 0, y: 15 }, 267 pctTargets('h2o_request_total_time'), 268 unit='ms', threshStyle='off', 269 desc='End-to-end request time distribution (p0/p25/p50/p75/p99).'), 270 271 tsPanel('Duration — percentiles', { h: 8, w: 12, x: 12, y: 15 }, 272 pctTargets('h2o_duration'), 273 unit='ms', 274 desc='Connection duration distribution.'), 275 276 tsPanel('Request Phases @ p50', { h: 8, w: 12, x: 0, y: 23 }, 277 [ 278 prom('h2o_connect_time_50%s' % sel, legend='connect', refId='A'), 279 prom('h2o_header_time_50%s' % sel, legend='header', refId='B'), 280 prom('h2o_body_time_50%s' % sel, legend='body', refId='C'), 281 prom('h2o_process_time_50%s' % sel, legend='process', refId='D'), 282 prom('h2o_response_time_50%s' % sel, legend='response', refId='E'), 283 prom('h2o_request_total_time_50%s' % sel, legend='total', refId='F'), 284 ], 285 unit='ms', 286 desc='Median latency contribution of each request phase.'), 287 288 tsPanel('Request Phases @ p99', { h: 8, w: 12, x: 12, y: 23 }, 289 [ 290 prom('h2o_connect_time_99%s' % sel, legend='connect', refId='A'), 291 prom('h2o_header_time_99%s' % sel, legend='header', refId='B'), 292 prom('h2o_body_time_99%s' % sel, legend='body', refId='C'), 293 prom('h2o_process_time_99%s' % sel, legend='process', refId='D'), 294 prom('h2o_response_time_99%s' % sel, legend='response', refId='E'), 295 prom('h2o_request_total_time_99%s' % sel, legend='total', refId='F'), 296 ], 297 unit='ms', 298 desc='Tail latency contribution of each request phase.'), 299 300 //=== HTTP Status & Errors ================================================ 301 rowPanel('HTTP Status & Errors', 31), 302 303 tsPanel('HTTP Status Errors', { h: 8, w: 12, x: 0, y: 32 }, 304 [prom(groupRate('h2o_status_errors_', 'code'), legend='{{code}}')], 305 unit='cps', stack='normal', fill=25, 306 desc='Per-status-code error response rate (4xx / 5xx).'), 307 308 tsPanel('Protocol Errors', { h: 8, w: 12, x: 12, y: 32 }, 309 [ 310 prom(groupRate('h2o_http2_errors_', 'kind'), legend='h2: {{kind}}', refId='A'), 311 prom(groupRate('h2o_http1_errors_', 'kind'), legend='h1: {{kind}}', refId='B'), 312 ], 313 unit='cps', 314 desc='HTTP/1 and HTTP/2 protocol error rates by kind.'), 315 316 barGaugePanel('Status Errors — total', { h: 7, w: 12, x: 0, y: 40 }, 317 [prom('label_replace({__name__=~"h2o_status_errors_.+",%s}, "code", "$1", "__name__", "h2o_status_errors_(.+)")' % selInner, legend='{{code}}')], 318 unit='short', display='gradient'), 319 320 tsPanel('HTTP/2 Activity', { h: 7, w: 12, x: 12, y: 40 }, 321 [ 322 prom(rate('h2o_http2_read_closed'), legend='read-closed', refId='A'), 323 prom(rate('h2o_http2_write_closed'), legend='write-closed', refId='B'), 324 prom(rate('h2o_http2_idle_timeout'), legend='idle-timeout', refId='C'), 325 prom(rate('h2o_http2_streaming_requests'), legend='streaming-requests', refId='D'), 326 ], 327 unit='cps', 328 desc='HTTP/2 stream lifecycle events.'), 329 330 //=== TLS / SSL =========================================================== 331 rowPanel('TLS / SSL', 47), 332 333 tsPanel('TLS Handshakes', { h: 7, w: 12, x: 0, y: 48 }, 334 [ 335 prom(rate('h2o_ssl_handshake_full'), legend='full', refId='A'), 336 prom(rate('h2o_ssl_handshake_resume'), legend='resume', refId='B'), 337 ], 338 unit='ops', 339 desc='Full vs. resumed TLS handshakes per second.'), 340 341 tsPanel('ALPN Negotiation', { h: 7, w: 12, x: 12, y: 48 }, 342 [ 343 prom(rate('h2o_ssl_alpn_h1'), legend='http/1.1', refId='A'), 344 prom(rate('h2o_ssl_alpn_h2'), legend='h2', refId='B'), 345 ], 346 unit='ops', 347 desc='Negotiated ALPN protocol per second.'), 348 349 gaugePanel('Handshake Resume Ratio', { h: 6, w: 6, x: 0, y: 55 }, 350 [prom('sum(%s) / clamp_min(sum(%s) + sum(%s), 1) * 100' 351 % [rate('h2o_ssl_handshake_resume'), rate('h2o_ssl_handshake_full'), rate('h2o_ssl_handshake_resume')])], 352 steps=[{ color: 'red', value: null }, { color: 'yellow', value: 40 }, { color: 'green', value: 70 }], 353 desc='Share of handshakes served from session resumption (higher is cheaper).'), 354 355 statPanel('SSL Errors', { h: 6, w: 6, x: 6, y: 55 }, 356 [prom('h2o_ssl_errors%s' % sel)], 357 unit='short', graphMode='area', steps=errSteps), 358 359 statPanel('Avg Full Handshake', { h: 6, w: 6, x: 12, y: 55 }, 360 [prom('%s / clamp_min(%s, 1)' % [rate('h2o_ssl_handshake_accumulated_time_full'), rate('h2o_ssl_handshake_full')])], 361 unit='µs', graphMode='area', 362 desc='Mean wall time per full handshake (H2O reports accumulated time in microseconds).'), 363 364 statPanel('Avg Resume Handshake', { h: 6, w: 6, x: 18, y: 55 }, 365 [prom('%s / clamp_min(%s, 1)' % [rate('h2o_ssl_handshake_accumulated_time_resume'), rate('h2o_ssl_handshake_resume')])], 366 unit='µs', graphMode='area', 367 desc='Mean wall time per resumed handshake.'), 368 369 //=== QUIC / HTTP3 ======================================================== 370 rowPanel('QUIC / HTTP3', 61), 371 372 tsPanel('QUIC Packets', { h: 8, w: 12, x: 0, y: 62 }, 373 [ 374 prom(rate('h2o_quic_num_packets_received'), legend='received', refId='A'), 375 prom(rate('h2o_quic_num_packets_sent'), legend='sent', refId='B'), 376 prom(rate('h2o_quic_num_packets_lost'), legend='lost', refId='C'), 377 prom(rate('h2o_quic_num_packets_ack_received'), legend='ack-received', refId='D'), 378 prom(rate('h2o_quic_packet_processed'), legend='processed', refId='E'), 379 ], 380 unit='pps', 381 desc='QUIC packet throughput and loss.'), 382 383 tsPanel('QUIC Bytes', { h: 8, w: 12, x: 12, y: 62 }, 384 [ 385 prom(rate('h2o_quic_num_bytes_received'), legend='received', refId='A'), 386 prom(rate('h2o_quic_num_bytes_sent'), legend='sent', refId='B'), 387 prom(rate('h2o_quic_num_bytes_lost'), legend='lost', refId='C'), 388 prom(rate('h2o_quic_num_bytes_stream_data_sent'), legend='stream-data-sent', refId='D'), 389 ], 390 unit='Bps', 391 desc='QUIC byte throughput.'), 392 393 statPanel('QUIC Packet Loss', { h: 6, w: 6, x: 0, y: 70 }, 394 [prom('%s / clamp_min(%s, 1) * 100' % [rate('h2o_quic_num_packets_lost'), rate('h2o_quic_num_packets_sent')])], 395 unit='percent', graphMode='area', steps=errSteps, 396 desc='Lost packets as a fraction of sent.'), 397 398 tsPanel('QUIC Paths', { h: 6, w: 9, x: 6, y: 70 }, 399 [prom(groupRate('h2o_quic_num_paths_', 'event'), legend='{{event}}')], 400 unit='cps', 401 desc='QUIC path validation / migration events.'), 402 403 tsPanel('HTTP/3 Packet Forwarding', { h: 6, w: 9, x: 15, y: 70 }, 404 [ 405 prom(rate('h2o_http3_packet_forwarded'), legend='forwarded', refId='A'), 406 prom(rate('h2o_http3_forwarded_packet_received'), legend='forwarded-received', refId='B'), 407 ], 408 unit='pps'), 409 410 tsPanel('QUIC Frames Received', { h: 8, w: 12, x: 0, y: 76 }, 411 [prom(groupRate('h2o_quic_num_frames_received_', 'frame'), legend='{{frame}}')], 412 unit='cps', 413 desc='Inbound QUIC frames by type.'), 414 415 tsPanel('QUIC Frames Sent', { h: 8, w: 12, x: 12, y: 76 }, 416 [prom(groupRate('h2o_quic_num_frames_sent_', 'frame'), legend='{{frame}}')], 417 unit='cps', 418 desc='Outbound QUIC frames by type.'), 419 420 tsPanel('QUIC Recovery & Congestion', { h: 7, w: 12, x: 0, y: 84 }, 421 [ 422 prom(rate('h2o_quic_num_ptos'), legend='ptos', refId='A'), 423 prom(rate('h2o_quic_num_handshake_timeouts'), legend='handshake-timeouts', refId='B'), 424 prom(rate('h2o_quic_num_initial_handshake_exceeded'), legend='initial-handshake-exceeded', refId='C'), 425 prom(rate('h2o_quic_num_paced'), legend='paced', refId='D'), 426 prom(rate('h2o_quic_num_rapid_start'), legend='rapid-start', refId='E'), 427 prom(rate('h2o_quic_num_respected_app_limited'), legend='respected-app-limited', refId='F'), 428 ], 429 unit='cps', 430 desc='QUIC loss recovery, pacing and congestion-control signals.'), 431 432 tsPanel('QUIC ECN', { h: 7, w: 12, x: 12, y: 84 }, 433 [ 434 prom(rate('h2o_quic_num_packets_received_ecn_ect0'), legend='rx ect0', refId='A'), 435 prom(rate('h2o_quic_num_packets_received_ecn_ect1'), legend='rx ect1', refId='B'), 436 prom(rate('h2o_quic_num_packets_received_ecn_ce'), legend='rx ce', refId='C'), 437 prom(rate('h2o_quic_num_packets_acked_ecn_ect0'), legend='acked ect0', refId='D'), 438 prom(rate('h2o_quic_num_packets_acked_ecn_ect1'), legend='acked ect1', refId='E'), 439 prom(rate('h2o_quic_num_packets_acked_ecn_ce'), legend='acked ce', refId='F'), 440 ], 441 unit='pps', 442 desc='Explicit Congestion Notification marks on received and acked packets.'), 443 444 //=== Memory & Internals ================================================== 445 rowPanel('Memory & Internals', 91), 446 447 tsPanel('Memory Pool Chunks', { h: 7, w: 12, x: 0, y: 92 }, 448 [ 449 prom('h2o_memory_mem_pool_chunks%s' % sel, legend='mem-pool', refId='A'), 450 prom('h2o_memory_socket_ssl_chunks%s' % sel, legend='socket-ssl', refId='B'), 451 prom('h2o_memory_socket_zerocopy_chunks%s' % sel, legend='socket-zerocopy', refId='C'), 452 prom('h2o_memory_socket_zerocopy_inflight%s' % sel, legend='zerocopy-inflight', refId='D'), 453 ], 454 unit='short', 455 desc='Recycled buffer chunk counts per allocator.'), 456 457 tsPanel('Event Loop Latency', { h: 7, w: 12, x: 12, y: 92 }, 458 [prom('h2o_evloop_latency_nanosec%s' % sel, legend='evloop')], 459 unit='ns', 460 desc='Event-loop latency (only populated when latency-optimization probing is enabled).'), 461 462 statPanel('mmap Errors', { h: 5, w: 6, x: 0, y: 99 }, 463 [prom('h2o_memory_mmap_errors%s' % sel)], 464 unit='short', graphMode='area', steps=errSteps), 465 466 statPanel('h2olog Lost', { h: 5, w: 6, x: 6, y: 99 }, 467 [prom('h2o_h2olog_lost%s' % sel)], 468 unit='short', graphMode='area', steps=errSteps, 469 desc='h2olog events dropped because the ring buffer was full.'), 470 471 statPanel('Max Connections', { h: 5, w: 6, x: 12, y: 99 }, 472 [prom('h2o_max_connections%s' % sel)], 473 unit='short', graphMode='none', colorMode='none'), 474 475 statPanel('Soft Limit Min Age', { h: 5, w: 6, x: 18, y: 99 }, 476 [prom('h2o_soft_connection_limit_min_age%s' % sel)], 477 unit='s', graphMode='none', colorMode='none', 478 desc='Minimum connection age before the soft connection limit may close it.'), 479 ]; 480 481 // Assign stable, unique panel IDs. 482 local panelsWithIds = std.mapWithIndex(function(i, p) p + { id: i + 1 }, panels); 483 484 // --------------------------------------------------------------------------- 485 // Dashboard 486 // --------------------------------------------------------------------------- 487 dashboard.new('H2O') 488 + dashboard.withUid('h2o') 489 + dashboard.withTags(['h2o', 'http', 'webserver']) 490 + dashboard.withRefresh('30s') 491 + dashboard.withTimezone('browser') 492 + dashboard.withVariables([datasourceVar, jobVar, instanceVar]) 493 + dashboard.withPanels(panelsWithIds) 494 + { graphTooltip: 1, time: { from: 'now-6h', to: 'now' } }