Skip to content

[Feat]: Show identifying labels instead of chart instance name #1154

Open

Description

@ralphm

Problem

Netdata chart instance names are generally (supposed to be) opaque identifiers. They show as long strings in the "instances" and grouping dropdowns, are hard to understand and look ugly.

Description

The chart names are identifiers created by concatenating differing properties of the underlying instrument being measured, with underscores as separators. An example is this:

Image

Let's take the chart instance named:

sensors.temperature_nvme-pci-0020_temp1_Composite_alarm@ip-10-20-161-169

This instance is on the node ip-10-20-161-169, with the chart labels for this instance ("sensor" for this context):

label value embedded in chart name
_collect_module sensors
_collect_plugin debugfs
chip_id nvme-pci-0020 ✅
driver nvme
feature temp1 ✅
label Composite ✅
path /sys/class/hwmon/hwmon0
subsystem nvme

Note

The temperature part comes from the sensor type (temp), which is not emitted as a label, and is mapped to a more user friendly name.
The _alert suffix is only needed to distinguish from other contexts, so we can ignore that.

It would be so much better if we could just show the (identifying) labels for a given instance. I think the right maximum should be 3, but expandable to all (non-system, starting with underscore) labels. E.g.:

chip_id nvme-pci-0020 feature temp1
label Composite +3» @ ip-10-20-161-169

Importance

really want

Value proposition

  1. Much easier to identify chart instances
  2. Less underscore clutter
  3. Makes it clear there are labels to be filtered on.

Proposed implementation

Goal

Per context, compute the smallest set of label names whose values uniquely identify every instance of that context, and render those label: value pairs in the Instances dropdown (and consistently in legend/tooltips). This is the classic minimum test cover / minimum unique column combination problem; NP-hard in general but trivially fast at per-context label counts.

The minimal set of labels could have multiple candidates. E.g. for k8s charts you generally have these combinations of labels identifying:

  • k8s_container_name, k8s_pod_name
  • k8s_container_name and k8s_controller_name

To resolve this in the below algorithm, we would need a global ordered list of label names that may conflict. It does not have to be a list of all labels. Any label name not mentioned has a lower priority.

To resolve the above conflict, we would put both k8s_pod_name and k8s_controller_name on the priority list, in that order, so that the first option "wins".

Caution

We could annotate all charts with their identifying labels. Unfortunately, that would fail for custom metrics, and so a more generic solution is needed.

Algorithm

Input: instances R (rows of label→value, value may be missing), label names L, global priority list P (partial: only shared/clashing names, rest alphabetical), MAX ≈ 3 (presentation cap). Ignore system labels (starting with an underscore).

  1. Drop constant labels: l ∈ L with a single value across R (they separate nothing).
    NOTE: skip this pruning when |R| ≤ 1 (every label is trivially "constant" then).
  2. A missing label also constitutes a value for a given chart!
  3. Feasibility: if two rows are identical on every remaining label, they are indistinguishable → mark them; display falls back to appending instance id.
  4. Find the smallest identifying set within the display cap (bounded, no time budget):
    • A label set S identifies R iff it separates every pair of rows (equivalently: S must "hit" D(i,j) = {l : v(i,l) ≠ v(j,l)} for every pair).
    • Search only k = 1..MAX: enumerate size-k label combinations in P order; test each by scanning rows with EARLY EXIT on the first colliding row.
    • The first identifying set found at the smallest k is the exact priority-lexicographic winner (P-ordered enumeration ⇒ winner first).
    • If level MAX is exhausted: declare k* > MAX — no identifying set fits the display; presentation falls back (see 5).
  5. Presentation rule:
    • |R| ≥ 2: base = winning identifying set (k* labels). Extras = top (MAX − k*) labels from the remaining separating labels by P. Constants stay out (uniform noise).
    • |R| ≥ 2 and k* > MAX: base = top-MAX labels by P (uniqueness not guaranteed); residual collisions are carried by the +N expand/tooltip in 6
    • |R| = 1: nothing to disambiguate; base = ∅; present top-MAX labels from the full label set by P (e.g. feature, label, chip_id for a single sensor).
  6. Render as bold name: value pairs ordered by P as above:
    • Chevron expands to a sub-row listing ALL labels (bold name: value).
    • Hover tooltip shows the full set.
    • Truncation drops the lowest-priority pairs first (guaranteed by P-ordering).

Complexity / safety

Measured (Node 22, depth-capped search, no time budget):

  • real table, 12 rows → ~1 ms
  • realistic k8s cgroups fleet, 10k instances → ~70 ms
  • pathological random (n=25, alphabet 8 ) → 68 ms (proven k* > MAX, no clock needed)
  • absurd n=100 → ~400 ms (optional: cap combos per level, e.g. 200k)

Work is bounded by Σ C(n, k≤MAX), independent of runtime. Compute lazily on dropdown open; cache per (context, instance-inventory fingerprint).

Edge cases (must handle)

  • Missing label values: two rows both missing a label are not separated by it.
  • Single instance per context → present meaningful labels anyway (no blank/∅ entries).
  • Duplicate label vectors → append opaque instance id as fallback (maybe in a smaller font)
  • Long values → truncate per-value before pair-list truncation; sort by the untruncated identity string.

Definition of done

  • Instances dropdown rows show priority-ordered label: value pairs; no opaque name@node strings by default.
  • Single-instance contexts show meaningful labels (not blank).
  • Duplicate/indistinguishable instances still visible via id fallback.
  • Same label rendering is reused for legend grouping by instance and tooltips.
  • Performance: compute ≤ ~100 ms worst realistic case, cached, non-blocking.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions