BanyanDB self-observability dashboard
Apache SkyWalking BanyanDB is the
native storage for SkyWalking. A production deployment is one cluster made of many nodes, each
running one or more containers with a role (liaison front door, data backend, and the lifecycle
tier-migration sidecar), and data is organized into groups. SkyWalking models that reality directly
and renders it on the Layer: BANYANDB dashboards in the Horizon UI:
| SkyWalking entity | BanyanDB concept | Identity |
|---|---|---|
Service |
one BanyanDB cluster | the cluster label |
ServiceInstance |
one container on a node | container_name + pod_name (joined by @, e.g. data@…-data-hot-0) |
| ↳ attributes | role / tier | container_name (liaison/data/lifecycle), node_type (hot/warm/cold), node_role, pod_name |
Endpoint |
one group (storage partition) | the group label (e.g. sw_metricsMinute) |
Requires BanyanDB 0.11+. This feature reads the FODC-proxy cluster-observability metric families and the queue / lifecycle metric families that BanyanDB introduced after 0.10. Run a 0.11+ cluster with the FODC proxy and the Prometheus metrics provider enabled.
Data flow
- Each BanyanDB container exposes its metrics; in a cluster the
FODC proxy
aggregates every container’s Prometheus metrics onto a single
/metricsendpoint (default:17913) and stamps each sample with per-container identity labels (pod_name,container_name,node_role, andnode_typeon data containers). - An OpenTelemetry Collector scrapes the FODC proxy
/metricsas the single Prometheus target, adds a staticcluster: <name>label (the only label SkyWalking must inject), and pushes via the OpenTelemetry gRPC exporter to the SkyWalking OAP Server. - The OAP Server parses the MAL rules under
otel-rules/banyandb/to filter / calculate / aggregate and store the cluster, instance and group metrics.
Set up
-
Run a BanyanDB 0.11+ cluster (liaison + data nodes; data nodes may be tiered hot/warm/cold) with the FODC proxy enabled and the Prometheus metrics provider on (default). Standalone mode is the degenerate case — one cluster, one node, one
container_name=standalone. -
Run an OpenTelemetry Collector whose
prometheusreceiver scrapes the FODC proxy/metrics(:17913) as the single target and adds a staticcluster: <name>label, exporting OTLP to OAP. The FODC proxy already stamps the per-node identity labels (pod_name/container_name/node_role/node_type), soclusteris the only label the collector must inject.The scrape
job_nameMUST bebanyandb-monitoring. Every rule file filters on{ tags -> tags.job_name == 'banyandb-monitoring' }(the OTel receiver maps the Prometheusjobto thejob_nametag), so a differently-named job produces no metrics.receivers: prometheus: config: scrape_configs: - job_name: "banyandb-monitoring" # REQUIRED — the rules filter on it scrape_interval: 15s static_configs: - targets: ["<fodc-proxy-host>:17913"] # the FODC proxy aggregates every node's metrics labels: cluster: <your-cluster-name> # the only label SkyWalking must inject exporters: otlp: endpoint: <oap-host>:11800 tls: insecure: true service: pipelines: metrics: receivers: [prometheus] processors: [batch] exporters: [otlp]Scrape the FODC proxy, not the individual nodes. The proxy resolves each node’s identity from the cluster and stamps
pod_name/container_name/node_roleand the data-node tier (node_type) onto every sample — context the raw per-node:2121endpoints do not carry. Direct per-node scraping is not recommended: it would have to hand-inject all of those identity labels for every node. (The e2e does exactly that only because it runs no FODC proxy — see the test collector config — and is not a production pattern.) -
Enable SkyWalking’s OpenTelemetry receiver. The
banyandb/*rules are enabled by default inenabledOtelMetricsRules. -
Open the Horizon UI →
BanyanDBlayer.
Metrics
The metric source expressions mirror the upstream BanyanDB Grafana boards, so the SkyWalking dashboards
stay in lockstep with the BanyanDB catalog. The rule files are
otel-rules/banyandb/banyandb-service.yaml, banyandb-instance.yaml, banyandb-endpoint.yaml,
banyandb-instance-relation.yaml and banyandb-trace-sampling.yaml.
The instance and endpoint catalogs are category-separated: the rule name carries a role prefix
(instance scope) or a data-type prefix (endpoint scope) so that a human can read a metric name and know
which role / data type it belongs to, and so the UI layer template can select the right panel set. The
storage prefix is on the rule name only — every metric still carries the
meter_banyandb_{instance,endpoint,instance_relation}_ family prefix, and the scope / entity keys are
unchanged.
Service scope — cluster summary (meter_banyandb_*)
| Unit | Metric | Description |
|---|---|---|
| w/s | meter_banyandb_cluster_write_rate |
Cluster write rate across measure/stream/trace |
| r/s | meter_banyandb_cluster_query_rate |
Cluster query rate |
| c/m | meter_banyandb_cluster_error_rate |
Cluster error rate (counts/min) |
| Count | meter_banyandb_reporting_instances |
Live container count by role |
| Count | meter_banyandb_total_cpu_cores |
Cluster CPU capacity |
| Bytes | meter_banyandb_total_memory_used |
Cluster memory used |
| Bytes | meter_banyandb_total_disk_used |
Cluster disk used |
Instance scope — per container (meter_banyandb_instance_*)
Instance rules are role-separated by name prefix. The shared resource / runtime block stays
unprefixed (the family is inherently per-instance and resolves on whatever container emits it);
front-door families carry a liaison_* prefix, storage / index / queue families a data_* prefix, and
the migration-sidecar health triple a lifecycle_* prefix. The prefix lets the UI select the panel set
per role (container_name liaison / data / lifecycle) and disambiguates the same wire family read
under two roles (e.g. the pending_data_count family is liaison_wqueue_pending on the front door and
data_wqueue_pending on the backend — each role rule reads only its own container’s series).
Shared — resources / disk-by-path / Go runtime (unprefixed; every container emits these, except
node_uptime which is absent on lifecycle — that container runs the metric service without the system
collector):
| Unit | Metric | Description |
|---|---|---|
| s | node_uptime |
Node uptime |
| Cores | cpu_usage |
CPU usage |
| Bytes | rss_memory |
Resident memory |
| percent | system_memory_percent |
System memory used % |
| percent | disk_usage_percent |
Disk used % (BanyanDB used_percent, averaged across the node’s data paths, which share one filesystem) |
| Bytes | disk_used_by_path / disk_total_by_path |
Disk used / total bytes by mount path |
| percent | disk_used_percent_by_path |
Disk used % by mount path |
| Bytes/s | network_recv / network_sent |
Network throughput by interface |
| Count | goroutines |
Go goroutines |
| s | gc_pause_avg |
Average GC pause |
| Bytes | heap_inuse / heap_next_gc |
Go heap in-use / next-GC threshold |
| Bytes/s | alloc_rate |
Go allocation rate |
Liaison (liaison_*; front door — the dashboard gates these on container_name == 'liaison'):
| Unit | Metric | Description |
|---|---|---|
| r/s | liaison_query_rate |
Query rate by data-model service (measure/stream/trace/property) |
| c/m | liaison_grpc_error_rate |
gRPC error rate (total + registry + stream-msg-received errors) |
| r/s | liaison_registry_op_rate |
Schema-registry / non-query operation rate |
| w/s | liaison_write_rate |
Write rate seen at the front door |
| ops | liaison_publish_throughput |
Tier-2 publish throughput by operation (liaison → data) |
| Bytes/s | liaison_publish_bytes |
Publish bytes |
| s | liaison_publish_latency_p99 |
Publish send latency p99 |
| ops | liaison_publish_batch_throughput |
Tier-2 publish batch throughput by operation (build-gated, BanyanDB #1169) |
| s | liaison_publish_batch_latency_p99 |
Publish batch send latency p99 (build-gated, BanyanDB #1169) |
| Count | liaison_wqueue_pending |
Front-door write-queue pending records |
Data (data_*; backend — the dashboard gates these on container_name == 'data'):
| Unit | Metric | Description |
|---|---|---|
| Count | data_total_data |
Total stored data elements |
| Count | data_wqueue_file_parts |
Write-queue on-disk file parts |
| Count | data_wqueue_mem_part |
Write-queue in-memory parts |
| Count | data_wqueue_pending |
Write-queue pending records |
| o/s | data_merge_file_rate |
Merge-loop rate |
| Count | data_merge_file_partitions |
Avg parts merged per loop (file path) |
| s | data_merge_file_latency |
Avg file-merge latency |
| o/s | data_series_write_rate |
Inverted-index write rate (measure + stream + trace storage indexes) |
| o/s | data_series_term_search_rate |
Inverted-index term-search rate |
| Count | data_total_series |
Inverted-index documents (measure + stream + trace storage indexes) |
| o/s | data_stream_tst_write_rate |
Stream tst index write rate |
| o/s | data_stream_tst_term_search_rate |
Stream tst index term-search rate |
| Count | data_stream_tst_total_docs |
Stream tst index documents |
| ops | data_queue_sub_throughput |
Subscribe-queue throughput by operation |
| s | data_queue_sub_latency_p99 |
Subscribe-queue latency p99 |
| ops | data_queue_sub_message_throughput |
Subscribe-queue per-message throughput by operation (BanyanDB #1169) |
| percent | data_retention_measure_disk_usage_percent |
Retention disk-usage % (measure scope) |
| percent | data_retention_stream_disk_usage_percent |
Retention disk-usage % (stream scope) |
| percent | data_retention_trace_disk_usage_percent |
Retention disk-usage % (trace scope) |
The trace storage inverted index is now folded into
data_series_write_rate/data_series_term_search_rate/data_total_series(it was silently dropped in the previous, measure+stream-only design).
Lifecycle (lifecycle_*; the tier-migration sidecar on hot/warm data pods —
container_name == 'lifecycle'):
| Unit | Metric | Description |
|---|---|---|
| Count | lifecycle_migration_cycles |
Cumulative migration cycles |
| s | lifecycle_last_run |
Seconds since the last migration cycle started (build-gated, BanyanDB #1167+) |
| Status | lifecycle_last_run_success |
Last cycle status (1 = OK, 0 = failed; build-gated, BanyanDB #1167+) |
Endpoint scope — per group (meter_banyandb_endpoint_*)
A group carries exactly one data-model type, and each type emits a different family namespace, so the
endpoint rules are type-separated by name prefix (measure_* / stream_* / stream_tst_* /
trace_* / property_*). The previous design summed measure + stream + trace into one unified rule per
concept, which (a) rendered all-empty panels for a property group and (b) silently dropped the trace
inverted index from series_* / total_series. The per-type split makes each rule read only the
families its type genuinely emits, and the UI selects the panel set by the group’s data type.
The queue / publish metrics stay type-agnostic (keyed on group + operation, not on a data-model
type) and keep their bare names.
Measure (measure_*):
| Unit | Metric | Description |
|---|---|---|
| w/s | measure_write_rate |
Write rate for the group |
| s | measure_query_latency |
Mean query latency for the group |
| Count | measure_total_data |
Total stored data elements for the group |
| o/s | measure_merge_file_rate |
Merge-loop rate for the group |
| s | measure_merge_file_latency |
Avg file-merge latency for the group |
| Count | measure_merge_file_partitions |
Avg parts merged per loop (file path) for the group |
| o/s | measure_series_write_rate |
Inverted-index write rate for the group |
| o/s | measure_series_term_search_rate |
Inverted-index term-search rate for the group |
| Count | measure_total_series |
Inverted-index documents for the group |
Stream (stream_* for the storage scope, stream_tst_* for the time-series-table scope):
| Unit | Metric | Description |
|---|---|---|
| w/s | stream_write_rate |
Write rate for the group |
| s | stream_query_latency |
Mean query latency for the group |
| Count | stream_total_data |
Total stored data elements for the group |
| o/s | stream_merge_file_rate |
Merge-loop rate for the group |
| s | stream_merge_file_latency |
Avg file-merge latency for the group |
| Count | stream_merge_file_partitions |
Avg parts merged per loop (file path) for the group |
| o/s | stream_series_write_rate |
Storage-scope inverted-index write rate for the group |
| o/s | stream_series_term_search_rate |
Storage-scope inverted-index term-search rate for the group |
| Count | stream_total_series |
Storage-scope inverted-index documents for the group |
| o/s | stream_tst_index_write_rate |
Tst-scope inverted-index write rate for the group |
| Count | stream_tst_total_series |
Tst-scope inverted-index documents for the group |
Trace (trace_*):
| Unit | Metric | Description |
|---|---|---|
| w/s | trace_write_rate |
Write rate for the group |
| s | trace_query_latency |
Mean query latency for the group |
| Count | trace_total_data |
Total stored data elements for the group |
| o/s | trace_merge_file_rate |
Merge-loop rate for the group |
| s | trace_merge_file_latency |
Avg file-merge latency for the group |
| Count | trace_merge_file_partitions |
Avg parts merged per loop (file path) for the group |
| o/s | trace_series_write_rate |
Storage-scope inverted-index write rate for the group |
| o/s | trace_series_term_search_rate |
Storage-scope inverted-index term-search rate for the group |
| Count | trace_total_series |
Storage-scope inverted-index documents for the group |
Property (property_*; the new data type — sw_property groups previously rendered all-empty
panels and now have their own metrics):
| Unit | Metric | Description |
|---|---|---|
| o/s | property_index_write_rate |
Inverted-index update rate (property “writes” are index updates) |
| o/s | property_index_merge_rate |
Inverted-index segment merge rate |
| s | property_index_merge_latency |
Mean inverted-index merge latency |
| o/s | property_series_term_search_rate |
Term-search rate (property’s real read-load signal — read via the registry/term-search path, not the liaison query method) |
| Count | property_total_series |
Inverted-index documents for the group |
Property has no
*_total_written, no tst table and no storage scope:write_rate/query_latency/total_dataare genuinely N/A for property and are not modeled —property_index_*/property_series_term_search_ratecarry the equivalent write and read load instead.
Queue / publish (type-agnostic; keyed on group + operation):
| Unit | Metric | Description |
|---|---|---|
| ops | queue_throughput |
Subscribe-queue throughput by operation for the group |
| s | queue_latency_p99 |
Publish-queue latency p99 for the group |
| ops | queue_batch_throughput |
Subscribe-queue batch throughput by operation for the group (BanyanDB #1169) |
| ops | queue_message_throughput |
Subscribe-queue per-message throughput by operation for the group (BanyanDB #1169) |
| Bytes/s | publish_bytes |
Publish bytes for the group |
Instance-relation scope — deployment topology (meter_banyandb_instance_relation_*)
The intra-cluster instance topology (the Horizon UI deployment component) models the pod-to-pod
flows within the single BanyanDB cluster service — the OAP-native equivalent of BanyanDB’s Grafana
“Topology: Pod-to-Pod Flows” view. Source and destination service are both the cluster, so the UI reads
these edges via a symmetric, same-service getServiceInstanceTopology(svc, svc) query; the Analyzer
emits the ServiceInstanceRelation server/client-side rows the deployment graph draws.
Each edge is detected from both ends (client + server resolve to the same relation id and share one edge
entity), and every per-edge metric keeps operation as a label so the dashboard can split per
operation. There are three edge kinds:
- Publish (
publish_*, CLIENT side — the liaison fans writes/queries out across the cluster; the SUB side below is the same edge’s SERVER half). - Queue-sub (
queue_sub_*, SERVER side — a node subscribes from its peers; theremote_role=lifecycleslice is the migration edge’s SERVER half). - Migration (
migration_*, CLIENT side — the lifecycle sidecar publishes migrated data to the next tier hot → warm → cold).
Each edge kind carries the same four facets:
| Suffix | Unit | Description |
|---|---|---|
_throughput |
msg/s | Per-second rate of finished operations on the edge |
_latency_p99 |
ms | p99 latency on the edge |
_error_throughput |
err/s | Per-second rate of errors on the edge |
_bytes_throughput |
B/s | Per-second bytes sent / received on the edge |
| Metric | Description |
|---|---|
publish_throughput / publish_latency_p99 / publish_error_throughput / publish_bytes_throughput |
Liaison publish (CLIENT) edge metrics |
queue_sub_throughput / queue_sub_latency_p99 / queue_sub_error_throughput / queue_sub_bytes_throughput |
Peer subscribe (SERVER) edge metrics |
migration_throughput / migration_latency_p99 / migration_error_throughput / migration_bytes_throughput |
Lifecycle migration (CLIENT) edge metrics |
The lifecycle’s last-migration timestamp and status are not modeled as edge metrics (they are label-less per-instance gauges with no destination labels); they stay instance-scope as
lifecycle_last_run/lifecycle_last_run_success/lifecycle_migration_cycles. The migration traffic (throughput / latency / error / bytes) above is already per-edge.
Trace tail sampling scope — sampler plugins (meter_banyandb_trace_sampling_*)
BanyanDB 0.11+ can run an ordered chain of sampler plugins during trace merge and finalization,
dropping whole traces after their fragments have already landed — tail sampling inside the storage
tier. See Trace Tail Sampling for how a trace is judged and how the chain is
configured from bydb.yml.
These rules live in their own file, otel-rules/banyandb/banyandb-trace-sampling.yaml, mirroring the
upstream BanyanDB — Trace Sampling Plugins
board, which upstream also ships separately from its two core boards. The reason is the same in both
places: the plugin chain is optional. On a cluster with no sampler configured none of the wire
families are registered and every metric in this section stays absent — that is the healthy
default, not a broken scrape.
Service scope with
groupas a label. Every family here is keyed ongroup, so the natural scope would be Endpoint (see Endpoint scope). They are modeled at Service scope instead, withgroupkept as a metric label, because sampling is presented as one cluster-wide page: a labeled metric renders every group as a series and still gives cluster totals when the label is aggregated away, whereas an Endpoint-scope metric cannot be rolled back up to the cluster — OAP does no cross-scope rollup. Each metric keeps exactly the labels its upstream panel splits on, listed in the Labels column below.
Two wire prefixes feed these rules, and the split is not cosmetic: plugin lifecycle, telemetry-host
safety, finalization and the plugin-published business metrics are banyandb_trace_pipeline_*, while
per-merge execution, batching and trace retention outcomes are banyandb_trace_tst_pipeline_* (the
trace time-series table is where merge runs).
Pipeline status and reconciliation:
| Unit | Metric | Labels | Description |
|---|---|---|---|
| Count | trace_sampling_active_samplers |
group |
Sampler plugins registered for the group. 0 means nothing is filtering |
| percent | trace_sampling_drop_ratio |
group |
Committed drop ratio — dropped / evaluated traces |
| c/m | trace_sampling_plugin_load_failures |
group, name, reason |
Plugin load failures. Reconciliation fails open, so the chain keeps running without the plugin |
| c/m | trace_sampling_register_rate / trace_sampling_update_rate |
group, result |
Pipeline registrations / updates (result = success or rejected) |
| c/m | trace_sampling_remove_rate |
group |
Explicit pipeline removals |
Plugin execution and batching (data containers):
| Unit | Metric | Labels | Description |
|---|---|---|---|
| o/s | trace_sampling_plugin_execution_rate |
group, plugin_name, result |
Decide calls/s. result = success, decide_error, length_mismatch, panic or late |
| ms | trace_sampling_plugin_decide_latency_p99 |
group, plugin_name |
p99 Decide wall time, successful calls only |
| ms | trace_sampling_plugin_decide_latency |
group, plugin_name |
Mean Decide wall time, successful calls only |
| o/s | trace_sampling_chain_batch_rate |
group, result |
Chain batches/s. result = success, timeout or circuit_open |
| ms | trace_sampling_plugin_time_per_trace |
group |
Whole-chain plugin time per evaluated trace — the sampling overhead |
| Count | trace_sampling_batch_size_p99 |
group |
p99 traces presented per successful chain batch |
| c/m | trace_sampling_link_bypasses |
group, plugin_name, reason |
Individual links bypassed after decide_error, length_mismatch or panic |
trace_sampling_plugin_time_per_traceis deliberately not split byplugin_name. The upstream panel divides a per-plugin numerator by a per-group denominator with PromQL’son (group) group_left; MAL arithmetic inner-joins on exact label equality and has nogroup_leftequivalent, so the numerator collapsesplugin_nameand the metric reports the chain total. The per-plugin split istrace_sampling_plugin_decide_latency.
Sampling outcomes — the trace-level denominators:
| Unit | Metric | Labels | Description |
|---|---|---|---|
| traces/s | trace_sampling_traces_evaluated |
group |
Traces evaluated by a completed, batch-aligned chain. The denominator for every ratio here |
| traces/s | trace_sampling_traces_retained |
group |
Traces retained by plugin verdict or by a safety fallback |
| traces/s | trace_sampling_traces_dropped |
group |
Traces removed from core and secondary-index output |
| traces/s | trace_sampling_traces_immature |
group |
Traces not yet eligible — their fragments are still inside the maturity boundary |
| o/s | trace_sampling_sidx_pruned |
group |
Secondary-index entries pruned after a confirmed drop |
Fail-open and bounded-retention safety. Every metric below retains data that a sampler proposed dropping, so a drop ratio under target is explained here before it is explained by plugin logic:
| Unit | Metric | Labels | Description |
|---|---|---|---|
| c/m | trace_sampling_oversized_traces_bypassed |
group |
Traces retained because their staged representation exceeded the per-trace budget |
| c/m | trace_sampling_ambiguous |
group |
Traces whose fragment state could not produce an unambiguous drop decision |
| c/m | trace_sampling_plugin_errors |
group, reason |
Chain-level errors that forced fail-open retention |
| o/s | trace_sampling_guard_bloom_probes |
group |
Bloom-filter probes checking for trace fragments outside the selected parts |
| c/m | trace_sampling_guard_deferred |
group |
Drops deferred because the fragment guard found a possible external fragment |
| c/m | trace_sampling_guard_budget_exhausted |
group |
Fragment-guard checks that exhausted their work budget |
| c/m | trace_sampling_guard_publication_rejected |
group |
Publications rejected after guard validation went stale |
| c/m | trace_sampling_guard_lossless_retry |
group |
Rejected publications retried without dropping traces |
| c/m | trace_sampling_guard_bypassed |
group |
Merges retained unsampled because the guard was unavailable or inapplicable |
Drop-set capacity and finalization. The per-merge dropped-trace-ID set is memory-bounded; when it hits the ceiling the merge retains the rest, which is what makes a drop ratio plateau under load:
| Unit | Metric | Labels | Description |
|---|---|---|---|
| c/m | trace_sampling_capped_merges |
group, lane |
Merges whose drop-ID set reached its ceiling |
| c/m | trace_sampling_traces_retained_by_ceiling |
group |
Proposed drops retained after the ceiling was reached |
| Count | trace_sampling_drop_set_entries_p99 |
group, lane |
p99 dropped trace IDs held per merge — read against the budget for headroom |
| Bytes | trace_sampling_drop_set_budget_bytes |
group |
Resolved per-merge memory budget for confirmed dropped trace IDs |
| Count | trace_sampling_finalize_rounds |
group |
Highest finalization round count across the group’s cooled shards |
| Status | trace_sampling_finalize_terminal |
group |
1 when a cooled shard can run no further finalization round — no more tail sampling for that data |
First-party sampler decisions (sw-trace-sampler / zipkin-trace-sampler):
| Unit | Metric | Labels | Description |
|---|---|---|---|
| o/s | trace_sampling_sampler_decisions |
group, plugin_name, verdict, rule |
Trace evaluations attributed to the first matching rule. verdict = keep or drop |
| rows/s | trace_sampling_sampler_rows |
group, plugin_name |
Span / segment rows carried by evaluated traces |
| rows/s | trace_sampling_sampler_rows_dropped |
group, plugin_name, rule |
Rows belonging to traces the sampler proposed dropping |
| percent | trace_sampling_sampler_dropped_row_ratio |
group, plugin_name |
Proposed dropped-row ratio |
| c/m | trace_sampling_sampler_row_count_unavailable |
group, plugin_name |
Evaluations whose metadata-only projection exposed no row count — these are missing from the row denominators |
These are proposed, not committed. A trace can be evaluated across several merge or finalization rounds, and the safety guards above can retain a trace the plugin voted to drop — so always read
trace_sampling_sampler_decisions{verdict=drop}againsttrace_sampling_traces_dropped, and treattrace_sampling_sampler_dropped_row_ratioas incomplete whiletrace_sampling_sampler_row_count_unavailableis nonzero.ruleis one of the fixed built-in values (duration,error,healthy_sample,healthy_rejected,no_keep_rule,decode_failure_*) or a positionaltag_00–tag_31slot followingkeepTagRulesorder; tag keys and values are never copied into the label, which is what keeps the label set bounded.
Adding a rule that divides two counters?
rate()/increase()keep their lower bound per (rule, wire family, label set). Two rules reducing one family to the same label set therefore no longer share a window — but check anyway that the operands of a ratio are the reductions you think they are, and verify the result against known inputs rather than trusting it by inspection.
Plugin telemetry host safety. The host caps what a plugin may publish (100 label-value series per instrument) and rate-limits its logs. Nonzero here means a plugin’s own telemetry is being truncated — its business metrics above are then incomplete, though the sampling itself is unaffected:
| Unit | Metric | Labels | Description |
|---|---|---|---|
| c/m | trace_sampling_plugin_telemetry_series_rejected |
group, plugin_name |
Series rejected by host safety bounds |
| c/m | trace_sampling_plugin_log_dropped |
group, plugin_name |
Plugin log lines dropped by the host rate limiter |
| c/m | trace_sampling_plugin_telemetry_panic |
group, plugin_name |
Panics recovered inside the plugin telemetry adapter |
Customizations
You can customize your own metrics / expressions. The metric definitions and expression rules are in
/config/otel-rules/banyandb. The dashboard panel configurations ship from the SkyWalking Horizon UI
bundle (apache/skywalking-horizon-ui); the OAP backend does not host UI dashboard JSONs.