Initial Commit of Matrix Handler
This commit is contained in:
		
							
								
								
									
										201
									
								
								vendor/github.com/prometheus/client_golang/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								vendor/github.com/prometheus/client_golang/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.
 | 
			
		||||
							
								
								
									
										23
									
								
								vendor/github.com/prometheus/client_golang/NOTICE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								vendor/github.com/prometheus/client_golang/NOTICE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
Prometheus instrumentation library for Go applications
 | 
			
		||||
Copyright 2012-2015 The Prometheus Authors
 | 
			
		||||
 | 
			
		||||
This product includes software developed at
 | 
			
		||||
SoundCloud Ltd. (http://soundcloud.com/).
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
The following components are included in this product:
 | 
			
		||||
 | 
			
		||||
perks - a fork of https://github.com/bmizerany/perks
 | 
			
		||||
https://github.com/beorn7/perks
 | 
			
		||||
Copyright 2013-2015 Blake Mizerany, Björn Rabenstein
 | 
			
		||||
See https://github.com/beorn7/perks/blob/master/README.md for license details.
 | 
			
		||||
 | 
			
		||||
Go support for Protocol Buffers - Google's data interchange format
 | 
			
		||||
http://github.com/golang/protobuf/
 | 
			
		||||
Copyright 2010 The Go Authors
 | 
			
		||||
See source code for license details.
 | 
			
		||||
 | 
			
		||||
Support for streaming Protocol Buffer messages for the Go language (golang).
 | 
			
		||||
https://github.com/matttproud/golang_protobuf_extensions
 | 
			
		||||
Copyright 2013 Matt T. Proud
 | 
			
		||||
Licensed under the Apache License, Version 2.0
 | 
			
		||||
							
								
								
									
										1
									
								
								vendor/github.com/prometheus/client_golang/prometheus/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								vendor/github.com/prometheus/client_golang/prometheus/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
command-line-arguments.test
 | 
			
		||||
							
								
								
									
										1
									
								
								vendor/github.com/prometheus/client_golang/prometheus/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								vendor/github.com/prometheus/client_golang/prometheus/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
See [](https://godoc.org/github.com/prometheus/client_golang/prometheus).
 | 
			
		||||
							
								
								
									
										29
									
								
								vendor/github.com/prometheus/client_golang/prometheus/build_info.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								vendor/github.com/prometheus/client_golang/prometheus/build_info.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
// Copyright 2019 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 go1.12
 | 
			
		||||
 | 
			
		||||
package prometheus
 | 
			
		||||
 | 
			
		||||
import "runtime/debug"
 | 
			
		||||
 | 
			
		||||
// readBuildInfo is a wrapper around debug.ReadBuildInfo for Go 1.12+.
 | 
			
		||||
func readBuildInfo() (path, version, sum string) {
 | 
			
		||||
	path, version, sum = "unknown", "unknown", "unknown"
 | 
			
		||||
	if bi, ok := debug.ReadBuildInfo(); ok {
 | 
			
		||||
		path = bi.Main.Path
 | 
			
		||||
		version = bi.Main.Version
 | 
			
		||||
		sum = bi.Main.Sum
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								vendor/github.com/prometheus/client_golang/prometheus/build_info_pre_1.12.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/prometheus/client_golang/prometheus/build_info_pre_1.12.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
// Copyright 2019 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 !go1.12
 | 
			
		||||
 | 
			
		||||
package prometheus
 | 
			
		||||
 | 
			
		||||
// readBuildInfo is a wrapper around debug.ReadBuildInfo for Go versions before
 | 
			
		||||
// 1.12. Remove this whole file once the minimum supported Go version is 1.12.
 | 
			
		||||
func readBuildInfo() (path, version, sum string) {
 | 
			
		||||
	return "unknown", "unknown", "unknown"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										120
									
								
								vendor/github.com/prometheus/client_golang/prometheus/collector.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								vendor/github.com/prometheus/client_golang/prometheus/collector.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,120 @@
 | 
			
		||||
// 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 prometheus
 | 
			
		||||
 | 
			
		||||
// Collector is the interface implemented by anything that can be used by
 | 
			
		||||
// Prometheus to collect metrics. A Collector has to be registered for
 | 
			
		||||
// collection. See Registerer.Register.
 | 
			
		||||
//
 | 
			
		||||
// The stock metrics provided by this package (Gauge, Counter, Summary,
 | 
			
		||||
// Histogram, Untyped) are also Collectors (which only ever collect one metric,
 | 
			
		||||
// namely itself). An implementer of Collector may, however, collect multiple
 | 
			
		||||
// metrics in a coordinated fashion and/or create metrics on the fly. Examples
 | 
			
		||||
// for collectors already implemented in this library are the metric vectors
 | 
			
		||||
// (i.e. collection of multiple instances of the same Metric but with different
 | 
			
		||||
// label values) like GaugeVec or SummaryVec, and the ExpvarCollector.
 | 
			
		||||
type Collector interface {
 | 
			
		||||
	// Describe sends the super-set of all possible descriptors of metrics
 | 
			
		||||
	// collected by this Collector to the provided channel and returns once
 | 
			
		||||
	// the last descriptor has been sent. The sent descriptors fulfill the
 | 
			
		||||
	// consistency and uniqueness requirements described in the Desc
 | 
			
		||||
	// documentation.
 | 
			
		||||
	//
 | 
			
		||||
	// It is valid if one and the same Collector sends duplicate
 | 
			
		||||
	// descriptors. Those duplicates are simply ignored. However, two
 | 
			
		||||
	// different Collectors must not send duplicate descriptors.
 | 
			
		||||
	//
 | 
			
		||||
	// Sending no descriptor at all marks the Collector as “unchecked”,
 | 
			
		||||
	// i.e. no checks will be performed at registration time, and the
 | 
			
		||||
	// Collector may yield any Metric it sees fit in its Collect method.
 | 
			
		||||
	//
 | 
			
		||||
	// This method idempotently sends the same descriptors throughout the
 | 
			
		||||
	// lifetime of the Collector. It may be called concurrently and
 | 
			
		||||
	// therefore must be implemented in a concurrency safe way.
 | 
			
		||||
	//
 | 
			
		||||
	// If a Collector encounters an error while executing this method, it
 | 
			
		||||
	// must send an invalid descriptor (created with NewInvalidDesc) to
 | 
			
		||||
	// signal the error to the registry.
 | 
			
		||||
	Describe(chan<- *Desc)
 | 
			
		||||
	// Collect is called by the Prometheus registry when collecting
 | 
			
		||||
	// metrics. The implementation sends each collected metric via the
 | 
			
		||||
	// provided channel and returns once the last metric has been sent. The
 | 
			
		||||
	// descriptor of each sent metric is one of those returned by Describe
 | 
			
		||||
	// (unless the Collector is unchecked, see above). Returned metrics that
 | 
			
		||||
	// share the same descriptor must differ in their variable label
 | 
			
		||||
	// values.
 | 
			
		||||
	//
 | 
			
		||||
	// This method may be called concurrently and must therefore be
 | 
			
		||||
	// implemented in a concurrency safe way. Blocking occurs at the expense
 | 
			
		||||
	// of total performance of rendering all registered metrics. Ideally,
 | 
			
		||||
	// Collector implementations support concurrent readers.
 | 
			
		||||
	Collect(chan<- Metric)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DescribeByCollect is a helper to implement the Describe method of a custom
 | 
			
		||||
// Collector. It collects the metrics from the provided Collector and sends
 | 
			
		||||
// their descriptors to the provided channel.
 | 
			
		||||
//
 | 
			
		||||
// If a Collector collects the same metrics throughout its lifetime, its
 | 
			
		||||
// Describe method can simply be implemented as:
 | 
			
		||||
//
 | 
			
		||||
//   func (c customCollector) Describe(ch chan<- *Desc) {
 | 
			
		||||
//   	DescribeByCollect(c, ch)
 | 
			
		||||
//   }
 | 
			
		||||
//
 | 
			
		||||
// However, this will not work if the metrics collected change dynamically over
 | 
			
		||||
// the lifetime of the Collector in a way that their combined set of descriptors
 | 
			
		||||
// changes as well. The shortcut implementation will then violate the contract
 | 
			
		||||
// of the Describe method. If a Collector sometimes collects no metrics at all
 | 
			
		||||
// (for example vectors like CounterVec, GaugeVec, etc., which only collect
 | 
			
		||||
// metrics after a metric with a fully specified label set has been accessed),
 | 
			
		||||
// it might even get registered as an unchecked Collector (cf. the Register
 | 
			
		||||
// method of the Registerer interface). Hence, only use this shortcut
 | 
			
		||||
// implementation of Describe if you are certain to fulfill the contract.
 | 
			
		||||
//
 | 
			
		||||
// The Collector example demonstrates a use of DescribeByCollect.
 | 
			
		||||
func DescribeByCollect(c Collector, descs chan<- *Desc) {
 | 
			
		||||
	metrics := make(chan Metric)
 | 
			
		||||
	go func() {
 | 
			
		||||
		c.Collect(metrics)
 | 
			
		||||
		close(metrics)
 | 
			
		||||
	}()
 | 
			
		||||
	for m := range metrics {
 | 
			
		||||
		descs <- m.Desc()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// selfCollector implements Collector for a single Metric so that the Metric
 | 
			
		||||
// collects itself. Add it as an anonymous field to a struct that implements
 | 
			
		||||
// Metric, and call init with the Metric itself as an argument.
 | 
			
		||||
type selfCollector struct {
 | 
			
		||||
	self Metric
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// init provides the selfCollector with a reference to the metric it is supposed
 | 
			
		||||
// to collect. It is usually called within the factory function to create a
 | 
			
		||||
// metric. See example.
 | 
			
		||||
func (c *selfCollector) init(self Metric) {
 | 
			
		||||
	c.self = self
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Describe implements Collector.
 | 
			
		||||
func (c *selfCollector) Describe(ch chan<- *Desc) {
 | 
			
		||||
	ch <- c.self.Desc()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Collect implements Collector.
 | 
			
		||||
func (c *selfCollector) Collect(ch chan<- Metric) {
 | 
			
		||||
	ch <- c.self
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										319
									
								
								vendor/github.com/prometheus/client_golang/prometheus/counter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										319
									
								
								vendor/github.com/prometheus/client_golang/prometheus/counter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,319 @@
 | 
			
		||||
// 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 prometheus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"math"
 | 
			
		||||
	"sync/atomic"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	dto "github.com/prometheus/client_model/go"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Counter is a Metric that represents a single numerical value that only ever
 | 
			
		||||
// goes up. That implies that it cannot be used to count items whose number can
 | 
			
		||||
// also go down, e.g. the number of currently running goroutines. Those
 | 
			
		||||
// "counters" are represented by Gauges.
 | 
			
		||||
//
 | 
			
		||||
// A Counter is typically used to count requests served, tasks completed, errors
 | 
			
		||||
// occurred, etc.
 | 
			
		||||
//
 | 
			
		||||
// To create Counter instances, use NewCounter.
 | 
			
		||||
type Counter interface {
 | 
			
		||||
	Metric
 | 
			
		||||
	Collector
 | 
			
		||||
 | 
			
		||||
	// Inc increments the counter by 1. Use Add to increment it by arbitrary
 | 
			
		||||
	// non-negative values.
 | 
			
		||||
	Inc()
 | 
			
		||||
	// Add adds the given value to the counter. It panics if the value is <
 | 
			
		||||
	// 0.
 | 
			
		||||
	Add(float64)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExemplarAdder is implemented by Counters that offer the option of adding a
 | 
			
		||||
// value to the Counter together with an exemplar. Its AddWithExemplar method
 | 
			
		||||
// works like the Add method of the Counter interface but also replaces the
 | 
			
		||||
// currently saved exemplar (if any) with a new one, created from the provided
 | 
			
		||||
// value, the current time as timestamp, and the provided labels. Empty Labels
 | 
			
		||||
// will lead to a valid (label-less) exemplar. But if Labels is nil, the current
 | 
			
		||||
// exemplar is left in place. AddWithExemplar panics if the value is < 0, if any
 | 
			
		||||
// of the provided labels are invalid, or if the provided labels contain more
 | 
			
		||||
// than 64 runes in total.
 | 
			
		||||
type ExemplarAdder interface {
 | 
			
		||||
	AddWithExemplar(value float64, exemplar Labels)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CounterOpts is an alias for Opts. See there for doc comments.
 | 
			
		||||
type CounterOpts Opts
 | 
			
		||||
 | 
			
		||||
// NewCounter creates a new Counter based on the provided CounterOpts.
 | 
			
		||||
//
 | 
			
		||||
// The returned implementation also implements ExemplarAdder. It is safe to
 | 
			
		||||
// perform the corresponding type assertion.
 | 
			
		||||
//
 | 
			
		||||
// The returned implementation tracks the counter value in two separate
 | 
			
		||||
// variables, a float64 and a uint64. The latter is used to track calls of the
 | 
			
		||||
// Inc method and calls of the Add method with a value that can be represented
 | 
			
		||||
// as a uint64. This allows atomic increments of the counter with optimal
 | 
			
		||||
// performance. (It is common to have an Inc call in very hot execution paths.)
 | 
			
		||||
// Both internal tracking values are added up in the Write method. This has to
 | 
			
		||||
// be taken into account when it comes to precision and overflow behavior.
 | 
			
		||||
func NewCounter(opts CounterOpts) Counter {
 | 
			
		||||
	desc := NewDesc(
 | 
			
		||||
		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
 | 
			
		||||
		opts.Help,
 | 
			
		||||
		nil,
 | 
			
		||||
		opts.ConstLabels,
 | 
			
		||||
	)
 | 
			
		||||
	result := &counter{desc: desc, labelPairs: desc.constLabelPairs, now: time.Now}
 | 
			
		||||
	result.init(result) // Init self-collection.
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type counter struct {
 | 
			
		||||
	// valBits contains the bits of the represented float64 value, while
 | 
			
		||||
	// valInt stores values that are exact integers. Both have to go first
 | 
			
		||||
	// in the struct to guarantee alignment for atomic operations.
 | 
			
		||||
	// http://golang.org/pkg/sync/atomic/#pkg-note-BUG
 | 
			
		||||
	valBits uint64
 | 
			
		||||
	valInt  uint64
 | 
			
		||||
 | 
			
		||||
	selfCollector
 | 
			
		||||
	desc *Desc
 | 
			
		||||
 | 
			
		||||
	labelPairs []*dto.LabelPair
 | 
			
		||||
	exemplar   atomic.Value // Containing nil or a *dto.Exemplar.
 | 
			
		||||
 | 
			
		||||
	now func() time.Time // To mock out time.Now() for testing.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *counter) Desc() *Desc {
 | 
			
		||||
	return c.desc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *counter) Add(v float64) {
 | 
			
		||||
	if v < 0 {
 | 
			
		||||
		panic(errors.New("counter cannot decrease in value"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ival := uint64(v)
 | 
			
		||||
	if float64(ival) == v {
 | 
			
		||||
		atomic.AddUint64(&c.valInt, ival)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		oldBits := atomic.LoadUint64(&c.valBits)
 | 
			
		||||
		newBits := math.Float64bits(math.Float64frombits(oldBits) + v)
 | 
			
		||||
		if atomic.CompareAndSwapUint64(&c.valBits, oldBits, newBits) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *counter) AddWithExemplar(v float64, e Labels) {
 | 
			
		||||
	c.Add(v)
 | 
			
		||||
	c.updateExemplar(v, e)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *counter) Inc() {
 | 
			
		||||
	atomic.AddUint64(&c.valInt, 1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *counter) Write(out *dto.Metric) error {
 | 
			
		||||
	fval := math.Float64frombits(atomic.LoadUint64(&c.valBits))
 | 
			
		||||
	ival := atomic.LoadUint64(&c.valInt)
 | 
			
		||||
	val := fval + float64(ival)
 | 
			
		||||
 | 
			
		||||
	var exemplar *dto.Exemplar
 | 
			
		||||
	if e := c.exemplar.Load(); e != nil {
 | 
			
		||||
		exemplar = e.(*dto.Exemplar)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return populateMetric(CounterValue, val, c.labelPairs, exemplar, out)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *counter) updateExemplar(v float64, l Labels) {
 | 
			
		||||
	if l == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	e, err := newExemplar(v, c.now(), l)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	c.exemplar.Store(e)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CounterVec is a Collector that bundles a set of Counters that all share the
 | 
			
		||||
// same Desc, but have different values for their variable labels. This is used
 | 
			
		||||
// if you want to count the same thing partitioned by various dimensions
 | 
			
		||||
// (e.g. number of HTTP requests, partitioned by response code and
 | 
			
		||||
// method). Create instances with NewCounterVec.
 | 
			
		||||
type CounterVec struct {
 | 
			
		||||
	*metricVec
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewCounterVec creates a new CounterVec based on the provided CounterOpts and
 | 
			
		||||
// partitioned by the given label names.
 | 
			
		||||
func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec {
 | 
			
		||||
	desc := NewDesc(
 | 
			
		||||
		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
 | 
			
		||||
		opts.Help,
 | 
			
		||||
		labelNames,
 | 
			
		||||
		opts.ConstLabels,
 | 
			
		||||
	)
 | 
			
		||||
	return &CounterVec{
 | 
			
		||||
		metricVec: newMetricVec(desc, func(lvs ...string) Metric {
 | 
			
		||||
			if len(lvs) != len(desc.variableLabels) {
 | 
			
		||||
				panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs))
 | 
			
		||||
			}
 | 
			
		||||
			result := &counter{desc: desc, labelPairs: makeLabelPairs(desc, lvs), now: time.Now}
 | 
			
		||||
			result.init(result) // Init self-collection.
 | 
			
		||||
			return result
 | 
			
		||||
		}),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetMetricWithLabelValues returns the Counter for the given slice of label
 | 
			
		||||
// values (same order as the VariableLabels in Desc). If that combination of
 | 
			
		||||
// label values is accessed for the first time, a new Counter is created.
 | 
			
		||||
//
 | 
			
		||||
// It is possible to call this method without using the returned Counter to only
 | 
			
		||||
// create the new Counter but leave it at its starting value 0. See also the
 | 
			
		||||
// SummaryVec example.
 | 
			
		||||
//
 | 
			
		||||
// Keeping the Counter for later use is possible (and should be considered if
 | 
			
		||||
// performance is critical), but keep in mind that Reset, DeleteLabelValues and
 | 
			
		||||
// Delete can be used to delete the Counter from the CounterVec. In that case,
 | 
			
		||||
// the Counter will still exist, but it will not be exported anymore, even if a
 | 
			
		||||
// Counter with the same label values is created later.
 | 
			
		||||
//
 | 
			
		||||
// An error is returned if the number of label values is not the same as the
 | 
			
		||||
// number of VariableLabels in Desc (minus any curried labels).
 | 
			
		||||
//
 | 
			
		||||
// Note that for more than one label value, this method is prone to mistakes
 | 
			
		||||
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
 | 
			
		||||
// an alternative to avoid that type of mistake. For higher label numbers, the
 | 
			
		||||
// latter has a much more readable (albeit more verbose) syntax, but it comes
 | 
			
		||||
// with a performance overhead (for creating and processing the Labels map).
 | 
			
		||||
// See also the GaugeVec example.
 | 
			
		||||
func (v *CounterVec) GetMetricWithLabelValues(lvs ...string) (Counter, error) {
 | 
			
		||||
	metric, err := v.metricVec.getMetricWithLabelValues(lvs...)
 | 
			
		||||
	if metric != nil {
 | 
			
		||||
		return metric.(Counter), err
 | 
			
		||||
	}
 | 
			
		||||
	return nil, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetMetricWith returns the Counter for the given Labels map (the label names
 | 
			
		||||
// must match those of the VariableLabels in Desc). If that label map is
 | 
			
		||||
// accessed for the first time, a new Counter is created. Implications of
 | 
			
		||||
// creating a Counter without using it and keeping the Counter for later use are
 | 
			
		||||
// the same as for GetMetricWithLabelValues.
 | 
			
		||||
//
 | 
			
		||||
// An error is returned if the number and names of the Labels are inconsistent
 | 
			
		||||
// with those of the VariableLabels in Desc (minus any curried labels).
 | 
			
		||||
//
 | 
			
		||||
// This method is used for the same purpose as
 | 
			
		||||
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
 | 
			
		||||
// methods.
 | 
			
		||||
func (v *CounterVec) GetMetricWith(labels Labels) (Counter, error) {
 | 
			
		||||
	metric, err := v.metricVec.getMetricWith(labels)
 | 
			
		||||
	if metric != nil {
 | 
			
		||||
		return metric.(Counter), err
 | 
			
		||||
	}
 | 
			
		||||
	return nil, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithLabelValues works as GetMetricWithLabelValues, but panics where
 | 
			
		||||
// GetMetricWithLabelValues would have returned an error. Not returning an
 | 
			
		||||
// error allows shortcuts like
 | 
			
		||||
//     myVec.WithLabelValues("404", "GET").Add(42)
 | 
			
		||||
func (v *CounterVec) WithLabelValues(lvs ...string) Counter {
 | 
			
		||||
	c, err := v.GetMetricWithLabelValues(lvs...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	return c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// With works as GetMetricWith, but panics where GetMetricWithLabels would have
 | 
			
		||||
// returned an error. Not returning an error allows shortcuts like
 | 
			
		||||
//     myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Add(42)
 | 
			
		||||
func (v *CounterVec) With(labels Labels) Counter {
 | 
			
		||||
	c, err := v.GetMetricWith(labels)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	return c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CurryWith returns a vector curried with the provided labels, i.e. the
 | 
			
		||||
// returned vector has those labels pre-set for all labeled operations performed
 | 
			
		||||
// on it. The cardinality of the curried vector is reduced accordingly. The
 | 
			
		||||
// order of the remaining labels stays the same (just with the curried labels
 | 
			
		||||
// taken out of the sequence – which is relevant for the
 | 
			
		||||
// (GetMetric)WithLabelValues methods). It is possible to curry a curried
 | 
			
		||||
// vector, but only with labels not yet used for currying before.
 | 
			
		||||
//
 | 
			
		||||
// The metrics contained in the CounterVec are shared between the curried and
 | 
			
		||||
// uncurried vectors. They are just accessed differently. Curried and uncurried
 | 
			
		||||
// vectors behave identically in terms of collection. Only one must be
 | 
			
		||||
// registered with a given registry (usually the uncurried version). The Reset
 | 
			
		||||
// method deletes all metrics, even if called on a curried vector.
 | 
			
		||||
func (v *CounterVec) CurryWith(labels Labels) (*CounterVec, error) {
 | 
			
		||||
	vec, err := v.curryWith(labels)
 | 
			
		||||
	if vec != nil {
 | 
			
		||||
		return &CounterVec{vec}, err
 | 
			
		||||
	}
 | 
			
		||||
	return nil, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MustCurryWith works as CurryWith but panics where CurryWith would have
 | 
			
		||||
// returned an error.
 | 
			
		||||
func (v *CounterVec) MustCurryWith(labels Labels) *CounterVec {
 | 
			
		||||
	vec, err := v.CurryWith(labels)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	return vec
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CounterFunc is a Counter whose value is determined at collect time by calling a
 | 
			
		||||
// provided function.
 | 
			
		||||
//
 | 
			
		||||
// To create CounterFunc instances, use NewCounterFunc.
 | 
			
		||||
type CounterFunc interface {
 | 
			
		||||
	Metric
 | 
			
		||||
	Collector
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewCounterFunc creates a new CounterFunc based on the provided
 | 
			
		||||
// CounterOpts. The value reported is determined by calling the given function
 | 
			
		||||
// from within the Write method. Take into account that metric collection may
 | 
			
		||||
// happen concurrently. If that results in concurrent calls to Write, like in
 | 
			
		||||
// the case where a CounterFunc is directly registered with Prometheus, the
 | 
			
		||||
// provided function must be concurrency-safe. The function should also honor
 | 
			
		||||
// the contract for a Counter (values only go up, not down), but compliance will
 | 
			
		||||
// not be checked.
 | 
			
		||||
func NewCounterFunc(opts CounterOpts, function func() float64) CounterFunc {
 | 
			
		||||
	return newValueFunc(NewDesc(
 | 
			
		||||
		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
 | 
			
		||||
		opts.Help,
 | 
			
		||||
		nil,
 | 
			
		||||
		opts.ConstLabels,
 | 
			
		||||
	), CounterValue, function)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										185
									
								
								vendor/github.com/prometheus/client_golang/prometheus/desc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								vendor/github.com/prometheus/client_golang/prometheus/desc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,185 @@
 | 
			
		||||
// Copyright 2016 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 prometheus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/cespare/xxhash/v2"
 | 
			
		||||
	"github.com/golang/protobuf/proto"
 | 
			
		||||
	"github.com/prometheus/common/model"
 | 
			
		||||
 | 
			
		||||
	dto "github.com/prometheus/client_model/go"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Desc is the descriptor used by every Prometheus Metric. It is essentially
 | 
			
		||||
// the immutable meta-data of a Metric. The normal Metric implementations
 | 
			
		||||
// included in this package manage their Desc under the hood. Users only have to
 | 
			
		||||
// deal with Desc if they use advanced features like the ExpvarCollector or
 | 
			
		||||
// custom Collectors and Metrics.
 | 
			
		||||
//
 | 
			
		||||
// Descriptors registered with the same registry have to fulfill certain
 | 
			
		||||
// consistency and uniqueness criteria if they share the same fully-qualified
 | 
			
		||||
// name: They must have the same help string and the same label names (aka label
 | 
			
		||||
// dimensions) in each, constLabels and variableLabels, but they must differ in
 | 
			
		||||
// the values of the constLabels.
 | 
			
		||||
//
 | 
			
		||||
// Descriptors that share the same fully-qualified names and the same label
 | 
			
		||||
// values of their constLabels are considered equal.
 | 
			
		||||
//
 | 
			
		||||
// Use NewDesc to create new Desc instances.
 | 
			
		||||
type Desc struct {
 | 
			
		||||
	// fqName has been built from Namespace, Subsystem, and Name.
 | 
			
		||||
	fqName string
 | 
			
		||||
	// help provides some helpful information about this metric.
 | 
			
		||||
	help string
 | 
			
		||||
	// constLabelPairs contains precalculated DTO label pairs based on
 | 
			
		||||
	// the constant labels.
 | 
			
		||||
	constLabelPairs []*dto.LabelPair
 | 
			
		||||
	// VariableLabels contains names of labels for which the metric
 | 
			
		||||
	// maintains variable values.
 | 
			
		||||
	variableLabels []string
 | 
			
		||||
	// id is a hash of the values of the ConstLabels and fqName. This
 | 
			
		||||
	// must be unique among all registered descriptors and can therefore be
 | 
			
		||||
	// used as an identifier of the descriptor.
 | 
			
		||||
	id uint64
 | 
			
		||||
	// dimHash is a hash of the label names (preset and variable) and the
 | 
			
		||||
	// Help string. Each Desc with the same fqName must have the same
 | 
			
		||||
	// dimHash.
 | 
			
		||||
	dimHash uint64
 | 
			
		||||
	// err is an error that occurred during construction. It is reported on
 | 
			
		||||
	// registration time.
 | 
			
		||||
	err error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewDesc allocates and initializes a new Desc. Errors are recorded in the Desc
 | 
			
		||||
// and will be reported on registration time. variableLabels and constLabels can
 | 
			
		||||
// be nil if no such labels should be set. fqName must not be empty.
 | 
			
		||||
//
 | 
			
		||||
// variableLabels only contain the label names. Their label values are variable
 | 
			
		||||
// and therefore not part of the Desc. (They are managed within the Metric.)
 | 
			
		||||
//
 | 
			
		||||
// For constLabels, the label values are constant. Therefore, they are fully
 | 
			
		||||
// specified in the Desc. See the Collector example for a usage pattern.
 | 
			
		||||
func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *Desc {
 | 
			
		||||
	d := &Desc{
 | 
			
		||||
		fqName:         fqName,
 | 
			
		||||
		help:           help,
 | 
			
		||||
		variableLabels: variableLabels,
 | 
			
		||||
	}
 | 
			
		||||
	if !model.IsValidMetricName(model.LabelValue(fqName)) {
 | 
			
		||||
		d.err = fmt.Errorf("%q is not a valid metric name", fqName)
 | 
			
		||||
		return d
 | 
			
		||||
	}
 | 
			
		||||
	// labelValues contains the label values of const labels (in order of
 | 
			
		||||
	// their sorted label names) plus the fqName (at position 0).
 | 
			
		||||
	labelValues := make([]string, 1, len(constLabels)+1)
 | 
			
		||||
	labelValues[0] = fqName
 | 
			
		||||
	labelNames := make([]string, 0, len(constLabels)+len(variableLabels))
 | 
			
		||||
	labelNameSet := map[string]struct{}{}
 | 
			
		||||
	// First add only the const label names and sort them...
 | 
			
		||||
	for labelName := range constLabels {
 | 
			
		||||
		if !checkLabelName(labelName) {
 | 
			
		||||
			d.err = fmt.Errorf("%q is not a valid label name for metric %q", labelName, fqName)
 | 
			
		||||
			return d
 | 
			
		||||
		}
 | 
			
		||||
		labelNames = append(labelNames, labelName)
 | 
			
		||||
		labelNameSet[labelName] = struct{}{}
 | 
			
		||||
	}
 | 
			
		||||
	sort.Strings(labelNames)
 | 
			
		||||
	// ... so that we can now add const label values in the order of their names.
 | 
			
		||||
	for _, labelName := range labelNames {
 | 
			
		||||
		labelValues = append(labelValues, constLabels[labelName])
 | 
			
		||||
	}
 | 
			
		||||
	// Validate the const label values. They can't have a wrong cardinality, so
 | 
			
		||||
	// use in len(labelValues) as expectedNumberOfValues.
 | 
			
		||||
	if err := validateLabelValues(labelValues, len(labelValues)); err != nil {
 | 
			
		||||
		d.err = err
 | 
			
		||||
		return d
 | 
			
		||||
	}
 | 
			
		||||
	// Now add the variable label names, but prefix them with something that
 | 
			
		||||
	// cannot be in a regular label name. That prevents matching the label
 | 
			
		||||
	// dimension with a different mix between preset and variable labels.
 | 
			
		||||
	for _, labelName := range variableLabels {
 | 
			
		||||
		if !checkLabelName(labelName) {
 | 
			
		||||
			d.err = fmt.Errorf("%q is not a valid label name for metric %q", labelName, fqName)
 | 
			
		||||
			return d
 | 
			
		||||
		}
 | 
			
		||||
		labelNames = append(labelNames, "$"+labelName)
 | 
			
		||||
		labelNameSet[labelName] = struct{}{}
 | 
			
		||||
	}
 | 
			
		||||
	if len(labelNames) != len(labelNameSet) {
 | 
			
		||||
		d.err = errors.New("duplicate label names")
 | 
			
		||||
		return d
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	xxh := xxhash.New()
 | 
			
		||||
	for _, val := range labelValues {
 | 
			
		||||
		xxh.WriteString(val)
 | 
			
		||||
		xxh.Write(separatorByteSlice)
 | 
			
		||||
	}
 | 
			
		||||
	d.id = xxh.Sum64()
 | 
			
		||||
	// Sort labelNames so that order doesn't matter for the hash.
 | 
			
		||||
	sort.Strings(labelNames)
 | 
			
		||||
	// Now hash together (in this order) the help string and the sorted
 | 
			
		||||
	// label names.
 | 
			
		||||
	xxh.Reset()
 | 
			
		||||
	xxh.WriteString(help)
 | 
			
		||||
	xxh.Write(separatorByteSlice)
 | 
			
		||||
	for _, labelName := range labelNames {
 | 
			
		||||
		xxh.WriteString(labelName)
 | 
			
		||||
		xxh.Write(separatorByteSlice)
 | 
			
		||||
	}
 | 
			
		||||
	d.dimHash = xxh.Sum64()
 | 
			
		||||
 | 
			
		||||
	d.constLabelPairs = make([]*dto.LabelPair, 0, len(constLabels))
 | 
			
		||||
	for n, v := range constLabels {
 | 
			
		||||
		d.constLabelPairs = append(d.constLabelPairs, &dto.LabelPair{
 | 
			
		||||
			Name:  proto.String(n),
 | 
			
		||||
			Value: proto.String(v),
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	sort.Sort(labelPairSorter(d.constLabelPairs))
 | 
			
		||||
	return d
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewInvalidDesc returns an invalid descriptor, i.e. a descriptor with the
 | 
			
		||||
// provided error set. If a collector returning such a descriptor is registered,
 | 
			
		||||
// registration will fail with the provided error. NewInvalidDesc can be used by
 | 
			
		||||
// a Collector to signal inability to describe itself.
 | 
			
		||||
func NewInvalidDesc(err error) *Desc {
 | 
			
		||||
	return &Desc{
 | 
			
		||||
		err: err,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *Desc) String() string {
 | 
			
		||||
	lpStrings := make([]string, 0, len(d.constLabelPairs))
 | 
			
		||||
	for _, lp := range d.constLabelPairs {
 | 
			
		||||
		lpStrings = append(
 | 
			
		||||
			lpStrings,
 | 
			
		||||
			fmt.Sprintf("%s=%q", lp.GetName(), lp.GetValue()),
 | 
			
		||||
		)
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf(
 | 
			
		||||
		"Desc{fqName: %q, help: %q, constLabels: {%s}, variableLabels: %v}",
 | 
			
		||||
		d.fqName,
 | 
			
		||||
		d.help,
 | 
			
		||||
		strings.Join(lpStrings, ","),
 | 
			
		||||
		d.variableLabels,
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										199
									
								
								vendor/github.com/prometheus/client_golang/prometheus/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										199
									
								
								vendor/github.com/prometheus/client_golang/prometheus/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,199 @@
 | 
			
		||||
// 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 prometheus is the core instrumentation package. It provides metrics
 | 
			
		||||
// primitives to instrument code for monitoring. It also offers a registry for
 | 
			
		||||
// metrics. Sub-packages allow to expose the registered metrics via HTTP
 | 
			
		||||
// (package promhttp) or push them to a Pushgateway (package push). There is
 | 
			
		||||
// also a sub-package promauto, which provides metrics constructors with
 | 
			
		||||
// automatic registration.
 | 
			
		||||
//
 | 
			
		||||
// All exported functions and methods are safe to be used concurrently unless
 | 
			
		||||
// specified otherwise.
 | 
			
		||||
//
 | 
			
		||||
// A Basic Example
 | 
			
		||||
//
 | 
			
		||||
// As a starting point, a very basic usage example:
 | 
			
		||||
//
 | 
			
		||||
//    package main
 | 
			
		||||
//
 | 
			
		||||
//    import (
 | 
			
		||||
//    	"log"
 | 
			
		||||
//    	"net/http"
 | 
			
		||||
//
 | 
			
		||||
//    	"github.com/prometheus/client_golang/prometheus"
 | 
			
		||||
//    	"github.com/prometheus/client_golang/prometheus/promhttp"
 | 
			
		||||
//    )
 | 
			
		||||
//
 | 
			
		||||
//    var (
 | 
			
		||||
//    	cpuTemp = prometheus.NewGauge(prometheus.GaugeOpts{
 | 
			
		||||
//    		Name: "cpu_temperature_celsius",
 | 
			
		||||
//    		Help: "Current temperature of the CPU.",
 | 
			
		||||
//    	})
 | 
			
		||||
//    	hdFailures = prometheus.NewCounterVec(
 | 
			
		||||
//    		prometheus.CounterOpts{
 | 
			
		||||
//    			Name: "hd_errors_total",
 | 
			
		||||
//    			Help: "Number of hard-disk errors.",
 | 
			
		||||
//    		},
 | 
			
		||||
//    		[]string{"device"},
 | 
			
		||||
//    	)
 | 
			
		||||
//    )
 | 
			
		||||
//
 | 
			
		||||
//    func init() {
 | 
			
		||||
//    	// Metrics have to be registered to be exposed:
 | 
			
		||||
//    	prometheus.MustRegister(cpuTemp)
 | 
			
		||||
//    	prometheus.MustRegister(hdFailures)
 | 
			
		||||
//    }
 | 
			
		||||
//
 | 
			
		||||
//    func main() {
 | 
			
		||||
//    	cpuTemp.Set(65.3)
 | 
			
		||||
//    	hdFailures.With(prometheus.Labels{"device":"/dev/sda"}).Inc()
 | 
			
		||||
//
 | 
			
		||||
//    	// The Handler function provides a default handler to expose metrics
 | 
			
		||||
//    	// via an HTTP server. "/metrics" is the usual endpoint for that.
 | 
			
		||||
//    	http.Handle("/metrics", promhttp.Handler())
 | 
			
		||||
//    	log.Fatal(http.ListenAndServe(":8080", nil))
 | 
			
		||||
//    }
 | 
			
		||||
//
 | 
			
		||||
//
 | 
			
		||||
// This is a complete program that exports two metrics, a Gauge and a Counter,
 | 
			
		||||
// the latter with a label attached to turn it into a (one-dimensional) vector.
 | 
			
		||||
//
 | 
			
		||||
// Metrics
 | 
			
		||||
//
 | 
			
		||||
// The number of exported identifiers in this package might appear a bit
 | 
			
		||||
// overwhelming. However, in addition to the basic plumbing shown in the example
 | 
			
		||||
// above, you only need to understand the different metric types and their
 | 
			
		||||
// vector versions for basic usage. Furthermore, if you are not concerned with
 | 
			
		||||
// fine-grained control of when and how to register metrics with the registry,
 | 
			
		||||
// have a look at the promauto package, which will effectively allow you to
 | 
			
		||||
// ignore registration altogether in simple cases.
 | 
			
		||||
//
 | 
			
		||||
// Above, you have already touched the Counter and the Gauge. There are two more
 | 
			
		||||
// advanced metric types: the Summary and Histogram. A more thorough description
 | 
			
		||||
// of those four metric types can be found in the Prometheus docs:
 | 
			
		||||
// https://prometheus.io/docs/concepts/metric_types/
 | 
			
		||||
//
 | 
			
		||||
// In addition to the fundamental metric types Gauge, Counter, Summary, and
 | 
			
		||||
// Histogram, a very important part of the Prometheus data model is the
 | 
			
		||||
// partitioning of samples along dimensions called labels, which results in
 | 
			
		||||
// metric vectors. The fundamental types are GaugeVec, CounterVec, SummaryVec,
 | 
			
		||||
// and HistogramVec.
 | 
			
		||||
//
 | 
			
		||||
// While only the fundamental metric types implement the Metric interface, both
 | 
			
		||||
// the metrics and their vector versions implement the Collector interface. A
 | 
			
		||||
// Collector manages the collection of a number of Metrics, but for convenience,
 | 
			
		||||
// a Metric can also “collect itself”. Note that Gauge, Counter, Summary, and
 | 
			
		||||
// Histogram are interfaces themselves while GaugeVec, CounterVec, SummaryVec,
 | 
			
		||||
// and HistogramVec are not.
 | 
			
		||||
//
 | 
			
		||||
// To create instances of Metrics and their vector versions, you need a suitable
 | 
			
		||||
// …Opts struct, i.e. GaugeOpts, CounterOpts, SummaryOpts, or HistogramOpts.
 | 
			
		||||
//
 | 
			
		||||
// Custom Collectors and constant Metrics
 | 
			
		||||
//
 | 
			
		||||
// While you could create your own implementations of Metric, most likely you
 | 
			
		||||
// will only ever implement the Collector interface on your own. At a first
 | 
			
		||||
// glance, a custom Collector seems handy to bundle Metrics for common
 | 
			
		||||
// registration (with the prime example of the different metric vectors above,
 | 
			
		||||
// which bundle all the metrics of the same name but with different labels).
 | 
			
		||||
//
 | 
			
		||||
// There is a more involved use case, too: If you already have metrics
 | 
			
		||||
// available, created outside of the Prometheus context, you don't need the
 | 
			
		||||
// interface of the various Metric types. You essentially want to mirror the
 | 
			
		||||
// existing numbers into Prometheus Metrics during collection. An own
 | 
			
		||||
// implementation of the Collector interface is perfect for that. You can create
 | 
			
		||||
// Metric instances “on the fly” using NewConstMetric, NewConstHistogram, and
 | 
			
		||||
// NewConstSummary (and their respective Must… versions). NewConstMetric is used
 | 
			
		||||
// for all metric types with just a float64 as their value: Counter, Gauge, and
 | 
			
		||||
// a special “type” called Untyped. Use the latter if you are not sure if the
 | 
			
		||||
// mirrored metric is a Counter or a Gauge. Creation of the Metric instance
 | 
			
		||||
// happens in the Collect method. The Describe method has to return separate
 | 
			
		||||
// Desc instances, representative of the “throw-away” metrics to be created
 | 
			
		||||
// later.  NewDesc comes in handy to create those Desc instances. Alternatively,
 | 
			
		||||
// you could return no Desc at all, which will mark the Collector “unchecked”.
 | 
			
		||||
// No checks are performed at registration time, but metric consistency will
 | 
			
		||||
// still be ensured at scrape time, i.e. any inconsistencies will lead to scrape
 | 
			
		||||
// errors. Thus, with unchecked Collectors, the responsibility to not collect
 | 
			
		||||
// metrics that lead to inconsistencies in the total scrape result lies with the
 | 
			
		||||
// implementer of the Collector. While this is not a desirable state, it is
 | 
			
		||||
// sometimes necessary. The typical use case is a situation where the exact
 | 
			
		||||
// metrics to be returned by a Collector cannot be predicted at registration
 | 
			
		||||
// time, but the implementer has sufficient knowledge of the whole system to
 | 
			
		||||
// guarantee metric consistency.
 | 
			
		||||
//
 | 
			
		||||
// The Collector example illustrates the use case. You can also look at the
 | 
			
		||||
// source code of the processCollector (mirroring process metrics), the
 | 
			
		||||
// goCollector (mirroring Go metrics), or the expvarCollector (mirroring expvar
 | 
			
		||||
// metrics) as examples that are used in this package itself.
 | 
			
		||||
//
 | 
			
		||||
// If you just need to call a function to get a single float value to collect as
 | 
			
		||||
// a metric, GaugeFunc, CounterFunc, or UntypedFunc might be interesting
 | 
			
		||||
// shortcuts.
 | 
			
		||||
//
 | 
			
		||||
// Advanced Uses of the Registry
 | 
			
		||||
//
 | 
			
		||||
// While MustRegister is the by far most common way of registering a Collector,
 | 
			
		||||
// sometimes you might want to handle the errors the registration might cause.
 | 
			
		||||
// As suggested by the name, MustRegister panics if an error occurs. With the
 | 
			
		||||
// Register function, the error is returned and can be handled.
 | 
			
		||||
//
 | 
			
		||||
// An error is returned if the registered Collector is incompatible or
 | 
			
		||||
// inconsistent with already registered metrics. The registry aims for
 | 
			
		||||
// consistency of the collected metrics according to the Prometheus data model.
 | 
			
		||||
// Inconsistencies are ideally detected at registration time, not at collect
 | 
			
		||||
// time. The former will usually be detected at start-up time of a program,
 | 
			
		||||
// while the latter will only happen at scrape time, possibly not even on the
 | 
			
		||||
// first scrape if the inconsistency only becomes relevant later. That is the
 | 
			
		||||
// main reason why a Collector and a Metric have to describe themselves to the
 | 
			
		||||
// registry.
 | 
			
		||||
//
 | 
			
		||||
// So far, everything we did operated on the so-called default registry, as it
 | 
			
		||||
// can be found in the global DefaultRegisterer variable. With NewRegistry, you
 | 
			
		||||
// can create a custom registry, or you can even implement the Registerer or
 | 
			
		||||
// Gatherer interfaces yourself. The methods Register and Unregister work in the
 | 
			
		||||
// same way on a custom registry as the global functions Register and Unregister
 | 
			
		||||
// on the default registry.
 | 
			
		||||
//
 | 
			
		||||
// There are a number of uses for custom registries: You can use registries with
 | 
			
		||||
// special properties, see NewPedanticRegistry. You can avoid global state, as
 | 
			
		||||
// it is imposed by the DefaultRegisterer. You can use multiple registries at
 | 
			
		||||
// the same time to expose different metrics in different ways.  You can use
 | 
			
		||||
// separate registries for testing purposes.
 | 
			
		||||
//
 | 
			
		||||
// Also note that the DefaultRegisterer comes registered with a Collector for Go
 | 
			
		||||
// runtime metrics (via NewGoCollector) and a Collector for process metrics (via
 | 
			
		||||
// NewProcessCollector). With a custom registry, you are in control and decide
 | 
			
		||||
// yourself about the Collectors to register.
 | 
			
		||||
//
 | 
			
		||||
// HTTP Exposition
 | 
			
		||||
//
 | 
			
		||||
// The Registry implements the Gatherer interface. The caller of the Gather
 | 
			
		||||
// method can then expose the gathered metrics in some way. Usually, the metrics
 | 
			
		||||
// are served via HTTP on the /metrics endpoint. That's happening in the example
 | 
			
		||||
// above. The tools to expose metrics via HTTP are in the promhttp sub-package.
 | 
			
		||||
//
 | 
			
		||||
// Pushing to the Pushgateway
 | 
			
		||||
//
 | 
			
		||||
// Function for pushing to the Pushgateway can be found in the push sub-package.
 | 
			
		||||
//
 | 
			
		||||
// Graphite Bridge
 | 
			
		||||
//
 | 
			
		||||
// Functions and examples to push metrics from a Gatherer to Graphite can be
 | 
			
		||||
// found in the graphite sub-package.
 | 
			
		||||
//
 | 
			
		||||
// Other Means of Exposition
 | 
			
		||||
//
 | 
			
		||||
// More ways of exposing metrics can easily be added by following the approaches
 | 
			
		||||
// of the existing implementations.
 | 
			
		||||
package prometheus
 | 
			
		||||
							
								
								
									
										119
									
								
								vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,119 @@
 | 
			
		||||
// 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 prometheus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"expvar"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type expvarCollector struct {
 | 
			
		||||
	exports map[string]*Desc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewExpvarCollector returns a newly allocated expvar Collector that still has
 | 
			
		||||
// to be registered with a Prometheus registry.
 | 
			
		||||
//
 | 
			
		||||
// An expvar Collector collects metrics from the expvar interface. It provides a
 | 
			
		||||
// quick way to expose numeric values that are already exported via expvar as
 | 
			
		||||
// Prometheus metrics. Note that the data models of expvar and Prometheus are
 | 
			
		||||
// fundamentally different, and that the expvar Collector is inherently slower
 | 
			
		||||
// than native Prometheus metrics. Thus, the expvar Collector is probably great
 | 
			
		||||
// for experiments and prototying, but you should seriously consider a more
 | 
			
		||||
// direct implementation of Prometheus metrics for monitoring production
 | 
			
		||||
// systems.
 | 
			
		||||
//
 | 
			
		||||
// The exports map has the following meaning:
 | 
			
		||||
//
 | 
			
		||||
// The keys in the map correspond to expvar keys, i.e. for every expvar key you
 | 
			
		||||
// want to export as Prometheus metric, you need an entry in the exports
 | 
			
		||||
// map. The descriptor mapped to each key describes how to export the expvar
 | 
			
		||||
// value. It defines the name and the help string of the Prometheus metric
 | 
			
		||||
// proxying the expvar value. The type will always be Untyped.
 | 
			
		||||
//
 | 
			
		||||
// For descriptors without variable labels, the expvar value must be a number or
 | 
			
		||||
// a bool. The number is then directly exported as the Prometheus sample
 | 
			
		||||
// value. (For a bool, 'false' translates to 0 and 'true' to 1). Expvar values
 | 
			
		||||
// that are not numbers or bools are silently ignored.
 | 
			
		||||
//
 | 
			
		||||
// If the descriptor has one variable label, the expvar value must be an expvar
 | 
			
		||||
// map. The keys in the expvar map become the various values of the one
 | 
			
		||||
// Prometheus label. The values in the expvar map must be numbers or bools again
 | 
			
		||||
// as above.
 | 
			
		||||
//
 | 
			
		||||
// For descriptors with more than one variable label, the expvar must be a
 | 
			
		||||
// nested expvar map, i.e. where the values of the topmost map are maps again
 | 
			
		||||
// etc. until a depth is reached that corresponds to the number of labels. The
 | 
			
		||||
// leaves of that structure must be numbers or bools as above to serve as the
 | 
			
		||||
// sample values.
 | 
			
		||||
//
 | 
			
		||||
// Anything that does not fit into the scheme above is silently ignored.
 | 
			
		||||
func NewExpvarCollector(exports map[string]*Desc) Collector {
 | 
			
		||||
	return &expvarCollector{
 | 
			
		||||
		exports: exports,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Describe implements Collector.
 | 
			
		||||
func (e *expvarCollector) Describe(ch chan<- *Desc) {
 | 
			
		||||
	for _, desc := range e.exports {
 | 
			
		||||
		ch <- desc
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Collect implements Collector.
 | 
			
		||||
func (e *expvarCollector) Collect(ch chan<- Metric) {
 | 
			
		||||
	for name, desc := range e.exports {
 | 
			
		||||
		var m Metric
 | 
			
		||||
		expVar := expvar.Get(name)
 | 
			
		||||
		if expVar == nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		var v interface{}
 | 
			
		||||
		labels := make([]string, len(desc.variableLabels))
 | 
			
		||||
		if err := json.Unmarshal([]byte(expVar.String()), &v); err != nil {
 | 
			
		||||
			ch <- NewInvalidMetric(desc, err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		var processValue func(v interface{}, i int)
 | 
			
		||||
		processValue = func(v interface{}, i int) {
 | 
			
		||||
			if i >= len(labels) {
 | 
			
		||||
				copiedLabels := append(make([]string, 0, len(labels)), labels...)
 | 
			
		||||
				switch v := v.(type) {
 | 
			
		||||
				case float64:
 | 
			
		||||
					m = MustNewConstMetric(desc, UntypedValue, v, copiedLabels...)
 | 
			
		||||
				case bool:
 | 
			
		||||
					if v {
 | 
			
		||||
						m = MustNewConstMetric(desc, UntypedValue, 1, copiedLabels...)
 | 
			
		||||
					} else {
 | 
			
		||||
						m = MustNewConstMetric(desc, UntypedValue, 0, copiedLabels...)
 | 
			
		||||
					}
 | 
			
		||||
				default:
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				ch <- m
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			vm, ok := v.(map[string]interface{})
 | 
			
		||||
			if !ok {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			for lv, val := range vm {
 | 
			
		||||
				labels[i] = lv
 | 
			
		||||
				processValue(val, i+1)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		processValue(v, 0)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										42
									
								
								vendor/github.com/prometheus/client_golang/prometheus/fnv.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								vendor/github.com/prometheus/client_golang/prometheus/fnv.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
// Copyright 2018 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 prometheus
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										289
									
								
								vendor/github.com/prometheus/client_golang/prometheus/gauge.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										289
									
								
								vendor/github.com/prometheus/client_golang/prometheus/gauge.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,289 @@
 | 
			
		||||
// 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 prometheus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"math"
 | 
			
		||||
	"sync/atomic"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	dto "github.com/prometheus/client_model/go"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Gauge is a Metric that represents a single numerical value that can
 | 
			
		||||
// arbitrarily go up and down.
 | 
			
		||||
//
 | 
			
		||||
// A Gauge is typically used for measured values like temperatures or current
 | 
			
		||||
// memory usage, but also "counts" that can go up and down, like the number of
 | 
			
		||||
// running goroutines.
 | 
			
		||||
//
 | 
			
		||||
// To create Gauge instances, use NewGauge.
 | 
			
		||||
type Gauge interface {
 | 
			
		||||
	Metric
 | 
			
		||||
	Collector
 | 
			
		||||
 | 
			
		||||
	// Set sets the Gauge to an arbitrary value.
 | 
			
		||||
	Set(float64)
 | 
			
		||||
	// Inc increments the Gauge by 1. Use Add to increment it by arbitrary
 | 
			
		||||
	// values.
 | 
			
		||||
	Inc()
 | 
			
		||||
	// Dec decrements the Gauge by 1. Use Sub to decrement it by arbitrary
 | 
			
		||||
	// values.
 | 
			
		||||
	Dec()
 | 
			
		||||
	// Add adds the given value to the Gauge. (The value can be negative,
 | 
			
		||||
	// resulting in a decrease of the Gauge.)
 | 
			
		||||
	Add(float64)
 | 
			
		||||
	// Sub subtracts the given value from the Gauge. (The value can be
 | 
			
		||||
	// negative, resulting in an increase of the Gauge.)
 | 
			
		||||
	Sub(float64)
 | 
			
		||||
 | 
			
		||||
	// SetToCurrentTime sets the Gauge to the current Unix time in seconds.
 | 
			
		||||
	SetToCurrentTime()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GaugeOpts is an alias for Opts. See there for doc comments.
 | 
			
		||||
type GaugeOpts Opts
 | 
			
		||||
 | 
			
		||||
// NewGauge creates a new Gauge based on the provided GaugeOpts.
 | 
			
		||||
//
 | 
			
		||||
// The returned implementation is optimized for a fast Set method. If you have a
 | 
			
		||||
// choice for managing the value of a Gauge via Set vs. Inc/Dec/Add/Sub, pick
 | 
			
		||||
// the former. For example, the Inc method of the returned Gauge is slower than
 | 
			
		||||
// the Inc method of a Counter returned by NewCounter. This matches the typical
 | 
			
		||||
// scenarios for Gauges and Counters, where the former tends to be Set-heavy and
 | 
			
		||||
// the latter Inc-heavy.
 | 
			
		||||
func NewGauge(opts GaugeOpts) Gauge {
 | 
			
		||||
	desc := NewDesc(
 | 
			
		||||
		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
 | 
			
		||||
		opts.Help,
 | 
			
		||||
		nil,
 | 
			
		||||
		opts.ConstLabels,
 | 
			
		||||
	)
 | 
			
		||||
	result := &gauge{desc: desc, labelPairs: desc.constLabelPairs}
 | 
			
		||||
	result.init(result) // Init self-collection.
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type gauge struct {
 | 
			
		||||
	// valBits contains the bits of the represented float64 value. It has
 | 
			
		||||
	// to go first in the struct to guarantee alignment for atomic
 | 
			
		||||
	// operations.  http://golang.org/pkg/sync/atomic/#pkg-note-BUG
 | 
			
		||||
	valBits uint64
 | 
			
		||||
 | 
			
		||||
	selfCollector
 | 
			
		||||
 | 
			
		||||
	desc       *Desc
 | 
			
		||||
	labelPairs []*dto.LabelPair
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *gauge) Desc() *Desc {
 | 
			
		||||
	return g.desc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *gauge) Set(val float64) {
 | 
			
		||||
	atomic.StoreUint64(&g.valBits, math.Float64bits(val))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *gauge) SetToCurrentTime() {
 | 
			
		||||
	g.Set(float64(time.Now().UnixNano()) / 1e9)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *gauge) Inc() {
 | 
			
		||||
	g.Add(1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *gauge) Dec() {
 | 
			
		||||
	g.Add(-1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *gauge) Add(val float64) {
 | 
			
		||||
	for {
 | 
			
		||||
		oldBits := atomic.LoadUint64(&g.valBits)
 | 
			
		||||
		newBits := math.Float64bits(math.Float64frombits(oldBits) + val)
 | 
			
		||||
		if atomic.CompareAndSwapUint64(&g.valBits, oldBits, newBits) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *gauge) Sub(val float64) {
 | 
			
		||||
	g.Add(val * -1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *gauge) Write(out *dto.Metric) error {
 | 
			
		||||
	val := math.Float64frombits(atomic.LoadUint64(&g.valBits))
 | 
			
		||||
	return populateMetric(GaugeValue, val, g.labelPairs, nil, out)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GaugeVec is a Collector that bundles a set of Gauges that all share the same
 | 
			
		||||
// Desc, but have different values for their variable labels. This is used if
 | 
			
		||||
// you want to count the same thing partitioned by various dimensions
 | 
			
		||||
// (e.g. number of operations queued, partitioned by user and operation
 | 
			
		||||
// type). Create instances with NewGaugeVec.
 | 
			
		||||
type GaugeVec struct {
 | 
			
		||||
	*metricVec
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewGaugeVec creates a new GaugeVec based on the provided GaugeOpts and
 | 
			
		||||
// partitioned by the given label names.
 | 
			
		||||
func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec {
 | 
			
		||||
	desc := NewDesc(
 | 
			
		||||
		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
 | 
			
		||||
		opts.Help,
 | 
			
		||||
		labelNames,
 | 
			
		||||
		opts.ConstLabels,
 | 
			
		||||
	)
 | 
			
		||||
	return &GaugeVec{
 | 
			
		||||
		metricVec: newMetricVec(desc, func(lvs ...string) Metric {
 | 
			
		||||
			if len(lvs) != len(desc.variableLabels) {
 | 
			
		||||
				panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs))
 | 
			
		||||
			}
 | 
			
		||||
			result := &gauge{desc: desc, labelPairs: makeLabelPairs(desc, lvs)}
 | 
			
		||||
			result.init(result) // Init self-collection.
 | 
			
		||||
			return result
 | 
			
		||||
		}),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetMetricWithLabelValues returns the Gauge for the given slice of label
 | 
			
		||||
// values (same order as the VariableLabels in Desc). If that combination of
 | 
			
		||||
// label values is accessed for the first time, a new Gauge is created.
 | 
			
		||||
//
 | 
			
		||||
// It is possible to call this method without using the returned Gauge to only
 | 
			
		||||
// create the new Gauge but leave it at its starting value 0. See also the
 | 
			
		||||
// SummaryVec example.
 | 
			
		||||
//
 | 
			
		||||
// Keeping the Gauge for later use is possible (and should be considered if
 | 
			
		||||
// performance is critical), but keep in mind that Reset, DeleteLabelValues and
 | 
			
		||||
// Delete can be used to delete the Gauge from the GaugeVec. In that case, the
 | 
			
		||||
// Gauge will still exist, but it will not be exported anymore, even if a
 | 
			
		||||
// Gauge with the same label values is created later. See also the CounterVec
 | 
			
		||||
// example.
 | 
			
		||||
//
 | 
			
		||||
// An error is returned if the number of label values is not the same as the
 | 
			
		||||
// number of VariableLabels in Desc (minus any curried labels).
 | 
			
		||||
//
 | 
			
		||||
// Note that for more than one label value, this method is prone to mistakes
 | 
			
		||||
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
 | 
			
		||||
// an alternative to avoid that type of mistake. For higher label numbers, the
 | 
			
		||||
// latter has a much more readable (albeit more verbose) syntax, but it comes
 | 
			
		||||
// with a performance overhead (for creating and processing the Labels map).
 | 
			
		||||
func (v *GaugeVec) GetMetricWithLabelValues(lvs ...string) (Gauge, error) {
 | 
			
		||||
	metric, err := v.metricVec.getMetricWithLabelValues(lvs...)
 | 
			
		||||
	if metric != nil {
 | 
			
		||||
		return metric.(Gauge), err
 | 
			
		||||
	}
 | 
			
		||||
	return nil, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetMetricWith returns the Gauge for the given Labels map (the label names
 | 
			
		||||
// must match those of the VariableLabels in Desc). If that label map is
 | 
			
		||||
// accessed for the first time, a new Gauge is created. Implications of
 | 
			
		||||
// creating a Gauge without using it and keeping the Gauge for later use are
 | 
			
		||||
// the same as for GetMetricWithLabelValues.
 | 
			
		||||
//
 | 
			
		||||
// An error is returned if the number and names of the Labels are inconsistent
 | 
			
		||||
// with those of the VariableLabels in Desc (minus any curried labels).
 | 
			
		||||
//
 | 
			
		||||
// This method is used for the same purpose as
 | 
			
		||||
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
 | 
			
		||||
// methods.
 | 
			
		||||
func (v *GaugeVec) GetMetricWith(labels Labels) (Gauge, error) {
 | 
			
		||||
	metric, err := v.metricVec.getMetricWith(labels)
 | 
			
		||||
	if metric != nil {
 | 
			
		||||
		return metric.(Gauge), err
 | 
			
		||||
	}
 | 
			
		||||
	return nil, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithLabelValues works as GetMetricWithLabelValues, but panics where
 | 
			
		||||
// GetMetricWithLabelValues would have returned an error. Not returning an
 | 
			
		||||
// error allows shortcuts like
 | 
			
		||||
//     myVec.WithLabelValues("404", "GET").Add(42)
 | 
			
		||||
func (v *GaugeVec) WithLabelValues(lvs ...string) Gauge {
 | 
			
		||||
	g, err := v.GetMetricWithLabelValues(lvs...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	return g
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// With works as GetMetricWith, but panics where GetMetricWithLabels would have
 | 
			
		||||
// returned an error. Not returning an error allows shortcuts like
 | 
			
		||||
//     myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Add(42)
 | 
			
		||||
func (v *GaugeVec) With(labels Labels) Gauge {
 | 
			
		||||
	g, err := v.GetMetricWith(labels)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	return g
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CurryWith returns a vector curried with the provided labels, i.e. the
 | 
			
		||||
// returned vector has those labels pre-set for all labeled operations performed
 | 
			
		||||
// on it. The cardinality of the curried vector is reduced accordingly. The
 | 
			
		||||
// order of the remaining labels stays the same (just with the curried labels
 | 
			
		||||
// taken out of the sequence – which is relevant for the
 | 
			
		||||
// (GetMetric)WithLabelValues methods). It is possible to curry a curried
 | 
			
		||||
// vector, but only with labels not yet used for currying before.
 | 
			
		||||
//
 | 
			
		||||
// The metrics contained in the GaugeVec are shared between the curried and
 | 
			
		||||
// uncurried vectors. They are just accessed differently. Curried and uncurried
 | 
			
		||||
// vectors behave identically in terms of collection. Only one must be
 | 
			
		||||
// registered with a given registry (usually the uncurried version). The Reset
 | 
			
		||||
// method deletes all metrics, even if called on a curried vector.
 | 
			
		||||
func (v *GaugeVec) CurryWith(labels Labels) (*GaugeVec, error) {
 | 
			
		||||
	vec, err := v.curryWith(labels)
 | 
			
		||||
	if vec != nil {
 | 
			
		||||
		return &GaugeVec{vec}, err
 | 
			
		||||
	}
 | 
			
		||||
	return nil, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MustCurryWith works as CurryWith but panics where CurryWith would have
 | 
			
		||||
// returned an error.
 | 
			
		||||
func (v *GaugeVec) MustCurryWith(labels Labels) *GaugeVec {
 | 
			
		||||
	vec, err := v.CurryWith(labels)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	return vec
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GaugeFunc is a Gauge whose value is determined at collect time by calling a
 | 
			
		||||
// provided function.
 | 
			
		||||
//
 | 
			
		||||
// To create GaugeFunc instances, use NewGaugeFunc.
 | 
			
		||||
type GaugeFunc interface {
 | 
			
		||||
	Metric
 | 
			
		||||
	Collector
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewGaugeFunc creates a new GaugeFunc based on the provided GaugeOpts. The
 | 
			
		||||
// value reported is determined by calling the given function from within the
 | 
			
		||||
// Write method. Take into account that metric collection may happen
 | 
			
		||||
// concurrently. Therefore, it must be safe to call the provided function
 | 
			
		||||
// concurrently.
 | 
			
		||||
//
 | 
			
		||||
// NewGaugeFunc is a good way to create an “info” style metric with a constant
 | 
			
		||||
// value of 1. Example:
 | 
			
		||||
// https://github.com/prometheus/common/blob/8558a5b7db3c84fa38b4766966059a7bd5bfa2ee/version/info.go#L36-L56
 | 
			
		||||
func NewGaugeFunc(opts GaugeOpts, function func() float64) GaugeFunc {
 | 
			
		||||
	return newValueFunc(NewDesc(
 | 
			
		||||
		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
 | 
			
		||||
		opts.Help,
 | 
			
		||||
		nil,
 | 
			
		||||
		opts.ConstLabels,
 | 
			
		||||
	), GaugeValue, function)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										396
									
								
								vendor/github.com/prometheus/client_golang/prometheus/go_collector.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										396
									
								
								vendor/github.com/prometheus/client_golang/prometheus/go_collector.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,396 @@
 | 
			
		||||
// Copyright 2018 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 prometheus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"runtime/debug"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type goCollector struct {
 | 
			
		||||
	goroutinesDesc *Desc
 | 
			
		||||
	threadsDesc    *Desc
 | 
			
		||||
	gcDesc         *Desc
 | 
			
		||||
	goInfoDesc     *Desc
 | 
			
		||||
 | 
			
		||||
	// ms... are memstats related.
 | 
			
		||||
	msLast          *runtime.MemStats // Previously collected memstats.
 | 
			
		||||
	msLastTimestamp time.Time
 | 
			
		||||
	msMtx           sync.Mutex // Protects msLast and msLastTimestamp.
 | 
			
		||||
	msMetrics       memStatsMetrics
 | 
			
		||||
	msRead          func(*runtime.MemStats) // For mocking in tests.
 | 
			
		||||
	msMaxWait       time.Duration           // Wait time for fresh memstats.
 | 
			
		||||
	msMaxAge        time.Duration           // Maximum allowed age of old memstats.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewGoCollector returns a collector that exports metrics about the current Go
 | 
			
		||||
// process. This includes memory stats. To collect those, runtime.ReadMemStats
 | 
			
		||||
// is called. This requires to “stop the world”, which usually only happens for
 | 
			
		||||
// garbage collection (GC). Take the following implications into account when
 | 
			
		||||
// deciding whether to use the Go collector:
 | 
			
		||||
//
 | 
			
		||||
// 1. The performance impact of stopping the world is the more relevant the more
 | 
			
		||||
// frequently metrics are collected. However, with Go1.9 or later the
 | 
			
		||||
// stop-the-world time per metrics collection is very short (~25µs) so that the
 | 
			
		||||
// performance impact will only matter in rare cases. However, with older Go
 | 
			
		||||
// versions, the stop-the-world duration depends on the heap size and can be
 | 
			
		||||
// quite significant (~1.7 ms/GiB as per
 | 
			
		||||
// https://go-review.googlesource.com/c/go/+/34937).
 | 
			
		||||
//
 | 
			
		||||
// 2. During an ongoing GC, nothing else can stop the world. Therefore, if the
 | 
			
		||||
// metrics collection happens to coincide with GC, it will only complete after
 | 
			
		||||
// GC has finished. Usually, GC is fast enough to not cause problems. However,
 | 
			
		||||
// with a very large heap, GC might take multiple seconds, which is enough to
 | 
			
		||||
// cause scrape timeouts in common setups. To avoid this problem, the Go
 | 
			
		||||
// collector will use the memstats from a previous collection if
 | 
			
		||||
// runtime.ReadMemStats takes more than 1s. However, if there are no previously
 | 
			
		||||
// collected memstats, or their collection is more than 5m ago, the collection
 | 
			
		||||
// will block until runtime.ReadMemStats succeeds. (The problem might be solved
 | 
			
		||||
// in Go1.13, see https://github.com/golang/go/issues/19812 for the related Go
 | 
			
		||||
// issue.)
 | 
			
		||||
func NewGoCollector() Collector {
 | 
			
		||||
	return &goCollector{
 | 
			
		||||
		goroutinesDesc: NewDesc(
 | 
			
		||||
			"go_goroutines",
 | 
			
		||||
			"Number of goroutines that currently exist.",
 | 
			
		||||
			nil, nil),
 | 
			
		||||
		threadsDesc: NewDesc(
 | 
			
		||||
			"go_threads",
 | 
			
		||||
			"Number of OS threads created.",
 | 
			
		||||
			nil, nil),
 | 
			
		||||
		gcDesc: NewDesc(
 | 
			
		||||
			"go_gc_duration_seconds",
 | 
			
		||||
			"A summary of the pause duration of garbage collection cycles.",
 | 
			
		||||
			nil, nil),
 | 
			
		||||
		goInfoDesc: NewDesc(
 | 
			
		||||
			"go_info",
 | 
			
		||||
			"Information about the Go environment.",
 | 
			
		||||
			nil, Labels{"version": runtime.Version()}),
 | 
			
		||||
		msLast:    &runtime.MemStats{},
 | 
			
		||||
		msRead:    runtime.ReadMemStats,
 | 
			
		||||
		msMaxWait: time.Second,
 | 
			
		||||
		msMaxAge:  5 * time.Minute,
 | 
			
		||||
		msMetrics: memStatsMetrics{
 | 
			
		||||
			{
 | 
			
		||||
				desc: NewDesc(
 | 
			
		||||
					memstatNamespace("alloc_bytes"),
 | 
			
		||||
					"Number of bytes allocated and still in use.",
 | 
			
		||||
					nil, nil,
 | 
			
		||||
				),
 | 
			
		||||
				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.Alloc) },
 | 
			
		||||
				valType: GaugeValue,
 | 
			
		||||
			}, {
 | 
			
		||||
				desc: NewDesc(
 | 
			
		||||
					memstatNamespace("alloc_bytes_total"),
 | 
			
		||||
					"Total number of bytes allocated, even if freed.",
 | 
			
		||||
					nil, nil,
 | 
			
		||||
				),
 | 
			
		||||
				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.TotalAlloc) },
 | 
			
		||||
				valType: CounterValue,
 | 
			
		||||
			}, {
 | 
			
		||||
				desc: NewDesc(
 | 
			
		||||
					memstatNamespace("sys_bytes"),
 | 
			
		||||
					"Number of bytes obtained from system.",
 | 
			
		||||
					nil, nil,
 | 
			
		||||
				),
 | 
			
		||||
				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.Sys) },
 | 
			
		||||
				valType: GaugeValue,
 | 
			
		||||
			}, {
 | 
			
		||||
				desc: NewDesc(
 | 
			
		||||
					memstatNamespace("lookups_total"),
 | 
			
		||||
					"Total number of pointer lookups.",
 | 
			
		||||
					nil, nil,
 | 
			
		||||
				),
 | 
			
		||||
				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.Lookups) },
 | 
			
		||||
				valType: CounterValue,
 | 
			
		||||
			}, {
 | 
			
		||||
				desc: NewDesc(
 | 
			
		||||
					memstatNamespace("mallocs_total"),
 | 
			
		||||
					"Total number of mallocs.",
 | 
			
		||||
					nil, nil,
 | 
			
		||||
				),
 | 
			
		||||
				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.Mallocs) },
 | 
			
		||||
				valType: CounterValue,
 | 
			
		||||
			}, {
 | 
			
		||||
				desc: NewDesc(
 | 
			
		||||
					memstatNamespace("frees_total"),
 | 
			
		||||
					"Total number of frees.",
 | 
			
		||||
					nil, nil,
 | 
			
		||||
				),
 | 
			
		||||
				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.Frees) },
 | 
			
		||||
				valType: CounterValue,
 | 
			
		||||
			}, {
 | 
			
		||||
				desc: NewDesc(
 | 
			
		||||
					memstatNamespace("heap_alloc_bytes"),
 | 
			
		||||
					"Number of heap bytes allocated and still in use.",
 | 
			
		||||
					nil, nil,
 | 
			
		||||
				),
 | 
			
		||||
				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.HeapAlloc) },
 | 
			
		||||
				valType: GaugeValue,
 | 
			
		||||
			}, {
 | 
			
		||||
				desc: NewDesc(
 | 
			
		||||
					memstatNamespace("heap_sys_bytes"),
 | 
			
		||||
					"Number of heap bytes obtained from system.",
 | 
			
		||||
					nil, nil,
 | 
			
		||||
				),
 | 
			
		||||
				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.HeapSys) },
 | 
			
		||||
				valType: GaugeValue,
 | 
			
		||||
			}, {
 | 
			
		||||
				desc: NewDesc(
 | 
			
		||||
					memstatNamespace("heap_idle_bytes"),
 | 
			
		||||
					"Number of heap bytes waiting to be used.",
 | 
			
		||||
					nil, nil,
 | 
			
		||||
				),
 | 
			
		||||
				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.HeapIdle) },
 | 
			
		||||
				valType: GaugeValue,
 | 
			
		||||
			}, {
 | 
			
		||||
				desc: NewDesc(
 | 
			
		||||
					memstatNamespace("heap_inuse_bytes"),
 | 
			
		||||
					"Number of heap bytes that are in use.",
 | 
			
		||||
					nil, nil,
 | 
			
		||||
				),
 | 
			
		||||
				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.HeapInuse) },
 | 
			
		||||
				valType: GaugeValue,
 | 
			
		||||
			}, {
 | 
			
		||||
				desc: NewDesc(
 | 
			
		||||
					memstatNamespace("heap_released_bytes"),
 | 
			
		||||
					"Number of heap bytes released to OS.",
 | 
			
		||||
					nil, nil,
 | 
			
		||||
				),
 | 
			
		||||
				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.HeapReleased) },
 | 
			
		||||
				valType: GaugeValue,
 | 
			
		||||
			}, {
 | 
			
		||||
				desc: NewDesc(
 | 
			
		||||
					memstatNamespace("heap_objects"),
 | 
			
		||||
					"Number of allocated objects.",
 | 
			
		||||
					nil, nil,
 | 
			
		||||
				),
 | 
			
		||||
				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.HeapObjects) },
 | 
			
		||||
				valType: GaugeValue,
 | 
			
		||||
			}, {
 | 
			
		||||
				desc: NewDesc(
 | 
			
		||||
					memstatNamespace("stack_inuse_bytes"),
 | 
			
		||||
					"Number of bytes in use by the stack allocator.",
 | 
			
		||||
					nil, nil,
 | 
			
		||||
				),
 | 
			
		||||
				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.StackInuse) },
 | 
			
		||||
				valType: GaugeValue,
 | 
			
		||||
			}, {
 | 
			
		||||
				desc: NewDesc(
 | 
			
		||||
					memstatNamespace("stack_sys_bytes"),
 | 
			
		||||
					"Number of bytes obtained from system for stack allocator.",
 | 
			
		||||
					nil, nil,
 | 
			
		||||
				),
 | 
			
		||||
				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.StackSys) },
 | 
			
		||||
				valType: GaugeValue,
 | 
			
		||||
			}, {
 | 
			
		||||
				desc: NewDesc(
 | 
			
		||||
					memstatNamespace("mspan_inuse_bytes"),
 | 
			
		||||
					"Number of bytes in use by mspan structures.",
 | 
			
		||||
					nil, nil,
 | 
			
		||||
				),
 | 
			
		||||
				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.MSpanInuse) },
 | 
			
		||||
				valType: GaugeValue,
 | 
			
		||||
			}, {
 | 
			
		||||
				desc: NewDesc(
 | 
			
		||||
					memstatNamespace("mspan_sys_bytes"),
 | 
			
		||||
					"Number of bytes used for mspan structures obtained from system.",
 | 
			
		||||
					nil, nil,
 | 
			
		||||
				),
 | 
			
		||||
				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.MSpanSys) },
 | 
			
		||||
				valType: GaugeValue,
 | 
			
		||||
			}, {
 | 
			
		||||
				desc: NewDesc(
 | 
			
		||||
					memstatNamespace("mcache_inuse_bytes"),
 | 
			
		||||
					"Number of bytes in use by mcache structures.",
 | 
			
		||||
					nil, nil,
 | 
			
		||||
				),
 | 
			
		||||
				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.MCacheInuse) },
 | 
			
		||||
				valType: GaugeValue,
 | 
			
		||||
			}, {
 | 
			
		||||
				desc: NewDesc(
 | 
			
		||||
					memstatNamespace("mcache_sys_bytes"),
 | 
			
		||||
					"Number of bytes used for mcache structures obtained from system.",
 | 
			
		||||
					nil, nil,
 | 
			
		||||
				),
 | 
			
		||||
				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.MCacheSys) },
 | 
			
		||||
				valType: GaugeValue,
 | 
			
		||||
			}, {
 | 
			
		||||
				desc: NewDesc(
 | 
			
		||||
					memstatNamespace("buck_hash_sys_bytes"),
 | 
			
		||||
					"Number of bytes used by the profiling bucket hash table.",
 | 
			
		||||
					nil, nil,
 | 
			
		||||
				),
 | 
			
		||||
				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.BuckHashSys) },
 | 
			
		||||
				valType: GaugeValue,
 | 
			
		||||
			}, {
 | 
			
		||||
				desc: NewDesc(
 | 
			
		||||
					memstatNamespace("gc_sys_bytes"),
 | 
			
		||||
					"Number of bytes used for garbage collection system metadata.",
 | 
			
		||||
					nil, nil,
 | 
			
		||||
				),
 | 
			
		||||
				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.GCSys) },
 | 
			
		||||
				valType: GaugeValue,
 | 
			
		||||
			}, {
 | 
			
		||||
				desc: NewDesc(
 | 
			
		||||
					memstatNamespace("other_sys_bytes"),
 | 
			
		||||
					"Number of bytes used for other system allocations.",
 | 
			
		||||
					nil, nil,
 | 
			
		||||
				),
 | 
			
		||||
				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.OtherSys) },
 | 
			
		||||
				valType: GaugeValue,
 | 
			
		||||
			}, {
 | 
			
		||||
				desc: NewDesc(
 | 
			
		||||
					memstatNamespace("next_gc_bytes"),
 | 
			
		||||
					"Number of heap bytes when next garbage collection will take place.",
 | 
			
		||||
					nil, nil,
 | 
			
		||||
				),
 | 
			
		||||
				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.NextGC) },
 | 
			
		||||
				valType: GaugeValue,
 | 
			
		||||
			}, {
 | 
			
		||||
				desc: NewDesc(
 | 
			
		||||
					memstatNamespace("last_gc_time_seconds"),
 | 
			
		||||
					"Number of seconds since 1970 of last garbage collection.",
 | 
			
		||||
					nil, nil,
 | 
			
		||||
				),
 | 
			
		||||
				eval:    func(ms *runtime.MemStats) float64 { return float64(ms.LastGC) / 1e9 },
 | 
			
		||||
				valType: GaugeValue,
 | 
			
		||||
			}, {
 | 
			
		||||
				desc: NewDesc(
 | 
			
		||||
					memstatNamespace("gc_cpu_fraction"),
 | 
			
		||||
					"The fraction of this program's available CPU time used by the GC since the program started.",
 | 
			
		||||
					nil, nil,
 | 
			
		||||
				),
 | 
			
		||||
				eval:    func(ms *runtime.MemStats) float64 { return ms.GCCPUFraction },
 | 
			
		||||
				valType: GaugeValue,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func memstatNamespace(s string) string {
 | 
			
		||||
	return "go_memstats_" + s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Describe returns all descriptions of the collector.
 | 
			
		||||
func (c *goCollector) Describe(ch chan<- *Desc) {
 | 
			
		||||
	ch <- c.goroutinesDesc
 | 
			
		||||
	ch <- c.threadsDesc
 | 
			
		||||
	ch <- c.gcDesc
 | 
			
		||||
	ch <- c.goInfoDesc
 | 
			
		||||
	for _, i := range c.msMetrics {
 | 
			
		||||
		ch <- i.desc
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Collect returns the current state of all metrics of the collector.
 | 
			
		||||
func (c *goCollector) Collect(ch chan<- Metric) {
 | 
			
		||||
	var (
 | 
			
		||||
		ms   = &runtime.MemStats{}
 | 
			
		||||
		done = make(chan struct{})
 | 
			
		||||
	)
 | 
			
		||||
	// Start reading memstats first as it might take a while.
 | 
			
		||||
	go func() {
 | 
			
		||||
		c.msRead(ms)
 | 
			
		||||
		c.msMtx.Lock()
 | 
			
		||||
		c.msLast = ms
 | 
			
		||||
		c.msLastTimestamp = time.Now()
 | 
			
		||||
		c.msMtx.Unlock()
 | 
			
		||||
		close(done)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	ch <- MustNewConstMetric(c.goroutinesDesc, GaugeValue, float64(runtime.NumGoroutine()))
 | 
			
		||||
	n, _ := runtime.ThreadCreateProfile(nil)
 | 
			
		||||
	ch <- MustNewConstMetric(c.threadsDesc, GaugeValue, float64(n))
 | 
			
		||||
 | 
			
		||||
	var stats debug.GCStats
 | 
			
		||||
	stats.PauseQuantiles = make([]time.Duration, 5)
 | 
			
		||||
	debug.ReadGCStats(&stats)
 | 
			
		||||
 | 
			
		||||
	quantiles := make(map[float64]float64)
 | 
			
		||||
	for idx, pq := range stats.PauseQuantiles[1:] {
 | 
			
		||||
		quantiles[float64(idx+1)/float64(len(stats.PauseQuantiles)-1)] = pq.Seconds()
 | 
			
		||||
	}
 | 
			
		||||
	quantiles[0.0] = stats.PauseQuantiles[0].Seconds()
 | 
			
		||||
	ch <- MustNewConstSummary(c.gcDesc, uint64(stats.NumGC), stats.PauseTotal.Seconds(), quantiles)
 | 
			
		||||
 | 
			
		||||
	ch <- MustNewConstMetric(c.goInfoDesc, GaugeValue, 1)
 | 
			
		||||
 | 
			
		||||
	timer := time.NewTimer(c.msMaxWait)
 | 
			
		||||
	select {
 | 
			
		||||
	case <-done: // Our own ReadMemStats succeeded in time. Use it.
 | 
			
		||||
		timer.Stop() // Important for high collection frequencies to not pile up timers.
 | 
			
		||||
		c.msCollect(ch, ms)
 | 
			
		||||
		return
 | 
			
		||||
	case <-timer.C: // Time out, use last memstats if possible. Continue below.
 | 
			
		||||
	}
 | 
			
		||||
	c.msMtx.Lock()
 | 
			
		||||
	if time.Since(c.msLastTimestamp) < c.msMaxAge {
 | 
			
		||||
		// Last memstats are recent enough. Collect from them under the lock.
 | 
			
		||||
		c.msCollect(ch, c.msLast)
 | 
			
		||||
		c.msMtx.Unlock()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	// If we are here, the last memstats are too old or don't exist. We have
 | 
			
		||||
	// to wait until our own ReadMemStats finally completes. For that to
 | 
			
		||||
	// happen, we have to release the lock.
 | 
			
		||||
	c.msMtx.Unlock()
 | 
			
		||||
	<-done
 | 
			
		||||
	c.msCollect(ch, ms)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *goCollector) msCollect(ch chan<- Metric, ms *runtime.MemStats) {
 | 
			
		||||
	for _, i := range c.msMetrics {
 | 
			
		||||
		ch <- MustNewConstMetric(i.desc, i.valType, i.eval(ms))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// memStatsMetrics provide description, value, and value type for memstat metrics.
 | 
			
		||||
type memStatsMetrics []struct {
 | 
			
		||||
	desc    *Desc
 | 
			
		||||
	eval    func(*runtime.MemStats) float64
 | 
			
		||||
	valType ValueType
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewBuildInfoCollector returns a collector collecting a single metric
 | 
			
		||||
// "go_build_info" with the constant value 1 and three labels "path", "version",
 | 
			
		||||
// and "checksum". Their label values contain the main module path, version, and
 | 
			
		||||
// checksum, respectively. The labels will only have meaningful values if the
 | 
			
		||||
// binary is built with Go module support and from source code retrieved from
 | 
			
		||||
// the source repository (rather than the local file system). This is usually
 | 
			
		||||
// accomplished by building from outside of GOPATH, specifying the full address
 | 
			
		||||
// of the main package, e.g. "GO111MODULE=on go run
 | 
			
		||||
// github.com/prometheus/client_golang/examples/random". If built without Go
 | 
			
		||||
// module support, all label values will be "unknown". If built with Go module
 | 
			
		||||
// support but using the source code from the local file system, the "path" will
 | 
			
		||||
// be set appropriately, but "checksum" will be empty and "version" will be
 | 
			
		||||
// "(devel)".
 | 
			
		||||
//
 | 
			
		||||
// This collector uses only the build information for the main module. See
 | 
			
		||||
// https://github.com/povilasv/prommod for an example of a collector for the
 | 
			
		||||
// module dependencies.
 | 
			
		||||
func NewBuildInfoCollector() Collector {
 | 
			
		||||
	path, version, sum := readBuildInfo()
 | 
			
		||||
	c := &selfCollector{MustNewConstMetric(
 | 
			
		||||
		NewDesc(
 | 
			
		||||
			"go_build_info",
 | 
			
		||||
			"Build information about the main Go module.",
 | 
			
		||||
			nil, Labels{"path": path, "version": version, "checksum": sum},
 | 
			
		||||
		),
 | 
			
		||||
		GaugeValue, 1)}
 | 
			
		||||
	c.init(c.self)
 | 
			
		||||
	return c
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										636
									
								
								vendor/github.com/prometheus/client_golang/prometheus/histogram.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										636
									
								
								vendor/github.com/prometheus/client_golang/prometheus/histogram.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,636 @@
 | 
			
		||||
// 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 prometheus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"math"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"sync/atomic"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/golang/protobuf/proto"
 | 
			
		||||
 | 
			
		||||
	dto "github.com/prometheus/client_model/go"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// A Histogram counts individual observations from an event or sample stream in
 | 
			
		||||
// configurable buckets. Similar to a summary, it also provides a sum of
 | 
			
		||||
// observations and an observation count.
 | 
			
		||||
//
 | 
			
		||||
// On the Prometheus server, quantiles can be calculated from a Histogram using
 | 
			
		||||
// the histogram_quantile function in the query language.
 | 
			
		||||
//
 | 
			
		||||
// Note that Histograms, in contrast to Summaries, can be aggregated with the
 | 
			
		||||
// Prometheus query language (see the documentation for detailed
 | 
			
		||||
// procedures). However, Histograms require the user to pre-define suitable
 | 
			
		||||
// buckets, and they are in general less accurate. The Observe method of a
 | 
			
		||||
// Histogram has a very low performance overhead in comparison with the Observe
 | 
			
		||||
// method of a Summary.
 | 
			
		||||
//
 | 
			
		||||
// To create Histogram instances, use NewHistogram.
 | 
			
		||||
type Histogram interface {
 | 
			
		||||
	Metric
 | 
			
		||||
	Collector
 | 
			
		||||
 | 
			
		||||
	// Observe adds a single observation to the histogram.
 | 
			
		||||
	Observe(float64)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// bucketLabel is used for the label that defines the upper bound of a
 | 
			
		||||
// bucket of a histogram ("le" -> "less or equal").
 | 
			
		||||
const bucketLabel = "le"
 | 
			
		||||
 | 
			
		||||
// DefBuckets are the default Histogram buckets. The default buckets are
 | 
			
		||||
// tailored to broadly measure the response time (in seconds) of a network
 | 
			
		||||
// service. Most likely, however, you will be required to define buckets
 | 
			
		||||
// customized to your use case.
 | 
			
		||||
var (
 | 
			
		||||
	DefBuckets = []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10}
 | 
			
		||||
 | 
			
		||||
	errBucketLabelNotAllowed = fmt.Errorf(
 | 
			
		||||
		"%q is not allowed as label name in histograms", bucketLabel,
 | 
			
		||||
	)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// LinearBuckets creates 'count' buckets, each 'width' wide, where the lowest
 | 
			
		||||
// bucket has an upper bound of 'start'. The final +Inf bucket is not counted
 | 
			
		||||
// and not included in the returned slice. The returned slice is meant to be
 | 
			
		||||
// used for the Buckets field of HistogramOpts.
 | 
			
		||||
//
 | 
			
		||||
// The function panics if 'count' is zero or negative.
 | 
			
		||||
func LinearBuckets(start, width float64, count int) []float64 {
 | 
			
		||||
	if count < 1 {
 | 
			
		||||
		panic("LinearBuckets needs a positive count")
 | 
			
		||||
	}
 | 
			
		||||
	buckets := make([]float64, count)
 | 
			
		||||
	for i := range buckets {
 | 
			
		||||
		buckets[i] = start
 | 
			
		||||
		start += width
 | 
			
		||||
	}
 | 
			
		||||
	return buckets
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExponentialBuckets creates 'count' buckets, where the lowest bucket has an
 | 
			
		||||
// upper bound of 'start' and each following bucket's upper bound is 'factor'
 | 
			
		||||
// times the previous bucket's upper bound. The final +Inf bucket is not counted
 | 
			
		||||
// and not included in the returned slice. The returned slice is meant to be
 | 
			
		||||
// used for the Buckets field of HistogramOpts.
 | 
			
		||||
//
 | 
			
		||||
// The function panics if 'count' is 0 or negative, if 'start' is 0 or negative,
 | 
			
		||||
// or if 'factor' is less than or equal 1.
 | 
			
		||||
func ExponentialBuckets(start, factor float64, count int) []float64 {
 | 
			
		||||
	if count < 1 {
 | 
			
		||||
		panic("ExponentialBuckets needs a positive count")
 | 
			
		||||
	}
 | 
			
		||||
	if start <= 0 {
 | 
			
		||||
		panic("ExponentialBuckets needs a positive start value")
 | 
			
		||||
	}
 | 
			
		||||
	if factor <= 1 {
 | 
			
		||||
		panic("ExponentialBuckets needs a factor greater than 1")
 | 
			
		||||
	}
 | 
			
		||||
	buckets := make([]float64, count)
 | 
			
		||||
	for i := range buckets {
 | 
			
		||||
		buckets[i] = start
 | 
			
		||||
		start *= factor
 | 
			
		||||
	}
 | 
			
		||||
	return buckets
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HistogramOpts bundles the options for creating a Histogram metric. It is
 | 
			
		||||
// mandatory to set Name to a non-empty string. All other fields are optional
 | 
			
		||||
// and can safely be left at their zero value, although it is strongly
 | 
			
		||||
// encouraged to set a Help string.
 | 
			
		||||
type HistogramOpts struct {
 | 
			
		||||
	// Namespace, Subsystem, and Name are components of the fully-qualified
 | 
			
		||||
	// name of the Histogram (created by joining these components with
 | 
			
		||||
	// "_"). Only Name is mandatory, the others merely help structuring the
 | 
			
		||||
	// name. Note that the fully-qualified name of the Histogram must be a
 | 
			
		||||
	// valid Prometheus metric name.
 | 
			
		||||
	Namespace string
 | 
			
		||||
	Subsystem string
 | 
			
		||||
	Name      string
 | 
			
		||||
 | 
			
		||||
	// Help provides information about this Histogram.
 | 
			
		||||
	//
 | 
			
		||||
	// Metrics with the same fully-qualified name must have the same Help
 | 
			
		||||
	// string.
 | 
			
		||||
	Help string
 | 
			
		||||
 | 
			
		||||
	// ConstLabels are used to attach fixed labels to this metric. Metrics
 | 
			
		||||
	// with the same fully-qualified name must have the same label names in
 | 
			
		||||
	// their ConstLabels.
 | 
			
		||||
	//
 | 
			
		||||
	// ConstLabels are only used rarely. In particular, do not use them to
 | 
			
		||||
	// attach the same labels to all your metrics. Those use cases are
 | 
			
		||||
	// better covered by target labels set by the scraping Prometheus
 | 
			
		||||
	// server, or by one specific metric (e.g. a build_info or a
 | 
			
		||||
	// machine_role metric). See also
 | 
			
		||||
	// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels-not-static-scraped-labels
 | 
			
		||||
	ConstLabels Labels
 | 
			
		||||
 | 
			
		||||
	// Buckets defines the buckets into which observations are counted. Each
 | 
			
		||||
	// element in the slice is the upper inclusive bound of a bucket. The
 | 
			
		||||
	// values must be sorted in strictly increasing order. There is no need
 | 
			
		||||
	// to add a highest bucket with +Inf bound, it will be added
 | 
			
		||||
	// implicitly. The default value is DefBuckets.
 | 
			
		||||
	Buckets []float64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewHistogram creates a new Histogram based on the provided HistogramOpts. It
 | 
			
		||||
// panics if the buckets in HistogramOpts are not in strictly increasing order.
 | 
			
		||||
//
 | 
			
		||||
// The returned implementation also implements ExemplarObserver. It is safe to
 | 
			
		||||
// perform the corresponding type assertion. Exemplars are tracked separately
 | 
			
		||||
// for each bucket.
 | 
			
		||||
func NewHistogram(opts HistogramOpts) Histogram {
 | 
			
		||||
	return newHistogram(
 | 
			
		||||
		NewDesc(
 | 
			
		||||
			BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
 | 
			
		||||
			opts.Help,
 | 
			
		||||
			nil,
 | 
			
		||||
			opts.ConstLabels,
 | 
			
		||||
		),
 | 
			
		||||
		opts,
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogram {
 | 
			
		||||
	if len(desc.variableLabels) != len(labelValues) {
 | 
			
		||||
		panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, labelValues))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, n := range desc.variableLabels {
 | 
			
		||||
		if n == bucketLabel {
 | 
			
		||||
			panic(errBucketLabelNotAllowed)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for _, lp := range desc.constLabelPairs {
 | 
			
		||||
		if lp.GetName() == bucketLabel {
 | 
			
		||||
			panic(errBucketLabelNotAllowed)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(opts.Buckets) == 0 {
 | 
			
		||||
		opts.Buckets = DefBuckets
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	h := &histogram{
 | 
			
		||||
		desc:        desc,
 | 
			
		||||
		upperBounds: opts.Buckets,
 | 
			
		||||
		labelPairs:  makeLabelPairs(desc, labelValues),
 | 
			
		||||
		counts:      [2]*histogramCounts{{}, {}},
 | 
			
		||||
		now:         time.Now,
 | 
			
		||||
	}
 | 
			
		||||
	for i, upperBound := range h.upperBounds {
 | 
			
		||||
		if i < len(h.upperBounds)-1 {
 | 
			
		||||
			if upperBound >= h.upperBounds[i+1] {
 | 
			
		||||
				panic(fmt.Errorf(
 | 
			
		||||
					"histogram buckets must be in increasing order: %f >= %f",
 | 
			
		||||
					upperBound, h.upperBounds[i+1],
 | 
			
		||||
				))
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			if math.IsInf(upperBound, +1) {
 | 
			
		||||
				// The +Inf bucket is implicit. Remove it here.
 | 
			
		||||
				h.upperBounds = h.upperBounds[:i]
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// Finally we know the final length of h.upperBounds and can make buckets
 | 
			
		||||
	// for both counts as well as exemplars:
 | 
			
		||||
	h.counts[0].buckets = make([]uint64, len(h.upperBounds))
 | 
			
		||||
	h.counts[1].buckets = make([]uint64, len(h.upperBounds))
 | 
			
		||||
	h.exemplars = make([]atomic.Value, len(h.upperBounds)+1)
 | 
			
		||||
 | 
			
		||||
	h.init(h) // Init self-collection.
 | 
			
		||||
	return h
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type histogramCounts struct {
 | 
			
		||||
	// sumBits contains the bits of the float64 representing the sum of all
 | 
			
		||||
	// observations. sumBits and count have to go first in the struct to
 | 
			
		||||
	// guarantee alignment for atomic operations.
 | 
			
		||||
	// http://golang.org/pkg/sync/atomic/#pkg-note-BUG
 | 
			
		||||
	sumBits uint64
 | 
			
		||||
	count   uint64
 | 
			
		||||
	buckets []uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type histogram struct {
 | 
			
		||||
	// countAndHotIdx enables lock-free writes with use of atomic updates.
 | 
			
		||||
	// The most significant bit is the hot index [0 or 1] of the count field
 | 
			
		||||
	// below. Observe calls update the hot one. All remaining bits count the
 | 
			
		||||
	// number of Observe calls. Observe starts by incrementing this counter,
 | 
			
		||||
	// and finish by incrementing the count field in the respective
 | 
			
		||||
	// histogramCounts, as a marker for completion.
 | 
			
		||||
	//
 | 
			
		||||
	// Calls of the Write method (which are non-mutating reads from the
 | 
			
		||||
	// perspective of the histogram) swap the hot–cold under the writeMtx
 | 
			
		||||
	// lock. A cooldown is awaited (while locked) by comparing the number of
 | 
			
		||||
	// observations with the initiation count. Once they match, then the
 | 
			
		||||
	// last observation on the now cool one has completed. All cool fields must
 | 
			
		||||
	// be merged into the new hot before releasing writeMtx.
 | 
			
		||||
	//
 | 
			
		||||
	// Fields with atomic access first! See alignment constraint:
 | 
			
		||||
	// http://golang.org/pkg/sync/atomic/#pkg-note-BUG
 | 
			
		||||
	countAndHotIdx uint64
 | 
			
		||||
 | 
			
		||||
	selfCollector
 | 
			
		||||
	desc     *Desc
 | 
			
		||||
	writeMtx sync.Mutex // Only used in the Write method.
 | 
			
		||||
 | 
			
		||||
	// Two counts, one is "hot" for lock-free observations, the other is
 | 
			
		||||
	// "cold" for writing out a dto.Metric. It has to be an array of
 | 
			
		||||
	// pointers to guarantee 64bit alignment of the histogramCounts, see
 | 
			
		||||
	// http://golang.org/pkg/sync/atomic/#pkg-note-BUG.
 | 
			
		||||
	counts [2]*histogramCounts
 | 
			
		||||
 | 
			
		||||
	upperBounds []float64
 | 
			
		||||
	labelPairs  []*dto.LabelPair
 | 
			
		||||
	exemplars   []atomic.Value // One more than buckets (to include +Inf), each a *dto.Exemplar.
 | 
			
		||||
 | 
			
		||||
	now func() time.Time // To mock out time.Now() for testing.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *histogram) Desc() *Desc {
 | 
			
		||||
	return h.desc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *histogram) Observe(v float64) {
 | 
			
		||||
	h.observe(v, h.findBucket(v))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *histogram) ObserveWithExemplar(v float64, e Labels) {
 | 
			
		||||
	i := h.findBucket(v)
 | 
			
		||||
	h.observe(v, i)
 | 
			
		||||
	h.updateExemplar(v, i, e)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *histogram) Write(out *dto.Metric) error {
 | 
			
		||||
	// For simplicity, we protect this whole method by a mutex. It is not in
 | 
			
		||||
	// the hot path, i.e. Observe is called much more often than Write. The
 | 
			
		||||
	// complication of making Write lock-free isn't worth it, if possible at
 | 
			
		||||
	// all.
 | 
			
		||||
	h.writeMtx.Lock()
 | 
			
		||||
	defer h.writeMtx.Unlock()
 | 
			
		||||
 | 
			
		||||
	// Adding 1<<63 switches the hot index (from 0 to 1 or from 1 to 0)
 | 
			
		||||
	// without touching the count bits. See the struct comments for a full
 | 
			
		||||
	// description of the algorithm.
 | 
			
		||||
	n := atomic.AddUint64(&h.countAndHotIdx, 1<<63)
 | 
			
		||||
	// count is contained unchanged in the lower 63 bits.
 | 
			
		||||
	count := n & ((1 << 63) - 1)
 | 
			
		||||
	// The most significant bit tells us which counts is hot. The complement
 | 
			
		||||
	// is thus the cold one.
 | 
			
		||||
	hotCounts := h.counts[n>>63]
 | 
			
		||||
	coldCounts := h.counts[(^n)>>63]
 | 
			
		||||
 | 
			
		||||
	// Await cooldown.
 | 
			
		||||
	for count != atomic.LoadUint64(&coldCounts.count) {
 | 
			
		||||
		runtime.Gosched() // Let observations get work done.
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	his := &dto.Histogram{
 | 
			
		||||
		Bucket:      make([]*dto.Bucket, len(h.upperBounds)),
 | 
			
		||||
		SampleCount: proto.Uint64(count),
 | 
			
		||||
		SampleSum:   proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.sumBits))),
 | 
			
		||||
	}
 | 
			
		||||
	var cumCount uint64
 | 
			
		||||
	for i, upperBound := range h.upperBounds {
 | 
			
		||||
		cumCount += atomic.LoadUint64(&coldCounts.buckets[i])
 | 
			
		||||
		his.Bucket[i] = &dto.Bucket{
 | 
			
		||||
			CumulativeCount: proto.Uint64(cumCount),
 | 
			
		||||
			UpperBound:      proto.Float64(upperBound),
 | 
			
		||||
		}
 | 
			
		||||
		if e := h.exemplars[i].Load(); e != nil {
 | 
			
		||||
			his.Bucket[i].Exemplar = e.(*dto.Exemplar)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// If there is an exemplar for the +Inf bucket, we have to add that bucket explicitly.
 | 
			
		||||
	if e := h.exemplars[len(h.upperBounds)].Load(); e != nil {
 | 
			
		||||
		b := &dto.Bucket{
 | 
			
		||||
			CumulativeCount: proto.Uint64(count),
 | 
			
		||||
			UpperBound:      proto.Float64(math.Inf(1)),
 | 
			
		||||
			Exemplar:        e.(*dto.Exemplar),
 | 
			
		||||
		}
 | 
			
		||||
		his.Bucket = append(his.Bucket, b)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	out.Histogram = his
 | 
			
		||||
	out.Label = h.labelPairs
 | 
			
		||||
 | 
			
		||||
	// Finally add all the cold counts to the new hot counts and reset the cold counts.
 | 
			
		||||
	atomic.AddUint64(&hotCounts.count, count)
 | 
			
		||||
	atomic.StoreUint64(&coldCounts.count, 0)
 | 
			
		||||
	for {
 | 
			
		||||
		oldBits := atomic.LoadUint64(&hotCounts.sumBits)
 | 
			
		||||
		newBits := math.Float64bits(math.Float64frombits(oldBits) + his.GetSampleSum())
 | 
			
		||||
		if atomic.CompareAndSwapUint64(&hotCounts.sumBits, oldBits, newBits) {
 | 
			
		||||
			atomic.StoreUint64(&coldCounts.sumBits, 0)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for i := range h.upperBounds {
 | 
			
		||||
		atomic.AddUint64(&hotCounts.buckets[i], atomic.LoadUint64(&coldCounts.buckets[i]))
 | 
			
		||||
		atomic.StoreUint64(&coldCounts.buckets[i], 0)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// findBucket returns the index of the bucket for the provided value, or
 | 
			
		||||
// len(h.upperBounds) for the +Inf bucket.
 | 
			
		||||
func (h *histogram) findBucket(v float64) int {
 | 
			
		||||
	// TODO(beorn7): For small numbers of buckets (<30), a linear search is
 | 
			
		||||
	// slightly faster than the binary search. If we really care, we could
 | 
			
		||||
	// switch from one search strategy to the other depending on the number
 | 
			
		||||
	// of buckets.
 | 
			
		||||
	//
 | 
			
		||||
	// Microbenchmarks (BenchmarkHistogramNoLabels):
 | 
			
		||||
	// 11 buckets: 38.3 ns/op linear - binary 48.7 ns/op
 | 
			
		||||
	// 100 buckets: 78.1 ns/op linear - binary 54.9 ns/op
 | 
			
		||||
	// 300 buckets: 154 ns/op linear - binary 61.6 ns/op
 | 
			
		||||
	return sort.SearchFloat64s(h.upperBounds, v)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// observe is the implementation for Observe without the findBucket part.
 | 
			
		||||
func (h *histogram) observe(v float64, bucket int) {
 | 
			
		||||
	// We increment h.countAndHotIdx so that the counter in the lower
 | 
			
		||||
	// 63 bits gets incremented. At the same time, we get the new value
 | 
			
		||||
	// back, which we can use to find the currently-hot counts.
 | 
			
		||||
	n := atomic.AddUint64(&h.countAndHotIdx, 1)
 | 
			
		||||
	hotCounts := h.counts[n>>63]
 | 
			
		||||
 | 
			
		||||
	if bucket < len(h.upperBounds) {
 | 
			
		||||
		atomic.AddUint64(&hotCounts.buckets[bucket], 1)
 | 
			
		||||
	}
 | 
			
		||||
	for {
 | 
			
		||||
		oldBits := atomic.LoadUint64(&hotCounts.sumBits)
 | 
			
		||||
		newBits := math.Float64bits(math.Float64frombits(oldBits) + v)
 | 
			
		||||
		if atomic.CompareAndSwapUint64(&hotCounts.sumBits, oldBits, newBits) {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// Increment count last as we take it as a signal that the observation
 | 
			
		||||
	// is complete.
 | 
			
		||||
	atomic.AddUint64(&hotCounts.count, 1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// updateExemplar replaces the exemplar for the provided bucket. With empty
 | 
			
		||||
// labels, it's a no-op. It panics if any of the labels is invalid.
 | 
			
		||||
func (h *histogram) updateExemplar(v float64, bucket int, l Labels) {
 | 
			
		||||
	if l == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	e, err := newExemplar(v, h.now(), l)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	h.exemplars[bucket].Store(e)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HistogramVec is a Collector that bundles a set of Histograms that all share the
 | 
			
		||||
// same Desc, but have different values for their variable labels. This is used
 | 
			
		||||
// if you want to count the same thing partitioned by various dimensions
 | 
			
		||||
// (e.g. HTTP request latencies, partitioned by status code and method). Create
 | 
			
		||||
// instances with NewHistogramVec.
 | 
			
		||||
type HistogramVec struct {
 | 
			
		||||
	*metricVec
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewHistogramVec creates a new HistogramVec based on the provided HistogramOpts and
 | 
			
		||||
// partitioned by the given label names.
 | 
			
		||||
func NewHistogramVec(opts HistogramOpts, labelNames []string) *HistogramVec {
 | 
			
		||||
	desc := NewDesc(
 | 
			
		||||
		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
 | 
			
		||||
		opts.Help,
 | 
			
		||||
		labelNames,
 | 
			
		||||
		opts.ConstLabels,
 | 
			
		||||
	)
 | 
			
		||||
	return &HistogramVec{
 | 
			
		||||
		metricVec: newMetricVec(desc, func(lvs ...string) Metric {
 | 
			
		||||
			return newHistogram(desc, opts, lvs...)
 | 
			
		||||
		}),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetMetricWithLabelValues returns the Histogram for the given slice of label
 | 
			
		||||
// values (same order as the VariableLabels in Desc). If that combination of
 | 
			
		||||
// label values is accessed for the first time, a new Histogram is created.
 | 
			
		||||
//
 | 
			
		||||
// It is possible to call this method without using the returned Histogram to only
 | 
			
		||||
// create the new Histogram but leave it at its starting value, a Histogram without
 | 
			
		||||
// any observations.
 | 
			
		||||
//
 | 
			
		||||
// Keeping the Histogram for later use is possible (and should be considered if
 | 
			
		||||
// performance is critical), but keep in mind that Reset, DeleteLabelValues and
 | 
			
		||||
// Delete can be used to delete the Histogram from the HistogramVec. In that case, the
 | 
			
		||||
// Histogram will still exist, but it will not be exported anymore, even if a
 | 
			
		||||
// Histogram with the same label values is created later. See also the CounterVec
 | 
			
		||||
// example.
 | 
			
		||||
//
 | 
			
		||||
// An error is returned if the number of label values is not the same as the
 | 
			
		||||
// number of VariableLabels in Desc (minus any curried labels).
 | 
			
		||||
//
 | 
			
		||||
// Note that for more than one label value, this method is prone to mistakes
 | 
			
		||||
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
 | 
			
		||||
// an alternative to avoid that type of mistake. For higher label numbers, the
 | 
			
		||||
// latter has a much more readable (albeit more verbose) syntax, but it comes
 | 
			
		||||
// with a performance overhead (for creating and processing the Labels map).
 | 
			
		||||
// See also the GaugeVec example.
 | 
			
		||||
func (v *HistogramVec) GetMetricWithLabelValues(lvs ...string) (Observer, error) {
 | 
			
		||||
	metric, err := v.metricVec.getMetricWithLabelValues(lvs...)
 | 
			
		||||
	if metric != nil {
 | 
			
		||||
		return metric.(Observer), err
 | 
			
		||||
	}
 | 
			
		||||
	return nil, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetMetricWith returns the Histogram for the given Labels map (the label names
 | 
			
		||||
// must match those of the VariableLabels in Desc). If that label map is
 | 
			
		||||
// accessed for the first time, a new Histogram is created. Implications of
 | 
			
		||||
// creating a Histogram without using it and keeping the Histogram for later use
 | 
			
		||||
// are the same as for GetMetricWithLabelValues.
 | 
			
		||||
//
 | 
			
		||||
// An error is returned if the number and names of the Labels are inconsistent
 | 
			
		||||
// with those of the VariableLabels in Desc (minus any curried labels).
 | 
			
		||||
//
 | 
			
		||||
// This method is used for the same purpose as
 | 
			
		||||
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
 | 
			
		||||
// methods.
 | 
			
		||||
func (v *HistogramVec) GetMetricWith(labels Labels) (Observer, error) {
 | 
			
		||||
	metric, err := v.metricVec.getMetricWith(labels)
 | 
			
		||||
	if metric != nil {
 | 
			
		||||
		return metric.(Observer), err
 | 
			
		||||
	}
 | 
			
		||||
	return nil, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithLabelValues works as GetMetricWithLabelValues, but panics where
 | 
			
		||||
// GetMetricWithLabelValues would have returned an error. Not returning an
 | 
			
		||||
// error allows shortcuts like
 | 
			
		||||
//     myVec.WithLabelValues("404", "GET").Observe(42.21)
 | 
			
		||||
func (v *HistogramVec) WithLabelValues(lvs ...string) Observer {
 | 
			
		||||
	h, err := v.GetMetricWithLabelValues(lvs...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	return h
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// With works as GetMetricWith but panics where GetMetricWithLabels would have
 | 
			
		||||
// returned an error. Not returning an error allows shortcuts like
 | 
			
		||||
//     myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Observe(42.21)
 | 
			
		||||
func (v *HistogramVec) With(labels Labels) Observer {
 | 
			
		||||
	h, err := v.GetMetricWith(labels)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	return h
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CurryWith returns a vector curried with the provided labels, i.e. the
 | 
			
		||||
// returned vector has those labels pre-set for all labeled operations performed
 | 
			
		||||
// on it. The cardinality of the curried vector is reduced accordingly. The
 | 
			
		||||
// order of the remaining labels stays the same (just with the curried labels
 | 
			
		||||
// taken out of the sequence – which is relevant for the
 | 
			
		||||
// (GetMetric)WithLabelValues methods). It is possible to curry a curried
 | 
			
		||||
// vector, but only with labels not yet used for currying before.
 | 
			
		||||
//
 | 
			
		||||
// The metrics contained in the HistogramVec are shared between the curried and
 | 
			
		||||
// uncurried vectors. They are just accessed differently. Curried and uncurried
 | 
			
		||||
// vectors behave identically in terms of collection. Only one must be
 | 
			
		||||
// registered with a given registry (usually the uncurried version). The Reset
 | 
			
		||||
// method deletes all metrics, even if called on a curried vector.
 | 
			
		||||
func (v *HistogramVec) CurryWith(labels Labels) (ObserverVec, error) {
 | 
			
		||||
	vec, err := v.curryWith(labels)
 | 
			
		||||
	if vec != nil {
 | 
			
		||||
		return &HistogramVec{vec}, err
 | 
			
		||||
	}
 | 
			
		||||
	return nil, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MustCurryWith works as CurryWith but panics where CurryWith would have
 | 
			
		||||
// returned an error.
 | 
			
		||||
func (v *HistogramVec) MustCurryWith(labels Labels) ObserverVec {
 | 
			
		||||
	vec, err := v.CurryWith(labels)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	return vec
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type constHistogram struct {
 | 
			
		||||
	desc       *Desc
 | 
			
		||||
	count      uint64
 | 
			
		||||
	sum        float64
 | 
			
		||||
	buckets    map[float64]uint64
 | 
			
		||||
	labelPairs []*dto.LabelPair
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *constHistogram) Desc() *Desc {
 | 
			
		||||
	return h.desc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *constHistogram) Write(out *dto.Metric) error {
 | 
			
		||||
	his := &dto.Histogram{}
 | 
			
		||||
	buckets := make([]*dto.Bucket, 0, len(h.buckets))
 | 
			
		||||
 | 
			
		||||
	his.SampleCount = proto.Uint64(h.count)
 | 
			
		||||
	his.SampleSum = proto.Float64(h.sum)
 | 
			
		||||
 | 
			
		||||
	for upperBound, count := range h.buckets {
 | 
			
		||||
		buckets = append(buckets, &dto.Bucket{
 | 
			
		||||
			CumulativeCount: proto.Uint64(count),
 | 
			
		||||
			UpperBound:      proto.Float64(upperBound),
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(buckets) > 0 {
 | 
			
		||||
		sort.Sort(buckSort(buckets))
 | 
			
		||||
	}
 | 
			
		||||
	his.Bucket = buckets
 | 
			
		||||
 | 
			
		||||
	out.Histogram = his
 | 
			
		||||
	out.Label = h.labelPairs
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewConstHistogram returns a metric representing a Prometheus histogram with
 | 
			
		||||
// fixed values for the count, sum, and bucket counts. As those parameters
 | 
			
		||||
// cannot be changed, the returned value does not implement the Histogram
 | 
			
		||||
// interface (but only the Metric interface). Users of this package will not
 | 
			
		||||
// have much use for it in regular operations. However, when implementing custom
 | 
			
		||||
// Collectors, it is useful as a throw-away metric that is generated on the fly
 | 
			
		||||
// to send it to Prometheus in the Collect method.
 | 
			
		||||
//
 | 
			
		||||
// buckets is a map of upper bounds to cumulative counts, excluding the +Inf
 | 
			
		||||
// bucket.
 | 
			
		||||
//
 | 
			
		||||
// NewConstHistogram returns an error if the length of labelValues is not
 | 
			
		||||
// consistent with the variable labels in Desc or if Desc is invalid.
 | 
			
		||||
func NewConstHistogram(
 | 
			
		||||
	desc *Desc,
 | 
			
		||||
	count uint64,
 | 
			
		||||
	sum float64,
 | 
			
		||||
	buckets map[float64]uint64,
 | 
			
		||||
	labelValues ...string,
 | 
			
		||||
) (Metric, error) {
 | 
			
		||||
	if desc.err != nil {
 | 
			
		||||
		return nil, desc.err
 | 
			
		||||
	}
 | 
			
		||||
	if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &constHistogram{
 | 
			
		||||
		desc:       desc,
 | 
			
		||||
		count:      count,
 | 
			
		||||
		sum:        sum,
 | 
			
		||||
		buckets:    buckets,
 | 
			
		||||
		labelPairs: makeLabelPairs(desc, labelValues),
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MustNewConstHistogram is a version of NewConstHistogram that panics where
 | 
			
		||||
// NewConstMetric would have returned an error.
 | 
			
		||||
func MustNewConstHistogram(
 | 
			
		||||
	desc *Desc,
 | 
			
		||||
	count uint64,
 | 
			
		||||
	sum float64,
 | 
			
		||||
	buckets map[float64]uint64,
 | 
			
		||||
	labelValues ...string,
 | 
			
		||||
) Metric {
 | 
			
		||||
	m, err := NewConstHistogram(desc, count, sum, buckets, labelValues...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	return m
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type buckSort []*dto.Bucket
 | 
			
		||||
 | 
			
		||||
func (s buckSort) Len() int {
 | 
			
		||||
	return len(s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s buckSort) Swap(i, j int) {
 | 
			
		||||
	s[i], s[j] = s[j], s[i]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s buckSort) Less(i, j int) bool {
 | 
			
		||||
	return s[i].GetUpperBound() < s[j].GetUpperBound()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										85
									
								
								vendor/github.com/prometheus/client_golang/prometheus/internal/metric.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								vendor/github.com/prometheus/client_golang/prometheus/internal/metric.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,85 @@
 | 
			
		||||
// Copyright 2018 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 internal
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"sort"
 | 
			
		||||
 | 
			
		||||
	dto "github.com/prometheus/client_model/go"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// metricSorter is a sortable slice of *dto.Metric.
 | 
			
		||||
type metricSorter []*dto.Metric
 | 
			
		||||
 | 
			
		||||
func (s metricSorter) Len() int {
 | 
			
		||||
	return len(s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s metricSorter) Swap(i, j int) {
 | 
			
		||||
	s[i], s[j] = s[j], s[i]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s metricSorter) Less(i, j int) bool {
 | 
			
		||||
	if len(s[i].Label) != len(s[j].Label) {
 | 
			
		||||
		// This should not happen. The metrics are
 | 
			
		||||
		// inconsistent. However, we have to deal with the fact, as
 | 
			
		||||
		// people might use custom collectors or metric family injection
 | 
			
		||||
		// to create inconsistent metrics. So let's simply compare the
 | 
			
		||||
		// number of labels in this case. That will still yield
 | 
			
		||||
		// reproducible sorting.
 | 
			
		||||
		return len(s[i].Label) < len(s[j].Label)
 | 
			
		||||
	}
 | 
			
		||||
	for n, lp := range s[i].Label {
 | 
			
		||||
		vi := lp.GetValue()
 | 
			
		||||
		vj := s[j].Label[n].GetValue()
 | 
			
		||||
		if vi != vj {
 | 
			
		||||
			return vi < vj
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We should never arrive here. Multiple metrics with the same
 | 
			
		||||
	// label set in the same scrape will lead to undefined ingestion
 | 
			
		||||
	// behavior. However, as above, we have to provide stable sorting
 | 
			
		||||
	// here, even for inconsistent metrics. So sort equal metrics
 | 
			
		||||
	// by their timestamp, with missing timestamps (implying "now")
 | 
			
		||||
	// coming last.
 | 
			
		||||
	if s[i].TimestampMs == nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	if s[j].TimestampMs == nil {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return s[i].GetTimestampMs() < s[j].GetTimestampMs()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NormalizeMetricFamilies returns a MetricFamily slice with empty
 | 
			
		||||
// MetricFamilies pruned and the remaining MetricFamilies sorted by name within
 | 
			
		||||
// the slice, with the contained Metrics sorted within each MetricFamily.
 | 
			
		||||
func NormalizeMetricFamilies(metricFamiliesByName map[string]*dto.MetricFamily) []*dto.MetricFamily {
 | 
			
		||||
	for _, mf := range metricFamiliesByName {
 | 
			
		||||
		sort.Sort(metricSorter(mf.Metric))
 | 
			
		||||
	}
 | 
			
		||||
	names := make([]string, 0, len(metricFamiliesByName))
 | 
			
		||||
	for name, mf := range metricFamiliesByName {
 | 
			
		||||
		if len(mf.Metric) > 0 {
 | 
			
		||||
			names = append(names, name)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	sort.Strings(names)
 | 
			
		||||
	result := make([]*dto.MetricFamily, 0, len(names))
 | 
			
		||||
	for _, name := range names {
 | 
			
		||||
		result = append(result, metricFamiliesByName[name])
 | 
			
		||||
	}
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										87
									
								
								vendor/github.com/prometheus/client_golang/prometheus/labels.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								vendor/github.com/prometheus/client_golang/prometheus/labels.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,87 @@
 | 
			
		||||
// Copyright 2018 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 prometheus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"unicode/utf8"
 | 
			
		||||
 | 
			
		||||
	"github.com/prometheus/common/model"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Labels represents a collection of label name -> value mappings. This type is
 | 
			
		||||
// commonly used with the With(Labels) and GetMetricWith(Labels) methods of
 | 
			
		||||
// metric vector Collectors, e.g.:
 | 
			
		||||
//     myVec.With(Labels{"code": "404", "method": "GET"}).Add(42)
 | 
			
		||||
//
 | 
			
		||||
// The other use-case is the specification of constant label pairs in Opts or to
 | 
			
		||||
// create a Desc.
 | 
			
		||||
type Labels map[string]string
 | 
			
		||||
 | 
			
		||||
// reservedLabelPrefix is a prefix which is not legal in user-supplied
 | 
			
		||||
// label names.
 | 
			
		||||
const reservedLabelPrefix = "__"
 | 
			
		||||
 | 
			
		||||
var errInconsistentCardinality = errors.New("inconsistent label cardinality")
 | 
			
		||||
 | 
			
		||||
func makeInconsistentCardinalityError(fqName string, labels, labelValues []string) error {
 | 
			
		||||
	return fmt.Errorf(
 | 
			
		||||
		"%s: %q has %d variable labels named %q but %d values %q were provided",
 | 
			
		||||
		errInconsistentCardinality, fqName,
 | 
			
		||||
		len(labels), labels,
 | 
			
		||||
		len(labelValues), labelValues,
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateValuesInLabels(labels Labels, expectedNumberOfValues int) error {
 | 
			
		||||
	if len(labels) != expectedNumberOfValues {
 | 
			
		||||
		return fmt.Errorf(
 | 
			
		||||
			"%s: expected %d label values but got %d in %#v",
 | 
			
		||||
			errInconsistentCardinality, expectedNumberOfValues,
 | 
			
		||||
			len(labels), labels,
 | 
			
		||||
		)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for name, val := range labels {
 | 
			
		||||
		if !utf8.ValidString(val) {
 | 
			
		||||
			return fmt.Errorf("label %s: value %q is not valid UTF-8", name, val)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateLabelValues(vals []string, expectedNumberOfValues int) error {
 | 
			
		||||
	if len(vals) != expectedNumberOfValues {
 | 
			
		||||
		return fmt.Errorf(
 | 
			
		||||
			"%s: expected %d label values but got %d in %#v",
 | 
			
		||||
			errInconsistentCardinality, expectedNumberOfValues,
 | 
			
		||||
			len(vals), vals,
 | 
			
		||||
		)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, val := range vals {
 | 
			
		||||
		if !utf8.ValidString(val) {
 | 
			
		||||
			return fmt.Errorf("label value %q is not valid UTF-8", val)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func checkLabelName(l string) bool {
 | 
			
		||||
	return model.LabelName(l).IsValid() && !strings.HasPrefix(l, reservedLabelPrefix)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										175
									
								
								vendor/github.com/prometheus/client_golang/prometheus/metric.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								vendor/github.com/prometheus/client_golang/prometheus/metric.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,175 @@
 | 
			
		||||
// 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 prometheus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/golang/protobuf/proto"
 | 
			
		||||
	"github.com/prometheus/common/model"
 | 
			
		||||
 | 
			
		||||
	dto "github.com/prometheus/client_model/go"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var separatorByteSlice = []byte{model.SeparatorByte} // For convenient use with xxhash.
 | 
			
		||||
 | 
			
		||||
// A Metric models a single sample value with its meta data being exported to
 | 
			
		||||
// Prometheus. Implementations of Metric in this package are Gauge, Counter,
 | 
			
		||||
// Histogram, Summary, and Untyped.
 | 
			
		||||
type Metric interface {
 | 
			
		||||
	// Desc returns the descriptor for the Metric. This method idempotently
 | 
			
		||||
	// returns the same descriptor throughout the lifetime of the
 | 
			
		||||
	// Metric. The returned descriptor is immutable by contract. A Metric
 | 
			
		||||
	// unable to describe itself must return an invalid descriptor (created
 | 
			
		||||
	// with NewInvalidDesc).
 | 
			
		||||
	Desc() *Desc
 | 
			
		||||
	// Write encodes the Metric into a "Metric" Protocol Buffer data
 | 
			
		||||
	// transmission object.
 | 
			
		||||
	//
 | 
			
		||||
	// Metric implementations must observe concurrency safety as reads of
 | 
			
		||||
	// this metric may occur at any time, and any blocking occurs at the
 | 
			
		||||
	// expense of total performance of rendering all registered
 | 
			
		||||
	// metrics. Ideally, Metric implementations should support concurrent
 | 
			
		||||
	// readers.
 | 
			
		||||
	//
 | 
			
		||||
	// While populating dto.Metric, it is the responsibility of the
 | 
			
		||||
	// implementation to ensure validity of the Metric protobuf (like valid
 | 
			
		||||
	// UTF-8 strings or syntactically valid metric and label names). It is
 | 
			
		||||
	// recommended to sort labels lexicographically. Callers of Write should
 | 
			
		||||
	// still make sure of sorting if they depend on it.
 | 
			
		||||
	Write(*dto.Metric) error
 | 
			
		||||
	// TODO(beorn7): The original rationale of passing in a pre-allocated
 | 
			
		||||
	// dto.Metric protobuf to save allocations has disappeared. The
 | 
			
		||||
	// signature of this method should be changed to "Write() (*dto.Metric,
 | 
			
		||||
	// error)".
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Opts bundles the options for creating most Metric types. Each metric
 | 
			
		||||
// implementation XXX has its own XXXOpts type, but in most cases, it is just be
 | 
			
		||||
// an alias of this type (which might change when the requirement arises.)
 | 
			
		||||
//
 | 
			
		||||
// It is mandatory to set Name to a non-empty string. All other fields are
 | 
			
		||||
// optional and can safely be left at their zero value, although it is strongly
 | 
			
		||||
// encouraged to set a Help string.
 | 
			
		||||
type Opts struct {
 | 
			
		||||
	// Namespace, Subsystem, and Name are components of the fully-qualified
 | 
			
		||||
	// name of the Metric (created by joining these components with
 | 
			
		||||
	// "_"). Only Name is mandatory, the others merely help structuring the
 | 
			
		||||
	// name. Note that the fully-qualified name of the metric must be a
 | 
			
		||||
	// valid Prometheus metric name.
 | 
			
		||||
	Namespace string
 | 
			
		||||
	Subsystem string
 | 
			
		||||
	Name      string
 | 
			
		||||
 | 
			
		||||
	// Help provides information about this metric.
 | 
			
		||||
	//
 | 
			
		||||
	// Metrics with the same fully-qualified name must have the same Help
 | 
			
		||||
	// string.
 | 
			
		||||
	Help string
 | 
			
		||||
 | 
			
		||||
	// ConstLabels are used to attach fixed labels to this metric. Metrics
 | 
			
		||||
	// with the same fully-qualified name must have the same label names in
 | 
			
		||||
	// their ConstLabels.
 | 
			
		||||
	//
 | 
			
		||||
	// ConstLabels are only used rarely. In particular, do not use them to
 | 
			
		||||
	// attach the same labels to all your metrics. Those use cases are
 | 
			
		||||
	// better covered by target labels set by the scraping Prometheus
 | 
			
		||||
	// server, or by one specific metric (e.g. a build_info or a
 | 
			
		||||
	// machine_role metric). See also
 | 
			
		||||
	// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels,-not-static-scraped-labels
 | 
			
		||||
	ConstLabels Labels
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BuildFQName joins the given three name components by "_". Empty name
 | 
			
		||||
// components are ignored. If the name parameter itself is empty, an empty
 | 
			
		||||
// string is returned, no matter what. Metric implementations included in this
 | 
			
		||||
// library use this function internally to generate the fully-qualified metric
 | 
			
		||||
// name from the name component in their Opts. Users of the library will only
 | 
			
		||||
// need this function if they implement their own Metric or instantiate a Desc
 | 
			
		||||
// (with NewDesc) directly.
 | 
			
		||||
func BuildFQName(namespace, subsystem, name string) string {
 | 
			
		||||
	if name == "" {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	switch {
 | 
			
		||||
	case namespace != "" && subsystem != "":
 | 
			
		||||
		return strings.Join([]string{namespace, subsystem, name}, "_")
 | 
			
		||||
	case namespace != "":
 | 
			
		||||
		return strings.Join([]string{namespace, name}, "_")
 | 
			
		||||
	case subsystem != "":
 | 
			
		||||
		return strings.Join([]string{subsystem, name}, "_")
 | 
			
		||||
	}
 | 
			
		||||
	return name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// labelPairSorter implements sort.Interface. It is used to sort a slice of
 | 
			
		||||
// dto.LabelPair pointers.
 | 
			
		||||
type labelPairSorter []*dto.LabelPair
 | 
			
		||||
 | 
			
		||||
func (s labelPairSorter) Len() int {
 | 
			
		||||
	return len(s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s labelPairSorter) Swap(i, j int) {
 | 
			
		||||
	s[i], s[j] = s[j], s[i]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s labelPairSorter) Less(i, j int) bool {
 | 
			
		||||
	return s[i].GetName() < s[j].GetName()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type invalidMetric struct {
 | 
			
		||||
	desc *Desc
 | 
			
		||||
	err  error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewInvalidMetric returns a metric whose Write method always returns the
 | 
			
		||||
// provided error. It is useful if a Collector finds itself unable to collect
 | 
			
		||||
// a metric and wishes to report an error to the registry.
 | 
			
		||||
func NewInvalidMetric(desc *Desc, err error) Metric {
 | 
			
		||||
	return &invalidMetric{desc, err}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *invalidMetric) Desc() *Desc { return m.desc }
 | 
			
		||||
 | 
			
		||||
func (m *invalidMetric) Write(*dto.Metric) error { return m.err }
 | 
			
		||||
 | 
			
		||||
type timestampedMetric struct {
 | 
			
		||||
	Metric
 | 
			
		||||
	t time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m timestampedMetric) Write(pb *dto.Metric) error {
 | 
			
		||||
	e := m.Metric.Write(pb)
 | 
			
		||||
	pb.TimestampMs = proto.Int64(m.t.Unix()*1000 + int64(m.t.Nanosecond()/1000000))
 | 
			
		||||
	return e
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewMetricWithTimestamp returns a new Metric wrapping the provided Metric in a
 | 
			
		||||
// way that it has an explicit timestamp set to the provided Time. This is only
 | 
			
		||||
// useful in rare cases as the timestamp of a Prometheus metric should usually
 | 
			
		||||
// be set by the Prometheus server during scraping. Exceptions include mirroring
 | 
			
		||||
// metrics with given timestamps from other metric
 | 
			
		||||
// sources.
 | 
			
		||||
//
 | 
			
		||||
// NewMetricWithTimestamp works best with MustNewConstMetric,
 | 
			
		||||
// MustNewConstHistogram, and MustNewConstSummary, see example.
 | 
			
		||||
//
 | 
			
		||||
// Currently, the exposition formats used by Prometheus are limited to
 | 
			
		||||
// millisecond resolution. Thus, the provided time will be rounded down to the
 | 
			
		||||
// next full millisecond value.
 | 
			
		||||
func NewMetricWithTimestamp(t time.Time, m Metric) Metric {
 | 
			
		||||
	return timestampedMetric{Metric: m, t: t}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										64
									
								
								vendor/github.com/prometheus/client_golang/prometheus/observer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								vendor/github.com/prometheus/client_golang/prometheus/observer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
			
		||||
// Copyright 2017 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 prometheus
 | 
			
		||||
 | 
			
		||||
// Observer is the interface that wraps the Observe method, which is used by
 | 
			
		||||
// Histogram and Summary to add observations.
 | 
			
		||||
type Observer interface {
 | 
			
		||||
	Observe(float64)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// The ObserverFunc type is an adapter to allow the use of ordinary
 | 
			
		||||
// functions as Observers. If f is a function with the appropriate
 | 
			
		||||
// signature, ObserverFunc(f) is an Observer that calls f.
 | 
			
		||||
//
 | 
			
		||||
// This adapter is usually used in connection with the Timer type, and there are
 | 
			
		||||
// two general use cases:
 | 
			
		||||
//
 | 
			
		||||
// The most common one is to use a Gauge as the Observer for a Timer.
 | 
			
		||||
// See the "Gauge" Timer example.
 | 
			
		||||
//
 | 
			
		||||
// The more advanced use case is to create a function that dynamically decides
 | 
			
		||||
// which Observer to use for observing the duration. See the "Complex" Timer
 | 
			
		||||
// example.
 | 
			
		||||
type ObserverFunc func(float64)
 | 
			
		||||
 | 
			
		||||
// Observe calls f(value). It implements Observer.
 | 
			
		||||
func (f ObserverFunc) Observe(value float64) {
 | 
			
		||||
	f(value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ObserverVec is an interface implemented by `HistogramVec` and `SummaryVec`.
 | 
			
		||||
type ObserverVec interface {
 | 
			
		||||
	GetMetricWith(Labels) (Observer, error)
 | 
			
		||||
	GetMetricWithLabelValues(lvs ...string) (Observer, error)
 | 
			
		||||
	With(Labels) Observer
 | 
			
		||||
	WithLabelValues(...string) Observer
 | 
			
		||||
	CurryWith(Labels) (ObserverVec, error)
 | 
			
		||||
	MustCurryWith(Labels) ObserverVec
 | 
			
		||||
 | 
			
		||||
	Collector
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExemplarObserver is implemented by Observers that offer the option of
 | 
			
		||||
// observing a value together with an exemplar. Its ObserveWithExemplar method
 | 
			
		||||
// works like the Observe method of an Observer but also replaces the currently
 | 
			
		||||
// saved exemplar (if any) with a new one, created from the provided value, the
 | 
			
		||||
// current time as timestamp, and the provided Labels. Empty Labels will lead to
 | 
			
		||||
// a valid (label-less) exemplar. But if Labels is nil, the current exemplar is
 | 
			
		||||
// left in place. ObserveWithExemplar panics if any of the provided labels are
 | 
			
		||||
// invalid or if the provided labels contain more than 64 runes in total.
 | 
			
		||||
type ExemplarObserver interface {
 | 
			
		||||
	ObserveWithExemplar(value float64, exemplar Labels)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										151
									
								
								vendor/github.com/prometheus/client_golang/prometheus/process_collector.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								vendor/github.com/prometheus/client_golang/prometheus/process_collector.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,151 @@
 | 
			
		||||
// 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 prometheus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"os"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type processCollector struct {
 | 
			
		||||
	collectFn       func(chan<- Metric)
 | 
			
		||||
	pidFn           func() (int, error)
 | 
			
		||||
	reportErrors    bool
 | 
			
		||||
	cpuTotal        *Desc
 | 
			
		||||
	openFDs, maxFDs *Desc
 | 
			
		||||
	vsize, maxVsize *Desc
 | 
			
		||||
	rss             *Desc
 | 
			
		||||
	startTime       *Desc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ProcessCollectorOpts defines the behavior of a process metrics collector
 | 
			
		||||
// created with NewProcessCollector.
 | 
			
		||||
type ProcessCollectorOpts struct {
 | 
			
		||||
	// PidFn returns the PID of the process the collector collects metrics
 | 
			
		||||
	// for. It is called upon each collection. By default, the PID of the
 | 
			
		||||
	// current process is used, as determined on construction time by
 | 
			
		||||
	// calling os.Getpid().
 | 
			
		||||
	PidFn func() (int, error)
 | 
			
		||||
	// If non-empty, each of the collected metrics is prefixed by the
 | 
			
		||||
	// provided string and an underscore ("_").
 | 
			
		||||
	Namespace string
 | 
			
		||||
	// If true, any error encountered during collection is reported as an
 | 
			
		||||
	// invalid metric (see NewInvalidMetric). Otherwise, errors are ignored
 | 
			
		||||
	// and the collected metrics will be incomplete. (Possibly, no metrics
 | 
			
		||||
	// will be collected at all.) While that's usually not desired, it is
 | 
			
		||||
	// appropriate for the common "mix-in" of process metrics, where process
 | 
			
		||||
	// metrics are nice to have, but failing to collect them should not
 | 
			
		||||
	// disrupt the collection of the remaining metrics.
 | 
			
		||||
	ReportErrors bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewProcessCollector returns a collector which exports the current state of
 | 
			
		||||
// process metrics including CPU, memory and file descriptor usage as well as
 | 
			
		||||
// the process start time. The detailed behavior is defined by the provided
 | 
			
		||||
// ProcessCollectorOpts. The zero value of ProcessCollectorOpts creates a
 | 
			
		||||
// collector for the current process with an empty namespace string and no error
 | 
			
		||||
// reporting.
 | 
			
		||||
//
 | 
			
		||||
// The collector only works on operating systems with a Linux-style proc
 | 
			
		||||
// filesystem and on Microsoft Windows. On other operating systems, it will not
 | 
			
		||||
// collect any metrics.
 | 
			
		||||
func NewProcessCollector(opts ProcessCollectorOpts) Collector {
 | 
			
		||||
	ns := ""
 | 
			
		||||
	if len(opts.Namespace) > 0 {
 | 
			
		||||
		ns = opts.Namespace + "_"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c := &processCollector{
 | 
			
		||||
		reportErrors: opts.ReportErrors,
 | 
			
		||||
		cpuTotal: NewDesc(
 | 
			
		||||
			ns+"process_cpu_seconds_total",
 | 
			
		||||
			"Total user and system CPU time spent in seconds.",
 | 
			
		||||
			nil, nil,
 | 
			
		||||
		),
 | 
			
		||||
		openFDs: NewDesc(
 | 
			
		||||
			ns+"process_open_fds",
 | 
			
		||||
			"Number of open file descriptors.",
 | 
			
		||||
			nil, nil,
 | 
			
		||||
		),
 | 
			
		||||
		maxFDs: NewDesc(
 | 
			
		||||
			ns+"process_max_fds",
 | 
			
		||||
			"Maximum number of open file descriptors.",
 | 
			
		||||
			nil, nil,
 | 
			
		||||
		),
 | 
			
		||||
		vsize: NewDesc(
 | 
			
		||||
			ns+"process_virtual_memory_bytes",
 | 
			
		||||
			"Virtual memory size in bytes.",
 | 
			
		||||
			nil, nil,
 | 
			
		||||
		),
 | 
			
		||||
		maxVsize: NewDesc(
 | 
			
		||||
			ns+"process_virtual_memory_max_bytes",
 | 
			
		||||
			"Maximum amount of virtual memory available in bytes.",
 | 
			
		||||
			nil, nil,
 | 
			
		||||
		),
 | 
			
		||||
		rss: NewDesc(
 | 
			
		||||
			ns+"process_resident_memory_bytes",
 | 
			
		||||
			"Resident memory size in bytes.",
 | 
			
		||||
			nil, nil,
 | 
			
		||||
		),
 | 
			
		||||
		startTime: NewDesc(
 | 
			
		||||
			ns+"process_start_time_seconds",
 | 
			
		||||
			"Start time of the process since unix epoch in seconds.",
 | 
			
		||||
			nil, nil,
 | 
			
		||||
		),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if opts.PidFn == nil {
 | 
			
		||||
		pid := os.Getpid()
 | 
			
		||||
		c.pidFn = func() (int, error) { return pid, nil }
 | 
			
		||||
	} else {
 | 
			
		||||
		c.pidFn = opts.PidFn
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Set up process metric collection if supported by the runtime.
 | 
			
		||||
	if canCollectProcess() {
 | 
			
		||||
		c.collectFn = c.processCollect
 | 
			
		||||
	} else {
 | 
			
		||||
		c.collectFn = func(ch chan<- Metric) {
 | 
			
		||||
			c.reportError(ch, nil, errors.New("process metrics not supported on this platform"))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Describe returns all descriptions of the collector.
 | 
			
		||||
func (c *processCollector) Describe(ch chan<- *Desc) {
 | 
			
		||||
	ch <- c.cpuTotal
 | 
			
		||||
	ch <- c.openFDs
 | 
			
		||||
	ch <- c.maxFDs
 | 
			
		||||
	ch <- c.vsize
 | 
			
		||||
	ch <- c.maxVsize
 | 
			
		||||
	ch <- c.rss
 | 
			
		||||
	ch <- c.startTime
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Collect returns the current state of all metrics of the collector.
 | 
			
		||||
func (c *processCollector) Collect(ch chan<- Metric) {
 | 
			
		||||
	c.collectFn(ch)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *processCollector) reportError(ch chan<- Metric, desc *Desc, err error) {
 | 
			
		||||
	if !c.reportErrors {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if desc == nil {
 | 
			
		||||
		desc = NewInvalidDesc(err)
 | 
			
		||||
	}
 | 
			
		||||
	ch <- NewInvalidMetric(desc, err)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										65
									
								
								vendor/github.com/prometheus/client_golang/prometheus/process_collector_other.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								vendor/github.com/prometheus/client_golang/prometheus/process_collector_other.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,65 @@
 | 
			
		||||
// Copyright 2019 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 !windows
 | 
			
		||||
 | 
			
		||||
package prometheus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/prometheus/procfs"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func canCollectProcess() bool {
 | 
			
		||||
	_, err := procfs.NewDefaultFS()
 | 
			
		||||
	return err == nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *processCollector) processCollect(ch chan<- Metric) {
 | 
			
		||||
	pid, err := c.pidFn()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.reportError(ch, nil, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p, err := procfs.NewProc(pid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.reportError(ch, nil, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if stat, err := p.Stat(); err == nil {
 | 
			
		||||
		ch <- MustNewConstMetric(c.cpuTotal, CounterValue, stat.CPUTime())
 | 
			
		||||
		ch <- MustNewConstMetric(c.vsize, GaugeValue, float64(stat.VirtualMemory()))
 | 
			
		||||
		ch <- MustNewConstMetric(c.rss, GaugeValue, float64(stat.ResidentMemory()))
 | 
			
		||||
		if startTime, err := stat.StartTime(); err == nil {
 | 
			
		||||
			ch <- MustNewConstMetric(c.startTime, GaugeValue, startTime)
 | 
			
		||||
		} else {
 | 
			
		||||
			c.reportError(ch, c.startTime, err)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		c.reportError(ch, nil, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if fds, err := p.FileDescriptorsLen(); err == nil {
 | 
			
		||||
		ch <- MustNewConstMetric(c.openFDs, GaugeValue, float64(fds))
 | 
			
		||||
	} else {
 | 
			
		||||
		c.reportError(ch, c.openFDs, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if limits, err := p.Limits(); err == nil {
 | 
			
		||||
		ch <- MustNewConstMetric(c.maxFDs, GaugeValue, float64(limits.OpenFiles))
 | 
			
		||||
		ch <- MustNewConstMetric(c.maxVsize, GaugeValue, float64(limits.AddressSpace))
 | 
			
		||||
	} else {
 | 
			
		||||
		c.reportError(ch, nil, err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										112
									
								
								vendor/github.com/prometheus/client_golang/prometheus/process_collector_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								vendor/github.com/prometheus/client_golang/prometheus/process_collector_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,112 @@
 | 
			
		||||
// Copyright 2019 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 prometheus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/sys/windows"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func canCollectProcess() bool {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	modpsapi    = syscall.NewLazyDLL("psapi.dll")
 | 
			
		||||
	modkernel32 = syscall.NewLazyDLL("kernel32.dll")
 | 
			
		||||
 | 
			
		||||
	procGetProcessMemoryInfo  = modpsapi.NewProc("GetProcessMemoryInfo")
 | 
			
		||||
	procGetProcessHandleCount = modkernel32.NewProc("GetProcessHandleCount")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type processMemoryCounters struct {
 | 
			
		||||
	// https://docs.microsoft.com/en-us/windows/desktop/api/psapi/ns-psapi-_process_memory_counters_ex
 | 
			
		||||
	_                          uint32
 | 
			
		||||
	PageFaultCount             uint32
 | 
			
		||||
	PeakWorkingSetSize         uint64
 | 
			
		||||
	WorkingSetSize             uint64
 | 
			
		||||
	QuotaPeakPagedPoolUsage    uint64
 | 
			
		||||
	QuotaPagedPoolUsage        uint64
 | 
			
		||||
	QuotaPeakNonPagedPoolUsage uint64
 | 
			
		||||
	QuotaNonPagedPoolUsage     uint64
 | 
			
		||||
	PagefileUsage              uint64
 | 
			
		||||
	PeakPagefileUsage          uint64
 | 
			
		||||
	PrivateUsage               uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getProcessMemoryInfo(handle windows.Handle) (processMemoryCounters, error) {
 | 
			
		||||
	mem := processMemoryCounters{}
 | 
			
		||||
	r1, _, err := procGetProcessMemoryInfo.Call(
 | 
			
		||||
		uintptr(handle),
 | 
			
		||||
		uintptr(unsafe.Pointer(&mem)),
 | 
			
		||||
		uintptr(unsafe.Sizeof(mem)),
 | 
			
		||||
	)
 | 
			
		||||
	if r1 != 1 {
 | 
			
		||||
		return mem, err
 | 
			
		||||
	} else {
 | 
			
		||||
		return mem, nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getProcessHandleCount(handle windows.Handle) (uint32, error) {
 | 
			
		||||
	var count uint32
 | 
			
		||||
	r1, _, err := procGetProcessHandleCount.Call(
 | 
			
		||||
		uintptr(handle),
 | 
			
		||||
		uintptr(unsafe.Pointer(&count)),
 | 
			
		||||
	)
 | 
			
		||||
	if r1 != 1 {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	} else {
 | 
			
		||||
		return count, nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *processCollector) processCollect(ch chan<- Metric) {
 | 
			
		||||
	h, err := windows.GetCurrentProcess()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.reportError(ch, nil, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var startTime, exitTime, kernelTime, userTime windows.Filetime
 | 
			
		||||
	err = windows.GetProcessTimes(h, &startTime, &exitTime, &kernelTime, &userTime)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.reportError(ch, nil, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	ch <- MustNewConstMetric(c.startTime, GaugeValue, float64(startTime.Nanoseconds()/1e9))
 | 
			
		||||
	ch <- MustNewConstMetric(c.cpuTotal, CounterValue, fileTimeToSeconds(kernelTime)+fileTimeToSeconds(userTime))
 | 
			
		||||
 | 
			
		||||
	mem, err := getProcessMemoryInfo(h)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.reportError(ch, nil, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	ch <- MustNewConstMetric(c.vsize, GaugeValue, float64(mem.PrivateUsage))
 | 
			
		||||
	ch <- MustNewConstMetric(c.rss, GaugeValue, float64(mem.WorkingSetSize))
 | 
			
		||||
 | 
			
		||||
	handles, err := getProcessHandleCount(h)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.reportError(ch, nil, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	ch <- MustNewConstMetric(c.openFDs, GaugeValue, float64(handles))
 | 
			
		||||
	ch <- MustNewConstMetric(c.maxFDs, GaugeValue, float64(16*1024*1024)) // Windows has a hard-coded max limit, not per-process.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fileTimeToSeconds(ft windows.Filetime) float64 {
 | 
			
		||||
	return float64(uint64(ft.HighDateTime)<<32+uint64(ft.LowDateTime)) / 1e7
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										366
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										366
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,366 @@
 | 
			
		||||
// Copyright 2017 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 promhttp
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	closeNotifier = 1 << iota
 | 
			
		||||
	flusher
 | 
			
		||||
	hijacker
 | 
			
		||||
	readerFrom
 | 
			
		||||
	pusher
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type delegator interface {
 | 
			
		||||
	http.ResponseWriter
 | 
			
		||||
 | 
			
		||||
	Status() int
 | 
			
		||||
	Written() int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type responseWriterDelegator struct {
 | 
			
		||||
	http.ResponseWriter
 | 
			
		||||
 | 
			
		||||
	status             int
 | 
			
		||||
	written            int64
 | 
			
		||||
	wroteHeader        bool
 | 
			
		||||
	observeWriteHeader func(int)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *responseWriterDelegator) Status() int {
 | 
			
		||||
	return r.status
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *responseWriterDelegator) Written() int64 {
 | 
			
		||||
	return r.written
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *responseWriterDelegator) WriteHeader(code int) {
 | 
			
		||||
	r.status = code
 | 
			
		||||
	r.wroteHeader = true
 | 
			
		||||
	r.ResponseWriter.WriteHeader(code)
 | 
			
		||||
	if r.observeWriteHeader != nil {
 | 
			
		||||
		r.observeWriteHeader(code)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *responseWriterDelegator) Write(b []byte) (int, error) {
 | 
			
		||||
	// If applicable, call WriteHeader here so that observeWriteHeader is
 | 
			
		||||
	// handled appropriately.
 | 
			
		||||
	if !r.wroteHeader {
 | 
			
		||||
		r.WriteHeader(http.StatusOK)
 | 
			
		||||
	}
 | 
			
		||||
	n, err := r.ResponseWriter.Write(b)
 | 
			
		||||
	r.written += int64(n)
 | 
			
		||||
	return n, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type closeNotifierDelegator struct{ *responseWriterDelegator }
 | 
			
		||||
type flusherDelegator struct{ *responseWriterDelegator }
 | 
			
		||||
type hijackerDelegator struct{ *responseWriterDelegator }
 | 
			
		||||
type readerFromDelegator struct{ *responseWriterDelegator }
 | 
			
		||||
type pusherDelegator struct{ *responseWriterDelegator }
 | 
			
		||||
 | 
			
		||||
func (d closeNotifierDelegator) CloseNotify() <-chan bool {
 | 
			
		||||
	//lint:ignore SA1019 http.CloseNotifier is deprecated but we don't want to
 | 
			
		||||
	//remove support from client_golang yet.
 | 
			
		||||
	return d.ResponseWriter.(http.CloseNotifier).CloseNotify()
 | 
			
		||||
}
 | 
			
		||||
func (d flusherDelegator) Flush() {
 | 
			
		||||
	// If applicable, call WriteHeader here so that observeWriteHeader is
 | 
			
		||||
	// handled appropriately.
 | 
			
		||||
	if !d.wroteHeader {
 | 
			
		||||
		d.WriteHeader(http.StatusOK)
 | 
			
		||||
	}
 | 
			
		||||
	d.ResponseWriter.(http.Flusher).Flush()
 | 
			
		||||
}
 | 
			
		||||
func (d hijackerDelegator) Hijack() (net.Conn, *bufio.ReadWriter, error) {
 | 
			
		||||
	return d.ResponseWriter.(http.Hijacker).Hijack()
 | 
			
		||||
}
 | 
			
		||||
func (d readerFromDelegator) ReadFrom(re io.Reader) (int64, error) {
 | 
			
		||||
	// If applicable, call WriteHeader here so that observeWriteHeader is
 | 
			
		||||
	// handled appropriately.
 | 
			
		||||
	if !d.wroteHeader {
 | 
			
		||||
		d.WriteHeader(http.StatusOK)
 | 
			
		||||
	}
 | 
			
		||||
	n, err := d.ResponseWriter.(io.ReaderFrom).ReadFrom(re)
 | 
			
		||||
	d.written += n
 | 
			
		||||
	return n, err
 | 
			
		||||
}
 | 
			
		||||
func (d pusherDelegator) Push(target string, opts *http.PushOptions) error {
 | 
			
		||||
	return d.ResponseWriter.(http.Pusher).Push(target, opts)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var pickDelegator = make([]func(*responseWriterDelegator) delegator, 32)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	// TODO(beorn7): Code generation would help here.
 | 
			
		||||
	pickDelegator[0] = func(d *responseWriterDelegator) delegator { // 0
 | 
			
		||||
		return d
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[closeNotifier] = func(d *responseWriterDelegator) delegator { // 1
 | 
			
		||||
		return closeNotifierDelegator{d}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[flusher] = func(d *responseWriterDelegator) delegator { // 2
 | 
			
		||||
		return flusherDelegator{d}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 3
 | 
			
		||||
		return struct {
 | 
			
		||||
			*responseWriterDelegator
 | 
			
		||||
			http.Flusher
 | 
			
		||||
			http.CloseNotifier
 | 
			
		||||
		}{d, flusherDelegator{d}, closeNotifierDelegator{d}}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[hijacker] = func(d *responseWriterDelegator) delegator { // 4
 | 
			
		||||
		return hijackerDelegator{d}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 5
 | 
			
		||||
		return struct {
 | 
			
		||||
			*responseWriterDelegator
 | 
			
		||||
			http.Hijacker
 | 
			
		||||
			http.CloseNotifier
 | 
			
		||||
		}{d, hijackerDelegator{d}, closeNotifierDelegator{d}}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 6
 | 
			
		||||
		return struct {
 | 
			
		||||
			*responseWriterDelegator
 | 
			
		||||
			http.Hijacker
 | 
			
		||||
			http.Flusher
 | 
			
		||||
		}{d, hijackerDelegator{d}, flusherDelegator{d}}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 7
 | 
			
		||||
		return struct {
 | 
			
		||||
			*responseWriterDelegator
 | 
			
		||||
			http.Hijacker
 | 
			
		||||
			http.Flusher
 | 
			
		||||
			http.CloseNotifier
 | 
			
		||||
		}{d, hijackerDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[readerFrom] = func(d *responseWriterDelegator) delegator { // 8
 | 
			
		||||
		return readerFromDelegator{d}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[readerFrom+closeNotifier] = func(d *responseWriterDelegator) delegator { // 9
 | 
			
		||||
		return struct {
 | 
			
		||||
			*responseWriterDelegator
 | 
			
		||||
			io.ReaderFrom
 | 
			
		||||
			http.CloseNotifier
 | 
			
		||||
		}{d, readerFromDelegator{d}, closeNotifierDelegator{d}}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[readerFrom+flusher] = func(d *responseWriterDelegator) delegator { // 10
 | 
			
		||||
		return struct {
 | 
			
		||||
			*responseWriterDelegator
 | 
			
		||||
			io.ReaderFrom
 | 
			
		||||
			http.Flusher
 | 
			
		||||
		}{d, readerFromDelegator{d}, flusherDelegator{d}}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[readerFrom+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 11
 | 
			
		||||
		return struct {
 | 
			
		||||
			*responseWriterDelegator
 | 
			
		||||
			io.ReaderFrom
 | 
			
		||||
			http.Flusher
 | 
			
		||||
			http.CloseNotifier
 | 
			
		||||
		}{d, readerFromDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[readerFrom+hijacker] = func(d *responseWriterDelegator) delegator { // 12
 | 
			
		||||
		return struct {
 | 
			
		||||
			*responseWriterDelegator
 | 
			
		||||
			io.ReaderFrom
 | 
			
		||||
			http.Hijacker
 | 
			
		||||
		}{d, readerFromDelegator{d}, hijackerDelegator{d}}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[readerFrom+hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 13
 | 
			
		||||
		return struct {
 | 
			
		||||
			*responseWriterDelegator
 | 
			
		||||
			io.ReaderFrom
 | 
			
		||||
			http.Hijacker
 | 
			
		||||
			http.CloseNotifier
 | 
			
		||||
		}{d, readerFromDelegator{d}, hijackerDelegator{d}, closeNotifierDelegator{d}}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[readerFrom+hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 14
 | 
			
		||||
		return struct {
 | 
			
		||||
			*responseWriterDelegator
 | 
			
		||||
			io.ReaderFrom
 | 
			
		||||
			http.Hijacker
 | 
			
		||||
			http.Flusher
 | 
			
		||||
		}{d, readerFromDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[readerFrom+hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 15
 | 
			
		||||
		return struct {
 | 
			
		||||
			*responseWriterDelegator
 | 
			
		||||
			io.ReaderFrom
 | 
			
		||||
			http.Hijacker
 | 
			
		||||
			http.Flusher
 | 
			
		||||
			http.CloseNotifier
 | 
			
		||||
		}{d, readerFromDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[pusher] = func(d *responseWriterDelegator) delegator { // 16
 | 
			
		||||
		return pusherDelegator{d}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[pusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 17
 | 
			
		||||
		return struct {
 | 
			
		||||
			*responseWriterDelegator
 | 
			
		||||
			http.Pusher
 | 
			
		||||
			http.CloseNotifier
 | 
			
		||||
		}{d, pusherDelegator{d}, closeNotifierDelegator{d}}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[pusher+flusher] = func(d *responseWriterDelegator) delegator { // 18
 | 
			
		||||
		return struct {
 | 
			
		||||
			*responseWriterDelegator
 | 
			
		||||
			http.Pusher
 | 
			
		||||
			http.Flusher
 | 
			
		||||
		}{d, pusherDelegator{d}, flusherDelegator{d}}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[pusher+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 19
 | 
			
		||||
		return struct {
 | 
			
		||||
			*responseWriterDelegator
 | 
			
		||||
			http.Pusher
 | 
			
		||||
			http.Flusher
 | 
			
		||||
			http.CloseNotifier
 | 
			
		||||
		}{d, pusherDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[pusher+hijacker] = func(d *responseWriterDelegator) delegator { // 20
 | 
			
		||||
		return struct {
 | 
			
		||||
			*responseWriterDelegator
 | 
			
		||||
			http.Pusher
 | 
			
		||||
			http.Hijacker
 | 
			
		||||
		}{d, pusherDelegator{d}, hijackerDelegator{d}}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[pusher+hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 21
 | 
			
		||||
		return struct {
 | 
			
		||||
			*responseWriterDelegator
 | 
			
		||||
			http.Pusher
 | 
			
		||||
			http.Hijacker
 | 
			
		||||
			http.CloseNotifier
 | 
			
		||||
		}{d, pusherDelegator{d}, hijackerDelegator{d}, closeNotifierDelegator{d}}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[pusher+hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 22
 | 
			
		||||
		return struct {
 | 
			
		||||
			*responseWriterDelegator
 | 
			
		||||
			http.Pusher
 | 
			
		||||
			http.Hijacker
 | 
			
		||||
			http.Flusher
 | 
			
		||||
		}{d, pusherDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[pusher+hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { //23
 | 
			
		||||
		return struct {
 | 
			
		||||
			*responseWriterDelegator
 | 
			
		||||
			http.Pusher
 | 
			
		||||
			http.Hijacker
 | 
			
		||||
			http.Flusher
 | 
			
		||||
			http.CloseNotifier
 | 
			
		||||
		}{d, pusherDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[pusher+readerFrom] = func(d *responseWriterDelegator) delegator { // 24
 | 
			
		||||
		return struct {
 | 
			
		||||
			*responseWriterDelegator
 | 
			
		||||
			http.Pusher
 | 
			
		||||
			io.ReaderFrom
 | 
			
		||||
		}{d, pusherDelegator{d}, readerFromDelegator{d}}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[pusher+readerFrom+closeNotifier] = func(d *responseWriterDelegator) delegator { // 25
 | 
			
		||||
		return struct {
 | 
			
		||||
			*responseWriterDelegator
 | 
			
		||||
			http.Pusher
 | 
			
		||||
			io.ReaderFrom
 | 
			
		||||
			http.CloseNotifier
 | 
			
		||||
		}{d, pusherDelegator{d}, readerFromDelegator{d}, closeNotifierDelegator{d}}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[pusher+readerFrom+flusher] = func(d *responseWriterDelegator) delegator { // 26
 | 
			
		||||
		return struct {
 | 
			
		||||
			*responseWriterDelegator
 | 
			
		||||
			http.Pusher
 | 
			
		||||
			io.ReaderFrom
 | 
			
		||||
			http.Flusher
 | 
			
		||||
		}{d, pusherDelegator{d}, readerFromDelegator{d}, flusherDelegator{d}}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[pusher+readerFrom+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 27
 | 
			
		||||
		return struct {
 | 
			
		||||
			*responseWriterDelegator
 | 
			
		||||
			http.Pusher
 | 
			
		||||
			io.ReaderFrom
 | 
			
		||||
			http.Flusher
 | 
			
		||||
			http.CloseNotifier
 | 
			
		||||
		}{d, pusherDelegator{d}, readerFromDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[pusher+readerFrom+hijacker] = func(d *responseWriterDelegator) delegator { // 28
 | 
			
		||||
		return struct {
 | 
			
		||||
			*responseWriterDelegator
 | 
			
		||||
			http.Pusher
 | 
			
		||||
			io.ReaderFrom
 | 
			
		||||
			http.Hijacker
 | 
			
		||||
		}{d, pusherDelegator{d}, readerFromDelegator{d}, hijackerDelegator{d}}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[pusher+readerFrom+hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 29
 | 
			
		||||
		return struct {
 | 
			
		||||
			*responseWriterDelegator
 | 
			
		||||
			http.Pusher
 | 
			
		||||
			io.ReaderFrom
 | 
			
		||||
			http.Hijacker
 | 
			
		||||
			http.CloseNotifier
 | 
			
		||||
		}{d, pusherDelegator{d}, readerFromDelegator{d}, hijackerDelegator{d}, closeNotifierDelegator{d}}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[pusher+readerFrom+hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 30
 | 
			
		||||
		return struct {
 | 
			
		||||
			*responseWriterDelegator
 | 
			
		||||
			http.Pusher
 | 
			
		||||
			io.ReaderFrom
 | 
			
		||||
			http.Hijacker
 | 
			
		||||
			http.Flusher
 | 
			
		||||
		}{d, pusherDelegator{d}, readerFromDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}}
 | 
			
		||||
	}
 | 
			
		||||
	pickDelegator[pusher+readerFrom+hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 31
 | 
			
		||||
		return struct {
 | 
			
		||||
			*responseWriterDelegator
 | 
			
		||||
			http.Pusher
 | 
			
		||||
			io.ReaderFrom
 | 
			
		||||
			http.Hijacker
 | 
			
		||||
			http.Flusher
 | 
			
		||||
			http.CloseNotifier
 | 
			
		||||
		}{d, pusherDelegator{d}, readerFromDelegator{d}, hijackerDelegator{d}, flusherDelegator{d}, closeNotifierDelegator{d}}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newDelegator(w http.ResponseWriter, observeWriteHeaderFunc func(int)) delegator {
 | 
			
		||||
	d := &responseWriterDelegator{
 | 
			
		||||
		ResponseWriter:     w,
 | 
			
		||||
		observeWriteHeader: observeWriteHeaderFunc,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	id := 0
 | 
			
		||||
	//lint:ignore SA1019 http.CloseNotifier is deprecated but we don't want to
 | 
			
		||||
	//remove support from client_golang yet.
 | 
			
		||||
	if _, ok := w.(http.CloseNotifier); ok {
 | 
			
		||||
		id += closeNotifier
 | 
			
		||||
	}
 | 
			
		||||
	if _, ok := w.(http.Flusher); ok {
 | 
			
		||||
		id += flusher
 | 
			
		||||
	}
 | 
			
		||||
	if _, ok := w.(http.Hijacker); ok {
 | 
			
		||||
		id += hijacker
 | 
			
		||||
	}
 | 
			
		||||
	if _, ok := w.(io.ReaderFrom); ok {
 | 
			
		||||
		id += readerFrom
 | 
			
		||||
	}
 | 
			
		||||
	if _, ok := w.(http.Pusher); ok {
 | 
			
		||||
		id += pusher
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return pickDelegator[id](d)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										380
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										380
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,380 @@
 | 
			
		||||
// Copyright 2016 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 promhttp provides tooling around HTTP servers and clients.
 | 
			
		||||
//
 | 
			
		||||
// First, the package allows the creation of http.Handler instances to expose
 | 
			
		||||
// Prometheus metrics via HTTP. promhttp.Handler acts on the
 | 
			
		||||
// prometheus.DefaultGatherer. With HandlerFor, you can create a handler for a
 | 
			
		||||
// custom registry or anything that implements the Gatherer interface. It also
 | 
			
		||||
// allows the creation of handlers that act differently on errors or allow to
 | 
			
		||||
// log errors.
 | 
			
		||||
//
 | 
			
		||||
// Second, the package provides tooling to instrument instances of http.Handler
 | 
			
		||||
// via middleware. Middleware wrappers follow the naming scheme
 | 
			
		||||
// InstrumentHandlerX, where X describes the intended use of the middleware.
 | 
			
		||||
// See each function's doc comment for specific details.
 | 
			
		||||
//
 | 
			
		||||
// Finally, the package allows for an http.RoundTripper to be instrumented via
 | 
			
		||||
// middleware. Middleware wrappers follow the naming scheme
 | 
			
		||||
// InstrumentRoundTripperX, where X describes the intended use of the
 | 
			
		||||
// middleware. See each function's doc comment for specific details.
 | 
			
		||||
package promhttp
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"compress/gzip"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/prometheus/common/expfmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/prometheus/client_golang/prometheus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	contentTypeHeader     = "Content-Type"
 | 
			
		||||
	contentEncodingHeader = "Content-Encoding"
 | 
			
		||||
	acceptEncodingHeader  = "Accept-Encoding"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var gzipPool = sync.Pool{
 | 
			
		||||
	New: func() interface{} {
 | 
			
		||||
		return gzip.NewWriter(nil)
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Handler returns an http.Handler for the prometheus.DefaultGatherer, using
 | 
			
		||||
// default HandlerOpts, i.e. it reports the first error as an HTTP error, it has
 | 
			
		||||
// no error logging, and it applies compression if requested by the client.
 | 
			
		||||
//
 | 
			
		||||
// The returned http.Handler is already instrumented using the
 | 
			
		||||
// InstrumentMetricHandler function and the prometheus.DefaultRegisterer. If you
 | 
			
		||||
// create multiple http.Handlers by separate calls of the Handler function, the
 | 
			
		||||
// metrics used for instrumentation will be shared between them, providing
 | 
			
		||||
// global scrape counts.
 | 
			
		||||
//
 | 
			
		||||
// This function is meant to cover the bulk of basic use cases. If you are doing
 | 
			
		||||
// anything that requires more customization (including using a non-default
 | 
			
		||||
// Gatherer, different instrumentation, and non-default HandlerOpts), use the
 | 
			
		||||
// HandlerFor function. See there for details.
 | 
			
		||||
func Handler() http.Handler {
 | 
			
		||||
	return InstrumentMetricHandler(
 | 
			
		||||
		prometheus.DefaultRegisterer, HandlerFor(prometheus.DefaultGatherer, HandlerOpts{}),
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandlerFor returns an uninstrumented http.Handler for the provided
 | 
			
		||||
// Gatherer. The behavior of the Handler is defined by the provided
 | 
			
		||||
// HandlerOpts. Thus, HandlerFor is useful to create http.Handlers for custom
 | 
			
		||||
// Gatherers, with non-default HandlerOpts, and/or with custom (or no)
 | 
			
		||||
// instrumentation. Use the InstrumentMetricHandler function to apply the same
 | 
			
		||||
// kind of instrumentation as it is used by the Handler function.
 | 
			
		||||
func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
 | 
			
		||||
	var (
 | 
			
		||||
		inFlightSem chan struct{}
 | 
			
		||||
		errCnt      = prometheus.NewCounterVec(
 | 
			
		||||
			prometheus.CounterOpts{
 | 
			
		||||
				Name: "promhttp_metric_handler_errors_total",
 | 
			
		||||
				Help: "Total number of internal errors encountered by the promhttp metric handler.",
 | 
			
		||||
			},
 | 
			
		||||
			[]string{"cause"},
 | 
			
		||||
		)
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	if opts.MaxRequestsInFlight > 0 {
 | 
			
		||||
		inFlightSem = make(chan struct{}, opts.MaxRequestsInFlight)
 | 
			
		||||
	}
 | 
			
		||||
	if opts.Registry != nil {
 | 
			
		||||
		// Initialize all possibilites that can occur below.
 | 
			
		||||
		errCnt.WithLabelValues("gathering")
 | 
			
		||||
		errCnt.WithLabelValues("encoding")
 | 
			
		||||
		if err := opts.Registry.Register(errCnt); err != nil {
 | 
			
		||||
			if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
 | 
			
		||||
				errCnt = are.ExistingCollector.(*prometheus.CounterVec)
 | 
			
		||||
			} else {
 | 
			
		||||
				panic(err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	h := http.HandlerFunc(func(rsp http.ResponseWriter, req *http.Request) {
 | 
			
		||||
		if inFlightSem != nil {
 | 
			
		||||
			select {
 | 
			
		||||
			case inFlightSem <- struct{}{}: // All good, carry on.
 | 
			
		||||
				defer func() { <-inFlightSem }()
 | 
			
		||||
			default:
 | 
			
		||||
				http.Error(rsp, fmt.Sprintf(
 | 
			
		||||
					"Limit of concurrent requests reached (%d), try again later.", opts.MaxRequestsInFlight,
 | 
			
		||||
				), http.StatusServiceUnavailable)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		mfs, err := reg.Gather()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if opts.ErrorLog != nil {
 | 
			
		||||
				opts.ErrorLog.Println("error gathering metrics:", err)
 | 
			
		||||
			}
 | 
			
		||||
			errCnt.WithLabelValues("gathering").Inc()
 | 
			
		||||
			switch opts.ErrorHandling {
 | 
			
		||||
			case PanicOnError:
 | 
			
		||||
				panic(err)
 | 
			
		||||
			case ContinueOnError:
 | 
			
		||||
				if len(mfs) == 0 {
 | 
			
		||||
					// Still report the error if no metrics have been gathered.
 | 
			
		||||
					httpError(rsp, err)
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			case HTTPErrorOnError:
 | 
			
		||||
				httpError(rsp, err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var contentType expfmt.Format
 | 
			
		||||
		if opts.EnableOpenMetrics {
 | 
			
		||||
			contentType = expfmt.NegotiateIncludingOpenMetrics(req.Header)
 | 
			
		||||
		} else {
 | 
			
		||||
			contentType = expfmt.Negotiate(req.Header)
 | 
			
		||||
		}
 | 
			
		||||
		header := rsp.Header()
 | 
			
		||||
		header.Set(contentTypeHeader, string(contentType))
 | 
			
		||||
 | 
			
		||||
		w := io.Writer(rsp)
 | 
			
		||||
		if !opts.DisableCompression && gzipAccepted(req.Header) {
 | 
			
		||||
			header.Set(contentEncodingHeader, "gzip")
 | 
			
		||||
			gz := gzipPool.Get().(*gzip.Writer)
 | 
			
		||||
			defer gzipPool.Put(gz)
 | 
			
		||||
 | 
			
		||||
			gz.Reset(w)
 | 
			
		||||
			defer gz.Close()
 | 
			
		||||
 | 
			
		||||
			w = gz
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		enc := expfmt.NewEncoder(w, contentType)
 | 
			
		||||
 | 
			
		||||
		var lastErr error
 | 
			
		||||
 | 
			
		||||
		// handleError handles the error according to opts.ErrorHandling
 | 
			
		||||
		// and returns true if we have to abort after the handling.
 | 
			
		||||
		handleError := func(err error) bool {
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
			lastErr = err
 | 
			
		||||
			if opts.ErrorLog != nil {
 | 
			
		||||
				opts.ErrorLog.Println("error encoding and sending metric family:", err)
 | 
			
		||||
			}
 | 
			
		||||
			errCnt.WithLabelValues("encoding").Inc()
 | 
			
		||||
			switch opts.ErrorHandling {
 | 
			
		||||
			case PanicOnError:
 | 
			
		||||
				panic(err)
 | 
			
		||||
			case HTTPErrorOnError:
 | 
			
		||||
				httpError(rsp, err)
 | 
			
		||||
				return true
 | 
			
		||||
			}
 | 
			
		||||
			// Do nothing in all other cases, including ContinueOnError.
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, mf := range mfs {
 | 
			
		||||
			if handleError(enc.Encode(mf)) {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if closer, ok := enc.(expfmt.Closer); ok {
 | 
			
		||||
			// This in particular takes care of the final "# EOF\n" line for OpenMetrics.
 | 
			
		||||
			if handleError(closer.Close()) {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if lastErr != nil {
 | 
			
		||||
			httpError(rsp, lastErr)
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	if opts.Timeout <= 0 {
 | 
			
		||||
		return h
 | 
			
		||||
	}
 | 
			
		||||
	return http.TimeoutHandler(h, opts.Timeout, fmt.Sprintf(
 | 
			
		||||
		"Exceeded configured timeout of %v.\n",
 | 
			
		||||
		opts.Timeout,
 | 
			
		||||
	))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InstrumentMetricHandler is usually used with an http.Handler returned by the
 | 
			
		||||
// HandlerFor function. It instruments the provided http.Handler with two
 | 
			
		||||
// metrics: A counter vector "promhttp_metric_handler_requests_total" to count
 | 
			
		||||
// scrapes partitioned by HTTP status code, and a gauge
 | 
			
		||||
// "promhttp_metric_handler_requests_in_flight" to track the number of
 | 
			
		||||
// simultaneous scrapes. This function idempotently registers collectors for
 | 
			
		||||
// both metrics with the provided Registerer. It panics if the registration
 | 
			
		||||
// fails. The provided metrics are useful to see how many scrapes hit the
 | 
			
		||||
// monitored target (which could be from different Prometheus servers or other
 | 
			
		||||
// scrapers), and how often they overlap (which would result in more than one
 | 
			
		||||
// scrape in flight at the same time). Note that the scrapes-in-flight gauge
 | 
			
		||||
// will contain the scrape by which it is exposed, while the scrape counter will
 | 
			
		||||
// only get incremented after the scrape is complete (as only then the status
 | 
			
		||||
// code is known). For tracking scrape durations, use the
 | 
			
		||||
// "scrape_duration_seconds" gauge created by the Prometheus server upon each
 | 
			
		||||
// scrape.
 | 
			
		||||
func InstrumentMetricHandler(reg prometheus.Registerer, handler http.Handler) http.Handler {
 | 
			
		||||
	cnt := prometheus.NewCounterVec(
 | 
			
		||||
		prometheus.CounterOpts{
 | 
			
		||||
			Name: "promhttp_metric_handler_requests_total",
 | 
			
		||||
			Help: "Total number of scrapes by HTTP status code.",
 | 
			
		||||
		},
 | 
			
		||||
		[]string{"code"},
 | 
			
		||||
	)
 | 
			
		||||
	// Initialize the most likely HTTP status codes.
 | 
			
		||||
	cnt.WithLabelValues("200")
 | 
			
		||||
	cnt.WithLabelValues("500")
 | 
			
		||||
	cnt.WithLabelValues("503")
 | 
			
		||||
	if err := reg.Register(cnt); err != nil {
 | 
			
		||||
		if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
 | 
			
		||||
			cnt = are.ExistingCollector.(*prometheus.CounterVec)
 | 
			
		||||
		} else {
 | 
			
		||||
			panic(err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	gge := prometheus.NewGauge(prometheus.GaugeOpts{
 | 
			
		||||
		Name: "promhttp_metric_handler_requests_in_flight",
 | 
			
		||||
		Help: "Current number of scrapes being served.",
 | 
			
		||||
	})
 | 
			
		||||
	if err := reg.Register(gge); err != nil {
 | 
			
		||||
		if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
 | 
			
		||||
			gge = are.ExistingCollector.(prometheus.Gauge)
 | 
			
		||||
		} else {
 | 
			
		||||
			panic(err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return InstrumentHandlerCounter(cnt, InstrumentHandlerInFlight(gge, handler))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandlerErrorHandling defines how a Handler serving metrics will handle
 | 
			
		||||
// errors.
 | 
			
		||||
type HandlerErrorHandling int
 | 
			
		||||
 | 
			
		||||
// These constants cause handlers serving metrics to behave as described if
 | 
			
		||||
// errors are encountered.
 | 
			
		||||
const (
 | 
			
		||||
	// Serve an HTTP status code 500 upon the first error
 | 
			
		||||
	// encountered. Report the error message in the body.
 | 
			
		||||
	HTTPErrorOnError HandlerErrorHandling = iota
 | 
			
		||||
	// Ignore errors and try to serve as many metrics as possible.  However,
 | 
			
		||||
	// if no metrics can be served, serve an HTTP status code 500 and the
 | 
			
		||||
	// last error message in the body. Only use this in deliberate "best
 | 
			
		||||
	// effort" metrics collection scenarios. In this case, it is highly
 | 
			
		||||
	// recommended to provide other means of detecting errors: By setting an
 | 
			
		||||
	// ErrorLog in HandlerOpts, the errors are logged. By providing a
 | 
			
		||||
	// Registry in HandlerOpts, the exposed metrics include an error counter
 | 
			
		||||
	// "promhttp_metric_handler_errors_total", which can be used for
 | 
			
		||||
	// alerts.
 | 
			
		||||
	ContinueOnError
 | 
			
		||||
	// Panic upon the first error encountered (useful for "crash only" apps).
 | 
			
		||||
	PanicOnError
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Logger is the minimal interface HandlerOpts needs for logging. Note that
 | 
			
		||||
// log.Logger from the standard library implements this interface, and it is
 | 
			
		||||
// easy to implement by custom loggers, if they don't do so already anyway.
 | 
			
		||||
type Logger interface {
 | 
			
		||||
	Println(v ...interface{})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandlerOpts specifies options how to serve metrics via an http.Handler. The
 | 
			
		||||
// zero value of HandlerOpts is a reasonable default.
 | 
			
		||||
type HandlerOpts struct {
 | 
			
		||||
	// ErrorLog specifies an optional logger for errors collecting and
 | 
			
		||||
	// serving metrics. If nil, errors are not logged at all.
 | 
			
		||||
	ErrorLog Logger
 | 
			
		||||
	// ErrorHandling defines how errors are handled. Note that errors are
 | 
			
		||||
	// logged regardless of the configured ErrorHandling provided ErrorLog
 | 
			
		||||
	// is not nil.
 | 
			
		||||
	ErrorHandling HandlerErrorHandling
 | 
			
		||||
	// If Registry is not nil, it is used to register a metric
 | 
			
		||||
	// "promhttp_metric_handler_errors_total", partitioned by "cause". A
 | 
			
		||||
	// failed registration causes a panic. Note that this error counter is
 | 
			
		||||
	// different from the instrumentation you get from the various
 | 
			
		||||
	// InstrumentHandler... helpers. It counts errors that don't necessarily
 | 
			
		||||
	// result in a non-2xx HTTP status code. There are two typical cases:
 | 
			
		||||
	// (1) Encoding errors that only happen after streaming of the HTTP body
 | 
			
		||||
	// has already started (and the status code 200 has been sent). This
 | 
			
		||||
	// should only happen with custom collectors. (2) Collection errors with
 | 
			
		||||
	// no effect on the HTTP status code because ErrorHandling is set to
 | 
			
		||||
	// ContinueOnError.
 | 
			
		||||
	Registry prometheus.Registerer
 | 
			
		||||
	// If DisableCompression is true, the handler will never compress the
 | 
			
		||||
	// response, even if requested by the client.
 | 
			
		||||
	DisableCompression bool
 | 
			
		||||
	// The number of concurrent HTTP requests is limited to
 | 
			
		||||
	// MaxRequestsInFlight. Additional requests are responded to with 503
 | 
			
		||||
	// Service Unavailable and a suitable message in the body. If
 | 
			
		||||
	// MaxRequestsInFlight is 0 or negative, no limit is applied.
 | 
			
		||||
	MaxRequestsInFlight int
 | 
			
		||||
	// If handling a request takes longer than Timeout, it is responded to
 | 
			
		||||
	// with 503 ServiceUnavailable and a suitable Message. No timeout is
 | 
			
		||||
	// applied if Timeout is 0 or negative. Note that with the current
 | 
			
		||||
	// implementation, reaching the timeout simply ends the HTTP requests as
 | 
			
		||||
	// described above (and even that only if sending of the body hasn't
 | 
			
		||||
	// started yet), while the bulk work of gathering all the metrics keeps
 | 
			
		||||
	// running in the background (with the eventual result to be thrown
 | 
			
		||||
	// away). Until the implementation is improved, it is recommended to
 | 
			
		||||
	// implement a separate timeout in potentially slow Collectors.
 | 
			
		||||
	Timeout time.Duration
 | 
			
		||||
	// If true, the experimental OpenMetrics encoding is added to the
 | 
			
		||||
	// possible options during content negotiation. Note that Prometheus
 | 
			
		||||
	// 2.5.0+ will negotiate OpenMetrics as first priority. OpenMetrics is
 | 
			
		||||
	// the only way to transmit exemplars. However, the move to OpenMetrics
 | 
			
		||||
	// is not completely transparent. Most notably, the values of "quantile"
 | 
			
		||||
	// labels of Summaries and "le" labels of Histograms are formatted with
 | 
			
		||||
	// a trailing ".0" if they would otherwise look like integer numbers
 | 
			
		||||
	// (which changes the identity of the resulting series on the Prometheus
 | 
			
		||||
	// server).
 | 
			
		||||
	EnableOpenMetrics bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// gzipAccepted returns whether the client will accept gzip-encoded content.
 | 
			
		||||
func gzipAccepted(header http.Header) bool {
 | 
			
		||||
	a := header.Get(acceptEncodingHeader)
 | 
			
		||||
	parts := strings.Split(a, ",")
 | 
			
		||||
	for _, part := range parts {
 | 
			
		||||
		part = strings.TrimSpace(part)
 | 
			
		||||
		if part == "gzip" || strings.HasPrefix(part, "gzip;") {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// httpError removes any content-encoding header and then calls http.Error with
 | 
			
		||||
// the provided error and http.StatusInternalServerErrer. Error contents is
 | 
			
		||||
// supposed to be uncompressed plain text. However, same as with a plain
 | 
			
		||||
// http.Error, any header settings will be void if the header has already been
 | 
			
		||||
// sent. The error message will still be written to the writer, but it will
 | 
			
		||||
// probably be of limited use.
 | 
			
		||||
func httpError(rsp http.ResponseWriter, err error) {
 | 
			
		||||
	rsp.Header().Del(contentEncodingHeader)
 | 
			
		||||
	http.Error(
 | 
			
		||||
		rsp,
 | 
			
		||||
		"An error has occurred while serving metrics:\n\n"+err.Error(),
 | 
			
		||||
		http.StatusInternalServerError,
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										219
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,219 @@
 | 
			
		||||
// Copyright 2017 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 promhttp
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/http/httptrace"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/prometheus/client_golang/prometheus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// The RoundTripperFunc type is an adapter to allow the use of ordinary
 | 
			
		||||
// functions as RoundTrippers. If f is a function with the appropriate
 | 
			
		||||
// signature, RountTripperFunc(f) is a RoundTripper that calls f.
 | 
			
		||||
type RoundTripperFunc func(req *http.Request) (*http.Response, error)
 | 
			
		||||
 | 
			
		||||
// RoundTrip implements the RoundTripper interface.
 | 
			
		||||
func (rt RoundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) {
 | 
			
		||||
	return rt(r)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InstrumentRoundTripperInFlight is a middleware that wraps the provided
 | 
			
		||||
// http.RoundTripper. It sets the provided prometheus.Gauge to the number of
 | 
			
		||||
// requests currently handled by the wrapped http.RoundTripper.
 | 
			
		||||
//
 | 
			
		||||
// See the example for ExampleInstrumentRoundTripperDuration for example usage.
 | 
			
		||||
func InstrumentRoundTripperInFlight(gauge prometheus.Gauge, next http.RoundTripper) RoundTripperFunc {
 | 
			
		||||
	return RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
 | 
			
		||||
		gauge.Inc()
 | 
			
		||||
		defer gauge.Dec()
 | 
			
		||||
		return next.RoundTrip(r)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InstrumentRoundTripperCounter is a middleware that wraps the provided
 | 
			
		||||
// http.RoundTripper to observe the request result with the provided CounterVec.
 | 
			
		||||
// The CounterVec must have zero, one, or two non-const non-curried labels. For
 | 
			
		||||
// those, the only allowed label names are "code" and "method". The function
 | 
			
		||||
// panics otherwise. Partitioning of the CounterVec happens by HTTP status code
 | 
			
		||||
// and/or HTTP method if the respective instance label names are present in the
 | 
			
		||||
// CounterVec. For unpartitioned counting, use a CounterVec with zero labels.
 | 
			
		||||
//
 | 
			
		||||
// If the wrapped RoundTripper panics or returns a non-nil error, the Counter
 | 
			
		||||
// is not incremented.
 | 
			
		||||
//
 | 
			
		||||
// See the example for ExampleInstrumentRoundTripperDuration for example usage.
 | 
			
		||||
func InstrumentRoundTripperCounter(counter *prometheus.CounterVec, next http.RoundTripper) RoundTripperFunc {
 | 
			
		||||
	code, method := checkLabels(counter)
 | 
			
		||||
 | 
			
		||||
	return RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
 | 
			
		||||
		resp, err := next.RoundTrip(r)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			counter.With(labels(code, method, r.Method, resp.StatusCode)).Inc()
 | 
			
		||||
		}
 | 
			
		||||
		return resp, err
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InstrumentRoundTripperDuration is a middleware that wraps the provided
 | 
			
		||||
// http.RoundTripper to observe the request duration with the provided
 | 
			
		||||
// ObserverVec.  The ObserverVec must have zero, one, or two non-const
 | 
			
		||||
// non-curried labels. For those, the only allowed label names are "code" and
 | 
			
		||||
// "method". The function panics otherwise. The Observe method of the Observer
 | 
			
		||||
// in the ObserverVec is called with the request duration in
 | 
			
		||||
// seconds. Partitioning happens by HTTP status code and/or HTTP method if the
 | 
			
		||||
// respective instance label names are present in the ObserverVec. For
 | 
			
		||||
// unpartitioned observations, use an ObserverVec with zero labels. Note that
 | 
			
		||||
// partitioning of Histograms is expensive and should be used judiciously.
 | 
			
		||||
//
 | 
			
		||||
// If the wrapped RoundTripper panics or returns a non-nil error, no values are
 | 
			
		||||
// reported.
 | 
			
		||||
//
 | 
			
		||||
// Note that this method is only guaranteed to never observe negative durations
 | 
			
		||||
// if used with Go1.9+.
 | 
			
		||||
func InstrumentRoundTripperDuration(obs prometheus.ObserverVec, next http.RoundTripper) RoundTripperFunc {
 | 
			
		||||
	code, method := checkLabels(obs)
 | 
			
		||||
 | 
			
		||||
	return RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
 | 
			
		||||
		start := time.Now()
 | 
			
		||||
		resp, err := next.RoundTrip(r)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			obs.With(labels(code, method, r.Method, resp.StatusCode)).Observe(time.Since(start).Seconds())
 | 
			
		||||
		}
 | 
			
		||||
		return resp, err
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InstrumentTrace is used to offer flexibility in instrumenting the available
 | 
			
		||||
// httptrace.ClientTrace hook functions. Each function is passed a float64
 | 
			
		||||
// representing the time in seconds since the start of the http request. A user
 | 
			
		||||
// may choose to use separately buckets Histograms, or implement custom
 | 
			
		||||
// instance labels on a per function basis.
 | 
			
		||||
type InstrumentTrace struct {
 | 
			
		||||
	GotConn              func(float64)
 | 
			
		||||
	PutIdleConn          func(float64)
 | 
			
		||||
	GotFirstResponseByte func(float64)
 | 
			
		||||
	Got100Continue       func(float64)
 | 
			
		||||
	DNSStart             func(float64)
 | 
			
		||||
	DNSDone              func(float64)
 | 
			
		||||
	ConnectStart         func(float64)
 | 
			
		||||
	ConnectDone          func(float64)
 | 
			
		||||
	TLSHandshakeStart    func(float64)
 | 
			
		||||
	TLSHandshakeDone     func(float64)
 | 
			
		||||
	WroteHeaders         func(float64)
 | 
			
		||||
	Wait100Continue      func(float64)
 | 
			
		||||
	WroteRequest         func(float64)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InstrumentRoundTripperTrace is a middleware that wraps the provided
 | 
			
		||||
// RoundTripper and reports times to hook functions provided in the
 | 
			
		||||
// InstrumentTrace struct. Hook functions that are not present in the provided
 | 
			
		||||
// InstrumentTrace struct are ignored. Times reported to the hook functions are
 | 
			
		||||
// time since the start of the request. Only with Go1.9+, those times are
 | 
			
		||||
// guaranteed to never be negative. (Earlier Go versions are not using a
 | 
			
		||||
// monotonic clock.) Note that partitioning of Histograms is expensive and
 | 
			
		||||
// should be used judiciously.
 | 
			
		||||
//
 | 
			
		||||
// For hook functions that receive an error as an argument, no observations are
 | 
			
		||||
// made in the event of a non-nil error value.
 | 
			
		||||
//
 | 
			
		||||
// See the example for ExampleInstrumentRoundTripperDuration for example usage.
 | 
			
		||||
func InstrumentRoundTripperTrace(it *InstrumentTrace, next http.RoundTripper) RoundTripperFunc {
 | 
			
		||||
	return RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
 | 
			
		||||
		start := time.Now()
 | 
			
		||||
 | 
			
		||||
		trace := &httptrace.ClientTrace{
 | 
			
		||||
			GotConn: func(_ httptrace.GotConnInfo) {
 | 
			
		||||
				if it.GotConn != nil {
 | 
			
		||||
					it.GotConn(time.Since(start).Seconds())
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			PutIdleConn: func(err error) {
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				if it.PutIdleConn != nil {
 | 
			
		||||
					it.PutIdleConn(time.Since(start).Seconds())
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			DNSStart: func(_ httptrace.DNSStartInfo) {
 | 
			
		||||
				if it.DNSStart != nil {
 | 
			
		||||
					it.DNSStart(time.Since(start).Seconds())
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			DNSDone: func(_ httptrace.DNSDoneInfo) {
 | 
			
		||||
				if it.DNSDone != nil {
 | 
			
		||||
					it.DNSDone(time.Since(start).Seconds())
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			ConnectStart: func(_, _ string) {
 | 
			
		||||
				if it.ConnectStart != nil {
 | 
			
		||||
					it.ConnectStart(time.Since(start).Seconds())
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			ConnectDone: func(_, _ string, err error) {
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				if it.ConnectDone != nil {
 | 
			
		||||
					it.ConnectDone(time.Since(start).Seconds())
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			GotFirstResponseByte: func() {
 | 
			
		||||
				if it.GotFirstResponseByte != nil {
 | 
			
		||||
					it.GotFirstResponseByte(time.Since(start).Seconds())
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			Got100Continue: func() {
 | 
			
		||||
				if it.Got100Continue != nil {
 | 
			
		||||
					it.Got100Continue(time.Since(start).Seconds())
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			TLSHandshakeStart: func() {
 | 
			
		||||
				if it.TLSHandshakeStart != nil {
 | 
			
		||||
					it.TLSHandshakeStart(time.Since(start).Seconds())
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			TLSHandshakeDone: func(_ tls.ConnectionState, err error) {
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
				if it.TLSHandshakeDone != nil {
 | 
			
		||||
					it.TLSHandshakeDone(time.Since(start).Seconds())
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			WroteHeaders: func() {
 | 
			
		||||
				if it.WroteHeaders != nil {
 | 
			
		||||
					it.WroteHeaders(time.Since(start).Seconds())
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			Wait100Continue: func() {
 | 
			
		||||
				if it.Wait100Continue != nil {
 | 
			
		||||
					it.Wait100Continue(time.Since(start).Seconds())
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			WroteRequest: func(_ httptrace.WroteRequestInfo) {
 | 
			
		||||
				if it.WroteRequest != nil {
 | 
			
		||||
					it.WroteRequest(time.Since(start).Seconds())
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
		r = r.WithContext(httptrace.WithClientTrace(r.Context(), trace))
 | 
			
		||||
 | 
			
		||||
		return next.RoundTrip(r)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										447
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										447
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,447 @@
 | 
			
		||||
// Copyright 2017 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 promhttp
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	dto "github.com/prometheus/client_model/go"
 | 
			
		||||
 | 
			
		||||
	"github.com/prometheus/client_golang/prometheus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// magicString is used for the hacky label test in checkLabels. Remove once fixed.
 | 
			
		||||
const magicString = "zZgWfBxLqvG8kc8IMv3POi2Bb0tZI3vAnBx+gBaFi9FyPzB/CzKUer1yufDa"
 | 
			
		||||
 | 
			
		||||
// InstrumentHandlerInFlight is a middleware that wraps the provided
 | 
			
		||||
// http.Handler. It sets the provided prometheus.Gauge to the number of
 | 
			
		||||
// requests currently handled by the wrapped http.Handler.
 | 
			
		||||
//
 | 
			
		||||
// See the example for InstrumentHandlerDuration for example usage.
 | 
			
		||||
func InstrumentHandlerInFlight(g prometheus.Gauge, next http.Handler) http.Handler {
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		g.Inc()
 | 
			
		||||
		defer g.Dec()
 | 
			
		||||
		next.ServeHTTP(w, r)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InstrumentHandlerDuration is a middleware that wraps the provided
 | 
			
		||||
// http.Handler to observe the request duration with the provided ObserverVec.
 | 
			
		||||
// The ObserverVec must have zero, one, or two non-const non-curried labels. For
 | 
			
		||||
// those, the only allowed label names are "code" and "method". The function
 | 
			
		||||
// panics otherwise. The Observe method of the Observer in the ObserverVec is
 | 
			
		||||
// called with the request duration in seconds. Partitioning happens by HTTP
 | 
			
		||||
// status code and/or HTTP method if the respective instance label names are
 | 
			
		||||
// present in the ObserverVec. For unpartitioned observations, use an
 | 
			
		||||
// ObserverVec with zero labels. Note that partitioning of Histograms is
 | 
			
		||||
// expensive and should be used judiciously.
 | 
			
		||||
//
 | 
			
		||||
// If the wrapped Handler does not set a status code, a status code of 200 is assumed.
 | 
			
		||||
//
 | 
			
		||||
// If the wrapped Handler panics, no values are reported.
 | 
			
		||||
//
 | 
			
		||||
// Note that this method is only guaranteed to never observe negative durations
 | 
			
		||||
// if used with Go1.9+.
 | 
			
		||||
func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler) http.HandlerFunc {
 | 
			
		||||
	code, method := checkLabels(obs)
 | 
			
		||||
 | 
			
		||||
	if code {
 | 
			
		||||
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
			now := time.Now()
 | 
			
		||||
			d := newDelegator(w, nil)
 | 
			
		||||
			next.ServeHTTP(d, r)
 | 
			
		||||
 | 
			
		||||
			obs.With(labels(code, method, r.Method, d.Status())).Observe(time.Since(now).Seconds())
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		now := time.Now()
 | 
			
		||||
		next.ServeHTTP(w, r)
 | 
			
		||||
		obs.With(labels(code, method, r.Method, 0)).Observe(time.Since(now).Seconds())
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InstrumentHandlerCounter is a middleware that wraps the provided http.Handler
 | 
			
		||||
// to observe the request result with the provided CounterVec.  The CounterVec
 | 
			
		||||
// must have zero, one, or two non-const non-curried labels. For those, the only
 | 
			
		||||
// allowed label names are "code" and "method". The function panics
 | 
			
		||||
// otherwise. Partitioning of the CounterVec happens by HTTP status code and/or
 | 
			
		||||
// HTTP method if the respective instance label names are present in the
 | 
			
		||||
// CounterVec. For unpartitioned counting, use a CounterVec with zero labels.
 | 
			
		||||
//
 | 
			
		||||
// If the wrapped Handler does not set a status code, a status code of 200 is assumed.
 | 
			
		||||
//
 | 
			
		||||
// If the wrapped Handler panics, the Counter is not incremented.
 | 
			
		||||
//
 | 
			
		||||
// See the example for InstrumentHandlerDuration for example usage.
 | 
			
		||||
func InstrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler) http.HandlerFunc {
 | 
			
		||||
	code, method := checkLabels(counter)
 | 
			
		||||
 | 
			
		||||
	if code {
 | 
			
		||||
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
			d := newDelegator(w, nil)
 | 
			
		||||
			next.ServeHTTP(d, r)
 | 
			
		||||
			counter.With(labels(code, method, r.Method, d.Status())).Inc()
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		next.ServeHTTP(w, r)
 | 
			
		||||
		counter.With(labels(code, method, r.Method, 0)).Inc()
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InstrumentHandlerTimeToWriteHeader is a middleware that wraps the provided
 | 
			
		||||
// http.Handler to observe with the provided ObserverVec the request duration
 | 
			
		||||
// until the response headers are written. The ObserverVec must have zero, one,
 | 
			
		||||
// or two non-const non-curried labels. For those, the only allowed label names
 | 
			
		||||
// are "code" and "method". The function panics otherwise. The Observe method of
 | 
			
		||||
// the Observer in the ObserverVec is called with the request duration in
 | 
			
		||||
// seconds. Partitioning happens by HTTP status code and/or HTTP method if the
 | 
			
		||||
// respective instance label names are present in the ObserverVec. For
 | 
			
		||||
// unpartitioned observations, use an ObserverVec with zero labels. Note that
 | 
			
		||||
// partitioning of Histograms is expensive and should be used judiciously.
 | 
			
		||||
//
 | 
			
		||||
// If the wrapped Handler panics before calling WriteHeader, no value is
 | 
			
		||||
// reported.
 | 
			
		||||
//
 | 
			
		||||
// Note that this method is only guaranteed to never observe negative durations
 | 
			
		||||
// if used with Go1.9+.
 | 
			
		||||
//
 | 
			
		||||
// See the example for InstrumentHandlerDuration for example usage.
 | 
			
		||||
func InstrumentHandlerTimeToWriteHeader(obs prometheus.ObserverVec, next http.Handler) http.HandlerFunc {
 | 
			
		||||
	code, method := checkLabels(obs)
 | 
			
		||||
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		now := time.Now()
 | 
			
		||||
		d := newDelegator(w, func(status int) {
 | 
			
		||||
			obs.With(labels(code, method, r.Method, status)).Observe(time.Since(now).Seconds())
 | 
			
		||||
		})
 | 
			
		||||
		next.ServeHTTP(d, r)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InstrumentHandlerRequestSize is a middleware that wraps the provided
 | 
			
		||||
// http.Handler to observe the request size with the provided ObserverVec.  The
 | 
			
		||||
// ObserverVec must have zero, one, or two non-const non-curried labels. For
 | 
			
		||||
// those, the only allowed label names are "code" and "method". The function
 | 
			
		||||
// panics otherwise. The Observe method of the Observer in the ObserverVec is
 | 
			
		||||
// called with the request size in bytes. Partitioning happens by HTTP status
 | 
			
		||||
// code and/or HTTP method if the respective instance label names are present in
 | 
			
		||||
// the ObserverVec. For unpartitioned observations, use an ObserverVec with zero
 | 
			
		||||
// labels. Note that partitioning of Histograms is expensive and should be used
 | 
			
		||||
// judiciously.
 | 
			
		||||
//
 | 
			
		||||
// If the wrapped Handler does not set a status code, a status code of 200 is assumed.
 | 
			
		||||
//
 | 
			
		||||
// If the wrapped Handler panics, no values are reported.
 | 
			
		||||
//
 | 
			
		||||
// See the example for InstrumentHandlerDuration for example usage.
 | 
			
		||||
func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler) http.HandlerFunc {
 | 
			
		||||
	code, method := checkLabels(obs)
 | 
			
		||||
 | 
			
		||||
	if code {
 | 
			
		||||
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
			d := newDelegator(w, nil)
 | 
			
		||||
			next.ServeHTTP(d, r)
 | 
			
		||||
			size := computeApproximateRequestSize(r)
 | 
			
		||||
			obs.With(labels(code, method, r.Method, d.Status())).Observe(float64(size))
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		next.ServeHTTP(w, r)
 | 
			
		||||
		size := computeApproximateRequestSize(r)
 | 
			
		||||
		obs.With(labels(code, method, r.Method, 0)).Observe(float64(size))
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InstrumentHandlerResponseSize is a middleware that wraps the provided
 | 
			
		||||
// http.Handler to observe the response size with the provided ObserverVec.  The
 | 
			
		||||
// ObserverVec must have zero, one, or two non-const non-curried labels. For
 | 
			
		||||
// those, the only allowed label names are "code" and "method". The function
 | 
			
		||||
// panics otherwise. The Observe method of the Observer in the ObserverVec is
 | 
			
		||||
// called with the response size in bytes. Partitioning happens by HTTP status
 | 
			
		||||
// code and/or HTTP method if the respective instance label names are present in
 | 
			
		||||
// the ObserverVec. For unpartitioned observations, use an ObserverVec with zero
 | 
			
		||||
// labels. Note that partitioning of Histograms is expensive and should be used
 | 
			
		||||
// judiciously.
 | 
			
		||||
//
 | 
			
		||||
// If the wrapped Handler does not set a status code, a status code of 200 is assumed.
 | 
			
		||||
//
 | 
			
		||||
// If the wrapped Handler panics, no values are reported.
 | 
			
		||||
//
 | 
			
		||||
// See the example for InstrumentHandlerDuration for example usage.
 | 
			
		||||
func InstrumentHandlerResponseSize(obs prometheus.ObserverVec, next http.Handler) http.Handler {
 | 
			
		||||
	code, method := checkLabels(obs)
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		d := newDelegator(w, nil)
 | 
			
		||||
		next.ServeHTTP(d, r)
 | 
			
		||||
		obs.With(labels(code, method, r.Method, d.Status())).Observe(float64(d.Written()))
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func checkLabels(c prometheus.Collector) (code bool, method bool) {
 | 
			
		||||
	// TODO(beorn7): Remove this hacky way to check for instance labels
 | 
			
		||||
	// once Descriptors can have their dimensionality queried.
 | 
			
		||||
	var (
 | 
			
		||||
		desc *prometheus.Desc
 | 
			
		||||
		m    prometheus.Metric
 | 
			
		||||
		pm   dto.Metric
 | 
			
		||||
		lvs  []string
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// Get the Desc from the Collector.
 | 
			
		||||
	descc := make(chan *prometheus.Desc, 1)
 | 
			
		||||
	c.Describe(descc)
 | 
			
		||||
 | 
			
		||||
	select {
 | 
			
		||||
	case desc = <-descc:
 | 
			
		||||
	default:
 | 
			
		||||
		panic("no description provided by collector")
 | 
			
		||||
	}
 | 
			
		||||
	select {
 | 
			
		||||
	case <-descc:
 | 
			
		||||
		panic("more than one description provided by collector")
 | 
			
		||||
	default:
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	close(descc)
 | 
			
		||||
 | 
			
		||||
	// Create a ConstMetric with the Desc. Since we don't know how many
 | 
			
		||||
	// variable labels there are, try for as long as it needs.
 | 
			
		||||
	for err := errors.New("dummy"); err != nil; lvs = append(lvs, magicString) {
 | 
			
		||||
		m, err = prometheus.NewConstMetric(desc, prometheus.UntypedValue, 0, lvs...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Write out the metric into a proto message and look at the labels.
 | 
			
		||||
	// If the value is not the magicString, it is a constLabel, which doesn't interest us.
 | 
			
		||||
	// If the label is curried, it doesn't interest us.
 | 
			
		||||
	// In all other cases, only "code" or "method" is allowed.
 | 
			
		||||
	if err := m.Write(&pm); err != nil {
 | 
			
		||||
		panic("error checking metric for labels")
 | 
			
		||||
	}
 | 
			
		||||
	for _, label := range pm.Label {
 | 
			
		||||
		name, value := label.GetName(), label.GetValue()
 | 
			
		||||
		if value != magicString || isLabelCurried(c, name) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		switch name {
 | 
			
		||||
		case "code":
 | 
			
		||||
			code = true
 | 
			
		||||
		case "method":
 | 
			
		||||
			method = true
 | 
			
		||||
		default:
 | 
			
		||||
			panic("metric partitioned with non-supported labels")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isLabelCurried(c prometheus.Collector, label string) bool {
 | 
			
		||||
	// This is even hackier than the label test above.
 | 
			
		||||
	// We essentially try to curry again and see if it works.
 | 
			
		||||
	// But for that, we need to type-convert to the two
 | 
			
		||||
	// types we use here, ObserverVec or *CounterVec.
 | 
			
		||||
	switch v := c.(type) {
 | 
			
		||||
	case *prometheus.CounterVec:
 | 
			
		||||
		if _, err := v.CurryWith(prometheus.Labels{label: "dummy"}); err == nil {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	case prometheus.ObserverVec:
 | 
			
		||||
		if _, err := v.CurryWith(prometheus.Labels{label: "dummy"}); err == nil {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		panic("unsupported metric vec type")
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// emptyLabels is a one-time allocation for non-partitioned metrics to avoid
 | 
			
		||||
// unnecessary allocations on each request.
 | 
			
		||||
var emptyLabels = prometheus.Labels{}
 | 
			
		||||
 | 
			
		||||
func labels(code, method bool, reqMethod string, status int) prometheus.Labels {
 | 
			
		||||
	if !(code || method) {
 | 
			
		||||
		return emptyLabels
 | 
			
		||||
	}
 | 
			
		||||
	labels := prometheus.Labels{}
 | 
			
		||||
 | 
			
		||||
	if code {
 | 
			
		||||
		labels["code"] = sanitizeCode(status)
 | 
			
		||||
	}
 | 
			
		||||
	if method {
 | 
			
		||||
		labels["method"] = sanitizeMethod(reqMethod)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return labels
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func computeApproximateRequestSize(r *http.Request) int {
 | 
			
		||||
	s := 0
 | 
			
		||||
	if r.URL != nil {
 | 
			
		||||
		s += len(r.URL.String())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s += len(r.Method)
 | 
			
		||||
	s += len(r.Proto)
 | 
			
		||||
	for name, values := range r.Header {
 | 
			
		||||
		s += len(name)
 | 
			
		||||
		for _, value := range values {
 | 
			
		||||
			s += len(value)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	s += len(r.Host)
 | 
			
		||||
 | 
			
		||||
	// N.B. r.Form and r.MultipartForm are assumed to be included in r.URL.
 | 
			
		||||
 | 
			
		||||
	if r.ContentLength != -1 {
 | 
			
		||||
		s += int(r.ContentLength)
 | 
			
		||||
	}
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func sanitizeMethod(m string) string {
 | 
			
		||||
	switch m {
 | 
			
		||||
	case "GET", "get":
 | 
			
		||||
		return "get"
 | 
			
		||||
	case "PUT", "put":
 | 
			
		||||
		return "put"
 | 
			
		||||
	case "HEAD", "head":
 | 
			
		||||
		return "head"
 | 
			
		||||
	case "POST", "post":
 | 
			
		||||
		return "post"
 | 
			
		||||
	case "DELETE", "delete":
 | 
			
		||||
		return "delete"
 | 
			
		||||
	case "CONNECT", "connect":
 | 
			
		||||
		return "connect"
 | 
			
		||||
	case "OPTIONS", "options":
 | 
			
		||||
		return "options"
 | 
			
		||||
	case "NOTIFY", "notify":
 | 
			
		||||
		return "notify"
 | 
			
		||||
	default:
 | 
			
		||||
		return strings.ToLower(m)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// If the wrapped http.Handler has not set a status code, i.e. the value is
 | 
			
		||||
// currently 0, santizeCode will return 200, for consistency with behavior in
 | 
			
		||||
// the stdlib.
 | 
			
		||||
func sanitizeCode(s int) string {
 | 
			
		||||
	switch s {
 | 
			
		||||
	case 100:
 | 
			
		||||
		return "100"
 | 
			
		||||
	case 101:
 | 
			
		||||
		return "101"
 | 
			
		||||
 | 
			
		||||
	case 200, 0:
 | 
			
		||||
		return "200"
 | 
			
		||||
	case 201:
 | 
			
		||||
		return "201"
 | 
			
		||||
	case 202:
 | 
			
		||||
		return "202"
 | 
			
		||||
	case 203:
 | 
			
		||||
		return "203"
 | 
			
		||||
	case 204:
 | 
			
		||||
		return "204"
 | 
			
		||||
	case 205:
 | 
			
		||||
		return "205"
 | 
			
		||||
	case 206:
 | 
			
		||||
		return "206"
 | 
			
		||||
 | 
			
		||||
	case 300:
 | 
			
		||||
		return "300"
 | 
			
		||||
	case 301:
 | 
			
		||||
		return "301"
 | 
			
		||||
	case 302:
 | 
			
		||||
		return "302"
 | 
			
		||||
	case 304:
 | 
			
		||||
		return "304"
 | 
			
		||||
	case 305:
 | 
			
		||||
		return "305"
 | 
			
		||||
	case 307:
 | 
			
		||||
		return "307"
 | 
			
		||||
 | 
			
		||||
	case 400:
 | 
			
		||||
		return "400"
 | 
			
		||||
	case 401:
 | 
			
		||||
		return "401"
 | 
			
		||||
	case 402:
 | 
			
		||||
		return "402"
 | 
			
		||||
	case 403:
 | 
			
		||||
		return "403"
 | 
			
		||||
	case 404:
 | 
			
		||||
		return "404"
 | 
			
		||||
	case 405:
 | 
			
		||||
		return "405"
 | 
			
		||||
	case 406:
 | 
			
		||||
		return "406"
 | 
			
		||||
	case 407:
 | 
			
		||||
		return "407"
 | 
			
		||||
	case 408:
 | 
			
		||||
		return "408"
 | 
			
		||||
	case 409:
 | 
			
		||||
		return "409"
 | 
			
		||||
	case 410:
 | 
			
		||||
		return "410"
 | 
			
		||||
	case 411:
 | 
			
		||||
		return "411"
 | 
			
		||||
	case 412:
 | 
			
		||||
		return "412"
 | 
			
		||||
	case 413:
 | 
			
		||||
		return "413"
 | 
			
		||||
	case 414:
 | 
			
		||||
		return "414"
 | 
			
		||||
	case 415:
 | 
			
		||||
		return "415"
 | 
			
		||||
	case 416:
 | 
			
		||||
		return "416"
 | 
			
		||||
	case 417:
 | 
			
		||||
		return "417"
 | 
			
		||||
	case 418:
 | 
			
		||||
		return "418"
 | 
			
		||||
 | 
			
		||||
	case 500:
 | 
			
		||||
		return "500"
 | 
			
		||||
	case 501:
 | 
			
		||||
		return "501"
 | 
			
		||||
	case 502:
 | 
			
		||||
		return "502"
 | 
			
		||||
	case 503:
 | 
			
		||||
		return "503"
 | 
			
		||||
	case 504:
 | 
			
		||||
		return "504"
 | 
			
		||||
	case 505:
 | 
			
		||||
		return "505"
 | 
			
		||||
 | 
			
		||||
	case 428:
 | 
			
		||||
		return "428"
 | 
			
		||||
	case 429:
 | 
			
		||||
		return "429"
 | 
			
		||||
	case 431:
 | 
			
		||||
		return "431"
 | 
			
		||||
	case 511:
 | 
			
		||||
		return "511"
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		return strconv.Itoa(s)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										947
									
								
								vendor/github.com/prometheus/client_golang/prometheus/registry.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										947
									
								
								vendor/github.com/prometheus/client_golang/prometheus/registry.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,947 @@
 | 
			
		||||
// 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 prometheus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"unicode/utf8"
 | 
			
		||||
 | 
			
		||||
	"github.com/cespare/xxhash/v2"
 | 
			
		||||
	"github.com/golang/protobuf/proto"
 | 
			
		||||
	"github.com/prometheus/common/expfmt"
 | 
			
		||||
 | 
			
		||||
	dto "github.com/prometheus/client_model/go"
 | 
			
		||||
 | 
			
		||||
	"github.com/prometheus/client_golang/prometheus/internal"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// Capacity for the channel to collect metrics and descriptors.
 | 
			
		||||
	capMetricChan = 1000
 | 
			
		||||
	capDescChan   = 10
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DefaultRegisterer and DefaultGatherer are the implementations of the
 | 
			
		||||
// Registerer and Gatherer interface a number of convenience functions in this
 | 
			
		||||
// package act on. Initially, both variables point to the same Registry, which
 | 
			
		||||
// has a process collector (currently on Linux only, see NewProcessCollector)
 | 
			
		||||
// and a Go collector (see NewGoCollector, in particular the note about
 | 
			
		||||
// stop-the-world implication with Go versions older than 1.9) already
 | 
			
		||||
// registered. This approach to keep default instances as global state mirrors
 | 
			
		||||
// the approach of other packages in the Go standard library. Note that there
 | 
			
		||||
// are caveats. Change the variables with caution and only if you understand the
 | 
			
		||||
// consequences. Users who want to avoid global state altogether should not use
 | 
			
		||||
// the convenience functions and act on custom instances instead.
 | 
			
		||||
var (
 | 
			
		||||
	defaultRegistry              = NewRegistry()
 | 
			
		||||
	DefaultRegisterer Registerer = defaultRegistry
 | 
			
		||||
	DefaultGatherer   Gatherer   = defaultRegistry
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	MustRegister(NewProcessCollector(ProcessCollectorOpts{}))
 | 
			
		||||
	MustRegister(NewGoCollector())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewRegistry creates a new vanilla Registry without any Collectors
 | 
			
		||||
// pre-registered.
 | 
			
		||||
func NewRegistry() *Registry {
 | 
			
		||||
	return &Registry{
 | 
			
		||||
		collectorsByID:  map[uint64]Collector{},
 | 
			
		||||
		descIDs:         map[uint64]struct{}{},
 | 
			
		||||
		dimHashesByName: map[string]uint64{},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewPedanticRegistry returns a registry that checks during collection if each
 | 
			
		||||
// collected Metric is consistent with its reported Desc, and if the Desc has
 | 
			
		||||
// actually been registered with the registry. Unchecked Collectors (those whose
 | 
			
		||||
// Describe method does not yield any descriptors) are excluded from the check.
 | 
			
		||||
//
 | 
			
		||||
// Usually, a Registry will be happy as long as the union of all collected
 | 
			
		||||
// Metrics is consistent and valid even if some metrics are not consistent with
 | 
			
		||||
// their own Desc or a Desc provided by their registered Collector. Well-behaved
 | 
			
		||||
// Collectors and Metrics will only provide consistent Descs. This Registry is
 | 
			
		||||
// useful to test the implementation of Collectors and Metrics.
 | 
			
		||||
func NewPedanticRegistry() *Registry {
 | 
			
		||||
	r := NewRegistry()
 | 
			
		||||
	r.pedanticChecksEnabled = true
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Registerer is the interface for the part of a registry in charge of
 | 
			
		||||
// registering and unregistering. Users of custom registries should use
 | 
			
		||||
// Registerer as type for registration purposes (rather than the Registry type
 | 
			
		||||
// directly). In that way, they are free to use custom Registerer implementation
 | 
			
		||||
// (e.g. for testing purposes).
 | 
			
		||||
type Registerer interface {
 | 
			
		||||
	// Register registers a new Collector to be included in metrics
 | 
			
		||||
	// collection. It returns an error if the descriptors provided by the
 | 
			
		||||
	// Collector are invalid or if they — in combination with descriptors of
 | 
			
		||||
	// already registered Collectors — do not fulfill the consistency and
 | 
			
		||||
	// uniqueness criteria described in the documentation of metric.Desc.
 | 
			
		||||
	//
 | 
			
		||||
	// If the provided Collector is equal to a Collector already registered
 | 
			
		||||
	// (which includes the case of re-registering the same Collector), the
 | 
			
		||||
	// returned error is an instance of AlreadyRegisteredError, which
 | 
			
		||||
	// contains the previously registered Collector.
 | 
			
		||||
	//
 | 
			
		||||
	// A Collector whose Describe method does not yield any Desc is treated
 | 
			
		||||
	// as unchecked. Registration will always succeed. No check for
 | 
			
		||||
	// re-registering (see previous paragraph) is performed. Thus, the
 | 
			
		||||
	// caller is responsible for not double-registering the same unchecked
 | 
			
		||||
	// Collector, and for providing a Collector that will not cause
 | 
			
		||||
	// inconsistent metrics on collection. (This would lead to scrape
 | 
			
		||||
	// errors.)
 | 
			
		||||
	Register(Collector) error
 | 
			
		||||
	// MustRegister works like Register but registers any number of
 | 
			
		||||
	// Collectors and panics upon the first registration that causes an
 | 
			
		||||
	// error.
 | 
			
		||||
	MustRegister(...Collector)
 | 
			
		||||
	// Unregister unregisters the Collector that equals the Collector passed
 | 
			
		||||
	// in as an argument.  (Two Collectors are considered equal if their
 | 
			
		||||
	// Describe method yields the same set of descriptors.) The function
 | 
			
		||||
	// returns whether a Collector was unregistered. Note that an unchecked
 | 
			
		||||
	// Collector cannot be unregistered (as its Describe method does not
 | 
			
		||||
	// yield any descriptor).
 | 
			
		||||
	//
 | 
			
		||||
	// Note that even after unregistering, it will not be possible to
 | 
			
		||||
	// register a new Collector that is inconsistent with the unregistered
 | 
			
		||||
	// Collector, e.g. a Collector collecting metrics with the same name but
 | 
			
		||||
	// a different help string. The rationale here is that the same registry
 | 
			
		||||
	// instance must only collect consistent metrics throughout its
 | 
			
		||||
	// lifetime.
 | 
			
		||||
	Unregister(Collector) bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Gatherer is the interface for the part of a registry in charge of gathering
 | 
			
		||||
// the collected metrics into a number of MetricFamilies. The Gatherer interface
 | 
			
		||||
// comes with the same general implication as described for the Registerer
 | 
			
		||||
// interface.
 | 
			
		||||
type Gatherer interface {
 | 
			
		||||
	// Gather calls the Collect method of the registered Collectors and then
 | 
			
		||||
	// gathers the collected metrics into a lexicographically sorted slice
 | 
			
		||||
	// of uniquely named MetricFamily protobufs. Gather ensures that the
 | 
			
		||||
	// returned slice is valid and self-consistent so that it can be used
 | 
			
		||||
	// for valid exposition. As an exception to the strict consistency
 | 
			
		||||
	// requirements described for metric.Desc, Gather will tolerate
 | 
			
		||||
	// different sets of label names for metrics of the same metric family.
 | 
			
		||||
	//
 | 
			
		||||
	// Even if an error occurs, Gather attempts to gather as many metrics as
 | 
			
		||||
	// possible. Hence, if a non-nil error is returned, the returned
 | 
			
		||||
	// MetricFamily slice could be nil (in case of a fatal error that
 | 
			
		||||
	// prevented any meaningful metric collection) or contain a number of
 | 
			
		||||
	// MetricFamily protobufs, some of which might be incomplete, and some
 | 
			
		||||
	// might be missing altogether. The returned error (which might be a
 | 
			
		||||
	// MultiError) explains the details. Note that this is mostly useful for
 | 
			
		||||
	// debugging purposes. If the gathered protobufs are to be used for
 | 
			
		||||
	// exposition in actual monitoring, it is almost always better to not
 | 
			
		||||
	// expose an incomplete result and instead disregard the returned
 | 
			
		||||
	// MetricFamily protobufs in case the returned error is non-nil.
 | 
			
		||||
	Gather() ([]*dto.MetricFamily, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Register registers the provided Collector with the DefaultRegisterer.
 | 
			
		||||
//
 | 
			
		||||
// Register is a shortcut for DefaultRegisterer.Register(c). See there for more
 | 
			
		||||
// details.
 | 
			
		||||
func Register(c Collector) error {
 | 
			
		||||
	return DefaultRegisterer.Register(c)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MustRegister registers the provided Collectors with the DefaultRegisterer and
 | 
			
		||||
// panics if any error occurs.
 | 
			
		||||
//
 | 
			
		||||
// MustRegister is a shortcut for DefaultRegisterer.MustRegister(cs...). See
 | 
			
		||||
// there for more details.
 | 
			
		||||
func MustRegister(cs ...Collector) {
 | 
			
		||||
	DefaultRegisterer.MustRegister(cs...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Unregister removes the registration of the provided Collector from the
 | 
			
		||||
// DefaultRegisterer.
 | 
			
		||||
//
 | 
			
		||||
// Unregister is a shortcut for DefaultRegisterer.Unregister(c). See there for
 | 
			
		||||
// more details.
 | 
			
		||||
func Unregister(c Collector) bool {
 | 
			
		||||
	return DefaultRegisterer.Unregister(c)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GathererFunc turns a function into a Gatherer.
 | 
			
		||||
type GathererFunc func() ([]*dto.MetricFamily, error)
 | 
			
		||||
 | 
			
		||||
// Gather implements Gatherer.
 | 
			
		||||
func (gf GathererFunc) Gather() ([]*dto.MetricFamily, error) {
 | 
			
		||||
	return gf()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AlreadyRegisteredError is returned by the Register method if the Collector to
 | 
			
		||||
// be registered has already been registered before, or a different Collector
 | 
			
		||||
// that collects the same metrics has been registered before. Registration fails
 | 
			
		||||
// in that case, but you can detect from the kind of error what has
 | 
			
		||||
// happened. The error contains fields for the existing Collector and the
 | 
			
		||||
// (rejected) new Collector that equals the existing one. This can be used to
 | 
			
		||||
// find out if an equal Collector has been registered before and switch over to
 | 
			
		||||
// using the old one, as demonstrated in the example.
 | 
			
		||||
type AlreadyRegisteredError struct {
 | 
			
		||||
	ExistingCollector, NewCollector Collector
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (err AlreadyRegisteredError) Error() string {
 | 
			
		||||
	return "duplicate metrics collector registration attempted"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MultiError is a slice of errors implementing the error interface. It is used
 | 
			
		||||
// by a Gatherer to report multiple errors during MetricFamily gathering.
 | 
			
		||||
type MultiError []error
 | 
			
		||||
 | 
			
		||||
func (errs MultiError) Error() string {
 | 
			
		||||
	if len(errs) == 0 {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	buf := &bytes.Buffer{}
 | 
			
		||||
	fmt.Fprintf(buf, "%d error(s) occurred:", len(errs))
 | 
			
		||||
	for _, err := range errs {
 | 
			
		||||
		fmt.Fprintf(buf, "\n* %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	return buf.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Append appends the provided error if it is not nil.
 | 
			
		||||
func (errs *MultiError) Append(err error) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		*errs = append(*errs, err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MaybeUnwrap returns nil if len(errs) is 0. It returns the first and only
 | 
			
		||||
// contained error as error if len(errs is 1). In all other cases, it returns
 | 
			
		||||
// the MultiError directly. This is helpful for returning a MultiError in a way
 | 
			
		||||
// that only uses the MultiError if needed.
 | 
			
		||||
func (errs MultiError) MaybeUnwrap() error {
 | 
			
		||||
	switch len(errs) {
 | 
			
		||||
	case 0:
 | 
			
		||||
		return nil
 | 
			
		||||
	case 1:
 | 
			
		||||
		return errs[0]
 | 
			
		||||
	default:
 | 
			
		||||
		return errs
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Registry registers Prometheus collectors, collects their metrics, and gathers
 | 
			
		||||
// them into MetricFamilies for exposition. It implements both Registerer and
 | 
			
		||||
// Gatherer. The zero value is not usable. Create instances with NewRegistry or
 | 
			
		||||
// NewPedanticRegistry.
 | 
			
		||||
type Registry struct {
 | 
			
		||||
	mtx                   sync.RWMutex
 | 
			
		||||
	collectorsByID        map[uint64]Collector // ID is a hash of the descIDs.
 | 
			
		||||
	descIDs               map[uint64]struct{}
 | 
			
		||||
	dimHashesByName       map[string]uint64
 | 
			
		||||
	uncheckedCollectors   []Collector
 | 
			
		||||
	pedanticChecksEnabled bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Register implements Registerer.
 | 
			
		||||
func (r *Registry) Register(c Collector) error {
 | 
			
		||||
	var (
 | 
			
		||||
		descChan           = make(chan *Desc, capDescChan)
 | 
			
		||||
		newDescIDs         = map[uint64]struct{}{}
 | 
			
		||||
		newDimHashesByName = map[string]uint64{}
 | 
			
		||||
		collectorID        uint64 // All desc IDs XOR'd together.
 | 
			
		||||
		duplicateDescErr   error
 | 
			
		||||
	)
 | 
			
		||||
	go func() {
 | 
			
		||||
		c.Describe(descChan)
 | 
			
		||||
		close(descChan)
 | 
			
		||||
	}()
 | 
			
		||||
	r.mtx.Lock()
 | 
			
		||||
	defer func() {
 | 
			
		||||
		// Drain channel in case of premature return to not leak a goroutine.
 | 
			
		||||
		for range descChan {
 | 
			
		||||
		}
 | 
			
		||||
		r.mtx.Unlock()
 | 
			
		||||
	}()
 | 
			
		||||
	// Conduct various tests...
 | 
			
		||||
	for desc := range descChan {
 | 
			
		||||
 | 
			
		||||
		// Is the descriptor valid at all?
 | 
			
		||||
		if desc.err != nil {
 | 
			
		||||
			return fmt.Errorf("descriptor %s is invalid: %s", desc, desc.err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Is the descID unique?
 | 
			
		||||
		// (In other words: Is the fqName + constLabel combination unique?)
 | 
			
		||||
		if _, exists := r.descIDs[desc.id]; exists {
 | 
			
		||||
			duplicateDescErr = fmt.Errorf("descriptor %s already exists with the same fully-qualified name and const label values", desc)
 | 
			
		||||
		}
 | 
			
		||||
		// If it is not a duplicate desc in this collector, XOR it to
 | 
			
		||||
		// the collectorID.  (We allow duplicate descs within the same
 | 
			
		||||
		// collector, but their existence must be a no-op.)
 | 
			
		||||
		if _, exists := newDescIDs[desc.id]; !exists {
 | 
			
		||||
			newDescIDs[desc.id] = struct{}{}
 | 
			
		||||
			collectorID ^= desc.id
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Are all the label names and the help string consistent with
 | 
			
		||||
		// previous descriptors of the same name?
 | 
			
		||||
		// First check existing descriptors...
 | 
			
		||||
		if dimHash, exists := r.dimHashesByName[desc.fqName]; exists {
 | 
			
		||||
			if dimHash != desc.dimHash {
 | 
			
		||||
				return fmt.Errorf("a previously registered descriptor with the same fully-qualified name as %s has different label names or a different help string", desc)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			// ...then check the new descriptors already seen.
 | 
			
		||||
			if dimHash, exists := newDimHashesByName[desc.fqName]; exists {
 | 
			
		||||
				if dimHash != desc.dimHash {
 | 
			
		||||
					return fmt.Errorf("descriptors reported by collector have inconsistent label names or help strings for the same fully-qualified name, offender is %s", desc)
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				newDimHashesByName[desc.fqName] = desc.dimHash
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// A Collector yielding no Desc at all is considered unchecked.
 | 
			
		||||
	if len(newDescIDs) == 0 {
 | 
			
		||||
		r.uncheckedCollectors = append(r.uncheckedCollectors, c)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if existing, exists := r.collectorsByID[collectorID]; exists {
 | 
			
		||||
		switch e := existing.(type) {
 | 
			
		||||
		case *wrappingCollector:
 | 
			
		||||
			return AlreadyRegisteredError{
 | 
			
		||||
				ExistingCollector: e.unwrapRecursively(),
 | 
			
		||||
				NewCollector:      c,
 | 
			
		||||
			}
 | 
			
		||||
		default:
 | 
			
		||||
			return AlreadyRegisteredError{
 | 
			
		||||
				ExistingCollector: e,
 | 
			
		||||
				NewCollector:      c,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// If the collectorID is new, but at least one of the descs existed
 | 
			
		||||
	// before, we are in trouble.
 | 
			
		||||
	if duplicateDescErr != nil {
 | 
			
		||||
		return duplicateDescErr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Only after all tests have passed, actually register.
 | 
			
		||||
	r.collectorsByID[collectorID] = c
 | 
			
		||||
	for hash := range newDescIDs {
 | 
			
		||||
		r.descIDs[hash] = struct{}{}
 | 
			
		||||
	}
 | 
			
		||||
	for name, dimHash := range newDimHashesByName {
 | 
			
		||||
		r.dimHashesByName[name] = dimHash
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Unregister implements Registerer.
 | 
			
		||||
func (r *Registry) Unregister(c Collector) bool {
 | 
			
		||||
	var (
 | 
			
		||||
		descChan    = make(chan *Desc, capDescChan)
 | 
			
		||||
		descIDs     = map[uint64]struct{}{}
 | 
			
		||||
		collectorID uint64 // All desc IDs XOR'd together.
 | 
			
		||||
	)
 | 
			
		||||
	go func() {
 | 
			
		||||
		c.Describe(descChan)
 | 
			
		||||
		close(descChan)
 | 
			
		||||
	}()
 | 
			
		||||
	for desc := range descChan {
 | 
			
		||||
		if _, exists := descIDs[desc.id]; !exists {
 | 
			
		||||
			collectorID ^= desc.id
 | 
			
		||||
			descIDs[desc.id] = struct{}{}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r.mtx.RLock()
 | 
			
		||||
	if _, exists := r.collectorsByID[collectorID]; !exists {
 | 
			
		||||
		r.mtx.RUnlock()
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	r.mtx.RUnlock()
 | 
			
		||||
 | 
			
		||||
	r.mtx.Lock()
 | 
			
		||||
	defer r.mtx.Unlock()
 | 
			
		||||
 | 
			
		||||
	delete(r.collectorsByID, collectorID)
 | 
			
		||||
	for id := range descIDs {
 | 
			
		||||
		delete(r.descIDs, id)
 | 
			
		||||
	}
 | 
			
		||||
	// dimHashesByName is left untouched as those must be consistent
 | 
			
		||||
	// throughout the lifetime of a program.
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MustRegister implements Registerer.
 | 
			
		||||
func (r *Registry) MustRegister(cs ...Collector) {
 | 
			
		||||
	for _, c := range cs {
 | 
			
		||||
		if err := r.Register(c); err != nil {
 | 
			
		||||
			panic(err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Gather implements Gatherer.
 | 
			
		||||
func (r *Registry) Gather() ([]*dto.MetricFamily, error) {
 | 
			
		||||
	var (
 | 
			
		||||
		checkedMetricChan   = make(chan Metric, capMetricChan)
 | 
			
		||||
		uncheckedMetricChan = make(chan Metric, capMetricChan)
 | 
			
		||||
		metricHashes        = map[uint64]struct{}{}
 | 
			
		||||
		wg                  sync.WaitGroup
 | 
			
		||||
		errs                MultiError          // The collected errors to return in the end.
 | 
			
		||||
		registeredDescIDs   map[uint64]struct{} // Only used for pedantic checks
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	r.mtx.RLock()
 | 
			
		||||
	goroutineBudget := len(r.collectorsByID) + len(r.uncheckedCollectors)
 | 
			
		||||
	metricFamiliesByName := make(map[string]*dto.MetricFamily, len(r.dimHashesByName))
 | 
			
		||||
	checkedCollectors := make(chan Collector, len(r.collectorsByID))
 | 
			
		||||
	uncheckedCollectors := make(chan Collector, len(r.uncheckedCollectors))
 | 
			
		||||
	for _, collector := range r.collectorsByID {
 | 
			
		||||
		checkedCollectors <- collector
 | 
			
		||||
	}
 | 
			
		||||
	for _, collector := range r.uncheckedCollectors {
 | 
			
		||||
		uncheckedCollectors <- collector
 | 
			
		||||
	}
 | 
			
		||||
	// In case pedantic checks are enabled, we have to copy the map before
 | 
			
		||||
	// giving up the RLock.
 | 
			
		||||
	if r.pedanticChecksEnabled {
 | 
			
		||||
		registeredDescIDs = make(map[uint64]struct{}, len(r.descIDs))
 | 
			
		||||
		for id := range r.descIDs {
 | 
			
		||||
			registeredDescIDs[id] = struct{}{}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	r.mtx.RUnlock()
 | 
			
		||||
 | 
			
		||||
	wg.Add(goroutineBudget)
 | 
			
		||||
 | 
			
		||||
	collectWorker := func() {
 | 
			
		||||
		for {
 | 
			
		||||
			select {
 | 
			
		||||
			case collector := <-checkedCollectors:
 | 
			
		||||
				collector.Collect(checkedMetricChan)
 | 
			
		||||
			case collector := <-uncheckedCollectors:
 | 
			
		||||
				collector.Collect(uncheckedMetricChan)
 | 
			
		||||
			default:
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			wg.Done()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Start the first worker now to make sure at least one is running.
 | 
			
		||||
	go collectWorker()
 | 
			
		||||
	goroutineBudget--
 | 
			
		||||
 | 
			
		||||
	// Close checkedMetricChan and uncheckedMetricChan once all collectors
 | 
			
		||||
	// are collected.
 | 
			
		||||
	go func() {
 | 
			
		||||
		wg.Wait()
 | 
			
		||||
		close(checkedMetricChan)
 | 
			
		||||
		close(uncheckedMetricChan)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	// Drain checkedMetricChan and uncheckedMetricChan in case of premature return.
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if checkedMetricChan != nil {
 | 
			
		||||
			for range checkedMetricChan {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if uncheckedMetricChan != nil {
 | 
			
		||||
			for range uncheckedMetricChan {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	// Copy the channel references so we can nil them out later to remove
 | 
			
		||||
	// them from the select statements below.
 | 
			
		||||
	cmc := checkedMetricChan
 | 
			
		||||
	umc := uncheckedMetricChan
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case metric, ok := <-cmc:
 | 
			
		||||
			if !ok {
 | 
			
		||||
				cmc = nil
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			errs.Append(processMetric(
 | 
			
		||||
				metric, metricFamiliesByName,
 | 
			
		||||
				metricHashes,
 | 
			
		||||
				registeredDescIDs,
 | 
			
		||||
			))
 | 
			
		||||
		case metric, ok := <-umc:
 | 
			
		||||
			if !ok {
 | 
			
		||||
				umc = nil
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			errs.Append(processMetric(
 | 
			
		||||
				metric, metricFamiliesByName,
 | 
			
		||||
				metricHashes,
 | 
			
		||||
				nil,
 | 
			
		||||
			))
 | 
			
		||||
		default:
 | 
			
		||||
			if goroutineBudget <= 0 || len(checkedCollectors)+len(uncheckedCollectors) == 0 {
 | 
			
		||||
				// All collectors are already being worked on or
 | 
			
		||||
				// we have already as many goroutines started as
 | 
			
		||||
				// there are collectors. Do the same as above,
 | 
			
		||||
				// just without the default.
 | 
			
		||||
				select {
 | 
			
		||||
				case metric, ok := <-cmc:
 | 
			
		||||
					if !ok {
 | 
			
		||||
						cmc = nil
 | 
			
		||||
						break
 | 
			
		||||
					}
 | 
			
		||||
					errs.Append(processMetric(
 | 
			
		||||
						metric, metricFamiliesByName,
 | 
			
		||||
						metricHashes,
 | 
			
		||||
						registeredDescIDs,
 | 
			
		||||
					))
 | 
			
		||||
				case metric, ok := <-umc:
 | 
			
		||||
					if !ok {
 | 
			
		||||
						umc = nil
 | 
			
		||||
						break
 | 
			
		||||
					}
 | 
			
		||||
					errs.Append(processMetric(
 | 
			
		||||
						metric, metricFamiliesByName,
 | 
			
		||||
						metricHashes,
 | 
			
		||||
						nil,
 | 
			
		||||
					))
 | 
			
		||||
				}
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			// Start more workers.
 | 
			
		||||
			go collectWorker()
 | 
			
		||||
			goroutineBudget--
 | 
			
		||||
			runtime.Gosched()
 | 
			
		||||
		}
 | 
			
		||||
		// Once both checkedMetricChan and uncheckdMetricChan are closed
 | 
			
		||||
		// and drained, the contraption above will nil out cmc and umc,
 | 
			
		||||
		// and then we can leave the collect loop here.
 | 
			
		||||
		if cmc == nil && umc == nil {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return internal.NormalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WriteToTextfile calls Gather on the provided Gatherer, encodes the result in the
 | 
			
		||||
// Prometheus text format, and writes it to a temporary file. Upon success, the
 | 
			
		||||
// temporary file is renamed to the provided filename.
 | 
			
		||||
//
 | 
			
		||||
// This is intended for use with the textfile collector of the node exporter.
 | 
			
		||||
// Note that the node exporter expects the filename to be suffixed with ".prom".
 | 
			
		||||
func WriteToTextfile(filename string, g Gatherer) error {
 | 
			
		||||
	tmp, err := ioutil.TempFile(filepath.Dir(filename), filepath.Base(filename))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer os.Remove(tmp.Name())
 | 
			
		||||
 | 
			
		||||
	mfs, err := g.Gather()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	for _, mf := range mfs {
 | 
			
		||||
		if _, err := expfmt.MetricFamilyToText(tmp, mf); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if err := tmp.Close(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := os.Chmod(tmp.Name(), 0644); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return os.Rename(tmp.Name(), filename)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// processMetric is an internal helper method only used by the Gather method.
 | 
			
		||||
func processMetric(
 | 
			
		||||
	metric Metric,
 | 
			
		||||
	metricFamiliesByName map[string]*dto.MetricFamily,
 | 
			
		||||
	metricHashes map[uint64]struct{},
 | 
			
		||||
	registeredDescIDs map[uint64]struct{},
 | 
			
		||||
) error {
 | 
			
		||||
	desc := metric.Desc()
 | 
			
		||||
	// Wrapped metrics collected by an unchecked Collector can have an
 | 
			
		||||
	// invalid Desc.
 | 
			
		||||
	if desc.err != nil {
 | 
			
		||||
		return desc.err
 | 
			
		||||
	}
 | 
			
		||||
	dtoMetric := &dto.Metric{}
 | 
			
		||||
	if err := metric.Write(dtoMetric); err != nil {
 | 
			
		||||
		return fmt.Errorf("error collecting metric %v: %s", desc, err)
 | 
			
		||||
	}
 | 
			
		||||
	metricFamily, ok := metricFamiliesByName[desc.fqName]
 | 
			
		||||
	if ok { // Existing name.
 | 
			
		||||
		if metricFamily.GetHelp() != desc.help {
 | 
			
		||||
			return fmt.Errorf(
 | 
			
		||||
				"collected metric %s %s has help %q but should have %q",
 | 
			
		||||
				desc.fqName, dtoMetric, desc.help, metricFamily.GetHelp(),
 | 
			
		||||
			)
 | 
			
		||||
		}
 | 
			
		||||
		// TODO(beorn7): Simplify switch once Desc has type.
 | 
			
		||||
		switch metricFamily.GetType() {
 | 
			
		||||
		case dto.MetricType_COUNTER:
 | 
			
		||||
			if dtoMetric.Counter == nil {
 | 
			
		||||
				return fmt.Errorf(
 | 
			
		||||
					"collected metric %s %s should be a Counter",
 | 
			
		||||
					desc.fqName, dtoMetric,
 | 
			
		||||
				)
 | 
			
		||||
			}
 | 
			
		||||
		case dto.MetricType_GAUGE:
 | 
			
		||||
			if dtoMetric.Gauge == nil {
 | 
			
		||||
				return fmt.Errorf(
 | 
			
		||||
					"collected metric %s %s should be a Gauge",
 | 
			
		||||
					desc.fqName, dtoMetric,
 | 
			
		||||
				)
 | 
			
		||||
			}
 | 
			
		||||
		case dto.MetricType_SUMMARY:
 | 
			
		||||
			if dtoMetric.Summary == nil {
 | 
			
		||||
				return fmt.Errorf(
 | 
			
		||||
					"collected metric %s %s should be a Summary",
 | 
			
		||||
					desc.fqName, dtoMetric,
 | 
			
		||||
				)
 | 
			
		||||
			}
 | 
			
		||||
		case dto.MetricType_UNTYPED:
 | 
			
		||||
			if dtoMetric.Untyped == nil {
 | 
			
		||||
				return fmt.Errorf(
 | 
			
		||||
					"collected metric %s %s should be Untyped",
 | 
			
		||||
					desc.fqName, dtoMetric,
 | 
			
		||||
				)
 | 
			
		||||
			}
 | 
			
		||||
		case dto.MetricType_HISTOGRAM:
 | 
			
		||||
			if dtoMetric.Histogram == nil {
 | 
			
		||||
				return fmt.Errorf(
 | 
			
		||||
					"collected metric %s %s should be a Histogram",
 | 
			
		||||
					desc.fqName, dtoMetric,
 | 
			
		||||
				)
 | 
			
		||||
			}
 | 
			
		||||
		default:
 | 
			
		||||
			panic("encountered MetricFamily with invalid type")
 | 
			
		||||
		}
 | 
			
		||||
	} else { // New name.
 | 
			
		||||
		metricFamily = &dto.MetricFamily{}
 | 
			
		||||
		metricFamily.Name = proto.String(desc.fqName)
 | 
			
		||||
		metricFamily.Help = proto.String(desc.help)
 | 
			
		||||
		// TODO(beorn7): Simplify switch once Desc has type.
 | 
			
		||||
		switch {
 | 
			
		||||
		case dtoMetric.Gauge != nil:
 | 
			
		||||
			metricFamily.Type = dto.MetricType_GAUGE.Enum()
 | 
			
		||||
		case dtoMetric.Counter != nil:
 | 
			
		||||
			metricFamily.Type = dto.MetricType_COUNTER.Enum()
 | 
			
		||||
		case dtoMetric.Summary != nil:
 | 
			
		||||
			metricFamily.Type = dto.MetricType_SUMMARY.Enum()
 | 
			
		||||
		case dtoMetric.Untyped != nil:
 | 
			
		||||
			metricFamily.Type = dto.MetricType_UNTYPED.Enum()
 | 
			
		||||
		case dtoMetric.Histogram != nil:
 | 
			
		||||
			metricFamily.Type = dto.MetricType_HISTOGRAM.Enum()
 | 
			
		||||
		default:
 | 
			
		||||
			return fmt.Errorf("empty metric collected: %s", dtoMetric)
 | 
			
		||||
		}
 | 
			
		||||
		if err := checkSuffixCollisions(metricFamily, metricFamiliesByName); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		metricFamiliesByName[desc.fqName] = metricFamily
 | 
			
		||||
	}
 | 
			
		||||
	if err := checkMetricConsistency(metricFamily, dtoMetric, metricHashes); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if registeredDescIDs != nil {
 | 
			
		||||
		// Is the desc registered at all?
 | 
			
		||||
		if _, exist := registeredDescIDs[desc.id]; !exist {
 | 
			
		||||
			return fmt.Errorf(
 | 
			
		||||
				"collected metric %s %s with unregistered descriptor %s",
 | 
			
		||||
				metricFamily.GetName(), dtoMetric, desc,
 | 
			
		||||
			)
 | 
			
		||||
		}
 | 
			
		||||
		if err := checkDescConsistency(metricFamily, dtoMetric, desc); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	metricFamily.Metric = append(metricFamily.Metric, dtoMetric)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Gatherers is a slice of Gatherer instances that implements the Gatherer
 | 
			
		||||
// interface itself. Its Gather method calls Gather on all Gatherers in the
 | 
			
		||||
// slice in order and returns the merged results. Errors returned from the
 | 
			
		||||
// Gather calls are all returned in a flattened MultiError. Duplicate and
 | 
			
		||||
// inconsistent Metrics are skipped (first occurrence in slice order wins) and
 | 
			
		||||
// reported in the returned error.
 | 
			
		||||
//
 | 
			
		||||
// Gatherers can be used to merge the Gather results from multiple
 | 
			
		||||
// Registries. It also provides a way to directly inject existing MetricFamily
 | 
			
		||||
// protobufs into the gathering by creating a custom Gatherer with a Gather
 | 
			
		||||
// method that simply returns the existing MetricFamily protobufs. Note that no
 | 
			
		||||
// registration is involved (in contrast to Collector registration), so
 | 
			
		||||
// obviously registration-time checks cannot happen. Any inconsistencies between
 | 
			
		||||
// the gathered MetricFamilies are reported as errors by the Gather method, and
 | 
			
		||||
// inconsistent Metrics are dropped. Invalid parts of the MetricFamilies
 | 
			
		||||
// (e.g. syntactically invalid metric or label names) will go undetected.
 | 
			
		||||
type Gatherers []Gatherer
 | 
			
		||||
 | 
			
		||||
// Gather implements Gatherer.
 | 
			
		||||
func (gs Gatherers) Gather() ([]*dto.MetricFamily, error) {
 | 
			
		||||
	var (
 | 
			
		||||
		metricFamiliesByName = map[string]*dto.MetricFamily{}
 | 
			
		||||
		metricHashes         = map[uint64]struct{}{}
 | 
			
		||||
		errs                 MultiError // The collected errors to return in the end.
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	for i, g := range gs {
 | 
			
		||||
		mfs, err := g.Gather()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if multiErr, ok := err.(MultiError); ok {
 | 
			
		||||
				for _, err := range multiErr {
 | 
			
		||||
					errs = append(errs, fmt.Errorf("[from Gatherer #%d] %s", i+1, err))
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				errs = append(errs, fmt.Errorf("[from Gatherer #%d] %s", i+1, err))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		for _, mf := range mfs {
 | 
			
		||||
			existingMF, exists := metricFamiliesByName[mf.GetName()]
 | 
			
		||||
			if exists {
 | 
			
		||||
				if existingMF.GetHelp() != mf.GetHelp() {
 | 
			
		||||
					errs = append(errs, fmt.Errorf(
 | 
			
		||||
						"gathered metric family %s has help %q but should have %q",
 | 
			
		||||
						mf.GetName(), mf.GetHelp(), existingMF.GetHelp(),
 | 
			
		||||
					))
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				if existingMF.GetType() != mf.GetType() {
 | 
			
		||||
					errs = append(errs, fmt.Errorf(
 | 
			
		||||
						"gathered metric family %s has type %s but should have %s",
 | 
			
		||||
						mf.GetName(), mf.GetType(), existingMF.GetType(),
 | 
			
		||||
					))
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				existingMF = &dto.MetricFamily{}
 | 
			
		||||
				existingMF.Name = mf.Name
 | 
			
		||||
				existingMF.Help = mf.Help
 | 
			
		||||
				existingMF.Type = mf.Type
 | 
			
		||||
				if err := checkSuffixCollisions(existingMF, metricFamiliesByName); err != nil {
 | 
			
		||||
					errs = append(errs, err)
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				metricFamiliesByName[mf.GetName()] = existingMF
 | 
			
		||||
			}
 | 
			
		||||
			for _, m := range mf.Metric {
 | 
			
		||||
				if err := checkMetricConsistency(existingMF, m, metricHashes); err != nil {
 | 
			
		||||
					errs = append(errs, err)
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				existingMF.Metric = append(existingMF.Metric, m)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return internal.NormalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// checkSuffixCollisions checks for collisions with the “magic” suffixes the
 | 
			
		||||
// Prometheus text format and the internal metric representation of the
 | 
			
		||||
// Prometheus server add while flattening Summaries and Histograms.
 | 
			
		||||
func checkSuffixCollisions(mf *dto.MetricFamily, mfs map[string]*dto.MetricFamily) error {
 | 
			
		||||
	var (
 | 
			
		||||
		newName              = mf.GetName()
 | 
			
		||||
		newType              = mf.GetType()
 | 
			
		||||
		newNameWithoutSuffix = ""
 | 
			
		||||
	)
 | 
			
		||||
	switch {
 | 
			
		||||
	case strings.HasSuffix(newName, "_count"):
 | 
			
		||||
		newNameWithoutSuffix = newName[:len(newName)-6]
 | 
			
		||||
	case strings.HasSuffix(newName, "_sum"):
 | 
			
		||||
		newNameWithoutSuffix = newName[:len(newName)-4]
 | 
			
		||||
	case strings.HasSuffix(newName, "_bucket"):
 | 
			
		||||
		newNameWithoutSuffix = newName[:len(newName)-7]
 | 
			
		||||
	}
 | 
			
		||||
	if newNameWithoutSuffix != "" {
 | 
			
		||||
		if existingMF, ok := mfs[newNameWithoutSuffix]; ok {
 | 
			
		||||
			switch existingMF.GetType() {
 | 
			
		||||
			case dto.MetricType_SUMMARY:
 | 
			
		||||
				if !strings.HasSuffix(newName, "_bucket") {
 | 
			
		||||
					return fmt.Errorf(
 | 
			
		||||
						"collected metric named %q collides with previously collected summary named %q",
 | 
			
		||||
						newName, newNameWithoutSuffix,
 | 
			
		||||
					)
 | 
			
		||||
				}
 | 
			
		||||
			case dto.MetricType_HISTOGRAM:
 | 
			
		||||
				return fmt.Errorf(
 | 
			
		||||
					"collected metric named %q collides with previously collected histogram named %q",
 | 
			
		||||
					newName, newNameWithoutSuffix,
 | 
			
		||||
				)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if newType == dto.MetricType_SUMMARY || newType == dto.MetricType_HISTOGRAM {
 | 
			
		||||
		if _, ok := mfs[newName+"_count"]; ok {
 | 
			
		||||
			return fmt.Errorf(
 | 
			
		||||
				"collected histogram or summary named %q collides with previously collected metric named %q",
 | 
			
		||||
				newName, newName+"_count",
 | 
			
		||||
			)
 | 
			
		||||
		}
 | 
			
		||||
		if _, ok := mfs[newName+"_sum"]; ok {
 | 
			
		||||
			return fmt.Errorf(
 | 
			
		||||
				"collected histogram or summary named %q collides with previously collected metric named %q",
 | 
			
		||||
				newName, newName+"_sum",
 | 
			
		||||
			)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if newType == dto.MetricType_HISTOGRAM {
 | 
			
		||||
		if _, ok := mfs[newName+"_bucket"]; ok {
 | 
			
		||||
			return fmt.Errorf(
 | 
			
		||||
				"collected histogram named %q collides with previously collected metric named %q",
 | 
			
		||||
				newName, newName+"_bucket",
 | 
			
		||||
			)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// checkMetricConsistency checks if the provided Metric is consistent with the
 | 
			
		||||
// provided MetricFamily. It also hashes the Metric labels and the MetricFamily
 | 
			
		||||
// name. If the resulting hash is already in the provided metricHashes, an error
 | 
			
		||||
// is returned. If not, it is added to metricHashes.
 | 
			
		||||
func checkMetricConsistency(
 | 
			
		||||
	metricFamily *dto.MetricFamily,
 | 
			
		||||
	dtoMetric *dto.Metric,
 | 
			
		||||
	metricHashes map[uint64]struct{},
 | 
			
		||||
) error {
 | 
			
		||||
	name := metricFamily.GetName()
 | 
			
		||||
 | 
			
		||||
	// Type consistency with metric family.
 | 
			
		||||
	if metricFamily.GetType() == dto.MetricType_GAUGE && dtoMetric.Gauge == nil ||
 | 
			
		||||
		metricFamily.GetType() == dto.MetricType_COUNTER && dtoMetric.Counter == nil ||
 | 
			
		||||
		metricFamily.GetType() == dto.MetricType_SUMMARY && dtoMetric.Summary == nil ||
 | 
			
		||||
		metricFamily.GetType() == dto.MetricType_HISTOGRAM && dtoMetric.Histogram == nil ||
 | 
			
		||||
		metricFamily.GetType() == dto.MetricType_UNTYPED && dtoMetric.Untyped == nil {
 | 
			
		||||
		return fmt.Errorf(
 | 
			
		||||
			"collected metric %q { %s} is not a %s",
 | 
			
		||||
			name, dtoMetric, metricFamily.GetType(),
 | 
			
		||||
		)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	previousLabelName := ""
 | 
			
		||||
	for _, labelPair := range dtoMetric.GetLabel() {
 | 
			
		||||
		labelName := labelPair.GetName()
 | 
			
		||||
		if labelName == previousLabelName {
 | 
			
		||||
			return fmt.Errorf(
 | 
			
		||||
				"collected metric %q { %s} has two or more labels with the same name: %s",
 | 
			
		||||
				name, dtoMetric, labelName,
 | 
			
		||||
			)
 | 
			
		||||
		}
 | 
			
		||||
		if !checkLabelName(labelName) {
 | 
			
		||||
			return fmt.Errorf(
 | 
			
		||||
				"collected metric %q { %s} has a label with an invalid name: %s",
 | 
			
		||||
				name, dtoMetric, labelName,
 | 
			
		||||
			)
 | 
			
		||||
		}
 | 
			
		||||
		if dtoMetric.Summary != nil && labelName == quantileLabel {
 | 
			
		||||
			return fmt.Errorf(
 | 
			
		||||
				"collected metric %q { %s} must not have an explicit %q label",
 | 
			
		||||
				name, dtoMetric, quantileLabel,
 | 
			
		||||
			)
 | 
			
		||||
		}
 | 
			
		||||
		if !utf8.ValidString(labelPair.GetValue()) {
 | 
			
		||||
			return fmt.Errorf(
 | 
			
		||||
				"collected metric %q { %s} has a label named %q whose value is not utf8: %#v",
 | 
			
		||||
				name, dtoMetric, labelName, labelPair.GetValue())
 | 
			
		||||
		}
 | 
			
		||||
		previousLabelName = labelName
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Is the metric unique (i.e. no other metric with the same name and the same labels)?
 | 
			
		||||
	h := xxhash.New()
 | 
			
		||||
	h.WriteString(name)
 | 
			
		||||
	h.Write(separatorByteSlice)
 | 
			
		||||
	// Make sure label pairs are sorted. We depend on it for the consistency
 | 
			
		||||
	// check.
 | 
			
		||||
	if !sort.IsSorted(labelPairSorter(dtoMetric.Label)) {
 | 
			
		||||
		// We cannot sort dtoMetric.Label in place as it is immutable by contract.
 | 
			
		||||
		copiedLabels := make([]*dto.LabelPair, len(dtoMetric.Label))
 | 
			
		||||
		copy(copiedLabels, dtoMetric.Label)
 | 
			
		||||
		sort.Sort(labelPairSorter(copiedLabels))
 | 
			
		||||
		dtoMetric.Label = copiedLabels
 | 
			
		||||
	}
 | 
			
		||||
	for _, lp := range dtoMetric.Label {
 | 
			
		||||
		h.WriteString(lp.GetName())
 | 
			
		||||
		h.Write(separatorByteSlice)
 | 
			
		||||
		h.WriteString(lp.GetValue())
 | 
			
		||||
		h.Write(separatorByteSlice)
 | 
			
		||||
	}
 | 
			
		||||
	hSum := h.Sum64()
 | 
			
		||||
	if _, exists := metricHashes[hSum]; exists {
 | 
			
		||||
		return fmt.Errorf(
 | 
			
		||||
			"collected metric %q { %s} was collected before with the same name and label values",
 | 
			
		||||
			name, dtoMetric,
 | 
			
		||||
		)
 | 
			
		||||
	}
 | 
			
		||||
	metricHashes[hSum] = struct{}{}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func checkDescConsistency(
 | 
			
		||||
	metricFamily *dto.MetricFamily,
 | 
			
		||||
	dtoMetric *dto.Metric,
 | 
			
		||||
	desc *Desc,
 | 
			
		||||
) error {
 | 
			
		||||
	// Desc help consistency with metric family help.
 | 
			
		||||
	if metricFamily.GetHelp() != desc.help {
 | 
			
		||||
		return fmt.Errorf(
 | 
			
		||||
			"collected metric %s %s has help %q but should have %q",
 | 
			
		||||
			metricFamily.GetName(), dtoMetric, metricFamily.GetHelp(), desc.help,
 | 
			
		||||
		)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Is the desc consistent with the content of the metric?
 | 
			
		||||
	lpsFromDesc := make([]*dto.LabelPair, len(desc.constLabelPairs), len(dtoMetric.Label))
 | 
			
		||||
	copy(lpsFromDesc, desc.constLabelPairs)
 | 
			
		||||
	for _, l := range desc.variableLabels {
 | 
			
		||||
		lpsFromDesc = append(lpsFromDesc, &dto.LabelPair{
 | 
			
		||||
			Name: proto.String(l),
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	if len(lpsFromDesc) != len(dtoMetric.Label) {
 | 
			
		||||
		return fmt.Errorf(
 | 
			
		||||
			"labels in collected metric %s %s are inconsistent with descriptor %s",
 | 
			
		||||
			metricFamily.GetName(), dtoMetric, desc,
 | 
			
		||||
		)
 | 
			
		||||
	}
 | 
			
		||||
	sort.Sort(labelPairSorter(lpsFromDesc))
 | 
			
		||||
	for i, lpFromDesc := range lpsFromDesc {
 | 
			
		||||
		lpFromMetric := dtoMetric.Label[i]
 | 
			
		||||
		if lpFromDesc.GetName() != lpFromMetric.GetName() ||
 | 
			
		||||
			lpFromDesc.Value != nil && lpFromDesc.GetValue() != lpFromMetric.GetValue() {
 | 
			
		||||
			return fmt.Errorf(
 | 
			
		||||
				"labels in collected metric %s %s are inconsistent with descriptor %s",
 | 
			
		||||
				metricFamily.GetName(), dtoMetric, desc,
 | 
			
		||||
			)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										736
									
								
								vendor/github.com/prometheus/client_golang/prometheus/summary.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										736
									
								
								vendor/github.com/prometheus/client_golang/prometheus/summary.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,736 @@
 | 
			
		||||
// 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 prometheus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"math"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"sync/atomic"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/beorn7/perks/quantile"
 | 
			
		||||
	"github.com/golang/protobuf/proto"
 | 
			
		||||
 | 
			
		||||
	dto "github.com/prometheus/client_model/go"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// quantileLabel is used for the label that defines the quantile in a
 | 
			
		||||
// summary.
 | 
			
		||||
const quantileLabel = "quantile"
 | 
			
		||||
 | 
			
		||||
// A Summary captures individual observations from an event or sample stream and
 | 
			
		||||
// summarizes them in a manner similar to traditional summary statistics: 1. sum
 | 
			
		||||
// of observations, 2. observation count, 3. rank estimations.
 | 
			
		||||
//
 | 
			
		||||
// A typical use-case is the observation of request latencies. By default, a
 | 
			
		||||
// Summary provides the median, the 90th and the 99th percentile of the latency
 | 
			
		||||
// as rank estimations. However, the default behavior will change in the
 | 
			
		||||
// upcoming v1.0.0 of the library. There will be no rank estimations at all by
 | 
			
		||||
// default. For a sane transition, it is recommended to set the desired rank
 | 
			
		||||
// estimations explicitly.
 | 
			
		||||
//
 | 
			
		||||
// Note that the rank estimations cannot be aggregated in a meaningful way with
 | 
			
		||||
// the Prometheus query language (i.e. you cannot average or add them). If you
 | 
			
		||||
// need aggregatable quantiles (e.g. you want the 99th percentile latency of all
 | 
			
		||||
// queries served across all instances of a service), consider the Histogram
 | 
			
		||||
// metric type. See the Prometheus documentation for more details.
 | 
			
		||||
//
 | 
			
		||||
// To create Summary instances, use NewSummary.
 | 
			
		||||
type Summary interface {
 | 
			
		||||
	Metric
 | 
			
		||||
	Collector
 | 
			
		||||
 | 
			
		||||
	// Observe adds a single observation to the summary.
 | 
			
		||||
	Observe(float64)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var errQuantileLabelNotAllowed = fmt.Errorf(
 | 
			
		||||
	"%q is not allowed as label name in summaries", quantileLabel,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Default values for SummaryOpts.
 | 
			
		||||
const (
 | 
			
		||||
	// DefMaxAge is the default duration for which observations stay
 | 
			
		||||
	// relevant.
 | 
			
		||||
	DefMaxAge time.Duration = 10 * time.Minute
 | 
			
		||||
	// DefAgeBuckets is the default number of buckets used to calculate the
 | 
			
		||||
	// age of observations.
 | 
			
		||||
	DefAgeBuckets = 5
 | 
			
		||||
	// DefBufCap is the standard buffer size for collecting Summary observations.
 | 
			
		||||
	DefBufCap = 500
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SummaryOpts bundles the options for creating a Summary metric. It is
 | 
			
		||||
// mandatory to set Name to a non-empty string. While all other fields are
 | 
			
		||||
// optional and can safely be left at their zero value, it is recommended to set
 | 
			
		||||
// a help string and to explicitly set the Objectives field to the desired value
 | 
			
		||||
// as the default value will change in the upcoming v1.0.0 of the library.
 | 
			
		||||
type SummaryOpts struct {
 | 
			
		||||
	// Namespace, Subsystem, and Name are components of the fully-qualified
 | 
			
		||||
	// name of the Summary (created by joining these components with
 | 
			
		||||
	// "_"). Only Name is mandatory, the others merely help structuring the
 | 
			
		||||
	// name. Note that the fully-qualified name of the Summary must be a
 | 
			
		||||
	// valid Prometheus metric name.
 | 
			
		||||
	Namespace string
 | 
			
		||||
	Subsystem string
 | 
			
		||||
	Name      string
 | 
			
		||||
 | 
			
		||||
	// Help provides information about this Summary.
 | 
			
		||||
	//
 | 
			
		||||
	// Metrics with the same fully-qualified name must have the same Help
 | 
			
		||||
	// string.
 | 
			
		||||
	Help string
 | 
			
		||||
 | 
			
		||||
	// ConstLabels are used to attach fixed labels to this metric. Metrics
 | 
			
		||||
	// with the same fully-qualified name must have the same label names in
 | 
			
		||||
	// their ConstLabels.
 | 
			
		||||
	//
 | 
			
		||||
	// Due to the way a Summary is represented in the Prometheus text format
 | 
			
		||||
	// and how it is handled by the Prometheus server internally, “quantile”
 | 
			
		||||
	// is an illegal label name. Construction of a Summary or SummaryVec
 | 
			
		||||
	// will panic if this label name is used in ConstLabels.
 | 
			
		||||
	//
 | 
			
		||||
	// ConstLabels are only used rarely. In particular, do not use them to
 | 
			
		||||
	// attach the same labels to all your metrics. Those use cases are
 | 
			
		||||
	// better covered by target labels set by the scraping Prometheus
 | 
			
		||||
	// server, or by one specific metric (e.g. a build_info or a
 | 
			
		||||
	// machine_role metric). See also
 | 
			
		||||
	// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels,-not-static-scraped-labels
 | 
			
		||||
	ConstLabels Labels
 | 
			
		||||
 | 
			
		||||
	// Objectives defines the quantile rank estimates with their respective
 | 
			
		||||
	// absolute error. If Objectives[q] = e, then the value reported for q
 | 
			
		||||
	// will be the φ-quantile value for some φ between q-e and q+e.  The
 | 
			
		||||
	// default value is an empty map, resulting in a summary without
 | 
			
		||||
	// quantiles.
 | 
			
		||||
	Objectives map[float64]float64
 | 
			
		||||
 | 
			
		||||
	// MaxAge defines the duration for which an observation stays relevant
 | 
			
		||||
	// for the summary. Must be positive. The default value is DefMaxAge.
 | 
			
		||||
	MaxAge time.Duration
 | 
			
		||||
 | 
			
		||||
	// AgeBuckets is the number of buckets used to exclude observations that
 | 
			
		||||
	// are older than MaxAge from the summary. A higher number has a
 | 
			
		||||
	// resource penalty, so only increase it if the higher resolution is
 | 
			
		||||
	// really required. For very high observation rates, you might want to
 | 
			
		||||
	// reduce the number of age buckets. With only one age bucket, you will
 | 
			
		||||
	// effectively see a complete reset of the summary each time MaxAge has
 | 
			
		||||
	// passed. The default value is DefAgeBuckets.
 | 
			
		||||
	AgeBuckets uint32
 | 
			
		||||
 | 
			
		||||
	// BufCap defines the default sample stream buffer size.  The default
 | 
			
		||||
	// value of DefBufCap should suffice for most uses. If there is a need
 | 
			
		||||
	// to increase the value, a multiple of 500 is recommended (because that
 | 
			
		||||
	// is the internal buffer size of the underlying package
 | 
			
		||||
	// "github.com/bmizerany/perks/quantile").
 | 
			
		||||
	BufCap uint32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Problem with the sliding-window decay algorithm... The Merge method of
 | 
			
		||||
// perk/quantile is actually not working as advertised - and it might be
 | 
			
		||||
// unfixable, as the underlying algorithm is apparently not capable of merging
 | 
			
		||||
// summaries in the first place. To avoid using Merge, we are currently adding
 | 
			
		||||
// observations to _each_ age bucket, i.e. the effort to add a sample is
 | 
			
		||||
// essentially multiplied by the number of age buckets. When rotating age
 | 
			
		||||
// buckets, we empty the previous head stream. On scrape time, we simply take
 | 
			
		||||
// the quantiles from the head stream (no merging required). Result: More effort
 | 
			
		||||
// on observation time, less effort on scrape time, which is exactly the
 | 
			
		||||
// opposite of what we try to accomplish, but at least the results are correct.
 | 
			
		||||
//
 | 
			
		||||
// The quite elegant previous contraption to merge the age buckets efficiently
 | 
			
		||||
// on scrape time (see code up commit 6b9530d72ea715f0ba612c0120e6e09fbf1d49d0)
 | 
			
		||||
// can't be used anymore.
 | 
			
		||||
 | 
			
		||||
// NewSummary creates a new Summary based on the provided SummaryOpts.
 | 
			
		||||
func NewSummary(opts SummaryOpts) Summary {
 | 
			
		||||
	return newSummary(
 | 
			
		||||
		NewDesc(
 | 
			
		||||
			BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
 | 
			
		||||
			opts.Help,
 | 
			
		||||
			nil,
 | 
			
		||||
			opts.ConstLabels,
 | 
			
		||||
		),
 | 
			
		||||
		opts,
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
 | 
			
		||||
	if len(desc.variableLabels) != len(labelValues) {
 | 
			
		||||
		panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, labelValues))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, n := range desc.variableLabels {
 | 
			
		||||
		if n == quantileLabel {
 | 
			
		||||
			panic(errQuantileLabelNotAllowed)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for _, lp := range desc.constLabelPairs {
 | 
			
		||||
		if lp.GetName() == quantileLabel {
 | 
			
		||||
			panic(errQuantileLabelNotAllowed)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if opts.Objectives == nil {
 | 
			
		||||
		opts.Objectives = map[float64]float64{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if opts.MaxAge < 0 {
 | 
			
		||||
		panic(fmt.Errorf("illegal max age MaxAge=%v", opts.MaxAge))
 | 
			
		||||
	}
 | 
			
		||||
	if opts.MaxAge == 0 {
 | 
			
		||||
		opts.MaxAge = DefMaxAge
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if opts.AgeBuckets == 0 {
 | 
			
		||||
		opts.AgeBuckets = DefAgeBuckets
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if opts.BufCap == 0 {
 | 
			
		||||
		opts.BufCap = DefBufCap
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(opts.Objectives) == 0 {
 | 
			
		||||
		// Use the lock-free implementation of a Summary without objectives.
 | 
			
		||||
		s := &noObjectivesSummary{
 | 
			
		||||
			desc:       desc,
 | 
			
		||||
			labelPairs: makeLabelPairs(desc, labelValues),
 | 
			
		||||
			counts:     [2]*summaryCounts{{}, {}},
 | 
			
		||||
		}
 | 
			
		||||
		s.init(s) // Init self-collection.
 | 
			
		||||
		return s
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s := &summary{
 | 
			
		||||
		desc: desc,
 | 
			
		||||
 | 
			
		||||
		objectives:       opts.Objectives,
 | 
			
		||||
		sortedObjectives: make([]float64, 0, len(opts.Objectives)),
 | 
			
		||||
 | 
			
		||||
		labelPairs: makeLabelPairs(desc, labelValues),
 | 
			
		||||
 | 
			
		||||
		hotBuf:         make([]float64, 0, opts.BufCap),
 | 
			
		||||
		coldBuf:        make([]float64, 0, opts.BufCap),
 | 
			
		||||
		streamDuration: opts.MaxAge / time.Duration(opts.AgeBuckets),
 | 
			
		||||
	}
 | 
			
		||||
	s.headStreamExpTime = time.Now().Add(s.streamDuration)
 | 
			
		||||
	s.hotBufExpTime = s.headStreamExpTime
 | 
			
		||||
 | 
			
		||||
	for i := uint32(0); i < opts.AgeBuckets; i++ {
 | 
			
		||||
		s.streams = append(s.streams, s.newStream())
 | 
			
		||||
	}
 | 
			
		||||
	s.headStream = s.streams[0]
 | 
			
		||||
 | 
			
		||||
	for qu := range s.objectives {
 | 
			
		||||
		s.sortedObjectives = append(s.sortedObjectives, qu)
 | 
			
		||||
	}
 | 
			
		||||
	sort.Float64s(s.sortedObjectives)
 | 
			
		||||
 | 
			
		||||
	s.init(s) // Init self-collection.
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type summary struct {
 | 
			
		||||
	selfCollector
 | 
			
		||||
 | 
			
		||||
	bufMtx sync.Mutex // Protects hotBuf and hotBufExpTime.
 | 
			
		||||
	mtx    sync.Mutex // Protects every other moving part.
 | 
			
		||||
	// Lock bufMtx before mtx if both are needed.
 | 
			
		||||
 | 
			
		||||
	desc *Desc
 | 
			
		||||
 | 
			
		||||
	objectives       map[float64]float64
 | 
			
		||||
	sortedObjectives []float64
 | 
			
		||||
 | 
			
		||||
	labelPairs []*dto.LabelPair
 | 
			
		||||
 | 
			
		||||
	sum float64
 | 
			
		||||
	cnt uint64
 | 
			
		||||
 | 
			
		||||
	hotBuf, coldBuf []float64
 | 
			
		||||
 | 
			
		||||
	streams                          []*quantile.Stream
 | 
			
		||||
	streamDuration                   time.Duration
 | 
			
		||||
	headStream                       *quantile.Stream
 | 
			
		||||
	headStreamIdx                    int
 | 
			
		||||
	headStreamExpTime, hotBufExpTime time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *summary) Desc() *Desc {
 | 
			
		||||
	return s.desc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *summary) Observe(v float64) {
 | 
			
		||||
	s.bufMtx.Lock()
 | 
			
		||||
	defer s.bufMtx.Unlock()
 | 
			
		||||
 | 
			
		||||
	now := time.Now()
 | 
			
		||||
	if now.After(s.hotBufExpTime) {
 | 
			
		||||
		s.asyncFlush(now)
 | 
			
		||||
	}
 | 
			
		||||
	s.hotBuf = append(s.hotBuf, v)
 | 
			
		||||
	if len(s.hotBuf) == cap(s.hotBuf) {
 | 
			
		||||
		s.asyncFlush(now)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *summary) Write(out *dto.Metric) error {
 | 
			
		||||
	sum := &dto.Summary{}
 | 
			
		||||
	qs := make([]*dto.Quantile, 0, len(s.objectives))
 | 
			
		||||
 | 
			
		||||
	s.bufMtx.Lock()
 | 
			
		||||
	s.mtx.Lock()
 | 
			
		||||
	// Swap bufs even if hotBuf is empty to set new hotBufExpTime.
 | 
			
		||||
	s.swapBufs(time.Now())
 | 
			
		||||
	s.bufMtx.Unlock()
 | 
			
		||||
 | 
			
		||||
	s.flushColdBuf()
 | 
			
		||||
	sum.SampleCount = proto.Uint64(s.cnt)
 | 
			
		||||
	sum.SampleSum = proto.Float64(s.sum)
 | 
			
		||||
 | 
			
		||||
	for _, rank := range s.sortedObjectives {
 | 
			
		||||
		var q float64
 | 
			
		||||
		if s.headStream.Count() == 0 {
 | 
			
		||||
			q = math.NaN()
 | 
			
		||||
		} else {
 | 
			
		||||
			q = s.headStream.Query(rank)
 | 
			
		||||
		}
 | 
			
		||||
		qs = append(qs, &dto.Quantile{
 | 
			
		||||
			Quantile: proto.Float64(rank),
 | 
			
		||||
			Value:    proto.Float64(q),
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.mtx.Unlock()
 | 
			
		||||
 | 
			
		||||
	if len(qs) > 0 {
 | 
			
		||||
		sort.Sort(quantSort(qs))
 | 
			
		||||
	}
 | 
			
		||||
	sum.Quantile = qs
 | 
			
		||||
 | 
			
		||||
	out.Summary = sum
 | 
			
		||||
	out.Label = s.labelPairs
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *summary) newStream() *quantile.Stream {
 | 
			
		||||
	return quantile.NewTargeted(s.objectives)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// asyncFlush needs bufMtx locked.
 | 
			
		||||
func (s *summary) asyncFlush(now time.Time) {
 | 
			
		||||
	s.mtx.Lock()
 | 
			
		||||
	s.swapBufs(now)
 | 
			
		||||
 | 
			
		||||
	// Unblock the original goroutine that was responsible for the mutation
 | 
			
		||||
	// that triggered the compaction.  But hold onto the global non-buffer
 | 
			
		||||
	// state mutex until the operation finishes.
 | 
			
		||||
	go func() {
 | 
			
		||||
		s.flushColdBuf()
 | 
			
		||||
		s.mtx.Unlock()
 | 
			
		||||
	}()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// rotateStreams needs mtx AND bufMtx locked.
 | 
			
		||||
func (s *summary) maybeRotateStreams() {
 | 
			
		||||
	for !s.hotBufExpTime.Equal(s.headStreamExpTime) {
 | 
			
		||||
		s.headStream.Reset()
 | 
			
		||||
		s.headStreamIdx++
 | 
			
		||||
		if s.headStreamIdx >= len(s.streams) {
 | 
			
		||||
			s.headStreamIdx = 0
 | 
			
		||||
		}
 | 
			
		||||
		s.headStream = s.streams[s.headStreamIdx]
 | 
			
		||||
		s.headStreamExpTime = s.headStreamExpTime.Add(s.streamDuration)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// flushColdBuf needs mtx locked.
 | 
			
		||||
func (s *summary) flushColdBuf() {
 | 
			
		||||
	for _, v := range s.coldBuf {
 | 
			
		||||
		for _, stream := range s.streams {
 | 
			
		||||
			stream.Insert(v)
 | 
			
		||||
		}
 | 
			
		||||
		s.cnt++
 | 
			
		||||
		s.sum += v
 | 
			
		||||
	}
 | 
			
		||||
	s.coldBuf = s.coldBuf[0:0]
 | 
			
		||||
	s.maybeRotateStreams()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// swapBufs needs mtx AND bufMtx locked, coldBuf must be empty.
 | 
			
		||||
func (s *summary) swapBufs(now time.Time) {
 | 
			
		||||
	if len(s.coldBuf) != 0 {
 | 
			
		||||
		panic("coldBuf is not empty")
 | 
			
		||||
	}
 | 
			
		||||
	s.hotBuf, s.coldBuf = s.coldBuf, s.hotBuf
 | 
			
		||||
	// hotBuf is now empty and gets new expiration set.
 | 
			
		||||
	for now.After(s.hotBufExpTime) {
 | 
			
		||||
		s.hotBufExpTime = s.hotBufExpTime.Add(s.streamDuration)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type summaryCounts struct {
 | 
			
		||||
	// sumBits contains the bits of the float64 representing the sum of all
 | 
			
		||||
	// observations. sumBits and count have to go first in the struct to
 | 
			
		||||
	// guarantee alignment for atomic operations.
 | 
			
		||||
	// http://golang.org/pkg/sync/atomic/#pkg-note-BUG
 | 
			
		||||
	sumBits uint64
 | 
			
		||||
	count   uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type noObjectivesSummary struct {
 | 
			
		||||
	// countAndHotIdx enables lock-free writes with use of atomic updates.
 | 
			
		||||
	// The most significant bit is the hot index [0 or 1] of the count field
 | 
			
		||||
	// below. Observe calls update the hot one. All remaining bits count the
 | 
			
		||||
	// number of Observe calls. Observe starts by incrementing this counter,
 | 
			
		||||
	// and finish by incrementing the count field in the respective
 | 
			
		||||
	// summaryCounts, as a marker for completion.
 | 
			
		||||
	//
 | 
			
		||||
	// Calls of the Write method (which are non-mutating reads from the
 | 
			
		||||
	// perspective of the summary) swap the hot–cold under the writeMtx
 | 
			
		||||
	// lock. A cooldown is awaited (while locked) by comparing the number of
 | 
			
		||||
	// observations with the initiation count. Once they match, then the
 | 
			
		||||
	// last observation on the now cool one has completed. All cool fields must
 | 
			
		||||
	// be merged into the new hot before releasing writeMtx.
 | 
			
		||||
 | 
			
		||||
	// Fields with atomic access first! See alignment constraint:
 | 
			
		||||
	// http://golang.org/pkg/sync/atomic/#pkg-note-BUG
 | 
			
		||||
	countAndHotIdx uint64
 | 
			
		||||
 | 
			
		||||
	selfCollector
 | 
			
		||||
	desc     *Desc
 | 
			
		||||
	writeMtx sync.Mutex // Only used in the Write method.
 | 
			
		||||
 | 
			
		||||
	// Two counts, one is "hot" for lock-free observations, the other is
 | 
			
		||||
	// "cold" for writing out a dto.Metric. It has to be an array of
 | 
			
		||||
	// pointers to guarantee 64bit alignment of the histogramCounts, see
 | 
			
		||||
	// http://golang.org/pkg/sync/atomic/#pkg-note-BUG.
 | 
			
		||||
	counts [2]*summaryCounts
 | 
			
		||||
 | 
			
		||||
	labelPairs []*dto.LabelPair
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *noObjectivesSummary) Desc() *Desc {
 | 
			
		||||
	return s.desc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *noObjectivesSummary) Observe(v float64) {
 | 
			
		||||
	// We increment h.countAndHotIdx so that the counter in the lower
 | 
			
		||||
	// 63 bits gets incremented. At the same time, we get the new value
 | 
			
		||||
	// back, which we can use to find the currently-hot counts.
 | 
			
		||||
	n := atomic.AddUint64(&s.countAndHotIdx, 1)
 | 
			
		||||
	hotCounts := s.counts[n>>63]
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		oldBits := atomic.LoadUint64(&hotCounts.sumBits)
 | 
			
		||||
		newBits := math.Float64bits(math.Float64frombits(oldBits) + v)
 | 
			
		||||
		if atomic.CompareAndSwapUint64(&hotCounts.sumBits, oldBits, newBits) {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// Increment count last as we take it as a signal that the observation
 | 
			
		||||
	// is complete.
 | 
			
		||||
	atomic.AddUint64(&hotCounts.count, 1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *noObjectivesSummary) Write(out *dto.Metric) error {
 | 
			
		||||
	// For simplicity, we protect this whole method by a mutex. It is not in
 | 
			
		||||
	// the hot path, i.e. Observe is called much more often than Write. The
 | 
			
		||||
	// complication of making Write lock-free isn't worth it, if possible at
 | 
			
		||||
	// all.
 | 
			
		||||
	s.writeMtx.Lock()
 | 
			
		||||
	defer s.writeMtx.Unlock()
 | 
			
		||||
 | 
			
		||||
	// Adding 1<<63 switches the hot index (from 0 to 1 or from 1 to 0)
 | 
			
		||||
	// without touching the count bits. See the struct comments for a full
 | 
			
		||||
	// description of the algorithm.
 | 
			
		||||
	n := atomic.AddUint64(&s.countAndHotIdx, 1<<63)
 | 
			
		||||
	// count is contained unchanged in the lower 63 bits.
 | 
			
		||||
	count := n & ((1 << 63) - 1)
 | 
			
		||||
	// The most significant bit tells us which counts is hot. The complement
 | 
			
		||||
	// is thus the cold one.
 | 
			
		||||
	hotCounts := s.counts[n>>63]
 | 
			
		||||
	coldCounts := s.counts[(^n)>>63]
 | 
			
		||||
 | 
			
		||||
	// Await cooldown.
 | 
			
		||||
	for count != atomic.LoadUint64(&coldCounts.count) {
 | 
			
		||||
		runtime.Gosched() // Let observations get work done.
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sum := &dto.Summary{
 | 
			
		||||
		SampleCount: proto.Uint64(count),
 | 
			
		||||
		SampleSum:   proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.sumBits))),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	out.Summary = sum
 | 
			
		||||
	out.Label = s.labelPairs
 | 
			
		||||
 | 
			
		||||
	// Finally add all the cold counts to the new hot counts and reset the cold counts.
 | 
			
		||||
	atomic.AddUint64(&hotCounts.count, count)
 | 
			
		||||
	atomic.StoreUint64(&coldCounts.count, 0)
 | 
			
		||||
	for {
 | 
			
		||||
		oldBits := atomic.LoadUint64(&hotCounts.sumBits)
 | 
			
		||||
		newBits := math.Float64bits(math.Float64frombits(oldBits) + sum.GetSampleSum())
 | 
			
		||||
		if atomic.CompareAndSwapUint64(&hotCounts.sumBits, oldBits, newBits) {
 | 
			
		||||
			atomic.StoreUint64(&coldCounts.sumBits, 0)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type quantSort []*dto.Quantile
 | 
			
		||||
 | 
			
		||||
func (s quantSort) Len() int {
 | 
			
		||||
	return len(s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s quantSort) Swap(i, j int) {
 | 
			
		||||
	s[i], s[j] = s[j], s[i]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s quantSort) Less(i, j int) bool {
 | 
			
		||||
	return s[i].GetQuantile() < s[j].GetQuantile()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SummaryVec is a Collector that bundles a set of Summaries that all share the
 | 
			
		||||
// same Desc, but have different values for their variable labels. This is used
 | 
			
		||||
// if you want to count the same thing partitioned by various dimensions
 | 
			
		||||
// (e.g. HTTP request latencies, partitioned by status code and method). Create
 | 
			
		||||
// instances with NewSummaryVec.
 | 
			
		||||
type SummaryVec struct {
 | 
			
		||||
	*metricVec
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewSummaryVec creates a new SummaryVec based on the provided SummaryOpts and
 | 
			
		||||
// partitioned by the given label names.
 | 
			
		||||
//
 | 
			
		||||
// Due to the way a Summary is represented in the Prometheus text format and how
 | 
			
		||||
// it is handled by the Prometheus server internally, “quantile” is an illegal
 | 
			
		||||
// label name. NewSummaryVec will panic if this label name is used.
 | 
			
		||||
func NewSummaryVec(opts SummaryOpts, labelNames []string) *SummaryVec {
 | 
			
		||||
	for _, ln := range labelNames {
 | 
			
		||||
		if ln == quantileLabel {
 | 
			
		||||
			panic(errQuantileLabelNotAllowed)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	desc := NewDesc(
 | 
			
		||||
		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
 | 
			
		||||
		opts.Help,
 | 
			
		||||
		labelNames,
 | 
			
		||||
		opts.ConstLabels,
 | 
			
		||||
	)
 | 
			
		||||
	return &SummaryVec{
 | 
			
		||||
		metricVec: newMetricVec(desc, func(lvs ...string) Metric {
 | 
			
		||||
			return newSummary(desc, opts, lvs...)
 | 
			
		||||
		}),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetMetricWithLabelValues returns the Summary for the given slice of label
 | 
			
		||||
// values (same order as the VariableLabels in Desc). If that combination of
 | 
			
		||||
// label values is accessed for the first time, a new Summary is created.
 | 
			
		||||
//
 | 
			
		||||
// It is possible to call this method without using the returned Summary to only
 | 
			
		||||
// create the new Summary but leave it at its starting value, a Summary without
 | 
			
		||||
// any observations.
 | 
			
		||||
//
 | 
			
		||||
// Keeping the Summary for later use is possible (and should be considered if
 | 
			
		||||
// performance is critical), but keep in mind that Reset, DeleteLabelValues and
 | 
			
		||||
// Delete can be used to delete the Summary from the SummaryVec. In that case,
 | 
			
		||||
// the Summary will still exist, but it will not be exported anymore, even if a
 | 
			
		||||
// Summary with the same label values is created later. See also the CounterVec
 | 
			
		||||
// example.
 | 
			
		||||
//
 | 
			
		||||
// An error is returned if the number of label values is not the same as the
 | 
			
		||||
// number of VariableLabels in Desc (minus any curried labels).
 | 
			
		||||
//
 | 
			
		||||
// Note that for more than one label value, this method is prone to mistakes
 | 
			
		||||
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
 | 
			
		||||
// an alternative to avoid that type of mistake. For higher label numbers, the
 | 
			
		||||
// latter has a much more readable (albeit more verbose) syntax, but it comes
 | 
			
		||||
// with a performance overhead (for creating and processing the Labels map).
 | 
			
		||||
// See also the GaugeVec example.
 | 
			
		||||
func (v *SummaryVec) GetMetricWithLabelValues(lvs ...string) (Observer, error) {
 | 
			
		||||
	metric, err := v.metricVec.getMetricWithLabelValues(lvs...)
 | 
			
		||||
	if metric != nil {
 | 
			
		||||
		return metric.(Observer), err
 | 
			
		||||
	}
 | 
			
		||||
	return nil, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetMetricWith returns the Summary for the given Labels map (the label names
 | 
			
		||||
// must match those of the VariableLabels in Desc). If that label map is
 | 
			
		||||
// accessed for the first time, a new Summary is created. Implications of
 | 
			
		||||
// creating a Summary without using it and keeping the Summary for later use are
 | 
			
		||||
// the same as for GetMetricWithLabelValues.
 | 
			
		||||
//
 | 
			
		||||
// An error is returned if the number and names of the Labels are inconsistent
 | 
			
		||||
// with those of the VariableLabels in Desc (minus any curried labels).
 | 
			
		||||
//
 | 
			
		||||
// This method is used for the same purpose as
 | 
			
		||||
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
 | 
			
		||||
// methods.
 | 
			
		||||
func (v *SummaryVec) GetMetricWith(labels Labels) (Observer, error) {
 | 
			
		||||
	metric, err := v.metricVec.getMetricWith(labels)
 | 
			
		||||
	if metric != nil {
 | 
			
		||||
		return metric.(Observer), err
 | 
			
		||||
	}
 | 
			
		||||
	return nil, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WithLabelValues works as GetMetricWithLabelValues, but panics where
 | 
			
		||||
// GetMetricWithLabelValues would have returned an error. Not returning an
 | 
			
		||||
// error allows shortcuts like
 | 
			
		||||
//     myVec.WithLabelValues("404", "GET").Observe(42.21)
 | 
			
		||||
func (v *SummaryVec) WithLabelValues(lvs ...string) Observer {
 | 
			
		||||
	s, err := v.GetMetricWithLabelValues(lvs...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// With works as GetMetricWith, but panics where GetMetricWithLabels would have
 | 
			
		||||
// returned an error. Not returning an error allows shortcuts like
 | 
			
		||||
//     myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Observe(42.21)
 | 
			
		||||
func (v *SummaryVec) With(labels Labels) Observer {
 | 
			
		||||
	s, err := v.GetMetricWith(labels)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CurryWith returns a vector curried with the provided labels, i.e. the
 | 
			
		||||
// returned vector has those labels pre-set for all labeled operations performed
 | 
			
		||||
// on it. The cardinality of the curried vector is reduced accordingly. The
 | 
			
		||||
// order of the remaining labels stays the same (just with the curried labels
 | 
			
		||||
// taken out of the sequence – which is relevant for the
 | 
			
		||||
// (GetMetric)WithLabelValues methods). It is possible to curry a curried
 | 
			
		||||
// vector, but only with labels not yet used for currying before.
 | 
			
		||||
//
 | 
			
		||||
// The metrics contained in the SummaryVec are shared between the curried and
 | 
			
		||||
// uncurried vectors. They are just accessed differently. Curried and uncurried
 | 
			
		||||
// vectors behave identically in terms of collection. Only one must be
 | 
			
		||||
// registered with a given registry (usually the uncurried version). The Reset
 | 
			
		||||
// method deletes all metrics, even if called on a curried vector.
 | 
			
		||||
func (v *SummaryVec) CurryWith(labels Labels) (ObserverVec, error) {
 | 
			
		||||
	vec, err := v.curryWith(labels)
 | 
			
		||||
	if vec != nil {
 | 
			
		||||
		return &SummaryVec{vec}, err
 | 
			
		||||
	}
 | 
			
		||||
	return nil, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MustCurryWith works as CurryWith but panics where CurryWith would have
 | 
			
		||||
// returned an error.
 | 
			
		||||
func (v *SummaryVec) MustCurryWith(labels Labels) ObserverVec {
 | 
			
		||||
	vec, err := v.CurryWith(labels)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	return vec
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type constSummary struct {
 | 
			
		||||
	desc       *Desc
 | 
			
		||||
	count      uint64
 | 
			
		||||
	sum        float64
 | 
			
		||||
	quantiles  map[float64]float64
 | 
			
		||||
	labelPairs []*dto.LabelPair
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *constSummary) Desc() *Desc {
 | 
			
		||||
	return s.desc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *constSummary) Write(out *dto.Metric) error {
 | 
			
		||||
	sum := &dto.Summary{}
 | 
			
		||||
	qs := make([]*dto.Quantile, 0, len(s.quantiles))
 | 
			
		||||
 | 
			
		||||
	sum.SampleCount = proto.Uint64(s.count)
 | 
			
		||||
	sum.SampleSum = proto.Float64(s.sum)
 | 
			
		||||
 | 
			
		||||
	for rank, q := range s.quantiles {
 | 
			
		||||
		qs = append(qs, &dto.Quantile{
 | 
			
		||||
			Quantile: proto.Float64(rank),
 | 
			
		||||
			Value:    proto.Float64(q),
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(qs) > 0 {
 | 
			
		||||
		sort.Sort(quantSort(qs))
 | 
			
		||||
	}
 | 
			
		||||
	sum.Quantile = qs
 | 
			
		||||
 | 
			
		||||
	out.Summary = sum
 | 
			
		||||
	out.Label = s.labelPairs
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewConstSummary returns a metric representing a Prometheus summary with fixed
 | 
			
		||||
// values for the count, sum, and quantiles. As those parameters cannot be
 | 
			
		||||
// changed, the returned value does not implement the Summary interface (but
 | 
			
		||||
// only the Metric interface). Users of this package will not have much use for
 | 
			
		||||
// it in regular operations. However, when implementing custom Collectors, it is
 | 
			
		||||
// useful as a throw-away metric that is generated on the fly to send it to
 | 
			
		||||
// Prometheus in the Collect method.
 | 
			
		||||
//
 | 
			
		||||
// quantiles maps ranks to quantile values. For example, a median latency of
 | 
			
		||||
// 0.23s and a 99th percentile latency of 0.56s would be expressed as:
 | 
			
		||||
//     map[float64]float64{0.5: 0.23, 0.99: 0.56}
 | 
			
		||||
//
 | 
			
		||||
// NewConstSummary returns an error if the length of labelValues is not
 | 
			
		||||
// consistent with the variable labels in Desc or if Desc is invalid.
 | 
			
		||||
func NewConstSummary(
 | 
			
		||||
	desc *Desc,
 | 
			
		||||
	count uint64,
 | 
			
		||||
	sum float64,
 | 
			
		||||
	quantiles map[float64]float64,
 | 
			
		||||
	labelValues ...string,
 | 
			
		||||
) (Metric, error) {
 | 
			
		||||
	if desc.err != nil {
 | 
			
		||||
		return nil, desc.err
 | 
			
		||||
	}
 | 
			
		||||
	if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &constSummary{
 | 
			
		||||
		desc:       desc,
 | 
			
		||||
		count:      count,
 | 
			
		||||
		sum:        sum,
 | 
			
		||||
		quantiles:  quantiles,
 | 
			
		||||
		labelPairs: makeLabelPairs(desc, labelValues),
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MustNewConstSummary is a version of NewConstSummary that panics where
 | 
			
		||||
// NewConstMetric would have returned an error.
 | 
			
		||||
func MustNewConstSummary(
 | 
			
		||||
	desc *Desc,
 | 
			
		||||
	count uint64,
 | 
			
		||||
	sum float64,
 | 
			
		||||
	quantiles map[float64]float64,
 | 
			
		||||
	labelValues ...string,
 | 
			
		||||
) Metric {
 | 
			
		||||
	m, err := NewConstSummary(desc, count, sum, quantiles, labelValues...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	return m
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										54
									
								
								vendor/github.com/prometheus/client_golang/prometheus/timer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								vendor/github.com/prometheus/client_golang/prometheus/timer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
			
		||||
// Copyright 2016 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 prometheus
 | 
			
		||||
 | 
			
		||||
import "time"
 | 
			
		||||
 | 
			
		||||
// Timer is a helper type to time functions. Use NewTimer to create new
 | 
			
		||||
// instances.
 | 
			
		||||
type Timer struct {
 | 
			
		||||
	begin    time.Time
 | 
			
		||||
	observer Observer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewTimer creates a new Timer. The provided Observer is used to observe a
 | 
			
		||||
// duration in seconds. Timer is usually used to time a function call in the
 | 
			
		||||
// following way:
 | 
			
		||||
//    func TimeMe() {
 | 
			
		||||
//        timer := NewTimer(myHistogram)
 | 
			
		||||
//        defer timer.ObserveDuration()
 | 
			
		||||
//        // Do actual work.
 | 
			
		||||
//    }
 | 
			
		||||
func NewTimer(o Observer) *Timer {
 | 
			
		||||
	return &Timer{
 | 
			
		||||
		begin:    time.Now(),
 | 
			
		||||
		observer: o,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ObserveDuration records the duration passed since the Timer was created with
 | 
			
		||||
// NewTimer. It calls the Observe method of the Observer provided during
 | 
			
		||||
// construction with the duration in seconds as an argument. The observed
 | 
			
		||||
// duration is also returned. ObserveDuration is usually called with a defer
 | 
			
		||||
// statement.
 | 
			
		||||
//
 | 
			
		||||
// Note that this method is only guaranteed to never observe negative durations
 | 
			
		||||
// if used with Go1.9+.
 | 
			
		||||
func (t *Timer) ObserveDuration() time.Duration {
 | 
			
		||||
	d := time.Since(t.begin)
 | 
			
		||||
	if t.observer != nil {
 | 
			
		||||
		t.observer.Observe(d.Seconds())
 | 
			
		||||
	}
 | 
			
		||||
	return d
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										42
									
								
								vendor/github.com/prometheus/client_golang/prometheus/untyped.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								vendor/github.com/prometheus/client_golang/prometheus/untyped.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
// 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 prometheus
 | 
			
		||||
 | 
			
		||||
// UntypedOpts is an alias for Opts. See there for doc comments.
 | 
			
		||||
type UntypedOpts Opts
 | 
			
		||||
 | 
			
		||||
// UntypedFunc works like GaugeFunc but the collected metric is of type
 | 
			
		||||
// "Untyped". UntypedFunc is useful to mirror an external metric of unknown
 | 
			
		||||
// type.
 | 
			
		||||
//
 | 
			
		||||
// To create UntypedFunc instances, use NewUntypedFunc.
 | 
			
		||||
type UntypedFunc interface {
 | 
			
		||||
	Metric
 | 
			
		||||
	Collector
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewUntypedFunc creates a new UntypedFunc based on the provided
 | 
			
		||||
// UntypedOpts. The value reported is determined by calling the given function
 | 
			
		||||
// from within the Write method. Take into account that metric collection may
 | 
			
		||||
// happen concurrently. If that results in concurrent calls to Write, like in
 | 
			
		||||
// the case where an UntypedFunc is directly registered with Prometheus, the
 | 
			
		||||
// provided function must be concurrency-safe.
 | 
			
		||||
func NewUntypedFunc(opts UntypedOpts, function func() float64) UntypedFunc {
 | 
			
		||||
	return newValueFunc(NewDesc(
 | 
			
		||||
		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
 | 
			
		||||
		opts.Help,
 | 
			
		||||
		nil,
 | 
			
		||||
		opts.ConstLabels,
 | 
			
		||||
	), UntypedValue, function)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										204
									
								
								vendor/github.com/prometheus/client_golang/prometheus/value.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								vendor/github.com/prometheus/client_golang/prometheus/value.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,204 @@
 | 
			
		||||
// 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 prometheus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"time"
 | 
			
		||||
	"unicode/utf8"
 | 
			
		||||
 | 
			
		||||
	"github.com/golang/protobuf/proto"
 | 
			
		||||
	"github.com/golang/protobuf/ptypes"
 | 
			
		||||
 | 
			
		||||
	dto "github.com/prometheus/client_model/go"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ValueType is an enumeration of metric types that represent a simple value.
 | 
			
		||||
type ValueType int
 | 
			
		||||
 | 
			
		||||
// Possible values for the ValueType enum. Use UntypedValue to mark a metric
 | 
			
		||||
// with an unknown type.
 | 
			
		||||
const (
 | 
			
		||||
	_ ValueType = iota
 | 
			
		||||
	CounterValue
 | 
			
		||||
	GaugeValue
 | 
			
		||||
	UntypedValue
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// valueFunc is a generic metric for simple values retrieved on collect time
 | 
			
		||||
// from a function. It implements Metric and Collector. Its effective type is
 | 
			
		||||
// determined by ValueType. This is a low-level building block used by the
 | 
			
		||||
// library to back the implementations of CounterFunc, GaugeFunc, and
 | 
			
		||||
// UntypedFunc.
 | 
			
		||||
type valueFunc struct {
 | 
			
		||||
	selfCollector
 | 
			
		||||
 | 
			
		||||
	desc       *Desc
 | 
			
		||||
	valType    ValueType
 | 
			
		||||
	function   func() float64
 | 
			
		||||
	labelPairs []*dto.LabelPair
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newValueFunc returns a newly allocated valueFunc with the given Desc and
 | 
			
		||||
// ValueType. The value reported is determined by calling the given function
 | 
			
		||||
// from within the Write method. Take into account that metric collection may
 | 
			
		||||
// happen concurrently. If that results in concurrent calls to Write, like in
 | 
			
		||||
// the case where a valueFunc is directly registered with Prometheus, the
 | 
			
		||||
// provided function must be concurrency-safe.
 | 
			
		||||
func newValueFunc(desc *Desc, valueType ValueType, function func() float64) *valueFunc {
 | 
			
		||||
	result := &valueFunc{
 | 
			
		||||
		desc:       desc,
 | 
			
		||||
		valType:    valueType,
 | 
			
		||||
		function:   function,
 | 
			
		||||
		labelPairs: makeLabelPairs(desc, nil),
 | 
			
		||||
	}
 | 
			
		||||
	result.init(result)
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v *valueFunc) Desc() *Desc {
 | 
			
		||||
	return v.desc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v *valueFunc) Write(out *dto.Metric) error {
 | 
			
		||||
	return populateMetric(v.valType, v.function(), v.labelPairs, nil, out)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewConstMetric returns a metric with one fixed value that cannot be
 | 
			
		||||
// changed. Users of this package will not have much use for it in regular
 | 
			
		||||
// operations. However, when implementing custom Collectors, it is useful as a
 | 
			
		||||
// throw-away metric that is generated on the fly to send it to Prometheus in
 | 
			
		||||
// the Collect method. NewConstMetric returns an error if the length of
 | 
			
		||||
// labelValues is not consistent with the variable labels in Desc or if Desc is
 | 
			
		||||
// invalid.
 | 
			
		||||
func NewConstMetric(desc *Desc, valueType ValueType, value float64, labelValues ...string) (Metric, error) {
 | 
			
		||||
	if desc.err != nil {
 | 
			
		||||
		return nil, desc.err
 | 
			
		||||
	}
 | 
			
		||||
	if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &constMetric{
 | 
			
		||||
		desc:       desc,
 | 
			
		||||
		valType:    valueType,
 | 
			
		||||
		val:        value,
 | 
			
		||||
		labelPairs: makeLabelPairs(desc, labelValues),
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MustNewConstMetric is a version of NewConstMetric that panics where
 | 
			
		||||
// NewConstMetric would have returned an error.
 | 
			
		||||
func MustNewConstMetric(desc *Desc, valueType ValueType, value float64, labelValues ...string) Metric {
 | 
			
		||||
	m, err := NewConstMetric(desc, valueType, value, labelValues...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	return m
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type constMetric struct {
 | 
			
		||||
	desc       *Desc
 | 
			
		||||
	valType    ValueType
 | 
			
		||||
	val        float64
 | 
			
		||||
	labelPairs []*dto.LabelPair
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *constMetric) Desc() *Desc {
 | 
			
		||||
	return m.desc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *constMetric) Write(out *dto.Metric) error {
 | 
			
		||||
	return populateMetric(m.valType, m.val, m.labelPairs, nil, out)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func populateMetric(
 | 
			
		||||
	t ValueType,
 | 
			
		||||
	v float64,
 | 
			
		||||
	labelPairs []*dto.LabelPair,
 | 
			
		||||
	e *dto.Exemplar,
 | 
			
		||||
	m *dto.Metric,
 | 
			
		||||
) error {
 | 
			
		||||
	m.Label = labelPairs
 | 
			
		||||
	switch t {
 | 
			
		||||
	case CounterValue:
 | 
			
		||||
		m.Counter = &dto.Counter{Value: proto.Float64(v), Exemplar: e}
 | 
			
		||||
	case GaugeValue:
 | 
			
		||||
		m.Gauge = &dto.Gauge{Value: proto.Float64(v)}
 | 
			
		||||
	case UntypedValue:
 | 
			
		||||
		m.Untyped = &dto.Untyped{Value: proto.Float64(v)}
 | 
			
		||||
	default:
 | 
			
		||||
		return fmt.Errorf("encountered unknown type %v", t)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func makeLabelPairs(desc *Desc, labelValues []string) []*dto.LabelPair {
 | 
			
		||||
	totalLen := len(desc.variableLabels) + len(desc.constLabelPairs)
 | 
			
		||||
	if totalLen == 0 {
 | 
			
		||||
		// Super fast path.
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if len(desc.variableLabels) == 0 {
 | 
			
		||||
		// Moderately fast path.
 | 
			
		||||
		return desc.constLabelPairs
 | 
			
		||||
	}
 | 
			
		||||
	labelPairs := make([]*dto.LabelPair, 0, totalLen)
 | 
			
		||||
	for i, n := range desc.variableLabels {
 | 
			
		||||
		labelPairs = append(labelPairs, &dto.LabelPair{
 | 
			
		||||
			Name:  proto.String(n),
 | 
			
		||||
			Value: proto.String(labelValues[i]),
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	labelPairs = append(labelPairs, desc.constLabelPairs...)
 | 
			
		||||
	sort.Sort(labelPairSorter(labelPairs))
 | 
			
		||||
	return labelPairs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExemplarMaxRunes is the max total number of runes allowed in exemplar labels.
 | 
			
		||||
const ExemplarMaxRunes = 64
 | 
			
		||||
 | 
			
		||||
// newExemplar creates a new dto.Exemplar from the provided values. An error is
 | 
			
		||||
// returned if any of the label names or values are invalid or if the total
 | 
			
		||||
// number of runes in the label names and values exceeds ExemplarMaxRunes.
 | 
			
		||||
func newExemplar(value float64, ts time.Time, l Labels) (*dto.Exemplar, error) {
 | 
			
		||||
	e := &dto.Exemplar{}
 | 
			
		||||
	e.Value = proto.Float64(value)
 | 
			
		||||
	tsProto, err := ptypes.TimestampProto(ts)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	e.Timestamp = tsProto
 | 
			
		||||
	labelPairs := make([]*dto.LabelPair, 0, len(l))
 | 
			
		||||
	var runes int
 | 
			
		||||
	for name, value := range l {
 | 
			
		||||
		if !checkLabelName(name) {
 | 
			
		||||
			return nil, fmt.Errorf("exemplar label name %q is invalid", name)
 | 
			
		||||
		}
 | 
			
		||||
		runes += utf8.RuneCountInString(name)
 | 
			
		||||
		if !utf8.ValidString(value) {
 | 
			
		||||
			return nil, fmt.Errorf("exemplar label value %q is not valid UTF-8", value)
 | 
			
		||||
		}
 | 
			
		||||
		runes += utf8.RuneCountInString(value)
 | 
			
		||||
		labelPairs = append(labelPairs, &dto.LabelPair{
 | 
			
		||||
			Name:  proto.String(name),
 | 
			
		||||
			Value: proto.String(value),
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	if runes > ExemplarMaxRunes {
 | 
			
		||||
		return nil, fmt.Errorf("exemplar labels have %d runes, exceeding the limit of %d", runes, ExemplarMaxRunes)
 | 
			
		||||
	}
 | 
			
		||||
	e.Label = labelPairs
 | 
			
		||||
	return e, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										484
									
								
								vendor/github.com/prometheus/client_golang/prometheus/vec.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										484
									
								
								vendor/github.com/prometheus/client_golang/prometheus/vec.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,484 @@
 | 
			
		||||
// 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 prometheus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"github.com/prometheus/common/model"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// metricVec is a Collector to bundle metrics of the same name that differ in
 | 
			
		||||
// their label values. metricVec is not used directly (and therefore
 | 
			
		||||
// unexported). It is used as a building block for implementations of vectors of
 | 
			
		||||
// a given metric type, like GaugeVec, CounterVec, SummaryVec, and HistogramVec.
 | 
			
		||||
// It also handles label currying.
 | 
			
		||||
type metricVec struct {
 | 
			
		||||
	*metricMap
 | 
			
		||||
 | 
			
		||||
	curry []curriedLabelValue
 | 
			
		||||
 | 
			
		||||
	// hashAdd and hashAddByte can be replaced for testing collision handling.
 | 
			
		||||
	hashAdd     func(h uint64, s string) uint64
 | 
			
		||||
	hashAddByte func(h uint64, b byte) uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newMetricVec returns an initialized metricVec.
 | 
			
		||||
func newMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *metricVec {
 | 
			
		||||
	return &metricVec{
 | 
			
		||||
		metricMap: &metricMap{
 | 
			
		||||
			metrics:   map[uint64][]metricWithLabelValues{},
 | 
			
		||||
			desc:      desc,
 | 
			
		||||
			newMetric: newMetric,
 | 
			
		||||
		},
 | 
			
		||||
		hashAdd:     hashAdd,
 | 
			
		||||
		hashAddByte: hashAddByte,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteLabelValues removes the metric where the variable labels are the same
 | 
			
		||||
// as those passed in as labels (same order as the VariableLabels in Desc). It
 | 
			
		||||
// returns true if a metric was deleted.
 | 
			
		||||
//
 | 
			
		||||
// It is not an error if the number of label values is not the same as the
 | 
			
		||||
// number of VariableLabels in Desc. However, such inconsistent label count can
 | 
			
		||||
// never match an actual metric, so the method will always return false in that
 | 
			
		||||
// case.
 | 
			
		||||
//
 | 
			
		||||
// Note that for more than one label value, this method is prone to mistakes
 | 
			
		||||
// caused by an incorrect order of arguments. Consider Delete(Labels) as an
 | 
			
		||||
// alternative to avoid that type of mistake. For higher label numbers, the
 | 
			
		||||
// latter has a much more readable (albeit more verbose) syntax, but it comes
 | 
			
		||||
// with a performance overhead (for creating and processing the Labels map).
 | 
			
		||||
// See also the CounterVec example.
 | 
			
		||||
func (m *metricVec) DeleteLabelValues(lvs ...string) bool {
 | 
			
		||||
	h, err := m.hashLabelValues(lvs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return m.metricMap.deleteByHashWithLabelValues(h, lvs, m.curry)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Delete deletes the metric where the variable labels are the same as those
 | 
			
		||||
// passed in as labels. It returns true if a metric was deleted.
 | 
			
		||||
//
 | 
			
		||||
// It is not an error if the number and names of the Labels are inconsistent
 | 
			
		||||
// with those of the VariableLabels in Desc. However, such inconsistent Labels
 | 
			
		||||
// can never match an actual metric, so the method will always return false in
 | 
			
		||||
// that case.
 | 
			
		||||
//
 | 
			
		||||
// This method is used for the same purpose as DeleteLabelValues(...string). See
 | 
			
		||||
// there for pros and cons of the two methods.
 | 
			
		||||
func (m *metricVec) Delete(labels Labels) bool {
 | 
			
		||||
	h, err := m.hashLabels(labels)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return m.metricMap.deleteByHashWithLabels(h, labels, m.curry)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Without explicit forwarding of Describe, Collect, Reset, those methods won't
 | 
			
		||||
// show up in GoDoc.
 | 
			
		||||
 | 
			
		||||
// Describe implements Collector.
 | 
			
		||||
func (m *metricVec) Describe(ch chan<- *Desc) { m.metricMap.Describe(ch) }
 | 
			
		||||
 | 
			
		||||
// Collect implements Collector.
 | 
			
		||||
func (m *metricVec) Collect(ch chan<- Metric) { m.metricMap.Collect(ch) }
 | 
			
		||||
 | 
			
		||||
// Reset deletes all metrics in this vector.
 | 
			
		||||
func (m *metricVec) Reset() { m.metricMap.Reset() }
 | 
			
		||||
 | 
			
		||||
func (m *metricVec) curryWith(labels Labels) (*metricVec, error) {
 | 
			
		||||
	var (
 | 
			
		||||
		newCurry []curriedLabelValue
 | 
			
		||||
		oldCurry = m.curry
 | 
			
		||||
		iCurry   int
 | 
			
		||||
	)
 | 
			
		||||
	for i, label := range m.desc.variableLabels {
 | 
			
		||||
		val, ok := labels[label]
 | 
			
		||||
		if iCurry < len(oldCurry) && oldCurry[iCurry].index == i {
 | 
			
		||||
			if ok {
 | 
			
		||||
				return nil, fmt.Errorf("label name %q is already curried", label)
 | 
			
		||||
			}
 | 
			
		||||
			newCurry = append(newCurry, oldCurry[iCurry])
 | 
			
		||||
			iCurry++
 | 
			
		||||
		} else {
 | 
			
		||||
			if !ok {
 | 
			
		||||
				continue // Label stays uncurried.
 | 
			
		||||
			}
 | 
			
		||||
			newCurry = append(newCurry, curriedLabelValue{i, val})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if l := len(oldCurry) + len(labels) - len(newCurry); l > 0 {
 | 
			
		||||
		return nil, fmt.Errorf("%d unknown label(s) found during currying", l)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &metricVec{
 | 
			
		||||
		metricMap:   m.metricMap,
 | 
			
		||||
		curry:       newCurry,
 | 
			
		||||
		hashAdd:     m.hashAdd,
 | 
			
		||||
		hashAddByte: m.hashAddByte,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *metricVec) getMetricWithLabelValues(lvs ...string) (Metric, error) {
 | 
			
		||||
	h, err := m.hashLabelValues(lvs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return m.metricMap.getOrCreateMetricWithLabelValues(h, lvs, m.curry), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *metricVec) getMetricWith(labels Labels) (Metric, error) {
 | 
			
		||||
	h, err := m.hashLabels(labels)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return m.metricMap.getOrCreateMetricWithLabels(h, labels, m.curry), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *metricVec) hashLabelValues(vals []string) (uint64, error) {
 | 
			
		||||
	if err := validateLabelValues(vals, len(m.desc.variableLabels)-len(m.curry)); err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		h             = hashNew()
 | 
			
		||||
		curry         = m.curry
 | 
			
		||||
		iVals, iCurry int
 | 
			
		||||
	)
 | 
			
		||||
	for i := 0; i < len(m.desc.variableLabels); i++ {
 | 
			
		||||
		if iCurry < len(curry) && curry[iCurry].index == i {
 | 
			
		||||
			h = m.hashAdd(h, curry[iCurry].value)
 | 
			
		||||
			iCurry++
 | 
			
		||||
		} else {
 | 
			
		||||
			h = m.hashAdd(h, vals[iVals])
 | 
			
		||||
			iVals++
 | 
			
		||||
		}
 | 
			
		||||
		h = m.hashAddByte(h, model.SeparatorByte)
 | 
			
		||||
	}
 | 
			
		||||
	return h, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *metricVec) hashLabels(labels Labels) (uint64, error) {
 | 
			
		||||
	if err := validateValuesInLabels(labels, len(m.desc.variableLabels)-len(m.curry)); err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		h      = hashNew()
 | 
			
		||||
		curry  = m.curry
 | 
			
		||||
		iCurry int
 | 
			
		||||
	)
 | 
			
		||||
	for i, label := range m.desc.variableLabels {
 | 
			
		||||
		val, ok := labels[label]
 | 
			
		||||
		if iCurry < len(curry) && curry[iCurry].index == i {
 | 
			
		||||
			if ok {
 | 
			
		||||
				return 0, fmt.Errorf("label name %q is already curried", label)
 | 
			
		||||
			}
 | 
			
		||||
			h = m.hashAdd(h, curry[iCurry].value)
 | 
			
		||||
			iCurry++
 | 
			
		||||
		} else {
 | 
			
		||||
			if !ok {
 | 
			
		||||
				return 0, fmt.Errorf("label name %q missing in label map", label)
 | 
			
		||||
			}
 | 
			
		||||
			h = m.hashAdd(h, val)
 | 
			
		||||
		}
 | 
			
		||||
		h = m.hashAddByte(h, model.SeparatorByte)
 | 
			
		||||
	}
 | 
			
		||||
	return h, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// metricWithLabelValues provides the metric and its label values for
 | 
			
		||||
// disambiguation on hash collision.
 | 
			
		||||
type metricWithLabelValues struct {
 | 
			
		||||
	values []string
 | 
			
		||||
	metric Metric
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// curriedLabelValue sets the curried value for a label at the given index.
 | 
			
		||||
type curriedLabelValue struct {
 | 
			
		||||
	index int
 | 
			
		||||
	value string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// metricMap is a helper for metricVec and shared between differently curried
 | 
			
		||||
// metricVecs.
 | 
			
		||||
type metricMap struct {
 | 
			
		||||
	mtx       sync.RWMutex // Protects metrics.
 | 
			
		||||
	metrics   map[uint64][]metricWithLabelValues
 | 
			
		||||
	desc      *Desc
 | 
			
		||||
	newMetric func(labelValues ...string) Metric
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Describe implements Collector. It will send exactly one Desc to the provided
 | 
			
		||||
// channel.
 | 
			
		||||
func (m *metricMap) Describe(ch chan<- *Desc) {
 | 
			
		||||
	ch <- m.desc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Collect implements Collector.
 | 
			
		||||
func (m *metricMap) Collect(ch chan<- Metric) {
 | 
			
		||||
	m.mtx.RLock()
 | 
			
		||||
	defer m.mtx.RUnlock()
 | 
			
		||||
 | 
			
		||||
	for _, metrics := range m.metrics {
 | 
			
		||||
		for _, metric := range metrics {
 | 
			
		||||
			ch <- metric.metric
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Reset deletes all metrics in this vector.
 | 
			
		||||
func (m *metricMap) Reset() {
 | 
			
		||||
	m.mtx.Lock()
 | 
			
		||||
	defer m.mtx.Unlock()
 | 
			
		||||
 | 
			
		||||
	for h := range m.metrics {
 | 
			
		||||
		delete(m.metrics, h)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// deleteByHashWithLabelValues removes the metric from the hash bucket h. If
 | 
			
		||||
// there are multiple matches in the bucket, use lvs to select a metric and
 | 
			
		||||
// remove only that metric.
 | 
			
		||||
func (m *metricMap) deleteByHashWithLabelValues(
 | 
			
		||||
	h uint64, lvs []string, curry []curriedLabelValue,
 | 
			
		||||
) bool {
 | 
			
		||||
	m.mtx.Lock()
 | 
			
		||||
	defer m.mtx.Unlock()
 | 
			
		||||
 | 
			
		||||
	metrics, ok := m.metrics[h]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	i := findMetricWithLabelValues(metrics, lvs, curry)
 | 
			
		||||
	if i >= len(metrics) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(metrics) > 1 {
 | 
			
		||||
		m.metrics[h] = append(metrics[:i], metrics[i+1:]...)
 | 
			
		||||
	} else {
 | 
			
		||||
		delete(m.metrics, h)
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// deleteByHashWithLabels removes the metric from the hash bucket h. If there
 | 
			
		||||
// are multiple matches in the bucket, use lvs to select a metric and remove
 | 
			
		||||
// only that metric.
 | 
			
		||||
func (m *metricMap) deleteByHashWithLabels(
 | 
			
		||||
	h uint64, labels Labels, curry []curriedLabelValue,
 | 
			
		||||
) bool {
 | 
			
		||||
	m.mtx.Lock()
 | 
			
		||||
	defer m.mtx.Unlock()
 | 
			
		||||
 | 
			
		||||
	metrics, ok := m.metrics[h]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	i := findMetricWithLabels(m.desc, metrics, labels, curry)
 | 
			
		||||
	if i >= len(metrics) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(metrics) > 1 {
 | 
			
		||||
		m.metrics[h] = append(metrics[:i], metrics[i+1:]...)
 | 
			
		||||
	} else {
 | 
			
		||||
		delete(m.metrics, h)
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getOrCreateMetricWithLabelValues retrieves the metric by hash and label value
 | 
			
		||||
// or creates it and returns the new one.
 | 
			
		||||
//
 | 
			
		||||
// This function holds the mutex.
 | 
			
		||||
func (m *metricMap) getOrCreateMetricWithLabelValues(
 | 
			
		||||
	hash uint64, lvs []string, curry []curriedLabelValue,
 | 
			
		||||
) Metric {
 | 
			
		||||
	m.mtx.RLock()
 | 
			
		||||
	metric, ok := m.getMetricWithHashAndLabelValues(hash, lvs, curry)
 | 
			
		||||
	m.mtx.RUnlock()
 | 
			
		||||
	if ok {
 | 
			
		||||
		return metric
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m.mtx.Lock()
 | 
			
		||||
	defer m.mtx.Unlock()
 | 
			
		||||
	metric, ok = m.getMetricWithHashAndLabelValues(hash, lvs, curry)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		inlinedLVs := inlineLabelValues(lvs, curry)
 | 
			
		||||
		metric = m.newMetric(inlinedLVs...)
 | 
			
		||||
		m.metrics[hash] = append(m.metrics[hash], metricWithLabelValues{values: inlinedLVs, metric: metric})
 | 
			
		||||
	}
 | 
			
		||||
	return metric
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getOrCreateMetricWithLabelValues retrieves the metric by hash and label value
 | 
			
		||||
// or creates it and returns the new one.
 | 
			
		||||
//
 | 
			
		||||
// This function holds the mutex.
 | 
			
		||||
func (m *metricMap) getOrCreateMetricWithLabels(
 | 
			
		||||
	hash uint64, labels Labels, curry []curriedLabelValue,
 | 
			
		||||
) Metric {
 | 
			
		||||
	m.mtx.RLock()
 | 
			
		||||
	metric, ok := m.getMetricWithHashAndLabels(hash, labels, curry)
 | 
			
		||||
	m.mtx.RUnlock()
 | 
			
		||||
	if ok {
 | 
			
		||||
		return metric
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m.mtx.Lock()
 | 
			
		||||
	defer m.mtx.Unlock()
 | 
			
		||||
	metric, ok = m.getMetricWithHashAndLabels(hash, labels, curry)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		lvs := extractLabelValues(m.desc, labels, curry)
 | 
			
		||||
		metric = m.newMetric(lvs...)
 | 
			
		||||
		m.metrics[hash] = append(m.metrics[hash], metricWithLabelValues{values: lvs, metric: metric})
 | 
			
		||||
	}
 | 
			
		||||
	return metric
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getMetricWithHashAndLabelValues gets a metric while handling possible
 | 
			
		||||
// collisions in the hash space. Must be called while holding the read mutex.
 | 
			
		||||
func (m *metricMap) getMetricWithHashAndLabelValues(
 | 
			
		||||
	h uint64, lvs []string, curry []curriedLabelValue,
 | 
			
		||||
) (Metric, bool) {
 | 
			
		||||
	metrics, ok := m.metrics[h]
 | 
			
		||||
	if ok {
 | 
			
		||||
		if i := findMetricWithLabelValues(metrics, lvs, curry); i < len(metrics) {
 | 
			
		||||
			return metrics[i].metric, true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getMetricWithHashAndLabels gets a metric while handling possible collisions in
 | 
			
		||||
// the hash space. Must be called while holding read mutex.
 | 
			
		||||
func (m *metricMap) getMetricWithHashAndLabels(
 | 
			
		||||
	h uint64, labels Labels, curry []curriedLabelValue,
 | 
			
		||||
) (Metric, bool) {
 | 
			
		||||
	metrics, ok := m.metrics[h]
 | 
			
		||||
	if ok {
 | 
			
		||||
		if i := findMetricWithLabels(m.desc, metrics, labels, curry); i < len(metrics) {
 | 
			
		||||
			return metrics[i].metric, true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// findMetricWithLabelValues returns the index of the matching metric or
 | 
			
		||||
// len(metrics) if not found.
 | 
			
		||||
func findMetricWithLabelValues(
 | 
			
		||||
	metrics []metricWithLabelValues, lvs []string, curry []curriedLabelValue,
 | 
			
		||||
) int {
 | 
			
		||||
	for i, metric := range metrics {
 | 
			
		||||
		if matchLabelValues(metric.values, lvs, curry) {
 | 
			
		||||
			return i
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return len(metrics)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// findMetricWithLabels returns the index of the matching metric or len(metrics)
 | 
			
		||||
// if not found.
 | 
			
		||||
func findMetricWithLabels(
 | 
			
		||||
	desc *Desc, metrics []metricWithLabelValues, labels Labels, curry []curriedLabelValue,
 | 
			
		||||
) int {
 | 
			
		||||
	for i, metric := range metrics {
 | 
			
		||||
		if matchLabels(desc, metric.values, labels, curry) {
 | 
			
		||||
			return i
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return len(metrics)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func matchLabelValues(values []string, lvs []string, curry []curriedLabelValue) bool {
 | 
			
		||||
	if len(values) != len(lvs)+len(curry) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	var iLVs, iCurry int
 | 
			
		||||
	for i, v := range values {
 | 
			
		||||
		if iCurry < len(curry) && curry[iCurry].index == i {
 | 
			
		||||
			if v != curry[iCurry].value {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
			iCurry++
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if v != lvs[iLVs] {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		iLVs++
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func matchLabels(desc *Desc, values []string, labels Labels, curry []curriedLabelValue) bool {
 | 
			
		||||
	if len(values) != len(labels)+len(curry) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	iCurry := 0
 | 
			
		||||
	for i, k := range desc.variableLabels {
 | 
			
		||||
		if iCurry < len(curry) && curry[iCurry].index == i {
 | 
			
		||||
			if values[i] != curry[iCurry].value {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
			iCurry++
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if values[i] != labels[k] {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func extractLabelValues(desc *Desc, labels Labels, curry []curriedLabelValue) []string {
 | 
			
		||||
	labelValues := make([]string, len(labels)+len(curry))
 | 
			
		||||
	iCurry := 0
 | 
			
		||||
	for i, k := range desc.variableLabels {
 | 
			
		||||
		if iCurry < len(curry) && curry[iCurry].index == i {
 | 
			
		||||
			labelValues[i] = curry[iCurry].value
 | 
			
		||||
			iCurry++
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		labelValues[i] = labels[k]
 | 
			
		||||
	}
 | 
			
		||||
	return labelValues
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func inlineLabelValues(lvs []string, curry []curriedLabelValue) []string {
 | 
			
		||||
	labelValues := make([]string, len(lvs)+len(curry))
 | 
			
		||||
	var iCurry, iLVs int
 | 
			
		||||
	for i := range labelValues {
 | 
			
		||||
		if iCurry < len(curry) && curry[iCurry].index == i {
 | 
			
		||||
			labelValues[i] = curry[iCurry].value
 | 
			
		||||
			iCurry++
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		labelValues[i] = lvs[iLVs]
 | 
			
		||||
		iLVs++
 | 
			
		||||
	}
 | 
			
		||||
	return labelValues
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										200
									
								
								vendor/github.com/prometheus/client_golang/prometheus/wrap.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								vendor/github.com/prometheus/client_golang/prometheus/wrap.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,200 @@
 | 
			
		||||
// Copyright 2018 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 prometheus
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"sort"
 | 
			
		||||
 | 
			
		||||
	"github.com/golang/protobuf/proto"
 | 
			
		||||
 | 
			
		||||
	dto "github.com/prometheus/client_model/go"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// WrapRegistererWith returns a Registerer wrapping the provided
 | 
			
		||||
// Registerer. Collectors registered with the returned Registerer will be
 | 
			
		||||
// registered with the wrapped Registerer in a modified way. The modified
 | 
			
		||||
// Collector adds the provided Labels to all Metrics it collects (as
 | 
			
		||||
// ConstLabels). The Metrics collected by the unmodified Collector must not
 | 
			
		||||
// duplicate any of those labels.
 | 
			
		||||
//
 | 
			
		||||
// WrapRegistererWith provides a way to add fixed labels to a subset of
 | 
			
		||||
// Collectors. It should not be used to add fixed labels to all metrics exposed.
 | 
			
		||||
//
 | 
			
		||||
// Conflicts between Collectors registered through the original Registerer with
 | 
			
		||||
// Collectors registered through the wrapping Registerer will still be
 | 
			
		||||
// detected. Any AlreadyRegisteredError returned by the Register method of
 | 
			
		||||
// either Registerer will contain the ExistingCollector in the form it was
 | 
			
		||||
// provided to the respective registry.
 | 
			
		||||
//
 | 
			
		||||
// The Collector example demonstrates a use of WrapRegistererWith.
 | 
			
		||||
func WrapRegistererWith(labels Labels, reg Registerer) Registerer {
 | 
			
		||||
	return &wrappingRegisterer{
 | 
			
		||||
		wrappedRegisterer: reg,
 | 
			
		||||
		labels:            labels,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WrapRegistererWithPrefix returns a Registerer wrapping the provided
 | 
			
		||||
// Registerer. Collectors registered with the returned Registerer will be
 | 
			
		||||
// registered with the wrapped Registerer in a modified way. The modified
 | 
			
		||||
// Collector adds the provided prefix to the name of all Metrics it collects.
 | 
			
		||||
//
 | 
			
		||||
// WrapRegistererWithPrefix is useful to have one place to prefix all metrics of
 | 
			
		||||
// a sub-system. To make this work, register metrics of the sub-system with the
 | 
			
		||||
// wrapping Registerer returned by WrapRegistererWithPrefix. It is rarely useful
 | 
			
		||||
// to use the same prefix for all metrics exposed. In particular, do not prefix
 | 
			
		||||
// metric names that are standardized across applications, as that would break
 | 
			
		||||
// horizontal monitoring, for example the metrics provided by the Go collector
 | 
			
		||||
// (see NewGoCollector) and the process collector (see NewProcessCollector). (In
 | 
			
		||||
// fact, those metrics are already prefixed with “go_” or “process_”,
 | 
			
		||||
// respectively.)
 | 
			
		||||
//
 | 
			
		||||
// Conflicts between Collectors registered through the original Registerer with
 | 
			
		||||
// Collectors registered through the wrapping Registerer will still be
 | 
			
		||||
// detected. Any AlreadyRegisteredError returned by the Register method of
 | 
			
		||||
// either Registerer will contain the ExistingCollector in the form it was
 | 
			
		||||
// provided to the respective registry.
 | 
			
		||||
func WrapRegistererWithPrefix(prefix string, reg Registerer) Registerer {
 | 
			
		||||
	return &wrappingRegisterer{
 | 
			
		||||
		wrappedRegisterer: reg,
 | 
			
		||||
		prefix:            prefix,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type wrappingRegisterer struct {
 | 
			
		||||
	wrappedRegisterer Registerer
 | 
			
		||||
	prefix            string
 | 
			
		||||
	labels            Labels
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *wrappingRegisterer) Register(c Collector) error {
 | 
			
		||||
	return r.wrappedRegisterer.Register(&wrappingCollector{
 | 
			
		||||
		wrappedCollector: c,
 | 
			
		||||
		prefix:           r.prefix,
 | 
			
		||||
		labels:           r.labels,
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *wrappingRegisterer) MustRegister(cs ...Collector) {
 | 
			
		||||
	for _, c := range cs {
 | 
			
		||||
		if err := r.Register(c); err != nil {
 | 
			
		||||
			panic(err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *wrappingRegisterer) Unregister(c Collector) bool {
 | 
			
		||||
	return r.wrappedRegisterer.Unregister(&wrappingCollector{
 | 
			
		||||
		wrappedCollector: c,
 | 
			
		||||
		prefix:           r.prefix,
 | 
			
		||||
		labels:           r.labels,
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type wrappingCollector struct {
 | 
			
		||||
	wrappedCollector Collector
 | 
			
		||||
	prefix           string
 | 
			
		||||
	labels           Labels
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *wrappingCollector) Collect(ch chan<- Metric) {
 | 
			
		||||
	wrappedCh := make(chan Metric)
 | 
			
		||||
	go func() {
 | 
			
		||||
		c.wrappedCollector.Collect(wrappedCh)
 | 
			
		||||
		close(wrappedCh)
 | 
			
		||||
	}()
 | 
			
		||||
	for m := range wrappedCh {
 | 
			
		||||
		ch <- &wrappingMetric{
 | 
			
		||||
			wrappedMetric: m,
 | 
			
		||||
			prefix:        c.prefix,
 | 
			
		||||
			labels:        c.labels,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *wrappingCollector) Describe(ch chan<- *Desc) {
 | 
			
		||||
	wrappedCh := make(chan *Desc)
 | 
			
		||||
	go func() {
 | 
			
		||||
		c.wrappedCollector.Describe(wrappedCh)
 | 
			
		||||
		close(wrappedCh)
 | 
			
		||||
	}()
 | 
			
		||||
	for desc := range wrappedCh {
 | 
			
		||||
		ch <- wrapDesc(desc, c.prefix, c.labels)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *wrappingCollector) unwrapRecursively() Collector {
 | 
			
		||||
	switch wc := c.wrappedCollector.(type) {
 | 
			
		||||
	case *wrappingCollector:
 | 
			
		||||
		return wc.unwrapRecursively()
 | 
			
		||||
	default:
 | 
			
		||||
		return wc
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type wrappingMetric struct {
 | 
			
		||||
	wrappedMetric Metric
 | 
			
		||||
	prefix        string
 | 
			
		||||
	labels        Labels
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *wrappingMetric) Desc() *Desc {
 | 
			
		||||
	return wrapDesc(m.wrappedMetric.Desc(), m.prefix, m.labels)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *wrappingMetric) Write(out *dto.Metric) error {
 | 
			
		||||
	if err := m.wrappedMetric.Write(out); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if len(m.labels) == 0 {
 | 
			
		||||
		// No wrapping labels.
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	for ln, lv := range m.labels {
 | 
			
		||||
		out.Label = append(out.Label, &dto.LabelPair{
 | 
			
		||||
			Name:  proto.String(ln),
 | 
			
		||||
			Value: proto.String(lv),
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	sort.Sort(labelPairSorter(out.Label))
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func wrapDesc(desc *Desc, prefix string, labels Labels) *Desc {
 | 
			
		||||
	constLabels := Labels{}
 | 
			
		||||
	for _, lp := range desc.constLabelPairs {
 | 
			
		||||
		constLabels[*lp.Name] = *lp.Value
 | 
			
		||||
	}
 | 
			
		||||
	for ln, lv := range labels {
 | 
			
		||||
		if _, alreadyUsed := constLabels[ln]; alreadyUsed {
 | 
			
		||||
			return &Desc{
 | 
			
		||||
				fqName:          desc.fqName,
 | 
			
		||||
				help:            desc.help,
 | 
			
		||||
				variableLabels:  desc.variableLabels,
 | 
			
		||||
				constLabelPairs: desc.constLabelPairs,
 | 
			
		||||
				err:             fmt.Errorf("attempted wrapping with already existing label name %q", ln),
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		constLabels[ln] = lv
 | 
			
		||||
	}
 | 
			
		||||
	// NewDesc will do remaining validations.
 | 
			
		||||
	newDesc := NewDesc(prefix+desc.fqName, desc.help, desc.variableLabels, constLabels)
 | 
			
		||||
	// Propagate errors if there was any. This will override any errer
 | 
			
		||||
	// created by NewDesc above, i.e. earlier errors get precedence.
 | 
			
		||||
	if desc.err != nil {
 | 
			
		||||
		newDesc.err = desc.err
 | 
			
		||||
	}
 | 
			
		||||
	return newDesc
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user