mirror of
https://github.com/jakejarvis/npqueue.git
synced 2025-04-26 06:55:21 -04:00
heroku metrics
This commit is contained in:
parent
d57e8d0afb
commit
5868d58832
0
.gitignore
vendored
Normal file
0
.gitignore
vendored
Normal file
13
Godeps/Godeps.json
generated
13
Godeps/Godeps.json
generated
@ -5,5 +5,16 @@
|
||||
"Packages": [
|
||||
"./..."
|
||||
],
|
||||
"Deps": []
|
||||
"Deps": [
|
||||
{
|
||||
"ImportPath": "github.com/heroku/x/hmetrics",
|
||||
"Comment": "v0.0.1-14-g6da54e7",
|
||||
"Rev": "6da54e7f9d01cfd8f5f316cb4c6ae5af36db5adc"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/heroku/x/hmetrics/onload",
|
||||
"Comment": "v0.0.1-14-g6da54e7",
|
||||
"Rev": "6da54e7f9d01cfd8f5f316cb4c6ae5af36db5adc"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
2
main.go
2
main.go
@ -12,6 +12,8 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
_ "github.com/heroku/x/hmetrics/onload"
|
||||
)
|
||||
|
||||
type ServerDetailsStruct struct {
|
||||
|
27
vendor/github.com/heroku/x/LICENSE.txt
generated
vendored
Normal file
27
vendor/github.com/heroku/x/LICENSE.txt
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
Copyright (c) 2018, Salesforce.com, Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
* Neither the name of Salesforce.com nor the names of its contributors may be
|
||||
used to endorse or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
217
vendor/github.com/heroku/x/hmetrics/hmetrics.go
generated
vendored
Normal file
217
vendor/github.com/heroku/x/hmetrics/hmetrics.go
generated
vendored
Normal file
@ -0,0 +1,217 @@
|
||||
/* Copyright (c) 2018 Salesforce
|
||||
* All rights reserved.
|
||||
* Licensed under the BSD 3-Clause license.
|
||||
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
Package hmetrics is a self-contained client for Heroku Go runtime metrics.
|
||||
|
||||
Typical usage is through the `github.com/heroku/x/hmetrics/onload` package
|
||||
imported like so:
|
||||
|
||||
import _ "github.com/heroku/x/hmetrics/onload"
|
||||
|
||||
You can find more information about Heroku Go runtime metrics here:
|
||||
https://devcenter.heroku.com/articles/language-runtime-metrics-go
|
||||
*/
|
||||
package hmetrics
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
interval = 20 * time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultEndpoint to report metrics to Heroku. The "HEROKU_METRICS_URL" env
|
||||
// var is set by the Heroku runtime when the `runtime-heroku-metrics` labs
|
||||
// flag is enabled. For more info see:
|
||||
// https://devcenter.heroku.com/articles/language-runtime-metrics
|
||||
//
|
||||
// DefaultEndpoint must be changed before Report is called.
|
||||
DefaultEndpoint = os.Getenv("HEROKU_METRICS_URL")
|
||||
)
|
||||
|
||||
// AlreadyStarted represents an Error condition of already being started.
|
||||
type AlreadyStarted struct{}
|
||||
|
||||
func (as AlreadyStarted) Error() string {
|
||||
return "already started"
|
||||
}
|
||||
|
||||
func (as AlreadyStarted) Fatal() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// HerokuMetricsURLUnset represents the Error condition when the
|
||||
// HEROKU_METRICS_URL environment variables is unset or an empty string.
|
||||
type HerokuMetricsURLUnset struct{}
|
||||
|
||||
func (e HerokuMetricsURLUnset) Error() string {
|
||||
return "cannot report metrics because HEROKU_METRICS_URL is unset"
|
||||
}
|
||||
|
||||
func (e HerokuMetricsURLUnset) Fatal() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// ErrHandler funcations are used to provide custom processing/handling of
|
||||
// errors encountered during the collection or reporting of metrics to Heroku.
|
||||
type ErrHandler func(err error) error
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
started bool
|
||||
)
|
||||
|
||||
// Report go metrics to the endpoint until the context is canceled.
|
||||
//
|
||||
// Only one call to the ErrHandler will happen at a time. Metrics can be dropped
|
||||
// or delayed if a call to ErrHandler takes longer than the reprting interval.
|
||||
// Processing of metrics continues if the ErrHandler returns nil, but aborts if
|
||||
// the ErrHandler itself returns an error. It is safe to pass a nil ErrHandler.
|
||||
//
|
||||
// Report is safe for concurrent usage, but calling it again w/o canceling the
|
||||
// context passed previously returns an AlreadyStarted error. This is to ensure
|
||||
// that metrics aren't duplicated. Report can be called again to restart
|
||||
// reporting after the context is canceled.
|
||||
func Report(ctx context.Context, endpoint string, ef ErrHandler) error {
|
||||
if err := startable(endpoint); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
report(ctx, &http.Client{Timeout: 20 * time.Second}, endpoint, errorHandler(ef))
|
||||
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
started = false
|
||||
return nil
|
||||
}
|
||||
|
||||
// err if not startable, otherwise start
|
||||
func startable(endpoint string) error {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if started {
|
||||
return AlreadyStarted{}
|
||||
}
|
||||
if endpoint == "" {
|
||||
return &url.Error{
|
||||
Op: "Empty",
|
||||
URL: endpoint,
|
||||
Err: errors.New("Empty string"),
|
||||
}
|
||||
}
|
||||
if _, err := url.Parse(endpoint); err != nil {
|
||||
return err
|
||||
}
|
||||
started = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func noopErrorHandler(_ error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func errorHandler(ef ErrHandler) ErrHandler {
|
||||
if ef == nil {
|
||||
return noopErrorHandler
|
||||
}
|
||||
return ef
|
||||
}
|
||||
|
||||
func report(ctx context.Context, client *http.Client, endpoint string, ef ErrHandler) {
|
||||
t := time.NewTicker(interval)
|
||||
defer t.Stop()
|
||||
|
||||
var buf bytes.Buffer
|
||||
var pauseTotalNS uint64
|
||||
var numGC uint32
|
||||
for {
|
||||
select {
|
||||
case <-t.C:
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
|
||||
var err error
|
||||
pauseTotalNS, numGC, err = gatherMetrics(&buf, pauseTotalNS, numGC)
|
||||
if err != nil {
|
||||
if err := ef(err); err != nil {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err := submitMetrics(ctx, client, &buf, endpoint); err != nil {
|
||||
if err := ef(err); err != nil {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// gatherMetrics and write the JSON encoded representation to w.
|
||||
// returns the sampled PauseTotalNs+NumGC & any encoding errors.
|
||||
// TODO: If we ever have high frequency charts HeapIdle minus HeapReleased could be interesting.
|
||||
func gatherMetrics(w io.Writer, prevPauseTotalNS uint64, prevNumGC uint32) (uint64, uint32, error) {
|
||||
var stats runtime.MemStats
|
||||
runtime.ReadMemStats(&stats)
|
||||
|
||||
// cribbed from https://github.com/codahale/metrics/blob/master/runtime/memstats.go
|
||||
result := struct {
|
||||
Counters map[string]float64 `json:"counters"`
|
||||
Gauges map[string]float64 `json:"gauges"`
|
||||
}{
|
||||
Counters: map[string]float64{
|
||||
"go.gc.collections": float64(stats.NumGC - prevNumGC),
|
||||
"go.gc.pause.ns": float64(stats.PauseTotalNs - prevPauseTotalNS),
|
||||
},
|
||||
Gauges: map[string]float64{
|
||||
"go.memory.heap.bytes": float64(stats.Alloc),
|
||||
"go.memory.stack.bytes": float64(stats.StackInuse),
|
||||
"go.memory.heap.objects": float64(stats.Mallocs - stats.Frees), // Number of "live" objects.
|
||||
"go.gc.goal": float64(stats.NextGC), // Goal heap size for next GC.
|
||||
"go.routines": float64(runtime.NumGoroutine()), // Current number of goroutines.
|
||||
},
|
||||
}
|
||||
|
||||
return stats.PauseTotalNs, stats.NumGC, json.NewEncoder(w).Encode(result)
|
||||
}
|
||||
|
||||
// submitMetrics read from r to the endpoint using the provided client
|
||||
func submitMetrics(ctx context.Context, client *http.Client, r io.Reader, endpoint string) error {
|
||||
req, err := http.NewRequest("POST", endpoint, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
|
||||
resp, err := client.Do(req.WithContext(ctx))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("expected %v (http.StatusOK) but got %s", http.StatusOK, resp.Status)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
71
vendor/github.com/heroku/x/hmetrics/onload/init.go
generated
vendored
Normal file
71
vendor/github.com/heroku/x/hmetrics/onload/init.go
generated
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
/* Copyright (c) 2018 Salesforce
|
||||
* All rights reserved.
|
||||
* Licensed under the BSD 3-Clause license.
|
||||
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
Package onload automatically starts hmetrics reporting, ignoring errors and
|
||||
retrying reporting, backing off in 10 second increments.
|
||||
|
||||
Use this package when you don't care about stopping reporting, specifying the
|
||||
endpoint, or being notified of any reporting errors.
|
||||
|
||||
This package will log all reporting errors via the stdlib log package if the
|
||||
environment variable HMETRICS_VERBOSE is set to "1", or any other true-like
|
||||
value as defined by strconv#ParseBool (see $ godoc strconv for more
|
||||
information).
|
||||
|
||||
usage:
|
||||
|
||||
import (
|
||||
_ "github.com/heroku/x/hmetrics/onload"
|
||||
)
|
||||
|
||||
See the package documentation at https://godoc.org/github.com/heroku/x/hmetrics
|
||||
for more info about Heroku Go metrics and advanced usage.
|
||||
|
||||
*/
|
||||
package onload
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/heroku/x/hmetrics"
|
||||
)
|
||||
|
||||
const (
|
||||
interval = 10
|
||||
)
|
||||
|
||||
func init() {
|
||||
var logger hmetrics.ErrHandler = func(_ error) error { return nil }
|
||||
|
||||
val := os.Getenv("HMETRICS_VERBOSE")
|
||||
should, err := strconv.ParseBool(val)
|
||||
if err == nil && should {
|
||||
logger = func(err error) error {
|
||||
log.Printf("[hmetrics] error: %v", err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
go func() {
|
||||
var backoff int64
|
||||
for backoff = 1; ; backoff++ {
|
||||
start := time.Now()
|
||||
|
||||
err = hmetrics.Report(context.Background(), hmetrics.DefaultEndpoint, logger)
|
||||
if time.Since(start) > 5*time.Minute {
|
||||
backoff = 1
|
||||
}
|
||||
logger(err)
|
||||
|
||||
time.Sleep(time.Duration(backoff*interval) * time.Second)
|
||||
}
|
||||
}()
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user