Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prometheus metrics #1381

Closed
wants to merge 18 commits into from
Closed

Prometheus metrics #1381

wants to merge 18 commits into from

Conversation

kminehart
Copy link
Contributor

Related issue

#1176

Proposed changes

Add custom Prometheus metrics and HTTP middlewares for request count, duration, and size.

Checklist

  • I have read the contributing guidelines
  • I confirm that this pull request does not address a security vulnerability. If this pull request addresses a security
    vulnerability, I confirm that I got green light (please contact hi@ory.sh) from the maintainers to push the changes.
  • I signed the Developer's Certificate of Origin
    by signing my commit(s). You can amend your signature to the most recent commit by using git commit --amend -s. If you
    amend the commit, you might need to force push using git push --force HEAD:<branch>. Please be very careful when using
    force push.
  • I have added tests that prove my fix is effective or that my feature works
  • I have added necessary documentation within the code base (if appropriate)
  • I have documented my changes in the developer guide (if appropriate)

Further comments

This is still a work in progress. Wanted to make sure that I was heading in the right direction code quality-wise. :)

Signed-off-by: Kevin Minehart <kmineh0151@gmail.com>
Signed-off-by: Kevin Minehart <kmineh0151@gmail.com>
Signed-off-by: Kevin Minehart <kmineh0151@gmail.com>
Signed-off-by: Kevin Minehart <kmineh0151@gmail.com>
@kminehart kminehart changed the title Prometheus metrics WIP: Prometheus metrics Apr 23, 2019
Signed-off-by: Kevin Minehart <kmineh0151@gmail.com>
consent/handler.go Outdated Show resolved Hide resolved
consent/handler.go Outdated Show resolved Hide resolved
consent/metrics.go Show resolved Hide resolved
metrics/prometheus/metrics.go Outdated Show resolved Hide resolved
metrics/prometheus/metrics.go Outdated Show resolved Hide resolved
metrics/prometheus/metrics.go Outdated Show resolved Hide resolved
Signed-off-by: Kevin Minehart <kmineh0151@gmail.com>
Signed-off-by: Kevin Minehart <kmineh0151@gmail.com>
Signed-off-by: Kevin Minehart <kmineh0151@gmail.com>
Signed-off-by: Kevin Minehart <kmineh0151@gmail.com>
Signed-off-by: Kevin Minehart <kmineh0151@gmail.com>
Signed-off-by: Kevin Minehart <kmineh0151@gmail.com>
@kminehart
Copy link
Contributor Author

kminehart commented May 1, 2019

So this will be a fun PR to merge with all them conflicts. Hopefully we can just force the merge when it's ready since I forgot to sign off on a merge commit.

Highlights:

  • First class Prometheus support.

    • Rather than implement a metrics interface or some kind of abstraction layer for metrics, I propose first class Prometheus support.
      • The Prometheus format is preparing to reach an IETF RFC standard, which means it's a suitable format for most services.
      • The Prometheus format is supported by Sysdig, Datadog, and InfluxDB (As a remote-write target).
    • The Prometheus client library is an excellent tool for receiving metrics ad-hoc (in realtime, like number of requests) and for watching for metrics at intervals (things like "# of x in the database), and comes bundled with good HTTP middleware and instrumentation.
  • The Manager interfaces now also implement the prometheus.Collector.

    • Once registered with the Registry (here), the client will continuously retrieve metrics for that manager. This is where SQL queries for "COUNT" and such will happen.
    • For metrics not retrieved by Collect, they are incremented as-need / in real-time, and then sent to the Registry in Collect.
  • A new interface, metrics.Bridge was created. This interface is for pushing metrics to external metric services that may not yet accept the Prometheus format.

  • The metrics.BridgeManager was created to manage these individual Bridge instances, repeatedly pushing metrics to each external service provided. It is initialized here

  • The Segment stuff in ory/x/metricsx was moved (mostly) to ory/hydra/metrics as the SegmentBridge

Todo:

  • Include path in HTTP metrics middleware (use ory/x/metricsx code)
  • Implement the SQL queries for metrics in the managers
  • Clean up & comment code. Add package-level comments and comments for exported functions.

@aeneasr
Copy link
Member

aeneasr commented May 2, 2019

Nice! What should I look out for when reviewing the WIP changes?

Regarding Segment, it is very important that the existing fields, keys, and events stay unchanged. I also want to avoid to move stuff from x/metrics back here as x/metrics is standardized across all repos and we do not want to diverge per project.

@kminehart
Copy link
Contributor Author

kminehart commented May 2, 2019

Gotya. That is actually the kind of information I was looking for! I'm looking for general ideas more than anything, like maybe, "this stuff would be better in ory/x", or "this isn't suitable for the manager" kind of stuff.

I'll make sure that the fields, keys, and events stay the same.

There's not a whole lot to review; usually when I open a WIP PR I'm just making sure I'm heading in the right direction.

@aeneasr
Copy link
Member

aeneasr commented May 3, 2019

Ok, perfect!

Would it make sense to have the bridge in x?

kminehart added 4 commits May 3, 2019 14:54
Signed-off-by: Kevin Minehart <kmineh0151@gmail.com>
Signed-off-by: Kevin Minehart <kmineh0151@gmail.com>
Signed-off-by: Kevin Minehart <kmineh0151@gmail.com>
Signed-off-by: Kevin Minehart <kmineh0151@gmail.com>
@kminehart kminehart changed the title WIP: Prometheus metrics Prometheus metrics May 5, 2019
@kminehart
Copy link
Contributor Author

kminehart commented May 5, 2019

Would it make sense to have the bridge in x?

I was actually thinking the same thing!

I went ahead and did that.

In order for this to pass, it'll need this: ory/x#49


There might be some metrics missing or collected in an inopportune time, or just flat-out wrong, as my understanding of Hydra's code doesn't expand far beyond changes I've made.

Signed-off-by: Kevin Minehart <kmineh0151@gmail.com>
Copy link
Member

@aeneasr aeneasr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interim review submission to see if my comments got lost or not :/


var (
metricClients = prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: "hydra",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to have the namespace fixed as a const somewhere? We should also decide if it's ory-hydra, ory_hydra, ORY Hydra, hydra or any other combination.

@@ -77,6 +82,7 @@ func RunServeAdmin(version, build, date string) func(cmd *cobra.Command, args []
return func(cmd *cobra.Command, args []string) {
d := driver.NewDefaultDriver(
logrusx.New(),
prometheus.NewRegistry(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is always prometheus.NewRegistry(), wouldn't it make more sense to create that within the registry_base?

ctx = context.Background()
)

optOut, err := cmd.Flags().GetBool("sqa-opt-out")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need all of this here or could we hide that in metricsx as it was previously?

},
)
}
b, err := metricsx.NewFormattedSegmentBridge(ctx, s, l, d.Registry().PrometheusManager().Registry)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I see - so segment actually works (mostly) independent of Prometheus except for e.g. memory stats?

@@ -351,9 +352,56 @@ func (m *SQLManager) GetClients(ctx context.Context, limit, offset int) (clients
func (m *SQLManager) CountClients(ctx context.Context) (int, error) {
var n int
if err := m.DB.QueryRow("SELECT count(*) FROM hydra_client").Scan(&n); err != nil {
fmt.Println(err.Error())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😅


defer func(p *HandledLoginRequest, err error) {
recordAcceptLoginRequest(p, err)
}(&p, err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Relying on a global error is very flaky - there are several places where err will be overshadowed, e.g. here and here and here. I'm not sure about other projects but I feel that multiple return values usually re-define/overshadow the original err scope:

package main

import (
	"fmt"
)

func main() {
	a := "a"
	fmt.Println(a) // "a"
	
	b, a := "b", "c"
	fmt.Println(a) // "c"
	fmt.Println(b) // "b"
}

@@ -118,13 +119,17 @@ func (m *MemoryManager) RevokeSubjectClientConsentSession(ctx context.Context, u
// do nothing
} else if err != nil {
return err
} else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This else is not really required right?

@kminehart
Copy link
Contributor Author

I'm gonna close this and re-open a new one when its ready.

@kminehart kminehart closed this May 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants