{"id":16696886,"url":"https://github.com/rustedbones/pekko-http-metrics","last_synced_at":"2025-04-10T02:41:36.559Z","repository":{"id":157633547,"uuid":"633564008","full_name":"RustedBones/pekko-http-metrics","owner":"RustedBones","description":"Easily collect and expose metrics in your pekko-http server","archived":false,"fork":false,"pushed_at":"2025-03-16T21:08:07.000Z","size":606,"stargazers_count":5,"open_issues_count":3,"forks_count":2,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-24T04:11:44.008Z","etag":null,"topics":["datadog","dropwizard","graphite","metrics","pekko","pekko-http","prometheus"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/RustedBones.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-04-27T19:39:21.000Z","updated_at":"2025-03-16T20:59:13.000Z","dependencies_parsed_at":"2024-03-17T13:25:48.643Z","dependency_job_id":"5800f8b0-5c7e-40fc-a0b2-fe40e6cfd76a","html_url":"https://github.com/RustedBones/pekko-http-metrics","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RustedBones%2Fpekko-http-metrics","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RustedBones%2Fpekko-http-metrics/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RustedBones%2Fpekko-http-metrics/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RustedBones%2Fpekko-http-metrics/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RustedBones","download_url":"https://codeload.github.com/RustedBones/pekko-http-metrics/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248144940,"owners_count":21055010,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["datadog","dropwizard","graphite","metrics","pekko","pekko-http","prometheus"],"created_at":"2024-10-12T17:45:09.315Z","updated_at":"2025-04-10T02:41:36.534Z","avatar_url":"https://github.com/RustedBones.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pekko-http-metrics\n\n[![Continuous Integration](https://github.com/RustedBones/pekko-http-metrics/actions/workflows/ci.yml/badge.svg)](https://github.com/RustedBones/pekko-http-metrics/actions/workflows/ci.yml)\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/fr.davit/pekko-http-metrics-core_2.13/badge.svg)](https://maven-badges.herokuapp.com/maven-central/fr.davit/pekko-http-metrics-core_2.13)\n[![Software License](https://img.shields.io/badge/license-Apache%202-brightgreen.svg?style=flat)](LICENSE)\n[![Scala Steward badge](https://img.shields.io/badge/Scala_Steward-helping-blue.svg?style=flat\u0026logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAQCAMAAAARSr4IAAAAVFBMVEUAAACHjojlOy5NWlrKzcYRKjGFjIbp293YycuLa3pYY2LSqql4f3pCUFTgSjNodYRmcXUsPD/NTTbjRS+2jomhgnzNc223cGvZS0HaSD0XLjbaSjElhIr+AAAAAXRSTlMAQObYZgAAAHlJREFUCNdNyosOwyAIhWHAQS1Vt7a77/3fcxxdmv0xwmckutAR1nkm4ggbyEcg/wWmlGLDAA3oL50xi6fk5ffZ3E2E3QfZDCcCN2YtbEWZt+Drc6u6rlqv7Uk0LdKqqr5rk2UCRXOk0vmQKGfc94nOJyQjouF9H/wCc9gECEYfONoAAAAASUVORK5CYII=)](https://scala-steward.org)\n\nEasily collect and expose metrics in your pekko-http server.\n\nThe following implementations are supported:\n\n* [datadog](#datadog) (via StatsD)\n* [dropwizard](#dropwizard)\n* [graphite](#graphite) (via Carbon)\n* [prometheus](#prometheus)\n\n## Versions\n\n| Version | Release date | Pekka Http version | Scala versions        |\n|---------|--------------|--------------------|-----------------------|\n| `2.0.0` | 2025-03-16   | `1.1.0`            | `3.3`, `2.13`         |\n| `1.1.0` | 2025-03-02   | `1.1.0`            | `3.3`, `2.13`         |\n| `1.0.1` | 2024-04-09   | `1.0.1`            | `3.3`, `2.13`, `2.12` |\n| `1.0.0` | 2023-08-14   | `1.0.0`            | `3.3`, `2.13`, `2.12` |\n\n## Getting pekko-http-metrics\n\nLibraries are published to Maven Central. Add to your `build.sbt`:\n\n```scala\nlibraryDependencies += \"fr.davit\" %% \"pekko-http-metrics-\u003cbackend\u003e\" % \u003cversion\u003e\n```\n\n### Server metrics\n\nThe library enables you to easily record the following metrics from a pekko-http server into a registry. The\nfollowing labeled metrics are recorded:\n\n- requests (`counter`) [method]\n- requests active (`gauge`) [method]\n- requests failures (`counter`) [method]\n- requests size (`histogram`) [method]\n- responses (`counter`) [method | path | status group]\n- responses errors [method | path | status group]\n- responses duration (`histogram`) [method | path | status group]\n- response size (`histogram`) [method | path | status group]\n- connections (`counter`)\n- connections active (`gauge`)\n\nRecord metrics from your pekko server by creating an `HttpMetricsServerBuilder` with the `newMeteredServerAt` extension\nmethod located in `HttpMetrics`\n\n```scala\nimport pekko.actor.ActorSystem\nimport pekko.http.scaladsl.Http\nimport pekko.http.scaladsl.server.Directives._\nimport pekko.http.scaladsl.server.Route\nimport fr.davit.pekko.http.metrics.core.{HttpMetricsRegistry, HttpMetricsSettings}\nimport fr.davit.pekko.http.metrics.core.HttpMetrics._ // import extension methods\n\nimplicit val system = ActorSystem()\n\nval settings: HttpMetricsSettings = ... // concrete settings implementation\n\nval registry: HttpMetricsRegistry = ... // concrete registry implementation\n\nval route: Route = ... // your route\n\nHttp()\n  .newMeteredServerAt(\"localhost\", 8080, registry)\n  .bindFlow(route)\n```\n\nRequests failure counter is incremented when no response could be emitted by the server (network error, ...)\n\nBy default, the response error counter will be incremented when the returned status code is an `Server error (5xx)`.\nYou can override this behaviour in the settings.\n\n```scala\nsettings.withDefineError(_.status.isFailure)\n```\n\nIn this example, all responses with status \u003e= 400 are considered as errors.\n\nFor HTTP2 you must use the `bind` or `bindSync` on the `HttpMetricsServerBuilder`. \nIn this case the connection metrics won't be available.\n\n```scala\nHttp()\n  .newMeteredServerAt(\"localhost\", 8080)\n  .bind(route)\n```\n\n#### Dimensions\n\nBy default, metrics dimensions are disabled. You can enable them in the settings.\n\n```scala\nsettings\n  .withIncludeMethodDimension(true)\n  .withIncludePathDimension(true)\n  .withIncludeStatusDimension(true)\n```\n\nCustom dimensions can be added to the message metrics:\n- extend the `HttpRequestLabeler` to add labels on requests \u0026 their associated response \n- extend the `HttpResponseLabeler` to add labels on responses only\n\nIn the example below, the `browser` dimension will be populated based on the user-agent header on requests and responses.\nThe responses going through the route will have the `user` dimension set with the provided username, other responses\nwill be `unlabelled`.\n\n```scala\nimport fr.davit.pekko.http.metrics.core.{AttributeLabeler, HttpRequestLabeler}\n\n// based on https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent#browser_name\nobject BrowserLabeler extends HttpRequestLabeler {\n override def name: String = \"browser\"\n override def label(request: HttpRequest): String = {\n  val products = for {\n   ua \u003c- request.header[`User-Agent`].toSeq\n   pv \u003c- ua.products\n  } yield pv.product\n  if (products.contains(\"Seamonkey\")) \"seamonkey\"\n  else if (products.contains(\"Firefox\")) \"firefox\"\n  else if (products.contains(\"Chromium\")) \"chromium\"\n  else if (products.contains(\"Chrome\")) \"chrome\"\n  else if (products.contains(\"Safari\")) \"safari\"\n  else if (products.contains(\"OPR\") || products.contains(\"Opera\")) \"opera\"\n  else \"other\"\n }\n}\n\nobject UserLabeler extends AttributeLabeler {\n  def name: String = \"user\"\n}\n\nval route = auth { username =\u003e\n metricsLabeled(UserLabeler, username) {\n  ...\n }\n}\n\nsettings.withCustomDimensions(BrowserLabeler, UserLabeler)\n```\n\n\nAdditional static server-level dimensions can be set to all metrics collected by the library.\nIn the example below, the `env` dimension with `prod` label will be added. \n\n```scala\nimport fr.davit.pekko.http.metrics.core.Dimension\nsettings.withServerDimensions(Dimension(\"env\", \"prod\"))\n```\n\n##### Method\n\nThe method of the request is used as dimension on the metrics. eg. `GET`\n\n##### Path\n\nMatched path of the request is used as dimension on the metrics.\n\nWhen enabled, all metrics will get `unlabelled` as path dimension by default,\nYou must use the labelled path directives defined in `HttpMetricsDirectives` to set the dimension value.\n\nYou must also be careful about cardinality: see [here](https://prometheus.io/docs/practices/naming/#labels).\nIf your path contains unbounded dynamic segments, you must give an explicit label to override the dynamic part:\n\n```scala\nimport fr.davit.pekko.http.metrics.core.scaladsl.server.HttpMetricsDirectives._\n\nval route = pathPrefixLabel(\"api\") {\n  pathLabeled(\"user\" / JavaUUID, \"user/:user-id\") { userId =\u003e\n    ...\n  }\n}\n```\n\nMoreover, all unhandled requests will have path dimension set to `unhandled`.\n\n##### Status group\n\nThe status group creates the following dimensions on the metrics: `1xx|2xx|3xx|4xx|5xx|other`\n\n### Expose metrics\n\nExpose the metrics from the registry on an http endpoint with the `metrics` directive.\n\n```scala\nimport fr.davit.pekko.http.metrics.core.scaladsl.server.HttpMetricsDirectives._\n\nval route = (get \u0026 path(\"metrics\"))(metrics(registry))\n```\n\nOf course, you will also need to have the implicit marshaller for your registry in scope.\n\n\n## Implementations\n\n### [Datadog]( https://docs.datadoghq.com/developers/dogstatsd/)\n\n| metric             | name                    |\n|--------------------|-------------------------|\n| requests           | requests_count          |\n| requests active    | requests_active         |\n| requests failures  | requests_failures_count |\n| requests size      | requests_bytes          |\n| responses          | responses_count         |\n| responses errors   | responses_errors_count  |\n| responses duration | responses_duration      |\n| responses size     | responses_bytes         |\n| connections        | connections_count       |\n| connections active | connections_active      |\n\nThe `DatadogRegistry` is just a facade to publish to your StatsD server. The registry itself not located in the JVM, \nfor this reason it is not possible to expose the metrics in your API.\n\nAdd to your `build.sbt`:\n\n```scala\nlibraryDependencies += \"fr.davit\" %% \"pekko-http-metrics-datadog\" % \u003cversion\u003e\n```\n\nCreate your registry\n\n```scala\nimport com.timgroup.statsd.StatsDClient\nimport fr.davit.pekko.http.metrics.core.HttpMetricsSettings\nimport fr.davit.pekko.http.metrics.datadog.{DatadogRegistry, DatadogSettings}\n\nval client: StatsDClient = ... // your statsd client\nval settings: HttpMetricsSettings = DatadogSettings.default\nval registry = DatadogRegistry(client, settings) // or DatadogRegistry(client) to use default settings\n```\n\nSee datadog's [documentation](https://github.com/dataDog/java-dogstatsd-client) on how to create a StatsD client.\n\n\n### [Dropwizard](https://metrics.dropwizard.io/)\n\n| metric             | name                            |\n|--------------------|---------------------------------|\n| requests           | ${namespace}.requests           |\n| requests active    | ${namespace}.requests.active    |\n| requests failures  | ${namespace}.requests.failures  |\n| requests size      | ${namespace}.requests.bytes     |\n| responses          | ${namespace}.responses          |\n| responses errors   | ${namespace}.responses.errors   |\n| responses duration | ${namespace}.responses.duration |\n| responses size     | ${namespace}.responses.bytes    |\n| connections        | ${namespace}.connections        |\n| connections active | ${namespace}.connections.active |\n\n**Important**: The `DropwizardRegistry` does not support labels.\nThis feature will be available with dropwizard `v5` (still in pre-release phase).\n\nAdd to your `build.sbt`:\n\n```scala\nlibraryDependencies += \"fr.davit\" %% \"pekko-http-metrics-dropwizard\" % \u003cversion\u003e\n// or for dropwizard v5\nlibraryDependencies += \"fr.davit\" %% \"pekko-http-metrics-dropwizard-v5\" % \u003cversion\u003e\n```\n\nCreate your registry\n\n```scala\nimport com.codahale.metrics.MetricRegistry\nimport fr.davit.pekko.http.metrics.core.HttpMetricsSettings\nimport fr.davit.pekko.http.metrics.dropwizard.{DropwizardRegistry, DropwizardSettings}\n\nval dropwizard: MetricRegistry = ... // your dropwizard registry\nval settings: HttpMetricsSettings = DropwizardSettings.default\nval registry = DropwizardRegistry(dropwizard, settings) // or DropwizardRegistry() to use a fresh registry \u0026 default settings\n```\n\nExpose the metrics\n\n```scala\nimport fr.davit.pekko.http.metrics.core.scaladsl.server.HttpMetricsDirectives._\nimport fr.davit.pekko.http.metrics.dropwizard.marshalling.DropwizardMarshallers._\n\nval route = (get \u0026 path(\"metrics\"))(metrics(registry))\n```\n\nAll metrics from the dropwizard metrics registry will be exposed.\nYou can find some external exporters [here](https://github.com/dropwizard/metrics/). For instance,\nto expose some JVM metrics, you have to add the dedicated dependency and register the metrics set into your collector registry:\n\n```sbt\nlibraryDependencies += \"com.codahale.metrics\" % \"metrics-jvm\" % \u003cversion\u003e\n```\n\n```scala\nimport com.codahale.metrics.jvm._\n\nval dropwizard: MetricRegistry = ... // your dropwizard registry\ndropwizard.register(\"jvm.gc\", new GarbageCollectorMetricSet())\ndropwizard.register(\"jvm.threads\", new CachedThreadStatesGaugeSet(10, TimeUnit.SECONDS))\ndropwizard.register(\"jvm.memory\", new MemoryUsageGaugeSet())\n\nval registry = DropwizardRegistry(dropwizard, settings)\n```\n\n### [Graphite](https://graphiteapp.org/)\n\n| metric             | name                            |\n|--------------------|---------------------------------|\n| requests           | ${namespace}.requests           |\n| requests active    | ${namespace}.requests.active    |\n| requests failures  | ${namespace}.requests.failures  |\n| requests size      | ${namespace}.requests.bytes     |\n| responses          | ${namespace}.responses          |\n| responses errors   | ${namespace}.responses.errors   |\n| responses duration | ${namespace}.responses.duration |\n| response size      | ${namespace}.responses.bytes    |\n| connections        | ${namespace}.connections        |\n| connections active | ${namespace}.connections.active |\n\nAdd to your `build.sbt`:\n\n```scala\nlibraryDependencies += \"fr.davit\" %% \"pekko-http-metrics-graphite\" % \u003cversion\u003e\n```\n\nCreate your carbon client and your registry\n\n```scala\nimport fr.davit.pekko.http.metrics.core.HttpMetricsSettings\nimport fr.davit.pekko.http.metrics.graphite.{CarbonClient, GraphiteRegistry, GraphiteSettings}\n\nval carbonClient: CarbonClient = CarbonClient(\"hostname\", 2003)\nval settings: HttpMetricsSettings = GraphiteSettings.default\nval registry = GraphiteRegistry(carbonClient, settings) // or PrometheusRegistry(carbonClient) to use default settings\n```\n\n### [Prometheus](http://prometheus.io/)\n\n| metric             | name                                    |\n|--------------------|-----------------------------------------|\n| requests           | ${namespace}_requests_total             |\n| requests active    | ${namespace}_requests_active            |\n| requests failures  | ${namespace}_requests_failures_total    |\n| requests size      | ${namespace}_requests_size_bytes        |\n| responses          | ${namespace}_responses_total            |\n| responses errors   | ${namespace}_responses_errors_total     |\n| responses duration | ${namespace}_responses_duration_seconds |\n| responses size     | ${namespace}_responses_size_bytes       |\n| connections        | ${namespace}_connections_total          |\n| connections active | ${namespace}_connections_active         |\n\nAdd to your `build.sbt`:\n\n```scala\nlibraryDependencies += \"fr.davit\" %% \"pekko-http-metrics-prometheus\" % \u003cversion\u003e\n```\n\nCreate your registry\n\n```scala\nimport io.prometheus.metrics.core.metrics.CollectorRegistry\nimport fr.davit.pekko.http.metrics.prometheus.{PrometheusRegistry, PrometheusSettings}\n\nval prometheus: CollectorRegistry = ... // your prometheus registry\nval settings: PrometheusSettings = PrometheusSettings.default\nval registry = PrometheusRegistry(prometheus, settings) // or PrometheusRegistry() to use the default registry \u0026 settings\n```\n\nYou can fine-tune the `histogram/summary` configuration of `buckets/quantiles` for the `request\n size`, `duration` and `response size` metrics.\n \n```scala\nsettings\n  .withDurationConfig(ClassicBuckets(1, 2, 3, 5, 8, 13, 21, 34))\n  .withReceivedBytesConfig(Quantiles(0.5, 0.75, 0.9, 0.95, 0.99))\n  .withSentBytesConfig(PrometheusSettings.DefaultQuantiles)\n```\n\nExpose the metrics\n\n```scala\nimport fr.davit.pekko.http.metrics.core.scaladsl.server.HttpMetricsDirectives._\nimport fr.davit.pekko.http.metrics.prometheus.marshalling.PrometheusMarshallers._\n\nval route = (get \u0026 path(\"metrics\"))(metrics(registry))\n```\n\nAll metrics from the prometheus collector registry will be exposed.\nMarshalling [format](https://prometheus.github.io/client_java/exporters/formats/) depends on the `Accept`/`Content-Type` header sent by the client:\n\n* `Content-Type: text/plain`: Prometheus text format\n* `Content-Type: application/openmetrics-text`: OpenMetrics text format\n* `Content-Type: application/vnd.google.protobuf`: Prometheus protobuf format\n\nNo `Accept` header or matching several (eg `Accept: application/*`) will take the 1st matching type from the above list.\n\n\nYou can find some instrumentations [here](https://prometheus.github.io/client_java). For instance, to expose some JVM\nmetrics, you have to add the dedicated dependency and initialize/register it to your collector registry:\n\n```sbt\nlibraryDependencies += \"io.prometheus\" % \"rometheus-metrics-instrumentation-jvm\" % \u003cvesion\u003e\n```\n\n```scala\nimport io.prometheus.metrics.model.registry.PrometheusRegistry\nimport io.prometheus.metrics.instrumentation.jvm.JvmMetrics\n\nval prometheus: PrometheusRegistry = ??? // your prometheus registry\nJvmMetrics.builder().register(prometheus)  // or JvmMetrics.builder().register() to use the default registry\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frustedbones%2Fpekko-http-metrics","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frustedbones%2Fpekko-http-metrics","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frustedbones%2Fpekko-http-metrics/lists"}