...

Source file src/github.com/prometheus/common/expfmt/expfmt.go

Documentation: github.com/prometheus/common/expfmt

     1  // Copyright 2015 The Prometheus Authors
     2  // Licensed under the Apache License, Version 2.0 (the "License");
     3  // you may not use this file except in compliance with the License.
     4  // You may obtain a copy of the License at
     5  //
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  // Package expfmt contains tools for reading and writing Prometheus metrics.
    15  package expfmt
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  
    21  	"github.com/prometheus/common/model"
    22  )
    23  
    24  // Format specifies the HTTP content type of the different wire protocols.
    25  type Format string
    26  
    27  // Constants to assemble the Content-Type values for the different wire
    28  // protocols. The Content-Type strings here are all for the legacy exposition
    29  // formats, where valid characters for metric names and label names are limited.
    30  // Support for arbitrary UTF-8 characters in those names is already partially
    31  // implemented in this module (see model.ValidationScheme), but to actually use
    32  // it on the wire, new content-type strings will have to be agreed upon and
    33  // added here.
    34  const (
    35  	TextVersion              = "0.0.4"
    36  	ProtoType                = `application/vnd.google.protobuf`
    37  	ProtoProtocol            = `io.prometheus.client.MetricFamily`
    38  	protoFmt                 = ProtoType + "; proto=" + ProtoProtocol + ";"
    39  	OpenMetricsType          = `application/openmetrics-text`
    40  	OpenMetricsVersion_0_0_1 = "0.0.1"
    41  	OpenMetricsVersion_1_0_0 = "1.0.0"
    42  
    43  	// The Content-Type values for the different wire protocols. Note that these
    44  	// values are now unexported. If code was relying on comparisons to these
    45  	// constants, instead use FormatType().
    46  	fmtUnknown           Format = `<unknown>`
    47  	fmtText              Format = `text/plain; version=` + TextVersion + `; charset=utf-8`
    48  	fmtProtoDelim        Format = protoFmt + ` encoding=delimited`
    49  	fmtProtoText         Format = protoFmt + ` encoding=text`
    50  	fmtProtoCompact      Format = protoFmt + ` encoding=compact-text`
    51  	fmtOpenMetrics_1_0_0 Format = OpenMetricsType + `; version=` + OpenMetricsVersion_1_0_0 + `; charset=utf-8`
    52  	fmtOpenMetrics_0_0_1 Format = OpenMetricsType + `; version=` + OpenMetricsVersion_0_0_1 + `; charset=utf-8`
    53  )
    54  
    55  const (
    56  	hdrContentType = "Content-Type"
    57  	hdrAccept      = "Accept"
    58  )
    59  
    60  // FormatType is a Go enum representing the overall category for the given
    61  // Format. As the number of Format permutations increases, doing basic string
    62  // comparisons are not feasible, so this enum captures the most useful
    63  // high-level attribute of the Format string.
    64  type FormatType int
    65  
    66  const (
    67  	TypeUnknown FormatType = iota
    68  	TypeProtoCompact
    69  	TypeProtoDelim
    70  	TypeProtoText
    71  	TypeTextPlain
    72  	TypeOpenMetrics
    73  )
    74  
    75  // NewFormat generates a new Format from the type provided. Mostly used for
    76  // tests, most Formats should be generated as part of content negotiation in
    77  // encode.go. If a type has more than one version, the latest version will be
    78  // returned.
    79  func NewFormat(t FormatType) Format {
    80  	switch t {
    81  	case TypeProtoCompact:
    82  		return fmtProtoCompact
    83  	case TypeProtoDelim:
    84  		return fmtProtoDelim
    85  	case TypeProtoText:
    86  		return fmtProtoText
    87  	case TypeTextPlain:
    88  		return fmtText
    89  	case TypeOpenMetrics:
    90  		return fmtOpenMetrics_1_0_0
    91  	default:
    92  		return fmtUnknown
    93  	}
    94  }
    95  
    96  // NewOpenMetricsFormat generates a new OpenMetrics format matching the
    97  // specified version number.
    98  func NewOpenMetricsFormat(version string) (Format, error) {
    99  	if version == OpenMetricsVersion_0_0_1 {
   100  		return fmtOpenMetrics_0_0_1, nil
   101  	}
   102  	if version == OpenMetricsVersion_1_0_0 {
   103  		return fmtOpenMetrics_1_0_0, nil
   104  	}
   105  	return fmtUnknown, fmt.Errorf("unknown open metrics version string")
   106  }
   107  
   108  // FormatType deduces an overall FormatType for the given format.
   109  func (f Format) FormatType() FormatType {
   110  	toks := strings.Split(string(f), ";")
   111  	params := make(map[string]string)
   112  	for i, t := range toks {
   113  		if i == 0 {
   114  			continue
   115  		}
   116  		args := strings.Split(t, "=")
   117  		if len(args) != 2 {
   118  			continue
   119  		}
   120  		params[strings.TrimSpace(args[0])] = strings.TrimSpace(args[1])
   121  	}
   122  
   123  	switch strings.TrimSpace(toks[0]) {
   124  	case ProtoType:
   125  		if params["proto"] != ProtoProtocol {
   126  			return TypeUnknown
   127  		}
   128  		switch params["encoding"] {
   129  		case "delimited":
   130  			return TypeProtoDelim
   131  		case "text":
   132  			return TypeProtoText
   133  		case "compact-text":
   134  			return TypeProtoCompact
   135  		default:
   136  			return TypeUnknown
   137  		}
   138  	case OpenMetricsType:
   139  		if params["charset"] != "utf-8" {
   140  			return TypeUnknown
   141  		}
   142  		return TypeOpenMetrics
   143  	case "text/plain":
   144  		v, ok := params["version"]
   145  		if !ok {
   146  			return TypeTextPlain
   147  		}
   148  		if v == TextVersion {
   149  			return TypeTextPlain
   150  		}
   151  		return TypeUnknown
   152  	default:
   153  		return TypeUnknown
   154  	}
   155  }
   156  
   157  // ToEscapingScheme returns an EscapingScheme depending on the Format. Iff the
   158  // Format contains a escaping=allow-utf-8 term, it will select NoEscaping. If a valid
   159  // "escaping" term exists, that will be used. Otherwise, the global default will
   160  // be returned.
   161  func (format Format) ToEscapingScheme() model.EscapingScheme {
   162  	for _, p := range strings.Split(string(format), ";") {
   163  		toks := strings.Split(p, "=")
   164  		if len(toks) != 2 {
   165  			continue
   166  		}
   167  		key, value := strings.TrimSpace(toks[0]), strings.TrimSpace(toks[1])
   168  		if key == model.EscapingKey {
   169  			scheme, err := model.ToEscapingScheme(value)
   170  			if err != nil {
   171  				return model.NameEscapingScheme
   172  			}
   173  			return scheme
   174  		}
   175  	}
   176  	return model.NameEscapingScheme
   177  }
   178  

View as plain text