123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165openPrometheusletfailffmt=Fmt.kstrfailwithfmtmoduleTextFormat_0_0_4=structletre_unquoted_escapes=Re.compile@@Re.set"\\\n"letre_quoted_escapes=Re.compile@@Re.set"\"\\\n"letquoteg=matchRe.Group.getg0with|"\\"->"\\\\"|"\n"->"\\n"|"\""->"\\\""|x->failf"Unexpected match %S"xletoutput_metric_typef=function|Counter->Fmt.stringf"counter"|Gauge->Fmt.stringf"gauge"|Summary->Fmt.stringf"summary"|Histogram->Fmt.stringf"histogram"letoutput_unquotedfs=Fmt.stringf@@Re.replacere_unquoted_escapes~f:quotesletoutput_quotedfs=Fmt.stringf@@Re.replacere_quoted_escapes~f:quotes(* Fmt.float by default prints floats using scientific exponential
* notation, which loses significant data on e.g. timestamp:
* Fmt.str "%a" Fmt.float 1575363850.57 --> 1.57536e+09 *)letfloat_fmtf=Fmt.pff"%f"letoutput_valuefv=matchclassify_floatvwith|FP_normal|FP_subnormal|FP_zero->float_fmtfv|FP_infinitewhenv>0.0->Fmt.stringf"+Inf"|FP_infinite->Fmt.stringf"-Inf"|FP_nan->Fmt.stringf"Nan"letoutput_pairsf(label_names,label_values)=letcont=reffalseinletoutput_pairnamevalue=if!contthenFmt.stringf", "elsecont:=true;Fmt.pff"%a=\"%a\""LabelName.ppnameoutput_quotedvalueinList.iter2output_pairlabel_nameslabel_valuesletoutput_labels~label_namesf=function|[]->()|label_values->Fmt.pff"{%a}"output_pairs(label_names,label_values)letoutput_sample~base~label_names~label_valuesf{Sample_set.ext;value;bucket}=letlabel_names,label_values=matchbucketwith|None->label_names,label_values|Some(label_name,label_value)->letlabel_value_str=Fmt.str"%a"output_valuelabel_valueinlabel_name::label_names,label_value_str::label_valuesinFmt.pff"%a%s%a %a@."MetricName.ppbaseext(output_labels~label_names)label_valuesoutput_valuevalueletoutput_metric~name~label_namesf(label_values,samples)=List.iter(output_sample~base:name~label_names~label_valuesf)samplesletoutputf=MetricFamilyMap.iter(funmetricsamples->let{MetricInfo.name;metric_type;help;label_names}=metricinFmt.pff"#HELP %a %a@.\
#TYPE %a %a@.\
%a"MetricName.ppnameoutput_unquotedhelpMetricName.ppnameoutput_metric_typemetric_type(LabelSetMap.pp~sep:Fmt.nop(output_metric~name~label_names))samples)endmoduleRuntime=structletcurrent=ref(Gc.quick_stat())letupdate()=current:=Gc.quick_stat()letsimple_metric~metric_type~helpnamefn=letinfo={MetricInfo.name=MetricName.vname;help;metric_type;label_names=[];}inletcollect()=LabelSetMap.singleton[][Sample_set.sample(fn())]ininfo,collectletocaml_gc_allocated_bytes=simple_metric~metric_type:Counter"ocaml_gc_allocated_bytes"Gc.allocated_bytes~help:"Total number of bytes allocated since the program was started."letocaml_gc_major_words=simple_metric~metric_type:Counter"ocaml_gc_major_words"(fun()->(!current).Gc.major_words)~help:"Number of words allocated in the major heap since the program was started."letocaml_gc_minor_collections=simple_metric~metric_type:Counter"ocaml_gc_minor_collections"(fun()->float_of_int(!current).Gc.minor_collections)~help:"Number of minor collection cycles completed since the program was started."letocaml_gc_major_collections=simple_metric~metric_type:Counter"ocaml_gc_major_collections"(fun()->float_of_int(!current).Gc.major_collections)~help:"Number of major collection cycles completed since the program was started."letocaml_gc_heap_words=simple_metric~metric_type:Gauge"ocaml_gc_heap_words"(fun()->float_of_int(!current).Gc.heap_words)~help:"Total size of the major heap, in words."letocaml_gc_compactions=simple_metric~metric_type:Counter"ocaml_gc_compactions"(fun()->float_of_int(!current).Gc.compactions)~help:"Number of heap compactions since the program was started."letocaml_gc_top_heap_words=simple_metric~metric_type:Counter"ocaml_gc_top_heap_words"(fun()->float_of_int(!current).Gc.top_heap_words)~help:"Maximum size reached by the major heap, in words."letprocess_cpu_seconds_total=simple_metric~metric_type:Counter"process_cpu_seconds_total"Sys.time~help:"Total user and system CPU time spent in seconds."letmetrics=[ocaml_gc_allocated_bytes;ocaml_gc_major_words;ocaml_gc_minor_collections;ocaml_gc_major_collections;ocaml_gc_heap_words;ocaml_gc_compactions;ocaml_gc_top_heap_words;process_cpu_seconds_total;]endopenLwt.InfixmoduleCohttp(Server:Cohttp_lwt.S.Server)=structletcallback_connreq_body=letopenCohttpinleturi=Request.urireqinmatchRequest.methreq,Uri.pathuriwith|`GET,"/metrics"->Prometheus.CollectorRegistry.(collectdefault)>>=fundata->letbody=Fmt.to_to_stringTextFormat_0_0_4.outputdatainletheaders=Header.init_with"Content-Type""text/plain; version=0.0.4"inServer.respond_string~status:`OK~headers~body()|_->Server.respond_error~status:`Bad_request~body:"Bad request"()endlet()=CollectorRegistry.(register_pre_collectdefault)Runtime.update;letadd(info,collector)=CollectorRegistry.(registerdefault)infocollectorinList.iteraddRuntime.metrics