1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057(** Opentelemetry types and instrumentation *)moduleLock=Lock(** Global lock *)moduleRand_bytes=Rand_bytes(** Generation of random identifiers *)open structletresult_bindxf=matchxwith|Errore->Errore|Okx->fxend(** {2 Wire format} *)(** Protobuf types *)moduleProto=structmoduleCommon=structincludeCommon_typesincludeCommon_ppinclude Common_pbendmodule Resource=structincludeResource_typesincludeResource_ppincludeResource_pbendmoduleTrace=structincludeTrace_typesincludeTrace_ppinclude Trace_pbendmoduleMetrics=structincludeMetrics_typesincludeMetrics_ppincludeMetrics_pbendmoduleTrace_service=structincludeTrace_service_typesincludeTrace_service_pbincludeTrace_service_ppendmoduleMetrics_service=structincludeMetrics_service_typesincludeMetrics_service_ppincludeMetrics_service_pbendmoduleStatus=structincludeStatus_typesincludeStatus_ppincludeStatus_pbendmoduleLogs=structincludeLogs_typesincludeLogs_pbincludeLogs_ppendmoduleLogs_service=structincludeLogs_service_typesincludeLogs_service_pbincludeLogs_service_ppendend(** {2 Timestamps} *)(** Unix timestamp.
These timestamps measure time since the Unix epoch (jan 1, 1970) UTC
in nanoseconds. *)moduleTimestamp_ns=structtypet=int64letns_in_a_day=Int64.(mul1_000_000_000L (of_int(24*3600)))(** Current unix timestamp in nanoseconds *)let[@inline]now_unix_ns():t=letspan=Ptime_clock.now()|>Ptime.to_spaninletd,ps=Ptime.Span.to_d_psspaninletd=Int64.(mul(of_intd)ns_in_a_day)inletns=Int64.(divps1_000L)inInt64.(adddns)end(** {2 Interface to data collector} *)(** Collector types
These types are used by backend implementations, to send events to
collectors such as Jaeger.
Note: most users will not need to touch this module *)moduleCollector=structopenPrototype'msgsender={send:'a.'msg->ret:(unit->'a)->'a}(** Sender interface for a message of type [msg].
Inspired from Logs' reporter
(see {{:https://erratique.ch/software/logs/doc/Logs/index.html#sync} its doc})
but without [over] as it doesn't make much sense in presence
of batching.
The [ret] callback is used to return the desired type (unit, or
a Lwt promise, or anything else) once the event has been transferred
to the backend.
It doesn't mean the event has been collected yet, it
could sit in a batch queue for a little while.
*)(** Collector client interface. *)moduletypeBACKEND=sigvalsend_trace:Trace.resource_spanslistsendervalsend_metrics:Metrics.resource_metricslistsendervalsend_logs:Logs.resource_logslistsendervalsignal_emit_gc_metrics:unit->unit(** Signal the backend that it should emit GC metrics when it has the
chance. This should be installed in a GC alarm or another form
of regular trigger. *)valtick:unit->unit(** Should be called regularly for background processing,
timeout checks, etc. *)valset_on_tick_callbacks:(unit->unit)listref->unit(** Give the collector the list of callbacks to be executed
when [tick()] is called. Each such callback should be short and
reentrant. Depending on the collector's implementation, it might be
called from a thread that is not the one that called [on_tick]. *)valcleanup:unit->unitendtypebackend=(moduleBACKEND)(* hidden *)openstructleton_tick_cbs_=ref[]letbackend:backendoptionref=refNoneend(** Set collector backend *)letset_backend(b:backend):unit=let(moduleB)=binB.set_on_tick_callbackson_tick_cbs_;backend:=Someb(** Is there a configured backend? *)let[@inline]has_backend():bool=!backend!=None(** Current backend, if any *)let[@inline]get_backend():backendoption=!backendletsend_trace(l:Trace.resource_spanslist)~ret=match!backendwith|None->ret()|Some(moduleB)->B.send_trace.sendl~retletsend_metrics(l:Metrics.resource_metricslist)~ret=match!backendwith|None->ret()|Some(moduleB)->B.send_metrics.sendl~retletsend_logs(l:Logs.resource_logslist)~ret=match!backendwith|None->ret()|Some(moduleB)->B.send_logs.sendl~retlet[@inline]rand_bytes_16()=!Rand_bytes.rand_bytes_16()let[@inline]rand_bytes_8()=!Rand_bytes.rand_bytes_8()leton_tickf=on_tick_cbs_:=f::!on_tick_cbs_(** Do background work. Call this regularly if the collector doesn't
already have a ticker thread or internal timer. *)lettick()=match!backendwith|None->()|Some(moduleB)->B.tick()endmoduleUtil_=structletbytes_to_hex(b:bytes):string=leti_to_hex(i:int)=ifi<10thenChar.chr(i+Char.code'0')elseChar.chr(i-10+Char.code'a')inletres=Bytes.create(2*Bytes.lengthb)infori=0toBytes.lengthb-1doletn=Char.code(Bytes.getbi)inBytes.setres(2*i)(i_to_hex((nland0xf0)lsr4));Bytes.setres((2*i)+1)(i_to_hex(nland0x0f))done;Bytes.unsafe_to_stringresletbytes_of_hex(s:string):bytes=letn_of_c=function|'0'..'9'asc->Char.codec-Char.code'0'|'a'..'f'asc->10+Char.codec-Char.code'a'|_->raise(Invalid_argument"invalid hex char")inifString.lengthsmod2<>0thenraise(Invalid_argument"hex sequence must be of even length");letres=Bytes.make(String.lengths/2)'\x00'infori=0to(String.lengths/2)-1doletn1=n_of_c(String.gets(2*i))inletn2=n_of_c(String.gets((2*i)+1))inletn=(n1lsl4)lorn2inBytes.setresi(Char.chrn)done;resend(** {2 Identifiers} *)(** Trace ID.
This 16 bytes identifier is shared by all spans in one trace. *)moduleTrace_id:sigtypetvalcreate:unit->tvalpp:Format.formatter->t->unitvalto_bytes:t->bytesvalof_bytes:bytes->tvalto_hex:t->stringvalof_hex:string->tend=structopenProto.Tracetypet=bytesletto_bytesself=selfletcreate():t=Collector.rand_bytes_16()letof_bytesb=ifBytes.lengthb=16thenbelseraise(Invalid_argument"trace IDs must be 16 bytes in length")letto_hexself=Util_.bytes_to_hexselfletof_hexs=of_bytes(Util_.bytes_of_hexs)letppfmtt=Format.fprintffmt"%s"(to_hext)end(** Unique ID of a span. *)moduleSpan_id:sigtypetvalcreate:unit->tvalpp:Format.formatter->t->unitvalto_bytes:t->bytesvalof_bytes:bytes->tvalto_hex:t->stringvalof_hex:string->tend=structopenProto.Tracetypet=bytesletto_bytesself=selfletcreate():t=Collector.rand_bytes_8()letof_bytesb=ifBytes.lengthb=8thenbelseraise(Invalid_argument"span IDs must be 8 bytes in length")letto_hexself=Util_.bytes_to_hexselfletof_hexs=of_bytes(Util_.bytes_of_hexs)letppfmtt=Format.fprintffmt"%s"(to_hext)end(** {2 Attributes and conventions} *)moduleConventions=structmoduleAttributes=structmoduleProcess=structmoduleRuntime=structletname="process.runtime.name"letversion="process.runtime.version"letdescription="process.runtime.description"endendmoduleService=structletname="service.name"letnamespace="service.namespace"letinstance_id="service.instance.id"letversion="service.version"endendmoduleMetrics=structmoduleProcess=structmoduleRuntime=structmoduleOcaml=structmoduleGC=structletcompactions="process.runtime.ocaml.gc.compactions"letmajor_collections="process.runtime.ocaml.gc.major_collections"letmajor_heap="process.runtime.ocaml.gc.major_heap"letminor_allocated="process.runtime.ocaml.gc.minor_allocated"letminor_collections="process.runtime.ocaml.gc.minor_collections"endendendendendendtypevalue=[`Intofint|`Stringofstring|`Boolofbool|`None]typekey_value=string*value(**/**)let_conv_value=letopenProto.Commoninfunction|`Inti->Some(Int_value(Int64.of_inti))|`Strings->Some(String_values)|`Boolb->Some(Bool_valueb)|`None->None(**/**)(**/**)let_conv_key_value(k,v)=letopenProto.Commoninletvalue=_conv_valuevindefault_key_value~key:k~value()(**/**)(** {2 Global settings} *)(** Process-wide metadata, environment variables, etc. *)moduleGlobals=structopenProto.Common(** Main service name metadata *)letservice_name=ref"unknown_service"(** Namespace for the service *)letservice_namespace=refNone(** Unique identifier for the service *)letservice_instance_id=refNoneletinstrumentation_library=default_instrumentation_library~version:"0.2"~name:"ocaml-opentelemetry"()(** Global attributes, initially set
via OTEL_RESOURCE_ATTRIBUTES and modifiable
by the user code. They will be attached to each outgoing metrics/traces. *)letglobal_attributes:key_valuelistref=letparse_pairs=matchString.split_on_char'='swith|[a;b]->default_key_value~key:a~value:(Some(String_valueb))()|_->failwith(Printf.sprintf"invalid attribute: %S"s)inref@@trySys.getenv"OTEL_RESOURCE_ATTRIBUTES"|>String.split_on_char','|>List.mapparse_pairwith_->[](** Add a global attribute *)letadd_global_attribute(key:string)(v:value):unit=global_attributes:=_conv_key_value(key,v)::!global_attributes(* add global attributes to this list *)letmerge_global_attributes_into:_list=letnot_redundantkv=List.for_all(funkv'->kv.key<>kv'.key)intoinList.rev_append(List.filternot_redundant!global_attributes)intoletmk_attributes?(service_name=!service_name)?(attrs=[])():_list=letl=List.map_conv_key_valueattrsinletl=default_key_value~key:Conventions.Attributes.Service.name~value:(Some(String_valueservice_name))()::linletl=match!service_instance_idwith|None->l|Somev->default_key_value~key:Conventions.Attributes.Service.instance_id~value:(Some(String_valuev))()::linletl=match!service_namespacewith|None->l|Somev->default_key_value~key:Conventions.Attributes.Service.namespace~value:(Some(String_valuev))()::linl|>merge_global_attributes_end(** {2 Traces and Spans} *)(** Events.
Events occur at a given time and can carry attributes. They always
belong in a span. *)moduleEvent:sigopenProto.Tracetypet=span_eventvalmake:?time_unix_nano:Timestamp_ns.t->?attrs:key_valuelist->string->tend=structopenProto.Tracetypet=span_eventletmake?(time_unix_nano=Timestamp_ns.now_unix_ns())?(attrs=[])(name:string):t=letattrs=List.map_conv_key_valueattrsindefault_span_event~time_unix_nano~name~attributes:attrs()end(** Spans.
A Span is the workhorse of traces, it indicates an operation that
took place over a given span of time (indicated by start_time and end_time)
as part of a hierarchical trace. All spans in a given trace are bound by
the use of the same {!Trace_id.t}. *)moduleSpan:sigopenProto.Tracetypet=spantypeid=Span_id.ttypenonreckind=span_span_kind=|Span_kind_unspecified|Span_kind_internal|Span_kind_server|Span_kind_client|Span_kind_producer|Span_kind_consumertypenonrecstatus_code=status_status_code=|Status_code_unset|Status_code_ok|Status_code_errortypenonrecstatus=status={message:string;code:status_code;}valid:t->Span_id.ttypekey_value=string*[`Intofint|`Stringofstring|`Boolofbool|`None]valcreate:?kind:kind->?id:id->?trace_state:string->?attrs:key_valuelist->?events:Event.tlist->?status:status->trace_id:Trace_id.t->?parent:id->?links:(Trace_id.t*Span_id.t*string)list->start_time:Timestamp_ns.t->end_time:Timestamp_ns.t->string->t*id(** [create ~trace_id name] creates a new span with its unique ID.
@param trace_id the trace this belongs to
@param parent parent span, if any
@param links list of links to other spans, each with their trace state
(see {{: https://www.w3.org/TR/trace-context/#tracestate-header} w3.org}) *)end=structopenProto.Tracetypet=spantypeid=Span_id.ttypenonreckind=span_span_kind=|Span_kind_unspecified|Span_kind_internal|Span_kind_server|Span_kind_client|Span_kind_producer|Span_kind_consumertypekey_value=string*[`Intofint|`Stringofstring|`Boolofbool|`None]typenonrecstatus_code=status_status_code=|Status_code_unset|Status_code_ok|Status_code_errortypenonrecstatus=status={message:string;code:status_code;}letidself=Span_id.of_bytesself.span_idletcreate?(kind=Span_kind_unspecified)?(id=Span_id.create())?trace_state?(attrs=[])?(events=[])?status~trace_id?parent?(links=[])~start_time~end_timename:t*id=lettrace_id=Trace_id.to_bytestrace_idinletparent_span_id=Option.mapSpan_id.to_bytesparentinletattributes=List.map_conv_key_valueattrsinletlinks=List.map(fun(trace_id,span_id,trace_state)->lettrace_id=Trace_id.to_bytestrace_idinletspan_id=Span_id.to_bytesspan_idindefault_span_link~trace_id~span_id~trace_state())linksinletspan=default_span~trace_id?parent_span_id~span_id:(Span_id.to_bytesid)~attributes~events?trace_state~status~kind~name~links~start_time_unix_nano:start_time~end_time_unix_nano:end_time()inspan,idend(** Traces.
See {{: https://opentelemetry.io/docs/reference/specification/overview/#tracing-signal} the spec} *)moduleTrace=structopenProto.Tracetypespan=Span.tletmake_resource_spans?service_name?attrsspans=letils=default_instrumentation_library_spans~instrumentation_library:(SomeGlobals.instrumentation_library)~spans()inletattributes=Globals.mk_attributes?service_name?attrs()inletresource=Proto.Resource.default_resource~attributes()indefault_resource_spans~resource:(Someresource)~instrumentation_library_spans:[ils]()(** Sync emitter.
This instructs the collector to forward
the spans to some backend at a later point.
{b NOTE} be careful not to call this inside a Gc alarm, as it can
cause deadlocks. *)letemit?service_name?attrs(spans:spanlist):unit=letrs=make_resource_spans?service_name?attrsspansinCollector.send_trace[rs]~ret:(fun()->())typescope={trace_id:Trace_id.t;span_id:Span_id.t;mutableevents:Event.tlist;mutableattrs:Span.key_valuelist;}(** Scope to be used with {!with_}. *)(** Add an event to the scope. It will be aggregated into the span.
Note that this takes a function that produces an event, and will only
call it if there is an instrumentation backend. *)let[@inline]add_event(scope:scope)(ev:unit->Event.t):unit=ifCollector.has_backend()thenscope.events<-ev()::scope.events(** Add an attr to the scope. It will be aggregated into the span.
Note that this takes a function that produces attributes, and will only
call it if there is an instrumentation backend. *)let[@inline]add_attrs(scope:scope)(attrs:unit->Span.key_valuelist):unit=ifCollector.has_backend()thenscope.attrs<-List.rev_append(attrs())scope.attrs(** Sync span guard.
{b NOTE} be careful not to call this inside a Gc alarm, as it can
cause deadlocks. *)letwith_?trace_state?service_name?(attrs:(string*[<value])list=[])?kind?trace_id?parent?scope?linksname(f:scope->'a):'a=lettrace_id=matchtrace_id,scopewith|Sometrace_id,_->trace_id|None,Somescope->scope.trace_id|None,None->Trace_id.create()inletparent=matchparent,scopewith|Somespan_id,_->Somespan_id|None,Somescope->Somescope.span_id|None,None->Noneinletstart_time=Timestamp_ns.now_unix_ns()inletspan_id=Span_id.create()inletscope={trace_id;span_id;events=[];attrs}in(* called once we're done, to emit a span *)letfinallyres=letstatus=matchreswith|Ok()->default_status~code:Status_code_ok()|Errore->default_status~code:Status_code_error~message:e()inletspan,_=(* TODO: should the attrs passed to with_ go on the Span (in Span.create) or on the ResourceSpan (in emit)?
(question also applies to Opentelemetry_lwt.Trace.with) *)Span.create?kind~trace_id?parent?links~id:span_id?trace_state~attrs:scope.attrs~events:scope.events~start_time~end_time:(Timestamp_ns.now_unix_ns())~statusnameinemit?service_name[span]intryletx=fscopeinfinally(Ok());xwithe->finally(Error(Printexc.to_stringe));raiseeend(** {2 Metrics} *)(** Metrics.
See {{: https://opentelemetry.io/docs/reference/specification/overview/#metric-signal} the spec} *)moduleMetrics=structopenMetrics_typestypet=Metrics_types.metric(** A single metric, measuring some time-varying quantity or statistical
distribution. It is composed of one or more data points that have
precise values and time stamps. Each distinct metric should have a
distinct name. *)openstructlet_program_start=Timestamp_ns.now_unix_ns()end(** Number data point, as a float *)letfloat?(start_time_unix_nano=_program_start)?(now=Timestamp_ns.now_unix_ns())?(attrs=[])(d:float):number_data_point=letattributes=attrs|>List.map_conv_key_valueindefault_number_data_point~start_time_unix_nano~time_unix_nano:now~attributes~value:(As_doubled)()(** Number data point, as an int *)letint?(start_time_unix_nano=_program_start)?(now=Timestamp_ns.now_unix_ns())?(attrs=[])(i:int):number_data_point=letattributes=attrs|>List.map_conv_key_valueindefault_number_data_point~start_time_unix_nano~time_unix_nano:now~attributes~value:(As_int(Int64.of_inti))()(** Aggregation of a scalar metric, always with the current value *)letgauge~name?description?unit_(l:number_data_pointlist):t=letdata=Gauge(default_gauge~data_points:l())indefault_metric~name?description?unit_~data()typeaggregation_temporality=Metrics_types.aggregation_temporality=|Aggregation_temporality_unspecified|Aggregation_temporality_delta|Aggregation_temporality_cumulative(** Sum of all reported measurements over a time interval *)letsum~name?description?unit_?(aggregation_temporality=Aggregation_temporality_cumulative)?is_monotonic(l:number_data_pointlist):t=letdata=Sum(default_sum~data_points:l?is_monotonic~aggregation_temporality())indefault_metric~name?description?unit_~data()(** Histogram data
@param count number of values in population (non negative)
@param sum sum of values in population (0 if count is 0)
@param bucket_counts count value of histogram for each bucket. Sum of
the counts must be equal to [count].
length must be [1+length explicit_bounds]
@param explicit_bounds strictly increasing list of bounds for the buckets *)lethistogram_data_point?(start_time_unix_nano=_program_start)?(now=Timestamp_ns.now_unix_ns())?(attrs=[])?(exemplars=[])?(explicit_bounds=[])?sum~bucket_counts~count():histogram_data_point=letattributes=attrs|>List.map_conv_key_valueindefault_histogram_data_point~start_time_unix_nano~time_unix_nano:now~attributes~exemplars~bucket_counts~explicit_bounds~count?sum()lethistogram~name?description?unit_?aggregation_temporality(l:histogram_data_pointlist):t=letdata=Histogram(default_histogram~data_points:l?aggregation_temporality())indefault_metric~name?description?unit_~data()(* TODO: exponential history *)(* TODO: summary *)(* TODO: exemplar *)(** Aggregate metrics into a {!Proto.Metrics.resource_metrics} *)letmake_resource_metrics?service_name?attrs(l:tlist):resource_metrics=letlm=default_instrumentation_library_metrics~instrumentation_library:(SomeGlobals.instrumentation_library)~metrics:l()inletattributes=Globals.mk_attributes?service_name?attrs()inletresource=Proto.Resource.default_resource~attributes()indefault_resource_metrics~instrumentation_library_metrics:[lm]~resource:(Someresource)()(** Emit some metrics to the collector (sync). This blocks until
the backend has pushed the metrics into some internal queue, or
discarded them.
{b NOTE} be careful not to call this inside a Gc alarm, as it can
cause deadlocks.
*)letemit?attrs(l:tlist):unit=letrm=make_resource_metrics?attrslinCollector.send_metrics[rm]~ret:ignoreend(** Logs.
See {{: https://opentelemetry.io/docs/reference/specification/overview/#log-signal} the spec} *)moduleLogs=structopenLogs_typestypet=log_record(** Severity level of a log event *)typeseverity=Logs_types.severity_number=|Severity_number_unspecified|Severity_number_trace|Severity_number_trace2|Severity_number_trace3|Severity_number_trace4|Severity_number_debug|Severity_number_debug2|Severity_number_debug3|Severity_number_debug4|Severity_number_info|Severity_number_info2|Severity_number_info3|Severity_number_info4|Severity_number_warn|Severity_number_warn2|Severity_number_warn3|Severity_number_warn4|Severity_number_error|Severity_number_error2|Severity_number_error3|Severity_number_error4|Severity_number_fatal|Severity_number_fatal2|Severity_number_fatal3|Severity_number_fatal4letpp_severity=Logs_pp.pp_severity_numbertypeflags=Logs_types.log_record_flags=|Log_record_flag_unspecified|Log_record_flag_trace_flags_maskletpp_flags=Logs_pp.pp_log_record_flags(** Make a single log entry *)letmake?time?(observed_time_unix_nano=Timestamp_ns.now_unix_ns())?severity?log_level?flags?trace_id?span_id(body:value):t=lettime_unix_nano=matchtimewith|None->observed_time_unix_nano|Somet->tinlettrace_id=Option.mapTrace_id.to_bytestrace_idinletspan_id=Option.mapSpan_id.to_bytesspan_idinletbody=_conv_valuebodyindefault_log_record~time_unix_nano~observed_time_unix_nano?severity_number:severity?severity_text:log_level?flags?trace_id?span_id~body()(** Make a log entry whose body is a string *)letmake_str?time?observed_time_unix_nano?severity?log_level?flags?trace_id?span_id(body:string):t=make?time?observed_time_unix_nano?severity?log_level?flags?trace_id?span_id(`Stringbody)(** Make a log entry with format *)letmake_strf?time?observed_time_unix_nano?severity?log_level?flags?trace_id?span_idfmt=Format.kasprintf(funbod->make_str?time?observed_time_unix_nano?severity?log_level?flags?trace_id?span_idbod)fmt(** Emit logs.
This instructs the collector to send the logs to some backend at
a later date.
{b NOTE} be careful not to call this inside a Gc alarm, as it can
cause deadlocks. *)letemit?service_name?attrs(l:tlist):unit=letattributes=Globals.mk_attributes?service_name?attrs()inletresource=Proto.Resource.default_resource~attributes()inletll=default_instrumentation_library_logs~instrumentation_library:(SomeGlobals.instrumentation_library)~log_records:l()inletrl=default_resource_logs~resource:(Someresource)~instrumentation_library_logs:[ll]()inCollector.send_logs[rl]~ret:ignoreend(** A set of callbacks that produce metrics when called.
The metrics are automatically called regularly.
This allows applications to register metrics callbacks from various points
in the program (or even in libraries), and not worry about setting
alarms/intervals to emit them. *)moduleMetrics_callbacks=structopenstructletcbs_:(unit->Metrics.tlist)listref=ref[]end(** [register f] adds the callback [f] to the list.
[f] will be called at unspecified times and is expected to return
a list of metrics. It might be called regularly by the backend,
in particular (but not only) when {!Collector.tick} is called. *)letregisterf:unit=if!cbs_=[]then(* make sure we call [f] (and others) at each tick *)Collector.on_tick(fun()->letm=List.map(funf->f())!cbs_|>List.flatteninMetrics.emitm);cbs_:=f::!cbs_end(** {2 Utils} *)(** Implementation of the W3C Trace Context spec
https://www.w3.org/TR/trace-context/
*)moduleTrace_context=struct(** The traceparent header
https://www.w3.org/TR/trace-context/#traceparent-header
*)moduleTraceparent=structletname="traceparent"(** Parse the value of the traceparent header.
The values are of the form:
{[
{version}-{trace_id}-{parent_id}-{flags}
]}
For example:
{[ 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01 ]}
[{flags}] are currently ignored.
*)letof_valuestr:(Trace_id.t*Span_id.t,string)result=let(let*)=result_bindinletblit~offset~len~or_=letbuf=Bytes.createleninlet*str=matchBytes.blit_stringstroffsetbuf0lenwith|()->Ok(Bytes.unsafe_to_stringbuf)|exceptionInvalid_argument_->Erroror_inOk(str,offset+len)inletconsumeexpected~offset~or_=letlen=String.lengthexpectedinlet*str,offset=blit~offset~len~or_inifstr=expectedthenOkoffsetelseErroror_inletoffset=0inlet*offset=consume"00"~offset~or_:"Expected version 00"inlet*offset=consume"-"~offset~or_:"Expected delimiter"inlet*trace_id,offset=blit~offset~len:32~or_:"Expected 32-digit trace-id"inlet*trace_id=matchTrace_id.of_hextrace_idwith|trace_id->Oktrace_id|exceptionInvalid_argument_->Error"Expected hex-encoded trace-id"inlet*offset=consume"-"~offset~or_:"Expected delimiter"inlet*parent_id,offset=blit~offset~len:16~or_:"Expected 16-digit parent-id"inlet*parent_id=matchSpan_id.of_hexparent_idwith|parent_id->Okparent_id|exceptionInvalid_argument_->Error"Expected hex-encoded parent-id"inlet*offset=consume"-"~offset~or_:"Expected delimiter"inlet*_flags,_offset=blit~offset~len:2~or_:"Expected 2-digit flags"inOk(trace_id,parent_id)letto_value~(trace_id:Trace_id.t)~(parent_id:Span_id.t)():string=Printf.sprintf"00-%s-%s-00"(Trace_id.to_hextrace_id)(Span_id.to_hexparent_id)endend(** Export GC metrics.
These metrics are emitted after each GC collection. *)moduleGC_metrics:sigvalbasic_setup:unit->unit(** Setup a hook that will emit GC statistics regularly *)valget_runtime_attributes:unit->Span.key_valuelist(** Get OCaml name and version runtime attributes *)valget_metrics:unit->Metrics.tlist(** Get a few metrics from the current state of the GC *)end=struct(** See https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/process.md#process-runtimes *)letruntime_attributes=lazyConventions.Attributes.[Process.Runtime.name,`String"ocaml";Process.Runtime.version,`StringSys.ocaml_version;]letget_runtime_attributes()=Lazy.forceruntime_attributesletbasic_setup()=(* emit metrics when GC is called *)leton_gc()=matchCollector.get_backend()with|None->()|Some(moduleC)->C.signal_emit_gc_metrics()inignore(Gc.create_alarmon_gc:Gc.alarm)letbytes_per_word=Sys.word_size/8letword_to_bytesn=n*bytes_per_wordletword_to_bytes_fn=n*.floatbytes_per_wordletget_metrics():Metrics.tlist=letgc=Gc.quick_stat()inletnow=Timestamp_ns.now_unix_ns()inletopenMetricsinletopenConventions.Metricsin[gauge~name:Process.Runtime.Ocaml.GC.major_heap~unit_:"B"[int~now(word_to_bytesgc.Gc.heap_words)];sum~name:Process.Runtime.Ocaml.GC.minor_allocated~aggregation_temporality:Metrics.Aggregation_temporality_cumulative~is_monotonic:true~unit_:"B"[float~now(word_to_bytes_fgc.Gc.minor_words)];sum~name:Process.Runtime.Ocaml.GC.minor_collections~aggregation_temporality:Metrics.Aggregation_temporality_cumulative~is_monotonic:true[int~nowgc.Gc.minor_collections];sum~name:Process.Runtime.Ocaml.GC.major_collections~aggregation_temporality:Metrics.Aggregation_temporality_cumulative~is_monotonic:true[int~nowgc.Gc.major_collections];sum~name:Process.Runtime.Ocaml.GC.compactions~aggregation_temporality:Metrics.Aggregation_temporality_cumulative~is_monotonic:true[int~nowgc.Gc.compactions];]end