123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313(*---------------------------------------------------------------------------
Copyright (c) 2015 The logs programmers. All rights reserved.
SPDX-License-Identifier: ISC
---------------------------------------------------------------------------*)letstrf=Format.asprintfletpp_print_textppfs=(* hint spaces and new lines with Format's funs *)letlen=String.lengthsinletleft=ref0inletright=ref0inletflush()=Format.pp_print_stringppf(String.subs!left(!right-!left));incrright;left:=!right;inwhile(!right<>len)doifs.[!right]='\n'then(flush();Format.pp_force_newlineppf())elseifs.[!right]=' 'then(flush();Format.pp_print_spaceppf())elseincrrightdone;if!left<>lenthenflush()(* Reporting levels *)typelevel=App|Error|Warning|Info|Debuglet_level=ref(SomeWarning)letlevel()=!_levelletpp_levelppf=function|App->()|Error->Format.pp_print_stringppf"ERROR"|Warning->Format.pp_print_stringppf"WARNING"|Info->Format.pp_print_stringppf"INFO"|Debug->Format.pp_print_stringppf"DEBUG"letlevel_to_string=function|None->"quiet"|SomeApp->"app"|SomeError->"error"|SomeWarning->"warning"|SomeInfo->"info"|SomeDebug->"debug"letlevel_of_string=function|"quiet"->OkNone|"app"->Ok(SomeApp)|"error"->Ok(SomeError)|"warning"->Ok(SomeWarning)|"info"->Ok(SomeInfo)|"debug"->Ok(SomeDebug)|l->Error(`Msg(strf"%S: unknown log level"l))(* Sources *)moduleSrc=structtypet={uid:int;name:string;doc:string;mutablelevel:leveloption}letuid=letid=ref(-1)infun()->incrid;!idletlist=ref[]letcreate?(doc="undocumented")name=letsrc={uid=uid();name;doc;level=!_level}inlist:=src::!list;srcletnames=s.nameletdocs=s.docletlevels=s.levelletset_levelsl=s.level<-lletequalsrc0src1=src0.uid=src1.uidletcomparesrc0src1=(compare:int->int->int)src0.uidsrc1.uidletppppfsrc=Format.fprintfppf"@[<1>(src@ @[<1>(name %S)@]@ @[<1>(uid %d)@] @[<1>(doc %S)@])@]"src.namesrc.uidsrc.docletlist()=!listendtypesrc=Src.tletdefault=Src.create"application"~doc:"The application log"letset_level?(all=true)l=_level:=l;ifallthenList.iter(funs->Src.set_levelsl)(Src.list())(* Message tags *)moduleTag=struct(* Universal type, see http://mlton.org/UniversalType *)typeuniv=exnletuniv(types)()=letmoduleM=structexceptionEofsoptionendin(funx->M.E(Somex)),(functionM.Ex->x|_->None)(* Tag definitions *)type'adef={uid:int;to_univ:'a->univ;of_univ:univ->'aoption;name:string;doc:string;pp:Format.formatter->'a->unit;}typedef_e=Def:'adef->def_eletlist=ref([]:def_elist)letuid=letid=ref(-1)infun()->incrid;!idletdef?(doc="undocumented")namepp=letto_univ,of_univ=univ()in{uid=uid();to_univ;of_univ;name;doc;pp}letnamed=d.nameletdocd=d.docletprinterd=d.ppletpp_defppfd=Format.fprintfppf"tag:%s"d.nameletlist()=!list(* Tag values *)typet=V:'adef*'a->tletppppf(V(d,v))=Format.fprintfppf"@[<1>(%a@ @[%a@])@]"pp_defdd.ppv(* Tag sets *)moduleKey=structtypet=V:'adef->tletcompare(Vk0)(Vk1)=(compare:int->int->int)k0.uidk1.uidendmoduleM=Map.Make(Key)typeset=tM.tletempty=M.emptyletis_empty=M.is_emptyletmemks=M.mem(Key.Vk)sletaddkvs=M.add(Key.Vk)(V(k,v))sletremks=M.remove(Key.Vk)sletfind:typea.adef->set->aoption=funks->trymatchM.find(Key.Vk)swith|V(k',v)->k.of_univ(k'.to_univv)withNot_found->Noneletgetks=matchfindkswith|None->invalid_arg(strf"tag named %s not found in set"k.name)|Somev->vletfoldfsacc=M.fold(fun_tacc->ftacc)saccletpp_setppfs=letpp_tagtagis_first=ifis_firstthen()elseFormat.fprintfppf"@,";Format.fprintfppf"%a"pptag;falseinFormat.fprintfppf"@[<1>{";ignore(foldpp_tagstrue);Format.fprintfppf"}@]";()end(* Reporters *)type('a,'b)msgf=(?header:string->?tags:Tag.set->('a,Format.formatter,unit,'b)format4->'a)->'btypereporter_mutex={lock:unit->unit;unlock:unit->unit}let_reporter_mutex=ref{lock=(fun()->());unlock=(fun()->())}letset_reporter_mutex~lock~unlock=_reporter_mutex:={lock;unlock}typereporter={report:'a'b.src->level->over:(unit->unit)->(unit->'b)->('a,'b)msgf->'b}letnop_reporter={report=fun__~overk_->over();k()}let_reporter=refnop_reporterletset_reporterr=_reporter:=rletreporter()=!_reporterletreportsrclevel~overkmsgf=letover()=over();!_reporter_mutex.unlock()in!_reporter_mutex.lock();!_reporter.reportsrclevel~overkmsgfletpp_headerppf(l,h)=matchhwith|None->ifl=Appthen()elseFormat.fprintfppf"[%a]"pp_levell|Someh->Format.fprintfppf"[%s]"hletpp_exec_header=letx=matchArray.lengthSys.argvwith|0->Filename.basenameSys.executable_name|n->Filename.basenameSys.argv.(0)inletpf=Format.fprintfinletpp_headerppf(l,h)=ifl=Appthen(matchhwithNone->()|Someh->pfppf"[%s] "h)elsematchhwith|None->pfppf"%s: [%a] "xpp_levell|Someh->pfppf"%s: [%s] "xhinpp_headerletformat_reporter?(pp_header=pp_exec_header)?(app=Format.std_formatter)?(dst=Format.err_formatter)()=letreportsrclevel~overkmsgf=letk_=over();k()inmsgf@@fun?header?tagsfmt->letppf=iflevel=AppthenappelsedstinFormat.kfprintfkppf("%a@["^^fmt^^"@]@.")pp_header(level,header)in{report}(* Log functions *)let_err_count=ref0leterr_count()=!_err_countletincr_err_count()=incr_err_countlet_warn_count=ref0letwarn_count()=!_warn_countletincr_warn_count()=incr_warn_counttype'alog=('a,unit)msgf->unitletover()=()letkmsg:typeab.(unit->b)->?src:src->level->(a,b)msgf->b=funk?(src=default)levelmsgf->matchSrc.levelsrcwith|None->k()|Somelevel'whenlevel>level'->(iflevel=Errorthenincr_err_countelseiflevel=Warningthenincr_warn_countelse());(k())|Some_->(iflevel=Errorthenincr_err_countelseiflevel=Warningthenincr_warn_countelse());reportsrclevel~overkmsgfletkunit_=()letmsg?srclevelmsgf=kmsgkunit?srclevelmsgfletapp?srcmsgf=kmsgkunit?srcAppmsgfleterr?srcmsgf=kmsgkunit?srcErrormsgfletwarn?srcmsgf=kmsgkunit?srcWarningmsgfletinfo?srcmsgf=kmsgkunit?srcInfomsgfletdebug?srcmsgf=kmsgkunit?srcDebugmsgf(* Log result errors *)leton_error?src?(level=Error)?header?tags~pp~use=function|Okv->v|Errore->kmsg(fun()->usee)?srclevel@@funm->m?header?tags"@[%a@]"ppeleton_error_msg?src?(level=Error)?header?tags~use=function|Okv->v|Error(`Msgmsg)->kmsguse?srclevel@@funm->m?header?tags"@[%a@]"pp_print_textmsg(* Source specific logging functions *)moduletypeLOG=sigvalmsg:level->'alogvalapp:'alogvalerr:'alogvalwarn:'alogvalinfo:'alogvaldebug:'alogvalkmsg:(unit->'b)->level->('a,'b)msgf->'bvalon_error:?level:level->?header:string->?tags:Tag.set->pp:(Format.formatter->'b->unit)->use:('b->'a)->('a,'b)result->'avalon_error_msg:?level:level->?header:string->?tags:Tag.set->use:(unit->'a)->('a,[`Msgofstring])result->'aendletsrc_logsrc=letmoduleLog=structletmsglevelmsgf=msg~srclevelmsgfletkmsgklevelmsgf=kmsgk~srclevelmsgfletappmsgf=msgAppmsgfleterrmsgf=msgErrormsgfletwarnmsgf=msgWarningmsgfletinfomsgf=msgInfomsgfletdebugmsgf=msgDebugmsgfleton_error?level?header?tags~pp~use=on_error~src?level?header?tags~pp~useleton_error_msg?level?header?tags~use=on_error_msg~src?level?header?tags~useendin(moduleLog:LOG)