123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290(* OCaml promise library
* http://www.ocsigen.org/lwt
* Copyright (C) 2002 Shawn Wagner <raevnos@pennmush.org>
* 2009 Jérémie Dimino <jeremie@dimino.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, with linking exceptions;
* either version 2.1 of the License, or (at your option) any later
* version. See COPYING file for details.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*)(* This code is an adaptation of [syslog-ocaml] *)includeLwt_log_coreopenLwt.Infixletprogram_name=Filename.basenameSys.executable_name(* Errors happening in this module are always logged to [stderr]: *)letlog_internfmt=Printf.eprintf("%s: Lwt_log: "^^fmt^^"\n%!")program_name(* +-----------------------------------------------------------------+
| Templates |
+-----------------------------------------------------------------+ *)letdate_stringtime=lettm=Unix.localtimetimeinletmonth_string=matchtm.Unix.tm_monwith|0->"Jan"|1->"Feb"|2->"Mar"|3->"Apr"|4->"May"|5->"Jun"|6->"Jul"|7->"Aug"|8->"Sep"|9->"Oct"|10->"Nov"|11->"Dec"|_->Printf.ksprintffailwith"Lwt_log.date_string: invalid month, %d"tm.Unix.tm_moninPrintf.sprintf"%s %2d %02d:%02d:%02d"month_stringtm.Unix.tm_mdaytm.Unix.tm_hourtm.Unix.tm_mintm.Unix.tm_secletrender~buffer~template~section~level~message=lettime=lazy(Unix.gettimeofday())inletfile,line,column=matchLwt.getlocation_keywith|Someloc->loc|None->("<unknown>",-1,-1)inBuffer.add_substitutebuffer(function|"date"->date_string(Lazy.forcetime)|"milliseconds"->Printf.sprintf"%03.0f"(mod_float(Lazy.forcetime*.1000.)1000.)|"name"->program_name|"pid"->string_of_int(Unix.getpid())|"message"->message|"level"->Lwt_log_core.string_of_levellevel|"section"->Section.namesection|"loc-file"->file|"loc-line"->string_of_intline|"loc-column"->string_of_intcolumn|var->Printf.ksprintfinvalid_arg"Lwt_log.render: unknown variable %S"var)template(* +-----------------------------------------------------------------+
| Predefined loggers |
+-----------------------------------------------------------------+ *)letchannel?(template="$(name): $(section): $(message)")~close_mode~channel()=make~output:(funsectionlevellines->Lwt_io.atomicbeginfunoc->letbuf=Buffer.create42inLwt_list.iter_s(funline->Buffer.clearbuf;render~buffer:buf~template~section~level~message:line;Buffer.add_charbuf'\n';Lwt_io.writeoc(Buffer.contentsbuf))lines>>=fun()->Lwt_io.flushocendchannel)~close:(matchclose_modewith|`Keep->Lwt.return|`Close->(fun()->Lwt_io.closechannel))let_=Lwt_log_core.default:=channel~close_mode:`Keep~channel:Lwt_io.stderr()letfile?(template="$(date): $(section): $(message)")?(mode=`Append)?(perm=0o640)~file_name()=letflags=matchmodewith|`Append->[Unix.O_WRONLY;Unix.O_CREAT;Unix.O_APPEND;Unix.O_NONBLOCK]|`Truncate->[Unix.O_WRONLY;Unix.O_CREAT;Unix.O_TRUNC;Unix.O_NONBLOCK]inLwt_unix.openfilefile_nameflagsperm>>=funfd->Lwt_unix.set_close_on_execfd;letoc=Lwt_io.of_fd~mode:Lwt_io.outputfdinLwt.return(channel~template~close_mode:`Close~channel:oc())letlevel_code=function|Fatal->0|Error->3|Warning->4|Notice->5|Info->6|Debug->7typesyslog_facility=[`Auth|`Authpriv|`Cron|`Daemon|`FTP|`Kernel|`Local0|`Local1|`Local2|`Local3|`Local4|`Local5|`Local6|`Local7|`LPR|`Mail|`News|`Syslog|`User|`UUCP|`NTP|`Security|`Console]letfacility_code=function|`Kernel->0|`User->1|`Mail->2|`Daemon->3|`Auth->4|`Syslog->5|`LPR->6|`News->7|`UUCP->8|`Cron->9|`Authpriv->10|`FTP->11|`NTP->12|`Security->13|`Console->14|`Local0->16|`Local1->17|`Local2->18|`Local3->19|`Local4->20|`Local5->21|`Local6->22|`Local7->23typesyslog_connection_type=STREAM|DGRAMletshutdownfd=Lwt_unix.shutdownfdUnix.SHUTDOWN_ALL;Lwt_unix.closefd(* Try to find a socket in [paths]. For each path it check that the
file is a socket and try to establish connection in DGRAM mode then in
STREAM mode. *)letsyslog_connectpaths=letrecloop=function|[]->(* No working socket found *)log_intern"no working socket found in {%s}; is syslogd running?"(String.concat", "(List.map(Printf.sprintf"\"%s\"")paths));Lwt.fail(Sys_error(Unix.error_messageUnix.ENOENT))|path::paths->begintryLwt.return(Some(Unix.statpath).Unix.st_kind)with|Unix.Unix_error(Unix.ENOENT,_,_)->Lwt.return_none|Unix.Unix_error(error,_,_)->log_intern"can not stat \"%s\": %s"path(Unix.error_messageerror);Lwt.return_noneend>>=(function|None->looppaths|SomeUnix.S_SOCK->begin(* First, we try with a dgram socket : *)letfd=Lwt_unix.socketUnix.PF_UNIXUnix.SOCK_DGRAM0inLwt.catch(fun()->Lwt_unix.connectfd(Unix.ADDR_UNIXpath)>>=fun()->Lwt_unix.set_close_on_execfd;Lwt.return(DGRAM,fd))(function|Unix.Unix_error(Unix.EPROTOTYPE,_,_)->beginLwt_unix.closefd>>=fun()->(* Then try with a stream socket: *)letfd=Lwt_unix.socketUnix.PF_UNIXUnix.SOCK_STREAM0inLwt.catch(fun()->Lwt_unix.connectfd(Unix.ADDR_UNIXpath)>>=fun()->Lwt_unix.set_close_on_execfd;Lwt.return(STREAM,fd))(function|Unix.Unix_error(error,_,_)->Lwt_unix.closefd>>=fun()->log_intern"can not connect to \"%s\": %s"path(Unix.error_messageerror);looppaths|exn->Lwt.failexn)end|Unix.Unix_error(error,_,_)->Lwt_unix.closefd>>=fun()->log_intern"can not connect to \"%s\": %s"path(Unix.error_messageerror);looppaths|exn->Lwt.failexn)[@ocaml.warning"-4"]end|Some_->log_intern"\"%s\" is not a socket"path;looppaths)[@ocaml.warning"-4"]inlooppaths(* Write the whole contents of a string on the given file
descriptor: *)letwrite_stringfdstr=letlen=String.lengthstrinletrecauxstart_ofs=ifstart_ofs=lenthenLwt.return_unitelseLwt_unix.write_stringfdstrstart_ofs(len-start_ofs)>>=funn->ifn<>0thenaux(start_ofs+n)elseLwt.return_unitinaux0lettruncatebufmax=ifBuffer.lengthbuf>maxthenbeginletsuffix="<truncated>"inletlen_suffix=String.lengthsuffixinBuffer.subbuf0(max-len_suffix)^suffixendelseBuffer.contentsbufletsyslog?(template="$(date) $(name)[$(pid)]: $(section): $(message)")?(paths=["/dev/log";"/var/run/log";"/var/run/syslog"])~facility()=letsyslog_socket=refNoneandmutex=Lwt_mutex.create()inletget_syslog()=match!syslog_socketwith|Somex->Lwt.returnx|None->syslog_connectpaths>>=funx->syslog_socket:=Somex;Lwt.returnxinmake~output:(funsectionlevellines->Lwt_mutex.with_lockmutex(fun()->letbuf=Buffer.create42inletmake_linesocket_typemsg=Buffer.clearbuf;Printf.bprintfbuf"<%d>"((facility_codefacilitylsl3)lorlevel_codelevel);render~buffer:buf~template~section~level~message:msg;ifsocket_type=STREAMthenBuffer.add_charbuf'\x00';truncatebuf1024inletrecprintsocket_typefd=function|[]->Lwt.return_unit|line::lines->Lwt.catch(fun()->write_stringfd(make_linesocket_typeline)>>=fun()->printsocket_typefdlines)(function|Unix.Unix_error(_,_,_)->(* Try to reconnect *)shutdownfd>>=fun()->syslog_socket:=None;get_syslog()>>=fun(socket_type,fd)->write_stringfd(make_linesocket_typeline)>>=fun()->printsocket_typefdlines|exn->Lwt.failexn)inget_syslog()>>=fun(socket_type,fd)->printsocket_typefdlines))~close:(fun()->match!syslog_socketwith|None->Lwt.return_unit|Some(_socket_type,fd)->shutdownfd)