added date/time info when logging to a local file
[OpenSRF.git] / src / python / osrf / log.py
1 # -----------------------------------------------------------------------
2 # Copyright (C) 2007  Georgia Public Library Service
3 # Bill Erickson <billserickson@gmail.com>
4
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
9
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 # -----------------------------------------------------------------------
15
16 import traceback, sys, os, re, threading, time
17 from osrf.const import OSRF_LOG_DEBUG, OSRF_LOG_ERR, OSRF_LOG_INFO, \
18     OSRF_LOG_INTERNAL, OSRF_LOG_TYPE_FILE, OSRF_LOG_TYPE_STDERR, \
19     OSRF_LOG_TYPE_SYSLOG, OSRF_LOG_WARN
20 LOG_SEMAPHORE = threading.BoundedSemaphore(value=1)
21
22
23 LOG_LEVEL = OSRF_LOG_DEBUG
24 LOG_TYPE = OSRF_LOG_TYPE_STDERR
25 LOG_FILE = None
26 FRGX = re.compile('/.*/')
27
28 _xid = '' # the current XID
29 _xid_pfx = '' # our XID prefix
30 _xid_ctr = 0
31 _xid_is_client = False # true if we are the request origin
32
33
34 def initialize(level, facility=None, logfile=None, is_client=False):
35     """Initialize the logging subsystem."""
36     global LOG_LEVEL, LOG_TYPE, LOG_FILE, _xid_is_client
37
38     _xid_is_client = is_client
39     LOG_LEVEL = level
40
41     if facility: 
42         try:
43             import syslog
44         except ImportError:
45             sys.stderr.write("syslog not found, logging to stderr\n")
46             return
47
48         LOG_TYPE = OSRF_LOG_TYPE_SYSLOG
49         initialize_syslog(facility, level)
50         return
51         
52     if logfile:
53         LOG_TYPE = OSRF_LOG_TYPE_FILE
54         LOG_FILE = logfile
55
56 def make_xid():
57     global _xid, _xid_pfx, _xid_is_client, _xid_ctr
58     if _xid_is_client:
59         if not _xid_pfx:
60             _xid_pfx = "%s%s" % (time.time(), os.getpid())
61         _xid = "%s%d" % (_xid_pfx, _xid_ctr)
62         _xid_ctr += 1
63          
64 def clear_xid():
65     global _xid
66     _xid =  ''
67
68 def set_xid(xid):
69     global _xid
70     _xid = xid
71
72 def get_xid():
73     return _xid
74
75 # -----------------------------------------------------------------------
76 # Define wrapper functions for the log levels
77 # -----------------------------------------------------------------------
78 def log_internal(debug_str):
79     __log(OSRF_LOG_INTERNAL, debug_str)
80 def log_debug(debug_str):
81     __log(OSRF_LOG_DEBUG, debug_str)
82 def log_info(debug_str):
83     __log(OSRF_LOG_INFO, debug_str)
84 def log_warn(debug_str):
85     __log(OSRF_LOG_WARN, debug_str)
86 def log_error(debug_str):
87     __log(OSRF_LOG_ERR, debug_str)
88
89 def __log(level, msg):
90     """Builds the log message and passes the message off to the logger."""
91     global LOG_LEVEL, LOG_TYPE
92
93     try:
94         import syslog
95     except:
96         if level == OSRF_LOG_ERR:
97             sys.stderr.write('ERR ' + msg)
98         return
99         
100     if int(level) > int(LOG_LEVEL): return
101
102     # find the caller info for logging the file and line number
103     tb = traceback.extract_stack(limit=3)
104     tb = tb[0]
105     lvl = 'DEBG'
106
107     if level == OSRF_LOG_INTERNAL:
108         lvl = 'INT '
109     if level == OSRF_LOG_INFO:
110         lvl = 'INFO'
111     if level == OSRF_LOG_WARN:
112         lvl = 'WARN'
113     if level == OSRF_LOG_ERR:
114         lvl = 'ERR '
115
116     filename = FRGX.sub('', tb[0])
117     msg = '[%s:%d:%s:%s:%s:%s] %s' % (lvl, os.getpid(), filename, tb[1], threading.currentThread().getName(), _xid, msg)
118
119     if LOG_TYPE == OSRF_LOG_TYPE_SYSLOG:
120         __log_syslog(level, msg)
121     else:
122         if LOG_TYPE == OSRF_LOG_TYPE_FILE:
123             __log_file(msg)
124         else:
125             sys.stderr.write("%s\n" % msg)
126
127     if level == OSRF_LOG_ERR and LOG_TYPE != OSRF_LOG_TYPE_STDERR:
128         sys.stderr.write(msg + '\n')
129
130 def __log_syslog(level, msg):
131     ''' Logs the message to syslog '''
132     import syslog
133
134     slvl = syslog.LOG_DEBUG
135     if level == OSRF_LOG_INTERNAL:
136         slvl = syslog.LOG_DEBUG
137     if level == OSRF_LOG_INFO:
138         slvl = syslog.LOG_INFO
139     if level == OSRF_LOG_WARN:
140         slvl = syslog.LOG_WARNING
141     if level == OSRF_LOG_ERR:
142         slvl = syslog.LOG_ERR
143
144     syslog.syslog(slvl, msg)
145
146 def __log_file(msg):
147     ''' Logs the message to a file. '''
148
149     global LOG_TYPE
150
151     timestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
152
153     logfile = None
154     try:
155         logfile = open(LOG_FILE, 'a')
156     except:
157         sys.stderr.write("cannot open log file for writing: %s\n" % LOG_FILE)
158         LOG_TYPE = OSRF_LOG_TYPE_STDERR
159         return
160     try:
161         LOG_SEMAPHORE.acquire()
162         logfile.write("%s %s\n" % (timestamp, msg))
163     finally:
164         LOG_SEMAPHORE.release()
165         
166     logfile.close()
167
168 def initialize_syslog(facility, level):
169     """Connect to syslog and set the logmask based on the level provided."""
170
171     import syslog
172     level = int(level)
173
174     if facility == 'local0':
175         facility = syslog.LOG_LOCAL0
176     if facility == 'local1':
177         facility = syslog.LOG_LOCAL1
178     if facility == 'local2':
179         facility = syslog.LOG_LOCAL2
180     if facility == 'local3':
181         facility = syslog.LOG_LOCAL3
182     if facility == 'local4':
183         facility = syslog.LOG_LOCAL4
184     if facility == 'local5':
185         facility = syslog.LOG_LOCAL5
186     if facility == 'local6':
187         facility = syslog.LOG_LOCAL6
188     # add other facility maps if necessary...
189
190     syslog.openlog(sys.argv[0], 0, facility)
191