mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-26 12:27:06 +00:00 
			
		
		
		
	Prometheus endpoint (#5256)
* Add prometheus collector and route * dep ensure -add github.com/prometheus/client_golang/prometheus * dep ensure -update github.com/golang/protobuf * add metrics to reserved usernames * add comment head in metrics package * fix style imports * add metrics settings * add bearer token check * mapping metrics configs * fix lint * update config cheat sheet * update conf sample, typo fix
This commit is contained in:
		 Stanislav
					Stanislav
				
			
				
					committed by
					
						 techknowlogick
						techknowlogick
					
				
			
			
				
	
			
			
			 techknowlogick
						techknowlogick
					
				
			
						parent
						
							92fb89f6e1
						
					
				
				
					commit
					078c404c3b
				
			
							
								
								
									
										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 | ||||
							
								
								
									
										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 Collecter (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 | ||||
| } | ||||
							
								
								
									
										277
									
								
								vendor/github.com/prometheus/client_golang/prometheus/counter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										277
									
								
								vendor/github.com/prometheus/client_golang/prometheus/counter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,277 @@ | ||||
| // 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" | ||||
|  | ||||
| 	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) | ||||
| } | ||||
|  | ||||
| // 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 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} | ||||
| 	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 | ||||
| } | ||||
|  | ||||
| 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) 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) | ||||
|  | ||||
| 	return populateMetric(CounterValue, val, c.labelPairs, out) | ||||
| } | ||||
|  | ||||
| // 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(errInconsistentCardinality) | ||||
| 			} | ||||
| 			result := &counter{desc: desc, labelPairs: makeLabelPairs(desc, lvs)} | ||||
| 			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) | ||||
| } | ||||
							
								
								
									
										184
									
								
								vendor/github.com/prometheus/client_golang/prometheus/desc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								vendor/github.com/prometheus/client_golang/prometheus/desc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,184 @@ | ||||
| // 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/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", labelName) | ||||
| 			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", labelName) | ||||
| 			return d | ||||
| 		} | ||||
| 		labelNames = append(labelNames, "$"+labelName) | ||||
| 		labelNameSet[labelName] = struct{}{} | ||||
| 	} | ||||
| 	if len(labelNames) != len(labelNameSet) { | ||||
| 		d.err = errors.New("duplicate label names") | ||||
| 		return d | ||||
| 	} | ||||
|  | ||||
| 	vh := hashNew() | ||||
| 	for _, val := range labelValues { | ||||
| 		vh = hashAdd(vh, val) | ||||
| 		vh = hashAddByte(vh, separatorByte) | ||||
| 	} | ||||
| 	d.id = vh | ||||
| 	// 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. | ||||
| 	lh := hashNew() | ||||
| 	lh = hashAdd(lh, help) | ||||
| 	lh = hashAddByte(lh, separatorByte) | ||||
| 	for _, labelName := range labelNames { | ||||
| 		lh = hashAdd(lh, labelName) | ||||
| 		lh = hashAddByte(lh, separatorByte) | ||||
| 	} | ||||
| 	d.dimHash = lh | ||||
|  | ||||
| 	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, | ||||
| 	) | ||||
| } | ||||
							
								
								
									
										201
									
								
								vendor/github.com/prometheus/client_golang/prometheus/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								vendor/github.com/prometheus/client_golang/prometheus/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,201 @@ | ||||
| // 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/ | ||||
| // | ||||
| // A fifth "type" of metric is Untyped. It behaves like a Gauge, but signals the | ||||
| // Prometheus server not to assume anything about its type. | ||||
| // | ||||
| // In addition to the fundamental metric types Gauge, Counter, Summary, | ||||
| // Histogram, and Untyped, 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, | ||||
| // HistogramVec, and UntypedVec. | ||||
| // | ||||
| // 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, | ||||
| // Histogram, and Untyped are interfaces themselves while GaugeVec, CounterVec, | ||||
| // SummaryVec, HistogramVec, and UntypedVec are not. | ||||
| // | ||||
| // To create instances of Metrics and their vector versions, you need a suitable | ||||
| // …Opts struct, i.e. GaugeOpts, CounterOpts, SummaryOpts, HistogramOpts, or | ||||
| // UntypedOpts. | ||||
| // | ||||
| // 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). That will happen 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 marke the Collector “unchecked”.  No | ||||
| // checks are porformed 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 situatios 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. | ||||
| // (The top-level functions in the prometheus package are deprecated.) | ||||
| // | ||||
| // 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 | ||||
| } | ||||
							
								
								
									
										286
									
								
								vendor/github.com/prometheus/client_golang/prometheus/gauge.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										286
									
								
								vendor/github.com/prometheus/client_golang/prometheus/gauge.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,286 @@ | ||||
| // 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, 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(errInconsistentCardinality) | ||||
| 			} | ||||
| 			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. If that results in concurrent calls to Write, like in the case | ||||
| // where a GaugeFunc is directly registered with Prometheus, the provided | ||||
| // function must be concurrency-safe. | ||||
| 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) | ||||
| } | ||||
							
								
								
									
										301
									
								
								vendor/github.com/prometheus/client_golang/prometheus/go_collector.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										301
									
								
								vendor/github.com/prometheus/client_golang/prometheus/go_collector.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,301 @@ | ||||
| // 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" | ||||
| 	"runtime" | ||||
| 	"runtime/debug" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| type goCollector struct { | ||||
| 	goroutinesDesc *Desc | ||||
| 	threadsDesc    *Desc | ||||
| 	gcDesc         *Desc | ||||
| 	goInfoDesc     *Desc | ||||
|  | ||||
| 	// metrics to describe and collect | ||||
| 	metrics memStatsMetrics | ||||
| } | ||||
|  | ||||
| // NewGoCollector returns a collector which exports metrics about the current Go | ||||
| // process. This includes memory stats. To collect those, runtime.ReadMemStats | ||||
| // is called. This causes a stop-the-world, which is very short with Go1.9+ | ||||
| // (~25µs). 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). | ||||
| 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 GC invocation durations.", | ||||
| 			nil, nil), | ||||
| 		goInfoDesc: NewDesc( | ||||
| 			"go_info", | ||||
| 			"Information about the Go environment.", | ||||
| 			nil, Labels{"version": runtime.Version()}), | ||||
| 		metrics: 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 fmt.Sprintf("go_memstats_%s", 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.metrics { | ||||
| 		ch <- i.desc | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Collect returns the current state of all metrics of the collector. | ||||
| func (c *goCollector) Collect(ch chan<- Metric) { | ||||
| 	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) | ||||
|  | ||||
| 	ms := &runtime.MemStats{} | ||||
| 	runtime.ReadMemStats(ms) | ||||
| 	for _, i := range c.metrics { | ||||
| 		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 | ||||
| } | ||||
							
								
								
									
										614
									
								
								vendor/github.com/prometheus/client_golang/prometheus/histogram.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										614
									
								
								vendor/github.com/prometheus/client_golang/prometheus/histogram.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,614 @@ | ||||
| // 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" | ||||
|  | ||||
| 	"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. | ||||
| 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(errInconsistentCardinality) | ||||
| 	} | ||||
|  | ||||
| 	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{&histogramCounts{}, &histogramCounts{}}, | ||||
| 	} | ||||
| 	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 counts | ||||
| 	// for both states: | ||||
| 	h.counts[0].buckets = make([]uint64, len(h.upperBounds)) | ||||
| 	h.counts[1].buckets = make([]uint64, len(h.upperBounds)) | ||||
|  | ||||
| 	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 is a complicated one. For lock-free yet atomic | ||||
| 	// observations, we need to save the total count of observations again, | ||||
| 	// combined with the index of the currently-hot counts struct, so that | ||||
| 	// we can perform the operation on both values atomically. The least | ||||
| 	// significant bit defines the hot counts struct. The remaining 63 bits | ||||
| 	// represent the total count of observations. This happens under the | ||||
| 	// assumption that the 63bit count will never overflow. Rationale: An | ||||
| 	// observations takes about 30ns. Let's assume it could happen in | ||||
| 	// 10ns. Overflowing the counter will then take at least (2^63)*10ns, | ||||
| 	// which is about 3000 years. | ||||
| 	// | ||||
| 	// This has to be first in the struct for 64bit alignment. See | ||||
| 	// http://golang.org/pkg/sync/atomic/#pkg-note-BUG | ||||
| 	countAndHotIdx uint64 | ||||
|  | ||||
| 	selfCollector | ||||
| 	desc     *Desc | ||||
| 	writeMtx sync.Mutex // Only used in the Write method. | ||||
|  | ||||
| 	upperBounds []float64 | ||||
|  | ||||
| 	// 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 | ||||
| 	hotIdx int // Index of currently-hot counts. Only used within Write. | ||||
|  | ||||
| 	labelPairs []*dto.LabelPair | ||||
| } | ||||
|  | ||||
| func (h *histogram) Desc() *Desc { | ||||
| 	return h.desc | ||||
| } | ||||
|  | ||||
| func (h *histogram) Observe(v float64) { | ||||
| 	// 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 | ||||
| 	i := sort.SearchFloat64s(h.upperBounds, v) | ||||
|  | ||||
| 	// We increment h.countAndHotIdx by 2 so that the counter in the upper | ||||
| 	// 63 bits gets incremented by 1. 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, 2) | ||||
| 	hotCounts := h.counts[n%2] | ||||
|  | ||||
| 	if i < len(h.upperBounds) { | ||||
| 		atomic.AddUint64(&hotCounts.buckets[i], 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) | ||||
| } | ||||
|  | ||||
| func (h *histogram) Write(out *dto.Metric) error { | ||||
| 	var ( | ||||
| 		his                   = &dto.Histogram{} | ||||
| 		buckets               = make([]*dto.Bucket, len(h.upperBounds)) | ||||
| 		hotCounts, coldCounts *histogramCounts | ||||
| 		count                 uint64 | ||||
| 	) | ||||
|  | ||||
| 	// For simplicity, we mutex the rest of this method. 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. | ||||
| 	h.writeMtx.Lock() | ||||
| 	defer h.writeMtx.Unlock() | ||||
|  | ||||
| 	// This is a bit arcane, which is why the following spells out this if | ||||
| 	// clause in English: | ||||
| 	// | ||||
| 	// If the currently-hot counts struct is #0, we atomically increment | ||||
| 	// h.countAndHotIdx by 1 so that from now on Observe will use the counts | ||||
| 	// struct #1. Furthermore, the atomic increment gives us the new value, | ||||
| 	// which, in its most significant 63 bits, tells us the count of | ||||
| 	// observations done so far up to and including currently ongoing | ||||
| 	// observations still using the counts struct just changed from hot to | ||||
| 	// cold. To have a normal uint64 for the count, we bitshift by 1 and | ||||
| 	// save the result in count. We also set h.hotIdx to 1 for the next | ||||
| 	// Write call, and we will refer to counts #1 as hotCounts and to counts | ||||
| 	// #0 as coldCounts. | ||||
| 	// | ||||
| 	// If the currently-hot counts struct is #1, we do the corresponding | ||||
| 	// things the other way round. We have to _decrement_ h.countAndHotIdx | ||||
| 	// (which is a bit arcane in itself, as we have to express -1 with an | ||||
| 	// unsigned int...). | ||||
| 	if h.hotIdx == 0 { | ||||
| 		count = atomic.AddUint64(&h.countAndHotIdx, 1) >> 1 | ||||
| 		h.hotIdx = 1 | ||||
| 		hotCounts = h.counts[1] | ||||
| 		coldCounts = h.counts[0] | ||||
| 	} else { | ||||
| 		count = atomic.AddUint64(&h.countAndHotIdx, ^uint64(0)) >> 1 // Decrement. | ||||
| 		h.hotIdx = 0 | ||||
| 		hotCounts = h.counts[0] | ||||
| 		coldCounts = h.counts[1] | ||||
| 	} | ||||
|  | ||||
| 	// Now we have to wait for the now-declared-cold counts to actually cool | ||||
| 	// down, i.e. wait for all observations still using it to finish. That's | ||||
| 	// the case once the count in the cold counts struct is the same as the | ||||
| 	// one atomically retrieved from the upper 63bits of h.countAndHotIdx. | ||||
| 	for { | ||||
| 		if count == atomic.LoadUint64(&coldCounts.count) { | ||||
| 			break | ||||
| 		} | ||||
| 		runtime.Gosched() // Let observations get work done. | ||||
| 	} | ||||
|  | ||||
| 	his.SampleCount = proto.Uint64(count) | ||||
| 	his.SampleSum = proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.sumBits))) | ||||
| 	var cumCount uint64 | ||||
| 	for i, upperBound := range h.upperBounds { | ||||
| 		cumCount += atomic.LoadUint64(&coldCounts.buckets[i]) | ||||
| 		buckets[i] = &dto.Bucket{ | ||||
| 			CumulativeCount: proto.Uint64(cumCount), | ||||
| 			UpperBound:      proto.Float64(upperBound), | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	his.Bucket = buckets | ||||
| 	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 | ||||
| } | ||||
|  | ||||
| // 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() | ||||
| } | ||||
							
								
								
									
										505
									
								
								vendor/github.com/prometheus/client_golang/prometheus/http.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										505
									
								
								vendor/github.com/prometheus/client_golang/prometheus/http.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,505 @@ | ||||
| // 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 ( | ||||
| 	"bufio" | ||||
| 	"bytes" | ||||
| 	"compress/gzip" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/prometheus/common/expfmt" | ||||
| ) | ||||
|  | ||||
| // TODO(beorn7): Remove this whole file. It is a partial mirror of | ||||
| // promhttp/http.go (to avoid circular import chains) where everything HTTP | ||||
| // related should live. The functions here are just for avoiding | ||||
| // breakage. Everything is deprecated. | ||||
|  | ||||
| const ( | ||||
| 	contentTypeHeader     = "Content-Type" | ||||
| 	contentLengthHeader   = "Content-Length" | ||||
| 	contentEncodingHeader = "Content-Encoding" | ||||
| 	acceptEncodingHeader  = "Accept-Encoding" | ||||
| ) | ||||
|  | ||||
| var bufPool sync.Pool | ||||
|  | ||||
| func getBuf() *bytes.Buffer { | ||||
| 	buf := bufPool.Get() | ||||
| 	if buf == nil { | ||||
| 		return &bytes.Buffer{} | ||||
| 	} | ||||
| 	return buf.(*bytes.Buffer) | ||||
| } | ||||
|  | ||||
| func giveBuf(buf *bytes.Buffer) { | ||||
| 	buf.Reset() | ||||
| 	bufPool.Put(buf) | ||||
| } | ||||
|  | ||||
| // Handler returns an HTTP handler for the DefaultGatherer. It is | ||||
| // already instrumented with InstrumentHandler (using "prometheus" as handler | ||||
| // name). | ||||
| // | ||||
| // Deprecated: Please note the issues described in the doc comment of | ||||
| // InstrumentHandler. You might want to consider using promhttp.Handler instead. | ||||
| func Handler() http.Handler { | ||||
| 	return InstrumentHandler("prometheus", UninstrumentedHandler()) | ||||
| } | ||||
|  | ||||
| // UninstrumentedHandler returns an HTTP handler for the DefaultGatherer. | ||||
| // | ||||
| // Deprecated: Use promhttp.HandlerFor(DefaultGatherer, promhttp.HandlerOpts{}) | ||||
| // instead. See there for further documentation. | ||||
| func UninstrumentedHandler() http.Handler { | ||||
| 	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | ||||
| 		mfs, err := DefaultGatherer.Gather() | ||||
| 		if err != nil { | ||||
| 			http.Error(w, "An error has occurred during metrics collection:\n\n"+err.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		contentType := expfmt.Negotiate(req.Header) | ||||
| 		buf := getBuf() | ||||
| 		defer giveBuf(buf) | ||||
| 		writer, encoding := decorateWriter(req, buf) | ||||
| 		enc := expfmt.NewEncoder(writer, contentType) | ||||
| 		var lastErr error | ||||
| 		for _, mf := range mfs { | ||||
| 			if err := enc.Encode(mf); err != nil { | ||||
| 				lastErr = err | ||||
| 				http.Error(w, "An error has occurred during metrics encoding:\n\n"+err.Error(), http.StatusInternalServerError) | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 		if closer, ok := writer.(io.Closer); ok { | ||||
| 			closer.Close() | ||||
| 		} | ||||
| 		if lastErr != nil && buf.Len() == 0 { | ||||
| 			http.Error(w, "No metrics encoded, last error:\n\n"+lastErr.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
| 		header := w.Header() | ||||
| 		header.Set(contentTypeHeader, string(contentType)) | ||||
| 		header.Set(contentLengthHeader, fmt.Sprint(buf.Len())) | ||||
| 		if encoding != "" { | ||||
| 			header.Set(contentEncodingHeader, encoding) | ||||
| 		} | ||||
| 		w.Write(buf.Bytes()) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // decorateWriter wraps a writer to handle gzip compression if requested.  It | ||||
| // returns the decorated writer and the appropriate "Content-Encoding" header | ||||
| // (which is empty if no compression is enabled). | ||||
| func decorateWriter(request *http.Request, writer io.Writer) (io.Writer, string) { | ||||
| 	header := request.Header.Get(acceptEncodingHeader) | ||||
| 	parts := strings.Split(header, ",") | ||||
| 	for _, part := range parts { | ||||
| 		part = strings.TrimSpace(part) | ||||
| 		if part == "gzip" || strings.HasPrefix(part, "gzip;") { | ||||
| 			return gzip.NewWriter(writer), "gzip" | ||||
| 		} | ||||
| 	} | ||||
| 	return writer, "" | ||||
| } | ||||
|  | ||||
| var instLabels = []string{"method", "code"} | ||||
|  | ||||
| type nower interface { | ||||
| 	Now() time.Time | ||||
| } | ||||
|  | ||||
| type nowFunc func() time.Time | ||||
|  | ||||
| func (n nowFunc) Now() time.Time { | ||||
| 	return n() | ||||
| } | ||||
|  | ||||
| var now nower = nowFunc(func() time.Time { | ||||
| 	return time.Now() | ||||
| }) | ||||
|  | ||||
| // InstrumentHandler wraps the given HTTP handler for instrumentation. It | ||||
| // registers four metric collectors (if not already done) and reports HTTP | ||||
| // metrics to the (newly or already) registered collectors: http_requests_total | ||||
| // (CounterVec), http_request_duration_microseconds (Summary), | ||||
| // http_request_size_bytes (Summary), http_response_size_bytes (Summary). Each | ||||
| // has a constant label named "handler" with the provided handlerName as | ||||
| // value. http_requests_total is a metric vector partitioned by HTTP method | ||||
| // (label name "method") and HTTP status code (label name "code"). | ||||
| // | ||||
| // Deprecated: InstrumentHandler has several issues. Use the tooling provided in | ||||
| // package promhttp instead. The issues are the following: (1) It uses Summaries | ||||
| // rather than Histograms. Summaries are not useful if aggregation across | ||||
| // multiple instances is required. (2) It uses microseconds as unit, which is | ||||
| // deprecated and should be replaced by seconds. (3) The size of the request is | ||||
| // calculated in a separate goroutine. Since this calculator requires access to | ||||
| // the request header, it creates a race with any writes to the header performed | ||||
| // during request handling.  httputil.ReverseProxy is a prominent example for a | ||||
| // handler performing such writes. (4) It has additional issues with HTTP/2, cf. | ||||
| // https://github.com/prometheus/client_golang/issues/272. | ||||
| func InstrumentHandler(handlerName string, handler http.Handler) http.HandlerFunc { | ||||
| 	return InstrumentHandlerFunc(handlerName, handler.ServeHTTP) | ||||
| } | ||||
|  | ||||
| // InstrumentHandlerFunc wraps the given function for instrumentation. It | ||||
| // otherwise works in the same way as InstrumentHandler (and shares the same | ||||
| // issues). | ||||
| // | ||||
| // Deprecated: InstrumentHandlerFunc is deprecated for the same reasons as | ||||
| // InstrumentHandler is. Use the tooling provided in package promhttp instead. | ||||
| func InstrumentHandlerFunc(handlerName string, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc { | ||||
| 	return InstrumentHandlerFuncWithOpts( | ||||
| 		SummaryOpts{ | ||||
| 			Subsystem:   "http", | ||||
| 			ConstLabels: Labels{"handler": handlerName}, | ||||
| 			Objectives:  map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, | ||||
| 		}, | ||||
| 		handlerFunc, | ||||
| 	) | ||||
| } | ||||
|  | ||||
| // InstrumentHandlerWithOpts works like InstrumentHandler (and shares the same | ||||
| // issues) but provides more flexibility (at the cost of a more complex call | ||||
| // syntax). As InstrumentHandler, this function registers four metric | ||||
| // collectors, but it uses the provided SummaryOpts to create them. However, the | ||||
| // fields "Name" and "Help" in the SummaryOpts are ignored. "Name" is replaced | ||||
| // by "requests_total", "request_duration_microseconds", "request_size_bytes", | ||||
| // and "response_size_bytes", respectively. "Help" is replaced by an appropriate | ||||
| // help string. The names of the variable labels of the http_requests_total | ||||
| // CounterVec are "method" (get, post, etc.), and "code" (HTTP status code). | ||||
| // | ||||
| // If InstrumentHandlerWithOpts is called as follows, it mimics exactly the | ||||
| // behavior of InstrumentHandler: | ||||
| // | ||||
| //     prometheus.InstrumentHandlerWithOpts( | ||||
| //         prometheus.SummaryOpts{ | ||||
| //              Subsystem:   "http", | ||||
| //              ConstLabels: prometheus.Labels{"handler": handlerName}, | ||||
| //         }, | ||||
| //         handler, | ||||
| //     ) | ||||
| // | ||||
| // Technical detail: "requests_total" is a CounterVec, not a SummaryVec, so it | ||||
| // cannot use SummaryOpts. Instead, a CounterOpts struct is created internally, | ||||
| // and all its fields are set to the equally named fields in the provided | ||||
| // SummaryOpts. | ||||
| // | ||||
| // Deprecated: InstrumentHandlerWithOpts is deprecated for the same reasons as | ||||
| // InstrumentHandler is. Use the tooling provided in package promhttp instead. | ||||
| func InstrumentHandlerWithOpts(opts SummaryOpts, handler http.Handler) http.HandlerFunc { | ||||
| 	return InstrumentHandlerFuncWithOpts(opts, handler.ServeHTTP) | ||||
| } | ||||
|  | ||||
| // InstrumentHandlerFuncWithOpts works like InstrumentHandlerFunc (and shares | ||||
| // the same issues) but provides more flexibility (at the cost of a more complex | ||||
| // call syntax). See InstrumentHandlerWithOpts for details how the provided | ||||
| // SummaryOpts are used. | ||||
| // | ||||
| // Deprecated: InstrumentHandlerFuncWithOpts is deprecated for the same reasons | ||||
| // as InstrumentHandler is. Use the tooling provided in package promhttp instead. | ||||
| func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc { | ||||
| 	reqCnt := NewCounterVec( | ||||
| 		CounterOpts{ | ||||
| 			Namespace:   opts.Namespace, | ||||
| 			Subsystem:   opts.Subsystem, | ||||
| 			Name:        "requests_total", | ||||
| 			Help:        "Total number of HTTP requests made.", | ||||
| 			ConstLabels: opts.ConstLabels, | ||||
| 		}, | ||||
| 		instLabels, | ||||
| 	) | ||||
| 	if err := Register(reqCnt); err != nil { | ||||
| 		if are, ok := err.(AlreadyRegisteredError); ok { | ||||
| 			reqCnt = are.ExistingCollector.(*CounterVec) | ||||
| 		} else { | ||||
| 			panic(err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	opts.Name = "request_duration_microseconds" | ||||
| 	opts.Help = "The HTTP request latencies in microseconds." | ||||
| 	reqDur := NewSummary(opts) | ||||
| 	if err := Register(reqDur); err != nil { | ||||
| 		if are, ok := err.(AlreadyRegisteredError); ok { | ||||
| 			reqDur = are.ExistingCollector.(Summary) | ||||
| 		} else { | ||||
| 			panic(err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	opts.Name = "request_size_bytes" | ||||
| 	opts.Help = "The HTTP request sizes in bytes." | ||||
| 	reqSz := NewSummary(opts) | ||||
| 	if err := Register(reqSz); err != nil { | ||||
| 		if are, ok := err.(AlreadyRegisteredError); ok { | ||||
| 			reqSz = are.ExistingCollector.(Summary) | ||||
| 		} else { | ||||
| 			panic(err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	opts.Name = "response_size_bytes" | ||||
| 	opts.Help = "The HTTP response sizes in bytes." | ||||
| 	resSz := NewSummary(opts) | ||||
| 	if err := Register(resSz); err != nil { | ||||
| 		if are, ok := err.(AlreadyRegisteredError); ok { | ||||
| 			resSz = are.ExistingCollector.(Summary) | ||||
| 		} else { | ||||
| 			panic(err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 		now := time.Now() | ||||
|  | ||||
| 		delegate := &responseWriterDelegator{ResponseWriter: w} | ||||
| 		out := computeApproximateRequestSize(r) | ||||
|  | ||||
| 		_, cn := w.(http.CloseNotifier) | ||||
| 		_, fl := w.(http.Flusher) | ||||
| 		_, hj := w.(http.Hijacker) | ||||
| 		_, rf := w.(io.ReaderFrom) | ||||
| 		var rw http.ResponseWriter | ||||
| 		if cn && fl && hj && rf { | ||||
| 			rw = &fancyResponseWriterDelegator{delegate} | ||||
| 		} else { | ||||
| 			rw = delegate | ||||
| 		} | ||||
| 		handlerFunc(rw, r) | ||||
|  | ||||
| 		elapsed := float64(time.Since(now)) / float64(time.Microsecond) | ||||
|  | ||||
| 		method := sanitizeMethod(r.Method) | ||||
| 		code := sanitizeCode(delegate.status) | ||||
| 		reqCnt.WithLabelValues(method, code).Inc() | ||||
| 		reqDur.Observe(elapsed) | ||||
| 		resSz.Observe(float64(delegate.written)) | ||||
| 		reqSz.Observe(float64(<-out)) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func computeApproximateRequestSize(r *http.Request) <-chan int { | ||||
| 	// Get URL length in current goroutine for avoiding a race condition. | ||||
| 	// HandlerFunc that runs in parallel may modify the URL. | ||||
| 	s := 0 | ||||
| 	if r.URL != nil { | ||||
| 		s += len(r.URL.String()) | ||||
| 	} | ||||
|  | ||||
| 	out := make(chan int, 1) | ||||
|  | ||||
| 	go func() { | ||||
| 		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) | ||||
| 		} | ||||
| 		out <- s | ||||
| 		close(out) | ||||
| 	}() | ||||
|  | ||||
| 	return out | ||||
| } | ||||
|  | ||||
| type responseWriterDelegator struct { | ||||
| 	http.ResponseWriter | ||||
|  | ||||
| 	status      int | ||||
| 	written     int64 | ||||
| 	wroteHeader bool | ||||
| } | ||||
|  | ||||
| func (r *responseWriterDelegator) WriteHeader(code int) { | ||||
| 	r.status = code | ||||
| 	r.wroteHeader = true | ||||
| 	r.ResponseWriter.WriteHeader(code) | ||||
| } | ||||
|  | ||||
| func (r *responseWriterDelegator) Write(b []byte) (int, error) { | ||||
| 	if !r.wroteHeader { | ||||
| 		r.WriteHeader(http.StatusOK) | ||||
| 	} | ||||
| 	n, err := r.ResponseWriter.Write(b) | ||||
| 	r.written += int64(n) | ||||
| 	return n, err | ||||
| } | ||||
|  | ||||
| type fancyResponseWriterDelegator struct { | ||||
| 	*responseWriterDelegator | ||||
| } | ||||
|  | ||||
| func (f *fancyResponseWriterDelegator) CloseNotify() <-chan bool { | ||||
| 	return f.ResponseWriter.(http.CloseNotifier).CloseNotify() | ||||
| } | ||||
|  | ||||
| func (f *fancyResponseWriterDelegator) Flush() { | ||||
| 	f.ResponseWriter.(http.Flusher).Flush() | ||||
| } | ||||
|  | ||||
| func (f *fancyResponseWriterDelegator) Hijack() (net.Conn, *bufio.ReadWriter, error) { | ||||
| 	return f.ResponseWriter.(http.Hijacker).Hijack() | ||||
| } | ||||
|  | ||||
| func (f *fancyResponseWriterDelegator) ReadFrom(r io.Reader) (int64, error) { | ||||
| 	if !f.wroteHeader { | ||||
| 		f.WriteHeader(http.StatusOK) | ||||
| 	} | ||||
| 	n, err := f.ResponseWriter.(io.ReaderFrom).ReadFrom(r) | ||||
| 	f.written += n | ||||
| 	return n, err | ||||
| } | ||||
|  | ||||
| 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) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func sanitizeCode(s int) string { | ||||
| 	switch s { | ||||
| 	case 100: | ||||
| 		return "100" | ||||
| 	case 101: | ||||
| 		return "101" | ||||
|  | ||||
| 	case 200: | ||||
| 		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) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										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 | ||||
| } | ||||
							
								
								
									
										70
									
								
								vendor/github.com/prometheus/client_golang/prometheus/labels.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								vendor/github.com/prometheus/client_golang/prometheus/labels.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| // 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 validateValuesInLabels(labels Labels, expectedNumberOfValues int) error { | ||||
| 	if len(labels) != expectedNumberOfValues { | ||||
| 		return errInconsistentCardinality | ||||
| 	} | ||||
|  | ||||
| 	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 errInconsistentCardinality | ||||
| 	} | ||||
|  | ||||
| 	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) | ||||
| } | ||||
							
								
								
									
										174
									
								
								vendor/github.com/prometheus/client_golang/prometheus/metric.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								vendor/github.com/prometheus/client_golang/prometheus/metric.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,174 @@ | ||||
| // 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" | ||||
|  | ||||
| 	dto "github.com/prometheus/client_model/go" | ||||
| ) | ||||
|  | ||||
| const separatorByte byte = 255 | ||||
|  | ||||
| // 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} | ||||
| } | ||||
							
								
								
									
										52
									
								
								vendor/github.com/prometheus/client_golang/prometheus/observer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								vendor/github.com/prometheus/client_golang/prometheus/observer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| // 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 | ||||
| } | ||||
							
								
								
									
										204
									
								
								vendor/github.com/prometheus/client_golang/prometheus/process_collector.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								vendor/github.com/prometheus/client_golang/prometheus/process_collector.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,204 @@ | ||||
| // 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" | ||||
|  | ||||
| 	"github.com/prometheus/procfs" | ||||
| ) | ||||
|  | ||||
| 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. | ||||
| // | ||||
| // Currently, the collector depends on a Linux-style proc filesystem and | ||||
| // therefore only exports metrics for Linux. | ||||
| // | ||||
| // Note: An older version of this function had the following signature: | ||||
| // | ||||
| //     NewProcessCollector(pid int, namespace string) Collector | ||||
| // | ||||
| // Most commonly, it was called as | ||||
| // | ||||
| //     NewProcessCollector(os.Getpid(), "") | ||||
| // | ||||
| // The following call of the current version is equivalent to the above: | ||||
| // | ||||
| //     NewProcessCollector(ProcessCollectorOpts{}) | ||||
| 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 _, err := procfs.NewStat(); err == nil { | ||||
| 		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) 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.NewStat(); 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.NewLimits(); err == nil { | ||||
| 		ch <- MustNewConstMetric(c.maxFDs, GaugeValue, float64(limits.OpenFiles)) | ||||
| 		ch <- MustNewConstMetric(c.maxVsize, GaugeValue, float64(limits.AddressSpace)) | ||||
| 	} else { | ||||
| 		c.reportError(ch, nil, err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 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) | ||||
| } | ||||
							
								
								
									
										199
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										199
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,199 @@ | ||||
| // 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 | ||||
|  | ||||
| 	handler, method    string | ||||
| 	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 !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 } | ||||
|  | ||||
| func (d closeNotifierDelegator) CloseNotify() <-chan bool { | ||||
| 	return d.ResponseWriter.(http.CloseNotifier).CloseNotify() | ||||
| } | ||||
| func (d flusherDelegator) Flush() { | ||||
| 	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 !d.wroteHeader { | ||||
| 		d.WriteHeader(http.StatusOK) | ||||
| 	} | ||||
| 	n, err := d.ResponseWriter.(io.ReaderFrom).ReadFrom(re) | ||||
| 	d.written += n | ||||
| 	return n, err | ||||
| } | ||||
|  | ||||
| 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}} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										181
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_1_8.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_1_8.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,181 @@ | ||||
| // 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. | ||||
|  | ||||
| // +build go1.8 | ||||
|  | ||||
| package promhttp | ||||
|  | ||||
| import ( | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| ) | ||||
|  | ||||
| type pusherDelegator struct{ *responseWriterDelegator } | ||||
|  | ||||
| func (d pusherDelegator) Push(target string, opts *http.PushOptions) error { | ||||
| 	return d.ResponseWriter.(http.Pusher).Push(target, opts) | ||||
| } | ||||
|  | ||||
| func init() { | ||||
| 	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 | ||||
| 	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) | ||||
| } | ||||
							
								
								
									
										44
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_pre_1_8.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_pre_1_8.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| // 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. | ||||
|  | ||||
| // +build !go1.8 | ||||
|  | ||||
| package promhttp | ||||
|  | ||||
| import ( | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| ) | ||||
|  | ||||
| func newDelegator(w http.ResponseWriter, observeWriteHeaderFunc func(int)) delegator { | ||||
| 	d := &responseWriterDelegator{ | ||||
| 		ResponseWriter:     w, | ||||
| 		observeWriteHeader: observeWriteHeaderFunc, | ||||
| 	} | ||||
|  | ||||
| 	id := 0 | ||||
| 	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 | ||||
| 	} | ||||
|  | ||||
| 	return pickDelegator[id](d) | ||||
| } | ||||
							
								
								
									
										311
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										311
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,311 @@ | ||||
| // 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 ( | ||||
| 	"bytes" | ||||
| 	"compress/gzip" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/prometheus/common/expfmt" | ||||
|  | ||||
| 	"github.com/prometheus/client_golang/prometheus" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	contentTypeHeader     = "Content-Type" | ||||
| 	contentLengthHeader   = "Content-Length" | ||||
| 	contentEncodingHeader = "Content-Encoding" | ||||
| 	acceptEncodingHeader  = "Accept-Encoding" | ||||
| ) | ||||
|  | ||||
| var bufPool sync.Pool | ||||
|  | ||||
| func getBuf() *bytes.Buffer { | ||||
| 	buf := bufPool.Get() | ||||
| 	if buf == nil { | ||||
| 		return &bytes.Buffer{} | ||||
| 	} | ||||
| 	return buf.(*bytes.Buffer) | ||||
| } | ||||
|  | ||||
| func giveBuf(buf *bytes.Buffer) { | ||||
| 	buf.Reset() | ||||
| 	bufPool.Put(buf) | ||||
| } | ||||
|  | ||||
| // 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{} | ||||
| 	if opts.MaxRequestsInFlight > 0 { | ||||
| 		inFlightSem = make(chan struct{}, opts.MaxRequestsInFlight) | ||||
| 	} | ||||
|  | ||||
| 	h := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | ||||
| 		if inFlightSem != nil { | ||||
| 			select { | ||||
| 			case inFlightSem <- struct{}{}: // All good, carry on. | ||||
| 				defer func() { <-inFlightSem }() | ||||
| 			default: | ||||
| 				http.Error(w, 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) | ||||
| 			} | ||||
| 			switch opts.ErrorHandling { | ||||
| 			case PanicOnError: | ||||
| 				panic(err) | ||||
| 			case ContinueOnError: | ||||
| 				if len(mfs) == 0 { | ||||
| 					http.Error(w, "No metrics gathered, last error:\n\n"+err.Error(), http.StatusInternalServerError) | ||||
| 					return | ||||
| 				} | ||||
| 			case HTTPErrorOnError: | ||||
| 				http.Error(w, "An error has occurred during metrics gathering:\n\n"+err.Error(), http.StatusInternalServerError) | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		contentType := expfmt.Negotiate(req.Header) | ||||
| 		buf := getBuf() | ||||
| 		defer giveBuf(buf) | ||||
| 		writer, encoding := decorateWriter(req, buf, opts.DisableCompression) | ||||
| 		enc := expfmt.NewEncoder(writer, contentType) | ||||
| 		var lastErr error | ||||
| 		for _, mf := range mfs { | ||||
| 			if err := enc.Encode(mf); err != nil { | ||||
| 				lastErr = err | ||||
| 				if opts.ErrorLog != nil { | ||||
| 					opts.ErrorLog.Println("error encoding metric family:", err) | ||||
| 				} | ||||
| 				switch opts.ErrorHandling { | ||||
| 				case PanicOnError: | ||||
| 					panic(err) | ||||
| 				case ContinueOnError: | ||||
| 					// Handled later. | ||||
| 				case HTTPErrorOnError: | ||||
| 					http.Error(w, "An error has occurred during metrics encoding:\n\n"+err.Error(), http.StatusInternalServerError) | ||||
| 					return | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		if closer, ok := writer.(io.Closer); ok { | ||||
| 			closer.Close() | ||||
| 		} | ||||
| 		if lastErr != nil && buf.Len() == 0 { | ||||
| 			http.Error(w, "No metrics encoded, last error:\n\n"+lastErr.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
| 		header := w.Header() | ||||
| 		header.Set(contentTypeHeader, string(contentType)) | ||||
| 		header.Set(contentLengthHeader, fmt.Sprint(buf.Len())) | ||||
| 		if encoding != "" { | ||||
| 			header.Set(contentEncodingHeader, encoding) | ||||
| 		} | ||||
| 		if _, err := w.Write(buf.Bytes()); err != nil && opts.ErrorLog != nil { | ||||
| 			opts.ErrorLog.Println("error while sending encoded metrics:", err) | ||||
| 		} | ||||
| 		// TODO(beorn7): Consider streaming serving of metrics. | ||||
| 	}) | ||||
|  | ||||
| 	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. It is recommended to at least | ||||
| 	// log errors (by providing an ErrorLog in HandlerOpts) to not mask | ||||
| 	// errors completely. | ||||
| 	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 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 | ||||
| } | ||||
|  | ||||
| // decorateWriter wraps a writer to handle gzip compression if requested.  It | ||||
| // returns the decorated writer and the appropriate "Content-Encoding" header | ||||
| // (which is empty if no compression is enabled). | ||||
| func decorateWriter(request *http.Request, writer io.Writer, compressionDisabled bool) (io.Writer, string) { | ||||
| 	if compressionDisabled { | ||||
| 		return writer, "" | ||||
| 	} | ||||
| 	header := request.Header.Get(acceptEncodingHeader) | ||||
| 	parts := strings.Split(header, ",") | ||||
| 	for _, part := range parts { | ||||
| 		part = strings.TrimSpace(part) | ||||
| 		if part == "gzip" || strings.HasPrefix(part, "gzip;") { | ||||
| 			return gzip.NewWriter(writer), "gzip" | ||||
| 		} | ||||
| 	} | ||||
| 	return writer, "" | ||||
| } | ||||
							
								
								
									
										97
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,97 @@ | ||||
| // 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 ( | ||||
| 	"net/http" | ||||
| 	"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 | ||||
| 	}) | ||||
| } | ||||
							
								
								
									
										144
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client_1_8.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client_1_8.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,144 @@ | ||||
| // 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. | ||||
|  | ||||
| // +build go1.8 | ||||
|  | ||||
| package promhttp | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"crypto/tls" | ||||
| 	"net/http" | ||||
| 	"net/http/httptrace" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| // 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(context.Background(), 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) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										895
									
								
								vendor/github.com/prometheus/client_golang/prometheus/registry.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										895
									
								
								vendor/github.com/prometheus/client_golang/prometheus/registry.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,895 @@ | ||||
| // 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" | ||||
| 	"runtime" | ||||
| 	"sort" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"unicode/utf8" | ||||
|  | ||||
| 	"github.com/golang/protobuf/proto" | ||||
|  | ||||
| 	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 methed 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 // Just a sum of all desc IDs. | ||||
| 		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, add 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 { | ||||
| 		return AlreadyRegisteredError{ | ||||
| 			ExistingCollector: existing, | ||||
| 			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 // Just a sum of the desc IDs. | ||||
| 	) | ||||
| 	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() | ||||
| } | ||||
|  | ||||
| // 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 calles 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 := hashNew() | ||||
| 	h = hashAdd(h, name) | ||||
| 	h = hashAddByte(h, separatorByte) | ||||
| 	// Make sure label pairs are sorted. We depend on it for the consistency | ||||
| 	// check. | ||||
| 	sort.Sort(labelPairSorter(dtoMetric.Label)) | ||||
| 	for _, lp := range dtoMetric.Label { | ||||
| 		h = hashAdd(h, lp.GetName()) | ||||
| 		h = hashAddByte(h, separatorByte) | ||||
| 		h = hashAdd(h, lp.GetValue()) | ||||
| 		h = hashAddByte(h, separatorByte) | ||||
| 	} | ||||
| 	if _, exists := metricHashes[h]; exists { | ||||
| 		return fmt.Errorf( | ||||
| 			"collected metric %q { %s} was collected before with the same name and label values", | ||||
| 			name, dtoMetric, | ||||
| 		) | ||||
| 	} | ||||
| 	metricHashes[h] = 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, 0, len(dtoMetric.Label)) | ||||
| 	lpsFromDesc = append(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 | ||||
| } | ||||
							
								
								
									
										626
									
								
								vendor/github.com/prometheus/client_golang/prometheus/summary.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										626
									
								
								vendor/github.com/prometheus/client_golang/prometheus/summary.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,626 @@ | ||||
| // 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" | ||||
| 	"sort" | ||||
| 	"sync" | ||||
| 	"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 v0.10 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) | ||||
| } | ||||
|  | ||||
| // DefObjectives are the default Summary quantile values. | ||||
| // | ||||
| // Deprecated: DefObjectives will not be used as the default objectives in | ||||
| // v0.10 of the library. The default Summary will have no quantiles then. | ||||
| var ( | ||||
| 	DefObjectives = map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001} | ||||
|  | ||||
| 	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 v0.10 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 DefObjectives. It is used if Objectives is left at | ||||
| 	// its zero value (i.e. nil). To create a Summary without Objectives, | ||||
| 	// set it to an empty map (i.e. map[float64]float64{}). | ||||
| 	// | ||||
| 	// Deprecated: Note that the current value of DefObjectives is | ||||
| 	// deprecated. It will be replaced by an empty map in v0.10 of the | ||||
| 	// library. Please explicitly set Objectives to the desired value. | ||||
| 	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 | ||||
| } | ||||
|  | ||||
| // Great fuck-up 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(errInconsistentCardinality) | ||||
| 	} | ||||
|  | ||||
| 	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 = DefObjectives | ||||
| 	} | ||||
|  | ||||
| 	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 | ||||
| 	} | ||||
|  | ||||
| 	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 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 | ||||
| } | ||||
							
								
								
									
										51
									
								
								vendor/github.com/prometheus/client_golang/prometheus/timer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								vendor/github.com/prometheus/client_golang/prometheus/timer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| // 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. 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() { | ||||
| 	if t.observer != nil { | ||||
| 		t.observer.Observe(time.Since(t.begin).Seconds()) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										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) | ||||
| } | ||||
							
								
								
									
										162
									
								
								vendor/github.com/prometheus/client_golang/prometheus/value.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								vendor/github.com/prometheus/client_golang/prometheus/value.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,162 @@ | ||||
| // 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" | ||||
|  | ||||
| 	"github.com/golang/protobuf/proto" | ||||
|  | ||||
| 	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. | ||||
| 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, 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, out) | ||||
| } | ||||
|  | ||||
| func populateMetric( | ||||
| 	t ValueType, | ||||
| 	v float64, | ||||
| 	labelPairs []*dto.LabelPair, | ||||
| 	m *dto.Metric, | ||||
| ) error { | ||||
| 	m.Label = labelPairs | ||||
| 	switch t { | ||||
| 	case CounterValue: | ||||
| 		m.Counter = &dto.Counter{Value: proto.Float64(v)} | ||||
| 	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 | ||||
| } | ||||
							
								
								
									
										472
									
								
								vendor/github.com/prometheus/client_golang/prometheus/vec.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										472
									
								
								vendor/github.com/prometheus/client_golang/prometheus/vec.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,472 @@ | ||||
| // 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. It uses basicMetricVec internally. | ||||
| 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) | ||||
| } | ||||
|  | ||||
| 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 | ||||
| } | ||||
							
								
								
									
										179
									
								
								vendor/github.com/prometheus/client_golang/prometheus/wrap.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								vendor/github.com/prometheus/client_golang/prometheus/wrap.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,179 @@ | ||||
| // 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. | ||||
| // | ||||
| // 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.) | ||||
| 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) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 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