You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

429 lines
11 KiB

  1. // Copyright 2015 The Prometheus Authors
  2. // Licensed under the Apache License, Version 2.0 (the "License");
  3. // you may not use this file except in compliance with the License.
  4. // You may obtain a copy of the License at
  5. //
  6. // http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. // Unless required by applicable law or agreed to in writing, software
  9. // distributed under the License is distributed on an "AS IS" BASIS,
  10. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. // See the License for the specific language governing permissions and
  12. // limitations under the License.
  13. package expfmt
  14. import (
  15. "fmt"
  16. "io"
  17. "math"
  18. "mime"
  19. "net/http"
  20. dto "github.com/prometheus/client_model/go"
  21. "github.com/matttproud/golang_protobuf_extensions/pbutil"
  22. "github.com/prometheus/common/model"
  23. )
  24. // Decoder types decode an input stream into metric families.
  25. type Decoder interface {
  26. Decode(*dto.MetricFamily) error
  27. }
  28. // DecodeOptions contains options used by the Decoder and in sample extraction.
  29. type DecodeOptions struct {
  30. // Timestamp is added to each value from the stream that has no explicit timestamp set.
  31. Timestamp model.Time
  32. }
  33. // ResponseFormat extracts the correct format from a HTTP response header.
  34. // If no matching format can be found FormatUnknown is returned.
  35. func ResponseFormat(h http.Header) Format {
  36. ct := h.Get(hdrContentType)
  37. mediatype, params, err := mime.ParseMediaType(ct)
  38. if err != nil {
  39. return FmtUnknown
  40. }
  41. const textType = "text/plain"
  42. switch mediatype {
  43. case ProtoType:
  44. if p, ok := params["proto"]; ok && p != ProtoProtocol {
  45. return FmtUnknown
  46. }
  47. if e, ok := params["encoding"]; ok && e != "delimited" {
  48. return FmtUnknown
  49. }
  50. return FmtProtoDelim
  51. case textType:
  52. if v, ok := params["version"]; ok && v != TextVersion {
  53. return FmtUnknown
  54. }
  55. return FmtText
  56. }
  57. return FmtUnknown
  58. }
  59. // NewDecoder returns a new decoder based on the given input format.
  60. // If the input format does not imply otherwise, a text format decoder is returned.
  61. func NewDecoder(r io.Reader, format Format) Decoder {
  62. switch format {
  63. case FmtProtoDelim:
  64. return &protoDecoder{r: r}
  65. }
  66. return &textDecoder{r: r}
  67. }
  68. // protoDecoder implements the Decoder interface for protocol buffers.
  69. type protoDecoder struct {
  70. r io.Reader
  71. }
  72. // Decode implements the Decoder interface.
  73. func (d *protoDecoder) Decode(v *dto.MetricFamily) error {
  74. _, err := pbutil.ReadDelimited(d.r, v)
  75. if err != nil {
  76. return err
  77. }
  78. if !model.IsValidMetricName(model.LabelValue(v.GetName())) {
  79. return fmt.Errorf("invalid metric name %q", v.GetName())
  80. }
  81. for _, m := range v.GetMetric() {
  82. if m == nil {
  83. continue
  84. }
  85. for _, l := range m.GetLabel() {
  86. if l == nil {
  87. continue
  88. }
  89. if !model.LabelValue(l.GetValue()).IsValid() {
  90. return fmt.Errorf("invalid label value %q", l.GetValue())
  91. }
  92. if !model.LabelName(l.GetName()).IsValid() {
  93. return fmt.Errorf("invalid label name %q", l.GetName())
  94. }
  95. }
  96. }
  97. return nil
  98. }
  99. // textDecoder implements the Decoder interface for the text protocol.
  100. type textDecoder struct {
  101. r io.Reader
  102. fams map[string]*dto.MetricFamily
  103. err error
  104. }
  105. // Decode implements the Decoder interface.
  106. func (d *textDecoder) Decode(v *dto.MetricFamily) error {
  107. if d.err == nil {
  108. // Read all metrics in one shot.
  109. var p TextParser
  110. d.fams, d.err = p.TextToMetricFamilies(d.r)
  111. // If we don't get an error, store io.EOF for the end.
  112. if d.err == nil {
  113. d.err = io.EOF
  114. }
  115. }
  116. // Pick off one MetricFamily per Decode until there's nothing left.
  117. for key, fam := range d.fams {
  118. v.Name = fam.Name
  119. v.Help = fam.Help
  120. v.Type = fam.Type
  121. v.Metric = fam.Metric
  122. delete(d.fams, key)
  123. return nil
  124. }
  125. return d.err
  126. }
  127. // SampleDecoder wraps a Decoder to extract samples from the metric families
  128. // decoded by the wrapped Decoder.
  129. type SampleDecoder struct {
  130. Dec Decoder
  131. Opts *DecodeOptions
  132. f dto.MetricFamily
  133. }
  134. // Decode calls the Decode method of the wrapped Decoder and then extracts the
  135. // samples from the decoded MetricFamily into the provided model.Vector.
  136. func (sd *SampleDecoder) Decode(s *model.Vector) error {
  137. err := sd.Dec.Decode(&sd.f)
  138. if err != nil {
  139. return err
  140. }
  141. *s, err = extractSamples(&sd.f, sd.Opts)
  142. return err
  143. }
  144. // ExtractSamples builds a slice of samples from the provided metric
  145. // families. If an error occurs during sample extraction, it continues to
  146. // extract from the remaining metric families. The returned error is the last
  147. // error that has occurred.
  148. func ExtractSamples(o *DecodeOptions, fams ...*dto.MetricFamily) (model.Vector, error) {
  149. var (
  150. all model.Vector
  151. lastErr error
  152. )
  153. for _, f := range fams {
  154. some, err := extractSamples(f, o)
  155. if err != nil {
  156. lastErr = err
  157. continue
  158. }
  159. all = append(all, some...)
  160. }
  161. return all, lastErr
  162. }
  163. func extractSamples(f *dto.MetricFamily, o *DecodeOptions) (model.Vector, error) {
  164. switch f.GetType() {
  165. case dto.MetricType_COUNTER:
  166. return extractCounter(o, f), nil
  167. case dto.MetricType_GAUGE:
  168. return extractGauge(o, f), nil
  169. case dto.MetricType_SUMMARY:
  170. return extractSummary(o, f), nil
  171. case dto.MetricType_UNTYPED:
  172. return extractUntyped(o, f), nil
  173. case dto.MetricType_HISTOGRAM:
  174. return extractHistogram(o, f), nil
  175. }
  176. return nil, fmt.Errorf("expfmt.extractSamples: unknown metric family type %v", f.GetType())
  177. }
  178. func extractCounter(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
  179. samples := make(model.Vector, 0, len(f.Metric))
  180. for _, m := range f.Metric {
  181. if m.Counter == nil {
  182. continue
  183. }
  184. lset := make(model.LabelSet, len(m.Label)+1)
  185. for _, p := range m.Label {
  186. lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
  187. }
  188. lset[model.MetricNameLabel] = model.LabelValue(f.GetName())
  189. smpl := &model.Sample{
  190. Metric: model.Metric(lset),
  191. Value: model.SampleValue(m.Counter.GetValue()),
  192. }
  193. if m.TimestampMs != nil {
  194. smpl.Timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
  195. } else {
  196. smpl.Timestamp = o.Timestamp
  197. }
  198. samples = append(samples, smpl)
  199. }
  200. return samples
  201. }
  202. func extractGauge(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
  203. samples := make(model.Vector, 0, len(f.Metric))
  204. for _, m := range f.Metric {
  205. if m.Gauge == nil {
  206. continue
  207. }
  208. lset := make(model.LabelSet, len(m.Label)+1)
  209. for _, p := range m.Label {
  210. lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
  211. }
  212. lset[model.MetricNameLabel] = model.LabelValue(f.GetName())
  213. smpl := &model.Sample{
  214. Metric: model.Metric(lset),
  215. Value: model.SampleValue(m.Gauge.GetValue()),
  216. }
  217. if m.TimestampMs != nil {
  218. smpl.Timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
  219. } else {
  220. smpl.Timestamp = o.Timestamp
  221. }
  222. samples = append(samples, smpl)
  223. }
  224. return samples
  225. }
  226. func extractUntyped(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
  227. samples := make(model.Vector, 0, len(f.Metric))
  228. for _, m := range f.Metric {
  229. if m.Untyped == nil {
  230. continue
  231. }
  232. lset := make(model.LabelSet, len(m.Label)+1)
  233. for _, p := range m.Label {
  234. lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
  235. }
  236. lset[model.MetricNameLabel] = model.LabelValue(f.GetName())
  237. smpl := &model.Sample{
  238. Metric: model.Metric(lset),
  239. Value: model.SampleValue(m.Untyped.GetValue()),
  240. }
  241. if m.TimestampMs != nil {
  242. smpl.Timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
  243. } else {
  244. smpl.Timestamp = o.Timestamp
  245. }
  246. samples = append(samples, smpl)
  247. }
  248. return samples
  249. }
  250. func extractSummary(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
  251. samples := make(model.Vector, 0, len(f.Metric))
  252. for _, m := range f.Metric {
  253. if m.Summary == nil {
  254. continue
  255. }
  256. timestamp := o.Timestamp
  257. if m.TimestampMs != nil {
  258. timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
  259. }
  260. for _, q := range m.Summary.Quantile {
  261. lset := make(model.LabelSet, len(m.Label)+2)
  262. for _, p := range m.Label {
  263. lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
  264. }
  265. // BUG(matt): Update other names to "quantile".
  266. lset[model.LabelName(model.QuantileLabel)] = model.LabelValue(fmt.Sprint(q.GetQuantile()))
  267. lset[model.MetricNameLabel] = model.LabelValue(f.GetName())
  268. samples = append(samples, &model.Sample{
  269. Metric: model.Metric(lset),
  270. Value: model.SampleValue(q.GetValue()),
  271. Timestamp: timestamp,
  272. })
  273. }
  274. lset := make(model.LabelSet, len(m.Label)+1)
  275. for _, p := range m.Label {
  276. lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
  277. }
  278. lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_sum")
  279. samples = append(samples, &model.Sample{
  280. Metric: model.Metric(lset),
  281. Value: model.SampleValue(m.Summary.GetSampleSum()),
  282. Timestamp: timestamp,
  283. })
  284. lset = make(model.LabelSet, len(m.Label)+1)
  285. for _, p := range m.Label {
  286. lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
  287. }
  288. lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_count")
  289. samples = append(samples, &model.Sample{
  290. Metric: model.Metric(lset),
  291. Value: model.SampleValue(m.Summary.GetSampleCount()),
  292. Timestamp: timestamp,
  293. })
  294. }
  295. return samples
  296. }
  297. func extractHistogram(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
  298. samples := make(model.Vector, 0, len(f.Metric))
  299. for _, m := range f.Metric {
  300. if m.Histogram == nil {
  301. continue
  302. }
  303. timestamp := o.Timestamp
  304. if m.TimestampMs != nil {
  305. timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
  306. }
  307. infSeen := false
  308. for _, q := range m.Histogram.Bucket {
  309. lset := make(model.LabelSet, len(m.Label)+2)
  310. for _, p := range m.Label {
  311. lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
  312. }
  313. lset[model.LabelName(model.BucketLabel)] = model.LabelValue(fmt.Sprint(q.GetUpperBound()))
  314. lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_bucket")
  315. if math.IsInf(q.GetUpperBound(), +1) {
  316. infSeen = true
  317. }
  318. samples = append(samples, &model.Sample{
  319. Metric: model.Metric(lset),
  320. Value: model.SampleValue(q.GetCumulativeCount()),
  321. Timestamp: timestamp,
  322. })
  323. }
  324. lset := make(model.LabelSet, len(m.Label)+1)
  325. for _, p := range m.Label {
  326. lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
  327. }
  328. lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_sum")
  329. samples = append(samples, &model.Sample{
  330. Metric: model.Metric(lset),
  331. Value: model.SampleValue(m.Histogram.GetSampleSum()),
  332. Timestamp: timestamp,
  333. })
  334. lset = make(model.LabelSet, len(m.Label)+1)
  335. for _, p := range m.Label {
  336. lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
  337. }
  338. lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_count")
  339. count := &model.Sample{
  340. Metric: model.Metric(lset),
  341. Value: model.SampleValue(m.Histogram.GetSampleCount()),
  342. Timestamp: timestamp,
  343. }
  344. samples = append(samples, count)
  345. if !infSeen {
  346. // Append an infinity bucket sample.
  347. lset := make(model.LabelSet, len(m.Label)+2)
  348. for _, p := range m.Label {
  349. lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
  350. }
  351. lset[model.LabelName(model.BucketLabel)] = model.LabelValue("+Inf")
  352. lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_bucket")
  353. samples = append(samples, &model.Sample{
  354. Metric: model.Metric(lset),
  355. Value: count.Value,
  356. Timestamp: timestamp,
  357. })
  358. }
  359. }
  360. return samples
  361. }