| // 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_test |
| |
| import ( |
| "bytes" |
| "fmt" |
| "math" |
| "net/http" |
| "runtime" |
| "strings" |
| "time" |
| |
| "github.com/golang/protobuf/proto" |
| "github.com/prometheus/common/expfmt" |
| |
| dto "github.com/prometheus/client_model/go" |
| |
| "github.com/prometheus/client_golang/prometheus" |
| "github.com/prometheus/client_golang/prometheus/promhttp" |
| ) |
| |
| func ExampleGauge() { |
| opsQueued := prometheus.NewGauge(prometheus.GaugeOpts{ |
| Namespace: "our_company", |
| Subsystem: "blob_storage", |
| Name: "ops_queued", |
| Help: "Number of blob storage operations waiting to be processed.", |
| }) |
| prometheus.MustRegister(opsQueued) |
| |
| // 10 operations queued by the goroutine managing incoming requests. |
| opsQueued.Add(10) |
| // A worker goroutine has picked up a waiting operation. |
| opsQueued.Dec() |
| // And once more... |
| opsQueued.Dec() |
| } |
| |
| func ExampleGaugeVec() { |
| opsQueued := prometheus.NewGaugeVec( |
| prometheus.GaugeOpts{ |
| Namespace: "our_company", |
| Subsystem: "blob_storage", |
| Name: "ops_queued", |
| Help: "Number of blob storage operations waiting to be processed, partitioned by user and type.", |
| }, |
| []string{ |
| // Which user has requested the operation? |
| "user", |
| // Of what type is the operation? |
| "type", |
| }, |
| ) |
| prometheus.MustRegister(opsQueued) |
| |
| // Increase a value using compact (but order-sensitive!) WithLabelValues(). |
| opsQueued.WithLabelValues("bob", "put").Add(4) |
| // Increase a value with a map using WithLabels. More verbose, but order |
| // doesn't matter anymore. |
| opsQueued.With(prometheus.Labels{"type": "delete", "user": "alice"}).Inc() |
| } |
| |
| func ExampleGaugeFunc() { |
| if err := prometheus.Register(prometheus.NewGaugeFunc( |
| prometheus.GaugeOpts{ |
| Subsystem: "runtime", |
| Name: "goroutines_count", |
| Help: "Number of goroutines that currently exist.", |
| }, |
| func() float64 { return float64(runtime.NumGoroutine()) }, |
| )); err == nil { |
| fmt.Println("GaugeFunc 'goroutines_count' registered.") |
| } |
| // Note that the count of goroutines is a gauge (and not a counter) as |
| // it can go up and down. |
| |
| // Output: |
| // GaugeFunc 'goroutines_count' registered. |
| } |
| |
| func ExampleCounterVec() { |
| httpReqs := prometheus.NewCounterVec( |
| prometheus.CounterOpts{ |
| Name: "http_requests_total", |
| Help: "How many HTTP requests processed, partitioned by status code and HTTP method.", |
| }, |
| []string{"code", "method"}, |
| ) |
| prometheus.MustRegister(httpReqs) |
| |
| httpReqs.WithLabelValues("404", "POST").Add(42) |
| |
| // If you have to access the same set of labels very frequently, it |
| // might be good to retrieve the metric only once and keep a handle to |
| // it. But beware of deletion of that metric, see below! |
| m := httpReqs.WithLabelValues("200", "GET") |
| for i := 0; i < 1000000; i++ { |
| m.Inc() |
| } |
| // Delete a metric from the vector. If you have previously kept a handle |
| // to that metric (as above), future updates via that handle will go |
| // unseen (even if you re-create a metric with the same label set |
| // later). |
| httpReqs.DeleteLabelValues("200", "GET") |
| // Same thing with the more verbose Labels syntax. |
| httpReqs.Delete(prometheus.Labels{"method": "GET", "code": "200"}) |
| } |
| |
| func ExampleRegister() { |
| // Imagine you have a worker pool and want to count the tasks completed. |
| taskCounter := prometheus.NewCounter(prometheus.CounterOpts{ |
| Subsystem: "worker_pool", |
| Name: "completed_tasks_total", |
| Help: "Total number of tasks completed.", |
| }) |
| // This will register fine. |
| if err := prometheus.Register(taskCounter); err != nil { |
| fmt.Println(err) |
| } else { |
| fmt.Println("taskCounter registered.") |
| } |
| // Don't forget to tell the HTTP server about the Prometheus handler. |
| // (In a real program, you still need to start the HTTP server...) |
| http.Handle("/metrics", promhttp.Handler()) |
| |
| // Now you can start workers and give every one of them a pointer to |
| // taskCounter and let it increment it whenever it completes a task. |
| taskCounter.Inc() // This has to happen somewhere in the worker code. |
| |
| // But wait, you want to see how individual workers perform. So you need |
| // a vector of counters, with one element for each worker. |
| taskCounterVec := prometheus.NewCounterVec( |
| prometheus.CounterOpts{ |
| Subsystem: "worker_pool", |
| Name: "completed_tasks_total", |
| Help: "Total number of tasks completed.", |
| }, |
| []string{"worker_id"}, |
| ) |
| |
| // Registering will fail because we already have a metric of that name. |
| if err := prometheus.Register(taskCounterVec); err != nil { |
| fmt.Println("taskCounterVec not registered:", err) |
| } else { |
| fmt.Println("taskCounterVec registered.") |
| } |
| |
| // To fix, first unregister the old taskCounter. |
| if prometheus.Unregister(taskCounter) { |
| fmt.Println("taskCounter unregistered.") |
| } |
| |
| // Try registering taskCounterVec again. |
| if err := prometheus.Register(taskCounterVec); err != nil { |
| fmt.Println("taskCounterVec not registered:", err) |
| } else { |
| fmt.Println("taskCounterVec registered.") |
| } |
| // Bummer! Still doesn't work. |
| |
| // Prometheus will not allow you to ever export metrics with |
| // inconsistent help strings or label names. After unregistering, the |
| // unregistered metrics will cease to show up in the /metrics HTTP |
| // response, but the registry still remembers that those metrics had |
| // been exported before. For this example, we will now choose a |
| // different name. (In a real program, you would obviously not export |
| // the obsolete metric in the first place.) |
| taskCounterVec = prometheus.NewCounterVec( |
| prometheus.CounterOpts{ |
| Subsystem: "worker_pool", |
| Name: "completed_tasks_by_id", |
| Help: "Total number of tasks completed.", |
| }, |
| []string{"worker_id"}, |
| ) |
| if err := prometheus.Register(taskCounterVec); err != nil { |
| fmt.Println("taskCounterVec not registered:", err) |
| } else { |
| fmt.Println("taskCounterVec registered.") |
| } |
| // Finally it worked! |
| |
| // The workers have to tell taskCounterVec their id to increment the |
| // right element in the metric vector. |
| taskCounterVec.WithLabelValues("42").Inc() // Code from worker 42. |
| |
| // Each worker could also keep a reference to their own counter element |
| // around. Pick the counter at initialization time of the worker. |
| myCounter := taskCounterVec.WithLabelValues("42") // From worker 42 initialization code. |
| myCounter.Inc() // Somewhere in the code of that worker. |
| |
| // Note that something like WithLabelValues("42", "spurious arg") would |
| // panic (because you have provided too many label values). If you want |
| // to get an error instead, use GetMetricWithLabelValues(...) instead. |
| notMyCounter, err := taskCounterVec.GetMetricWithLabelValues("42", "spurious arg") |
| if err != nil { |
| fmt.Println("Worker initialization failed:", err) |
| } |
| if notMyCounter == nil { |
| fmt.Println("notMyCounter is nil.") |
| } |
| |
| // A different (and somewhat tricky) approach is to use |
| // ConstLabels. ConstLabels are pairs of label names and label values |
| // that never change. Each worker creates and registers an own Counter |
| // instance where the only difference is in the value of the |
| // ConstLabels. Those Counters can all be registered because the |
| // different ConstLabel values guarantee that each worker will increment |
| // a different Counter metric. |
| counterOpts := prometheus.CounterOpts{ |
| Subsystem: "worker_pool", |
| Name: "completed_tasks", |
| Help: "Total number of tasks completed.", |
| ConstLabels: prometheus.Labels{"worker_id": "42"}, |
| } |
| taskCounterForWorker42 := prometheus.NewCounter(counterOpts) |
| if err := prometheus.Register(taskCounterForWorker42); err != nil { |
| fmt.Println("taskCounterVForWorker42 not registered:", err) |
| } else { |
| fmt.Println("taskCounterForWorker42 registered.") |
| } |
| // Obviously, in real code, taskCounterForWorker42 would be a member |
| // variable of a worker struct, and the "42" would be retrieved with a |
| // GetId() method or something. The Counter would be created and |
| // registered in the initialization code of the worker. |
| |
| // For the creation of the next Counter, we can recycle |
| // counterOpts. Just change the ConstLabels. |
| counterOpts.ConstLabels = prometheus.Labels{"worker_id": "2001"} |
| taskCounterForWorker2001 := prometheus.NewCounter(counterOpts) |
| if err := prometheus.Register(taskCounterForWorker2001); err != nil { |
| fmt.Println("taskCounterVForWorker2001 not registered:", err) |
| } else { |
| fmt.Println("taskCounterForWorker2001 registered.") |
| } |
| |
| taskCounterForWorker2001.Inc() |
| taskCounterForWorker42.Inc() |
| taskCounterForWorker2001.Inc() |
| |
| // Yet another approach would be to turn the workers themselves into |
| // Collectors and register them. See the Collector example for details. |
| |
| // Output: |
| // taskCounter registered. |
| // taskCounterVec not registered: a previously registered descriptor with the same fully-qualified name as Desc{fqName: "worker_pool_completed_tasks_total", help: "Total number of tasks completed.", constLabels: {}, variableLabels: [worker_id]} has different label names or a different help string |
| // taskCounter unregistered. |
| // taskCounterVec not registered: a previously registered descriptor with the same fully-qualified name as Desc{fqName: "worker_pool_completed_tasks_total", help: "Total number of tasks completed.", constLabels: {}, variableLabels: [worker_id]} has different label names or a different help string |
| // taskCounterVec registered. |
| // Worker initialization failed: inconsistent label cardinality: expected 1 label values but got 2 in []string{"42", "spurious arg"} |
| // notMyCounter is nil. |
| // taskCounterForWorker42 registered. |
| // taskCounterForWorker2001 registered. |
| } |
| |
| func ExampleSummary() { |
| temps := prometheus.NewSummary(prometheus.SummaryOpts{ |
| Name: "pond_temperature_celsius", |
| Help: "The temperature of the frog pond.", |
| Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, |
| }) |
| |
| // Simulate some observations. |
| for i := 0; i < 1000; i++ { |
| temps.Observe(30 + math.Floor(120*math.Sin(float64(i)*0.1))/10) |
| } |
| |
| // Just for demonstration, let's check the state of the summary by |
| // (ab)using its Write method (which is usually only used by Prometheus |
| // internally). |
| metric := &dto.Metric{} |
| temps.Write(metric) |
| fmt.Println(proto.MarshalTextString(metric)) |
| |
| // Output: |
| // summary: < |
| // sample_count: 1000 |
| // sample_sum: 29969.50000000001 |
| // quantile: < |
| // quantile: 0.5 |
| // value: 31.1 |
| // > |
| // quantile: < |
| // quantile: 0.9 |
| // value: 41.3 |
| // > |
| // quantile: < |
| // quantile: 0.99 |
| // value: 41.9 |
| // > |
| // > |
| } |
| |
| func ExampleSummaryVec() { |
| temps := prometheus.NewSummaryVec( |
| prometheus.SummaryOpts{ |
| Name: "pond_temperature_celsius", |
| Help: "The temperature of the frog pond.", |
| Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, |
| }, |
| []string{"species"}, |
| ) |
| |
| // Simulate some observations. |
| for i := 0; i < 1000; i++ { |
| temps.WithLabelValues("litoria-caerulea").Observe(30 + math.Floor(120*math.Sin(float64(i)*0.1))/10) |
| temps.WithLabelValues("lithobates-catesbeianus").Observe(32 + math.Floor(100*math.Cos(float64(i)*0.11))/10) |
| } |
| |
| // Create a Summary without any observations. |
| temps.WithLabelValues("leiopelma-hochstetteri") |
| |
| // Just for demonstration, let's check the state of the summary vector |
| // by registering it with a custom registry and then let it collect the |
| // metrics. |
| reg := prometheus.NewRegistry() |
| reg.MustRegister(temps) |
| |
| metricFamilies, err := reg.Gather() |
| if err != nil || len(metricFamilies) != 1 { |
| panic("unexpected behavior of custom test registry") |
| } |
| fmt.Println(proto.MarshalTextString(metricFamilies[0])) |
| |
| // Output: |
| // name: "pond_temperature_celsius" |
| // help: "The temperature of the frog pond." |
| // type: SUMMARY |
| // metric: < |
| // label: < |
| // name: "species" |
| // value: "leiopelma-hochstetteri" |
| // > |
| // summary: < |
| // sample_count: 0 |
| // sample_sum: 0 |
| // quantile: < |
| // quantile: 0.5 |
| // value: nan |
| // > |
| // quantile: < |
| // quantile: 0.9 |
| // value: nan |
| // > |
| // quantile: < |
| // quantile: 0.99 |
| // value: nan |
| // > |
| // > |
| // > |
| // metric: < |
| // label: < |
| // name: "species" |
| // value: "lithobates-catesbeianus" |
| // > |
| // summary: < |
| // sample_count: 1000 |
| // sample_sum: 31956.100000000017 |
| // quantile: < |
| // quantile: 0.5 |
| // value: 32.4 |
| // > |
| // quantile: < |
| // quantile: 0.9 |
| // value: 41.4 |
| // > |
| // quantile: < |
| // quantile: 0.99 |
| // value: 41.9 |
| // > |
| // > |
| // > |
| // metric: < |
| // label: < |
| // name: "species" |
| // value: "litoria-caerulea" |
| // > |
| // summary: < |
| // sample_count: 1000 |
| // sample_sum: 29969.50000000001 |
| // quantile: < |
| // quantile: 0.5 |
| // value: 31.1 |
| // > |
| // quantile: < |
| // quantile: 0.9 |
| // value: 41.3 |
| // > |
| // quantile: < |
| // quantile: 0.99 |
| // value: 41.9 |
| // > |
| // > |
| // > |
| } |
| |
| func ExampleNewConstSummary() { |
| desc := prometheus.NewDesc( |
| "http_request_duration_seconds", |
| "A summary of the HTTP request durations.", |
| []string{"code", "method"}, |
| prometheus.Labels{"owner": "example"}, |
| ) |
| |
| // Create a constant summary from values we got from a 3rd party telemetry system. |
| s := prometheus.MustNewConstSummary( |
| desc, |
| 4711, 403.34, |
| map[float64]float64{0.5: 42.3, 0.9: 323.3}, |
| "200", "get", |
| ) |
| |
| // Just for demonstration, let's check the state of the summary by |
| // (ab)using its Write method (which is usually only used by Prometheus |
| // internally). |
| metric := &dto.Metric{} |
| s.Write(metric) |
| fmt.Println(proto.MarshalTextString(metric)) |
| |
| // Output: |
| // label: < |
| // name: "code" |
| // value: "200" |
| // > |
| // label: < |
| // name: "method" |
| // value: "get" |
| // > |
| // label: < |
| // name: "owner" |
| // value: "example" |
| // > |
| // summary: < |
| // sample_count: 4711 |
| // sample_sum: 403.34 |
| // quantile: < |
| // quantile: 0.5 |
| // value: 42.3 |
| // > |
| // quantile: < |
| // quantile: 0.9 |
| // value: 323.3 |
| // > |
| // > |
| } |
| |
| func ExampleHistogram() { |
| temps := prometheus.NewHistogram(prometheus.HistogramOpts{ |
| Name: "pond_temperature_celsius", |
| Help: "The temperature of the frog pond.", // Sorry, we can't measure how badly it smells. |
| Buckets: prometheus.LinearBuckets(20, 5, 5), // 5 buckets, each 5 centigrade wide. |
| }) |
| |
| // Simulate some observations. |
| for i := 0; i < 1000; i++ { |
| temps.Observe(30 + math.Floor(120*math.Sin(float64(i)*0.1))/10) |
| } |
| |
| // Just for demonstration, let's check the state of the histogram by |
| // (ab)using its Write method (which is usually only used by Prometheus |
| // internally). |
| metric := &dto.Metric{} |
| temps.Write(metric) |
| fmt.Println(proto.MarshalTextString(metric)) |
| |
| // Output: |
| // histogram: < |
| // sample_count: 1000 |
| // sample_sum: 29969.50000000001 |
| // bucket: < |
| // cumulative_count: 192 |
| // upper_bound: 20 |
| // > |
| // bucket: < |
| // cumulative_count: 366 |
| // upper_bound: 25 |
| // > |
| // bucket: < |
| // cumulative_count: 501 |
| // upper_bound: 30 |
| // > |
| // bucket: < |
| // cumulative_count: 638 |
| // upper_bound: 35 |
| // > |
| // bucket: < |
| // cumulative_count: 816 |
| // upper_bound: 40 |
| // > |
| // > |
| } |
| |
| func ExampleNewConstHistogram() { |
| desc := prometheus.NewDesc( |
| "http_request_duration_seconds", |
| "A histogram of the HTTP request durations.", |
| []string{"code", "method"}, |
| prometheus.Labels{"owner": "example"}, |
| ) |
| |
| // Create a constant histogram from values we got from a 3rd party telemetry system. |
| h := prometheus.MustNewConstHistogram( |
| desc, |
| 4711, 403.34, |
| map[float64]uint64{25: 121, 50: 2403, 100: 3221, 200: 4233}, |
| "200", "get", |
| ) |
| |
| // Just for demonstration, let's check the state of the histogram by |
| // (ab)using its Write method (which is usually only used by Prometheus |
| // internally). |
| metric := &dto.Metric{} |
| h.Write(metric) |
| fmt.Println(proto.MarshalTextString(metric)) |
| |
| // Output: |
| // label: < |
| // name: "code" |
| // value: "200" |
| // > |
| // label: < |
| // name: "method" |
| // value: "get" |
| // > |
| // label: < |
| // name: "owner" |
| // value: "example" |
| // > |
| // histogram: < |
| // sample_count: 4711 |
| // sample_sum: 403.34 |
| // bucket: < |
| // cumulative_count: 121 |
| // upper_bound: 25 |
| // > |
| // bucket: < |
| // cumulative_count: 2403 |
| // upper_bound: 50 |
| // > |
| // bucket: < |
| // cumulative_count: 3221 |
| // upper_bound: 100 |
| // > |
| // bucket: < |
| // cumulative_count: 4233 |
| // upper_bound: 200 |
| // > |
| // > |
| } |
| |
| func ExampleAlreadyRegisteredError() { |
| reqCounter := prometheus.NewCounter(prometheus.CounterOpts{ |
| Name: "requests_total", |
| Help: "The total number of requests served.", |
| }) |
| if err := prometheus.Register(reqCounter); err != nil { |
| if are, ok := err.(prometheus.AlreadyRegisteredError); ok { |
| // A counter for that metric has been registered before. |
| // Use the old counter from now on. |
| reqCounter = are.ExistingCollector.(prometheus.Counter) |
| } else { |
| // Something else went wrong! |
| panic(err) |
| } |
| } |
| reqCounter.Inc() |
| } |
| |
| func ExampleGatherers() { |
| reg := prometheus.NewRegistry() |
| temp := prometheus.NewGaugeVec( |
| prometheus.GaugeOpts{ |
| Name: "temperature_kelvin", |
| Help: "Temperature in Kelvin.", |
| }, |
| []string{"location"}, |
| ) |
| reg.MustRegister(temp) |
| temp.WithLabelValues("outside").Set(273.14) |
| temp.WithLabelValues("inside").Set(298.44) |
| |
| var parser expfmt.TextParser |
| |
| text := ` |
| # TYPE humidity_percent gauge |
| # HELP humidity_percent Humidity in %. |
| humidity_percent{location="outside"} 45.4 |
| humidity_percent{location="inside"} 33.2 |
| # TYPE temperature_kelvin gauge |
| # HELP temperature_kelvin Temperature in Kelvin. |
| temperature_kelvin{location="somewhere else"} 4.5 |
| ` |
| |
| parseText := func() ([]*dto.MetricFamily, error) { |
| parsed, err := parser.TextToMetricFamilies(strings.NewReader(text)) |
| if err != nil { |
| return nil, err |
| } |
| var result []*dto.MetricFamily |
| for _, mf := range parsed { |
| result = append(result, mf) |
| } |
| return result, nil |
| } |
| |
| gatherers := prometheus.Gatherers{ |
| reg, |
| prometheus.GathererFunc(parseText), |
| } |
| |
| gathering, err := gatherers.Gather() |
| if err != nil { |
| fmt.Println(err) |
| } |
| |
| out := &bytes.Buffer{} |
| for _, mf := range gathering { |
| if _, err := expfmt.MetricFamilyToText(out, mf); err != nil { |
| panic(err) |
| } |
| } |
| fmt.Print(out.String()) |
| fmt.Println("----------") |
| |
| // Note how the temperature_kelvin metric family has been merged from |
| // different sources. Now try |
| text = ` |
| # TYPE humidity_percent gauge |
| # HELP humidity_percent Humidity in %. |
| humidity_percent{location="outside"} 45.4 |
| humidity_percent{location="inside"} 33.2 |
| # TYPE temperature_kelvin gauge |
| # HELP temperature_kelvin Temperature in Kelvin. |
| # Duplicate metric: |
| temperature_kelvin{location="outside"} 265.3 |
| # Missing location label (note that this is undesirable but valid): |
| temperature_kelvin 4.5 |
| ` |
| |
| gathering, err = gatherers.Gather() |
| if err != nil { |
| fmt.Println(err) |
| } |
| // Note that still as many metrics as possible are returned: |
| out.Reset() |
| for _, mf := range gathering { |
| if _, err := expfmt.MetricFamilyToText(out, mf); err != nil { |
| panic(err) |
| } |
| } |
| fmt.Print(out.String()) |
| |
| // Output: |
| // # HELP humidity_percent Humidity in %. |
| // # TYPE humidity_percent gauge |
| // humidity_percent{location="inside"} 33.2 |
| // humidity_percent{location="outside"} 45.4 |
| // # HELP temperature_kelvin Temperature in Kelvin. |
| // # TYPE temperature_kelvin gauge |
| // temperature_kelvin{location="inside"} 298.44 |
| // temperature_kelvin{location="outside"} 273.14 |
| // temperature_kelvin{location="somewhere else"} 4.5 |
| // ---------- |
| // collected metric "temperature_kelvin" { label:<name:"location" value:"outside" > gauge:<value:265.3 > } was collected before with the same name and label values |
| // # HELP humidity_percent Humidity in %. |
| // # TYPE humidity_percent gauge |
| // humidity_percent{location="inside"} 33.2 |
| // humidity_percent{location="outside"} 45.4 |
| // # HELP temperature_kelvin Temperature in Kelvin. |
| // # TYPE temperature_kelvin gauge |
| // temperature_kelvin 4.5 |
| // temperature_kelvin{location="inside"} 298.44 |
| // temperature_kelvin{location="outside"} 273.14 |
| } |
| |
| func ExampleNewMetricWithTimestamp() { |
| desc := prometheus.NewDesc( |
| "temperature_kelvin", |
| "Current temperature in Kelvin.", |
| nil, nil, |
| ) |
| |
| // Create a constant gauge from values we got from an external |
| // temperature reporting system. Those values are reported with a slight |
| // delay, so we want to add the timestamp of the actual measurement. |
| temperatureReportedByExternalSystem := 298.15 |
| timeReportedByExternalSystem := time.Date(2009, time.November, 10, 23, 0, 0, 12345678, time.UTC) |
| s := prometheus.NewMetricWithTimestamp( |
| timeReportedByExternalSystem, |
| prometheus.MustNewConstMetric( |
| desc, prometheus.GaugeValue, temperatureReportedByExternalSystem, |
| ), |
| ) |
| |
| // Just for demonstration, let's check the state of the gauge by |
| // (ab)using its Write method (which is usually only used by Prometheus |
| // internally). |
| metric := &dto.Metric{} |
| s.Write(metric) |
| fmt.Println(proto.MarshalTextString(metric)) |
| |
| // Output: |
| // gauge: < |
| // value: 298.15 |
| // > |
| // timestamp_ms: 1257894000012 |
| } |