package tiny_httpd
Minimal HTTP server using threads
Install
Dune Dependency
Authors
Maintainers
Sources
tiny_httpd-0.17.0.tbz
sha256=67c636f1bbaf93da0cb0a12cb44803299892caf51e637815c753f0af6f4e7b7e
sha512=c254415a69df5a1bec255a223dbc2d10370b2f533da0b14ff458545da7aa5e4a634972272e7eef64425b6360494d6b3446761c03b49fd7558765cbfefae2178b
doc/src/tiny_httpd.prometheus/tiny_httpd_prometheus.ml.html
Source file tiny_httpd_prometheus.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
(* https://prometheus.io/docs/instrumenting/exposition_formats/#text-based-format *) open Common_p_ let bpf = Printf.bprintf 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 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; mutable on_will_emit: (unit -> unit) list; } let validate_descr_ what s = if String.contains s '\n' then invalid_arg (spf "%s: description cannot contain '\n'" what) let buf = if tags <> [] then ( bpf buf "{"; List.iteri (fun i (k, v) -> if i > 0 then bpf buf ","; bpf buf "%s=%S" k v) tags; bpf buf "}" ) let opt_iter_ f = function | None -> () | Some x -> f x module Counter = struct type t = counter let create (reg : registry) ?( = []) ?descr name : t = let self : t = { name; descr; tags; c = A.make 0 } in opt_iter_ (validate_descr_ "counter") descr; reg.counters <- self :: reg.counters; self let emit buf (self : t) = opt_iter_ (bpf buf "# HELP %s %s\n" self.name) self.descr; bpf buf "# TYPE %s counter\n" self.name; bpf buf "%s%a %d\n" self.name emit_tags_ self.tags (A.get self.c); () let[@inline] incr self = A.incr self.c let[@inline] incr_by self n = ignore (A.fetch_and_add self.c n : int) let incr_to self n = while let old = A.get self.c in if old < n then not (A.compare_and_set self.c old n) else false do () done end module Gauge = struct type t = gauge let create (reg : registry) ?( = []) ?descr name : t = opt_iter_ (validate_descr_ "gauge") descr; let self : t = { name; descr; tags; g = A.make 0 } in reg.gauges <- self :: reg.gauges; self let emit buf (self : t) = opt_iter_ (bpf buf "# HELP %s %s\n" self.name) self.descr; bpf buf "# TYPE %s gauge\n" self.name; bpf buf "%s%a %d\n" self.name emit_tags_ self.tags (A.get self.g); () let[@inline] set self x = A.set self.g x let[@inline] incr self = A.incr self.g let[@inline] incr_by self n = ignore (A.fetch_and_add self.g n : int) let[@inline] decr self = A.decr self.g let[@inline] decr_by self n = ignore (A.fetch_and_add self.g (-n) : int) end module Histogram = struct type t = histogram let create reg ?( = []) ?descr ~buckets name : t = opt_iter_ (validate_descr_ "histogram") descr; let buckets = List.sort Stdlib.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_bucket%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 type t = registry let create () : t = { counters = []; gauges = []; hists = []; on_will_emit = [] } let on_will_emit self f = self.on_will_emit <- f :: self.on_will_emit let emit (buf : Buffer.t) (self : t) : unit = List.iter (fun f -> f ()) self.on_will_emit; List.iter (Gauge.emit buf) self.gauges; List.iter (Counter.emit buf) self.counters; List.iter (Histogram.emit buf) self.hists; () let emit_str (self : t) : string = let buf = Buffer.create 32 in emit buf self; Buffer.contents buf end let global = Registry.create () let http_middleware (reg : Registry.t) : Server.Middleware.t = let c_req = Counter.create reg "tiny_httpd_requests" ~descr:"number of HTTP requests" in let c_err = Counter.create reg "tiny_httpd_errors" ~descr:"number of HTTP errors" 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 : Server.Middleware.handler -> fun req ~resp : unit -> let start = Time_.now_us () in Counter.incr c_req; h req ~resp:(fun (response : Response.t) -> 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 >= 400 then Counter.incr c_err; resp response) let add_route_to_server (server : Server.t) (reg : registry) : unit = Server.add_route_handler server Route.(exact "metrics" @/ return) @@ fun _req -> let str = Registry.emit_str reg in Response.make_string @@ Ok str let instrument_server (server : Server.t) reg : unit = Server.add_middleware ~stage:(`Stage 1) server (http_middleware reg); add_route_to_server server reg module GC_metrics = struct type t = { major_coll: counter; major_heap: gauge; compactions: counter } let create reg : t = let major_coll = Counter.create reg ~descr:"major GC collections" "ocaml_gc_major" in let major_heap = Gauge.create reg ~descr:"size of major heap" "ocaml_gc_major_heap_size" in let compactions = Counter.create reg ~descr:"number of GC compactions" "ocaml_gc_compactions" in { major_coll; major_heap; compactions } let update (self : t) = let stats = Gc.quick_stat () in Counter.incr_to self.major_coll stats.major_collections; Counter.incr_to self.compactions stats.compactions; Gauge.set self.major_heap (stats.heap_words * 8) let create_and_update_before_emit reg : unit = let gc = create reg in Registry.on_will_emit reg (fun () -> update gc) end
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>