The expfmt package provides tools for reading and writing Prometheus metrics in various exposition formats including text format, protocol buffers, and OpenMetrics.
import "github.com/prometheus/common/expfmt"This package handles the encoding and decoding of Prometheus metrics across multiple format versions. It supports content negotiation, format detection, and streaming parsing of metrics data. The package is used throughout the Prometheus ecosystem for metrics exposition and scraping.
const (
TextVersion = "0.0.4"
ProtoType = `application/vnd.google.protobuf`
ProtoProtocol = `io.prometheus.client.MetricFamily`
ProtoFmt = ProtoType + "; proto=" + ProtoProtocol + ";" // Deprecated
OpenMetricsType = `application/openmetrics-text`
OpenMetricsVersion_0_0_1 = "0.0.1"
OpenMetricsVersion_1_0_0 = "1.0.0"
)Constants defining version strings and media types for different exposition formats.
type Format stringRepresents a Prometheus exposition format with content type and optional parameters.
const (
FmtUnknown Format = `<unknown>` // Deprecated
FmtText Format = `text/plain; version=0.0.4; charset=utf-8` // Deprecated
FmtProtoDelim Format = ProtoFmt + ` encoding=delimited` // Deprecated
FmtProtoText Format = ProtoFmt + ` encoding=text` // Deprecated
FmtProtoCompact Format = ProtoFmt + ` encoding=compact-text` // Deprecated
FmtOpenMetrics_1_0_0 Format = OpenMetricsType + `; version=1.0.0; charset=utf-8` // Deprecated
FmtOpenMetrics_0_0_1 Format = OpenMetricsType + `; version=0.0.1; charset=utf-8` // Deprecated
)Pre-defined format constants. Deprecated in favor of using NewFormat and NewOpenMetricsFormat.
func (f Format) WithEscapingScheme(s model.EscapingScheme) FormatReturns a copy of the format with the specified escaping scheme parameter.
Parameters:
s - Escaping scheme to applyReturns: New Format with escaping scheme
func (f Format) FormatType() FormatTypeDeduces the FormatType from the format string.
Returns: The FormatType
func (f Format) ToEscapingScheme() model.EscapingSchemeReturns the EscapingScheme encoded in the Format, or NoEscaping if not specified.
Returns: The escaping scheme
type FormatType intRepresents the type of exposition format.
const (
TypeUnknown FormatType = iota
TypeProtoCompact
TypeProtoDelim
TypeProtoText
TypeTextPlain
TypeOpenMetrics
)TypeUnknown - Unknown or unsupported formatTypeProtoCompact - Protocol buffer compact text encodingTypeProtoDelim - Protocol buffer delimited encodingTypeProtoText - Protocol buffer text encodingTypeTextPlain - Text format (0.0.4)TypeOpenMetrics - OpenMetrics text formattype DecodeOptions struct {
Timestamp model.Time
}Options for decoding metrics.
Fields:
Timestamp - Timestamp added to each sample that doesn't have an explicit timestamptype SampleDecoder struct {
Dec Decoder
Opts *DecodeOptions
}Decoder that extracts samples from metric families.
Fields:
Dec - The underlying decoderOpts - Decode optionsfunc (sd *SampleDecoder) Decode(s *model.Vector) errorDecodes metric families and extracts samples into the provided vector.
Parameters:
s - Vector to append samples toReturns: Error if decoding fails
type ParseError struct {
Line int
Msg string
}Error type for parse errors with line number information.
Fields:
Line - Line number where error occurredMsg - Error messagefunc (e ParseError) Error() stringReturns formatted error message with line number.
type TextParser struct {
// Parser for text format metrics
}Parser for text format metrics.
func (p *TextParser) TextToMetricFamilies(in io.Reader) (map[string]*dto.MetricFamily, error)Parses text format metrics into metric families.
Parameters:
in - Reader containing text format metricsReturns: Map of metric family names to MetricFamily, error
type Decoder interface {
Decode(*dto.MetricFamily) error
}Interface for decoding Prometheus metrics. Implementations decode one metric family per call.
Methods:
Decode(*dto.MetricFamily) error - Decodes the next metric familytype Encoder interface {
Encode(*dto.MetricFamily) error
}Interface for encoding Prometheus metrics. Implementations encode one metric family per call.
Methods:
Encode(*dto.MetricFamily) error - Encodes a metric familytype Closer interface {
Close() error
}Interface for closeable encoders. Some encoders need to write finalization data (like OpenMetrics EOF marker).
Methods:
Close() error - Closes the encoder and writes any finalization datafunc NewFormat(t FormatType) FormatGenerates a new Format from FormatType.
Parameters:
t - The format typeReturns: Format string
Example:
format := expfmt.NewFormat(expfmt.TypeTextPlain)
// Returns: "text/plain; version=0.0.4; charset=utf-8"func NewOpenMetricsFormat(version string) (Format, error)Generates an OpenMetrics format with the specified version.
Parameters:
version - OpenMetrics version (e.g., "1.0.0" or "0.0.1")Returns: (Format, error)
Example:
format, err := expfmt.NewOpenMetricsFormat("1.0.0")
// Returns: "application/openmetrics-text; version=1.0.0; charset=utf-8"func ResponseFormat(h http.Header) FormatExtracts format from HTTP response Content-Type header.
Parameters:
h - HTTP response headersReturns: Detected Format
func Negotiate(h http.Header) FormatNegotiates format from Accept header, excluding OpenMetrics formats. This is the standard negotiation function for compatibility.
Parameters:
h - HTTP headers containing Accept headerReturns: Best matching Format
func NegotiateIncludingOpenMetrics(h http.Header) FormatNegotiates format from Accept header, including OpenMetrics formats.
Parameters:
h - HTTP headers containing Accept headerReturns: Best matching Format including OpenMetrics
func NewDecoder(r io.Reader, format Format) DecoderReturns a decoder for the specified format.
Parameters:
r - Reader to decode fromformat - Format to decodeReturns: Decoder instance
Note: For unsupported or unknown formats, the decoder falls back to text format parsing for backward compatibility. This fallback behavior exists for historical reasons and may not fully support OpenMetrics or the latest features of Prometheus text format.
Example:
decoder := expfmt.NewDecoder(reader, expfmt.NewFormat(expfmt.TypeTextPlain))
for {
var mf dto.MetricFamily
if err := decoder.Decode(&mf); err != nil {
if err == io.EOF {
break
}
// handle error
}
// process metric family
}func NewEncoder(w io.Writer, format Format, options ...EncoderOption) EncoderReturns an encoder for the specified format.
Parameters:
w - Writer to encode toformat - Format to encodeoptions - Optional encoder optionsReturns: Encoder instance
Example:
encoder := expfmt.NewEncoder(writer, expfmt.NewFormat(expfmt.TypeTextPlain))
err := encoder.Encode(metricFamily)type EncoderOption func(*encoderOption)Function type for encoder options.
func WithCreatedLines() EncoderOptionConfigures OpenMetrics encoder to include _created lines for counters and histograms.
Returns: EncoderOption
func WithUnit() EncoderOptionEnables unit to be written and added to metric name suffix in OpenMetrics format.
Returns: EncoderOption
func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (written int, err error)Writes a metric family in text format (0.0.4).
Parameters:
out - Output writerin - Metric family to writeReturns: (int, error) - Bytes written, error
Example:
written, err := expfmt.MetricFamilyToText(os.Stdout, metricFamily)func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily, options ...EncoderOption) (written int, err error)Writes a metric family in OpenMetrics format.
Parameters:
out - Output writerin - Metric family to writeoptions - Encoder optionsReturns: (int, error) - Bytes written, error
Example:
written, err := expfmt.MetricFamilyToOpenMetrics(os.Stdout, metricFamily, expfmt.WithCreatedLines())func FinalizeOpenMetrics(w io.Writer) (written int, err error)Writes the OpenMetrics EOF marker ("# EOF\n") to finalize the output.
Parameters:
w - Output writerReturns: (int, error) - Bytes written, error
Example:
// After writing all metric families
expfmt.FinalizeOpenMetrics(writer)func ExtractSamples(o *DecodeOptions, fams ...*dto.MetricFamily) (model.Vector, error)Extracts samples from metric families into a model.Vector.
Parameters:
o - Decode options (can be nil)fams - Metric families to extract fromReturns: (model.Vector, error) - Samples, error
Example:
samples, err := expfmt.ExtractSamples(nil, metricFamily1, metricFamily2)func NewTextParser(nameValidationScheme model.ValidationScheme) TextParserCreates a new text format parser with the specified name validation scheme.
Parameters:
nameValidationScheme - Validation scheme for metric namesReturns: TextParser
Example:
parser := expfmt.NewTextParser(model.UTF8Validation)
families, err := parser.TextToMetricFamilies(reader)package main
import (
"fmt"
"strings"
"github.com/prometheus/common/expfmt"
"github.com/prometheus/common/model"
)
const metrics = `
# HELP http_requests_total Total HTTP requests
# TYPE http_requests_total counter
http_requests_total{method="GET",status="200"} 1234
http_requests_total{method="POST",status="200"} 567
`
func main() {
parser := expfmt.NewTextParser(model.UTF8Validation)
families, err := parser.TextToMetricFamilies(strings.NewReader(metrics))
if err != nil {
panic(err)
}
for name, family := range families {
fmt.Printf("Metric: %s\n", name)
fmt.Printf(" Type: %s\n", family.GetType())
fmt.Printf(" Help: %s\n", family.GetHelp())
fmt.Printf(" Metrics: %d\n", len(family.GetMetric()))
}
}package main
import (
"fmt"
"io"
"os"
dto "github.com/prometheus/client_model/go"
"github.com/prometheus/common/expfmt"
)
func main() {
file, err := os.Open("metrics.txt")
if err != nil {
panic(err)
}
defer file.Close()
format := expfmt.NewFormat(expfmt.TypeTextPlain)
decoder := expfmt.NewDecoder(file, format)
for {
var mf dto.MetricFamily
if err := decoder.Decode(&mf); err != nil {
if err == io.EOF {
break
}
panic(err)
}
fmt.Printf("Family: %s (%s)\n", mf.GetName(), mf.GetType())
for _, m := range mf.GetMetric() {
fmt.Printf(" Metric: %v\n", m)
}
}
}package main
import (
"os"
dto "github.com/prometheus/client_model/go"
"github.com/prometheus/common/expfmt"
)
func main() {
// Create a metric family
metricFamily := &dto.MetricFamily{
Name: strPtr("example_metric"),
Type: metricTypePtr(dto.MetricType_GAUGE),
Help: strPtr("An example metric"),
Metric: []*dto.Metric{
{
Label: []*dto.LabelPair{
{Name: strPtr("label"), Value: strPtr("value")},
},
Gauge: &dto.Gauge{Value: float64Ptr(42.0)},
},
},
}
// Encode as text format
format := expfmt.NewFormat(expfmt.TypeTextPlain)
encoder := expfmt.NewEncoder(os.Stdout, format)
encoder.Encode(metricFamily)
}
func strPtr(s string) *string { return &s }
func float64Ptr(f float64) *float64 { return &f }
func metricTypePtr(t dto.MetricType) *dto.MetricType { return &t }package main
import (
"fmt"
"net/http"
"github.com/prometheus/common/expfmt"
)
func metricsHandler(w http.ResponseWriter, r *http.Request) {
// Negotiate format based on Accept header
format := expfmt.Negotiate(r.Header)
// Set response content type
w.Header().Set("Content-Type", string(format))
// Create encoder
encoder := expfmt.NewEncoder(w, format)
// Encode metrics...
// encoder.Encode(metricFamily)
}
func main() {
http.HandleFunc("/metrics", metricsHandler)
http.ListenAndServe(":9090", nil)
}package main
import (
"os"
dto "github.com/prometheus/client_model/go"
"github.com/prometheus/common/expfmt"
)
func main() {
format, _ := expfmt.NewOpenMetricsFormat("1.0.0")
encoder := expfmt.NewEncoder(
os.Stdout,
format,
expfmt.WithCreatedLines(),
expfmt.WithUnit(),
)
// Encode metric families
// encoder.Encode(metricFamily)
// Finalize OpenMetrics output
if closer, ok := encoder.(expfmt.Closer); ok {
closer.Close()
}
}package main
import (
"fmt"
"strings"
"github.com/prometheus/common/expfmt"
"github.com/prometheus/common/model"
)
func main() {
parser := expfmt.NewTextParser(model.UTF8Validation)
families, err := parser.TextToMetricFamilies(strings.NewReader(metricsText))
if err != nil {
panic(err)
}
// Convert map to slice
var familySlice []*dto.MetricFamily
for _, family := range families {
familySlice = append(familySlice, family)
}
// Extract samples
opts := &expfmt.DecodeOptions{
Timestamp: model.Now(),
}
samples, err := expfmt.ExtractSamples(opts, familySlice...)
if err != nil {
panic(err)
}
for _, sample := range samples {
fmt.Printf("%s %v @ %v\n", sample.Metric, sample.Value, sample.Timestamp)
}
}package main
import (
"fmt"
"io"
"net/http"
dto "github.com/prometheus/client_model/go"
"github.com/prometheus/common/expfmt"
)
func scrapeMetrics(url string) error {
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
// Detect format from response headers
format := expfmt.ResponseFormat(resp.Header)
fmt.Printf("Detected format: %s\n", format)
// Decode metrics
decoder := expfmt.NewDecoder(resp.Body, format)
for {
var mf dto.MetricFamily
if err := decoder.Decode(&mf); err != nil {
if err == io.EOF {
break
}
return err
}
fmt.Printf("Scraped: %s\n", mf.GetName())
}
return nil
}
func main() {
scrapeMetrics("http://localhost:9090/metrics")
}package main
import (
"os"
dto "github.com/prometheus/client_model/go"
"github.com/prometheus/common/expfmt"
)
func main() {
format, _ := expfmt.NewOpenMetricsFormat("1.0.0")
encoder := expfmt.NewEncoder(os.Stdout, format)
// Write multiple metric families
for _, mf := range getMetricFamilies() {
encoder.Encode(mf)
}
// Write EOF marker
expfmt.FinalizeOpenMetrics(os.Stdout)
}
func getMetricFamilies() []*dto.MetricFamily {
// Return your metric families
return nil
}package main
import (
"fmt"
"strings"
dto "github.com/prometheus/client_model/go"
"github.com/prometheus/common/expfmt"
"github.com/prometheus/common/model"
)
func main() {
format := expfmt.NewFormat(expfmt.TypeTextPlain)
decoder := expfmt.NewDecoder(strings.NewReader(metricsText), format)
sampleDecoder := &expfmt.SampleDecoder{
Dec: decoder,
Opts: &expfmt.DecodeOptions{
Timestamp: model.Now(),
},
}
var samples model.Vector
if err := sampleDecoder.Decode(&samples); err != nil {
panic(err)
}
for _, sample := range samples {
fmt.Printf("%s{%s} => %v\n",
sample.Metric[model.MetricNameLabel],
sample.Metric,
sample.Value)
}
}text/plain; version=0.0.4; charset=utf-8application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimitedapplication/openmetrics-text; version=1.0.0; charset=utf-8When implementing a metrics endpoint, use Negotiate or NegotiateIncludingOpenMetrics to automatically select the best format based on the client's Accept header.
OpenMetrics format requires an EOF marker. Always call FinalizeOpenMetrics or use the encoder's Close method after writing all metrics.
Decoders and encoders support streaming - they process one metric family at a time, which is memory-efficient for large metric sets.
Encoders and decoders are not thread-safe. Create separate instances for concurrent use.