;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; log4scm ;;; ;;; Author : Hans Oesterholt-Dijkema ;;; License : LGPL ;;; Short Description : This module provides a logging facility for mzscheme. ;;; ;;; $Id: log4scm.scm,v 1.28 2007/05/19 23:25:21 HansOesterholt Exp $ ;;; ;;; $Log: log4scm.scm,v $ ;;; Revision 1.28 2007/05/19 23:25:21 HansOesterholt ;;; *** empty log message *** ;;; ;;; Revision 1.27 2007/05/18 18:12:50 HansOesterholt ;;; Changed the logger function to work better with large chunks with carrage returns. ;;; ;;; Revision 1.26 2007/05/09 22:36:27 HansOesterholt ;;; *** empty log message *** ;;; ;;; Revision 1.25 2007/04/18 18:37:21 HansOesterholt ;;; *** empty log message *** ;;; ;;; Revision 1.24 2006/01/12 00:50:28 HansOesterholt ;;; Small change to adjust for planet package datastructs ;;; ;;; Revision 1.23 2006/01/05 20:30:38 HansOesterholt ;;; Adaptation to planet requires ;;; ;;; Revision 1.22 2005/11/22 19:53:02 HansOesterholt ;;; no message ;;; ;;; Revision 1.21 2005/11/13 20:02:18 HansOesterholt ;;; -- Small things ;;; ;;; Revision 1.20 2005/11/13 19:44:11 HansOesterholt ;;; - Bug fixing. ;;; - Making things more consistent. ;;; - Adding the log4scm-viewer application ;;; - Making things start as a real windows application. ;;; ;;; Revision 1.19 2005/11/13 16:41:31 HansOesterholt ;;; Some minor bug fixing. ;;; ;;; Revision 1.18 2005/11/13 16:12:15 HansOesterholt ;;; Changed log4scm to use log4scm-cfg instead ;;; of it's own functions. ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (module log4scm mzscheme (require (planet "fifo.scm" ("oesterholt" "datastructs.plt" 1 0))) (require (lib "file.ss" "mzlib")) (require (lib "time.ss" "srfi" "19")) (require (planet "sutil.scm" ("oesterholt" "ho-utils.plt" 1 0))) (require (planet "scfg.scm" ("oesterholt" "ho-utils.plt" 1 0))) (require "log4scm-cfg.scm") (provide ;; starting/stopping the logger for a thread log-start log-stop log-sync ;; notify functions log-fatal-notify ;; log functions log-debug log-info log-warn log-warning log-error log-fatal ;; configuration options log-level log-keep-days log-mode log-max-line-length ) (define (unlink f) (with-handlers ((exn:fail:filesystem? (lambda (exn) (log-warning "log4scm: cannot remove log file " f) 'try-next-time))) (delete-directory/files f))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;=head1 Log4SCM ;;; ;;;With Log4SCM scheme programs can log messages to log files. ;;; ;;;=head2 Background ;;; ;;;This implementation of logging is a derivative of a C log server ;;;that has been written to provide high speed logging possibilities ;;;in a production messaging environment. ;;; ;;;=head3 A description of the log server ;;; ;;;A number of (different) programs can ;;;log to a central log server, using SYSV IPC message queuing. ;;;Each program can log to a logical log name, for which the log ;;;server has a configuration. ;;; ;;;The log server processes all messages from the IPC queue and ;;;multiplexes them to various log files. By making the log process ;;;asynchronous, queueing up messages, the logging process by clients ;;;performs very well, while the log server may be processing messages ;;;at a lower rate. ;;; ;;;Not only does the log server provide an asynchronous logging facility, ;;;it also provides logging by day and purging of expired log files. ;;;Log files are created for each day. Each log file having an extension ;;;in the form of C<CCYYMMDD>. On a per logical log id basis, the number ;;;of days that logs need to be preserved can be set. ;;; ;;;The log server in this production environment knows of 5 levels ;;;of messages: ;;; ;;;=over 1 ;;; ;;;=item debug ;;; ;;;Log level 'debug' is used for programm debugging purposes. Logging ;;;for this level can be turned on and off. Client programs read a ;;;flag about this, from shared memory. If the 'debug' level is ;;;turned off, clients won't log the message. ;;; ;;;=item info ;;; ;;;Log level 'info' is used for information about steps that a program ;;;takes to do a specific task. Logging for this level can also be turned ;;;on and off. ;;; ;;;=item warn ;;; ;;;Log level 'warn' is used for warnings about the processing that a ;;;program does. A warning may eventually turn into an error if the ;;;warning persists, or even result in a fatal situation. Logging ;;;for this level can also be turned on and off. ;;; ;;;=item error ;;; ;;;Log level 'error' is used for errors that occur during program ;;;execution. Errors are severe for a program or module itself, ;;;but not for an entire system, application or service. They do not threaten ;;;the execution of the whole. ;;; ;;;=item fatal ;;; ;;;Log level 'fatal' is used for fatal errors. Fatal errors are errors ;;;that prevent the working of an entire system, application or service. ;;; ;;;=back ;;; ;;;Usually monitoring tools monitor log files for messages marked 'fatal'. ;;;When a 'fatal' message occures, a signal is given to enterprise wide ;;;monitoring tools like HP Openview or Tivoli. ;;; ;;;=head2 Log 4 Scheme features ;;; ;;;Log4SCM is in a way a simplified implementation of the previously mentioned ;;;log server. It doesn't do IPC and doesn't provide log functionality ;;;for multiple running programs, multiplexing to multiple log files. ;;; ;;;What it does provide is an asynchronous logging functionality that ;;;can be used in scheme programs. This log facility runs in a separate ;;;thread. Communication between the main thread and the log facility ;;;is done using a fifo. ;;; ;;;The levels of logging are the same as mentioned in the previous section; ;;;debug, info, warn, error and fatal are provided. ;;; ;;;In this implementation, ;;;a callback function can be attached to the log server for fatal messages. ;;;This enables the program to do e.g. some cleanup, displaying a messagebox, ;;;handling bug exchange information, checking for program version updates, ;;;etc. on a fatal error. Handling of fatal messages is therefore synchronous. ;;; ;;;Instead of using IPC shared memory to communicate a flag that ;;;indicates if to log 'debug', 'info' and 'warn' ;;;levels to the 'logging client threads' an other implementation is made: ;;;When setting the log level for a ;;;thread, the log functions associated with the levels for which no logging ;;;is necessary, will be replaced by noop functions. ;;; ;;;A noop function will return C<'not-logged>. A normal log function returns C<'logged>. ;;; ;;;There are two logging modes. The C<'reference> mode is faster, but if the logged ;;;message contains mutable data, like e.g. the elements of a list that can be ;;;changed using set-car! or set-cdr!, there's a good change (especially when using ;;;log-debug), that the contents of message have changed before they have been written ;;;to the log file. Especially when debugging, it's probably best to change the log ;;;mode to C<'copy>, which makes the log function copy the contents of the message ;;;to a string before handing them to the logging fifo. The log mode defaults to C<'reference>. ;;; ;;;Configuration is done on a per log file basis, not for all log files ;;;together. XML is used for log file behaviour configuration. API Functions ;;;are provided to update the configuration. ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;=head2 Implementation ;;; ;;;=head3 Prerequisites ;;; ;;;This module requires C<L<SXML, SSAX|http://ssax.sf.net>>, ;;;C<L<datastructs|http://www.elemental-programming.org/epwiki/scheme%20datastructures>>, ;;;C<file.ss> from C<mzlib> and C<srfi 19>, both provided with mzscheme. ;;; ;;;=head3 Variables ;;; ;;;=verbatim scm,8 (define-syntax thrcell-ref (syntax-rules () ((_ a) a))) (define-syntax thrcell-set! (syntax-rules () ((_ a b) (set! a b)))) (define-syntax make-thrcell (syntax-rules () ((_ a b) a))) ;Log datastructures are thread specific ;Using thrcells, we can get "child" ;threads to share the same datastructures (define log-fh (make-thrcell #f #t)) (define log-name (make-thrcell #f #t)) (define log-cfg (make-thrcell #f #t)) (define log-lvl (make-thrcell 'warn #t)) (define log-keep (make-thrcell 7 #t)) (define log-mymode (make-thrcell 'reference #t)) (define log-fifo (make-thrcell #f #t)) (define log-dt (make-thrcell #f #t)) (define log-thread (make-thrcell #f #t)) (define cfg-refresh-thread (make-thrcell #f #t)) (define log-notify (make-thrcell (lambda args #f) #t)) ;Proxy functions (define (log-none . msg) 'not-logged) (define log-internal-debug (make-thrcell log-none #t)) (define log-internal-info (make-thrcell log-none #t)) (define log-internal-warn (make-thrcell log-none #t)) (define log-internal-error (make-thrcell log-none #t)) (define log-internal-fatal (make-thrcell log-none #t)) (define MAX-LOG-LINE-LENGTH 120) ;;;=verbatim ;;; ;;;=verbatim ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;=head3 Reading in (and writing out) the configuration ;;; ;;;=verbatim scm,8 ;Internal functions for the configuration file (define (read-log-config filename-prefix) (let* ((filename (string-append filename-prefix ".scfg"))) (let ((cfg (log4scm-cfg filename))) (thrcell-set! log-keep (-> cfg keep)) (thrcell-set! log-lvl (-> cfg level)) (thrcell-set! log-mymode (-> cfg mode)) (thrcell-set! log-cfg cfg)))) (define (write-log-config filename-prefix) (let* ((filename (string-append filename-prefix ".scfg")) (cfg (thrcell-ref log-cfg))) (if cfg (begin (-> cfg keep (thrcell-ref log-keep)) (-> cfg level (thrcell-ref log-lvl)) (-> cfg mode (thrcell-ref log-mymode)) (-> cfg commit filename))))) ;;;=verbatim ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;;=head3 Opening and rotating a log file ;;; ;;;The functions here are used to open, close and rotate (cq. expire) log files. ;;;These functions are used when starting a log thread and when the date of log ;;;messages change. C<basedir> and <basename> are used to get the directory or ;;;file component of a full path. ;;; ;;;=verbatim scm,8 ;Rotating and opening a log file (define (open-new-or-existing-log-and-rotate-logs filename-prefix) (let* ((fn-prefix (basename filename-prefix)) (fn-len (string-length fn-prefix)) (dt (current-time)) (ext (string-append filename-prefix "." (date->string (time-utc->date dt) "~Y-~m-~d")))) ;First open log for appending (let ((fh (open-output-file ext 'append))) (thrcell-set! log-fh fh)) ;And set the current date (thrcell-set! log-dt (time-utc->date dt)) ;Next expire logs (let* ((odt (subtract-duration dt (make-time 'time-duration 0 (* (thrcell-ref log-keep) 24 3600)))) (oext (string-append filename-prefix "." (date->string (time-utc->date odt) "~Y-~m-~d")))) (for-each (lambda (filename) (unlink filename) ) (let ((files (filter (lambda (filename) (string<? (basename filename) oext)) (glob (string-append filename-prefix ".*"))))) (if (eq? files #f) (list) files)))) )) ;Closing a log file (define (close-log) (close-output-port (thrcell-ref log-fh))) ;;;=verbatim ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;;=head3 log functions ;;; ;;;These functions provide the actual log functions, that ;;;the 'internal' log function thrcells point to. ;;; ;;;Note that these functions pass the given C<msg> message ;;;through to the C<log-fifo>, without copying internal ;;;references etc. This can result in unexpected behaviour, ;;;because the logger thread works asynchronously, and normally ;;;slower, because it writes to a file, from the logging ;;;thread. Modifying the data given to <msg>, will probably ;;;change the messages waiting to be logged. ;;; ;;;E.g.: ;;; ;;; (define a (list 2 3)) ;;; (log-info a) ;;; (set-car! a 8) ;;; ;;;May result in "(8 3)" being written to the log file. ;;; ;;;=verbatim scm,8 ;(define (displayp . args) ; (display args)(newline)) (define (copy-args arglist) (let ((s (open-output-string))) (for-each (lambda (arg) (display arg s)) arglist) (let ((str (get-output-string s))) (close-output-port s) str))) ;log functions (define-syntax def-logger (syntax-rules () ((_ name level) (define (name . msg) ; (displayp level " - " msg) (fifo+ (thrcell-ref log-fifo) (cons level (cons (current-date) (if (eq? (thrcell-ref log-mymode) 'copy) (list (list (copy-args (car msg)))) msg)))) 'logged)))) (def-logger my-debug 'debug) (def-logger my-info 'info) (def-logger my-warn 'warning) (def-logger my-error 'error) (define (my-fatal sem . msg) (fifo+ (thrcell-ref log-fifo) (cons 'fatal (cons (current-date) (cons sem msg)))) 'logged) ;;;=verbatim ;;; ;;;Using the C<set-log-functions> function, the thread cells ;;;for the log functions are modified. For log level 'error and ;;;above, 'log-internal-[debug,info,warn]' are set to 'log-none'. ;;; ;;;=verbatim scm,8 ;setting used log-functions (define (set-log-functions level) (define (ord level) (cond ((eq? level 'debug) 0) ((eq? level 'info) 1) ((eq? level 'warn) 2) ((eq? level 'error) 3) (else 4))) (let ((lvl (ord level))) (if (<= lvl 0) (thrcell-set! log-internal-debug my-debug) (thrcell-set! log-internal-debug log-none)) (if (<= lvl 1) (thrcell-set! log-internal-info my-info) (thrcell-set! log-internal-info log-none)) (if (<= lvl 2) (thrcell-set! log-internal-warn my-warn) (thrcell-set! log-internal-warn log-none)) (thrcell-set! log-internal-error my-error) (thrcell-set! log-internal-fatal my-fatal))) ;;;=verbatim ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;;=head3 The logger ;;; ;;;The logger function is the worker function of the log thread. ;;;This function puts all provided messages on the current log-file. ;;;It reads log requests or commands from a fifo. The C<'stop-logging> ;;;command is used to stop the logger. The C<'log-fatal-notify> command ;;;is used to change the notify function (callback function) for this ;;;log facility. It is communicated over the fifo to the logger, because ;;;setting it directly would pose problems as can be seen from the image ;;;below. ;;; ;;;=image Threads and callbacks,/images/logger.png,/images/logger.png,png,center ;;; ;;;Suppose in this image, child thread 2 wants to update the callback function ;;;for the fatal level notifyer. If it updates it's own callback function** ;;;variable, the logger thread won't notice, because variables are local ;;;to threads. If the notifyer function variable would be made shared for ;;;all threads, only one callback function could be installed for all threads. ;;; ;;;So it is made sure, that the new callback function is (asynchronously) ;;;communicated over the fifo to the logger thread. The logger thread stores ;;;the function in it's local notifyer variable. ;;; ;;;If a message with a fatal level is read from the fifo, the callback function ;;;is called. A call to the C<log-fatal> function is I<synchronous>. If a lot ;;;of messages have been logged, it can take a while for the callback function ;;;to be called and for the log-fatal function to end. ;;; ;;;=verbatim scm,8 ;logger (define (logger) (define (log-entry dt level msgs) (define (make-msg msgs) (if (null? msgs) "" (string-append (format "~a" (car msgs)) (make-msg (cdr msgs))))) (define (log dt level msg-lines count) (if (null? msg-lines) (flush-output (thrcell-ref log-fh)) (begin (display (format "~a ~a ~a~a~%" dt level (if (= count 0) "" "+ ") (substr (car msg-lines) 0 MAX-LOG-LINE-LENGTH)) (thrcell-ref log-fh)) (log dt level (cdr msg-lines) (+ count 1))))) (begin ;; TODO: seek to the end of the file (concurrent access, and flush) (let ((M (make-msg msgs))) (let ((lines (splitstr M #\newline))) (log dt level lines 0))))) (define (fillout lvl len) (let ((l (symbol->string lvl))) (string-append l (make-string (- len (string-length l)) #\space)))) (define (log-fatal-notify? obj) (if (list? obj) (if (null? obj) #f (eq? (car obj) 'log-fatal-notify)) #f)) (define (do-log) (let ((entry (fifo- (thrcell-ref log-fifo)))) (if (eq? entry 'stop-logging) #t (if (eq? entry 'refresh-configuration) (begin (read-log-config (thrcell-ref log-name)) (set-log-functions (thrcell-ref log-lvl)) (do-log)) (begin (if (log-fatal-notify? entry) (let ((proc (cadr entry))) (log-info 'log-fatal-notify " request, proc: " proc) (thrcell-set! log-notify proc)) (begin (let* ((lvl (car entry)) (dt (cadr entry)) (sem (if (eq? lvl 'fatal) (caddr entry) #f)) (msgs (if (eq? lvl 'fatal) (cadddr entry) (caddr entry))) (mdt (thrcell-ref log-dt))) (if (not (and (= (srfi:date-day dt) (srfi:date-day mdt)) (= (srfi:date-month dt) (srfi:date-month mdt)) (= (srfi:date-year dt) (srfi:date-year mdt)))) (begin (close-log) (open-new-or-existing-log-and-rotate-logs (thrcell-ref log-name)))) (log-entry (date->string dt "~Y-~m-~dT~H:~M:~S~z") (fillout lvl 8) msgs) (if (eq? lvl 'fatal) (begin ((thrcell-ref log-notify) dt msgs) (sem)))))) ;tail recursive call... (do-log)))))) (do-log)) (define (config-file-refresh) (sleep 2) (fifo+ (thrcell-ref log-fifo) 'refresh-configuration) (config-file-refresh)) ;;;=verbatim ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;;=head2 The Log4SCM API ;;; ;;;=head3 Synopsys ;;; ;;;The following sample code is illustrative for using log4scm ;;;with multiple threads. ;;; ;;; (require "log4scm.scm") ;;; ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;; (define (next-level level) ;;; (cond ;;; ((eq? level 'debug) 'info) ;;; ((eq? level 'info) 'warn) ;;; ((eq? level 'warn) 'error) ;;; ((eq? level 'error) 'fatal) ;;; ((eq? level 'fatal) 'debug))) ;;; ;;; (define (do-log level . msg) ;;; (cond ;;; ((eq? level 'debug) (log-debug msg)) ;;; ((eq? level 'info) (log-info msg)) ;;; ((eq? level 'warn) (log-warn msg)) ;;; ((eq? level 'error) (log-error msg)) ;;; ((eq? level 'fatal) (log-fatal msg)))) ;;; ;;; ;;; (define-syntax counter ;;; (syntax-rules () ;;; ((_ inc-op get-op name) ;;; (begin ;;; (define name (let ((cnt 0)) ;;; (lambda (op) ;;; (cond ;;; ((eq? op 'inc) ;;; (begin ;;; (set! cnt (+ cnt 1)))) ;;; ((eq? op 'get) ;;; (begin ;;; (let ((v cnt)) ;;; cnt))))))) ;;; (define (inc-op) (name 'inc)) ;;; (define (get-op) (name 'get)))))) ;;; ;;; (counter inc-tt1 get-tt1 TT1) ;;; (counter inc-tt2 get-tt2 TT2) ;;; ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;; (define (test2 n level) ;;; ;;; (define (t2 n level) ;;; (if (> n 0) ;;; (begin ;;; (do-log level "T2" n " fatal: " (get-tt2)) ;;; (t2 (- n 1) (next-level level))) ;;; #t)) ;;; ;;; (log-start "test2") ;;; (log-level (next-level level)) ;;; (log-fatal-notify (lambda (dt msgs) (inc-tt2))) ;;; (t2 n level) ;;; (log-stop)) ;;; ;;; ;;; (define (test1 n level) ;;; ;;; (define (t1 n level) ;;; (if (> n 0) ;;; (begin ;;; (do-log level "T1" n " fatal: " (get-tt1)) ;;; (t1 (- n 1) (next-level level))) ;;; #t)) ;;; ;;; (log-start "test1") ;;; (log-level (next-level level)) ;;; (log-fatal-notify (lambda (dt msgs) (inc-tt1))) ;;; (thread (lambda () (test2 (* n 2) (next-level level)))) ;;; (t1 n level) ;;; (log-stop)) ;;; ;;; ;;; (test1 20 'debug) ;;; ;;; ;;;=head3 C<(log-start filename-prefix : string) : boolean> ;;; ;;;C<Filename-prefix> is the prefix of a filename. [filename-prefix]B<.scfg> is ;;;used for the logfile configuration. [filename-prefix]B<.CCYYMMDD> is used ;;;for log files. Log files are created per day. Each new day, expired log ;;;files will be cleaned. ;;; ;;;A call to C<log-start> will start the logging for the current thread and ;;;its child threads. New C<log-start> calls can be issued in child threads. ;;; ;;;C<log-start> must not be called more than ones in a thread. C<log-start> ;;;and C<log-stop> can be called alternating. ;;; ;;;=verbatim scm,8 ;Starting a log system for current threading environment (define (log-start filename-prefix) (thrcell-set! log-name filename-prefix) (read-log-config (thrcell-ref log-name)) (open-new-or-existing-log-and-rotate-logs filename-prefix) (thrcell-set! log-fifo (fifo)) (set-log-functions (thrcell-ref log-lvl)) (thrcell-set! log-thread (thread logger)) (thrcell-set! cfg-refresh-thread (thread config-file-refresh)) #t) ;;;=verbatim ;;; ;;;=head3 C<(log-stop)> ;;; ;;;C<log-stop> will put a 'stop-logging command on the log-fifo and ;;;wait until the logger thread stops. ;;; ;;;=verbatim scm,8 (define (log-stop) (kill-thread (thrcell-ref cfg-refresh-thread)) (fifo+ (thrcell-ref log-fifo) 'stop-logging) (thread-wait (thrcell-ref log-thread)) (close-output-port (thrcell-ref log-fh))) ;;;=verbatim ;;; ;;;=head3 C<(log-sync)> ;;; ;;;C<log-sync> will sync logging, make fifo empty. ;;;Wait until the logger thread stops, and restart it. ;;; ;;;=verbatim scm,8 (define (log-sync) (log-stop) (log-start (thrcell-ref log-name))) ;;;=verbatim ;;; ;;;=head3 C<(log-debug . msgs) : '[not-]logged> ;;; ;;;This function will log debug messages. If the debug level is turned ;;;on, it will return 'logged. 'not-logged will be returned ;;;otherwise. ;;; ;;;=verbatim scm,8 (define (log-debug . msgs) ((thrcell-ref log-internal-debug) msgs)) ;;;=verbatim ;;; ;;;=head3 C<(log-info . msgs) : '[not-]logged> ;;; ;;;This function will log info messages. If the info level is turned ;;;on, it will return 'logged. 'not-logged will be returned ;;;otherwise. ;;; ;;;=verbatim scm,8 (define (log-info . msgs) ((thrcell-ref log-internal-info) msgs)) ;;;=verbatim ;;; ;;;=head3 C<(log-warn . msgs) : '[not-]logged> ;;; ;;;This function will log warning messages. If the warn level is turned ;;;on, it will return 'logged. 'not-logged will be returned ;;;otherwise. Alias for C<log-warn>, is C<log-warning>. ;;; ;;;=verbatim scm,8 (define (log-warn . msgs) ((thrcell-ref log-internal-warn) msgs)) (define (log-warning . msgs) ((thrcell-ref log-internal-warn) msgs)) ;;;=verbatim ;;; ;;;=head3 C<(log-error . msgs) : 'logged> ;;; ;;;This function will log error messages. These messages ;;;cannot be turned off. ;;; ;;;=verbatim scm,8 (define (log-error . msgs) ((thrcell-ref log-internal-error) msgs)) ;;;=verbatim ;;; ;;;=head3 C<(log-fatal . msgs) : 'logged> ;;; ;;;This function will log fatal messages. These messages ;;;cannot be turned off. If a log-fatal-notifyer is registered, ;;;this function will be called. log-fatal will result in a synchronous ;;;log call. Normally log-fatal will also result in terminating ;;;a program. ;;; ;;;=verbatim scm,8 (define (log-fatal . msgs) (let ((sem (make-semaphore 0))) ((thrcell-ref log-internal-fatal) (lambda () (semaphore-post sem)) msgs) (semaphore-wait sem))) ;;;=verbatim ;;; ;;;=head3 C<(log-level . level) : current-level> ;;; ;;;This function will report and/or set the current log level. ;;;If 'level' is given as an argument, the current log level will ;;;be set to its value. If no arguments are given, only the ;;;current log level is returned. ;;; ;;;=verbatim scm,8 (define (log-level . arg) (if (not (null? arg)) (begin (thrcell-set! log-lvl (car arg)) (set-log-functions (car arg)) (write-log-config (thrcell-ref log-name)))) (thrcell-ref log-lvl)) ;;;=verbatim ;;; ;;;=head3 C<(log-keep-days . days) : days> ;;; ;;;This function will report and/or set the current number of days ;;;after which log files will expire and be cleaned by the logger thread. ;;;If 'days' is given, the number of days until log files expire is ;;;set to 'days'. Otherwise, only the current number of days is returned. ;;; ;;;Precondition (not checked): daysE<gt>=1. ;;; ;;;=verbatim scm,8 (define (log-keep-days . arg) (if (not (null? arg)) (begin (thrcell-set! log-keep (car arg)) (write-log-config (thrcell-ref log-name)))) (thrcell-ref log-keep)) ;;;=verbatim ;;; ;;;=head3 C<(log-max-line-length . len) : integer> ;;; ;;;This function will report and/or set the current log line length. ;;;THIS IS A GLOBAL SETTING, NOT IN A CONFIG FILE. Defaults to 120. ;;; ;;;=verbatim scm,8 (define (log-max-line-length . arg) (if (not (null? arg)) (set! MAX-LOG-LINE-LENGTH (car arg))) MAX-LOG-LINE-LENGTH) ;;;=verbatim ;;; ;;;=head3 C<(log-mode . mode) : mode> ;;; ;;;This function will report and/or set the current log mode. Possible ;;;modes are 'copy and 'reference. 'copy mode is slower, because the ;;;log functions will copy all arguments to a string instead of referencing ;;;them. But it can be very usefull when logging volatile data. ;;; ;;;=verbatim scm,8 (define (log-mode . arg) (if (not (null? arg)) (begin (thrcell-set! log-mymode (car arg)) (write-log-config (thrcell-ref log-name)))) (thrcell-ref log-mymode)) ;;;=verbatim ;;; ;;;=head3 C<(log-fatal-notify procedure) : #t> ;;; ;;;C<log-fatal-notify> will set a callback function that will ;;;be called on a log messages of level fatal. Programs can ;;;use this function to cleanup code, report fatal errors in ;;;a popup messagebox, or whatever. ;;; ;;;Nothing is done with the result of 'proc'. 'proc' is called ;;;with two arguments. First, a date (string? date --> #t); ;;;and second any data. ;;; ;;;=verbatim scm,8 (define (log-fatal-notify proc) (fifo+ (thrcell-ref log-fifo) (list 'log-fatal-notify proc)) #t) ;;;=verbatim ;;; ;;;=head1 Downloading ;;; ;;;Log4SCM can be downloaded at L<sourceforge|http://sourceforge.net/project/showfiles.php?group_id=89865&package_id=120236> ;;; ;;;=head1 Info ;;; ;;;S<C<Author(s):>> Hans Oesterholt (hansatelementalprogrammingdotorgextension).E<lb> ;;;S<C<Copyright:>> (c) 2005.E<lb> ;;;S<C<License :>> L<Elemental Programming Artistic License|http://www.elemental-programming.org/epwiki/ep_license.html>.E<lb> ;;;S<C<File :>> log4scm.scm $Id: log4scm.scm,v 1.28 2007/05/19 23:25:21 HansOesterholt Exp $ ;;; ;;;=cut )