first commit
This commit is contained in:
commit
c284a2c0ab
65 changed files with 7138 additions and 0 deletions
115
monitoring/monitoring.go
Normal file
115
monitoring/monitoring.go
Normal file
|
@ -0,0 +1,115 @@
|
|||
package monitoring
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
newrelic "github.com/newrelic/go-agent"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/smallstep/ca-component/logging"
|
||||
)
|
||||
|
||||
// Middleware is a function returns another http.Handler that wraps the given
|
||||
// handler.
|
||||
type Middleware func(next http.Handler) http.Handler
|
||||
|
||||
// Monitoring is the type holding a middleware that traces the request to an
|
||||
// application.
|
||||
type Monitoring struct {
|
||||
middleware Middleware
|
||||
}
|
||||
|
||||
// monitoring config represents the JSON attributes used for configuration. At
|
||||
// this moment only fields for NewRelic are supported.
|
||||
type monitoringConfig struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
// New initializes the monitoring with the given configuration.
|
||||
// Right now it only supports newrelic as the monitoring backend.
|
||||
func New(raw json.RawMessage) (*Monitoring, error) {
|
||||
var config monitoringConfig
|
||||
if err := json.Unmarshal(raw, &config); err != nil {
|
||||
return nil, errors.Wrap(err, "error unmarshalling monitoring attribute")
|
||||
}
|
||||
|
||||
m := new(Monitoring)
|
||||
switch strings.ToLower(config.Type) {
|
||||
case "", "newrelic":
|
||||
app, err := newrelic.NewApplication(newrelic.NewConfig(config.Name, config.Key))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error loading New Relic application")
|
||||
}
|
||||
m.middleware = newRelicMiddleware(app)
|
||||
default:
|
||||
return nil, errors.Errorf("unsupported monitoring.type '%s'", config.Type)
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Middleware is an HTTP middleware that traces the request with the configured
|
||||
// monitoring backednd.
|
||||
func (m *Monitoring) Middleware(next http.Handler) http.Handler {
|
||||
return m.middleware(next)
|
||||
}
|
||||
|
||||
func newRelicMiddleware(app newrelic.Application) Middleware {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Start transaction
|
||||
txn := app.StartTransaction(transactionName(r), w, r)
|
||||
defer txn.End()
|
||||
|
||||
// Wrap request writer if necessary
|
||||
rw := logging.NewResponseLogger(w)
|
||||
|
||||
// Call next handler
|
||||
next.ServeHTTP(rw, r)
|
||||
|
||||
// Report status (using same key NewRelic uses by default)
|
||||
status := rw.StatusCode()
|
||||
txn.AddAttribute("httpResponseCode", strconv.Itoa(status))
|
||||
|
||||
// Add custom attributes
|
||||
if v, ok := logging.GetRequestID(r.Context()); ok {
|
||||
txn.AddAttribute("request.id", v)
|
||||
}
|
||||
|
||||
// Report errors if necessary
|
||||
if status >= http.StatusBadRequest {
|
||||
var errorNoticed bool
|
||||
if fields := rw.Fields(); fields != nil {
|
||||
if v, ok := fields["error"]; ok {
|
||||
if err, ok := v.(error); ok {
|
||||
txn.NoticeError(err)
|
||||
errorNoticed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if !errorNoticed {
|
||||
txn.NoticeError(fmt.Errorf("request failed with status code %d", status))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func transactionName(r *http.Request) string {
|
||||
// From https://github.com/gorilla/handlers
|
||||
uri := r.RequestURI
|
||||
// Requests using the CONNECT method over HTTP/2.0 must use
|
||||
// the authority field (aka r.Host) to identify the target.
|
||||
// Refer: https://httpwg.github.io/specs/rfc7540.html#CONNECT
|
||||
if r.ProtoMajor == 2 && r.Method == "CONNECT" {
|
||||
uri = r.Host
|
||||
}
|
||||
if uri == "" {
|
||||
uri = r.URL.RequestURI()
|
||||
}
|
||||
return uri
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue