Initial Commit of Matrix Handler
This commit is contained in:
		
							
								
								
									
										201
									
								
								vendor/github.com/prometheus/common/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								vendor/github.com/prometheus/common/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,201 @@
 | 
			
		||||
                                 Apache License
 | 
			
		||||
                           Version 2.0, January 2004
 | 
			
		||||
                        http://www.apache.org/licenses/
 | 
			
		||||
 | 
			
		||||
   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 | 
			
		||||
 | 
			
		||||
   1. Definitions.
 | 
			
		||||
 | 
			
		||||
      "License" shall mean the terms and conditions for use, reproduction,
 | 
			
		||||
      and distribution as defined by Sections 1 through 9 of this document.
 | 
			
		||||
 | 
			
		||||
      "Licensor" shall mean the copyright owner or entity authorized by
 | 
			
		||||
      the copyright owner that is granting the License.
 | 
			
		||||
 | 
			
		||||
      "Legal Entity" shall mean the union of the acting entity and all
 | 
			
		||||
      other entities that control, are controlled by, or are under common
 | 
			
		||||
      control with that entity. For the purposes of this definition,
 | 
			
		||||
      "control" means (i) the power, direct or indirect, to cause the
 | 
			
		||||
      direction or management of such entity, whether by contract or
 | 
			
		||||
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
 | 
			
		||||
      outstanding shares, or (iii) beneficial ownership of such entity.
 | 
			
		||||
 | 
			
		||||
      "You" (or "Your") shall mean an individual or Legal Entity
 | 
			
		||||
      exercising permissions granted by this License.
 | 
			
		||||
 | 
			
		||||
      "Source" form shall mean the preferred form for making modifications,
 | 
			
		||||
      including but not limited to software source code, documentation
 | 
			
		||||
      source, and configuration files.
 | 
			
		||||
 | 
			
		||||
      "Object" form shall mean any form resulting from mechanical
 | 
			
		||||
      transformation or translation of a Source form, including but
 | 
			
		||||
      not limited to compiled object code, generated documentation,
 | 
			
		||||
      and conversions to other media types.
 | 
			
		||||
 | 
			
		||||
      "Work" shall mean the work of authorship, whether in Source or
 | 
			
		||||
      Object form, made available under the License, as indicated by a
 | 
			
		||||
      copyright notice that is included in or attached to the work
 | 
			
		||||
      (an example is provided in the Appendix below).
 | 
			
		||||
 | 
			
		||||
      "Derivative Works" shall mean any work, whether in Source or Object
 | 
			
		||||
      form, that is based on (or derived from) the Work and for which the
 | 
			
		||||
      editorial revisions, annotations, elaborations, or other modifications
 | 
			
		||||
      represent, as a whole, an original work of authorship. For the purposes
 | 
			
		||||
      of this License, Derivative Works shall not include works that remain
 | 
			
		||||
      separable from, or merely link (or bind by name) to the interfaces of,
 | 
			
		||||
      the Work and Derivative Works thereof.
 | 
			
		||||
 | 
			
		||||
      "Contribution" shall mean any work of authorship, including
 | 
			
		||||
      the original version of the Work and any modifications or additions
 | 
			
		||||
      to that Work or Derivative Works thereof, that is intentionally
 | 
			
		||||
      submitted to Licensor for inclusion in the Work by the copyright owner
 | 
			
		||||
      or by an individual or Legal Entity authorized to submit on behalf of
 | 
			
		||||
      the copyright owner. For the purposes of this definition, "submitted"
 | 
			
		||||
      means any form of electronic, verbal, or written communication sent
 | 
			
		||||
      to the Licensor or its representatives, including but not limited to
 | 
			
		||||
      communication on electronic mailing lists, source code control systems,
 | 
			
		||||
      and issue tracking systems that are managed by, or on behalf of, the
 | 
			
		||||
      Licensor for the purpose of discussing and improving the Work, but
 | 
			
		||||
      excluding communication that is conspicuously marked or otherwise
 | 
			
		||||
      designated in writing by the copyright owner as "Not a Contribution."
 | 
			
		||||
 | 
			
		||||
      "Contributor" shall mean Licensor and any individual or Legal Entity
 | 
			
		||||
      on behalf of whom a Contribution has been received by Licensor and
 | 
			
		||||
      subsequently incorporated within the Work.
 | 
			
		||||
 | 
			
		||||
   2. Grant of Copyright License. Subject to the terms and conditions of
 | 
			
		||||
      this License, each Contributor hereby grants to You a perpetual,
 | 
			
		||||
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
			
		||||
      copyright license to reproduce, prepare Derivative Works of,
 | 
			
		||||
      publicly display, publicly perform, sublicense, and distribute the
 | 
			
		||||
      Work and such Derivative Works in Source or Object form.
 | 
			
		||||
 | 
			
		||||
   3. Grant of Patent License. Subject to the terms and conditions of
 | 
			
		||||
      this License, each Contributor hereby grants to You a perpetual,
 | 
			
		||||
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
			
		||||
      (except as stated in this section) patent license to make, have made,
 | 
			
		||||
      use, offer to sell, sell, import, and otherwise transfer the Work,
 | 
			
		||||
      where such license applies only to those patent claims licensable
 | 
			
		||||
      by such Contributor that are necessarily infringed by their
 | 
			
		||||
      Contribution(s) alone or by combination of their Contribution(s)
 | 
			
		||||
      with the Work to which such Contribution(s) was submitted. If You
 | 
			
		||||
      institute patent litigation against any entity (including a
 | 
			
		||||
      cross-claim or counterclaim in a lawsuit) alleging that the Work
 | 
			
		||||
      or a Contribution incorporated within the Work constitutes direct
 | 
			
		||||
      or contributory patent infringement, then any patent licenses
 | 
			
		||||
      granted to You under this License for that Work shall terminate
 | 
			
		||||
      as of the date such litigation is filed.
 | 
			
		||||
 | 
			
		||||
   4. Redistribution. You may reproduce and distribute copies of the
 | 
			
		||||
      Work or Derivative Works thereof in any medium, with or without
 | 
			
		||||
      modifications, and in Source or Object form, provided that You
 | 
			
		||||
      meet the following conditions:
 | 
			
		||||
 | 
			
		||||
      (a) You must give any other recipients of the Work or
 | 
			
		||||
          Derivative Works a copy of this License; and
 | 
			
		||||
 | 
			
		||||
      (b) You must cause any modified files to carry prominent notices
 | 
			
		||||
          stating that You changed the files; and
 | 
			
		||||
 | 
			
		||||
      (c) You must retain, in the Source form of any Derivative Works
 | 
			
		||||
          that You distribute, all copyright, patent, trademark, and
 | 
			
		||||
          attribution notices from the Source form of the Work,
 | 
			
		||||
          excluding those notices that do not pertain to any part of
 | 
			
		||||
          the Derivative Works; and
 | 
			
		||||
 | 
			
		||||
      (d) If the Work includes a "NOTICE" text file as part of its
 | 
			
		||||
          distribution, then any Derivative Works that You distribute must
 | 
			
		||||
          include a readable copy of the attribution notices contained
 | 
			
		||||
          within such NOTICE file, excluding those notices that do not
 | 
			
		||||
          pertain to any part of the Derivative Works, in at least one
 | 
			
		||||
          of the following places: within a NOTICE text file distributed
 | 
			
		||||
          as part of the Derivative Works; within the Source form or
 | 
			
		||||
          documentation, if provided along with the Derivative Works; or,
 | 
			
		||||
          within a display generated by the Derivative Works, if and
 | 
			
		||||
          wherever such third-party notices normally appear. The contents
 | 
			
		||||
          of the NOTICE file are for informational purposes only and
 | 
			
		||||
          do not modify the License. You may add Your own attribution
 | 
			
		||||
          notices within Derivative Works that You distribute, alongside
 | 
			
		||||
          or as an addendum to the NOTICE text from the Work, provided
 | 
			
		||||
          that such additional attribution notices cannot be construed
 | 
			
		||||
          as modifying the License.
 | 
			
		||||
 | 
			
		||||
      You may add Your own copyright statement to Your modifications and
 | 
			
		||||
      may provide additional or different license terms and conditions
 | 
			
		||||
      for use, reproduction, or distribution of Your modifications, or
 | 
			
		||||
      for any such Derivative Works as a whole, provided Your use,
 | 
			
		||||
      reproduction, and distribution of the Work otherwise complies with
 | 
			
		||||
      the conditions stated in this License.
 | 
			
		||||
 | 
			
		||||
   5. Submission of Contributions. Unless You explicitly state otherwise,
 | 
			
		||||
      any Contribution intentionally submitted for inclusion in the Work
 | 
			
		||||
      by You to the Licensor shall be under the terms and conditions of
 | 
			
		||||
      this License, without any additional terms or conditions.
 | 
			
		||||
      Notwithstanding the above, nothing herein shall supersede or modify
 | 
			
		||||
      the terms of any separate license agreement you may have executed
 | 
			
		||||
      with Licensor regarding such Contributions.
 | 
			
		||||
 | 
			
		||||
   6. Trademarks. This License does not grant permission to use the trade
 | 
			
		||||
      names, trademarks, service marks, or product names of the Licensor,
 | 
			
		||||
      except as required for reasonable and customary use in describing the
 | 
			
		||||
      origin of the Work and reproducing the content of the NOTICE file.
 | 
			
		||||
 | 
			
		||||
   7. Disclaimer of Warranty. Unless required by applicable law or
 | 
			
		||||
      agreed to in writing, Licensor provides the Work (and each
 | 
			
		||||
      Contributor provides its Contributions) on an "AS IS" BASIS,
 | 
			
		||||
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 | 
			
		||||
      implied, including, without limitation, any warranties or conditions
 | 
			
		||||
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
 | 
			
		||||
      PARTICULAR PURPOSE. You are solely responsible for determining the
 | 
			
		||||
      appropriateness of using or redistributing the Work and assume any
 | 
			
		||||
      risks associated with Your exercise of permissions under this License.
 | 
			
		||||
 | 
			
		||||
   8. Limitation of Liability. In no event and under no legal theory,
 | 
			
		||||
      whether in tort (including negligence), contract, or otherwise,
 | 
			
		||||
      unless required by applicable law (such as deliberate and grossly
 | 
			
		||||
      negligent acts) or agreed to in writing, shall any Contributor be
 | 
			
		||||
      liable to You for damages, including any direct, indirect, special,
 | 
			
		||||
      incidental, or consequential damages of any character arising as a
 | 
			
		||||
      result of this License or out of the use or inability to use the
 | 
			
		||||
      Work (including but not limited to damages for loss of goodwill,
 | 
			
		||||
      work stoppage, computer failure or malfunction, or any and all
 | 
			
		||||
      other commercial damages or losses), even if such Contributor
 | 
			
		||||
      has been advised of the possibility of such damages.
 | 
			
		||||
 | 
			
		||||
   9. Accepting Warranty or Additional Liability. While redistributing
 | 
			
		||||
      the Work or Derivative Works thereof, You may choose to offer,
 | 
			
		||||
      and charge a fee for, acceptance of support, warranty, indemnity,
 | 
			
		||||
      or other liability obligations and/or rights consistent with this
 | 
			
		||||
      License. However, in accepting such obligations, You may act only
 | 
			
		||||
      on Your own behalf and on Your sole responsibility, not on behalf
 | 
			
		||||
      of any other Contributor, and only if You agree to indemnify,
 | 
			
		||||
      defend, and hold each Contributor harmless for any liability
 | 
			
		||||
      incurred by, or claims asserted against, such Contributor by reason
 | 
			
		||||
      of your accepting any such warranty or additional liability.
 | 
			
		||||
 | 
			
		||||
   END OF TERMS AND CONDITIONS
 | 
			
		||||
 | 
			
		||||
   APPENDIX: How to apply the Apache License to your work.
 | 
			
		||||
 | 
			
		||||
      To apply the Apache License to your work, attach the following
 | 
			
		||||
      boilerplate notice, with the fields enclosed by brackets "[]"
 | 
			
		||||
      replaced with your own identifying information. (Don't include
 | 
			
		||||
      the brackets!)  The text should be enclosed in the appropriate
 | 
			
		||||
      comment syntax for the file format. We also recommend that a
 | 
			
		||||
      file or class name and description of purpose be included on the
 | 
			
		||||
      same "printed page" as the copyright notice for easier
 | 
			
		||||
      identification within third-party archives.
 | 
			
		||||
 | 
			
		||||
   Copyright [yyyy] [name of copyright owner]
 | 
			
		||||
 | 
			
		||||
   Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
   you may not use this file except in compliance with the License.
 | 
			
		||||
   You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
       http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
   Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
   distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
   See the License for the specific language governing permissions and
 | 
			
		||||
   limitations under the License.
 | 
			
		||||
							
								
								
									
										5
									
								
								vendor/github.com/prometheus/common/NOTICE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/prometheus/common/NOTICE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
Common libraries shared by Prometheus Go components.
 | 
			
		||||
Copyright 2015 The Prometheus Authors
 | 
			
		||||
 | 
			
		||||
This product includes software developed at
 | 
			
		||||
SoundCloud Ltd. (http://soundcloud.com/).
 | 
			
		||||
							
								
								
									
										429
									
								
								vendor/github.com/prometheus/common/expfmt/decode.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										429
									
								
								vendor/github.com/prometheus/common/expfmt/decode.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,429 @@
 | 
			
		||||
// Copyright 2015 The Prometheus Authors
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
// http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
package expfmt
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"math"
 | 
			
		||||
	"mime"
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	dto "github.com/prometheus/client_model/go"
 | 
			
		||||
 | 
			
		||||
	"github.com/matttproud/golang_protobuf_extensions/pbutil"
 | 
			
		||||
	"github.com/prometheus/common/model"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Decoder types decode an input stream into metric families.
 | 
			
		||||
type Decoder interface {
 | 
			
		||||
	Decode(*dto.MetricFamily) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DecodeOptions contains options used by the Decoder and in sample extraction.
 | 
			
		||||
type DecodeOptions struct {
 | 
			
		||||
	// Timestamp is added to each value from the stream that has no explicit timestamp set.
 | 
			
		||||
	Timestamp model.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ResponseFormat extracts the correct format from a HTTP response header.
 | 
			
		||||
// If no matching format can be found FormatUnknown is returned.
 | 
			
		||||
func ResponseFormat(h http.Header) Format {
 | 
			
		||||
	ct := h.Get(hdrContentType)
 | 
			
		||||
 | 
			
		||||
	mediatype, params, err := mime.ParseMediaType(ct)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return FmtUnknown
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const textType = "text/plain"
 | 
			
		||||
 | 
			
		||||
	switch mediatype {
 | 
			
		||||
	case ProtoType:
 | 
			
		||||
		if p, ok := params["proto"]; ok && p != ProtoProtocol {
 | 
			
		||||
			return FmtUnknown
 | 
			
		||||
		}
 | 
			
		||||
		if e, ok := params["encoding"]; ok && e != "delimited" {
 | 
			
		||||
			return FmtUnknown
 | 
			
		||||
		}
 | 
			
		||||
		return FmtProtoDelim
 | 
			
		||||
 | 
			
		||||
	case textType:
 | 
			
		||||
		if v, ok := params["version"]; ok && v != TextVersion {
 | 
			
		||||
			return FmtUnknown
 | 
			
		||||
		}
 | 
			
		||||
		return FmtText
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return FmtUnknown
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewDecoder returns a new decoder based on the given input format.
 | 
			
		||||
// If the input format does not imply otherwise, a text format decoder is returned.
 | 
			
		||||
func NewDecoder(r io.Reader, format Format) Decoder {
 | 
			
		||||
	switch format {
 | 
			
		||||
	case FmtProtoDelim:
 | 
			
		||||
		return &protoDecoder{r: r}
 | 
			
		||||
	}
 | 
			
		||||
	return &textDecoder{r: r}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// protoDecoder implements the Decoder interface for protocol buffers.
 | 
			
		||||
type protoDecoder struct {
 | 
			
		||||
	r io.Reader
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Decode implements the Decoder interface.
 | 
			
		||||
func (d *protoDecoder) Decode(v *dto.MetricFamily) error {
 | 
			
		||||
	_, err := pbutil.ReadDelimited(d.r, v)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if !model.IsValidMetricName(model.LabelValue(v.GetName())) {
 | 
			
		||||
		return fmt.Errorf("invalid metric name %q", v.GetName())
 | 
			
		||||
	}
 | 
			
		||||
	for _, m := range v.GetMetric() {
 | 
			
		||||
		if m == nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		for _, l := range m.GetLabel() {
 | 
			
		||||
			if l == nil {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if !model.LabelValue(l.GetValue()).IsValid() {
 | 
			
		||||
				return fmt.Errorf("invalid label value %q", l.GetValue())
 | 
			
		||||
			}
 | 
			
		||||
			if !model.LabelName(l.GetName()).IsValid() {
 | 
			
		||||
				return fmt.Errorf("invalid label name %q", l.GetName())
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// textDecoder implements the Decoder interface for the text protocol.
 | 
			
		||||
type textDecoder struct {
 | 
			
		||||
	r    io.Reader
 | 
			
		||||
	p    TextParser
 | 
			
		||||
	fams []*dto.MetricFamily
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Decode implements the Decoder interface.
 | 
			
		||||
func (d *textDecoder) Decode(v *dto.MetricFamily) error {
 | 
			
		||||
	// TODO(fabxc): Wrap this as a line reader to make streaming safer.
 | 
			
		||||
	if len(d.fams) == 0 {
 | 
			
		||||
		// No cached metric families, read everything and parse metrics.
 | 
			
		||||
		fams, err := d.p.TextToMetricFamilies(d.r)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if len(fams) == 0 {
 | 
			
		||||
			return io.EOF
 | 
			
		||||
		}
 | 
			
		||||
		d.fams = make([]*dto.MetricFamily, 0, len(fams))
 | 
			
		||||
		for _, f := range fams {
 | 
			
		||||
			d.fams = append(d.fams, f)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*v = *d.fams[0]
 | 
			
		||||
	d.fams = d.fams[1:]
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SampleDecoder wraps a Decoder to extract samples from the metric families
 | 
			
		||||
// decoded by the wrapped Decoder.
 | 
			
		||||
type SampleDecoder struct {
 | 
			
		||||
	Dec  Decoder
 | 
			
		||||
	Opts *DecodeOptions
 | 
			
		||||
 | 
			
		||||
	f dto.MetricFamily
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Decode calls the Decode method of the wrapped Decoder and then extracts the
 | 
			
		||||
// samples from the decoded MetricFamily into the provided model.Vector.
 | 
			
		||||
func (sd *SampleDecoder) Decode(s *model.Vector) error {
 | 
			
		||||
	err := sd.Dec.Decode(&sd.f)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	*s, err = extractSamples(&sd.f, sd.Opts)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractSamples builds a slice of samples from the provided metric
 | 
			
		||||
// families. If an error occurrs during sample extraction, it continues to
 | 
			
		||||
// extract from the remaining metric families. The returned error is the last
 | 
			
		||||
// error that has occurred.
 | 
			
		||||
func ExtractSamples(o *DecodeOptions, fams ...*dto.MetricFamily) (model.Vector, error) {
 | 
			
		||||
	var (
 | 
			
		||||
		all     model.Vector
 | 
			
		||||
		lastErr error
 | 
			
		||||
	)
 | 
			
		||||
	for _, f := range fams {
 | 
			
		||||
		some, err := extractSamples(f, o)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			lastErr = err
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		all = append(all, some...)
 | 
			
		||||
	}
 | 
			
		||||
	return all, lastErr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func extractSamples(f *dto.MetricFamily, o *DecodeOptions) (model.Vector, error) {
 | 
			
		||||
	switch f.GetType() {
 | 
			
		||||
	case dto.MetricType_COUNTER:
 | 
			
		||||
		return extractCounter(o, f), nil
 | 
			
		||||
	case dto.MetricType_GAUGE:
 | 
			
		||||
		return extractGauge(o, f), nil
 | 
			
		||||
	case dto.MetricType_SUMMARY:
 | 
			
		||||
		return extractSummary(o, f), nil
 | 
			
		||||
	case dto.MetricType_UNTYPED:
 | 
			
		||||
		return extractUntyped(o, f), nil
 | 
			
		||||
	case dto.MetricType_HISTOGRAM:
 | 
			
		||||
		return extractHistogram(o, f), nil
 | 
			
		||||
	}
 | 
			
		||||
	return nil, fmt.Errorf("expfmt.extractSamples: unknown metric family type %v", f.GetType())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func extractCounter(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
 | 
			
		||||
	samples := make(model.Vector, 0, len(f.Metric))
 | 
			
		||||
 | 
			
		||||
	for _, m := range f.Metric {
 | 
			
		||||
		if m.Counter == nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		lset := make(model.LabelSet, len(m.Label)+1)
 | 
			
		||||
		for _, p := range m.Label {
 | 
			
		||||
			lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
 | 
			
		||||
		}
 | 
			
		||||
		lset[model.MetricNameLabel] = model.LabelValue(f.GetName())
 | 
			
		||||
 | 
			
		||||
		smpl := &model.Sample{
 | 
			
		||||
			Metric: model.Metric(lset),
 | 
			
		||||
			Value:  model.SampleValue(m.Counter.GetValue()),
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if m.TimestampMs != nil {
 | 
			
		||||
			smpl.Timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
 | 
			
		||||
		} else {
 | 
			
		||||
			smpl.Timestamp = o.Timestamp
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		samples = append(samples, smpl)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return samples
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func extractGauge(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
 | 
			
		||||
	samples := make(model.Vector, 0, len(f.Metric))
 | 
			
		||||
 | 
			
		||||
	for _, m := range f.Metric {
 | 
			
		||||
		if m.Gauge == nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		lset := make(model.LabelSet, len(m.Label)+1)
 | 
			
		||||
		for _, p := range m.Label {
 | 
			
		||||
			lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
 | 
			
		||||
		}
 | 
			
		||||
		lset[model.MetricNameLabel] = model.LabelValue(f.GetName())
 | 
			
		||||
 | 
			
		||||
		smpl := &model.Sample{
 | 
			
		||||
			Metric: model.Metric(lset),
 | 
			
		||||
			Value:  model.SampleValue(m.Gauge.GetValue()),
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if m.TimestampMs != nil {
 | 
			
		||||
			smpl.Timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
 | 
			
		||||
		} else {
 | 
			
		||||
			smpl.Timestamp = o.Timestamp
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		samples = append(samples, smpl)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return samples
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func extractUntyped(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
 | 
			
		||||
	samples := make(model.Vector, 0, len(f.Metric))
 | 
			
		||||
 | 
			
		||||
	for _, m := range f.Metric {
 | 
			
		||||
		if m.Untyped == nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		lset := make(model.LabelSet, len(m.Label)+1)
 | 
			
		||||
		for _, p := range m.Label {
 | 
			
		||||
			lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
 | 
			
		||||
		}
 | 
			
		||||
		lset[model.MetricNameLabel] = model.LabelValue(f.GetName())
 | 
			
		||||
 | 
			
		||||
		smpl := &model.Sample{
 | 
			
		||||
			Metric: model.Metric(lset),
 | 
			
		||||
			Value:  model.SampleValue(m.Untyped.GetValue()),
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if m.TimestampMs != nil {
 | 
			
		||||
			smpl.Timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
 | 
			
		||||
		} else {
 | 
			
		||||
			smpl.Timestamp = o.Timestamp
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		samples = append(samples, smpl)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return samples
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func extractSummary(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
 | 
			
		||||
	samples := make(model.Vector, 0, len(f.Metric))
 | 
			
		||||
 | 
			
		||||
	for _, m := range f.Metric {
 | 
			
		||||
		if m.Summary == nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		timestamp := o.Timestamp
 | 
			
		||||
		if m.TimestampMs != nil {
 | 
			
		||||
			timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, q := range m.Summary.Quantile {
 | 
			
		||||
			lset := make(model.LabelSet, len(m.Label)+2)
 | 
			
		||||
			for _, p := range m.Label {
 | 
			
		||||
				lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
 | 
			
		||||
			}
 | 
			
		||||
			// BUG(matt): Update other names to "quantile".
 | 
			
		||||
			lset[model.LabelName(model.QuantileLabel)] = model.LabelValue(fmt.Sprint(q.GetQuantile()))
 | 
			
		||||
			lset[model.MetricNameLabel] = model.LabelValue(f.GetName())
 | 
			
		||||
 | 
			
		||||
			samples = append(samples, &model.Sample{
 | 
			
		||||
				Metric:    model.Metric(lset),
 | 
			
		||||
				Value:     model.SampleValue(q.GetValue()),
 | 
			
		||||
				Timestamp: timestamp,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		lset := make(model.LabelSet, len(m.Label)+1)
 | 
			
		||||
		for _, p := range m.Label {
 | 
			
		||||
			lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
 | 
			
		||||
		}
 | 
			
		||||
		lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_sum")
 | 
			
		||||
 | 
			
		||||
		samples = append(samples, &model.Sample{
 | 
			
		||||
			Metric:    model.Metric(lset),
 | 
			
		||||
			Value:     model.SampleValue(m.Summary.GetSampleSum()),
 | 
			
		||||
			Timestamp: timestamp,
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		lset = make(model.LabelSet, len(m.Label)+1)
 | 
			
		||||
		for _, p := range m.Label {
 | 
			
		||||
			lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
 | 
			
		||||
		}
 | 
			
		||||
		lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_count")
 | 
			
		||||
 | 
			
		||||
		samples = append(samples, &model.Sample{
 | 
			
		||||
			Metric:    model.Metric(lset),
 | 
			
		||||
			Value:     model.SampleValue(m.Summary.GetSampleCount()),
 | 
			
		||||
			Timestamp: timestamp,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return samples
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func extractHistogram(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
 | 
			
		||||
	samples := make(model.Vector, 0, len(f.Metric))
 | 
			
		||||
 | 
			
		||||
	for _, m := range f.Metric {
 | 
			
		||||
		if m.Histogram == nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		timestamp := o.Timestamp
 | 
			
		||||
		if m.TimestampMs != nil {
 | 
			
		||||
			timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		infSeen := false
 | 
			
		||||
 | 
			
		||||
		for _, q := range m.Histogram.Bucket {
 | 
			
		||||
			lset := make(model.LabelSet, len(m.Label)+2)
 | 
			
		||||
			for _, p := range m.Label {
 | 
			
		||||
				lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
 | 
			
		||||
			}
 | 
			
		||||
			lset[model.LabelName(model.BucketLabel)] = model.LabelValue(fmt.Sprint(q.GetUpperBound()))
 | 
			
		||||
			lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_bucket")
 | 
			
		||||
 | 
			
		||||
			if math.IsInf(q.GetUpperBound(), +1) {
 | 
			
		||||
				infSeen = true
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			samples = append(samples, &model.Sample{
 | 
			
		||||
				Metric:    model.Metric(lset),
 | 
			
		||||
				Value:     model.SampleValue(q.GetCumulativeCount()),
 | 
			
		||||
				Timestamp: timestamp,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		lset := make(model.LabelSet, len(m.Label)+1)
 | 
			
		||||
		for _, p := range m.Label {
 | 
			
		||||
			lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
 | 
			
		||||
		}
 | 
			
		||||
		lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_sum")
 | 
			
		||||
 | 
			
		||||
		samples = append(samples, &model.Sample{
 | 
			
		||||
			Metric:    model.Metric(lset),
 | 
			
		||||
			Value:     model.SampleValue(m.Histogram.GetSampleSum()),
 | 
			
		||||
			Timestamp: timestamp,
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		lset = make(model.LabelSet, len(m.Label)+1)
 | 
			
		||||
		for _, p := range m.Label {
 | 
			
		||||
			lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
 | 
			
		||||
		}
 | 
			
		||||
		lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_count")
 | 
			
		||||
 | 
			
		||||
		count := &model.Sample{
 | 
			
		||||
			Metric:    model.Metric(lset),
 | 
			
		||||
			Value:     model.SampleValue(m.Histogram.GetSampleCount()),
 | 
			
		||||
			Timestamp: timestamp,
 | 
			
		||||
		}
 | 
			
		||||
		samples = append(samples, count)
 | 
			
		||||
 | 
			
		||||
		if !infSeen {
 | 
			
		||||
			// Append an infinity bucket sample.
 | 
			
		||||
			lset := make(model.LabelSet, len(m.Label)+2)
 | 
			
		||||
			for _, p := range m.Label {
 | 
			
		||||
				lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
 | 
			
		||||
			}
 | 
			
		||||
			lset[model.LabelName(model.BucketLabel)] = model.LabelValue("+Inf")
 | 
			
		||||
			lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_bucket")
 | 
			
		||||
 | 
			
		||||
			samples = append(samples, &model.Sample{
 | 
			
		||||
				Metric:    model.Metric(lset),
 | 
			
		||||
				Value:     count.Value,
 | 
			
		||||
				Timestamp: timestamp,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return samples
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										162
									
								
								vendor/github.com/prometheus/common/expfmt/encode.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								vendor/github.com/prometheus/common/expfmt/encode.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,162 @@
 | 
			
		||||
// Copyright 2015 The Prometheus Authors
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
// http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
package expfmt
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"github.com/golang/protobuf/proto"
 | 
			
		||||
	"github.com/matttproud/golang_protobuf_extensions/pbutil"
 | 
			
		||||
	"github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg"
 | 
			
		||||
 | 
			
		||||
	dto "github.com/prometheus/client_model/go"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Encoder types encode metric families into an underlying wire protocol.
 | 
			
		||||
type Encoder interface {
 | 
			
		||||
	Encode(*dto.MetricFamily) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Closer is implemented by Encoders that need to be closed to finalize
 | 
			
		||||
// encoding. (For example, OpenMetrics needs a final `# EOF` line.)
 | 
			
		||||
//
 | 
			
		||||
// Note that all Encoder implementations returned from this package implement
 | 
			
		||||
// Closer, too, even if the Close call is a no-op. This happens in preparation
 | 
			
		||||
// for adding a Close method to the Encoder interface directly in a (mildly
 | 
			
		||||
// breaking) release in the future.
 | 
			
		||||
type Closer interface {
 | 
			
		||||
	Close() error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type encoderCloser struct {
 | 
			
		||||
	encode func(*dto.MetricFamily) error
 | 
			
		||||
	close  func() error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ec encoderCloser) Encode(v *dto.MetricFamily) error {
 | 
			
		||||
	return ec.encode(v)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ec encoderCloser) Close() error {
 | 
			
		||||
	return ec.close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Negotiate returns the Content-Type based on the given Accept header. If no
 | 
			
		||||
// appropriate accepted type is found, FmtText is returned (which is the
 | 
			
		||||
// Prometheus text format). This function will never negotiate FmtOpenMetrics,
 | 
			
		||||
// as the support is still experimental. To include the option to negotiate
 | 
			
		||||
// FmtOpenMetrics, use NegotiateOpenMetrics.
 | 
			
		||||
func Negotiate(h http.Header) Format {
 | 
			
		||||
	for _, ac := range goautoneg.ParseAccept(h.Get(hdrAccept)) {
 | 
			
		||||
		ver := ac.Params["version"]
 | 
			
		||||
		if ac.Type+"/"+ac.SubType == ProtoType && ac.Params["proto"] == ProtoProtocol {
 | 
			
		||||
			switch ac.Params["encoding"] {
 | 
			
		||||
			case "delimited":
 | 
			
		||||
				return FmtProtoDelim
 | 
			
		||||
			case "text":
 | 
			
		||||
				return FmtProtoText
 | 
			
		||||
			case "compact-text":
 | 
			
		||||
				return FmtProtoCompact
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if ac.Type == "text" && ac.SubType == "plain" && (ver == TextVersion || ver == "") {
 | 
			
		||||
			return FmtText
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return FmtText
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NegotiateIncludingOpenMetrics works like Negotiate but includes
 | 
			
		||||
// FmtOpenMetrics as an option for the result. Note that this function is
 | 
			
		||||
// temporary and will disappear once FmtOpenMetrics is fully supported and as
 | 
			
		||||
// such may be negotiated by the normal Negotiate function.
 | 
			
		||||
func NegotiateIncludingOpenMetrics(h http.Header) Format {
 | 
			
		||||
	for _, ac := range goautoneg.ParseAccept(h.Get(hdrAccept)) {
 | 
			
		||||
		ver := ac.Params["version"]
 | 
			
		||||
		if ac.Type+"/"+ac.SubType == ProtoType && ac.Params["proto"] == ProtoProtocol {
 | 
			
		||||
			switch ac.Params["encoding"] {
 | 
			
		||||
			case "delimited":
 | 
			
		||||
				return FmtProtoDelim
 | 
			
		||||
			case "text":
 | 
			
		||||
				return FmtProtoText
 | 
			
		||||
			case "compact-text":
 | 
			
		||||
				return FmtProtoCompact
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if ac.Type == "text" && ac.SubType == "plain" && (ver == TextVersion || ver == "") {
 | 
			
		||||
			return FmtText
 | 
			
		||||
		}
 | 
			
		||||
		if ac.Type+"/"+ac.SubType == OpenMetricsType && (ver == OpenMetricsVersion || ver == "") {
 | 
			
		||||
			return FmtOpenMetrics
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return FmtText
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewEncoder returns a new encoder based on content type negotiation. All
 | 
			
		||||
// Encoder implementations returned by NewEncoder also implement Closer, and
 | 
			
		||||
// callers should always call the Close method. It is currently only required
 | 
			
		||||
// for FmtOpenMetrics, but a future (breaking) release will add the Close method
 | 
			
		||||
// to the Encoder interface directly. The current version of the Encoder
 | 
			
		||||
// interface is kept for backwards compatibility.
 | 
			
		||||
func NewEncoder(w io.Writer, format Format) Encoder {
 | 
			
		||||
	switch format {
 | 
			
		||||
	case FmtProtoDelim:
 | 
			
		||||
		return encoderCloser{
 | 
			
		||||
			encode: func(v *dto.MetricFamily) error {
 | 
			
		||||
				_, err := pbutil.WriteDelimited(w, v)
 | 
			
		||||
				return err
 | 
			
		||||
			},
 | 
			
		||||
			close: func() error { return nil },
 | 
			
		||||
		}
 | 
			
		||||
	case FmtProtoCompact:
 | 
			
		||||
		return encoderCloser{
 | 
			
		||||
			encode: func(v *dto.MetricFamily) error {
 | 
			
		||||
				_, err := fmt.Fprintln(w, v.String())
 | 
			
		||||
				return err
 | 
			
		||||
			},
 | 
			
		||||
			close: func() error { return nil },
 | 
			
		||||
		}
 | 
			
		||||
	case FmtProtoText:
 | 
			
		||||
		return encoderCloser{
 | 
			
		||||
			encode: func(v *dto.MetricFamily) error {
 | 
			
		||||
				_, err := fmt.Fprintln(w, proto.MarshalTextString(v))
 | 
			
		||||
				return err
 | 
			
		||||
			},
 | 
			
		||||
			close: func() error { return nil },
 | 
			
		||||
		}
 | 
			
		||||
	case FmtText:
 | 
			
		||||
		return encoderCloser{
 | 
			
		||||
			encode: func(v *dto.MetricFamily) error {
 | 
			
		||||
				_, err := MetricFamilyToText(w, v)
 | 
			
		||||
				return err
 | 
			
		||||
			},
 | 
			
		||||
			close: func() error { return nil },
 | 
			
		||||
		}
 | 
			
		||||
	case FmtOpenMetrics:
 | 
			
		||||
		return encoderCloser{
 | 
			
		||||
			encode: func(v *dto.MetricFamily) error {
 | 
			
		||||
				_, err := MetricFamilyToOpenMetrics(w, v)
 | 
			
		||||
				return err
 | 
			
		||||
			},
 | 
			
		||||
			close: func() error {
 | 
			
		||||
				_, err := FinalizeOpenMetrics(w)
 | 
			
		||||
				return err
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	panic(fmt.Errorf("expfmt.NewEncoder: unknown format %q", format))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										41
									
								
								vendor/github.com/prometheus/common/expfmt/expfmt.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								vendor/github.com/prometheus/common/expfmt/expfmt.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
// Copyright 2015 The Prometheus Authors
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
// http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
// Package expfmt contains tools for reading and writing Prometheus metrics.
 | 
			
		||||
package expfmt
 | 
			
		||||
 | 
			
		||||
// Format specifies the HTTP content type of the different wire protocols.
 | 
			
		||||
type Format string
 | 
			
		||||
 | 
			
		||||
// Constants to assemble the Content-Type values for the different wire protocols.
 | 
			
		||||
const (
 | 
			
		||||
	TextVersion        = "0.0.4"
 | 
			
		||||
	ProtoType          = `application/vnd.google.protobuf`
 | 
			
		||||
	ProtoProtocol      = `io.prometheus.client.MetricFamily`
 | 
			
		||||
	ProtoFmt           = ProtoType + "; proto=" + ProtoProtocol + ";"
 | 
			
		||||
	OpenMetricsType    = `application/openmetrics-text`
 | 
			
		||||
	OpenMetricsVersion = "0.0.1"
 | 
			
		||||
 | 
			
		||||
	// The Content-Type values for the different wire protocols.
 | 
			
		||||
	FmtUnknown      Format = `<unknown>`
 | 
			
		||||
	FmtText         Format = `text/plain; version=` + TextVersion + `; charset=utf-8`
 | 
			
		||||
	FmtProtoDelim   Format = ProtoFmt + ` encoding=delimited`
 | 
			
		||||
	FmtProtoText    Format = ProtoFmt + ` encoding=text`
 | 
			
		||||
	FmtProtoCompact Format = ProtoFmt + ` encoding=compact-text`
 | 
			
		||||
	FmtOpenMetrics  Format = OpenMetricsType + `; version=` + OpenMetricsVersion + `; charset=utf-8`
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	hdrContentType = "Content-Type"
 | 
			
		||||
	hdrAccept      = "Accept"
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										36
									
								
								vendor/github.com/prometheus/common/expfmt/fuzz.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								vendor/github.com/prometheus/common/expfmt/fuzz.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
			
		||||
// Copyright 2014 The Prometheus Authors
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
// http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
// Build only when actually fuzzing
 | 
			
		||||
// +build gofuzz
 | 
			
		||||
 | 
			
		||||
package expfmt
 | 
			
		||||
 | 
			
		||||
import "bytes"
 | 
			
		||||
 | 
			
		||||
// Fuzz text metric parser with with github.com/dvyukov/go-fuzz:
 | 
			
		||||
//
 | 
			
		||||
//     go-fuzz-build github.com/prometheus/common/expfmt
 | 
			
		||||
//     go-fuzz -bin expfmt-fuzz.zip -workdir fuzz
 | 
			
		||||
//
 | 
			
		||||
// Further input samples should go in the folder fuzz/corpus.
 | 
			
		||||
func Fuzz(in []byte) int {
 | 
			
		||||
	parser := TextParser{}
 | 
			
		||||
	_, err := parser.TextToMetricFamilies(bytes.NewReader(in))
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 1
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										527
									
								
								vendor/github.com/prometheus/common/expfmt/openmetrics_create.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										527
									
								
								vendor/github.com/prometheus/common/expfmt/openmetrics_create.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,527 @@
 | 
			
		||||
// Copyright 2020 The Prometheus Authors
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
// http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
package expfmt
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"math"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/golang/protobuf/ptypes"
 | 
			
		||||
	"github.com/prometheus/common/model"
 | 
			
		||||
 | 
			
		||||
	dto "github.com/prometheus/client_model/go"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// MetricFamilyToOpenMetrics converts a MetricFamily proto message into the
 | 
			
		||||
// OpenMetrics text format and writes the resulting lines to 'out'. It returns
 | 
			
		||||
// the number of bytes written and any error encountered. The output will have
 | 
			
		||||
// the same order as the input, no further sorting is performed. Furthermore,
 | 
			
		||||
// this function assumes the input is already sanitized and does not perform any
 | 
			
		||||
// sanity checks. If the input contains duplicate metrics or invalid metric or
 | 
			
		||||
// label names, the conversion will result in invalid text format output.
 | 
			
		||||
//
 | 
			
		||||
// This function fulfills the type 'expfmt.encoder'.
 | 
			
		||||
//
 | 
			
		||||
// Note that OpenMetrics requires a final `# EOF` line. Since this function acts
 | 
			
		||||
// on individual metric families, it is the responsibility of the caller to
 | 
			
		||||
// append this line to 'out' once all metric families have been written.
 | 
			
		||||
// Conveniently, this can be done by calling FinalizeOpenMetrics.
 | 
			
		||||
//
 | 
			
		||||
// The output should be fully OpenMetrics compliant. However, there are a few
 | 
			
		||||
// missing features and peculiarities to avoid complications when switching from
 | 
			
		||||
// Prometheus to OpenMetrics or vice versa:
 | 
			
		||||
//
 | 
			
		||||
// - Counters are expected to have the `_total` suffix in their metric name. In
 | 
			
		||||
//   the output, the suffix will be truncated from the `# TYPE` and `# HELP`
 | 
			
		||||
//   line. A counter with a missing `_total` suffix is not an error. However,
 | 
			
		||||
//   its type will be set to `unknown` in that case to avoid invalid OpenMetrics
 | 
			
		||||
//   output.
 | 
			
		||||
//
 | 
			
		||||
// - No support for the following (optional) features: `# UNIT` line, `_created`
 | 
			
		||||
//   line, info type, stateset type, gaugehistogram type.
 | 
			
		||||
//
 | 
			
		||||
// - The size of exemplar labels is not checked (i.e. it's possible to create
 | 
			
		||||
//   exemplars that are larger than allowed by the OpenMetrics specification).
 | 
			
		||||
//
 | 
			
		||||
// - The value of Counters is not checked. (OpenMetrics doesn't allow counters
 | 
			
		||||
//   with a `NaN` value.)
 | 
			
		||||
func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily) (written int, err error) {
 | 
			
		||||
	name := in.GetName()
 | 
			
		||||
	if name == "" {
 | 
			
		||||
		return 0, fmt.Errorf("MetricFamily has no name: %s", in)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Try the interface upgrade. If it doesn't work, we'll use a
 | 
			
		||||
	// bufio.Writer from the sync.Pool.
 | 
			
		||||
	w, ok := out.(enhancedWriter)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		b := bufPool.Get().(*bufio.Writer)
 | 
			
		||||
		b.Reset(out)
 | 
			
		||||
		w = b
 | 
			
		||||
		defer func() {
 | 
			
		||||
			bErr := b.Flush()
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				err = bErr
 | 
			
		||||
			}
 | 
			
		||||
			bufPool.Put(b)
 | 
			
		||||
		}()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		n          int
 | 
			
		||||
		metricType = in.GetType()
 | 
			
		||||
		shortName  = name
 | 
			
		||||
	)
 | 
			
		||||
	if metricType == dto.MetricType_COUNTER && strings.HasSuffix(shortName, "_total") {
 | 
			
		||||
		shortName = name[:len(name)-6]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Comments, first HELP, then TYPE.
 | 
			
		||||
	if in.Help != nil {
 | 
			
		||||
		n, err = w.WriteString("# HELP ")
 | 
			
		||||
		written += n
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		n, err = w.WriteString(shortName)
 | 
			
		||||
		written += n
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		err = w.WriteByte(' ')
 | 
			
		||||
		written++
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		n, err = writeEscapedString(w, *in.Help, true)
 | 
			
		||||
		written += n
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		err = w.WriteByte('\n')
 | 
			
		||||
		written++
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	n, err = w.WriteString("# TYPE ")
 | 
			
		||||
	written += n
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	n, err = w.WriteString(shortName)
 | 
			
		||||
	written += n
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	switch metricType {
 | 
			
		||||
	case dto.MetricType_COUNTER:
 | 
			
		||||
		if strings.HasSuffix(name, "_total") {
 | 
			
		||||
			n, err = w.WriteString(" counter\n")
 | 
			
		||||
		} else {
 | 
			
		||||
			n, err = w.WriteString(" unknown\n")
 | 
			
		||||
		}
 | 
			
		||||
	case dto.MetricType_GAUGE:
 | 
			
		||||
		n, err = w.WriteString(" gauge\n")
 | 
			
		||||
	case dto.MetricType_SUMMARY:
 | 
			
		||||
		n, err = w.WriteString(" summary\n")
 | 
			
		||||
	case dto.MetricType_UNTYPED:
 | 
			
		||||
		n, err = w.WriteString(" unknown\n")
 | 
			
		||||
	case dto.MetricType_HISTOGRAM:
 | 
			
		||||
		n, err = w.WriteString(" histogram\n")
 | 
			
		||||
	default:
 | 
			
		||||
		return written, fmt.Errorf("unknown metric type %s", metricType.String())
 | 
			
		||||
	}
 | 
			
		||||
	written += n
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Finally the samples, one line for each.
 | 
			
		||||
	for _, metric := range in.Metric {
 | 
			
		||||
		switch metricType {
 | 
			
		||||
		case dto.MetricType_COUNTER:
 | 
			
		||||
			if metric.Counter == nil {
 | 
			
		||||
				return written, fmt.Errorf(
 | 
			
		||||
					"expected counter in metric %s %s", name, metric,
 | 
			
		||||
				)
 | 
			
		||||
			}
 | 
			
		||||
			// Note that we have ensured above that either the name
 | 
			
		||||
			// ends on `_total` or that the rendered type is
 | 
			
		||||
			// `unknown`. Therefore, no `_total` must be added here.
 | 
			
		||||
			n, err = writeOpenMetricsSample(
 | 
			
		||||
				w, name, "", metric, "", 0,
 | 
			
		||||
				metric.Counter.GetValue(), 0, false,
 | 
			
		||||
				metric.Counter.Exemplar,
 | 
			
		||||
			)
 | 
			
		||||
		case dto.MetricType_GAUGE:
 | 
			
		||||
			if metric.Gauge == nil {
 | 
			
		||||
				return written, fmt.Errorf(
 | 
			
		||||
					"expected gauge in metric %s %s", name, metric,
 | 
			
		||||
				)
 | 
			
		||||
			}
 | 
			
		||||
			n, err = writeOpenMetricsSample(
 | 
			
		||||
				w, name, "", metric, "", 0,
 | 
			
		||||
				metric.Gauge.GetValue(), 0, false,
 | 
			
		||||
				nil,
 | 
			
		||||
			)
 | 
			
		||||
		case dto.MetricType_UNTYPED:
 | 
			
		||||
			if metric.Untyped == nil {
 | 
			
		||||
				return written, fmt.Errorf(
 | 
			
		||||
					"expected untyped in metric %s %s", name, metric,
 | 
			
		||||
				)
 | 
			
		||||
			}
 | 
			
		||||
			n, err = writeOpenMetricsSample(
 | 
			
		||||
				w, name, "", metric, "", 0,
 | 
			
		||||
				metric.Untyped.GetValue(), 0, false,
 | 
			
		||||
				nil,
 | 
			
		||||
			)
 | 
			
		||||
		case dto.MetricType_SUMMARY:
 | 
			
		||||
			if metric.Summary == nil {
 | 
			
		||||
				return written, fmt.Errorf(
 | 
			
		||||
					"expected summary in metric %s %s", name, metric,
 | 
			
		||||
				)
 | 
			
		||||
			}
 | 
			
		||||
			for _, q := range metric.Summary.Quantile {
 | 
			
		||||
				n, err = writeOpenMetricsSample(
 | 
			
		||||
					w, name, "", metric,
 | 
			
		||||
					model.QuantileLabel, q.GetQuantile(),
 | 
			
		||||
					q.GetValue(), 0, false,
 | 
			
		||||
					nil,
 | 
			
		||||
				)
 | 
			
		||||
				written += n
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			n, err = writeOpenMetricsSample(
 | 
			
		||||
				w, name, "_sum", metric, "", 0,
 | 
			
		||||
				metric.Summary.GetSampleSum(), 0, false,
 | 
			
		||||
				nil,
 | 
			
		||||
			)
 | 
			
		||||
			written += n
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			n, err = writeOpenMetricsSample(
 | 
			
		||||
				w, name, "_count", metric, "", 0,
 | 
			
		||||
				0, metric.Summary.GetSampleCount(), true,
 | 
			
		||||
				nil,
 | 
			
		||||
			)
 | 
			
		||||
		case dto.MetricType_HISTOGRAM:
 | 
			
		||||
			if metric.Histogram == nil {
 | 
			
		||||
				return written, fmt.Errorf(
 | 
			
		||||
					"expected histogram in metric %s %s", name, metric,
 | 
			
		||||
				)
 | 
			
		||||
			}
 | 
			
		||||
			infSeen := false
 | 
			
		||||
			for _, b := range metric.Histogram.Bucket {
 | 
			
		||||
				n, err = writeOpenMetricsSample(
 | 
			
		||||
					w, name, "_bucket", metric,
 | 
			
		||||
					model.BucketLabel, b.GetUpperBound(),
 | 
			
		||||
					0, b.GetCumulativeCount(), true,
 | 
			
		||||
					b.Exemplar,
 | 
			
		||||
				)
 | 
			
		||||
				written += n
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				if math.IsInf(b.GetUpperBound(), +1) {
 | 
			
		||||
					infSeen = true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if !infSeen {
 | 
			
		||||
				n, err = writeOpenMetricsSample(
 | 
			
		||||
					w, name, "_bucket", metric,
 | 
			
		||||
					model.BucketLabel, math.Inf(+1),
 | 
			
		||||
					0, metric.Histogram.GetSampleCount(), true,
 | 
			
		||||
					nil,
 | 
			
		||||
				)
 | 
			
		||||
				written += n
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			n, err = writeOpenMetricsSample(
 | 
			
		||||
				w, name, "_sum", metric, "", 0,
 | 
			
		||||
				metric.Histogram.GetSampleSum(), 0, false,
 | 
			
		||||
				nil,
 | 
			
		||||
			)
 | 
			
		||||
			written += n
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			n, err = writeOpenMetricsSample(
 | 
			
		||||
				w, name, "_count", metric, "", 0,
 | 
			
		||||
				0, metric.Histogram.GetSampleCount(), true,
 | 
			
		||||
				nil,
 | 
			
		||||
			)
 | 
			
		||||
		default:
 | 
			
		||||
			return written, fmt.Errorf(
 | 
			
		||||
				"unexpected type in metric %s %s", name, metric,
 | 
			
		||||
			)
 | 
			
		||||
		}
 | 
			
		||||
		written += n
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FinalizeOpenMetrics writes the final `# EOF\n` line required by OpenMetrics.
 | 
			
		||||
func FinalizeOpenMetrics(w io.Writer) (written int, err error) {
 | 
			
		||||
	return w.Write([]byte("# EOF\n"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// writeOpenMetricsSample writes a single sample in OpenMetrics text format to
 | 
			
		||||
// w, given the metric name, the metric proto message itself, optionally an
 | 
			
		||||
// additional label name with a float64 value (use empty string as label name if
 | 
			
		||||
// not required), the value (optionally as float64 or uint64, determined by
 | 
			
		||||
// useIntValue), and optionally an exemplar (use nil if not required). The
 | 
			
		||||
// function returns the number of bytes written and any error encountered.
 | 
			
		||||
func writeOpenMetricsSample(
 | 
			
		||||
	w enhancedWriter,
 | 
			
		||||
	name, suffix string,
 | 
			
		||||
	metric *dto.Metric,
 | 
			
		||||
	additionalLabelName string, additionalLabelValue float64,
 | 
			
		||||
	floatValue float64, intValue uint64, useIntValue bool,
 | 
			
		||||
	exemplar *dto.Exemplar,
 | 
			
		||||
) (int, error) {
 | 
			
		||||
	var written int
 | 
			
		||||
	n, err := w.WriteString(name)
 | 
			
		||||
	written += n
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return written, err
 | 
			
		||||
	}
 | 
			
		||||
	if suffix != "" {
 | 
			
		||||
		n, err = w.WriteString(suffix)
 | 
			
		||||
		written += n
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	n, err = writeOpenMetricsLabelPairs(
 | 
			
		||||
		w, metric.Label, additionalLabelName, additionalLabelValue,
 | 
			
		||||
	)
 | 
			
		||||
	written += n
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return written, err
 | 
			
		||||
	}
 | 
			
		||||
	err = w.WriteByte(' ')
 | 
			
		||||
	written++
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return written, err
 | 
			
		||||
	}
 | 
			
		||||
	if useIntValue {
 | 
			
		||||
		n, err = writeUint(w, intValue)
 | 
			
		||||
	} else {
 | 
			
		||||
		n, err = writeOpenMetricsFloat(w, floatValue)
 | 
			
		||||
	}
 | 
			
		||||
	written += n
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return written, err
 | 
			
		||||
	}
 | 
			
		||||
	if metric.TimestampMs != nil {
 | 
			
		||||
		err = w.WriteByte(' ')
 | 
			
		||||
		written++
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
		// TODO(beorn7): Format this directly without converting to a float first.
 | 
			
		||||
		n, err = writeOpenMetricsFloat(w, float64(*metric.TimestampMs)/1000)
 | 
			
		||||
		written += n
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if exemplar != nil {
 | 
			
		||||
		n, err = writeExemplar(w, exemplar)
 | 
			
		||||
		written += n
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	err = w.WriteByte('\n')
 | 
			
		||||
	written++
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return written, err
 | 
			
		||||
	}
 | 
			
		||||
	return written, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// writeOpenMetricsLabelPairs works like writeOpenMetrics but formats the float
 | 
			
		||||
// in OpenMetrics style.
 | 
			
		||||
func writeOpenMetricsLabelPairs(
 | 
			
		||||
	w enhancedWriter,
 | 
			
		||||
	in []*dto.LabelPair,
 | 
			
		||||
	additionalLabelName string, additionalLabelValue float64,
 | 
			
		||||
) (int, error) {
 | 
			
		||||
	if len(in) == 0 && additionalLabelName == "" {
 | 
			
		||||
		return 0, nil
 | 
			
		||||
	}
 | 
			
		||||
	var (
 | 
			
		||||
		written   int
 | 
			
		||||
		separator byte = '{'
 | 
			
		||||
	)
 | 
			
		||||
	for _, lp := range in {
 | 
			
		||||
		err := w.WriteByte(separator)
 | 
			
		||||
		written++
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
		n, err := w.WriteString(lp.GetName())
 | 
			
		||||
		written += n
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
		n, err = w.WriteString(`="`)
 | 
			
		||||
		written += n
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
		n, err = writeEscapedString(w, lp.GetValue(), true)
 | 
			
		||||
		written += n
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
		err = w.WriteByte('"')
 | 
			
		||||
		written++
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
		separator = ','
 | 
			
		||||
	}
 | 
			
		||||
	if additionalLabelName != "" {
 | 
			
		||||
		err := w.WriteByte(separator)
 | 
			
		||||
		written++
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
		n, err := w.WriteString(additionalLabelName)
 | 
			
		||||
		written += n
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
		n, err = w.WriteString(`="`)
 | 
			
		||||
		written += n
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
		n, err = writeOpenMetricsFloat(w, additionalLabelValue)
 | 
			
		||||
		written += n
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
		err = w.WriteByte('"')
 | 
			
		||||
		written++
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	err := w.WriteByte('}')
 | 
			
		||||
	written++
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return written, err
 | 
			
		||||
	}
 | 
			
		||||
	return written, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// writeExemplar writes the provided exemplar in OpenMetrics format to w. The
 | 
			
		||||
// function returns the number of bytes written and any error encountered.
 | 
			
		||||
func writeExemplar(w enhancedWriter, e *dto.Exemplar) (int, error) {
 | 
			
		||||
	written := 0
 | 
			
		||||
	n, err := w.WriteString(" # ")
 | 
			
		||||
	written += n
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return written, err
 | 
			
		||||
	}
 | 
			
		||||
	n, err = writeOpenMetricsLabelPairs(w, e.Label, "", 0)
 | 
			
		||||
	written += n
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return written, err
 | 
			
		||||
	}
 | 
			
		||||
	err = w.WriteByte(' ')
 | 
			
		||||
	written++
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return written, err
 | 
			
		||||
	}
 | 
			
		||||
	n, err = writeOpenMetricsFloat(w, e.GetValue())
 | 
			
		||||
	written += n
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return written, err
 | 
			
		||||
	}
 | 
			
		||||
	if e.Timestamp != nil {
 | 
			
		||||
		err = w.WriteByte(' ')
 | 
			
		||||
		written++
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
		ts, err := ptypes.Timestamp((*e).Timestamp)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
		// TODO(beorn7): Format this directly from components of ts to
 | 
			
		||||
		// avoid overflow/underflow and precision issues of the float
 | 
			
		||||
		// conversion.
 | 
			
		||||
		n, err = writeOpenMetricsFloat(w, float64(ts.UnixNano())/1e9)
 | 
			
		||||
		written += n
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return written, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// writeOpenMetricsFloat works like writeFloat but appends ".0" if the resulting
 | 
			
		||||
// number would otherwise contain neither a "." nor an "e".
 | 
			
		||||
func writeOpenMetricsFloat(w enhancedWriter, f float64) (int, error) {
 | 
			
		||||
	switch {
 | 
			
		||||
	case f == 1:
 | 
			
		||||
		return w.WriteString("1.0")
 | 
			
		||||
	case f == 0:
 | 
			
		||||
		return w.WriteString("0.0")
 | 
			
		||||
	case f == -1:
 | 
			
		||||
		return w.WriteString("-1.0")
 | 
			
		||||
	case math.IsNaN(f):
 | 
			
		||||
		return w.WriteString("NaN")
 | 
			
		||||
	case math.IsInf(f, +1):
 | 
			
		||||
		return w.WriteString("+Inf")
 | 
			
		||||
	case math.IsInf(f, -1):
 | 
			
		||||
		return w.WriteString("-Inf")
 | 
			
		||||
	default:
 | 
			
		||||
		bp := numBufPool.Get().(*[]byte)
 | 
			
		||||
		*bp = strconv.AppendFloat((*bp)[:0], f, 'g', -1, 64)
 | 
			
		||||
		if !bytes.ContainsAny(*bp, "e.") {
 | 
			
		||||
			*bp = append(*bp, '.', '0')
 | 
			
		||||
		}
 | 
			
		||||
		written, err := w.Write(*bp)
 | 
			
		||||
		numBufPool.Put(bp)
 | 
			
		||||
		return written, err
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// writeUint is like writeInt just for uint64.
 | 
			
		||||
func writeUint(w enhancedWriter, u uint64) (int, error) {
 | 
			
		||||
	bp := numBufPool.Get().(*[]byte)
 | 
			
		||||
	*bp = strconv.AppendUint((*bp)[:0], u, 10)
 | 
			
		||||
	written, err := w.Write(*bp)
 | 
			
		||||
	numBufPool.Put(bp)
 | 
			
		||||
	return written, err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										465
									
								
								vendor/github.com/prometheus/common/expfmt/text_create.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										465
									
								
								vendor/github.com/prometheus/common/expfmt/text_create.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,465 @@
 | 
			
		||||
// Copyright 2014 The Prometheus Authors
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
// http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
package expfmt
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"math"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"github.com/prometheus/common/model"
 | 
			
		||||
 | 
			
		||||
	dto "github.com/prometheus/client_model/go"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// enhancedWriter has all the enhanced write functions needed here. bufio.Writer
 | 
			
		||||
// implements it.
 | 
			
		||||
type enhancedWriter interface {
 | 
			
		||||
	io.Writer
 | 
			
		||||
	WriteRune(r rune) (n int, err error)
 | 
			
		||||
	WriteString(s string) (n int, err error)
 | 
			
		||||
	WriteByte(c byte) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	initialNumBufSize = 24
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	bufPool = sync.Pool{
 | 
			
		||||
		New: func() interface{} {
 | 
			
		||||
			return bufio.NewWriter(ioutil.Discard)
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	numBufPool = sync.Pool{
 | 
			
		||||
		New: func() interface{} {
 | 
			
		||||
			b := make([]byte, 0, initialNumBufSize)
 | 
			
		||||
			return &b
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// MetricFamilyToText converts a MetricFamily proto message into text format and
 | 
			
		||||
// writes the resulting lines to 'out'. It returns the number of bytes written
 | 
			
		||||
// and any error encountered. The output will have the same order as the input,
 | 
			
		||||
// no further sorting is performed. Furthermore, this function assumes the input
 | 
			
		||||
// is already sanitized and does not perform any sanity checks. If the input
 | 
			
		||||
// contains duplicate metrics or invalid metric or label names, the conversion
 | 
			
		||||
// will result in invalid text format output.
 | 
			
		||||
//
 | 
			
		||||
// This method fulfills the type 'prometheus.encoder'.
 | 
			
		||||
func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (written int, err error) {
 | 
			
		||||
	// Fail-fast checks.
 | 
			
		||||
	if len(in.Metric) == 0 {
 | 
			
		||||
		return 0, fmt.Errorf("MetricFamily has no metrics: %s", in)
 | 
			
		||||
	}
 | 
			
		||||
	name := in.GetName()
 | 
			
		||||
	if name == "" {
 | 
			
		||||
		return 0, fmt.Errorf("MetricFamily has no name: %s", in)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Try the interface upgrade. If it doesn't work, we'll use a
 | 
			
		||||
	// bufio.Writer from the sync.Pool.
 | 
			
		||||
	w, ok := out.(enhancedWriter)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		b := bufPool.Get().(*bufio.Writer)
 | 
			
		||||
		b.Reset(out)
 | 
			
		||||
		w = b
 | 
			
		||||
		defer func() {
 | 
			
		||||
			bErr := b.Flush()
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				err = bErr
 | 
			
		||||
			}
 | 
			
		||||
			bufPool.Put(b)
 | 
			
		||||
		}()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var n int
 | 
			
		||||
 | 
			
		||||
	// Comments, first HELP, then TYPE.
 | 
			
		||||
	if in.Help != nil {
 | 
			
		||||
		n, err = w.WriteString("# HELP ")
 | 
			
		||||
		written += n
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		n, err = w.WriteString(name)
 | 
			
		||||
		written += n
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		err = w.WriteByte(' ')
 | 
			
		||||
		written++
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		n, err = writeEscapedString(w, *in.Help, false)
 | 
			
		||||
		written += n
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		err = w.WriteByte('\n')
 | 
			
		||||
		written++
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	n, err = w.WriteString("# TYPE ")
 | 
			
		||||
	written += n
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	n, err = w.WriteString(name)
 | 
			
		||||
	written += n
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	metricType := in.GetType()
 | 
			
		||||
	switch metricType {
 | 
			
		||||
	case dto.MetricType_COUNTER:
 | 
			
		||||
		n, err = w.WriteString(" counter\n")
 | 
			
		||||
	case dto.MetricType_GAUGE:
 | 
			
		||||
		n, err = w.WriteString(" gauge\n")
 | 
			
		||||
	case dto.MetricType_SUMMARY:
 | 
			
		||||
		n, err = w.WriteString(" summary\n")
 | 
			
		||||
	case dto.MetricType_UNTYPED:
 | 
			
		||||
		n, err = w.WriteString(" untyped\n")
 | 
			
		||||
	case dto.MetricType_HISTOGRAM:
 | 
			
		||||
		n, err = w.WriteString(" histogram\n")
 | 
			
		||||
	default:
 | 
			
		||||
		return written, fmt.Errorf("unknown metric type %s", metricType.String())
 | 
			
		||||
	}
 | 
			
		||||
	written += n
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Finally the samples, one line for each.
 | 
			
		||||
	for _, metric := range in.Metric {
 | 
			
		||||
		switch metricType {
 | 
			
		||||
		case dto.MetricType_COUNTER:
 | 
			
		||||
			if metric.Counter == nil {
 | 
			
		||||
				return written, fmt.Errorf(
 | 
			
		||||
					"expected counter in metric %s %s", name, metric,
 | 
			
		||||
				)
 | 
			
		||||
			}
 | 
			
		||||
			n, err = writeSample(
 | 
			
		||||
				w, name, "", metric, "", 0,
 | 
			
		||||
				metric.Counter.GetValue(),
 | 
			
		||||
			)
 | 
			
		||||
		case dto.MetricType_GAUGE:
 | 
			
		||||
			if metric.Gauge == nil {
 | 
			
		||||
				return written, fmt.Errorf(
 | 
			
		||||
					"expected gauge in metric %s %s", name, metric,
 | 
			
		||||
				)
 | 
			
		||||
			}
 | 
			
		||||
			n, err = writeSample(
 | 
			
		||||
				w, name, "", metric, "", 0,
 | 
			
		||||
				metric.Gauge.GetValue(),
 | 
			
		||||
			)
 | 
			
		||||
		case dto.MetricType_UNTYPED:
 | 
			
		||||
			if metric.Untyped == nil {
 | 
			
		||||
				return written, fmt.Errorf(
 | 
			
		||||
					"expected untyped in metric %s %s", name, metric,
 | 
			
		||||
				)
 | 
			
		||||
			}
 | 
			
		||||
			n, err = writeSample(
 | 
			
		||||
				w, name, "", metric, "", 0,
 | 
			
		||||
				metric.Untyped.GetValue(),
 | 
			
		||||
			)
 | 
			
		||||
		case dto.MetricType_SUMMARY:
 | 
			
		||||
			if metric.Summary == nil {
 | 
			
		||||
				return written, fmt.Errorf(
 | 
			
		||||
					"expected summary in metric %s %s", name, metric,
 | 
			
		||||
				)
 | 
			
		||||
			}
 | 
			
		||||
			for _, q := range metric.Summary.Quantile {
 | 
			
		||||
				n, err = writeSample(
 | 
			
		||||
					w, name, "", metric,
 | 
			
		||||
					model.QuantileLabel, q.GetQuantile(),
 | 
			
		||||
					q.GetValue(),
 | 
			
		||||
				)
 | 
			
		||||
				written += n
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			n, err = writeSample(
 | 
			
		||||
				w, name, "_sum", metric, "", 0,
 | 
			
		||||
				metric.Summary.GetSampleSum(),
 | 
			
		||||
			)
 | 
			
		||||
			written += n
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			n, err = writeSample(
 | 
			
		||||
				w, name, "_count", metric, "", 0,
 | 
			
		||||
				float64(metric.Summary.GetSampleCount()),
 | 
			
		||||
			)
 | 
			
		||||
		case dto.MetricType_HISTOGRAM:
 | 
			
		||||
			if metric.Histogram == nil {
 | 
			
		||||
				return written, fmt.Errorf(
 | 
			
		||||
					"expected histogram in metric %s %s", name, metric,
 | 
			
		||||
				)
 | 
			
		||||
			}
 | 
			
		||||
			infSeen := false
 | 
			
		||||
			for _, b := range metric.Histogram.Bucket {
 | 
			
		||||
				n, err = writeSample(
 | 
			
		||||
					w, name, "_bucket", metric,
 | 
			
		||||
					model.BucketLabel, b.GetUpperBound(),
 | 
			
		||||
					float64(b.GetCumulativeCount()),
 | 
			
		||||
				)
 | 
			
		||||
				written += n
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				if math.IsInf(b.GetUpperBound(), +1) {
 | 
			
		||||
					infSeen = true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if !infSeen {
 | 
			
		||||
				n, err = writeSample(
 | 
			
		||||
					w, name, "_bucket", metric,
 | 
			
		||||
					model.BucketLabel, math.Inf(+1),
 | 
			
		||||
					float64(metric.Histogram.GetSampleCount()),
 | 
			
		||||
				)
 | 
			
		||||
				written += n
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			n, err = writeSample(
 | 
			
		||||
				w, name, "_sum", metric, "", 0,
 | 
			
		||||
				metric.Histogram.GetSampleSum(),
 | 
			
		||||
			)
 | 
			
		||||
			written += n
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			n, err = writeSample(
 | 
			
		||||
				w, name, "_count", metric, "", 0,
 | 
			
		||||
				float64(metric.Histogram.GetSampleCount()),
 | 
			
		||||
			)
 | 
			
		||||
		default:
 | 
			
		||||
			return written, fmt.Errorf(
 | 
			
		||||
				"unexpected type in metric %s %s", name, metric,
 | 
			
		||||
			)
 | 
			
		||||
		}
 | 
			
		||||
		written += n
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// writeSample writes a single sample in text format to w, given the metric
 | 
			
		||||
// name, the metric proto message itself, optionally an additional label name
 | 
			
		||||
// with a float64 value (use empty string as label name if not required), and
 | 
			
		||||
// the value. The function returns the number of bytes written and any error
 | 
			
		||||
// encountered.
 | 
			
		||||
func writeSample(
 | 
			
		||||
	w enhancedWriter,
 | 
			
		||||
	name, suffix string,
 | 
			
		||||
	metric *dto.Metric,
 | 
			
		||||
	additionalLabelName string, additionalLabelValue float64,
 | 
			
		||||
	value float64,
 | 
			
		||||
) (int, error) {
 | 
			
		||||
	var written int
 | 
			
		||||
	n, err := w.WriteString(name)
 | 
			
		||||
	written += n
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return written, err
 | 
			
		||||
	}
 | 
			
		||||
	if suffix != "" {
 | 
			
		||||
		n, err = w.WriteString(suffix)
 | 
			
		||||
		written += n
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	n, err = writeLabelPairs(
 | 
			
		||||
		w, metric.Label, additionalLabelName, additionalLabelValue,
 | 
			
		||||
	)
 | 
			
		||||
	written += n
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return written, err
 | 
			
		||||
	}
 | 
			
		||||
	err = w.WriteByte(' ')
 | 
			
		||||
	written++
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return written, err
 | 
			
		||||
	}
 | 
			
		||||
	n, err = writeFloat(w, value)
 | 
			
		||||
	written += n
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return written, err
 | 
			
		||||
	}
 | 
			
		||||
	if metric.TimestampMs != nil {
 | 
			
		||||
		err = w.WriteByte(' ')
 | 
			
		||||
		written++
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
		n, err = writeInt(w, *metric.TimestampMs)
 | 
			
		||||
		written += n
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	err = w.WriteByte('\n')
 | 
			
		||||
	written++
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return written, err
 | 
			
		||||
	}
 | 
			
		||||
	return written, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// writeLabelPairs converts a slice of LabelPair proto messages plus the
 | 
			
		||||
// explicitly given additional label pair into text formatted as required by the
 | 
			
		||||
// text format and writes it to 'w'. An empty slice in combination with an empty
 | 
			
		||||
// string 'additionalLabelName' results in nothing being written. Otherwise, the
 | 
			
		||||
// label pairs are written, escaped as required by the text format, and enclosed
 | 
			
		||||
// in '{...}'. The function returns the number of bytes written and any error
 | 
			
		||||
// encountered.
 | 
			
		||||
func writeLabelPairs(
 | 
			
		||||
	w enhancedWriter,
 | 
			
		||||
	in []*dto.LabelPair,
 | 
			
		||||
	additionalLabelName string, additionalLabelValue float64,
 | 
			
		||||
) (int, error) {
 | 
			
		||||
	if len(in) == 0 && additionalLabelName == "" {
 | 
			
		||||
		return 0, nil
 | 
			
		||||
	}
 | 
			
		||||
	var (
 | 
			
		||||
		written   int
 | 
			
		||||
		separator byte = '{'
 | 
			
		||||
	)
 | 
			
		||||
	for _, lp := range in {
 | 
			
		||||
		err := w.WriteByte(separator)
 | 
			
		||||
		written++
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
		n, err := w.WriteString(lp.GetName())
 | 
			
		||||
		written += n
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
		n, err = w.WriteString(`="`)
 | 
			
		||||
		written += n
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
		n, err = writeEscapedString(w, lp.GetValue(), true)
 | 
			
		||||
		written += n
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
		err = w.WriteByte('"')
 | 
			
		||||
		written++
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
		separator = ','
 | 
			
		||||
	}
 | 
			
		||||
	if additionalLabelName != "" {
 | 
			
		||||
		err := w.WriteByte(separator)
 | 
			
		||||
		written++
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
		n, err := w.WriteString(additionalLabelName)
 | 
			
		||||
		written += n
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
		n, err = w.WriteString(`="`)
 | 
			
		||||
		written += n
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
		n, err = writeFloat(w, additionalLabelValue)
 | 
			
		||||
		written += n
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
		err = w.WriteByte('"')
 | 
			
		||||
		written++
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return written, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	err := w.WriteByte('}')
 | 
			
		||||
	written++
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return written, err
 | 
			
		||||
	}
 | 
			
		||||
	return written, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// writeEscapedString replaces '\' by '\\', new line character by '\n', and - if
 | 
			
		||||
// includeDoubleQuote is true - '"' by '\"'.
 | 
			
		||||
var (
 | 
			
		||||
	escaper       = strings.NewReplacer("\\", `\\`, "\n", `\n`)
 | 
			
		||||
	quotedEscaper = strings.NewReplacer("\\", `\\`, "\n", `\n`, "\"", `\"`)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func writeEscapedString(w enhancedWriter, v string, includeDoubleQuote bool) (int, error) {
 | 
			
		||||
	if includeDoubleQuote {
 | 
			
		||||
		return quotedEscaper.WriteString(w, v)
 | 
			
		||||
	}
 | 
			
		||||
	return escaper.WriteString(w, v)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// writeFloat is equivalent to fmt.Fprint with a float64 argument but hardcodes
 | 
			
		||||
// a few common cases for increased efficiency. For non-hardcoded cases, it uses
 | 
			
		||||
// strconv.AppendFloat to avoid allocations, similar to writeInt.
 | 
			
		||||
func writeFloat(w enhancedWriter, f float64) (int, error) {
 | 
			
		||||
	switch {
 | 
			
		||||
	case f == 1:
 | 
			
		||||
		return 1, w.WriteByte('1')
 | 
			
		||||
	case f == 0:
 | 
			
		||||
		return 1, w.WriteByte('0')
 | 
			
		||||
	case f == -1:
 | 
			
		||||
		return w.WriteString("-1")
 | 
			
		||||
	case math.IsNaN(f):
 | 
			
		||||
		return w.WriteString("NaN")
 | 
			
		||||
	case math.IsInf(f, +1):
 | 
			
		||||
		return w.WriteString("+Inf")
 | 
			
		||||
	case math.IsInf(f, -1):
 | 
			
		||||
		return w.WriteString("-Inf")
 | 
			
		||||
	default:
 | 
			
		||||
		bp := numBufPool.Get().(*[]byte)
 | 
			
		||||
		*bp = strconv.AppendFloat((*bp)[:0], f, 'g', -1, 64)
 | 
			
		||||
		written, err := w.Write(*bp)
 | 
			
		||||
		numBufPool.Put(bp)
 | 
			
		||||
		return written, err
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// writeInt is equivalent to fmt.Fprint with an int64 argument but uses
 | 
			
		||||
// strconv.AppendInt with a byte slice taken from a sync.Pool to avoid
 | 
			
		||||
// allocations.
 | 
			
		||||
func writeInt(w enhancedWriter, i int64) (int, error) {
 | 
			
		||||
	bp := numBufPool.Get().(*[]byte)
 | 
			
		||||
	*bp = strconv.AppendInt((*bp)[:0], i, 10)
 | 
			
		||||
	written, err := w.Write(*bp)
 | 
			
		||||
	numBufPool.Put(bp)
 | 
			
		||||
	return written, err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										764
									
								
								vendor/github.com/prometheus/common/expfmt/text_parse.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										764
									
								
								vendor/github.com/prometheus/common/expfmt/text_parse.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,764 @@
 | 
			
		||||
// Copyright 2014 The Prometheus Authors
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
// http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
package expfmt
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"math"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	dto "github.com/prometheus/client_model/go"
 | 
			
		||||
 | 
			
		||||
	"github.com/golang/protobuf/proto"
 | 
			
		||||
	"github.com/prometheus/common/model"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// A stateFn is a function that represents a state in a state machine. By
 | 
			
		||||
// executing it, the state is progressed to the next state. The stateFn returns
 | 
			
		||||
// another stateFn, which represents the new state. The end state is represented
 | 
			
		||||
// by nil.
 | 
			
		||||
type stateFn func() stateFn
 | 
			
		||||
 | 
			
		||||
// ParseError signals errors while parsing the simple and flat text-based
 | 
			
		||||
// exchange format.
 | 
			
		||||
type ParseError struct {
 | 
			
		||||
	Line int
 | 
			
		||||
	Msg  string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Error implements the error interface.
 | 
			
		||||
func (e ParseError) Error() string {
 | 
			
		||||
	return fmt.Sprintf("text format parsing error in line %d: %s", e.Line, e.Msg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TextParser is used to parse the simple and flat text-based exchange format. Its
 | 
			
		||||
// zero value is ready to use.
 | 
			
		||||
type TextParser struct {
 | 
			
		||||
	metricFamiliesByName map[string]*dto.MetricFamily
 | 
			
		||||
	buf                  *bufio.Reader // Where the parsed input is read through.
 | 
			
		||||
	err                  error         // Most recent error.
 | 
			
		||||
	lineCount            int           // Tracks the line count for error messages.
 | 
			
		||||
	currentByte          byte          // The most recent byte read.
 | 
			
		||||
	currentToken         bytes.Buffer  // Re-used each time a token has to be gathered from multiple bytes.
 | 
			
		||||
	currentMF            *dto.MetricFamily
 | 
			
		||||
	currentMetric        *dto.Metric
 | 
			
		||||
	currentLabelPair     *dto.LabelPair
 | 
			
		||||
 | 
			
		||||
	// The remaining member variables are only used for summaries/histograms.
 | 
			
		||||
	currentLabels map[string]string // All labels including '__name__' but excluding 'quantile'/'le'
 | 
			
		||||
	// Summary specific.
 | 
			
		||||
	summaries       map[uint64]*dto.Metric // Key is created with LabelsToSignature.
 | 
			
		||||
	currentQuantile float64
 | 
			
		||||
	// Histogram specific.
 | 
			
		||||
	histograms    map[uint64]*dto.Metric // Key is created with LabelsToSignature.
 | 
			
		||||
	currentBucket float64
 | 
			
		||||
	// These tell us if the currently processed line ends on '_count' or
 | 
			
		||||
	// '_sum' respectively and belong to a summary/histogram, representing the sample
 | 
			
		||||
	// count and sum of that summary/histogram.
 | 
			
		||||
	currentIsSummaryCount, currentIsSummarySum     bool
 | 
			
		||||
	currentIsHistogramCount, currentIsHistogramSum bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TextToMetricFamilies reads 'in' as the simple and flat text-based exchange
 | 
			
		||||
// format and creates MetricFamily proto messages. It returns the MetricFamily
 | 
			
		||||
// proto messages in a map where the metric names are the keys, along with any
 | 
			
		||||
// error encountered.
 | 
			
		||||
//
 | 
			
		||||
// If the input contains duplicate metrics (i.e. lines with the same metric name
 | 
			
		||||
// and exactly the same label set), the resulting MetricFamily will contain
 | 
			
		||||
// duplicate Metric proto messages. Similar is true for duplicate label
 | 
			
		||||
// names. Checks for duplicates have to be performed separately, if required.
 | 
			
		||||
// Also note that neither the metrics within each MetricFamily are sorted nor
 | 
			
		||||
// the label pairs within each Metric. Sorting is not required for the most
 | 
			
		||||
// frequent use of this method, which is sample ingestion in the Prometheus
 | 
			
		||||
// server. However, for presentation purposes, you might want to sort the
 | 
			
		||||
// metrics, and in some cases, you must sort the labels, e.g. for consumption by
 | 
			
		||||
// the metric family injection hook of the Prometheus registry.
 | 
			
		||||
//
 | 
			
		||||
// Summaries and histograms are rather special beasts. You would probably not
 | 
			
		||||
// use them in the simple text format anyway. This method can deal with
 | 
			
		||||
// summaries and histograms if they are presented in exactly the way the
 | 
			
		||||
// text.Create function creates them.
 | 
			
		||||
//
 | 
			
		||||
// This method must not be called concurrently. If you want to parse different
 | 
			
		||||
// input concurrently, instantiate a separate Parser for each goroutine.
 | 
			
		||||
func (p *TextParser) TextToMetricFamilies(in io.Reader) (map[string]*dto.MetricFamily, error) {
 | 
			
		||||
	p.reset(in)
 | 
			
		||||
	for nextState := p.startOfLine; nextState != nil; nextState = nextState() {
 | 
			
		||||
		// Magic happens here...
 | 
			
		||||
	}
 | 
			
		||||
	// Get rid of empty metric families.
 | 
			
		||||
	for k, mf := range p.metricFamiliesByName {
 | 
			
		||||
		if len(mf.GetMetric()) == 0 {
 | 
			
		||||
			delete(p.metricFamiliesByName, k)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// If p.err is io.EOF now, we have run into a premature end of the input
 | 
			
		||||
	// stream. Turn this error into something nicer and more
 | 
			
		||||
	// meaningful. (io.EOF is often used as a signal for the legitimate end
 | 
			
		||||
	// of an input stream.)
 | 
			
		||||
	if p.err == io.EOF {
 | 
			
		||||
		p.parseError("unexpected end of input stream")
 | 
			
		||||
	}
 | 
			
		||||
	return p.metricFamiliesByName, p.err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *TextParser) reset(in io.Reader) {
 | 
			
		||||
	p.metricFamiliesByName = map[string]*dto.MetricFamily{}
 | 
			
		||||
	if p.buf == nil {
 | 
			
		||||
		p.buf = bufio.NewReader(in)
 | 
			
		||||
	} else {
 | 
			
		||||
		p.buf.Reset(in)
 | 
			
		||||
	}
 | 
			
		||||
	p.err = nil
 | 
			
		||||
	p.lineCount = 0
 | 
			
		||||
	if p.summaries == nil || len(p.summaries) > 0 {
 | 
			
		||||
		p.summaries = map[uint64]*dto.Metric{}
 | 
			
		||||
	}
 | 
			
		||||
	if p.histograms == nil || len(p.histograms) > 0 {
 | 
			
		||||
		p.histograms = map[uint64]*dto.Metric{}
 | 
			
		||||
	}
 | 
			
		||||
	p.currentQuantile = math.NaN()
 | 
			
		||||
	p.currentBucket = math.NaN()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// startOfLine represents the state where the next byte read from p.buf is the
 | 
			
		||||
// start of a line (or whitespace leading up to it).
 | 
			
		||||
func (p *TextParser) startOfLine() stateFn {
 | 
			
		||||
	p.lineCount++
 | 
			
		||||
	if p.skipBlankTab(); p.err != nil {
 | 
			
		||||
		// End of input reached. This is the only case where
 | 
			
		||||
		// that is not an error but a signal that we are done.
 | 
			
		||||
		p.err = nil
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	switch p.currentByte {
 | 
			
		||||
	case '#':
 | 
			
		||||
		return p.startComment
 | 
			
		||||
	case '\n':
 | 
			
		||||
		return p.startOfLine // Empty line, start the next one.
 | 
			
		||||
	}
 | 
			
		||||
	return p.readingMetricName
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// startComment represents the state where the next byte read from p.buf is the
 | 
			
		||||
// start of a comment (or whitespace leading up to it).
 | 
			
		||||
func (p *TextParser) startComment() stateFn {
 | 
			
		||||
	if p.skipBlankTab(); p.err != nil {
 | 
			
		||||
		return nil // Unexpected end of input.
 | 
			
		||||
	}
 | 
			
		||||
	if p.currentByte == '\n' {
 | 
			
		||||
		return p.startOfLine
 | 
			
		||||
	}
 | 
			
		||||
	if p.readTokenUntilWhitespace(); p.err != nil {
 | 
			
		||||
		return nil // Unexpected end of input.
 | 
			
		||||
	}
 | 
			
		||||
	// If we have hit the end of line already, there is nothing left
 | 
			
		||||
	// to do. This is not considered a syntax error.
 | 
			
		||||
	if p.currentByte == '\n' {
 | 
			
		||||
		return p.startOfLine
 | 
			
		||||
	}
 | 
			
		||||
	keyword := p.currentToken.String()
 | 
			
		||||
	if keyword != "HELP" && keyword != "TYPE" {
 | 
			
		||||
		// Generic comment, ignore by fast forwarding to end of line.
 | 
			
		||||
		for p.currentByte != '\n' {
 | 
			
		||||
			if p.currentByte, p.err = p.buf.ReadByte(); p.err != nil {
 | 
			
		||||
				return nil // Unexpected end of input.
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return p.startOfLine
 | 
			
		||||
	}
 | 
			
		||||
	// There is something. Next has to be a metric name.
 | 
			
		||||
	if p.skipBlankTab(); p.err != nil {
 | 
			
		||||
		return nil // Unexpected end of input.
 | 
			
		||||
	}
 | 
			
		||||
	if p.readTokenAsMetricName(); p.err != nil {
 | 
			
		||||
		return nil // Unexpected end of input.
 | 
			
		||||
	}
 | 
			
		||||
	if p.currentByte == '\n' {
 | 
			
		||||
		// At the end of the line already.
 | 
			
		||||
		// Again, this is not considered a syntax error.
 | 
			
		||||
		return p.startOfLine
 | 
			
		||||
	}
 | 
			
		||||
	if !isBlankOrTab(p.currentByte) {
 | 
			
		||||
		p.parseError("invalid metric name in comment")
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	p.setOrCreateCurrentMF()
 | 
			
		||||
	if p.skipBlankTab(); p.err != nil {
 | 
			
		||||
		return nil // Unexpected end of input.
 | 
			
		||||
	}
 | 
			
		||||
	if p.currentByte == '\n' {
 | 
			
		||||
		// At the end of the line already.
 | 
			
		||||
		// Again, this is not considered a syntax error.
 | 
			
		||||
		return p.startOfLine
 | 
			
		||||
	}
 | 
			
		||||
	switch keyword {
 | 
			
		||||
	case "HELP":
 | 
			
		||||
		return p.readingHelp
 | 
			
		||||
	case "TYPE":
 | 
			
		||||
		return p.readingType
 | 
			
		||||
	}
 | 
			
		||||
	panic(fmt.Sprintf("code error: unexpected keyword %q", keyword))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readingMetricName represents the state where the last byte read (now in
 | 
			
		||||
// p.currentByte) is the first byte of a metric name.
 | 
			
		||||
func (p *TextParser) readingMetricName() stateFn {
 | 
			
		||||
	if p.readTokenAsMetricName(); p.err != nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if p.currentToken.Len() == 0 {
 | 
			
		||||
		p.parseError("invalid metric name")
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	p.setOrCreateCurrentMF()
 | 
			
		||||
	// Now is the time to fix the type if it hasn't happened yet.
 | 
			
		||||
	if p.currentMF.Type == nil {
 | 
			
		||||
		p.currentMF.Type = dto.MetricType_UNTYPED.Enum()
 | 
			
		||||
	}
 | 
			
		||||
	p.currentMetric = &dto.Metric{}
 | 
			
		||||
	// Do not append the newly created currentMetric to
 | 
			
		||||
	// currentMF.Metric right now. First wait if this is a summary,
 | 
			
		||||
	// and the metric exists already, which we can only know after
 | 
			
		||||
	// having read all the labels.
 | 
			
		||||
	if p.skipBlankTabIfCurrentBlankTab(); p.err != nil {
 | 
			
		||||
		return nil // Unexpected end of input.
 | 
			
		||||
	}
 | 
			
		||||
	return p.readingLabels
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readingLabels represents the state where the last byte read (now in
 | 
			
		||||
// p.currentByte) is either the first byte of the label set (i.e. a '{'), or the
 | 
			
		||||
// first byte of the value (otherwise).
 | 
			
		||||
func (p *TextParser) readingLabels() stateFn {
 | 
			
		||||
	// Summaries/histograms are special. We have to reset the
 | 
			
		||||
	// currentLabels map, currentQuantile and currentBucket before starting to
 | 
			
		||||
	// read labels.
 | 
			
		||||
	if p.currentMF.GetType() == dto.MetricType_SUMMARY || p.currentMF.GetType() == dto.MetricType_HISTOGRAM {
 | 
			
		||||
		p.currentLabels = map[string]string{}
 | 
			
		||||
		p.currentLabels[string(model.MetricNameLabel)] = p.currentMF.GetName()
 | 
			
		||||
		p.currentQuantile = math.NaN()
 | 
			
		||||
		p.currentBucket = math.NaN()
 | 
			
		||||
	}
 | 
			
		||||
	if p.currentByte != '{' {
 | 
			
		||||
		return p.readingValue
 | 
			
		||||
	}
 | 
			
		||||
	return p.startLabelName
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// startLabelName represents the state where the next byte read from p.buf is
 | 
			
		||||
// the start of a label name (or whitespace leading up to it).
 | 
			
		||||
func (p *TextParser) startLabelName() stateFn {
 | 
			
		||||
	if p.skipBlankTab(); p.err != nil {
 | 
			
		||||
		return nil // Unexpected end of input.
 | 
			
		||||
	}
 | 
			
		||||
	if p.currentByte == '}' {
 | 
			
		||||
		if p.skipBlankTab(); p.err != nil {
 | 
			
		||||
			return nil // Unexpected end of input.
 | 
			
		||||
		}
 | 
			
		||||
		return p.readingValue
 | 
			
		||||
	}
 | 
			
		||||
	if p.readTokenAsLabelName(); p.err != nil {
 | 
			
		||||
		return nil // Unexpected end of input.
 | 
			
		||||
	}
 | 
			
		||||
	if p.currentToken.Len() == 0 {
 | 
			
		||||
		p.parseError(fmt.Sprintf("invalid label name for metric %q", p.currentMF.GetName()))
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	p.currentLabelPair = &dto.LabelPair{Name: proto.String(p.currentToken.String())}
 | 
			
		||||
	if p.currentLabelPair.GetName() == string(model.MetricNameLabel) {
 | 
			
		||||
		p.parseError(fmt.Sprintf("label name %q is reserved", model.MetricNameLabel))
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	// Special summary/histogram treatment. Don't add 'quantile' and 'le'
 | 
			
		||||
	// labels to 'real' labels.
 | 
			
		||||
	if !(p.currentMF.GetType() == dto.MetricType_SUMMARY && p.currentLabelPair.GetName() == model.QuantileLabel) &&
 | 
			
		||||
		!(p.currentMF.GetType() == dto.MetricType_HISTOGRAM && p.currentLabelPair.GetName() == model.BucketLabel) {
 | 
			
		||||
		p.currentMetric.Label = append(p.currentMetric.Label, p.currentLabelPair)
 | 
			
		||||
	}
 | 
			
		||||
	if p.skipBlankTabIfCurrentBlankTab(); p.err != nil {
 | 
			
		||||
		return nil // Unexpected end of input.
 | 
			
		||||
	}
 | 
			
		||||
	if p.currentByte != '=' {
 | 
			
		||||
		p.parseError(fmt.Sprintf("expected '=' after label name, found %q", p.currentByte))
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return p.startLabelValue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// startLabelValue represents the state where the next byte read from p.buf is
 | 
			
		||||
// the start of a (quoted) label value (or whitespace leading up to it).
 | 
			
		||||
func (p *TextParser) startLabelValue() stateFn {
 | 
			
		||||
	if p.skipBlankTab(); p.err != nil {
 | 
			
		||||
		return nil // Unexpected end of input.
 | 
			
		||||
	}
 | 
			
		||||
	if p.currentByte != '"' {
 | 
			
		||||
		p.parseError(fmt.Sprintf("expected '\"' at start of label value, found %q", p.currentByte))
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if p.readTokenAsLabelValue(); p.err != nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if !model.LabelValue(p.currentToken.String()).IsValid() {
 | 
			
		||||
		p.parseError(fmt.Sprintf("invalid label value %q", p.currentToken.String()))
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	p.currentLabelPair.Value = proto.String(p.currentToken.String())
 | 
			
		||||
	// Special treatment of summaries:
 | 
			
		||||
	// - Quantile labels are special, will result in dto.Quantile later.
 | 
			
		||||
	// - Other labels have to be added to currentLabels for signature calculation.
 | 
			
		||||
	if p.currentMF.GetType() == dto.MetricType_SUMMARY {
 | 
			
		||||
		if p.currentLabelPair.GetName() == model.QuantileLabel {
 | 
			
		||||
			if p.currentQuantile, p.err = parseFloat(p.currentLabelPair.GetValue()); p.err != nil {
 | 
			
		||||
				// Create a more helpful error message.
 | 
			
		||||
				p.parseError(fmt.Sprintf("expected float as value for 'quantile' label, got %q", p.currentLabelPair.GetValue()))
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			p.currentLabels[p.currentLabelPair.GetName()] = p.currentLabelPair.GetValue()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// Similar special treatment of histograms.
 | 
			
		||||
	if p.currentMF.GetType() == dto.MetricType_HISTOGRAM {
 | 
			
		||||
		if p.currentLabelPair.GetName() == model.BucketLabel {
 | 
			
		||||
			if p.currentBucket, p.err = parseFloat(p.currentLabelPair.GetValue()); p.err != nil {
 | 
			
		||||
				// Create a more helpful error message.
 | 
			
		||||
				p.parseError(fmt.Sprintf("expected float as value for 'le' label, got %q", p.currentLabelPair.GetValue()))
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			p.currentLabels[p.currentLabelPair.GetName()] = p.currentLabelPair.GetValue()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if p.skipBlankTab(); p.err != nil {
 | 
			
		||||
		return nil // Unexpected end of input.
 | 
			
		||||
	}
 | 
			
		||||
	switch p.currentByte {
 | 
			
		||||
	case ',':
 | 
			
		||||
		return p.startLabelName
 | 
			
		||||
 | 
			
		||||
	case '}':
 | 
			
		||||
		if p.skipBlankTab(); p.err != nil {
 | 
			
		||||
			return nil // Unexpected end of input.
 | 
			
		||||
		}
 | 
			
		||||
		return p.readingValue
 | 
			
		||||
	default:
 | 
			
		||||
		p.parseError(fmt.Sprintf("unexpected end of label value %q", p.currentLabelPair.GetValue()))
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readingValue represents the state where the last byte read (now in
 | 
			
		||||
// p.currentByte) is the first byte of the sample value (i.e. a float).
 | 
			
		||||
func (p *TextParser) readingValue() stateFn {
 | 
			
		||||
	// When we are here, we have read all the labels, so for the
 | 
			
		||||
	// special case of a summary/histogram, we can finally find out
 | 
			
		||||
	// if the metric already exists.
 | 
			
		||||
	if p.currentMF.GetType() == dto.MetricType_SUMMARY {
 | 
			
		||||
		signature := model.LabelsToSignature(p.currentLabels)
 | 
			
		||||
		if summary := p.summaries[signature]; summary != nil {
 | 
			
		||||
			p.currentMetric = summary
 | 
			
		||||
		} else {
 | 
			
		||||
			p.summaries[signature] = p.currentMetric
 | 
			
		||||
			p.currentMF.Metric = append(p.currentMF.Metric, p.currentMetric)
 | 
			
		||||
		}
 | 
			
		||||
	} else if p.currentMF.GetType() == dto.MetricType_HISTOGRAM {
 | 
			
		||||
		signature := model.LabelsToSignature(p.currentLabels)
 | 
			
		||||
		if histogram := p.histograms[signature]; histogram != nil {
 | 
			
		||||
			p.currentMetric = histogram
 | 
			
		||||
		} else {
 | 
			
		||||
			p.histograms[signature] = p.currentMetric
 | 
			
		||||
			p.currentMF.Metric = append(p.currentMF.Metric, p.currentMetric)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		p.currentMF.Metric = append(p.currentMF.Metric, p.currentMetric)
 | 
			
		||||
	}
 | 
			
		||||
	if p.readTokenUntilWhitespace(); p.err != nil {
 | 
			
		||||
		return nil // Unexpected end of input.
 | 
			
		||||
	}
 | 
			
		||||
	value, err := parseFloat(p.currentToken.String())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// Create a more helpful error message.
 | 
			
		||||
		p.parseError(fmt.Sprintf("expected float as value, got %q", p.currentToken.String()))
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	switch p.currentMF.GetType() {
 | 
			
		||||
	case dto.MetricType_COUNTER:
 | 
			
		||||
		p.currentMetric.Counter = &dto.Counter{Value: proto.Float64(value)}
 | 
			
		||||
	case dto.MetricType_GAUGE:
 | 
			
		||||
		p.currentMetric.Gauge = &dto.Gauge{Value: proto.Float64(value)}
 | 
			
		||||
	case dto.MetricType_UNTYPED:
 | 
			
		||||
		p.currentMetric.Untyped = &dto.Untyped{Value: proto.Float64(value)}
 | 
			
		||||
	case dto.MetricType_SUMMARY:
 | 
			
		||||
		// *sigh*
 | 
			
		||||
		if p.currentMetric.Summary == nil {
 | 
			
		||||
			p.currentMetric.Summary = &dto.Summary{}
 | 
			
		||||
		}
 | 
			
		||||
		switch {
 | 
			
		||||
		case p.currentIsSummaryCount:
 | 
			
		||||
			p.currentMetric.Summary.SampleCount = proto.Uint64(uint64(value))
 | 
			
		||||
		case p.currentIsSummarySum:
 | 
			
		||||
			p.currentMetric.Summary.SampleSum = proto.Float64(value)
 | 
			
		||||
		case !math.IsNaN(p.currentQuantile):
 | 
			
		||||
			p.currentMetric.Summary.Quantile = append(
 | 
			
		||||
				p.currentMetric.Summary.Quantile,
 | 
			
		||||
				&dto.Quantile{
 | 
			
		||||
					Quantile: proto.Float64(p.currentQuantile),
 | 
			
		||||
					Value:    proto.Float64(value),
 | 
			
		||||
				},
 | 
			
		||||
			)
 | 
			
		||||
		}
 | 
			
		||||
	case dto.MetricType_HISTOGRAM:
 | 
			
		||||
		// *sigh*
 | 
			
		||||
		if p.currentMetric.Histogram == nil {
 | 
			
		||||
			p.currentMetric.Histogram = &dto.Histogram{}
 | 
			
		||||
		}
 | 
			
		||||
		switch {
 | 
			
		||||
		case p.currentIsHistogramCount:
 | 
			
		||||
			p.currentMetric.Histogram.SampleCount = proto.Uint64(uint64(value))
 | 
			
		||||
		case p.currentIsHistogramSum:
 | 
			
		||||
			p.currentMetric.Histogram.SampleSum = proto.Float64(value)
 | 
			
		||||
		case !math.IsNaN(p.currentBucket):
 | 
			
		||||
			p.currentMetric.Histogram.Bucket = append(
 | 
			
		||||
				p.currentMetric.Histogram.Bucket,
 | 
			
		||||
				&dto.Bucket{
 | 
			
		||||
					UpperBound:      proto.Float64(p.currentBucket),
 | 
			
		||||
					CumulativeCount: proto.Uint64(uint64(value)),
 | 
			
		||||
				},
 | 
			
		||||
			)
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		p.err = fmt.Errorf("unexpected type for metric name %q", p.currentMF.GetName())
 | 
			
		||||
	}
 | 
			
		||||
	if p.currentByte == '\n' {
 | 
			
		||||
		return p.startOfLine
 | 
			
		||||
	}
 | 
			
		||||
	return p.startTimestamp
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// startTimestamp represents the state where the next byte read from p.buf is
 | 
			
		||||
// the start of the timestamp (or whitespace leading up to it).
 | 
			
		||||
func (p *TextParser) startTimestamp() stateFn {
 | 
			
		||||
	if p.skipBlankTab(); p.err != nil {
 | 
			
		||||
		return nil // Unexpected end of input.
 | 
			
		||||
	}
 | 
			
		||||
	if p.readTokenUntilWhitespace(); p.err != nil {
 | 
			
		||||
		return nil // Unexpected end of input.
 | 
			
		||||
	}
 | 
			
		||||
	timestamp, err := strconv.ParseInt(p.currentToken.String(), 10, 64)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// Create a more helpful error message.
 | 
			
		||||
		p.parseError(fmt.Sprintf("expected integer as timestamp, got %q", p.currentToken.String()))
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	p.currentMetric.TimestampMs = proto.Int64(timestamp)
 | 
			
		||||
	if p.readTokenUntilNewline(false); p.err != nil {
 | 
			
		||||
		return nil // Unexpected end of input.
 | 
			
		||||
	}
 | 
			
		||||
	if p.currentToken.Len() > 0 {
 | 
			
		||||
		p.parseError(fmt.Sprintf("spurious string after timestamp: %q", p.currentToken.String()))
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return p.startOfLine
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readingHelp represents the state where the last byte read (now in
 | 
			
		||||
// p.currentByte) is the first byte of the docstring after 'HELP'.
 | 
			
		||||
func (p *TextParser) readingHelp() stateFn {
 | 
			
		||||
	if p.currentMF.Help != nil {
 | 
			
		||||
		p.parseError(fmt.Sprintf("second HELP line for metric name %q", p.currentMF.GetName()))
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	// Rest of line is the docstring.
 | 
			
		||||
	if p.readTokenUntilNewline(true); p.err != nil {
 | 
			
		||||
		return nil // Unexpected end of input.
 | 
			
		||||
	}
 | 
			
		||||
	p.currentMF.Help = proto.String(p.currentToken.String())
 | 
			
		||||
	return p.startOfLine
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readingType represents the state where the last byte read (now in
 | 
			
		||||
// p.currentByte) is the first byte of the type hint after 'HELP'.
 | 
			
		||||
func (p *TextParser) readingType() stateFn {
 | 
			
		||||
	if p.currentMF.Type != nil {
 | 
			
		||||
		p.parseError(fmt.Sprintf("second TYPE line for metric name %q, or TYPE reported after samples", p.currentMF.GetName()))
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	// Rest of line is the type.
 | 
			
		||||
	if p.readTokenUntilNewline(false); p.err != nil {
 | 
			
		||||
		return nil // Unexpected end of input.
 | 
			
		||||
	}
 | 
			
		||||
	metricType, ok := dto.MetricType_value[strings.ToUpper(p.currentToken.String())]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		p.parseError(fmt.Sprintf("unknown metric type %q", p.currentToken.String()))
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	p.currentMF.Type = dto.MetricType(metricType).Enum()
 | 
			
		||||
	return p.startOfLine
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// parseError sets p.err to a ParseError at the current line with the given
 | 
			
		||||
// message.
 | 
			
		||||
func (p *TextParser) parseError(msg string) {
 | 
			
		||||
	p.err = ParseError{
 | 
			
		||||
		Line: p.lineCount,
 | 
			
		||||
		Msg:  msg,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// skipBlankTab reads (and discards) bytes from p.buf until it encounters a byte
 | 
			
		||||
// that is neither ' ' nor '\t'. That byte is left in p.currentByte.
 | 
			
		||||
func (p *TextParser) skipBlankTab() {
 | 
			
		||||
	for {
 | 
			
		||||
		if p.currentByte, p.err = p.buf.ReadByte(); p.err != nil || !isBlankOrTab(p.currentByte) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// skipBlankTabIfCurrentBlankTab works exactly as skipBlankTab but doesn't do
 | 
			
		||||
// anything if p.currentByte is neither ' ' nor '\t'.
 | 
			
		||||
func (p *TextParser) skipBlankTabIfCurrentBlankTab() {
 | 
			
		||||
	if isBlankOrTab(p.currentByte) {
 | 
			
		||||
		p.skipBlankTab()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readTokenUntilWhitespace copies bytes from p.buf into p.currentToken.  The
 | 
			
		||||
// first byte considered is the byte already read (now in p.currentByte).  The
 | 
			
		||||
// first whitespace byte encountered is still copied into p.currentByte, but not
 | 
			
		||||
// into p.currentToken.
 | 
			
		||||
func (p *TextParser) readTokenUntilWhitespace() {
 | 
			
		||||
	p.currentToken.Reset()
 | 
			
		||||
	for p.err == nil && !isBlankOrTab(p.currentByte) && p.currentByte != '\n' {
 | 
			
		||||
		p.currentToken.WriteByte(p.currentByte)
 | 
			
		||||
		p.currentByte, p.err = p.buf.ReadByte()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readTokenUntilNewline copies bytes from p.buf into p.currentToken.  The first
 | 
			
		||||
// byte considered is the byte already read (now in p.currentByte).  The first
 | 
			
		||||
// newline byte encountered is still copied into p.currentByte, but not into
 | 
			
		||||
// p.currentToken. If recognizeEscapeSequence is true, two escape sequences are
 | 
			
		||||
// recognized: '\\' translates into '\', and '\n' into a line-feed character.
 | 
			
		||||
// All other escape sequences are invalid and cause an error.
 | 
			
		||||
func (p *TextParser) readTokenUntilNewline(recognizeEscapeSequence bool) {
 | 
			
		||||
	p.currentToken.Reset()
 | 
			
		||||
	escaped := false
 | 
			
		||||
	for p.err == nil {
 | 
			
		||||
		if recognizeEscapeSequence && escaped {
 | 
			
		||||
			switch p.currentByte {
 | 
			
		||||
			case '\\':
 | 
			
		||||
				p.currentToken.WriteByte(p.currentByte)
 | 
			
		||||
			case 'n':
 | 
			
		||||
				p.currentToken.WriteByte('\n')
 | 
			
		||||
			default:
 | 
			
		||||
				p.parseError(fmt.Sprintf("invalid escape sequence '\\%c'", p.currentByte))
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			escaped = false
 | 
			
		||||
		} else {
 | 
			
		||||
			switch p.currentByte {
 | 
			
		||||
			case '\n':
 | 
			
		||||
				return
 | 
			
		||||
			case '\\':
 | 
			
		||||
				escaped = true
 | 
			
		||||
			default:
 | 
			
		||||
				p.currentToken.WriteByte(p.currentByte)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		p.currentByte, p.err = p.buf.ReadByte()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readTokenAsMetricName copies a metric name from p.buf into p.currentToken.
 | 
			
		||||
// The first byte considered is the byte already read (now in p.currentByte).
 | 
			
		||||
// The first byte not part of a metric name is still copied into p.currentByte,
 | 
			
		||||
// but not into p.currentToken.
 | 
			
		||||
func (p *TextParser) readTokenAsMetricName() {
 | 
			
		||||
	p.currentToken.Reset()
 | 
			
		||||
	if !isValidMetricNameStart(p.currentByte) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for {
 | 
			
		||||
		p.currentToken.WriteByte(p.currentByte)
 | 
			
		||||
		p.currentByte, p.err = p.buf.ReadByte()
 | 
			
		||||
		if p.err != nil || !isValidMetricNameContinuation(p.currentByte) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readTokenAsLabelName copies a label name from p.buf into p.currentToken.
 | 
			
		||||
// The first byte considered is the byte already read (now in p.currentByte).
 | 
			
		||||
// The first byte not part of a label name is still copied into p.currentByte,
 | 
			
		||||
// but not into p.currentToken.
 | 
			
		||||
func (p *TextParser) readTokenAsLabelName() {
 | 
			
		||||
	p.currentToken.Reset()
 | 
			
		||||
	if !isValidLabelNameStart(p.currentByte) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for {
 | 
			
		||||
		p.currentToken.WriteByte(p.currentByte)
 | 
			
		||||
		p.currentByte, p.err = p.buf.ReadByte()
 | 
			
		||||
		if p.err != nil || !isValidLabelNameContinuation(p.currentByte) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readTokenAsLabelValue copies a label value from p.buf into p.currentToken.
 | 
			
		||||
// In contrast to the other 'readTokenAs...' functions, which start with the
 | 
			
		||||
// last read byte in p.currentByte, this method ignores p.currentByte and starts
 | 
			
		||||
// with reading a new byte from p.buf. The first byte not part of a label value
 | 
			
		||||
// is still copied into p.currentByte, but not into p.currentToken.
 | 
			
		||||
func (p *TextParser) readTokenAsLabelValue() {
 | 
			
		||||
	p.currentToken.Reset()
 | 
			
		||||
	escaped := false
 | 
			
		||||
	for {
 | 
			
		||||
		if p.currentByte, p.err = p.buf.ReadByte(); p.err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if escaped {
 | 
			
		||||
			switch p.currentByte {
 | 
			
		||||
			case '"', '\\':
 | 
			
		||||
				p.currentToken.WriteByte(p.currentByte)
 | 
			
		||||
			case 'n':
 | 
			
		||||
				p.currentToken.WriteByte('\n')
 | 
			
		||||
			default:
 | 
			
		||||
				p.parseError(fmt.Sprintf("invalid escape sequence '\\%c'", p.currentByte))
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			escaped = false
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		switch p.currentByte {
 | 
			
		||||
		case '"':
 | 
			
		||||
			return
 | 
			
		||||
		case '\n':
 | 
			
		||||
			p.parseError(fmt.Sprintf("label value %q contains unescaped new-line", p.currentToken.String()))
 | 
			
		||||
			return
 | 
			
		||||
		case '\\':
 | 
			
		||||
			escaped = true
 | 
			
		||||
		default:
 | 
			
		||||
			p.currentToken.WriteByte(p.currentByte)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *TextParser) setOrCreateCurrentMF() {
 | 
			
		||||
	p.currentIsSummaryCount = false
 | 
			
		||||
	p.currentIsSummarySum = false
 | 
			
		||||
	p.currentIsHistogramCount = false
 | 
			
		||||
	p.currentIsHistogramSum = false
 | 
			
		||||
	name := p.currentToken.String()
 | 
			
		||||
	if p.currentMF = p.metricFamiliesByName[name]; p.currentMF != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	// Try out if this is a _sum or _count for a summary/histogram.
 | 
			
		||||
	summaryName := summaryMetricName(name)
 | 
			
		||||
	if p.currentMF = p.metricFamiliesByName[summaryName]; p.currentMF != nil {
 | 
			
		||||
		if p.currentMF.GetType() == dto.MetricType_SUMMARY {
 | 
			
		||||
			if isCount(name) {
 | 
			
		||||
				p.currentIsSummaryCount = true
 | 
			
		||||
			}
 | 
			
		||||
			if isSum(name) {
 | 
			
		||||
				p.currentIsSummarySum = true
 | 
			
		||||
			}
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	histogramName := histogramMetricName(name)
 | 
			
		||||
	if p.currentMF = p.metricFamiliesByName[histogramName]; p.currentMF != nil {
 | 
			
		||||
		if p.currentMF.GetType() == dto.MetricType_HISTOGRAM {
 | 
			
		||||
			if isCount(name) {
 | 
			
		||||
				p.currentIsHistogramCount = true
 | 
			
		||||
			}
 | 
			
		||||
			if isSum(name) {
 | 
			
		||||
				p.currentIsHistogramSum = true
 | 
			
		||||
			}
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	p.currentMF = &dto.MetricFamily{Name: proto.String(name)}
 | 
			
		||||
	p.metricFamiliesByName[name] = p.currentMF
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isValidLabelNameStart(b byte) bool {
 | 
			
		||||
	return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isValidLabelNameContinuation(b byte) bool {
 | 
			
		||||
	return isValidLabelNameStart(b) || (b >= '0' && b <= '9')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isValidMetricNameStart(b byte) bool {
 | 
			
		||||
	return isValidLabelNameStart(b) || b == ':'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isValidMetricNameContinuation(b byte) bool {
 | 
			
		||||
	return isValidLabelNameContinuation(b) || b == ':'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isBlankOrTab(b byte) bool {
 | 
			
		||||
	return b == ' ' || b == '\t'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isCount(name string) bool {
 | 
			
		||||
	return len(name) > 6 && name[len(name)-6:] == "_count"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isSum(name string) bool {
 | 
			
		||||
	return len(name) > 4 && name[len(name)-4:] == "_sum"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isBucket(name string) bool {
 | 
			
		||||
	return len(name) > 7 && name[len(name)-7:] == "_bucket"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func summaryMetricName(name string) string {
 | 
			
		||||
	switch {
 | 
			
		||||
	case isCount(name):
 | 
			
		||||
		return name[:len(name)-6]
 | 
			
		||||
	case isSum(name):
 | 
			
		||||
		return name[:len(name)-4]
 | 
			
		||||
	default:
 | 
			
		||||
		return name
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func histogramMetricName(name string) string {
 | 
			
		||||
	switch {
 | 
			
		||||
	case isCount(name):
 | 
			
		||||
		return name[:len(name)-6]
 | 
			
		||||
	case isSum(name):
 | 
			
		||||
		return name[:len(name)-4]
 | 
			
		||||
	case isBucket(name):
 | 
			
		||||
		return name[:len(name)-7]
 | 
			
		||||
	default:
 | 
			
		||||
		return name
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseFloat(s string) (float64, error) {
 | 
			
		||||
	if strings.ContainsAny(s, "pP_") {
 | 
			
		||||
		return 0, fmt.Errorf("unsupported character in float")
 | 
			
		||||
	}
 | 
			
		||||
	return strconv.ParseFloat(s, 64)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										67
									
								
								vendor/github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg/README.txt
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								vendor/github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg/README.txt
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
			
		||||
PACKAGE
 | 
			
		||||
 | 
			
		||||
package goautoneg
 | 
			
		||||
import "bitbucket.org/ww/goautoneg"
 | 
			
		||||
 | 
			
		||||
HTTP Content-Type Autonegotiation.
 | 
			
		||||
 | 
			
		||||
The functions in this package implement the behaviour specified in
 | 
			
		||||
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2011, Open Knowledge Foundation Ltd.
 | 
			
		||||
All rights reserved.
 | 
			
		||||
 | 
			
		||||
Redistribution and use in source and binary forms, with or without
 | 
			
		||||
modification, are permitted provided that the following conditions are
 | 
			
		||||
met:
 | 
			
		||||
 | 
			
		||||
    Redistributions of source code must retain the above copyright
 | 
			
		||||
    notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 | 
			
		||||
    Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
    notice, this list of conditions and the following disclaimer in
 | 
			
		||||
    the documentation and/or other materials provided with the
 | 
			
		||||
    distribution.
 | 
			
		||||
 | 
			
		||||
    Neither the name of the Open Knowledge Foundation Ltd. nor the
 | 
			
		||||
    names of its contributors may be used to endorse or promote
 | 
			
		||||
    products derived from this software without specific prior written
 | 
			
		||||
    permission.
 | 
			
		||||
 | 
			
		||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
			
		||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
			
		||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
			
		||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
			
		||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
			
		||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
			
		||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
			
		||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
			
		||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
			
		||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FUNCTIONS
 | 
			
		||||
 | 
			
		||||
func Negotiate(header string, alternatives []string) (content_type string)
 | 
			
		||||
Negotiate the most appropriate content_type given the accept header
 | 
			
		||||
and a list of alternatives.
 | 
			
		||||
 | 
			
		||||
func ParseAccept(header string) (accept []Accept)
 | 
			
		||||
Parse an Accept Header string returning a sorted list
 | 
			
		||||
of clauses
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
TYPES
 | 
			
		||||
 | 
			
		||||
type Accept struct {
 | 
			
		||||
    Type, SubType string
 | 
			
		||||
    Q             float32
 | 
			
		||||
    Params        map[string]string
 | 
			
		||||
}
 | 
			
		||||
Structure to represent a clause in an HTTP Accept Header
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
SUBDIRECTORIES
 | 
			
		||||
 | 
			
		||||
	.hg
 | 
			
		||||
							
								
								
									
										162
									
								
								vendor/github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg/autoneg.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								vendor/github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg/autoneg.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,162 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright (c) 2011, Open Knowledge Foundation Ltd.
 | 
			
		||||
All rights reserved.
 | 
			
		||||
 | 
			
		||||
HTTP Content-Type Autonegotiation.
 | 
			
		||||
 | 
			
		||||
The functions in this package implement the behaviour specified in
 | 
			
		||||
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
 | 
			
		||||
 | 
			
		||||
Redistribution and use in source and binary forms, with or without
 | 
			
		||||
modification, are permitted provided that the following conditions are
 | 
			
		||||
met:
 | 
			
		||||
 | 
			
		||||
    Redistributions of source code must retain the above copyright
 | 
			
		||||
    notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 | 
			
		||||
    Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
    notice, this list of conditions and the following disclaimer in
 | 
			
		||||
    the documentation and/or other materials provided with the
 | 
			
		||||
    distribution.
 | 
			
		||||
 | 
			
		||||
    Neither the name of the Open Knowledge Foundation Ltd. nor the
 | 
			
		||||
    names of its contributors may be used to endorse or promote
 | 
			
		||||
    products derived from this software without specific prior written
 | 
			
		||||
    permission.
 | 
			
		||||
 | 
			
		||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
			
		||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
			
		||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
			
		||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
			
		||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
			
		||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
			
		||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
			
		||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
			
		||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
			
		||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
package goautoneg
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Structure to represent a clause in an HTTP Accept Header
 | 
			
		||||
type Accept struct {
 | 
			
		||||
	Type, SubType string
 | 
			
		||||
	Q             float64
 | 
			
		||||
	Params        map[string]string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// For internal use, so that we can use the sort interface
 | 
			
		||||
type accept_slice []Accept
 | 
			
		||||
 | 
			
		||||
func (accept accept_slice) Len() int {
 | 
			
		||||
	slice := []Accept(accept)
 | 
			
		||||
	return len(slice)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (accept accept_slice) Less(i, j int) bool {
 | 
			
		||||
	slice := []Accept(accept)
 | 
			
		||||
	ai, aj := slice[i], slice[j]
 | 
			
		||||
	if ai.Q > aj.Q {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	if ai.Type != "*" && aj.Type == "*" {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	if ai.SubType != "*" && aj.SubType == "*" {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (accept accept_slice) Swap(i, j int) {
 | 
			
		||||
	slice := []Accept(accept)
 | 
			
		||||
	slice[i], slice[j] = slice[j], slice[i]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Parse an Accept Header string returning a sorted list
 | 
			
		||||
// of clauses
 | 
			
		||||
func ParseAccept(header string) (accept []Accept) {
 | 
			
		||||
	parts := strings.Split(header, ",")
 | 
			
		||||
	accept = make([]Accept, 0, len(parts))
 | 
			
		||||
	for _, part := range parts {
 | 
			
		||||
		part := strings.Trim(part, " ")
 | 
			
		||||
 | 
			
		||||
		a := Accept{}
 | 
			
		||||
		a.Params = make(map[string]string)
 | 
			
		||||
		a.Q = 1.0
 | 
			
		||||
 | 
			
		||||
		mrp := strings.Split(part, ";")
 | 
			
		||||
 | 
			
		||||
		media_range := mrp[0]
 | 
			
		||||
		sp := strings.Split(media_range, "/")
 | 
			
		||||
		a.Type = strings.Trim(sp[0], " ")
 | 
			
		||||
 | 
			
		||||
		switch {
 | 
			
		||||
		case len(sp) == 1 && a.Type == "*":
 | 
			
		||||
			a.SubType = "*"
 | 
			
		||||
		case len(sp) == 2:
 | 
			
		||||
			a.SubType = strings.Trim(sp[1], " ")
 | 
			
		||||
		default:
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(mrp) == 1 {
 | 
			
		||||
			accept = append(accept, a)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, param := range mrp[1:] {
 | 
			
		||||
			sp := strings.SplitN(param, "=", 2)
 | 
			
		||||
			if len(sp) != 2 {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			token := strings.Trim(sp[0], " ")
 | 
			
		||||
			if token == "q" {
 | 
			
		||||
				a.Q, _ = strconv.ParseFloat(sp[1], 32)
 | 
			
		||||
			} else {
 | 
			
		||||
				a.Params[token] = strings.Trim(sp[1], " ")
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		accept = append(accept, a)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	slice := accept_slice(accept)
 | 
			
		||||
	sort.Sort(slice)
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Negotiate the most appropriate content_type given the accept header
 | 
			
		||||
// and a list of alternatives.
 | 
			
		||||
func Negotiate(header string, alternatives []string) (content_type string) {
 | 
			
		||||
	asp := make([][]string, 0, len(alternatives))
 | 
			
		||||
	for _, ctype := range alternatives {
 | 
			
		||||
		asp = append(asp, strings.SplitN(ctype, "/", 2))
 | 
			
		||||
	}
 | 
			
		||||
	for _, clause := range ParseAccept(header) {
 | 
			
		||||
		for i, ctsp := range asp {
 | 
			
		||||
			if clause.Type == ctsp[0] && clause.SubType == ctsp[1] {
 | 
			
		||||
				content_type = alternatives[i]
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if clause.Type == ctsp[0] && clause.SubType == "*" {
 | 
			
		||||
				content_type = alternatives[i]
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if clause.Type == "*" && clause.SubType == "*" {
 | 
			
		||||
				content_type = alternatives[i]
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										136
									
								
								vendor/github.com/prometheus/common/model/alert.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								vendor/github.com/prometheus/common/model/alert.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,136 @@
 | 
			
		||||
// Copyright 2013 The Prometheus Authors
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
// http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type AlertStatus string
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	AlertFiring   AlertStatus = "firing"
 | 
			
		||||
	AlertResolved AlertStatus = "resolved"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Alert is a generic representation of an alert in the Prometheus eco-system.
 | 
			
		||||
type Alert struct {
 | 
			
		||||
	// Label value pairs for purpose of aggregation, matching, and disposition
 | 
			
		||||
	// dispatching. This must minimally include an "alertname" label.
 | 
			
		||||
	Labels LabelSet `json:"labels"`
 | 
			
		||||
 | 
			
		||||
	// Extra key/value information which does not define alert identity.
 | 
			
		||||
	Annotations LabelSet `json:"annotations"`
 | 
			
		||||
 | 
			
		||||
	// The known time range for this alert. Both ends are optional.
 | 
			
		||||
	StartsAt     time.Time `json:"startsAt,omitempty"`
 | 
			
		||||
	EndsAt       time.Time `json:"endsAt,omitempty"`
 | 
			
		||||
	GeneratorURL string    `json:"generatorURL"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Name returns the name of the alert. It is equivalent to the "alertname" label.
 | 
			
		||||
func (a *Alert) Name() string {
 | 
			
		||||
	return string(a.Labels[AlertNameLabel])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Fingerprint returns a unique hash for the alert. It is equivalent to
 | 
			
		||||
// the fingerprint of the alert's label set.
 | 
			
		||||
func (a *Alert) Fingerprint() Fingerprint {
 | 
			
		||||
	return a.Labels.Fingerprint()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (a *Alert) String() string {
 | 
			
		||||
	s := fmt.Sprintf("%s[%s]", a.Name(), a.Fingerprint().String()[:7])
 | 
			
		||||
	if a.Resolved() {
 | 
			
		||||
		return s + "[resolved]"
 | 
			
		||||
	}
 | 
			
		||||
	return s + "[active]"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Resolved returns true iff the activity interval ended in the past.
 | 
			
		||||
func (a *Alert) Resolved() bool {
 | 
			
		||||
	return a.ResolvedAt(time.Now())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ResolvedAt returns true off the activity interval ended before
 | 
			
		||||
// the given timestamp.
 | 
			
		||||
func (a *Alert) ResolvedAt(ts time.Time) bool {
 | 
			
		||||
	if a.EndsAt.IsZero() {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return !a.EndsAt.After(ts)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Status returns the status of the alert.
 | 
			
		||||
func (a *Alert) Status() AlertStatus {
 | 
			
		||||
	if a.Resolved() {
 | 
			
		||||
		return AlertResolved
 | 
			
		||||
	}
 | 
			
		||||
	return AlertFiring
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Validate checks whether the alert data is inconsistent.
 | 
			
		||||
func (a *Alert) Validate() error {
 | 
			
		||||
	if a.StartsAt.IsZero() {
 | 
			
		||||
		return fmt.Errorf("start time missing")
 | 
			
		||||
	}
 | 
			
		||||
	if !a.EndsAt.IsZero() && a.EndsAt.Before(a.StartsAt) {
 | 
			
		||||
		return fmt.Errorf("start time must be before end time")
 | 
			
		||||
	}
 | 
			
		||||
	if err := a.Labels.Validate(); err != nil {
 | 
			
		||||
		return fmt.Errorf("invalid label set: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	if len(a.Labels) == 0 {
 | 
			
		||||
		return fmt.Errorf("at least one label pair required")
 | 
			
		||||
	}
 | 
			
		||||
	if err := a.Annotations.Validate(); err != nil {
 | 
			
		||||
		return fmt.Errorf("invalid annotations: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Alert is a list of alerts that can be sorted in chronological order.
 | 
			
		||||
type Alerts []*Alert
 | 
			
		||||
 | 
			
		||||
func (as Alerts) Len() int      { return len(as) }
 | 
			
		||||
func (as Alerts) Swap(i, j int) { as[i], as[j] = as[j], as[i] }
 | 
			
		||||
 | 
			
		||||
func (as Alerts) Less(i, j int) bool {
 | 
			
		||||
	if as[i].StartsAt.Before(as[j].StartsAt) {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	if as[i].EndsAt.Before(as[j].EndsAt) {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return as[i].Fingerprint() < as[j].Fingerprint()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HasFiring returns true iff one of the alerts is not resolved.
 | 
			
		||||
func (as Alerts) HasFiring() bool {
 | 
			
		||||
	for _, a := range as {
 | 
			
		||||
		if !a.Resolved() {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Status returns StatusFiring iff at least one of the alerts is firing.
 | 
			
		||||
func (as Alerts) Status() AlertStatus {
 | 
			
		||||
	if as.HasFiring() {
 | 
			
		||||
		return AlertFiring
 | 
			
		||||
	}
 | 
			
		||||
	return AlertResolved
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										105
									
								
								vendor/github.com/prometheus/common/model/fingerprinting.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								vendor/github.com/prometheus/common/model/fingerprinting.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,105 @@
 | 
			
		||||
// Copyright 2013 The Prometheus Authors
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
// http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Fingerprint provides a hash-capable representation of a Metric.
 | 
			
		||||
// For our purposes, FNV-1A 64-bit is used.
 | 
			
		||||
type Fingerprint uint64
 | 
			
		||||
 | 
			
		||||
// FingerprintFromString transforms a string representation into a Fingerprint.
 | 
			
		||||
func FingerprintFromString(s string) (Fingerprint, error) {
 | 
			
		||||
	num, err := strconv.ParseUint(s, 16, 64)
 | 
			
		||||
	return Fingerprint(num), err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseFingerprint parses the input string into a fingerprint.
 | 
			
		||||
func ParseFingerprint(s string) (Fingerprint, error) {
 | 
			
		||||
	num, err := strconv.ParseUint(s, 16, 64)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	return Fingerprint(num), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f Fingerprint) String() string {
 | 
			
		||||
	return fmt.Sprintf("%016x", uint64(f))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Fingerprints represents a collection of Fingerprint subject to a given
 | 
			
		||||
// natural sorting scheme. It implements sort.Interface.
 | 
			
		||||
type Fingerprints []Fingerprint
 | 
			
		||||
 | 
			
		||||
// Len implements sort.Interface.
 | 
			
		||||
func (f Fingerprints) Len() int {
 | 
			
		||||
	return len(f)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Less implements sort.Interface.
 | 
			
		||||
func (f Fingerprints) Less(i, j int) bool {
 | 
			
		||||
	return f[i] < f[j]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Swap implements sort.Interface.
 | 
			
		||||
func (f Fingerprints) Swap(i, j int) {
 | 
			
		||||
	f[i], f[j] = f[j], f[i]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FingerprintSet is a set of Fingerprints.
 | 
			
		||||
type FingerprintSet map[Fingerprint]struct{}
 | 
			
		||||
 | 
			
		||||
// Equal returns true if both sets contain the same elements (and not more).
 | 
			
		||||
func (s FingerprintSet) Equal(o FingerprintSet) bool {
 | 
			
		||||
	if len(s) != len(o) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for k := range s {
 | 
			
		||||
		if _, ok := o[k]; !ok {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Intersection returns the elements contained in both sets.
 | 
			
		||||
func (s FingerprintSet) Intersection(o FingerprintSet) FingerprintSet {
 | 
			
		||||
	myLength, otherLength := len(s), len(o)
 | 
			
		||||
	if myLength == 0 || otherLength == 0 {
 | 
			
		||||
		return FingerprintSet{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	subSet := s
 | 
			
		||||
	superSet := o
 | 
			
		||||
 | 
			
		||||
	if otherLength < myLength {
 | 
			
		||||
		subSet = o
 | 
			
		||||
		superSet = s
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	out := FingerprintSet{}
 | 
			
		||||
 | 
			
		||||
	for k := range subSet {
 | 
			
		||||
		if _, ok := superSet[k]; ok {
 | 
			
		||||
			out[k] = struct{}{}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										42
									
								
								vendor/github.com/prometheus/common/model/fnv.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								vendor/github.com/prometheus/common/model/fnv.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
// Copyright 2015 The Prometheus Authors
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
// http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
// Inline and byte-free variant of hash/fnv's fnv64a.
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	offset64 = 14695981039346656037
 | 
			
		||||
	prime64  = 1099511628211
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// hashNew initializies a new fnv64a hash value.
 | 
			
		||||
func hashNew() uint64 {
 | 
			
		||||
	return offset64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// hashAdd adds a string to a fnv64a hash value, returning the updated hash.
 | 
			
		||||
func hashAdd(h uint64, s string) uint64 {
 | 
			
		||||
	for i := 0; i < len(s); i++ {
 | 
			
		||||
		h ^= uint64(s[i])
 | 
			
		||||
		h *= prime64
 | 
			
		||||
	}
 | 
			
		||||
	return h
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// hashAddByte adds a byte to a fnv64a hash value, returning the updated hash.
 | 
			
		||||
func hashAddByte(h uint64, b byte) uint64 {
 | 
			
		||||
	h ^= uint64(b)
 | 
			
		||||
	h *= prime64
 | 
			
		||||
	return h
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										210
									
								
								vendor/github.com/prometheus/common/model/labels.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								vendor/github.com/prometheus/common/model/labels.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,210 @@
 | 
			
		||||
// Copyright 2013 The Prometheus Authors
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
// http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"unicode/utf8"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// AlertNameLabel is the name of the label containing the an alert's name.
 | 
			
		||||
	AlertNameLabel = "alertname"
 | 
			
		||||
 | 
			
		||||
	// ExportedLabelPrefix is the prefix to prepend to the label names present in
 | 
			
		||||
	// exported metrics if a label of the same name is added by the server.
 | 
			
		||||
	ExportedLabelPrefix = "exported_"
 | 
			
		||||
 | 
			
		||||
	// MetricNameLabel is the label name indicating the metric name of a
 | 
			
		||||
	// timeseries.
 | 
			
		||||
	MetricNameLabel = "__name__"
 | 
			
		||||
 | 
			
		||||
	// SchemeLabel is the name of the label that holds the scheme on which to
 | 
			
		||||
	// scrape a target.
 | 
			
		||||
	SchemeLabel = "__scheme__"
 | 
			
		||||
 | 
			
		||||
	// AddressLabel is the name of the label that holds the address of
 | 
			
		||||
	// a scrape target.
 | 
			
		||||
	AddressLabel = "__address__"
 | 
			
		||||
 | 
			
		||||
	// MetricsPathLabel is the name of the label that holds the path on which to
 | 
			
		||||
	// scrape a target.
 | 
			
		||||
	MetricsPathLabel = "__metrics_path__"
 | 
			
		||||
 | 
			
		||||
	// ReservedLabelPrefix is a prefix which is not legal in user-supplied
 | 
			
		||||
	// label names.
 | 
			
		||||
	ReservedLabelPrefix = "__"
 | 
			
		||||
 | 
			
		||||
	// MetaLabelPrefix is a prefix for labels that provide meta information.
 | 
			
		||||
	// Labels with this prefix are used for intermediate label processing and
 | 
			
		||||
	// will not be attached to time series.
 | 
			
		||||
	MetaLabelPrefix = "__meta_"
 | 
			
		||||
 | 
			
		||||
	// TmpLabelPrefix is a prefix for temporary labels as part of relabelling.
 | 
			
		||||
	// Labels with this prefix are used for intermediate label processing and
 | 
			
		||||
	// will not be attached to time series. This is reserved for use in
 | 
			
		||||
	// Prometheus configuration files by users.
 | 
			
		||||
	TmpLabelPrefix = "__tmp_"
 | 
			
		||||
 | 
			
		||||
	// ParamLabelPrefix is a prefix for labels that provide URL parameters
 | 
			
		||||
	// used to scrape a target.
 | 
			
		||||
	ParamLabelPrefix = "__param_"
 | 
			
		||||
 | 
			
		||||
	// JobLabel is the label name indicating the job from which a timeseries
 | 
			
		||||
	// was scraped.
 | 
			
		||||
	JobLabel = "job"
 | 
			
		||||
 | 
			
		||||
	// InstanceLabel is the label name used for the instance label.
 | 
			
		||||
	InstanceLabel = "instance"
 | 
			
		||||
 | 
			
		||||
	// BucketLabel is used for the label that defines the upper bound of a
 | 
			
		||||
	// bucket of a histogram ("le" -> "less or equal").
 | 
			
		||||
	BucketLabel = "le"
 | 
			
		||||
 | 
			
		||||
	// QuantileLabel is used for the label that defines the quantile in a
 | 
			
		||||
	// summary.
 | 
			
		||||
	QuantileLabel = "quantile"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// LabelNameRE is a regular expression matching valid label names. Note that the
 | 
			
		||||
// IsValid method of LabelName performs the same check but faster than a match
 | 
			
		||||
// with this regular expression.
 | 
			
		||||
var LabelNameRE = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$")
 | 
			
		||||
 | 
			
		||||
// A LabelName is a key for a LabelSet or Metric.  It has a value associated
 | 
			
		||||
// therewith.
 | 
			
		||||
type LabelName string
 | 
			
		||||
 | 
			
		||||
// IsValid is true iff the label name matches the pattern of LabelNameRE. This
 | 
			
		||||
// method, however, does not use LabelNameRE for the check but a much faster
 | 
			
		||||
// hardcoded implementation.
 | 
			
		||||
func (ln LabelName) IsValid() bool {
 | 
			
		||||
	if len(ln) == 0 {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	for i, b := range ln {
 | 
			
		||||
		if !((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_' || (b >= '0' && b <= '9' && i > 0)) {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
 | 
			
		||||
func (ln *LabelName) UnmarshalYAML(unmarshal func(interface{}) error) error {
 | 
			
		||||
	var s string
 | 
			
		||||
	if err := unmarshal(&s); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if !LabelName(s).IsValid() {
 | 
			
		||||
		return fmt.Errorf("%q is not a valid label name", s)
 | 
			
		||||
	}
 | 
			
		||||
	*ln = LabelName(s)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalJSON implements the json.Unmarshaler interface.
 | 
			
		||||
func (ln *LabelName) UnmarshalJSON(b []byte) error {
 | 
			
		||||
	var s string
 | 
			
		||||
	if err := json.Unmarshal(b, &s); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if !LabelName(s).IsValid() {
 | 
			
		||||
		return fmt.Errorf("%q is not a valid label name", s)
 | 
			
		||||
	}
 | 
			
		||||
	*ln = LabelName(s)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LabelNames is a sortable LabelName slice. In implements sort.Interface.
 | 
			
		||||
type LabelNames []LabelName
 | 
			
		||||
 | 
			
		||||
func (l LabelNames) Len() int {
 | 
			
		||||
	return len(l)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l LabelNames) Less(i, j int) bool {
 | 
			
		||||
	return l[i] < l[j]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l LabelNames) Swap(i, j int) {
 | 
			
		||||
	l[i], l[j] = l[j], l[i]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l LabelNames) String() string {
 | 
			
		||||
	labelStrings := make([]string, 0, len(l))
 | 
			
		||||
	for _, label := range l {
 | 
			
		||||
		labelStrings = append(labelStrings, string(label))
 | 
			
		||||
	}
 | 
			
		||||
	return strings.Join(labelStrings, ", ")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A LabelValue is an associated value for a LabelName.
 | 
			
		||||
type LabelValue string
 | 
			
		||||
 | 
			
		||||
// IsValid returns true iff the string is a valid UTF8.
 | 
			
		||||
func (lv LabelValue) IsValid() bool {
 | 
			
		||||
	return utf8.ValidString(string(lv))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LabelValues is a sortable LabelValue slice. It implements sort.Interface.
 | 
			
		||||
type LabelValues []LabelValue
 | 
			
		||||
 | 
			
		||||
func (l LabelValues) Len() int {
 | 
			
		||||
	return len(l)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l LabelValues) Less(i, j int) bool {
 | 
			
		||||
	return string(l[i]) < string(l[j])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l LabelValues) Swap(i, j int) {
 | 
			
		||||
	l[i], l[j] = l[j], l[i]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LabelPair pairs a name with a value.
 | 
			
		||||
type LabelPair struct {
 | 
			
		||||
	Name  LabelName
 | 
			
		||||
	Value LabelValue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LabelPairs is a sortable slice of LabelPair pointers. It implements
 | 
			
		||||
// sort.Interface.
 | 
			
		||||
type LabelPairs []*LabelPair
 | 
			
		||||
 | 
			
		||||
func (l LabelPairs) Len() int {
 | 
			
		||||
	return len(l)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l LabelPairs) Less(i, j int) bool {
 | 
			
		||||
	switch {
 | 
			
		||||
	case l[i].Name > l[j].Name:
 | 
			
		||||
		return false
 | 
			
		||||
	case l[i].Name < l[j].Name:
 | 
			
		||||
		return true
 | 
			
		||||
	case l[i].Value > l[j].Value:
 | 
			
		||||
		return false
 | 
			
		||||
	case l[i].Value < l[j].Value:
 | 
			
		||||
		return true
 | 
			
		||||
	default:
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l LabelPairs) Swap(i, j int) {
 | 
			
		||||
	l[i], l[j] = l[j], l[i]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										169
									
								
								vendor/github.com/prometheus/common/model/labelset.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								vendor/github.com/prometheus/common/model/labelset.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,169 @@
 | 
			
		||||
// Copyright 2013 The Prometheus Authors
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
// http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// A LabelSet is a collection of LabelName and LabelValue pairs.  The LabelSet
 | 
			
		||||
// may be fully-qualified down to the point where it may resolve to a single
 | 
			
		||||
// Metric in the data store or not.  All operations that occur within the realm
 | 
			
		||||
// of a LabelSet can emit a vector of Metric entities to which the LabelSet may
 | 
			
		||||
// match.
 | 
			
		||||
type LabelSet map[LabelName]LabelValue
 | 
			
		||||
 | 
			
		||||
// Validate checks whether all names and values in the label set
 | 
			
		||||
// are valid.
 | 
			
		||||
func (ls LabelSet) Validate() error {
 | 
			
		||||
	for ln, lv := range ls {
 | 
			
		||||
		if !ln.IsValid() {
 | 
			
		||||
			return fmt.Errorf("invalid name %q", ln)
 | 
			
		||||
		}
 | 
			
		||||
		if !lv.IsValid() {
 | 
			
		||||
			return fmt.Errorf("invalid value %q", lv)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Equal returns true iff both label sets have exactly the same key/value pairs.
 | 
			
		||||
func (ls LabelSet) Equal(o LabelSet) bool {
 | 
			
		||||
	if len(ls) != len(o) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	for ln, lv := range ls {
 | 
			
		||||
		olv, ok := o[ln]
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		if olv != lv {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Before compares the metrics, using the following criteria:
 | 
			
		||||
//
 | 
			
		||||
// If m has fewer labels than o, it is before o. If it has more, it is not.
 | 
			
		||||
//
 | 
			
		||||
// If the number of labels is the same, the superset of all label names is
 | 
			
		||||
// sorted alphanumerically. The first differing label pair found in that order
 | 
			
		||||
// determines the outcome: If the label does not exist at all in m, then m is
 | 
			
		||||
// before o, and vice versa. Otherwise the label value is compared
 | 
			
		||||
// alphanumerically.
 | 
			
		||||
//
 | 
			
		||||
// If m and o are equal, the method returns false.
 | 
			
		||||
func (ls LabelSet) Before(o LabelSet) bool {
 | 
			
		||||
	if len(ls) < len(o) {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	if len(ls) > len(o) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	lns := make(LabelNames, 0, len(ls)+len(o))
 | 
			
		||||
	for ln := range ls {
 | 
			
		||||
		lns = append(lns, ln)
 | 
			
		||||
	}
 | 
			
		||||
	for ln := range o {
 | 
			
		||||
		lns = append(lns, ln)
 | 
			
		||||
	}
 | 
			
		||||
	// It's probably not worth it to de-dup lns.
 | 
			
		||||
	sort.Sort(lns)
 | 
			
		||||
	for _, ln := range lns {
 | 
			
		||||
		mlv, ok := ls[ln]
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
		olv, ok := o[ln]
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		if mlv < olv {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
		if mlv > olv {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Clone returns a copy of the label set.
 | 
			
		||||
func (ls LabelSet) Clone() LabelSet {
 | 
			
		||||
	lsn := make(LabelSet, len(ls))
 | 
			
		||||
	for ln, lv := range ls {
 | 
			
		||||
		lsn[ln] = lv
 | 
			
		||||
	}
 | 
			
		||||
	return lsn
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Merge is a helper function to non-destructively merge two label sets.
 | 
			
		||||
func (l LabelSet) Merge(other LabelSet) LabelSet {
 | 
			
		||||
	result := make(LabelSet, len(l))
 | 
			
		||||
 | 
			
		||||
	for k, v := range l {
 | 
			
		||||
		result[k] = v
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for k, v := range other {
 | 
			
		||||
		result[k] = v
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l LabelSet) String() string {
 | 
			
		||||
	lstrs := make([]string, 0, len(l))
 | 
			
		||||
	for l, v := range l {
 | 
			
		||||
		lstrs = append(lstrs, fmt.Sprintf("%s=%q", l, v))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sort.Strings(lstrs)
 | 
			
		||||
	return fmt.Sprintf("{%s}", strings.Join(lstrs, ", "))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Fingerprint returns the LabelSet's fingerprint.
 | 
			
		||||
func (ls LabelSet) Fingerprint() Fingerprint {
 | 
			
		||||
	return labelSetToFingerprint(ls)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FastFingerprint returns the LabelSet's Fingerprint calculated by a faster hashing
 | 
			
		||||
// algorithm, which is, however, more susceptible to hash collisions.
 | 
			
		||||
func (ls LabelSet) FastFingerprint() Fingerprint {
 | 
			
		||||
	return labelSetToFastFingerprint(ls)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalJSON implements the json.Unmarshaler interface.
 | 
			
		||||
func (l *LabelSet) UnmarshalJSON(b []byte) error {
 | 
			
		||||
	var m map[LabelName]LabelValue
 | 
			
		||||
	if err := json.Unmarshal(b, &m); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	// encoding/json only unmarshals maps of the form map[string]T. It treats
 | 
			
		||||
	// LabelName as a string and does not call its UnmarshalJSON method.
 | 
			
		||||
	// Thus, we have to replicate the behavior here.
 | 
			
		||||
	for ln := range m {
 | 
			
		||||
		if !ln.IsValid() {
 | 
			
		||||
			return fmt.Errorf("%q is not a valid label name", ln)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	*l = LabelSet(m)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										102
									
								
								vendor/github.com/prometheus/common/model/metric.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								vendor/github.com/prometheus/common/model/metric.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,102 @@
 | 
			
		||||
// Copyright 2013 The Prometheus Authors
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
// http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// MetricNameRE is a regular expression matching valid metric
 | 
			
		||||
	// names. Note that the IsValidMetricName function performs the same
 | 
			
		||||
	// check but faster than a match with this regular expression.
 | 
			
		||||
	MetricNameRE = regexp.MustCompile(`^[a-zA-Z_:][a-zA-Z0-9_:]*$`)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// A Metric is similar to a LabelSet, but the key difference is that a Metric is
 | 
			
		||||
// a singleton and refers to one and only one stream of samples.
 | 
			
		||||
type Metric LabelSet
 | 
			
		||||
 | 
			
		||||
// Equal compares the metrics.
 | 
			
		||||
func (m Metric) Equal(o Metric) bool {
 | 
			
		||||
	return LabelSet(m).Equal(LabelSet(o))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Before compares the metrics' underlying label sets.
 | 
			
		||||
func (m Metric) Before(o Metric) bool {
 | 
			
		||||
	return LabelSet(m).Before(LabelSet(o))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Clone returns a copy of the Metric.
 | 
			
		||||
func (m Metric) Clone() Metric {
 | 
			
		||||
	clone := make(Metric, len(m))
 | 
			
		||||
	for k, v := range m {
 | 
			
		||||
		clone[k] = v
 | 
			
		||||
	}
 | 
			
		||||
	return clone
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m Metric) String() string {
 | 
			
		||||
	metricName, hasName := m[MetricNameLabel]
 | 
			
		||||
	numLabels := len(m) - 1
 | 
			
		||||
	if !hasName {
 | 
			
		||||
		numLabels = len(m)
 | 
			
		||||
	}
 | 
			
		||||
	labelStrings := make([]string, 0, numLabels)
 | 
			
		||||
	for label, value := range m {
 | 
			
		||||
		if label != MetricNameLabel {
 | 
			
		||||
			labelStrings = append(labelStrings, fmt.Sprintf("%s=%q", label, value))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch numLabels {
 | 
			
		||||
	case 0:
 | 
			
		||||
		if hasName {
 | 
			
		||||
			return string(metricName)
 | 
			
		||||
		}
 | 
			
		||||
		return "{}"
 | 
			
		||||
	default:
 | 
			
		||||
		sort.Strings(labelStrings)
 | 
			
		||||
		return fmt.Sprintf("%s{%s}", metricName, strings.Join(labelStrings, ", "))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Fingerprint returns a Metric's Fingerprint.
 | 
			
		||||
func (m Metric) Fingerprint() Fingerprint {
 | 
			
		||||
	return LabelSet(m).Fingerprint()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FastFingerprint returns a Metric's Fingerprint calculated by a faster hashing
 | 
			
		||||
// algorithm, which is, however, more susceptible to hash collisions.
 | 
			
		||||
func (m Metric) FastFingerprint() Fingerprint {
 | 
			
		||||
	return LabelSet(m).FastFingerprint()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsValidMetricName returns true iff name matches the pattern of MetricNameRE.
 | 
			
		||||
// This function, however, does not use MetricNameRE for the check but a much
 | 
			
		||||
// faster hardcoded implementation.
 | 
			
		||||
func IsValidMetricName(n LabelValue) bool {
 | 
			
		||||
	if len(n) == 0 {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	for i, b := range n {
 | 
			
		||||
		if !((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_' || b == ':' || (b >= '0' && b <= '9' && i > 0)) {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								vendor/github.com/prometheus/common/model/model.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								vendor/github.com/prometheus/common/model/model.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
// Copyright 2013 The Prometheus Authors
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
// http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
// Package model contains common data structures that are shared across
 | 
			
		||||
// Prometheus components and libraries.
 | 
			
		||||
package model
 | 
			
		||||
							
								
								
									
										144
									
								
								vendor/github.com/prometheus/common/model/signature.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								vendor/github.com/prometheus/common/model/signature.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,144 @@
 | 
			
		||||
// Copyright 2014 The Prometheus Authors
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
// http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"sort"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SeparatorByte is a byte that cannot occur in valid UTF-8 sequences and is
 | 
			
		||||
// used to separate label names, label values, and other strings from each other
 | 
			
		||||
// when calculating their combined hash value (aka signature aka fingerprint).
 | 
			
		||||
const SeparatorByte byte = 255
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// cache the signature of an empty label set.
 | 
			
		||||
	emptyLabelSignature = hashNew()
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// LabelsToSignature returns a quasi-unique signature (i.e., fingerprint) for a
 | 
			
		||||
// given label set. (Collisions are possible but unlikely if the number of label
 | 
			
		||||
// sets the function is applied to is small.)
 | 
			
		||||
func LabelsToSignature(labels map[string]string) uint64 {
 | 
			
		||||
	if len(labels) == 0 {
 | 
			
		||||
		return emptyLabelSignature
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	labelNames := make([]string, 0, len(labels))
 | 
			
		||||
	for labelName := range labels {
 | 
			
		||||
		labelNames = append(labelNames, labelName)
 | 
			
		||||
	}
 | 
			
		||||
	sort.Strings(labelNames)
 | 
			
		||||
 | 
			
		||||
	sum := hashNew()
 | 
			
		||||
	for _, labelName := range labelNames {
 | 
			
		||||
		sum = hashAdd(sum, labelName)
 | 
			
		||||
		sum = hashAddByte(sum, SeparatorByte)
 | 
			
		||||
		sum = hashAdd(sum, labels[labelName])
 | 
			
		||||
		sum = hashAddByte(sum, SeparatorByte)
 | 
			
		||||
	}
 | 
			
		||||
	return sum
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// labelSetToFingerprint works exactly as LabelsToSignature but takes a LabelSet as
 | 
			
		||||
// parameter (rather than a label map) and returns a Fingerprint.
 | 
			
		||||
func labelSetToFingerprint(ls LabelSet) Fingerprint {
 | 
			
		||||
	if len(ls) == 0 {
 | 
			
		||||
		return Fingerprint(emptyLabelSignature)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	labelNames := make(LabelNames, 0, len(ls))
 | 
			
		||||
	for labelName := range ls {
 | 
			
		||||
		labelNames = append(labelNames, labelName)
 | 
			
		||||
	}
 | 
			
		||||
	sort.Sort(labelNames)
 | 
			
		||||
 | 
			
		||||
	sum := hashNew()
 | 
			
		||||
	for _, labelName := range labelNames {
 | 
			
		||||
		sum = hashAdd(sum, string(labelName))
 | 
			
		||||
		sum = hashAddByte(sum, SeparatorByte)
 | 
			
		||||
		sum = hashAdd(sum, string(ls[labelName]))
 | 
			
		||||
		sum = hashAddByte(sum, SeparatorByte)
 | 
			
		||||
	}
 | 
			
		||||
	return Fingerprint(sum)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// labelSetToFastFingerprint works similar to labelSetToFingerprint but uses a
 | 
			
		||||
// faster and less allocation-heavy hash function, which is more susceptible to
 | 
			
		||||
// create hash collisions. Therefore, collision detection should be applied.
 | 
			
		||||
func labelSetToFastFingerprint(ls LabelSet) Fingerprint {
 | 
			
		||||
	if len(ls) == 0 {
 | 
			
		||||
		return Fingerprint(emptyLabelSignature)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var result uint64
 | 
			
		||||
	for labelName, labelValue := range ls {
 | 
			
		||||
		sum := hashNew()
 | 
			
		||||
		sum = hashAdd(sum, string(labelName))
 | 
			
		||||
		sum = hashAddByte(sum, SeparatorByte)
 | 
			
		||||
		sum = hashAdd(sum, string(labelValue))
 | 
			
		||||
		result ^= sum
 | 
			
		||||
	}
 | 
			
		||||
	return Fingerprint(result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SignatureForLabels works like LabelsToSignature but takes a Metric as
 | 
			
		||||
// parameter (rather than a label map) and only includes the labels with the
 | 
			
		||||
// specified LabelNames into the signature calculation. The labels passed in
 | 
			
		||||
// will be sorted by this function.
 | 
			
		||||
func SignatureForLabels(m Metric, labels ...LabelName) uint64 {
 | 
			
		||||
	if len(labels) == 0 {
 | 
			
		||||
		return emptyLabelSignature
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sort.Sort(LabelNames(labels))
 | 
			
		||||
 | 
			
		||||
	sum := hashNew()
 | 
			
		||||
	for _, label := range labels {
 | 
			
		||||
		sum = hashAdd(sum, string(label))
 | 
			
		||||
		sum = hashAddByte(sum, SeparatorByte)
 | 
			
		||||
		sum = hashAdd(sum, string(m[label]))
 | 
			
		||||
		sum = hashAddByte(sum, SeparatorByte)
 | 
			
		||||
	}
 | 
			
		||||
	return sum
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SignatureWithoutLabels works like LabelsToSignature but takes a Metric as
 | 
			
		||||
// parameter (rather than a label map) and excludes the labels with any of the
 | 
			
		||||
// specified LabelNames from the signature calculation.
 | 
			
		||||
func SignatureWithoutLabels(m Metric, labels map[LabelName]struct{}) uint64 {
 | 
			
		||||
	if len(m) == 0 {
 | 
			
		||||
		return emptyLabelSignature
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	labelNames := make(LabelNames, 0, len(m))
 | 
			
		||||
	for labelName := range m {
 | 
			
		||||
		if _, exclude := labels[labelName]; !exclude {
 | 
			
		||||
			labelNames = append(labelNames, labelName)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(labelNames) == 0 {
 | 
			
		||||
		return emptyLabelSignature
 | 
			
		||||
	}
 | 
			
		||||
	sort.Sort(labelNames)
 | 
			
		||||
 | 
			
		||||
	sum := hashNew()
 | 
			
		||||
	for _, labelName := range labelNames {
 | 
			
		||||
		sum = hashAdd(sum, string(labelName))
 | 
			
		||||
		sum = hashAddByte(sum, SeparatorByte)
 | 
			
		||||
		sum = hashAdd(sum, string(m[labelName]))
 | 
			
		||||
		sum = hashAddByte(sum, SeparatorByte)
 | 
			
		||||
	}
 | 
			
		||||
	return sum
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										106
									
								
								vendor/github.com/prometheus/common/model/silence.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								vendor/github.com/prometheus/common/model/silence.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,106 @@
 | 
			
		||||
// Copyright 2015 The Prometheus Authors
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
// http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Matcher describes a matches the value of a given label.
 | 
			
		||||
type Matcher struct {
 | 
			
		||||
	Name    LabelName `json:"name"`
 | 
			
		||||
	Value   string    `json:"value"`
 | 
			
		||||
	IsRegex bool      `json:"isRegex"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *Matcher) UnmarshalJSON(b []byte) error {
 | 
			
		||||
	type plain Matcher
 | 
			
		||||
	if err := json.Unmarshal(b, (*plain)(m)); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(m.Name) == 0 {
 | 
			
		||||
		return fmt.Errorf("label name in matcher must not be empty")
 | 
			
		||||
	}
 | 
			
		||||
	if m.IsRegex {
 | 
			
		||||
		if _, err := regexp.Compile(m.Value); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Validate returns true iff all fields of the matcher have valid values.
 | 
			
		||||
func (m *Matcher) Validate() error {
 | 
			
		||||
	if !m.Name.IsValid() {
 | 
			
		||||
		return fmt.Errorf("invalid name %q", m.Name)
 | 
			
		||||
	}
 | 
			
		||||
	if m.IsRegex {
 | 
			
		||||
		if _, err := regexp.Compile(m.Value); err != nil {
 | 
			
		||||
			return fmt.Errorf("invalid regular expression %q", m.Value)
 | 
			
		||||
		}
 | 
			
		||||
	} else if !LabelValue(m.Value).IsValid() || len(m.Value) == 0 {
 | 
			
		||||
		return fmt.Errorf("invalid value %q", m.Value)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Silence defines the representation of a silence definition in the Prometheus
 | 
			
		||||
// eco-system.
 | 
			
		||||
type Silence struct {
 | 
			
		||||
	ID uint64 `json:"id,omitempty"`
 | 
			
		||||
 | 
			
		||||
	Matchers []*Matcher `json:"matchers"`
 | 
			
		||||
 | 
			
		||||
	StartsAt time.Time `json:"startsAt"`
 | 
			
		||||
	EndsAt   time.Time `json:"endsAt"`
 | 
			
		||||
 | 
			
		||||
	CreatedAt time.Time `json:"createdAt,omitempty"`
 | 
			
		||||
	CreatedBy string    `json:"createdBy"`
 | 
			
		||||
	Comment   string    `json:"comment,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Validate returns true iff all fields of the silence have valid values.
 | 
			
		||||
func (s *Silence) Validate() error {
 | 
			
		||||
	if len(s.Matchers) == 0 {
 | 
			
		||||
		return fmt.Errorf("at least one matcher required")
 | 
			
		||||
	}
 | 
			
		||||
	for _, m := range s.Matchers {
 | 
			
		||||
		if err := m.Validate(); err != nil {
 | 
			
		||||
			return fmt.Errorf("invalid matcher: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if s.StartsAt.IsZero() {
 | 
			
		||||
		return fmt.Errorf("start time missing")
 | 
			
		||||
	}
 | 
			
		||||
	if s.EndsAt.IsZero() {
 | 
			
		||||
		return fmt.Errorf("end time missing")
 | 
			
		||||
	}
 | 
			
		||||
	if s.EndsAt.Before(s.StartsAt) {
 | 
			
		||||
		return fmt.Errorf("start time must be before end time")
 | 
			
		||||
	}
 | 
			
		||||
	if s.CreatedBy == "" {
 | 
			
		||||
		return fmt.Errorf("creator information missing")
 | 
			
		||||
	}
 | 
			
		||||
	if s.Comment == "" {
 | 
			
		||||
		return fmt.Errorf("comment missing")
 | 
			
		||||
	}
 | 
			
		||||
	if s.CreatedAt.IsZero() {
 | 
			
		||||
		return fmt.Errorf("creation timestamp missing")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										270
									
								
								vendor/github.com/prometheus/common/model/time.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										270
									
								
								vendor/github.com/prometheus/common/model/time.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,270 @@
 | 
			
		||||
// Copyright 2013 The Prometheus Authors
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
// http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"math"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// MinimumTick is the minimum supported time resolution. This has to be
 | 
			
		||||
	// at least time.Second in order for the code below to work.
 | 
			
		||||
	minimumTick = time.Millisecond
 | 
			
		||||
	// second is the Time duration equivalent to one second.
 | 
			
		||||
	second = int64(time.Second / minimumTick)
 | 
			
		||||
	// The number of nanoseconds per minimum tick.
 | 
			
		||||
	nanosPerTick = int64(minimumTick / time.Nanosecond)
 | 
			
		||||
 | 
			
		||||
	// Earliest is the earliest Time representable. Handy for
 | 
			
		||||
	// initializing a high watermark.
 | 
			
		||||
	Earliest = Time(math.MinInt64)
 | 
			
		||||
	// Latest is the latest Time representable. Handy for initializing
 | 
			
		||||
	// a low watermark.
 | 
			
		||||
	Latest = Time(math.MaxInt64)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Time is the number of milliseconds since the epoch
 | 
			
		||||
// (1970-01-01 00:00 UTC) excluding leap seconds.
 | 
			
		||||
type Time int64
 | 
			
		||||
 | 
			
		||||
// Interval describes an interval between two timestamps.
 | 
			
		||||
type Interval struct {
 | 
			
		||||
	Start, End Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Now returns the current time as a Time.
 | 
			
		||||
func Now() Time {
 | 
			
		||||
	return TimeFromUnixNano(time.Now().UnixNano())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TimeFromUnix returns the Time equivalent to the Unix Time t
 | 
			
		||||
// provided in seconds.
 | 
			
		||||
func TimeFromUnix(t int64) Time {
 | 
			
		||||
	return Time(t * second)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TimeFromUnixNano returns the Time equivalent to the Unix Time
 | 
			
		||||
// t provided in nanoseconds.
 | 
			
		||||
func TimeFromUnixNano(t int64) Time {
 | 
			
		||||
	return Time(t / nanosPerTick)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Equal reports whether two Times represent the same instant.
 | 
			
		||||
func (t Time) Equal(o Time) bool {
 | 
			
		||||
	return t == o
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Before reports whether the Time t is before o.
 | 
			
		||||
func (t Time) Before(o Time) bool {
 | 
			
		||||
	return t < o
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// After reports whether the Time t is after o.
 | 
			
		||||
func (t Time) After(o Time) bool {
 | 
			
		||||
	return t > o
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Add returns the Time t + d.
 | 
			
		||||
func (t Time) Add(d time.Duration) Time {
 | 
			
		||||
	return t + Time(d/minimumTick)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sub returns the Duration t - o.
 | 
			
		||||
func (t Time) Sub(o Time) time.Duration {
 | 
			
		||||
	return time.Duration(t-o) * minimumTick
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Time returns the time.Time representation of t.
 | 
			
		||||
func (t Time) Time() time.Time {
 | 
			
		||||
	return time.Unix(int64(t)/second, (int64(t)%second)*nanosPerTick)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Unix returns t as a Unix time, the number of seconds elapsed
 | 
			
		||||
// since January 1, 1970 UTC.
 | 
			
		||||
func (t Time) Unix() int64 {
 | 
			
		||||
	return int64(t) / second
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnixNano returns t as a Unix time, the number of nanoseconds elapsed
 | 
			
		||||
// since January 1, 1970 UTC.
 | 
			
		||||
func (t Time) UnixNano() int64 {
 | 
			
		||||
	return int64(t) * nanosPerTick
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// The number of digits after the dot.
 | 
			
		||||
var dotPrecision = int(math.Log10(float64(second)))
 | 
			
		||||
 | 
			
		||||
// String returns a string representation of the Time.
 | 
			
		||||
func (t Time) String() string {
 | 
			
		||||
	return strconv.FormatFloat(float64(t)/float64(second), 'f', -1, 64)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MarshalJSON implements the json.Marshaler interface.
 | 
			
		||||
func (t Time) MarshalJSON() ([]byte, error) {
 | 
			
		||||
	return []byte(t.String()), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalJSON implements the json.Unmarshaler interface.
 | 
			
		||||
func (t *Time) UnmarshalJSON(b []byte) error {
 | 
			
		||||
	p := strings.Split(string(b), ".")
 | 
			
		||||
	switch len(p) {
 | 
			
		||||
	case 1:
 | 
			
		||||
		v, err := strconv.ParseInt(string(p[0]), 10, 64)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		*t = Time(v * second)
 | 
			
		||||
 | 
			
		||||
	case 2:
 | 
			
		||||
		v, err := strconv.ParseInt(string(p[0]), 10, 64)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		v *= second
 | 
			
		||||
 | 
			
		||||
		prec := dotPrecision - len(p[1])
 | 
			
		||||
		if prec < 0 {
 | 
			
		||||
			p[1] = p[1][:dotPrecision]
 | 
			
		||||
		} else if prec > 0 {
 | 
			
		||||
			p[1] = p[1] + strings.Repeat("0", prec)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		va, err := strconv.ParseInt(p[1], 10, 32)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// If the value was something like -0.1 the negative is lost in the
 | 
			
		||||
		// parsing because of the leading zero, this ensures that we capture it.
 | 
			
		||||
		if len(p[0]) > 0 && p[0][0] == '-' && v+va > 0 {
 | 
			
		||||
			*t = Time(v+va) * -1
 | 
			
		||||
		} else {
 | 
			
		||||
			*t = Time(v + va)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		return fmt.Errorf("invalid time %q", string(b))
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Duration wraps time.Duration. It is used to parse the custom duration format
 | 
			
		||||
// from YAML.
 | 
			
		||||
// This type should not propagate beyond the scope of input/output processing.
 | 
			
		||||
type Duration time.Duration
 | 
			
		||||
 | 
			
		||||
// Set implements pflag/flag.Value
 | 
			
		||||
func (d *Duration) Set(s string) error {
 | 
			
		||||
	var err error
 | 
			
		||||
	*d, err = ParseDuration(s)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Type implements pflag.Value
 | 
			
		||||
func (d *Duration) Type() string {
 | 
			
		||||
	return "duration"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var durationRE = regexp.MustCompile("^([0-9]+)(y|w|d|h|m|s|ms)$")
 | 
			
		||||
 | 
			
		||||
// ParseDuration parses a string into a time.Duration, assuming that a year
 | 
			
		||||
// always has 365d, a week always has 7d, and a day always has 24h.
 | 
			
		||||
func ParseDuration(durationStr string) (Duration, error) {
 | 
			
		||||
	matches := durationRE.FindStringSubmatch(durationStr)
 | 
			
		||||
	if len(matches) != 3 {
 | 
			
		||||
		return 0, fmt.Errorf("not a valid duration string: %q", durationStr)
 | 
			
		||||
	}
 | 
			
		||||
	var (
 | 
			
		||||
		n, _ = strconv.Atoi(matches[1])
 | 
			
		||||
		dur  = time.Duration(n) * time.Millisecond
 | 
			
		||||
	)
 | 
			
		||||
	switch unit := matches[2]; unit {
 | 
			
		||||
	case "y":
 | 
			
		||||
		dur *= 1000 * 60 * 60 * 24 * 365
 | 
			
		||||
	case "w":
 | 
			
		||||
		dur *= 1000 * 60 * 60 * 24 * 7
 | 
			
		||||
	case "d":
 | 
			
		||||
		dur *= 1000 * 60 * 60 * 24
 | 
			
		||||
	case "h":
 | 
			
		||||
		dur *= 1000 * 60 * 60
 | 
			
		||||
	case "m":
 | 
			
		||||
		dur *= 1000 * 60
 | 
			
		||||
	case "s":
 | 
			
		||||
		dur *= 1000
 | 
			
		||||
	case "ms":
 | 
			
		||||
		// Value already correct
 | 
			
		||||
	default:
 | 
			
		||||
		return 0, fmt.Errorf("invalid time unit in duration string: %q", unit)
 | 
			
		||||
	}
 | 
			
		||||
	return Duration(dur), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d Duration) String() string {
 | 
			
		||||
	var (
 | 
			
		||||
		ms   = int64(time.Duration(d) / time.Millisecond)
 | 
			
		||||
		unit = "ms"
 | 
			
		||||
	)
 | 
			
		||||
	if ms == 0 {
 | 
			
		||||
		return "0s"
 | 
			
		||||
	}
 | 
			
		||||
	factors := map[string]int64{
 | 
			
		||||
		"y":  1000 * 60 * 60 * 24 * 365,
 | 
			
		||||
		"w":  1000 * 60 * 60 * 24 * 7,
 | 
			
		||||
		"d":  1000 * 60 * 60 * 24,
 | 
			
		||||
		"h":  1000 * 60 * 60,
 | 
			
		||||
		"m":  1000 * 60,
 | 
			
		||||
		"s":  1000,
 | 
			
		||||
		"ms": 1,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch int64(0) {
 | 
			
		||||
	case ms % factors["y"]:
 | 
			
		||||
		unit = "y"
 | 
			
		||||
	case ms % factors["w"]:
 | 
			
		||||
		unit = "w"
 | 
			
		||||
	case ms % factors["d"]:
 | 
			
		||||
		unit = "d"
 | 
			
		||||
	case ms % factors["h"]:
 | 
			
		||||
		unit = "h"
 | 
			
		||||
	case ms % factors["m"]:
 | 
			
		||||
		unit = "m"
 | 
			
		||||
	case ms % factors["s"]:
 | 
			
		||||
		unit = "s"
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%v%v", ms/factors[unit], unit)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MarshalYAML implements the yaml.Marshaler interface.
 | 
			
		||||
func (d Duration) MarshalYAML() (interface{}, error) {
 | 
			
		||||
	return d.String(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
 | 
			
		||||
func (d *Duration) UnmarshalYAML(unmarshal func(interface{}) error) error {
 | 
			
		||||
	var s string
 | 
			
		||||
	if err := unmarshal(&s); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	dur, err := ParseDuration(s)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	*d = dur
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										416
									
								
								vendor/github.com/prometheus/common/model/value.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										416
									
								
								vendor/github.com/prometheus/common/model/value.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,416 @@
 | 
			
		||||
// Copyright 2013 The Prometheus Authors
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
// http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
package model
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"math"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// ZeroSamplePair is the pseudo zero-value of SamplePair used to signal a
 | 
			
		||||
	// non-existing sample pair. It is a SamplePair with timestamp Earliest and
 | 
			
		||||
	// value 0.0. Note that the natural zero value of SamplePair has a timestamp
 | 
			
		||||
	// of 0, which is possible to appear in a real SamplePair and thus not
 | 
			
		||||
	// suitable to signal a non-existing SamplePair.
 | 
			
		||||
	ZeroSamplePair = SamplePair{Timestamp: Earliest}
 | 
			
		||||
 | 
			
		||||
	// ZeroSample is the pseudo zero-value of Sample used to signal a
 | 
			
		||||
	// non-existing sample. It is a Sample with timestamp Earliest, value 0.0,
 | 
			
		||||
	// and metric nil. Note that the natural zero value of Sample has a timestamp
 | 
			
		||||
	// of 0, which is possible to appear in a real Sample and thus not suitable
 | 
			
		||||
	// to signal a non-existing Sample.
 | 
			
		||||
	ZeroSample = Sample{Timestamp: Earliest}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// A SampleValue is a representation of a value for a given sample at a given
 | 
			
		||||
// time.
 | 
			
		||||
type SampleValue float64
 | 
			
		||||
 | 
			
		||||
// MarshalJSON implements json.Marshaler.
 | 
			
		||||
func (v SampleValue) MarshalJSON() ([]byte, error) {
 | 
			
		||||
	return json.Marshal(v.String())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalJSON implements json.Unmarshaler.
 | 
			
		||||
func (v *SampleValue) UnmarshalJSON(b []byte) error {
 | 
			
		||||
	if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' {
 | 
			
		||||
		return fmt.Errorf("sample value must be a quoted string")
 | 
			
		||||
	}
 | 
			
		||||
	f, err := strconv.ParseFloat(string(b[1:len(b)-1]), 64)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	*v = SampleValue(f)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Equal returns true if the value of v and o is equal or if both are NaN. Note
 | 
			
		||||
// that v==o is false if both are NaN. If you want the conventional float
 | 
			
		||||
// behavior, use == to compare two SampleValues.
 | 
			
		||||
func (v SampleValue) Equal(o SampleValue) bool {
 | 
			
		||||
	if v == o {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return math.IsNaN(float64(v)) && math.IsNaN(float64(o))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v SampleValue) String() string {
 | 
			
		||||
	return strconv.FormatFloat(float64(v), 'f', -1, 64)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SamplePair pairs a SampleValue with a Timestamp.
 | 
			
		||||
type SamplePair struct {
 | 
			
		||||
	Timestamp Time
 | 
			
		||||
	Value     SampleValue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MarshalJSON implements json.Marshaler.
 | 
			
		||||
func (s SamplePair) MarshalJSON() ([]byte, error) {
 | 
			
		||||
	t, err := json.Marshal(s.Timestamp)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	v, err := json.Marshal(s.Value)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalJSON implements json.Unmarshaler.
 | 
			
		||||
func (s *SamplePair) UnmarshalJSON(b []byte) error {
 | 
			
		||||
	v := [...]json.Unmarshaler{&s.Timestamp, &s.Value}
 | 
			
		||||
	return json.Unmarshal(b, &v)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Equal returns true if this SamplePair and o have equal Values and equal
 | 
			
		||||
// Timestamps. The semantics of Value equality is defined by SampleValue.Equal.
 | 
			
		||||
func (s *SamplePair) Equal(o *SamplePair) bool {
 | 
			
		||||
	return s == o || (s.Value.Equal(o.Value) && s.Timestamp.Equal(o.Timestamp))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s SamplePair) String() string {
 | 
			
		||||
	return fmt.Sprintf("%s @[%s]", s.Value, s.Timestamp)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sample is a sample pair associated with a metric.
 | 
			
		||||
type Sample struct {
 | 
			
		||||
	Metric    Metric      `json:"metric"`
 | 
			
		||||
	Value     SampleValue `json:"value"`
 | 
			
		||||
	Timestamp Time        `json:"timestamp"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Equal compares first the metrics, then the timestamp, then the value. The
 | 
			
		||||
// semantics of value equality is defined by SampleValue.Equal.
 | 
			
		||||
func (s *Sample) Equal(o *Sample) bool {
 | 
			
		||||
	if s == o {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !s.Metric.Equal(o.Metric) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	if !s.Timestamp.Equal(o.Timestamp) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return s.Value.Equal(o.Value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s Sample) String() string {
 | 
			
		||||
	return fmt.Sprintf("%s => %s", s.Metric, SamplePair{
 | 
			
		||||
		Timestamp: s.Timestamp,
 | 
			
		||||
		Value:     s.Value,
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MarshalJSON implements json.Marshaler.
 | 
			
		||||
func (s Sample) MarshalJSON() ([]byte, error) {
 | 
			
		||||
	v := struct {
 | 
			
		||||
		Metric Metric     `json:"metric"`
 | 
			
		||||
		Value  SamplePair `json:"value"`
 | 
			
		||||
	}{
 | 
			
		||||
		Metric: s.Metric,
 | 
			
		||||
		Value: SamplePair{
 | 
			
		||||
			Timestamp: s.Timestamp,
 | 
			
		||||
			Value:     s.Value,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return json.Marshal(&v)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalJSON implements json.Unmarshaler.
 | 
			
		||||
func (s *Sample) UnmarshalJSON(b []byte) error {
 | 
			
		||||
	v := struct {
 | 
			
		||||
		Metric Metric     `json:"metric"`
 | 
			
		||||
		Value  SamplePair `json:"value"`
 | 
			
		||||
	}{
 | 
			
		||||
		Metric: s.Metric,
 | 
			
		||||
		Value: SamplePair{
 | 
			
		||||
			Timestamp: s.Timestamp,
 | 
			
		||||
			Value:     s.Value,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := json.Unmarshal(b, &v); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.Metric = v.Metric
 | 
			
		||||
	s.Timestamp = v.Value.Timestamp
 | 
			
		||||
	s.Value = v.Value.Value
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Samples is a sortable Sample slice. It implements sort.Interface.
 | 
			
		||||
type Samples []*Sample
 | 
			
		||||
 | 
			
		||||
func (s Samples) Len() int {
 | 
			
		||||
	return len(s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Less compares first the metrics, then the timestamp.
 | 
			
		||||
func (s Samples) Less(i, j int) bool {
 | 
			
		||||
	switch {
 | 
			
		||||
	case s[i].Metric.Before(s[j].Metric):
 | 
			
		||||
		return true
 | 
			
		||||
	case s[j].Metric.Before(s[i].Metric):
 | 
			
		||||
		return false
 | 
			
		||||
	case s[i].Timestamp.Before(s[j].Timestamp):
 | 
			
		||||
		return true
 | 
			
		||||
	default:
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s Samples) Swap(i, j int) {
 | 
			
		||||
	s[i], s[j] = s[j], s[i]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Equal compares two sets of samples and returns true if they are equal.
 | 
			
		||||
func (s Samples) Equal(o Samples) bool {
 | 
			
		||||
	if len(s) != len(o) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, sample := range s {
 | 
			
		||||
		if !sample.Equal(o[i]) {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SampleStream is a stream of Values belonging to an attached COWMetric.
 | 
			
		||||
type SampleStream struct {
 | 
			
		||||
	Metric Metric       `json:"metric"`
 | 
			
		||||
	Values []SamplePair `json:"values"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ss SampleStream) String() string {
 | 
			
		||||
	vals := make([]string, len(ss.Values))
 | 
			
		||||
	for i, v := range ss.Values {
 | 
			
		||||
		vals[i] = v.String()
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%s =>\n%s", ss.Metric, strings.Join(vals, "\n"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Value is a generic interface for values resulting from a query evaluation.
 | 
			
		||||
type Value interface {
 | 
			
		||||
	Type() ValueType
 | 
			
		||||
	String() string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (Matrix) Type() ValueType  { return ValMatrix }
 | 
			
		||||
func (Vector) Type() ValueType  { return ValVector }
 | 
			
		||||
func (*Scalar) Type() ValueType { return ValScalar }
 | 
			
		||||
func (*String) Type() ValueType { return ValString }
 | 
			
		||||
 | 
			
		||||
type ValueType int
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	ValNone ValueType = iota
 | 
			
		||||
	ValScalar
 | 
			
		||||
	ValVector
 | 
			
		||||
	ValMatrix
 | 
			
		||||
	ValString
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// MarshalJSON implements json.Marshaler.
 | 
			
		||||
func (et ValueType) MarshalJSON() ([]byte, error) {
 | 
			
		||||
	return json.Marshal(et.String())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (et *ValueType) UnmarshalJSON(b []byte) error {
 | 
			
		||||
	var s string
 | 
			
		||||
	if err := json.Unmarshal(b, &s); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	switch s {
 | 
			
		||||
	case "<ValNone>":
 | 
			
		||||
		*et = ValNone
 | 
			
		||||
	case "scalar":
 | 
			
		||||
		*et = ValScalar
 | 
			
		||||
	case "vector":
 | 
			
		||||
		*et = ValVector
 | 
			
		||||
	case "matrix":
 | 
			
		||||
		*et = ValMatrix
 | 
			
		||||
	case "string":
 | 
			
		||||
		*et = ValString
 | 
			
		||||
	default:
 | 
			
		||||
		return fmt.Errorf("unknown value type %q", s)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e ValueType) String() string {
 | 
			
		||||
	switch e {
 | 
			
		||||
	case ValNone:
 | 
			
		||||
		return "<ValNone>"
 | 
			
		||||
	case ValScalar:
 | 
			
		||||
		return "scalar"
 | 
			
		||||
	case ValVector:
 | 
			
		||||
		return "vector"
 | 
			
		||||
	case ValMatrix:
 | 
			
		||||
		return "matrix"
 | 
			
		||||
	case ValString:
 | 
			
		||||
		return "string"
 | 
			
		||||
	}
 | 
			
		||||
	panic("ValueType.String: unhandled value type")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Scalar is a scalar value evaluated at the set timestamp.
 | 
			
		||||
type Scalar struct {
 | 
			
		||||
	Value     SampleValue `json:"value"`
 | 
			
		||||
	Timestamp Time        `json:"timestamp"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s Scalar) String() string {
 | 
			
		||||
	return fmt.Sprintf("scalar: %v @[%v]", s.Value, s.Timestamp)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MarshalJSON implements json.Marshaler.
 | 
			
		||||
func (s Scalar) MarshalJSON() ([]byte, error) {
 | 
			
		||||
	v := strconv.FormatFloat(float64(s.Value), 'f', -1, 64)
 | 
			
		||||
	return json.Marshal([...]interface{}{s.Timestamp, string(v)})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalJSON implements json.Unmarshaler.
 | 
			
		||||
func (s *Scalar) UnmarshalJSON(b []byte) error {
 | 
			
		||||
	var f string
 | 
			
		||||
	v := [...]interface{}{&s.Timestamp, &f}
 | 
			
		||||
 | 
			
		||||
	if err := json.Unmarshal(b, &v); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	value, err := strconv.ParseFloat(f, 64)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("error parsing sample value: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	s.Value = SampleValue(value)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// String is a string value evaluated at the set timestamp.
 | 
			
		||||
type String struct {
 | 
			
		||||
	Value     string `json:"value"`
 | 
			
		||||
	Timestamp Time   `json:"timestamp"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *String) String() string {
 | 
			
		||||
	return s.Value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MarshalJSON implements json.Marshaler.
 | 
			
		||||
func (s String) MarshalJSON() ([]byte, error) {
 | 
			
		||||
	return json.Marshal([]interface{}{s.Timestamp, s.Value})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnmarshalJSON implements json.Unmarshaler.
 | 
			
		||||
func (s *String) UnmarshalJSON(b []byte) error {
 | 
			
		||||
	v := [...]interface{}{&s.Timestamp, &s.Value}
 | 
			
		||||
	return json.Unmarshal(b, &v)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Vector is basically only an alias for Samples, but the
 | 
			
		||||
// contract is that in a Vector, all Samples have the same timestamp.
 | 
			
		||||
type Vector []*Sample
 | 
			
		||||
 | 
			
		||||
func (vec Vector) String() string {
 | 
			
		||||
	entries := make([]string, len(vec))
 | 
			
		||||
	for i, s := range vec {
 | 
			
		||||
		entries[i] = s.String()
 | 
			
		||||
	}
 | 
			
		||||
	return strings.Join(entries, "\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (vec Vector) Len() int      { return len(vec) }
 | 
			
		||||
func (vec Vector) Swap(i, j int) { vec[i], vec[j] = vec[j], vec[i] }
 | 
			
		||||
 | 
			
		||||
// Less compares first the metrics, then the timestamp.
 | 
			
		||||
func (vec Vector) Less(i, j int) bool {
 | 
			
		||||
	switch {
 | 
			
		||||
	case vec[i].Metric.Before(vec[j].Metric):
 | 
			
		||||
		return true
 | 
			
		||||
	case vec[j].Metric.Before(vec[i].Metric):
 | 
			
		||||
		return false
 | 
			
		||||
	case vec[i].Timestamp.Before(vec[j].Timestamp):
 | 
			
		||||
		return true
 | 
			
		||||
	default:
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Equal compares two sets of samples and returns true if they are equal.
 | 
			
		||||
func (vec Vector) Equal(o Vector) bool {
 | 
			
		||||
	if len(vec) != len(o) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, sample := range vec {
 | 
			
		||||
		if !sample.Equal(o[i]) {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Matrix is a list of time series.
 | 
			
		||||
type Matrix []*SampleStream
 | 
			
		||||
 | 
			
		||||
func (m Matrix) Len() int           { return len(m) }
 | 
			
		||||
func (m Matrix) Less(i, j int) bool { return m[i].Metric.Before(m[j].Metric) }
 | 
			
		||||
func (m Matrix) Swap(i, j int)      { m[i], m[j] = m[j], m[i] }
 | 
			
		||||
 | 
			
		||||
func (mat Matrix) String() string {
 | 
			
		||||
	matCp := make(Matrix, len(mat))
 | 
			
		||||
	copy(matCp, mat)
 | 
			
		||||
	sort.Sort(matCp)
 | 
			
		||||
 | 
			
		||||
	strs := make([]string, len(matCp))
 | 
			
		||||
 | 
			
		||||
	for i, ss := range matCp {
 | 
			
		||||
		strs[i] = ss.String()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return strings.Join(strs, "\n")
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user