# MetricMapping

Source: https://docs.modelplane.ai/reference/metricmappings/

How one component's metrics become part of the modelplane_* surface. Modelplane renders every MetricMapping into each inference cluster's collector, so a mapping is written once on the control plane and reaches the whole fleet.
A mapping naming a component Modelplane already provides renames for is additive: its renames run after the built-in ones, on whatever those left behind. A metric a built-in already renamed no longer answers to the name it was emitted under, so a second mapping selecting on that name matches nothing and the built-in stands. Select on the `modelplane_` name instead to rename one of Modelplane's own.

Apply instances as `apiVersion: modelplane.ai/v1alpha1`, `kind: MetricMapping`.

[Concept guide: Monitor the Fleet](https://docs.modelplane.ai/platform/telemetry/index.md)

## Definition

The CompositeResourceDefinition this reference is generated from, with the complete OpenAPI schema, validation rules, and defaults:

```yaml
apiVersion: apiextensions.crossplane.io/v2
kind: CompositeResourceDefinition
metadata:
  name: metricmappings.modelplane.ai
spec:
  group: modelplane.ai
  names:
    categories: [crossplane, modelplane, platform]
    kind: MetricMapping
    plural: metricmappings
    shortNames: [mm]
  scope: Cluster
  versions:
  - name: v1alpha1
    served: true
    referenceable: true
    additionalPrinterColumns:
    - name: CLUSTERS
      type: integer
      jsonPath: .status.clusters
    - name: AGE
      type: date
      jsonPath: .metadata.creationTimestamp
    schema:
      openAPIV3Schema:
        type: object
        required: [spec]
        properties:
          spec:
            type: object
            required: [metrics]
            description: >-
              How one component's metrics become part of the modelplane_*
              surface. Modelplane renders every MetricMapping into each
              inference cluster's collector, so a mapping is written once on
              the control plane and reaches the whole fleet.

              A mapping naming a component Modelplane already provides
              renames for is additive: its renames run after the built-in
              ones, on whatever those left behind. A metric a built-in
              already renamed no longer answers to the name it was emitted
              under, so a second mapping selecting on that name matches
              nothing and the built-in stands. Select on the `modelplane_`
              name instead to rename one of Modelplane's own.
            properties:
              metrics:
                type: array
                minItems: 1
                maxItems: 128
                description: >-
                  The metrics to rename. Give two components' metrics the same
                  name only if they measure the same thing, and histograms only
                  if their buckets match too. A quantile across mismatched
                  buckets is wrong.
                items:
                  type: object
                  required: [from, to]
                  properties:
                    from:
                      type: string
                      maxLength: 255
                      pattern: '^[a-zA-Z_:][a-zA-Z0-9_:]*$'
                      description: >-
                        The metric's name as the component exposes it, matched
                        exactly wherever it appears in the fleet.

                        A histogram is named by its base name, without the
                        _count, _sum or _bucket a Prometheus query would use:
                        the collector holds it as one metric, and `part` is
                        what reaches into it.
                    to:
                      type: string
                      maxLength: 255
                      pattern: '^modelplane_[a-z0-9_]*[a-z0-9]$'
                      description: >-
                        The name to export the metric under. Only modelplane_*
                        metrics leave a cluster, so a metric no mapping renames
                        never leaves its cluster.

                        Name it in base units - seconds, bytes, joules, a ratio
                        from nought to one - because that is what `fromUnit`
                        converts to.
                    part:
                      type: string
                      enum: [Count, Sum]
                      description: >-
                        Take a part of a histogram as a counter of its own,
                        rather than the histogram itself. Count is how many
                        observations it holds, which is a request count where
                        the histogram measures request duration. Sum is their
                        total.

                        The extraction leaves the histogram alone, but the
                        collector exports only what a mapping renames, so the
                        histogram itself is dropped unless another mapping
                        gives it a `modelplane_` name of its own. Write that
                        second mapping to keep both.
                    labels:
                      type: array
                      maxItems: 16
                      description: >-
                        Labels to set on this metric's series. To fold several
                        metrics into one name, give each its own entry with
                        the same `to` and a different fixed value, such as
                        direction: input and direction: output on
                        modelplane_tokens_total.
                      items:
                        type: object
                        required: [name]
                        x-kubernetes-validations:
                        - rule: "has(self.value) != has(self.from)"
                          message: set either value, for a fixed label, or from, to carry one the component already emits.
                        - rule: "!has(self.values) || has(self.from)"
                          message: values remaps what from carries, so it needs from. A fixed value has nothing to remap.
                        properties:
                          name:
                            type: string
                            maxLength: 63
                            pattern: '^[a-zA-Z_][a-zA-Z0-9_]*$'
                            description: The label to set.
                          value:
                            type: string
                            maxLength: 253
                            description: >-
                              A fixed value, the same on every series this
                              mapping produces. This is what tells two folded
                              metrics apart.
                          from:
                            type: string
                            maxLength: 63
                            pattern: '^[a-zA-Z_][a-zA-Z0-9_]*$'
                            description: >-
                              A label the component already emits. Modelplane
                              copies its value into this label and removes the
                              original.

                              Naming the label itself keeps it: that is how
                              `values` rewrites what a component writes without
                              renaming the label.
                          values:
                            type: object
                            maxProperties: 32
                            additionalProperties:
                              type: string
                              maxLength: 253
                            description: >-
                              What each of that label's values becomes, for
                              putting an engine's own vocabulary into
                              Modelplane's. A value with no entry here is left
                              as the component wrote it.
                    fromUnit:
                      type: string
                      enum: [Millijoules, Mebibytes, Milliseconds, Nanoseconds, Percent]
                      description: >-
                        What the component measures this in, when that isn't
                        the unit the name claims. Modelplane converts to the
                        base unit: millijoules and milliseconds are divided by
                        a thousand, nanoseconds by a billion, percent by a
                        hundred, and mebibytes multiplied out to bytes. A
                        histogram is converted whole - its sum, its bounds and
                        its bucket boundaries - so its quantiles come out in
                        the target unit too.

                        Percent is for a component that counts a saturation
                        from nought to a hundred where the name says a ratio.
                        Check rather than assume: vLLM publishes
                        kv_cache_usage_perc and the value is a fraction, so a
                        name is no guide.
          status:
            type: object
            properties:
              clusters:
                type: integer
                description: >-
                  How many inference clusters apply this mapping.
              conditions:
                type: array
                items:
                  type: object
```
