diff --git a/cmd/server/handler.go b/cmd/server/handler.go index 8623a122f60..bbb80c48460 100644 --- a/cmd/server/handler.go +++ b/cmd/server/handler.go @@ -102,14 +102,16 @@ func RunHost(c *config.Config) func(cmd *cobra.Command, args []string) { n := negroni.New() - metrics := c.GetMetrics() + telemetryMetrics := c.GetTelemetryMetrics() if ok, _ := cmd.Flags().GetBool("disable-telemetry"); !ok && os.Getenv("DISABLE_TELEMETRY") != "1" { - go metrics.RegisterSegment(c.BuildVersion, c.BuildHash, c.BuildTime) - go metrics.CommitTelemetry() - go metrics.TickKeepAlive() - n.Use(metrics) + go telemetryMetrics.RegisterSegment(c.BuildVersion, c.BuildHash, c.BuildTime) + go telemetryMetrics.CommitTelemetry() + go telemetryMetrics.TickKeepAlive() + n.Use(telemetryMetrics) } + n.Use(c.GetPrometheusMetrics()) + n.Use(negronilogrus.NewMiddlewareFromLogger(logger, c.Issuer)) n.UseFunc(serverHandler.rejectInsecureRequests) n.UseHandler(router) diff --git a/cmd/server/handler_health_factory.go b/cmd/server/handler_health_factory.go index 70bbed37622..ff6403a2807 100644 --- a/cmd/server/handler_health_factory.go +++ b/cmd/server/handler_health_factory.go @@ -23,7 +23,7 @@ import ( func newHealthHandler(c *config.Config, router *httprouter.Router) *health.Handler { h := &health.Handler{ - Metrics: c.GetMetrics(), + Metrics: c.GetTelemetryMetrics(), H: herodot.NewJSONWriter(c.GetLogger()), W: c.Context().Warden, ResourcePrefix: c.AccessControlResourcePrefix, diff --git a/config/config.go b/config/config.go index 673f6b757b9..2c8cc6a8abb 100644 --- a/config/config.go +++ b/config/config.go @@ -26,7 +26,6 @@ import ( "os" "strings" "time" - "github.com/ory/ladon" lmem "github.com/ory/ladon/manager/memory" lsql "github.com/ory/ladon/manager/sql" @@ -38,11 +37,12 @@ import ( foauth2 "github.com/spotxchange/fosite/handler/oauth2" "github.com/spotxchange/fosite/token/hmac" "github.com/spotxchange/hydra/health" - "github.com/spotxchange/hydra/metrics" - "github.com/spotxchange/hydra/pkg" + "github.com/spotxchange/hydra/metrics/telemetry" + "github.com/spotxchange/hydra/metrics/prometheus" "github.com/spotxchange/hydra/warden/group" - "golang.org/x/oauth2" + "github.com/spotxchange/hydra/pkg" "golang.org/x/oauth2/clientcredentials" + "golang.org/x/oauth2" "gopkg.in/yaml.v2" ) @@ -81,7 +81,8 @@ type Config struct { BuildHash string `yaml:"-"` BuildTime string `yaml:"-"` logger *logrus.Logger `yaml:"-"` - metrics *metrics.MetricsManager `yaml:"-"` + telemetry *telemetry.MetricsManager `yaml:"-"` + prometheus *prometheus.MetricsManager `yaml:"-"` cluster *url.URL `yaml:"-"` oauth2Client *http.Client `yaml:"-"` context *Context `yaml:"-"` @@ -143,12 +144,21 @@ func (c *Config) GetLogger() *logrus.Logger { return c.logger } -func (c *Config) GetMetrics() *metrics.MetricsManager { - if c.metrics == nil { - c.metrics = metrics.NewMetricsManager(c.Issuer, c.DatabaseURL, c.GetLogger()) +func (c *Config) GetTelemetryMetrics() *telemetry.MetricsManager { + if c.telemetry == nil { + c.telemetry = telemetry.NewMetricsManager(c.Issuer, c.DatabaseURL, c.GetLogger()) + } + + return c.telemetry +} + +func (c *Config) GetPrometheusMetrics() *prometheus.MetricsManager { + if c.prometheus == nil { + c.GetLogger().Info("Setting up Prometheus metrics") + c.prometheus = prometheus.NewMetricsManager(c.BuildVersion, c.BuildHash, c.BuildTime) } - return c.metrics + return c.prometheus } func (c *Config) DoesRequestSatisfyTermination(r *http.Request) error { diff --git a/health/handler.go b/health/handler.go index fe4db78b880..05ce5d1b6b0 100644 --- a/health/handler.go +++ b/health/handler.go @@ -20,16 +20,18 @@ import ( "github.com/julienschmidt/httprouter" "github.com/ory/herodot" "github.com/spotxchange/hydra/firewall" - "github.com/spotxchange/hydra/metrics" + "github.com/spotxchange/hydra/metrics/telemetry" + "github.com/prometheus/client_golang/prometheus/promhttp" ) const ( HealthStatusPath = "/health/status" HealthMetricsPath = "/health/metrics" + PrometheusStatusPath = "/health/prometheus" ) type Handler struct { - Metrics *metrics.MetricsManager + Metrics *telemetry.MetricsManager H *herodot.JSONWriter W firewall.Firewall ResourcePrefix string @@ -50,6 +52,7 @@ func (h *Handler) PrefixResource(resource string) string { func (h *Handler) SetRoutes(r *httprouter.Router) { r.GET(HealthStatusPath, h.Health) r.GET(HealthMetricsPath, h.Statistics) + r.Handler("GET", PrometheusStatusPath, promhttp.Handler()) } // swagger:route GET /health/status health getInstanceStatus diff --git a/metrics/prometheus/metrics.go b/metrics/prometheus/metrics.go new file mode 100644 index 00000000000..0df3f1bd46b --- /dev/null +++ b/metrics/prometheus/metrics.go @@ -0,0 +1,42 @@ +package prometheus + +// Metrics prototypes +// Example: +// Counter *prometheus.CounterVec +// ResponseTime *prometheus.HistogramVec +type Metrics struct{} + +// Method for creation new custom Prometheus metrics +// Example: +// pm := &Metrics{ +// Counter: prometheus.NewCounterVec( +// prometheus.CounterOpts{ +// Name: "servicename_requests_total", +// Help: "Description", +// ConstLabels: map[string]string{ +// "version": version, +// "hash": hash, +// "buildTime": buildTime, +// }, +// }, +// []string{"endpoint"}, +// ), +// ResponseTime: prometheus.NewHistogramVec( +// prometheus.HistogramOpts{ +// Name: "servicename_response_time_seconds", +// Help: "Description", +// ConstLabels: map[string]string{ +// "version": version, +// "hash": hash, +// "buildTime": buildTime, +// }, +// }, +// []string{"endpoint"}, +// ), +// } +// prometheus.Register(pm.Counter) +// prometheus.Register(pm.ResponseTime) +func NewMetrics(version, hash, buildTime string) *Metrics { + pm := &Metrics{} + return pm +} \ No newline at end of file diff --git a/metrics/prometheus/middleware.go b/metrics/prometheus/middleware.go new file mode 100644 index 00000000000..773c2e9339a --- /dev/null +++ b/metrics/prometheus/middleware.go @@ -0,0 +1,27 @@ +package prometheus + +import ( + "net/http" +) + +type MetricsManager struct { + prometheusMetrics *Metrics +} + +func NewMetricsManager(version, hash, buildTime string) *MetricsManager { + return &MetricsManager{ + prometheusMetrics: NewMetrics(version, hash, buildTime), + } +} + +// Main middleware method to collect metrics for Prometheus. +// Example: +// start := time.Now() +// next(rw, r) +// Request counter metric +// pmm.prometheusMetrics.Counter.WithLabelValues(r.URL.Path).Inc() +// Response time metric +// pmm.prometheusMetrics.ResponseTime.WithLabelValues(r.URL.Path).Observe(time.Since(start).Seconds()) +func (pmm *MetricsManager) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { + next(rw, r) +} \ No newline at end of file diff --git a/metrics/metrics.go b/metrics/telemetry/metrics.go similarity index 99% rename from metrics/metrics.go rename to metrics/telemetry/metrics.go index 1a519b4eebf..47f2e900300 100644 --- a/metrics/metrics.go +++ b/metrics/telemetry/metrics.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package metrics +package telemetry import ( "runtime" diff --git a/metrics/middleware.go b/metrics/telemetry/middleware.go similarity index 99% rename from metrics/middleware.go rename to metrics/telemetry/middleware.go index 272a90eb08a..7155c8921b8 100644 --- a/metrics/middleware.go +++ b/metrics/telemetry/middleware.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package metrics +package telemetry import ( "net/http" diff --git a/metrics/middleware_test.go b/metrics/telemetry/middleware_test.go similarity index 99% rename from metrics/middleware_test.go rename to metrics/telemetry/middleware_test.go index 58769bd8e2b..db109a1c067 100644 --- a/metrics/middleware_test.go +++ b/metrics/telemetry/middleware_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package metrics_test +package telemetry import ( "fmt" diff --git a/oauth2/handler.go b/oauth2/handler.go index be7f250ddb2..af4dd8318d9 100644 --- a/oauth2/handler.go +++ b/oauth2/handler.go @@ -446,13 +446,17 @@ func (h *Handler) TokenHandler(w http.ResponseWriter, r *http.Request, _ httprou return } - if ar, ok := accessResponse.(*fosite.AccessResponse); !ok || r.URL.Query().Get("odata") == "disabled" { - h.OAuth2.WriteAccessResponse(w, accessRequest, accessResponse) - } else { + if ar, ok := accessResponse.(*fosite.AccessResponse); ok && h.isODataEnabled(r) { h.OAuth2.WriteAccessResponse(w, accessRequest, &oDataAccessResponse{ar}) + } else { + h.OAuth2.WriteAccessResponse(w, accessRequest, accessResponse) } } +func (h *Handler) isODataEnabled(r *http.Request) bool { + return r.Header.Get("x-odata-response") == "1" +} + // swagger:route GET /oauth2/auth oAuth2 oauthAuth // // The OAuth 2.0 authorize endpoint