/usr/share/guile/site/logging/logger.scm is in guile-library 0.2.1-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 | ;; (logging logger) -- write methods to log files
;; Copyright (C) 2003 Richard Todd
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; 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 General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
#!
;;; Commentary:
@cindex logging
@cindex loggers, relationship with handlers
@cindex handlers, relationship with loggers
@cindex log levels
This is a logging subsystem similar to the one in the python standard
library. There are two main concepts to understand when working with
the logging modules. These are loggers and log handlers.
@table @asis
@item Loggers
Loggers are the front end interfaces for program logging.
They can be registered by name so that no part of a program
needs to be concerned with passing around loggers. In
addition, a default logger can be designated so that, for
most applications, the program does not need to be concerned
with logger instances at all beyond the initial setup.
Log messages all flow through a logger. Messages carry with them a
level (for example: 'WARNING, 'ERROR, 'CRITICAL), and loggers can
filter out messages on a level basis at runtime. This way, the amount
of logging can be turned up during development and bug investigation,
but turned back down on stable releases.
Loggers depend on Log Handlers to actually get text to the log's
destination (for example, a disk file). A single Logger can send
messages through multiple Log Handlers, effectively multicasting logs
to multiple destinations.
@item Log Handlers
Log Handlers actually route text to a destination. One or more handlers
must be attached to a logger for any text to actually appear in a log.
Handlers apply a configurable transformation to the text so that it is
formatted properly for the destination (for instance: syslogs, or a
text file). Like the loggers, they can filter out messages based on
log levels. By using filters on both the Logger and the Handlers,
precise controls can be put on which log messages go where, even
within a single logger.
@end table
@section Example use of logger
Here is an example program that sets up a logger with two handlers. One
handler sends the log messages to a text log that rotates its logs. The
other handler sends logs to standard error, and has its levels set so that
INFO and WARN-level logs don't get through.
@lisp
(use-modules (logging logger)
(logging rotating-log)
(logging port-log)
(scheme documentation)
(oop goops))
;; ----------------------------------------------------------------------
;; Support functions
;; ----------------------------------------------------------------------
(define (setup-logging)
(let ((lgr (make <logger>))
(rotating (make <rotating-log>
#:num-files 3
#:size-limit 1024
#:file-name "test-log-file"))
(err (make <port-log> #:port (current-error-port))))
;; don't want to see warnings or info on the screen!!
(disable-log-level! err 'WARN)
(disable-log-level! err 'INFO)
;; add the handlers to our logger
(add-handler! lgr rotating)
(add-handler! lgr err)
;; make this the application's default logger
(set-default-logger! lgr)
(open-log! lgr)))
(define (shutdown-logging)
(flush-log) ;; since no args, it uses the default
(close-log!) ;; since no args, it uses the default
(set-default-logger! #f))
;; ----------------------------------------------------------------------
;; Main code
;; ----------------------------------------------------------------------
(setup-logging)
;; Due to log levels, this will get to file,
;; but not to stderr
(log-msg 'WARN "This is a warning.")
;; This will get to file AND stderr
(log-msg 'CRITICAL "ERROR message!!!")
(shutdown-logging)
@end lisp
;;; Code:
!#
(define-module (logging logger)
#:export (
;; handler exports...
<log-handler>
emit-log
accept-log
;; logger exports...
<logger>
add-handler!
log-msg
;; module-level methods...
set-default-logger!
register-logger!
lookup-logger
;; these work on loggers and handlers...
enable-log-level!
disable-log-level!
flush-log
open-log!
close-log!
)
#:use-module (oop goops)
#:use-module (scheme documentation))
;;; ----------------------------------------------------------------------
(define default-logger #f)
(define all-loggers (make-hash-table 7))
(define (set-default-logger! lgr)
"Sets the given logger, @var{lgr}, as the default for logging methods where
a logger is not given. @var{lgr} can be an instance of @code{<logger>},
a string that has been registered via @code{register-logger!}, or @code{#f}
to remove the default logger.
With this mechanism, most applications will never need to worry about
logger registration or lookup.
@lisp
;; example 1
(set-default-logger! \"main\") ;; look up \"main\" logger and make it the default
;; example 2
(define lgr (make <logger>))
(add-handler! lgr
(make <port-handler>
#:port (current-error-port)))
(set-default-logger! lgr)
(log-msg 'CRITICAL \"This is a message to the default logger!!!\")
(log-msg lgr 'CRITICAL \"This is a message to a specific logger!!!\")
@end lisp"
(cond ((string? lgr)
(set! default-logger (hash-ref all-loggers lgr)))
((is-a? lgr <logger>) (set! default-logger lgr))
((not lgr) (set! default-logger #f))
(else (throw 'bad-type "expected a string, #f, or a <logger>"))))
(define (register-logger! str lgr)
"Makes @var{lgr} accessible from other parts of the program by a name
given in @var{str}. @var{str} should be a string, and @var{lgr}
should be an instance of class @code{<logger>}.
@lisp
(define main-log (make <logger>))
(define corba-log (make <logger>))
(register-logger! \"main\" main-log)
(register-logger! \"corba\" corba-log)
;; in a completely different part of the program....
(log-msg (lookup-logger \"corba\") 'WARNING \"This is a corba warning.\")
@end lisp"
(if (not (string? str))
(throw 'bad-type "Expected a string for the log registration"))
(hash-set! all-loggers str lgr))
(define (lookup-logger str)
"Looks up an instance of class @code{<logger>} by the name given
in @var{str}. The string should have already been registered via
a call to @code{register-logger!}."
(if (not (string? str))
(throw 'bad-type "Expected a string for the logger lookup"))
(hash-ref all-loggers str))
(define-class-with-docs <logger> ()
"This is the class that aggregates and manages log handlers. It also
maintains the global information about which levels of log messages
are enabled, and which have been suppressed. Keyword arguments accepted
on creation are:
@table @code
@item #:handlers
This optional parameter must be a list of objects derived from @code{<log-handler>}.
Handlers can always be added later via @code{add-handler!} calls.
@end table"
(levels #:init-form (make-hash-table 17) #:getter levels)
(log-handlers #:init-value '() #:accessor handlers #:init-keyword #:handlers))
(define (log-helper lgr level objs)
;; the level must be enabled in the logger to proceed...
(if (level-enabled? lgr level)
(let ((cur-time (current-time)))
(for-each (lambda (str)
(if (not (string-null? str))
;; pass the string to each log handler for lgr
(for-each (lambda (handler)
(accept-log handler level cur-time str))
(handlers lgr))))
;; split the string at newlines into different log statements
(string-split
(with-output-to-string (lambda () (for-each (lambda (o) (display o)) objs)))
#\nl)))))
(define-generic-with-docs log-msg
"@code{log-msg [lgr] lvl arg1 arg2 ...}. Send a log message
made up of the @code{display}'ed representation of the given
arguments. The log is generated at level @var{lvl}, which should
be a symbol. If the @var{lvl} is disabled, the log message is
not generated. Generated log messages are sent through each of
@var{lgr}'s handlers.
If the @var{lgr} parameter is omitted, then the default logger
is used, if one is set.
As the args are @code{display}'ed, a large string is built up. Then,
the string is split at newlines and sent through the log handlers as
independent log messages. The reason for this behavior is to make
output nicer for log handlers that prepend information like pid and
timestamps to log statements.
@lisp
;; logging to default logger, level of WARN
(log-msg 'WARN \"Warning! \" x \" is bigger than \" y \"!!!\")
;; looking up a logger and logging to it
(let ((l (lookup-logger \"main\")))
(log-msg l 'CRITICAL \"FAILURE TO COMMUNICATE!\")
(log-msg l 'CRITICAL \"ABORTING NOW\"))
@end lisp")
(define-method (log-msg (lvl <symbol>) . objs)
(if default-logger
(log-helper default-logger lvl objs)))
(define-method (log-msg (lgr <logger>) lvl . objs)
(log-helper lgr lvl objs))
;; the default formatter makes a log statement like:
;; 2003/12/29 14:53:02 (CRITICAL): The servers are melting!
(define (default-log-formatter lvl time str)
(with-output-to-string
(lambda ()
(display (strftime "%F %H:%M:%S" (localtime time)))
(display " (")
(display (symbol->string lvl))
(display "): ")
(display str)
(newline))))
(define-class-with-docs <log-handler> ()
"This is the base class for all of the log handlers, and encompasses
the basic functionality that all handlers are expected to have.
Keyword arguments recognized by the @code{<log-handler>} at creation
time are:
@table @code
@item #:formatter
This optional parameter must be a function that takes three arguments:
the log level, the time (as from @code{current-time}), and the log string
itself. The function must return a string representing the formatted log.
Here is an example invokation of the default formatter, and what it's
output looks like:
@lisp
(default-log-formatter 'CRITICAL
(current-time)
\"The servers are melting!\")
==> \"2003/12/29 14:53:02 (CRITICAL): The servers are melting!\"
@end lisp
@end table"
(formatter #:init-value default-log-formatter #:getter log-formatter #:init-keyword #:formatter)
(levels #:init-form (make-hash-table 17) #:getter levels))
(define-generic-with-docs add-handler!
"@code{add-handler! lgr handler}. Adds @var{handler} to @var{lgr}'s list of handlers. All subsequent
logs will be sent through the new handler, as well as any previously
registered handlers.")
(define-method (add-handler! (lgr <logger>) (handler <log-handler>))
(set! (handlers lgr)
(cons handler (handlers lgr))))
(define-generic-with-docs accept-log
"@code{accept-log handler lvl time str}. If @var{lvl} is
enabled for @var{handler}, then @var{str} will be formatted and
sent to the log via the @code{emit-log} method. Formatting is
done via the formatting function given at @var{handler}'s
creation time, or by the default if none was given.
This method should not normally need to be overridden by subclasses.
This method should not normally be called by users of the logging
system. It is only exported so that writers of log handlers can
override this behavior.")
;; This can be overridden by log handlers if this default behaviour
;; is not desired..
(define-method (accept-log (self <log-handler>) level time str)
(if (level-enabled? self level)
(emit-log self ((log-formatter self) level time str))))
;; This should be overridden by all log handlers to actually
;; write out a string.
(define-generic-with-docs emit-log
"@code{emit-log handler str}. This method should be implemented
for all the handlers. This sends a string to their output media.
All level checking and formatting has already been done by
@code{accept-log}.")
(define-generic-with-docs open-log!
"@code{open-log! handler}. Tells the @code{handler} to open its log. Handlers for which
an open operation doesn't make sense can choose not to implement this method.
The default implementation just returns @code{#t}.")
;; provide do-nothing open for handlers that don't care about it
(define-method (open-log! (lh <log-handler>))
#t)
(define-generic-with-docs close-log!
"@code{open-log! handler}. Tells the @code{handler} to close its
log. Handlers for which a close operation doesn't make sense can
choose not to implement this method. The default implementation
just returns @code{#t}.")
;; provide do-nothing close for handlers that don't care about it
(define-method (close-log! (lh <log-handler>))
#t)
(define-generic-with-docs flush-log
"@code{flush-log handler}. Tells the @code{handler} to output
any log statements it may have buffered up. Handlers for which a
flush operation doesn't make sense can choose not to implement
this method. The default implementation just returns
@code{#t}.")
;; provide do-nothing flush for handlers that don't care about it
(define-method (flush-log (lh <log-handler>))
#t)
;; if called with no args, pass to the default logger...
(define-method (flush-log)
(if default-logger
(flush-log default-logger)))
;; if called on a logger, pass the call to all the handlers...
(define-method (flush-log (lgr <logger>))
(for-each (lambda (handler)
(flush-log handler))
(handlers lgr)))
(define-method (flush-log!)
(if default-logger
(flush-log! default-logger)))
(define-method (open-log! (lgr <logger>))
(for-each (lambda (handler)
(open-log! handler))
(handlers lgr)))
(define-method (open-log!)
(if default-logger
(open-log! default-logger)))
(define-method (close-log! (lgr <logger>))
(for-each (lambda (handler)
(close-log! handler))
(handlers lgr)))
(define-method (close-log!)
(if default-logger
(close-log! default-logger)))
;; ----------------------------------------------------------------------
;; These functions work on both <logger> and <log-handler>.
;; I could make them methods, but the contents would just be duplicated
;; Making them methods would allow people to make subclasses that altered
;; the log level behavior, I guess...
;; ----------------------------------------------------------------------
(define (enable-log-level! lgr lvl)
"Enables a specific logging level given by the symbol @var{lvl},
such that messages at that level will be sent to the log
handlers. @var{lgr} can be of type @code{<logger>} or
@code{<log-handler>}.
Note that any levels that are neither enabled or disabled are treated
as enabled by the logging system. This is so that misspelt level
names do not cause a logging blackout."
(hashq-set! (levels lgr) lvl #t))
(define (disable-log-level! lgr lvl)
"Disables a specific logging level, such that messages at that
level will not be sent to the log handlers. @var{lgr} can be of
type @code{<logger>} or @code{<log-handler>}.
Note that any levels that are neither enabled or disabled are treated
as enabled by the logging system. This is so that misspelt level
names do not cause a logging blackout."
(hashq-set! (levels lgr) lvl #f))
(define (level-enabled? lgr lvl)
;; defaults to #t so that if you misspell the log level you get your log
(hashq-ref (levels lgr) lvl #t))
;;; arch-tag: b90591f5-553e-4967-8f6e-83ab9a727a35
|