1// Copyright 2014 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 14package prometheus_test 15 16import ( 17 "bytes" 18 "fmt" 19 "math" 20 "net/http" 21 "runtime" 22 "strings" 23 "time" 24 25 //lint:ignore SA1019 Need to keep deprecated package for compatibility. 26 "github.com/golang/protobuf/proto" 27 "github.com/prometheus/common/expfmt" 28 29 dto "github.com/prometheus/client_model/go" 30 31 "github.com/prometheus/client_golang/prometheus" 32 "github.com/prometheus/client_golang/prometheus/promhttp" 33) 34 35func ExampleGauge() { 36 opsQueued := prometheus.NewGauge(prometheus.GaugeOpts{ 37 Namespace: "our_company", 38 Subsystem: "blob_storage", 39 Name: "ops_queued", 40 Help: "Number of blob storage operations waiting to be processed.", 41 }) 42 prometheus.MustRegister(opsQueued) 43 44 // 10 operations queued by the goroutine managing incoming requests. 45 opsQueued.Add(10) 46 // A worker goroutine has picked up a waiting operation. 47 opsQueued.Dec() 48 // And once more... 49 opsQueued.Dec() 50} 51 52func ExampleGaugeVec() { 53 opsQueued := prometheus.NewGaugeVec( 54 prometheus.GaugeOpts{ 55 Namespace: "our_company", 56 Subsystem: "blob_storage", 57 Name: "ops_queued", 58 Help: "Number of blob storage operations waiting to be processed, partitioned by user and type.", 59 }, 60 []string{ 61 // Which user has requested the operation? 62 "user", 63 // Of what type is the operation? 64 "type", 65 }, 66 ) 67 prometheus.MustRegister(opsQueued) 68 69 // Increase a value using compact (but order-sensitive!) WithLabelValues(). 70 opsQueued.WithLabelValues("bob", "put").Add(4) 71 // Increase a value with a map using WithLabels. More verbose, but order 72 // doesn't matter anymore. 73 opsQueued.With(prometheus.Labels{"type": "delete", "user": "alice"}).Inc() 74} 75 76func ExampleGaugeFunc_simple() { 77 if err := prometheus.Register(prometheus.NewGaugeFunc( 78 prometheus.GaugeOpts{ 79 Subsystem: "runtime", 80 Name: "goroutines_count", 81 Help: "Number of goroutines that currently exist.", 82 }, 83 func() float64 { return float64(runtime.NumGoroutine()) }, 84 )); err == nil { 85 fmt.Println("GaugeFunc 'goroutines_count' registered.") 86 } 87 // Note that the count of goroutines is a gauge (and not a counter) as 88 // it can go up and down. 89 90 // Output: 91 // GaugeFunc 'goroutines_count' registered. 92} 93 94func ExampleGaugeFunc_constLabels() { 95 // primaryDB and secondaryDB represent two example *sql.DB connections we want to instrument. 96 var primaryDB, secondaryDB interface { 97 Stats() struct{ OpenConnections int } 98 } 99 100 if err := prometheus.Register(prometheus.NewGaugeFunc( 101 prometheus.GaugeOpts{ 102 Namespace: "mysql", 103 Name: "connections_open", 104 Help: "Number of mysql connections open.", 105 ConstLabels: prometheus.Labels{"destination": "primary"}, 106 }, 107 func() float64 { return float64(primaryDB.Stats().OpenConnections) }, 108 )); err == nil { 109 fmt.Println(`GaugeFunc 'connections_open' for primary DB connection registered with labels {destination="primary"}`) 110 } 111 112 if err := prometheus.Register(prometheus.NewGaugeFunc( 113 prometheus.GaugeOpts{ 114 Namespace: "mysql", 115 Name: "connections_open", 116 Help: "Number of mysql connections open.", 117 ConstLabels: prometheus.Labels{"destination": "secondary"}, 118 }, 119 func() float64 { return float64(secondaryDB.Stats().OpenConnections) }, 120 )); err == nil { 121 fmt.Println(`GaugeFunc 'connections_open' for secondary DB connection registered with labels {destination="secondary"}`) 122 } 123 124 // Note that we can register more than once GaugeFunc with same metric name 125 // as long as their const labels are consistent. 126 127 // Output: 128 // GaugeFunc 'connections_open' for primary DB connection registered with labels {destination="primary"} 129 // GaugeFunc 'connections_open' for secondary DB connection registered with labels {destination="secondary"} 130} 131 132func ExampleCounterVec() { 133 httpReqs := prometheus.NewCounterVec( 134 prometheus.CounterOpts{ 135 Name: "http_requests_total", 136 Help: "How many HTTP requests processed, partitioned by status code and HTTP method.", 137 }, 138 []string{"code", "method"}, 139 ) 140 prometheus.MustRegister(httpReqs) 141 142 httpReqs.WithLabelValues("404", "POST").Add(42) 143 144 // If you have to access the same set of labels very frequently, it 145 // might be good to retrieve the metric only once and keep a handle to 146 // it. But beware of deletion of that metric, see below! 147 m := httpReqs.WithLabelValues("200", "GET") 148 for i := 0; i < 1000000; i++ { 149 m.Inc() 150 } 151 // Delete a metric from the vector. If you have previously kept a handle 152 // to that metric (as above), future updates via that handle will go 153 // unseen (even if you re-create a metric with the same label set 154 // later). 155 httpReqs.DeleteLabelValues("200", "GET") 156 // Same thing with the more verbose Labels syntax. 157 httpReqs.Delete(prometheus.Labels{"method": "GET", "code": "200"}) 158} 159 160func ExampleRegister() { 161 // Imagine you have a worker pool and want to count the tasks completed. 162 taskCounter := prometheus.NewCounter(prometheus.CounterOpts{ 163 Subsystem: "worker_pool", 164 Name: "completed_tasks_total", 165 Help: "Total number of tasks completed.", 166 }) 167 // This will register fine. 168 if err := prometheus.Register(taskCounter); err != nil { 169 fmt.Println(err) 170 } else { 171 fmt.Println("taskCounter registered.") 172 } 173 // Don't forget to tell the HTTP server about the Prometheus handler. 174 // (In a real program, you still need to start the HTTP server...) 175 http.Handle("/metrics", promhttp.Handler()) 176 177 // Now you can start workers and give every one of them a pointer to 178 // taskCounter and let it increment it whenever it completes a task. 179 taskCounter.Inc() // This has to happen somewhere in the worker code. 180 181 // But wait, you want to see how individual workers perform. So you need 182 // a vector of counters, with one element for each worker. 183 taskCounterVec := prometheus.NewCounterVec( 184 prometheus.CounterOpts{ 185 Subsystem: "worker_pool", 186 Name: "completed_tasks_total", 187 Help: "Total number of tasks completed.", 188 }, 189 []string{"worker_id"}, 190 ) 191 192 // Registering will fail because we already have a metric of that name. 193 if err := prometheus.Register(taskCounterVec); err != nil { 194 fmt.Println("taskCounterVec not registered:", err) 195 } else { 196 fmt.Println("taskCounterVec registered.") 197 } 198 199 // To fix, first unregister the old taskCounter. 200 if prometheus.Unregister(taskCounter) { 201 fmt.Println("taskCounter unregistered.") 202 } 203 204 // Try registering taskCounterVec again. 205 if err := prometheus.Register(taskCounterVec); err != nil { 206 fmt.Println("taskCounterVec not registered:", err) 207 } else { 208 fmt.Println("taskCounterVec registered.") 209 } 210 // Bummer! Still doesn't work. 211 212 // Prometheus will not allow you to ever export metrics with 213 // inconsistent help strings or label names. After unregistering, the 214 // unregistered metrics will cease to show up in the /metrics HTTP 215 // response, but the registry still remembers that those metrics had 216 // been exported before. For this example, we will now choose a 217 // different name. (In a real program, you would obviously not export 218 // the obsolete metric in the first place.) 219 taskCounterVec = prometheus.NewCounterVec( 220 prometheus.CounterOpts{ 221 Subsystem: "worker_pool", 222 Name: "completed_tasks_by_id", 223 Help: "Total number of tasks completed.", 224 }, 225 []string{"worker_id"}, 226 ) 227 if err := prometheus.Register(taskCounterVec); err != nil { 228 fmt.Println("taskCounterVec not registered:", err) 229 } else { 230 fmt.Println("taskCounterVec registered.") 231 } 232 // Finally it worked! 233 234 // The workers have to tell taskCounterVec their id to increment the 235 // right element in the metric vector. 236 taskCounterVec.WithLabelValues("42").Inc() // Code from worker 42. 237 238 // Each worker could also keep a reference to their own counter element 239 // around. Pick the counter at initialization time of the worker. 240 myCounter := taskCounterVec.WithLabelValues("42") // From worker 42 initialization code. 241 myCounter.Inc() // Somewhere in the code of that worker. 242 243 // Note that something like WithLabelValues("42", "spurious arg") would 244 // panic (because you have provided too many label values). If you want 245 // to get an error instead, use GetMetricWithLabelValues(...) instead. 246 notMyCounter, err := taskCounterVec.GetMetricWithLabelValues("42", "spurious arg") 247 if err != nil { 248 fmt.Println("Worker initialization failed:", err) 249 } 250 if notMyCounter == nil { 251 fmt.Println("notMyCounter is nil.") 252 } 253 254 // A different (and somewhat tricky) approach is to use 255 // ConstLabels. ConstLabels are pairs of label names and label values 256 // that never change. Each worker creates and registers an own Counter 257 // instance where the only difference is in the value of the 258 // ConstLabels. Those Counters can all be registered because the 259 // different ConstLabel values guarantee that each worker will increment 260 // a different Counter metric. 261 counterOpts := prometheus.CounterOpts{ 262 Subsystem: "worker_pool", 263 Name: "completed_tasks", 264 Help: "Total number of tasks completed.", 265 ConstLabels: prometheus.Labels{"worker_id": "42"}, 266 } 267 taskCounterForWorker42 := prometheus.NewCounter(counterOpts) 268 if err := prometheus.Register(taskCounterForWorker42); err != nil { 269 fmt.Println("taskCounterVForWorker42 not registered:", err) 270 } else { 271 fmt.Println("taskCounterForWorker42 registered.") 272 } 273 // Obviously, in real code, taskCounterForWorker42 would be a member 274 // variable of a worker struct, and the "42" would be retrieved with a 275 // GetId() method or something. The Counter would be created and 276 // registered in the initialization code of the worker. 277 278 // For the creation of the next Counter, we can recycle 279 // counterOpts. Just change the ConstLabels. 280 counterOpts.ConstLabels = prometheus.Labels{"worker_id": "2001"} 281 taskCounterForWorker2001 := prometheus.NewCounter(counterOpts) 282 if err := prometheus.Register(taskCounterForWorker2001); err != nil { 283 fmt.Println("taskCounterVForWorker2001 not registered:", err) 284 } else { 285 fmt.Println("taskCounterForWorker2001 registered.") 286 } 287 288 taskCounterForWorker2001.Inc() 289 taskCounterForWorker42.Inc() 290 taskCounterForWorker2001.Inc() 291 292 // Yet another approach would be to turn the workers themselves into 293 // Collectors and register them. See the Collector example for details. 294 295 // Output: 296 // taskCounter registered. 297 // 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 298 // taskCounter unregistered. 299 // 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 300 // taskCounterVec registered. 301 // Worker initialization failed: inconsistent label cardinality: expected 1 label values but got 2 in []string{"42", "spurious arg"} 302 // notMyCounter is nil. 303 // taskCounterForWorker42 registered. 304 // taskCounterForWorker2001 registered. 305} 306 307func ExampleSummary() { 308 temps := prometheus.NewSummary(prometheus.SummaryOpts{ 309 Name: "pond_temperature_celsius", 310 Help: "The temperature of the frog pond.", 311 Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, 312 }) 313 314 // Simulate some observations. 315 for i := 0; i < 1000; i++ { 316 temps.Observe(30 + math.Floor(120*math.Sin(float64(i)*0.1))/10) 317 } 318 319 // Just for demonstration, let's check the state of the summary by 320 // (ab)using its Write method (which is usually only used by Prometheus 321 // internally). 322 metric := &dto.Metric{} 323 temps.Write(metric) 324 fmt.Println(proto.MarshalTextString(metric)) 325 326 // Output: 327 // summary: < 328 // sample_count: 1000 329 // sample_sum: 29969.50000000001 330 // quantile: < 331 // quantile: 0.5 332 // value: 31.1 333 // > 334 // quantile: < 335 // quantile: 0.9 336 // value: 41.3 337 // > 338 // quantile: < 339 // quantile: 0.99 340 // value: 41.9 341 // > 342 // > 343} 344 345func ExampleSummaryVec() { 346 temps := prometheus.NewSummaryVec( 347 prometheus.SummaryOpts{ 348 Name: "pond_temperature_celsius", 349 Help: "The temperature of the frog pond.", 350 Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, 351 }, 352 []string{"species"}, 353 ) 354 355 // Simulate some observations. 356 for i := 0; i < 1000; i++ { 357 temps.WithLabelValues("litoria-caerulea").Observe(30 + math.Floor(120*math.Sin(float64(i)*0.1))/10) 358 temps.WithLabelValues("lithobates-catesbeianus").Observe(32 + math.Floor(100*math.Cos(float64(i)*0.11))/10) 359 } 360 361 // Create a Summary without any observations. 362 temps.WithLabelValues("leiopelma-hochstetteri") 363 364 // Just for demonstration, let's check the state of the summary vector 365 // by registering it with a custom registry and then let it collect the 366 // metrics. 367 reg := prometheus.NewRegistry() 368 reg.MustRegister(temps) 369 370 metricFamilies, err := reg.Gather() 371 if err != nil || len(metricFamilies) != 1 { 372 panic("unexpected behavior of custom test registry") 373 } 374 fmt.Println(proto.MarshalTextString(metricFamilies[0])) 375 376 // Output: 377 // name: "pond_temperature_celsius" 378 // help: "The temperature of the frog pond." 379 // type: SUMMARY 380 // metric: < 381 // label: < 382 // name: "species" 383 // value: "leiopelma-hochstetteri" 384 // > 385 // summary: < 386 // sample_count: 0 387 // sample_sum: 0 388 // quantile: < 389 // quantile: 0.5 390 // value: nan 391 // > 392 // quantile: < 393 // quantile: 0.9 394 // value: nan 395 // > 396 // quantile: < 397 // quantile: 0.99 398 // value: nan 399 // > 400 // > 401 // > 402 // metric: < 403 // label: < 404 // name: "species" 405 // value: "lithobates-catesbeianus" 406 // > 407 // summary: < 408 // sample_count: 1000 409 // sample_sum: 31956.100000000017 410 // quantile: < 411 // quantile: 0.5 412 // value: 32.4 413 // > 414 // quantile: < 415 // quantile: 0.9 416 // value: 41.4 417 // > 418 // quantile: < 419 // quantile: 0.99 420 // value: 41.9 421 // > 422 // > 423 // > 424 // metric: < 425 // label: < 426 // name: "species" 427 // value: "litoria-caerulea" 428 // > 429 // summary: < 430 // sample_count: 1000 431 // sample_sum: 29969.50000000001 432 // quantile: < 433 // quantile: 0.5 434 // value: 31.1 435 // > 436 // quantile: < 437 // quantile: 0.9 438 // value: 41.3 439 // > 440 // quantile: < 441 // quantile: 0.99 442 // value: 41.9 443 // > 444 // > 445 // > 446} 447 448func ExampleNewConstSummary() { 449 desc := prometheus.NewDesc( 450 "http_request_duration_seconds", 451 "A summary of the HTTP request durations.", 452 []string{"code", "method"}, 453 prometheus.Labels{"owner": "example"}, 454 ) 455 456 // Create a constant summary from values we got from a 3rd party telemetry system. 457 s := prometheus.MustNewConstSummary( 458 desc, 459 4711, 403.34, 460 map[float64]float64{0.5: 42.3, 0.9: 323.3}, 461 "200", "get", 462 ) 463 464 // Just for demonstration, let's check the state of the summary by 465 // (ab)using its Write method (which is usually only used by Prometheus 466 // internally). 467 metric := &dto.Metric{} 468 s.Write(metric) 469 fmt.Println(proto.MarshalTextString(metric)) 470 471 // Output: 472 // label: < 473 // name: "code" 474 // value: "200" 475 // > 476 // label: < 477 // name: "method" 478 // value: "get" 479 // > 480 // label: < 481 // name: "owner" 482 // value: "example" 483 // > 484 // summary: < 485 // sample_count: 4711 486 // sample_sum: 403.34 487 // quantile: < 488 // quantile: 0.5 489 // value: 42.3 490 // > 491 // quantile: < 492 // quantile: 0.9 493 // value: 323.3 494 // > 495 // > 496} 497 498func ExampleHistogram() { 499 temps := prometheus.NewHistogram(prometheus.HistogramOpts{ 500 Name: "pond_temperature_celsius", 501 Help: "The temperature of the frog pond.", // Sorry, we can't measure how badly it smells. 502 Buckets: prometheus.LinearBuckets(20, 5, 5), // 5 buckets, each 5 centigrade wide. 503 }) 504 505 // Simulate some observations. 506 for i := 0; i < 1000; i++ { 507 temps.Observe(30 + math.Floor(120*math.Sin(float64(i)*0.1))/10) 508 } 509 510 // Just for demonstration, let's check the state of the histogram by 511 // (ab)using its Write method (which is usually only used by Prometheus 512 // internally). 513 metric := &dto.Metric{} 514 temps.Write(metric) 515 fmt.Println(proto.MarshalTextString(metric)) 516 517 // Output: 518 // histogram: < 519 // sample_count: 1000 520 // sample_sum: 29969.50000000001 521 // bucket: < 522 // cumulative_count: 192 523 // upper_bound: 20 524 // > 525 // bucket: < 526 // cumulative_count: 366 527 // upper_bound: 25 528 // > 529 // bucket: < 530 // cumulative_count: 501 531 // upper_bound: 30 532 // > 533 // bucket: < 534 // cumulative_count: 638 535 // upper_bound: 35 536 // > 537 // bucket: < 538 // cumulative_count: 816 539 // upper_bound: 40 540 // > 541 // > 542} 543 544func ExampleNewConstHistogram() { 545 desc := prometheus.NewDesc( 546 "http_request_duration_seconds", 547 "A histogram of the HTTP request durations.", 548 []string{"code", "method"}, 549 prometheus.Labels{"owner": "example"}, 550 ) 551 552 // Create a constant histogram from values we got from a 3rd party telemetry system. 553 h := prometheus.MustNewConstHistogram( 554 desc, 555 4711, 403.34, 556 map[float64]uint64{25: 121, 50: 2403, 100: 3221, 200: 4233}, 557 "200", "get", 558 ) 559 560 // Just for demonstration, let's check the state of the histogram by 561 // (ab)using its Write method (which is usually only used by Prometheus 562 // internally). 563 metric := &dto.Metric{} 564 h.Write(metric) 565 fmt.Println(proto.MarshalTextString(metric)) 566 567 // Output: 568 // label: < 569 // name: "code" 570 // value: "200" 571 // > 572 // label: < 573 // name: "method" 574 // value: "get" 575 // > 576 // label: < 577 // name: "owner" 578 // value: "example" 579 // > 580 // histogram: < 581 // sample_count: 4711 582 // sample_sum: 403.34 583 // bucket: < 584 // cumulative_count: 121 585 // upper_bound: 25 586 // > 587 // bucket: < 588 // cumulative_count: 2403 589 // upper_bound: 50 590 // > 591 // bucket: < 592 // cumulative_count: 3221 593 // upper_bound: 100 594 // > 595 // bucket: < 596 // cumulative_count: 4233 597 // upper_bound: 200 598 // > 599 // > 600} 601 602func ExampleAlreadyRegisteredError() { 603 reqCounter := prometheus.NewCounter(prometheus.CounterOpts{ 604 Name: "requests_total", 605 Help: "The total number of requests served.", 606 }) 607 if err := prometheus.Register(reqCounter); err != nil { 608 if are, ok := err.(prometheus.AlreadyRegisteredError); ok { 609 // A counter for that metric has been registered before. 610 // Use the old counter from now on. 611 reqCounter = are.ExistingCollector.(prometheus.Counter) 612 } else { 613 // Something else went wrong! 614 panic(err) 615 } 616 } 617 reqCounter.Inc() 618} 619 620func ExampleGatherers() { 621 reg := prometheus.NewRegistry() 622 temp := prometheus.NewGaugeVec( 623 prometheus.GaugeOpts{ 624 Name: "temperature_kelvin", 625 Help: "Temperature in Kelvin.", 626 }, 627 []string{"location"}, 628 ) 629 reg.MustRegister(temp) 630 temp.WithLabelValues("outside").Set(273.14) 631 temp.WithLabelValues("inside").Set(298.44) 632 633 var parser expfmt.TextParser 634 635 text := ` 636# TYPE humidity_percent gauge 637# HELP humidity_percent Humidity in %. 638humidity_percent{location="outside"} 45.4 639humidity_percent{location="inside"} 33.2 640# TYPE temperature_kelvin gauge 641# HELP temperature_kelvin Temperature in Kelvin. 642temperature_kelvin{location="somewhere else"} 4.5 643` 644 645 parseText := func() ([]*dto.MetricFamily, error) { 646 parsed, err := parser.TextToMetricFamilies(strings.NewReader(text)) 647 if err != nil { 648 return nil, err 649 } 650 var result []*dto.MetricFamily 651 for _, mf := range parsed { 652 result = append(result, mf) 653 } 654 return result, nil 655 } 656 657 gatherers := prometheus.Gatherers{ 658 reg, 659 prometheus.GathererFunc(parseText), 660 } 661 662 gathering, err := gatherers.Gather() 663 if err != nil { 664 fmt.Println(err) 665 } 666 667 out := &bytes.Buffer{} 668 for _, mf := range gathering { 669 if _, err := expfmt.MetricFamilyToText(out, mf); err != nil { 670 panic(err) 671 } 672 } 673 fmt.Print(out.String()) 674 fmt.Println("----------") 675 676 // Note how the temperature_kelvin metric family has been merged from 677 // different sources. Now try 678 text = ` 679# TYPE humidity_percent gauge 680# HELP humidity_percent Humidity in %. 681humidity_percent{location="outside"} 45.4 682humidity_percent{location="inside"} 33.2 683# TYPE temperature_kelvin gauge 684# HELP temperature_kelvin Temperature in Kelvin. 685# Duplicate metric: 686temperature_kelvin{location="outside"} 265.3 687 # Missing location label (note that this is undesirable but valid): 688temperature_kelvin 4.5 689` 690 691 gathering, err = gatherers.Gather() 692 if err != nil { 693 fmt.Println(err) 694 } 695 // Note that still as many metrics as possible are returned: 696 out.Reset() 697 for _, mf := range gathering { 698 if _, err := expfmt.MetricFamilyToText(out, mf); err != nil { 699 panic(err) 700 } 701 } 702 fmt.Print(out.String()) 703 704 // Output: 705 // # HELP humidity_percent Humidity in %. 706 // # TYPE humidity_percent gauge 707 // humidity_percent{location="inside"} 33.2 708 // humidity_percent{location="outside"} 45.4 709 // # HELP temperature_kelvin Temperature in Kelvin. 710 // # TYPE temperature_kelvin gauge 711 // temperature_kelvin{location="inside"} 298.44 712 // temperature_kelvin{location="outside"} 273.14 713 // temperature_kelvin{location="somewhere else"} 4.5 714 // ---------- 715 // collected metric "temperature_kelvin" { label:<name:"location" value:"outside" > gauge:<value:265.3 > } was collected before with the same name and label values 716 // # HELP humidity_percent Humidity in %. 717 // # TYPE humidity_percent gauge 718 // humidity_percent{location="inside"} 33.2 719 // humidity_percent{location="outside"} 45.4 720 // # HELP temperature_kelvin Temperature in Kelvin. 721 // # TYPE temperature_kelvin gauge 722 // temperature_kelvin 4.5 723 // temperature_kelvin{location="inside"} 298.44 724 // temperature_kelvin{location="outside"} 273.14 725} 726 727func ExampleNewMetricWithTimestamp() { 728 desc := prometheus.NewDesc( 729 "temperature_kelvin", 730 "Current temperature in Kelvin.", 731 nil, nil, 732 ) 733 734 // Create a constant gauge from values we got from an external 735 // temperature reporting system. Those values are reported with a slight 736 // delay, so we want to add the timestamp of the actual measurement. 737 temperatureReportedByExternalSystem := 298.15 738 timeReportedByExternalSystem := time.Date(2009, time.November, 10, 23, 0, 0, 12345678, time.UTC) 739 s := prometheus.NewMetricWithTimestamp( 740 timeReportedByExternalSystem, 741 prometheus.MustNewConstMetric( 742 desc, prometheus.GaugeValue, temperatureReportedByExternalSystem, 743 ), 744 ) 745 746 // Just for demonstration, let's check the state of the gauge by 747 // (ab)using its Write method (which is usually only used by Prometheus 748 // internally). 749 metric := &dto.Metric{} 750 s.Write(metric) 751 fmt.Println(proto.MarshalTextString(metric)) 752 753 // Output: 754 // gauge: < 755 // value: 298.15 756 // > 757 // timestamp_ms: 1257894000012 758} 759