{"id":24381851,"url":"https://github.com/carlduevel/clometheus","last_synced_at":"2025-12-12T01:19:53.161Z","repository":{"id":62433915,"uuid":"137396615","full_name":"carlduevel/clometheus","owner":"carlduevel","description":"Zero dependency Clojure Prometheus Client","archived":false,"fork":false,"pushed_at":"2022-04-07T18:30:46.000Z","size":80,"stargazers_count":11,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-11T00:06:23.166Z","etag":null,"topics":["clojure","metrics","prometheus"],"latest_commit_sha":null,"homepage":"","language":"Clojure","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/carlduevel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-06-14T18:50:29.000Z","updated_at":"2022-04-07T18:30:50.000Z","dependencies_parsed_at":"2022-11-01T21:01:49.420Z","dependency_job_id":null,"html_url":"https://github.com/carlduevel/clometheus","commit_stats":null,"previous_names":["hackbert/clometheus"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/carlduevel%2Fclometheus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/carlduevel%2Fclometheus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/carlduevel%2Fclometheus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/carlduevel%2Fclometheus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/carlduevel","download_url":"https://codeload.github.com/carlduevel/clometheus/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248317707,"owners_count":21083528,"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":["clojure","metrics","prometheus"],"created_at":"2025-01-19T09:13:29.357Z","updated_at":"2025-12-12T01:19:48.109Z","avatar_url":"https://github.com/carlduevel.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# clometheus\n\n__clometheus__ is a zero dependency instrumentation library for [Prometheus](https://prometheus.io/).\n\n[![Clojars Project](https://img.shields.io/clojars/v/online.duevel/clometheus.svg)](https://clojars.org/online.duevel/clometheus)\n\n[![Build Status](https://travis-ci.com/carlduevel/clometheus.svg?branch=master)](https://travis-ci.com/carlduevel/clometheus)\n\n[![Dependencies Status](https://versions.deps.co/carlduevel/clometheus/status.svg)](https://versions.deps.co/carlduevel/clometheus)\n\n## Why?\n\nThere are a lot of clojure wrappers for the [Java Prometheus client](https://github.com/prometheus/client_java) out there.\nThey all force you to register metrics before using them, because that is\nhow the java client works. This is a good idea in Java, but not so much in Clojure\nas it leads to code like this:\n\n```clojure\n(defonce my-registry (prometheus/create-registry)) ; a registry object to pass around and take care off.\n(defonce __my-counter (register-counter :my-dear-counter registry)) ; a ref that is not interesting because it will not be referenced later\n....\n\n(prometheus/inc! :my-dear-counter) ; This is error prone: Get the label wrong and after all: What type is it of? What labels does it have?\n\n```\nInstead to instrument with clometheus, you just do it on the spot.\nNo registry to take care of (if you do not want to) and no registration necessary:\n\n```clojure\n(require '[clometheus.core :as c])\n(c/inc! (c/counter \"my_counter\"))\n```\nClometheus can do this because it does not just wrap the prometheus library but the instrumentation\ncode is implemented in Clojure itself (with the summary implementation being the sole exception as it is the same\nas in the Java client).\n\n## Usage\n\n### Counters\nA simple counter:\n```clojure\n(def request-counter (c/counter \"requests_total\"))\n(c/inc! request-counter)\n\n```\nOr with more bells and whistles:\n\n```clojure\n\n(def request-counter-with-labels (c/counter \"requests_total\" :description \"Counter for http requests.\" :labels [\"rc\" \"method\"]))\n\n(c/inc! request-counter-with-labels :by 2 :labels {\"rc\" 200 \"method\" \"GET\"})\n\n```\nRemember: Counters can only go up!\n\n### Gauges\nA simple gauge:\n```clojure\n(def sessions-gauge (c/gauge \"sessions\"))\n(c/set! sessions-gauge 12)\n(c/inc! sessions-gauge)\n(c/dec! sessions-gauge)\n```\nOr with description and labels:\n```clojure\n(def db-conn-gauge (c/gauge \"database_connections\" :description \"Database connection count\" :lables [\"db\" \"host\"]))\n(c/inc! db-conn-gauge :lables {\"db\" \"my-db\" \"host\" \"somehost\"})\n\n ```\n Sometimes a callback gauge is convenient:\n\n ```clojure\n (c/gauge  \"connection_pool_size\" :description \"Size of connection pool\" :callback-fn #(42))\n ```\n### Histograms\nA histogram with labels:\n```clojure\n(def http-histogram (c/histogram \"http_duration_in_s\" :description \"duration for processing an http request\" :labels [\"method\" \"path\" \"rc\"]))\n(c/observe! http-histogram 0.01 :labels {\"method\" \"get\" \"path\" \"/\" \"rc\" \"200\"})\n```\nThis will use the default bucket sizes of the prometheus Java client.\nBut you can also define your own:\n\n```clojure\n(c/histogram \"special_buckets\" :buckets [0.1 1 10])\n```\n\n### Summaries\nYou can have a summary without specifying quantiles:\n```clojure\n(def summary (c/summary \"look_ma_no_quantiles\"))\n(c/observe summary 1.0)\n```\nBut more likely you want some quantiles:\n```clojure\n(c/summary \"kafka_lag_in_seconds\" :labels [\"consumer_group_id\" \"topic\"] :quantiles [(c/quantile 0.5  0.05)(c/quantile 0.9  0.01) (c/quantile 0.99 0.001)])\n```\n\n### Exposing metrics\n#### Via web server\nCreate a compojure route to export your metrics:\n```clojure\n(require '[clometheus.txt-format :as txt]\n         '[compojure.core :refer (GET)])\n(GET \"/metrics\" [] (txt/metrics-response))\n```\n\n#### Using prometheus java client\nIt is possible to use the prometheus java client alongside clometheus.\nExporting metrics can then be done like this:\n\n```clojure\n(require '[clometheus.txt-format :as txt]\n         '[clometheus.core :as c])\n(import io.prometheus.client.exporter.common.TextFormat\n        io.prometheus.client.CollectorRegistry\n        clometheus.core.ICollectorRegistry)\n\n\n(defn- text-format\n  ([] (text-format  CollectorRegistry/defaultRegistry c/default-registry))\n  ([^CollectorRegistry java-client-registry ^ICollectorRegistry clometheus-registry]\n   (with-open [out (java.io.StringWriter.)]\n     (TextFormat/write004 out (.metricFamilySamples java-client-registry))\n     (doseq [sample (c/collect clometheus-registry)]\n       (txt/write out sample))\n     (str out))))\n\n(defn metrics-response\n  ([] (metrics-response CollectorRegistry/defaultRegistry c/default-registry))\n  ([^CollectorRegistry java-client-registry  ^ICollectorRegistry clometheus-registry]\n   {:headers {\"Content-Type\" \"text/plain; version=0.0.4; charset=utf-8\"}\n    :status  200\n    :body    (text-format java-client-registry clometheus-registry)}))\n```\n\nThere is one caveat though: There should not be the same metrics in both\nregistries. So this expression should be true:\n\n```clojure\n(require '[clojure.set :as set]\n         '[clometheus.core :as c])\n(import io.prometheus.client.CollectorRegistry)\n\n(empty? (set/intersection(-\u003e\u003e (CollectorRegistry/defaultRegistry)\n                                .metricFamilySamples\n                                enumeration-seq\n                                (mapcat #(.samples %))\n                                (map #(.-name %))\n                                set)\n                           (set (keys c/default-registry))))\n```\n#### Pushgateway\nUse [this](https://github.com/carlduevel/clometheus-pushgateway). \n\n### Misc\nIn order to [avoid missing metrics](https://www.robustperception.io/existential-issues-with-metrics)\nyou should define metrics before using them.\nSo for an error counter do not:\n```clojure\n(c/inc! (c/counter \"my_error_counter\"))\n```\nBut define the error counter beforehand so it does not show up out of nowhere:\n```clojure\n(def my-error-counter (c/counter \"my_error_counter\"))\n[...]\n(c/inc! my-error-counter)\n```\n\n## License\n\nThis project is licensed under the Apache License 2.0.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcarlduevel%2Fclometheus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcarlduevel%2Fclometheus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcarlduevel%2Fclometheus/lists"}