123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111openCore_kernelopenAsync_kernelopenAsync_unixletuser_agent=sprintf"%s/%s"Config.nameConfig.versionletdsn_to_auth_header{Dsn.uri;public_key;private_key;_}time=letvalue=letbase=sprintf"Sentry sentry_version=7, sentry_client=%s, sentry_timestamp=%d, sentry_key=%s"user_agentTime.(to_span_since_epochtime|>Span.to_sec|>Float.iround_exn)public_keyin(* Only had a private key if we have one and we're talking over a secure
channel *)matchUri.schemeuri,private_keywith|Some"https",Someprivate_key->sprintf"%s, sentry_secret=%s"baseprivate_key|_->baseinCohttp.Header.init_with"X-Sentry-Auth"value;;letmake_headers~dsntimestamp=dsn_to_auth_headerdsntimestamp|>Fn.flipCohttp.Header.prepend_user_agentuser_agent|>funh->Cohttp.Header.addh"Content-Type""application/json";;letrecsend_request~headers~datauri=letbody=Cohttp_async.Body.of_stringdatainlet%bindresponse,body=Cohttp_async.Client.post~headers~bodyuriinifCohttp.Response.statusresponse|>Cohttp.Code.code_of_status|>Cohttp.Code.is_redirectionthenCohttp.Response.headersresponse|>Cohttp.Header.get_location|>function|None->failwithf"Redirect with no Location header from %s"(Uri.to_stringuri)()|Someuri->send_request~headers~dataurielsereturn(response,body);;typeapi_error={status:Cohttp.Code.status_code;error:stringoption;event:Event.t}[@@derivingsexp_of]exceptionApi_errorofapi_errorletsend_event_and_wait_exn~dsnevent=letheaders=make_headers~dsnevent.Event.timestampinleturi=Dsn.event_store_uridsninletdata=Event.to_json_stringeventinlet%bindresponse,body=send_request~headers~datauriinmatchCohttp.Response.statusresponsewith|`OK->Cohttp_async.Body.to_stringbody>>|Payloads_j.response_of_string>>|fun{Payloads_j.id}->id|status->leterror=Cohttp.Response.headersresponse|>Fn.flipCohttp.Header.get"X-Sentry-Error"inraise(Api_error{status;error;event});;letsend_event_and_wait~dsnevent=Monitor.try_with(fun()->send_event_and_wait_exn~dsnevent)>>|function|Okid->Log.Global.info"Successfully uploaded sentry event %s"(Uuid.unwrapevent.event_id);Someid|Errore->(matchMonitor.extract_exnewith|Api_error{status=`Too_many_requests;event;_}->Log.Global.error"Event %s not uploaded due to Sentry API limits."(Uuid.unwrapevent.event_id);None|_->Exn.to_stringe|>Log.Global.error"Failed to upload Sentry event: %s";None);;letevent_pipe=letreader,writer=Pipe.create()in(* Use a pipe to let us sent events asynchronously and still ensure that they're
all written before the program exits *)letclosep=ifnot(Pipe.is_closedp)then(Pipe.closep;Pipe.upstream_flushedp>>|ignore)elsereturn()inPipe.iter~flushed:When_value_processedreader~f:(fun(dsn,event)->send_event_and_wait~dsnevent|>Deferred.ignore_m)|>don't_wait_for;Shutdown.at_shutdown(fun()->closewriter>>=fun()->Pipe.downstream_flushedwriter>>=fun_->Log.Global.flushed());Gc.add_finalizer_exnwriter(Fn.composedon't_wait_forclose);writer;;letsend_event~dsnevent=Pipe.write_without_pushback_if_openevent_pipe(dsn,event)