mirror of
https://github.com/c-cube/tiny_httpd.git
synced 2025-12-06 19:25:32 -05:00
add histograms to prometheus
This commit is contained in:
parent
2da3bd3fc7
commit
c19b8dc16f
11 changed files with 157 additions and 8 deletions
|
|
@ -13,6 +13,8 @@
|
||||||
(name tiny_httpd)
|
(name tiny_httpd)
|
||||||
(synopsis "Minimal HTTP server using threads")
|
(synopsis "Minimal HTTP server using threads")
|
||||||
(tags (http thread server tiny_httpd http_of_dir simplehttpserver))
|
(tags (http thread server tiny_httpd http_of_dir simplehttpserver))
|
||||||
|
(depopts
|
||||||
|
(mtime (>= 2.0)))
|
||||||
(depends
|
(depends
|
||||||
seq
|
seq
|
||||||
base-threads
|
base-threads
|
||||||
|
|
|
||||||
2
src/dune
2
src/dune
|
|
@ -7,7 +7,7 @@
|
||||||
(library
|
(library
|
||||||
(name tiny_httpd)
|
(name tiny_httpd)
|
||||||
(public_name tiny_httpd)
|
(public_name tiny_httpd)
|
||||||
(libraries threads seq)
|
(libraries threads seq unix)
|
||||||
(wrapped false))
|
(wrapped false))
|
||||||
|
|
||||||
(rule
|
(rule
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,12 @@
|
||||||
|
|
||||||
|
|
||||||
(library
|
(library
|
||||||
(name tiny_httpd_prometheus)
|
(name tiny_httpd_prometheus)
|
||||||
(public_name tiny_httpd.prometheus)
|
(public_name tiny_httpd.prometheus)
|
||||||
(synopsis "Metrics using prometheus")
|
(synopsis "Metrics using prometheus")
|
||||||
(private_modules common_)
|
(private_modules common_ time_)
|
||||||
(libraries tiny_httpd))
|
(libraries
|
||||||
|
tiny_httpd unix
|
||||||
|
(select time_.ml from
|
||||||
|
(mtime mtime.clock.os -> time_.mtime.ml)
|
||||||
|
(-> time_.default.ml))))
|
||||||
|
|
|
||||||
3
src/prometheus/time_.default.ml
Normal file
3
src/prometheus/time_.default.ml
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
let[@inline] now_us () =
|
||||||
|
let t = Unix.gettimeofday () in
|
||||||
|
t *. 1e6 |> ceil
|
||||||
1
src/prometheus/time_.mli
Normal file
1
src/prometheus/time_.mli
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
val now_us : unit -> float
|
||||||
3
src/prometheus/time_.mtime.ml
Normal file
3
src/prometheus/time_.mtime.ml
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
let[@inline] now_us () =
|
||||||
|
let t = Mtime_clock.now_ns () in
|
||||||
|
Int64.(div t 1000L |> to_float)
|
||||||
|
|
@ -9,7 +9,20 @@ let bpf = Printf.bprintf
|
||||||
type tags = (string * string) list
|
type tags = (string * string) list
|
||||||
type counter = { name: string; tags: tags; descr: string option; c: int A.t }
|
type counter = { name: string; tags: tags; descr: string option; c: int A.t }
|
||||||
type gauge = { name: string; tags: tags; descr: string option; g: int A.t }
|
type gauge = { name: string; tags: tags; descr: string option; g: int A.t }
|
||||||
type registry = { mutable counters: counter list; mutable gauges: gauge list }
|
|
||||||
|
type histogram = {
|
||||||
|
name: string;
|
||||||
|
tags: tags;
|
||||||
|
descr: string option;
|
||||||
|
sum: float A.t;
|
||||||
|
buckets: (float * int A.t) array;
|
||||||
|
}
|
||||||
|
|
||||||
|
type registry = {
|
||||||
|
mutable counters: counter list;
|
||||||
|
mutable gauges: gauge list;
|
||||||
|
mutable hists: histogram list;
|
||||||
|
}
|
||||||
|
|
||||||
let validate_descr_ what s =
|
let validate_descr_ what s =
|
||||||
if String.contains s '\n' then
|
if String.contains s '\n' then
|
||||||
|
|
@ -73,14 +86,71 @@ module Gauge = struct
|
||||||
let[@inline] decr_by self n = ignore (A.fetch_and_add self.g (-n) : int)
|
let[@inline] decr_by self n = ignore (A.fetch_and_add self.g (-n) : int)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
module Histogram = struct
|
||||||
|
type t = histogram
|
||||||
|
|
||||||
|
let create reg ?(tags = []) ?descr ~buckets name : t =
|
||||||
|
opt_iter_ (validate_descr_ "histogram") descr;
|
||||||
|
let buckets =
|
||||||
|
List.sort Float.compare buckets
|
||||||
|
|> List.map (fun thresh -> thresh, A.make 0)
|
||||||
|
in
|
||||||
|
let buckets = Array.of_list @@ buckets @ [ infinity, A.make 0 ] in
|
||||||
|
let self : t = { name; descr; tags; sum = A.make 0.; buckets } in
|
||||||
|
reg.hists <- self :: reg.hists;
|
||||||
|
self
|
||||||
|
|
||||||
|
let add (self : t) n =
|
||||||
|
while
|
||||||
|
let old = A.get self.sum in
|
||||||
|
not (A.compare_and_set self.sum old (old +. n))
|
||||||
|
do
|
||||||
|
()
|
||||||
|
done;
|
||||||
|
let i = ref 0 in
|
||||||
|
let continue = ref true in
|
||||||
|
while !continue && !i < Array.length self.buckets do
|
||||||
|
let thresh, count = self.buckets.(!i) in
|
||||||
|
if n <= thresh then (
|
||||||
|
continue := false;
|
||||||
|
A.incr count
|
||||||
|
) else
|
||||||
|
incr i
|
||||||
|
done
|
||||||
|
|
||||||
|
let emit buf (self : t) : unit =
|
||||||
|
opt_iter_ (bpf buf "# HELP %s %s\n" self.name) self.descr;
|
||||||
|
bpf buf "# TYPE %s histogram\n" self.name;
|
||||||
|
|
||||||
|
let count = ref 0 in
|
||||||
|
for i = 0 to Array.length self.buckets - 1 do
|
||||||
|
let thresh, buck_count = self.buckets.(i) in
|
||||||
|
count := !count + A.get buck_count;
|
||||||
|
|
||||||
|
let name =
|
||||||
|
if thresh = infinity then
|
||||||
|
"+Inf"
|
||||||
|
else
|
||||||
|
string_of_float thresh
|
||||||
|
in
|
||||||
|
bpf buf "%s%a %d\n" self.name emit_tags_
|
||||||
|
(("le", name) :: self.tags)
|
||||||
|
!count
|
||||||
|
done;
|
||||||
|
bpf buf "%s_count%a %d\n" self.name emit_tags_ self.tags !count;
|
||||||
|
bpf buf "%s_sum%a %.3f\n" self.name emit_tags_ self.tags (A.get self.sum);
|
||||||
|
()
|
||||||
|
end
|
||||||
|
|
||||||
module Registry = struct
|
module Registry = struct
|
||||||
type t = registry
|
type t = registry
|
||||||
|
|
||||||
let create () : t = { counters = []; gauges = [] }
|
let create () : t = { counters = []; gauges = []; hists = [] }
|
||||||
|
|
||||||
let emit (buf : Buffer.t) (self : t) : unit =
|
let emit (buf : Buffer.t) (self : t) : unit =
|
||||||
List.iter (Gauge.emit buf) self.gauges;
|
List.iter (Gauge.emit buf) self.gauges;
|
||||||
List.iter (Counter.emit buf) self.counters;
|
List.iter (Counter.emit buf) self.counters;
|
||||||
|
List.iter (Histogram.emit buf) self.hists;
|
||||||
()
|
()
|
||||||
|
|
||||||
let emit_str (self : t) : string =
|
let emit_str (self : t) : string =
|
||||||
|
|
@ -100,12 +170,22 @@ let http_middleware (reg : Registry.t) : H.Middleware.t =
|
||||||
let c_err =
|
let c_err =
|
||||||
Counter.create reg "tiny_httpd_errors" ~descr:"number of HTTP errors"
|
Counter.create reg "tiny_httpd_errors" ~descr:"number of HTTP errors"
|
||||||
in
|
in
|
||||||
|
let h_latency =
|
||||||
|
Histogram.create reg "tiny_httpd_latency" ~descr:"latency of HTTP responses"
|
||||||
|
~buckets:[ 0.001; 0.01; 0.1; 0.5; 1.; 5.; 10. ]
|
||||||
|
in
|
||||||
|
|
||||||
fun h : H.Middleware.handler ->
|
fun h : H.Middleware.handler ->
|
||||||
fun req ~resp : unit ->
|
fun req ~resp : unit ->
|
||||||
|
let start = Time_.now_us () in
|
||||||
Counter.incr c_req;
|
Counter.incr c_req;
|
||||||
h req ~resp:(fun (response : H.Response.t) ->
|
h req ~resp:(fun (response : H.Response.t) ->
|
||||||
let code = response.code in
|
let code = response.code in
|
||||||
|
|
||||||
|
let elapsed_us = Time_.now_us () -. start in
|
||||||
|
let elapsed_s = elapsed_us /. 1e6 in
|
||||||
|
Histogram.add h_latency elapsed_s;
|
||||||
|
|
||||||
if code < 200 || code >= 300 then Counter.incr c_err;
|
if code < 200 || code >= 300 then Counter.incr c_err;
|
||||||
resp response)
|
resp response)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,21 @@ module Gauge : sig
|
||||||
val decr_by : t -> int -> unit
|
val decr_by : t -> int -> unit
|
||||||
end
|
end
|
||||||
|
|
||||||
|
module Histogram : sig
|
||||||
|
type t
|
||||||
|
(** Histogram *)
|
||||||
|
|
||||||
|
val create :
|
||||||
|
Registry.t ->
|
||||||
|
?tags:tags ->
|
||||||
|
?descr:string ->
|
||||||
|
buckets:float list ->
|
||||||
|
string ->
|
||||||
|
t
|
||||||
|
|
||||||
|
val add : t -> float -> unit
|
||||||
|
end
|
||||||
|
|
||||||
(* TODO:
|
(* TODO:
|
||||||
module Histogram : sig
|
module Histogram : sig
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,16 @@ yolo_gauge{level="max"} 2525
|
||||||
t_c2 1
|
t_c2 1
|
||||||
# TYPE t_c1 counter
|
# TYPE t_c1 counter
|
||||||
t_c1 42
|
t_c1 42
|
||||||
|
# HELP latency latency
|
||||||
|
# TYPE latency histogram
|
||||||
|
latency{le="0.01"} 2
|
||||||
|
latency{le="0.1"} 4
|
||||||
|
latency{le="0.5"} 7
|
||||||
|
latency{le="1."} 8
|
||||||
|
latency{le="10."} 9
|
||||||
|
latency{le="+Inf"} 10
|
||||||
|
latency_count 10
|
||||||
|
latency_sum 31.530
|
||||||
|
|
||||||
```
|
```
|
||||||
==== second try====
|
==== second try====
|
||||||
|
|
@ -18,5 +28,15 @@ yolo_gauge{level="max"} 42000
|
||||||
t_c2 2
|
t_c2 2
|
||||||
# TYPE t_c1 counter
|
# TYPE t_c1 counter
|
||||||
t_c1 53
|
t_c1 53
|
||||||
|
# HELP latency latency
|
||||||
|
# TYPE latency histogram
|
||||||
|
latency{le="0.01"} 2
|
||||||
|
latency{le="0.1"} 4
|
||||||
|
latency{le="0.5"} 8
|
||||||
|
latency{le="1."} 9
|
||||||
|
latency{le="10."} 10
|
||||||
|
latency{le="+Inf"} 12
|
||||||
|
latency_count 12
|
||||||
|
latency_sum 54.930
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,26 @@ let c1 = P.Counter.create reg "t_c1"
|
||||||
let c2 = P.Counter.create reg "t_c2" ~descr:"more awesome than c1"
|
let c2 = P.Counter.create reg "t_c2" ~descr:"more awesome than c1"
|
||||||
let g1 = P.Gauge.create reg ~tags:[ "level", "max" ] "yolo_gauge"
|
let g1 = P.Gauge.create reg ~tags:[ "level", "max" ] "yolo_gauge"
|
||||||
|
|
||||||
|
let h1 =
|
||||||
|
P.Histogram.create reg ~descr:"latency"
|
||||||
|
~buckets:[ 0.01; 0.1; 0.5; 1.; 10. ]
|
||||||
|
"latency"
|
||||||
|
|
||||||
let () =
|
let () =
|
||||||
print_endline "==== first try ====";
|
print_endline "==== first try ====";
|
||||||
P.Counter.incr_by c1 42;
|
P.Counter.incr_by c1 42;
|
||||||
P.Counter.incr c2;
|
P.Counter.incr c2;
|
||||||
P.Gauge.set g1 2525;
|
P.Gauge.set g1 2525;
|
||||||
|
P.Histogram.add h1 0.2;
|
||||||
|
P.Histogram.add h1 0.003;
|
||||||
|
P.Histogram.add h1 0.002;
|
||||||
|
P.Histogram.add h1 0.025;
|
||||||
|
P.Histogram.add h1 0.9;
|
||||||
|
P.Histogram.add h1 7.4;
|
||||||
|
P.Histogram.add h1 22.2;
|
||||||
|
P.Histogram.add h1 0.3;
|
||||||
|
P.Histogram.add h1 0.4;
|
||||||
|
P.Histogram.add h1 0.1;
|
||||||
|
|
||||||
pf "```\n%s\n```\n" @@ P.Registry.emit_str reg
|
pf "```\n%s\n```\n" @@ P.Registry.emit_str reg
|
||||||
|
|
||||||
|
|
@ -19,5 +34,7 @@ let () =
|
||||||
P.Counter.incr_by c1 11;
|
P.Counter.incr_by c1 11;
|
||||||
P.Counter.incr c2;
|
P.Counter.incr c2;
|
||||||
P.Gauge.set g1 42_000;
|
P.Gauge.set g1 42_000;
|
||||||
|
P.Histogram.add h1 23.2;
|
||||||
|
P.Histogram.add h1 0.2;
|
||||||
|
|
||||||
pf "```\n%s\n```\n" @@ P.Registry.emit_str reg
|
pf "```\n%s\n```\n" @@ P.Registry.emit_str reg
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,9 @@ depends: [
|
||||||
"ptime" {with-test}
|
"ptime" {with-test}
|
||||||
"qcheck-core" {>= "0.9" & with-test}
|
"qcheck-core" {>= "0.9" & with-test}
|
||||||
]
|
]
|
||||||
|
depopts: [
|
||||||
|
"mtime" {>= "2.0"}
|
||||||
|
]
|
||||||
build: [
|
build: [
|
||||||
["dune" "subst"] {dev}
|
["dune" "subst"] {dev}
|
||||||
[
|
[
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue