123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650(** Opentelemetry types and instrumentation *)openstructletspf=Printf.sprintfmoduleAtomic=Opentelemetry_atomic.AtomicmoduleAmbient_context=Opentelemetry_ambient_contextendmoduleLock=Lock(** Global lock. *)moduleRand_bytes=Rand_bytes(** Generation of random identifiers. *)moduleAList=AList(** Atomic list, for internal usage
@since 0.7 *)(** {2 Wire format} *)moduleProto=Opentelemetry_proto(** Protobuf types.
This is mostly useful internally. Users should not need to touch it. *)(** {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=structopenOpentelemetry_prototype'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)AList.t->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)moduleNoop_backend:BACKEND=structletnoop_sender_~ret=ret()letsend_trace:Trace.resource_spanslistsender={send=noop_sender}letsend_metrics:Metrics.resource_metricslistsender={send=noop_sender}letsend_logs:Logs.resource_logslistsender={send=noop_sender}letsignal_emit_gc_metrics()=()lettick()=()letset_on_tick_callbacks_cbs=()letcleanup()=()endmoduleDebug_backend(B:BACKEND):BACKEND=structopenProtoletsend_trace:Trace.resource_spanslistsender={send=(funl~ret->Format.eprintf"SPANS: %a@."(Format.pp_print_listTrace.pp_resource_spans)l;B.send_trace.sendl~ret);}letsend_metrics:Metrics.resource_metricslistsender={send=(funl~ret->Format.eprintf"METRICS: %a@."(Format.pp_print_listMetrics.pp_resource_metrics)l;B.send_metrics.sendl~ret);}letsend_logs:Logs.resource_logslistsender={send=(funl~ret->Format.eprintf"LOGS: %a@."(Format.pp_print_listLogs.pp_resource_logs)l;B.send_logs.sendl~ret);}letsignal_emit_gc_metrics()=B.signal_emit_gc_metrics()lettick()=B.tick()letset_on_tick_callbackscbs=B.set_on_tick_callbackscbsletcleanup()=B.cleanup()endletdebug_backend:backend=(moduleDebug_backend(Noop_backend))(* hidden *)openstructleton_tick_cbs_=AList.make()letbackend:backendoptionAtomic.t=Atomic.makeNoneend(** Set collector backend *)letset_backend(b:backend):unit=let(moduleB)=binB.set_on_tick_callbackson_tick_cbs_;Atomic.setbackend(Someb)(** Remove current backend, if any.
@since NEXT_RELEASE *)letremove_backend():unit=matchAtomic.exchangebackendNonewith|None->()|Some(moduleB)->B.tick();B.cleanup()(** Is there a configured backend? *)let[@inline]has_backend():bool=Atomic.getbackend!=None(** Current backend, if any *)let[@inline]get_backend():backendoption=Atomic.getbackendletsend_trace(l:Trace.resource_spanslist)~ret=matchAtomic.getbackendwith|None->ret()|Some(moduleB)->B.send_trace.sendl~retletsend_metrics(l:Metrics.resource_metricslist)~ret=matchAtomic.getbackendwith|None->ret()|Some(moduleB)->B.send_metrics.sendl~retletsend_logs(l:Logs.resource_logslist)~ret=matchAtomic.getbackendwith|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()let[@inline]on_tickf=AList.addon_tick_cbs_f(** Do background work. Call this regularly if the collector doesn't
already have a ticker thread or internal timer. *)lettick()=matchAtomic.getbackendwith|None->()|Some(moduleB)->B.tick()letwith_setup_debug_backendb?(enable=true)()f=let(moduleB:BACKEND)=binifenablethen(set_backendb;Fun.protect~finally:B.cleanupf)elsef()end(**/**)moduleUtil_=structletint_to_hex(i:int)=ifi<10thenChar.chr(i+Char.code'0')elseChar.chr(i-10+Char.code'a')letbytes_to_hex_intobresoff:unit=fori=0toBytes.lengthb-1doletn=Char.code(Bytes.getbi)inBytes.setres((2*i)+off)(int_to_hex((nland0xf0)lsr4));Bytes.setres((2*i)+1+off)(int_to_hex(nland0x0f))doneletbytes_to_hex(b:bytes):string=letres=Bytes.create(2*Bytes.lengthb)inbytes_to_hex_intobres0;Bytes.unsafe_to_stringresletint_of_hex=function|'0'..'9'asc->Char.codec-Char.code'0'|'a'..'f'asc->10+Char.codec-Char.code'a'|c->raise(Invalid_argument(spf"invalid hex char: %C"c))letbytes_of_hex_substring(s:string)offlen=iflenmod2<>0thenraise(Invalid_argument"hex sequence must be of even length");letres=Bytes.make(len/2)'\x00'infori=0to(len/2)-1doletn1=int_of_hex(String.gets(off+(2*i)))inletn2=int_of_hex(String.gets(off+(2*i)+1))inletn=(n1lsl4)lorn2inBytes.setresi(Char.chrn)done;resletbytes_of_hex(s:string):bytes=bytes_of_hex_substrings0(String.lengths)letbytes_non_zero(self:bytes):bool=tryfori=0toBytes.lengthself-1doifChar.code(Bytes.unsafe_getselfi)<>0thenraise_notraceExitdone;falsewithExit->trueend(**/**)(** {2 Identifiers} *)(** Trace ID.
This 16 bytes identifier is shared by all spans in one trace. *)moduleTrace_id:sigtypetvalcreate:unit->tvaldummy:tvalpp:Format.formatter->t->unitvalis_valid:t->boolvalto_bytes:t->bytesvalof_bytes:bytes->tvalto_hex:t->stringvalto_hex_into:t->bytes->int->unitvalof_hex:string->tvalof_hex_substring:string->int->tend=structtypet=bytesletto_bytesself=selfletdummy:t=Bytes.make16'\x00'letcreate():t=letb=Collector.rand_bytes_16()inassert(Bytes.lengthb=16);(* make sure the identifier is not all 0, which is a dummy identifier. *)Bytes.setb0(Char.unsafe_chr(Char.code(Bytes.getb0)lor1));bletof_bytesb=ifBytes.lengthb=16thenbelseraise(Invalid_argument"trace ID must be 16 bytes in length")letis_valid=Util_.bytes_non_zeroletto_hex=Util_.bytes_to_hexletto_hex_into=Util_.bytes_to_hex_intolet[@inline]of_hexs=of_bytes(Util_.bytes_of_hexs)let[@inline]of_hex_substringsoff=of_bytes(Util_.bytes_of_hex_substringsoff32)letppfmtt=Format.fprintffmt"%s"(to_hext)end(** Hmap key to carry around a {!Trace_id.t}, to remember what the current
trace is.
@since 0.8 *)letk_trace_id:Trace_id.tHmap.key=Hmap.Key.create()(** Unique ID of a span. *)moduleSpan_id:sigtypetvalcreate:unit->tvaldummy:tvalpp:Format.formatter->t->unitvalis_valid:t->boolvalto_bytes:t->bytesvalof_bytes:bytes->tvalto_hex:t->stringvalto_hex_into:t->bytes->int->unitvalof_hex:string->tvalof_hex_substring:string->int->tend=structtypet=bytesletto_bytesself=selfletdummy:t=Bytes.make8'\x00'letcreate():t=letb=Collector.rand_bytes_8()inassert(Bytes.lengthb=8);(* make sure the identifier is not all 0, which is a dummy identifier. *)Bytes.setb0(Char.unsafe_chr(Char.code(Bytes.getb0)lor1));bletis_valid=Util_.bytes_non_zeroletof_bytesb=ifBytes.lengthb=8thenbelseraise(Invalid_argument"span IDs must be 8 bytes in length")letto_hex=Util_.bytes_to_hexletto_hex_into=Util_.bytes_to_hex_intolet[@inline]of_hexs=of_bytes(Util_.bytes_of_hexs)let[@inline]of_hex_substringsoff=of_bytes(Util_.bytes_of_hex_substringsoff16)letppfmtt=Format.fprintffmt"%s"(to_hext)end(** Span context. This bundles up a trace ID and parent ID.
{{: https://opentelemetry.io/docs/specs/otel/trace/api/#spancontext} https://opentelemetry.io/docs/specs/otel/trace/api/#spancontext}
@since 0.7 *)moduleSpan_ctx:sigtypetvalmake:?sampled:bool->trace_id:Trace_id.t->parent_id:Span_id.t->unit->tvaldummy:t(** Invalid span context, to be used as a placeholder *)valis_valid:t->boolvaltrace_id:t->Trace_id.tvalparent_id:t->Span_id.tvalsampled:t->boolvalto_w3c_trace_context:t->bytesvalof_w3c_trace_context:bytes->(t,string)resultvalof_w3c_trace_context_exn:bytes->t(** @raise Invalid_argument if parsing failed *)end=struct(* TODO: trace state *)typet={trace_id:Trace_id.t;parent_id:Span_id.t;sampled:bool;}letdummy={trace_id=Trace_id.dummy;parent_id=Span_id.dummy;sampled=false}letmake?(sampled=false)~trace_id~parent_id():t={trace_id;parent_id;sampled}let[@inline]is_validself=Trace_id.is_validself.trace_id&&Span_id.is_validself.parent_idlet[@inline]sampledself=self.sampledlet[@inline]trace_idself=self.trace_idlet[@inline]parent_idself=self.parent_idletto_w3c_trace_context(self:t):bytes=letbs=Bytes.create55inBytes.setbs0'0';Bytes.setbs1'0';Bytes.setbs2'-';Trace_id.to_hex_intoself.trace_idbs3;(* +32 *)Bytes.setbs(3+32)'-';Span_id.to_hex_intoself.parent_idbs36;(* +16 *)Bytes.setbs52'-';Bytes.setbs53'0';Bytes.setbs54(ifself.sampledthen'1'else'0');bsletof_w3c_trace_contextbs:_result=tryifBytes.lengthbs<>55theninvalid_arg"trace context must be 55 bytes";(matchint_of_string_opt(Bytes.sub_stringbs02)with|Some0->()|Somen->invalid_arg@@spf"version is %d, expected 0"n|None->invalid_arg"expected 2-digit version");ifBytes.getbs2<>'-'theninvalid_arg"expected '-' before trace_id";lettrace_id=tryTrace_id.of_hex_substring(Bytes.unsafe_to_stringbs)3withInvalid_argumentmsg->invalid_arg(spf"in trace id: %s"msg)inifBytes.getbs(3+32)<>'-'theninvalid_arg"expected '-' before parent_id";letparent_id=trySpan_id.of_hex_substring(Bytes.unsafe_to_stringbs)36withInvalid_argumentmsg->invalid_arg(spf"in span id: %s"msg)inifBytes.getbs52<>'-'theninvalid_arg"expected '-' after parent_id";letsampled=int_of_string_opt(Bytes.sub_stringbs532)=Some1in(* ignore flags *)Ok{trace_id;parent_id;sampled}withInvalid_argumentmsg->Errormsgletof_w3c_trace_context_exnbs=matchof_w3c_trace_contextbswith|Okt->t|Errormsg->invalid_arg@@spf"invalid w3c trace context: %s"msgend(** Hmap key to carry around a {!Span_ctx.t}, e.g. to remember what the current
parent span is.
@since 0.8 *)letk_span_ctx:Span_ctx.tHmap.key=Hmap.Key.create()(** {2 Attributes and conventions} *)(** Semantic conventions
{{: https://opentelemetry.io/docs/specs/semconv/} https://opentelemetry.io/docs/specs/semconv/} *)moduleConventions=structmoduleAttributes=structmoduleProcess=structmoduleRuntime=structletname="process.runtime.name"letversion="process.runtime.version"letdescription="process.runtime.description"endend(** https://opentelemetry.io/docs/specs/semconv/attributes-registry/code/ *)moduleCode=struct(** Int *)letcolumn="code.column"letfilepath="code.filepath"letfunction_="code.function"(** int *)letline="code.lineno"letnamespace="code.namespace"letstacktrace="code.stacktrace"endmoduleService=structletname="service.name"letnamespace="service.namespace"letinstance_id="service.instance.id"letversion="service.version"endmoduleHTTP=structleterror_type="error.type"letrequest_method="http.request.method"letroute="http.route"leturl_full="url.full"(** HTTP status code, int *)letresponse_status_code="http.response.status_code"letserver_address="server.address"letserver_port="server.port"(** http or https *)leturl_scheme="url.scheme"end(** https://github.com/open-telemetry/semantic-conventions/blob/main/docs/resource/host.md *)moduleHost=structletid="host.id"letname="host.name"lettype_="host.type"letarch="host.arch"letip="host.ip"letmac="host.mac"letimage_id="host.image.id"letimage_name="host.image.name"letimage_version="host.image.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"endendendend(** https://opentelemetry.io/docs/specs/semconv/http/ *)moduleHTTP=structmoduleServer=structletrequest_duration="http.server.request.duration"letactive_requests="http.server.active_requests"(** Histogram *)letrequest_body_size="http.server.request.body.size"(** Histogram *)letresponse_body_size="http.server.response.body.size"endmoduleClient=structletrequest_duration="http.client.request.duration"(** Histogram *)letrequest_body_size="http.client.request.body.size"(** Histogram *)letresponse_body_size="http.client.response.body.size"endendendendtypevalue=[`Intofint|`Stringofstring|`Boolofbool|`Floatoffloat|`None](** A value in a key/value attribute *)typekey_value=string*valueopenstructlet_conv_value=letopenProto.Commoninfunction|`Inti->Some(Int_value(Int64.of_inti))|`Strings->Some(String_values)|`Boolb->Some(Bool_valueb)|`Floatf->Some(Double_valuef)|`None->Nonelet_conv_key_value(k,v)=letopenProto.Commoninletvalue=_conv_valuevindefault_key_value~key:k~value()end(** {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_scope~version:"0.11.1"~name:"ocaml-otel"()(** 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)into(** Default span kind in {!Span.create}.
This will be used in all spans that do not specify [~kind] explicitly;
it is set to "internal", following directions from the [.proto] file.
It can be convenient to set "client" or "server" uniformly in here.
@since 0.4 *)letdefault_span_kind=refProto.Trace.Span_kind_internalletmk_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(** Span Link
A pointer from the current span to another span in the same trace or in a
different trace. For example, this can be used in batching operations,
where a single batch handler processes multiple requests from different
traces or when the handler receives a request from a different project.
*)moduleSpan_link:sigopenProto.Tracetypet=span_linkvalmake:trace_id:Trace_id.t->span_id:Span_id.t->?trace_state:string->?attrs:key_valuelist->?dropped_attributes_count:int->unit->tvalof_span_ctx:?trace_state:string->?attrs:key_valuelist->?dropped_attributes_count:int->Span_ctx.t->tend=structopenProto.Tracetypet=span_linkletmake~trace_id~span_id?trace_state?(attrs=[])?dropped_attributes_count():t=letattributes=List.map_conv_key_valueattrsinletdropped_attributes_count=Option.mapInt32.of_intdropped_attributes_countindefault_span_link~trace_id:(Trace_id.to_bytestrace_id)~span_id:(Span_id.to_bytesspan_id)?trace_state~attributes?dropped_attributes_count()let[@inline]of_span_ctx?trace_state?attrs?dropped_attributes_count(ctx:Span_ctx.t):t=make~trace_id:(Span_ctx.trace_idctx)~span_id:(Span_ctx.parent_idctx)?trace_state?attrs?dropped_attributes_count()endmoduleSpan_status:sigopenProto.Tracetypet=status={message:string;code:status_status_code;}typecode=status_status_code=|Status_code_unset|Status_code_ok|Status_code_errorvalmake:message:string->code:code->tend=structopenProto.Tracetypet=status={message:string;code:status_status_code;}typecode=status_status_code=|Status_code_unset|Status_code_ok|Status_code_errorletmake~message~code={message;code}end(** @since NEXT_RELEASE *)moduleSpan_kind:sigopenProto.Tracetypet=span_span_kind=|Span_kind_unspecified|Span_kind_internal|Span_kind_server|Span_kind_client|Span_kind_producer|Span_kind_consumerend=structopenProto.Tracetypet=span_span_kind=|Span_kind_unspecified|Span_kind_internal|Span_kind_server|Span_kind_client|Span_kind_producer|Span_kind_consumerend(** {2 Scopes} *)(** Scopes.
A scope is a trace ID and the span ID of the currently active span.
*)moduleScope:sigtypeitem_listtypet={trace_id:Trace_id.t;span_id:Span_id.t;mutableitems:item_list;}valattrs:t->key_valuelistvalevents:t->Event.tlistvallinks:t->Span_link.tlistvalstatus:t->Span_status.toptionvalkind:t->Span_kind.toptionvalmake:trace_id:Trace_id.t->span_id:Span_id.t->?events:Event.tlist->?attrs:key_valuelist->?links:Span_link.tlist->?status:Span_status.t->unit->tvalto_span_link:?trace_state:string->?attrs:key_valuelist->?dropped_attributes_count:int->t->Span_link.t(** Turn the scope into a span link *)valto_span_ctx:t->Span_ctx.t(** Turn the scope into a span context *)valadd_event:t->(unit->Event.t)->unit(** 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. *)valrecord_exception:t->exn->Printexc.raw_backtrace->unitvaladd_attrs:t->(unit->key_valuelist)->unit(** Add attributes 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. *)valadd_links:t->(unit->Span_link.tlist)->unit(** Add links to the scope. It will be aggregated into the span.
Note that this takes a function that produces links, and will only
call it if there is an instrumentation backend. *)valset_status:t->Span_status.t->unit(** set the span status.
Note that this function will be
called only if there is an instrumentation backend. *)valset_kind:t->Span_kind.t->unit(** Set the span's kind.
@since NEXT_RELEASE *)valambient_scope_key:tAmbient_context.key(** The opaque key necessary to access/set the ambient scope with
{!Ambient_context}. *)valget_ambient_scope:?scope:t->unit->toption(** Obtain current scope from {!Ambient_context}, if available. *)valwith_ambient_scope:t->(unit->'a)->'a(** [with_ambient_scope sc thunk] calls [thunk()] in a context where [sc] is
the (thread|continuation)-local scope, then reverts to the previous local
scope, if any.
@see <https://github.com/ELLIOTTCABLE/ocaml-ambient-context> ambient-context docs *)end=structtypeitem_list=|Nil|EvofEvent.t*item_list|Attrofkey_value*item_list|Span_linkofSpan_link.t*item_list|Span_statusofSpan_status.t*item_list|Span_kindofSpan_kind.t*item_listtypet={trace_id:Trace_id.t;span_id:Span_id.t;mutableitems:item_list;}letattrsscope=letrecloopacc=function|Nil->acc|Attr(attr,l)->loop(attr::acc)l|Ev(_,l)|Span_kind(_,l)|Span_link(_,l)|Span_status(_,l)->loopacclinloop[]scope.itemsleteventsscope=letrecloopacc=function|Nil->acc|Ev(event,l)->loop(event::acc)l|Attr(_,l)|Span_kind(_,l)|Span_link(_,l)|Span_status(_,l)->loopacclinloop[]scope.itemsletlinksscope=letrecloopacc=function|Nil->acc|Span_link(span_link,l)->loop(span_link::acc)l|Ev(_,l)|Span_kind(_,l)|Attr(_,l)|Span_status(_,l)->loopacclinloop[]scope.itemsletstatusscope=letrecloop=function|Nil->None|Span_status(status,_)->Somestatus|Ev(_,l)|Attr(_,l)|Span_kind(_,l)|Span_link(_,l)->looplinloopscope.itemsletkindscope=letrecloop=function|Nil->None|Span_kind(k,_)->Somek|Ev(_,l)|Span_status(_,l)|Attr(_,l)|Span_link(_,l)->looplinloopscope.itemsletmake~trace_id~span_id?(events=[])?(attrs=[])?(links=[])?status():t=letitems=letitems=matchstatuswith|None->Nil|Somestatus->Span_status(status,Nil)inletitems=List.fold_left(funaccev->Ev(ev,acc))itemseventsinletitems=List.fold_left(funaccattr->Attr(attr,acc))itemsattrsinList.fold_left(funacclink->Span_link(link,acc))itemslinksin{trace_id;span_id;items}let[@inline]to_span_link?trace_state?attrs?dropped_attributes_count(self:t):Span_link.t=Span_link.make?trace_state?attrs?dropped_attributes_count~trace_id:self.trace_id~span_id:self.span_id()let[@inline]to_span_ctx(self:t):Span_ctx.t=Span_ctx.make~trace_id:self.trace_id~parent_id:self.span_id()let[@inline]add_event(scope:t)(ev:unit->Event.t):unit=ifCollector.has_backend()thenscope.items<-Ev(ev(),scope.items)let[@inline]record_exception(scope:t)(exn:exn)(bt:Printexc.raw_backtrace):unit=ifCollector.has_backend()then(letev=Event.make"exception"~attrs:["message",`String(Printexc.to_stringexn);"type",`String(Printexc.exn_slot_nameexn);"stacktrace",`String(Printexc.raw_backtrace_to_stringbt);]inscope.items<-Ev(ev,scope.items))let[@inline]add_attrs(scope:t)(attrs:unit->key_valuelist):unit=ifCollector.has_backend()thenscope.items<-List.fold_left(funaccattr->Attr(attr,acc))scope.items(attrs())let[@inline]add_links(scope:t)(links:unit->Span_link.tlist):unit=ifCollector.has_backend()thenscope.items<-List.fold_left(funacclink->Span_link(link,acc))scope.items(links())letset_status(scope:t)(status:Span_status.t):unit=ifCollector.has_backend()thenscope.items<-Span_status(status,scope.items)letset_kind(scope:t)(k:Span_kind.t):unit=ifCollector.has_backend()thenscope.items<-Span_kind(k,scope.items)letambient_scope_key:tAmbient_context.key=Ambient_context.create_key()letget_ambient_scope?scope():toption=matchscopewith|Some_->scope|None->Ambient_context.getambient_scope_keylet[@inline]with_ambient_scope(sc:t)(f:unit->'a):'a=Ambient_context.with_bindingambient_scope_keysc(fun_->f())end(** {2 Traces} *)(** 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.ttypekind=Span_kind.t=|Span_kind_unspecified|Span_kind_internal|Span_kind_server|Span_kind_client|Span_kind_producer|Span_kind_consumervalid:t->Span_id.ttypekey_value=string*[`Intofint|`Stringofstring|`Boolofbool|`Floatoffloat|`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:Span_link.tlist->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.ttypekind=Span_kind.t=|Span_kind_unspecified|Span_kind_internal|Span_kind_server|Span_kind_client|Span_kind_producer|Span_kind_consumertypekey_value=string*[`Intofint|`Stringofstring|`Boolofbool|`Floatoffloat|`None]letidself=Span_id.of_bytesself.span_idletcreate?(kind=!Globals.default_span_kind)?(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_valueattrsinletspan=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_scope_spans~scope:(SomeGlobals.instrumentation_library)~spans()inletattributes=Globals.mk_attributes?service_name?attrs()inletresource=Proto.Resource.default_resource~attributes()indefault_resource_spans~resource:(Someresource)~scope_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=Scope.t={trace_id:Trace_id.t;span_id:Span_id.t;mutableitems:Scope.item_list;}[@@deprecated"use Scope.t"]letadd_event=Scope.add_event[@@deprecated"use Scope.add_event"]letadd_attrs=Scope.add_attrs[@@deprecated"use Scope.add_attrs"]letwith_'?(force_new_trace_id=false)?trace_state?service_name?(attrs:(string*[<value])list=[])?kind?trace_id?parent?scope?(links=[])namecb=letscope=ifforce_new_trace_idthenNoneelseScope.get_ambient_scope?scope()inlettrace_id=matchtrace_id,scopewith|_whenforce_new_trace_id->Trace_id.create()|Sometrace_id,_->trace_id|None,Somescope->scope.trace_id|None,None->Trace_id.create()inletparent=matchparent,scopewith|_whenforce_new_trace_id->None|Somespan_id,_->Somespan_id|None,Somescope->Somescope.span_id|None,None->Noneinletstart_time=Timestamp_ns.now_unix_ns()inletspan_id=Span_id.create()inletscope=Scope.make~trace_id~span_id~attrs~links()in(* called once we're done, to emit a span *)letfinallyres=letstatus=matchScope.statusscopewith|Somestatus->Somestatus|None->(matchreswith|Ok()->(* By default, all spans are Unset, which means a span completed without error.
The Ok status is reserved for when you need to explicitly mark a span as successful
rather than stick with the default of Unset (i.e., “without error”).
https://opentelemetry.io/docs/languages/go/instrumentation/#set-span-status *)None|Error(e,bt)->Scope.record_exceptionscopeebt;Some(default_status~code:Status_code_error~message:(Printexc.to_stringe)()))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:(Scope.linksscope)~id:span_id?trace_state~attrs:(Scope.attrsscope)~events:(Scope.eventsscope)~start_time~end_time:(Timestamp_ns.now_unix_ns())?statusnameinemit?service_name[span]inletthunk()=(* set global scope in this thread *)Scope.with_ambient_scopescope@@fun()->cbscopeinthunk,finally(** Sync span guard.
Notably, this includes {e implicit} scope-tracking: if called without a
[~scope] argument (or [~parent]/[~trace_id]), it will check in the
{!Ambient_context} for a surrounding environment, and use that as the
scope. Similarly, it uses {!Scope.with_ambient_scope} to {e set} a new
scope in the ambient context, so that any logically-nested calls to
{!with_} will use this span as their parent.
{b NOTE} be careful not to call this inside a Gc alarm, as it can
cause deadlocks.
@param force_new_trace_id if true (default false), the span will not use a
ambient scope, the [~scope] argument, nor [~trace_id], but will instead
always create fresh identifiers for this span *)letwith_?force_new_trace_id?trace_state?service_name?attrs?kind?trace_id?parent?scope?linksname(cb:Scope.t->'a):'a=letthunk,finally=with_'?force_new_trace_id?trace_state?service_name?attrs?kind?trace_id?parent?scope?linksnamecbintryletrv=thunk()infinally(Ok());rvwithe->letbt=Printexc.get_raw_backtrace()infinally(Error(e,bt));raiseeend(** {2 Metrics} *)(** Metrics.
See {{: https://opentelemetry.io/docs/reference/specification/overview/#metric-signal} the spec} *)moduleMetrics=structopenProtoopenProto.Metricstypet=Metrics.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.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_scope_metrics~scope:(SomeGlobals.instrumentation_library)~metrics:l()inletattributes=Globals.mk_attributes?service_name?attrs()inletresource=Proto.Resource.default_resource~attributes()indefault_resource_metrics~scope_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(** 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 Logs} *)(** Logs.
See {{: https://opentelemetry.io/docs/reference/specification/overview/#log-signal} the spec} *)moduleLogs=structopenOpentelemetry_protoopenLogstypet=log_record(** Severity level of a log event *)typeseverity=Logs.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_severity_numbertypeflags=Logs.log_record_flags=|Log_record_flags_do_not_use|Log_record_flags_trace_flags_maskletpp_flags=Logs.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_scope_logs~scope:(SomeGlobals.instrumentation_library)~log_records:l()inletrl=default_resource_logs~resource:(Someresource)~scope_logs:[ll]()inCollector.send_logs[rl]~ret:ignoreend(** {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=matchSpan_ctx.of_w3c_trace_context(Bytes.unsafe_of_stringstr)with|Oksp->Ok(Span_ctx.trace_idsp,Span_ctx.parent_idsp)|Error_ase->eletto_value?(sampled:booloption)~(trace_id:Trace_id.t)~(parent_id:Span_id.t)():string=letspan_ctx=Span_ctx.make?sampled~trace_id~parent_id()inBytes.unsafe_to_string@@Span_ctx.to_w3c_trace_contextspan_ctxendend(** 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 on every tick (assuming
a ticker thread) *)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()=leton_tick()=matchCollector.get_backend()with|None->()|Some(moduleC)->C.signal_emit_gc_metrics()inCollector.on_tickon_tickletbytes_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