File: errors.py

package info (click to toggle)
python-jtoolkit 0.7.8-2
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 1,436 kB
  • ctags: 2,536
  • sloc: python: 15,143; makefile: 20
file content (292 lines) | stat: -rwxr-xr-x 11,396 bytes parent folder | download | duplicates (2)
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
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""does error handling in a way that works for a web server"""

# Copyright 2002, 2003 St James Software
# 
# This file is part of jToolkit.
#
# jToolkit 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 2 of the License, or
# (at your option) any later version.
# 
# jToolkit 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 jToolkit; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

import sys
import traceback
import time
try:
  import threading
except ImportError:
  import dummy_threading as threading
try:
  from jToolkit import mailer
except ImportError:
  mailer = None

class ErrorHandler:
  """a class that handles error logging etc"""
  def __init__(self, instance):
    self.instance = instance

  def traceback_str(self):
    exc_info = sys.exc_info()
    return "".join(traceback.format_exception(exc_info[0], exc_info[1], exc_info[2]))

  def exception_str(self):
    exc_info = sys.exc_info()
    return "".join(traceback.format_exception_only(exc_info[0], exc_info[1]))

  def writelog(self, filename, message):
    # Append a log to a field
    f = open(filename,'a')
    #f.write(time.asctime()+': ')
    f.write(time.strftime('%Y-%m-%d %H:%M:%S')+': ')
    if isinstance(message, unicode):
      f.write(message.encode('utf8'))
    else:
      f.write(message)
    f.write('\n')
    f.close()

  def logerror(self, msg):
    if getattr(self.instance, "errorfile", None):
      self.writelog(self.instance.errorfile, msg)

  def logtrace(self, msg):
    if getattr(self.instance, "tracefile", None):
      self.writelog(self.instance.tracefile, msg)

  def logtracewithtime(self, msg):
    t = time.time()
    ms = int((t - int(t))*1000.0)
    self.logtrace(".%03d: %s" % (ms, msg))

  def getauditstr(self, recorddict=None):
    def tounicode(value):
      if isinstance(value, str): return value.decode('utf8')
      else:
        try:
          return unicode(value)
        except ValueError:
          return unicode(repr(value))
    if recorddict is not None:
      return ', '.join([key + ':' + tounicode(value) for key, value in recorddict.iteritems()])
    return ''

  def logaudit(self, msg, recorddict=None):
    if not getattr(self.instance, "auditfile", None):
      return
    msg += self.getauditstr(recorddict)
    self.writelog(self.instance.auditfile,msg)

class NullErrorHandler(ErrorHandler):
  """a class that handles error logging etc, but does absolutely nothing"""
  def __init__(self):
    pass

  def logerror(self, msg):
    pass

  def logtrace(self, msg):
    pass

  def logaudit(self, msg, recorddict=None):
    pass

class ConsoleErrorHandler(ErrorHandler):
  def __init__(self, errorfile=sys.stderr, tracefile=sys.stdout):
    self.errorfile = errorfile
    self.tracefile = tracefile

  def writelog(self, f, message):
    f.write(time.strftime('%Y-%m-%d %H:%M:%S')+': ')
    if isinstance(message, unicode):
      f.write(message.encode('utf8'))
    else:
      f.write(message)
    f.write('\n')
    f.flush()

  def logerror(self, message):
    self.writelog(self.errorfile, message)

  def logtrace(self, message):
    if self.tracefile is not None:
      self.writelog(self.tracefile, message)

  def logaudit(self, msg, recorddict=None):
    self.logtrace(msg + self.getauditstr(recorddict))

  def logtracewithtime(self, msg):
    t = time.time()
    ms = int((t - int(t))*1000.0)
    self.logtrace(".%03d: %s" % (ms, msg))

class TeeErrorHandler(ErrorHandler):
    """An error handler that tees the errors to multiple secondary handlers"""
    def __init__(self, *errorhandlers):
        """constructs the error handler"""
        self.errorhandlers = errorhandlers

    def logerror(self, message):
        """logs an error"""
        for errorhandler in self.errorhandlers:
            errorhandler.logerror(message)

    def logtrace(self, message):
        """logs a trace"""
        for errorhandler in self.errorhandlers:
            errorhandler.logtrace(message)

    def logaudit(self, message, recorddict=None):
        """logs an audit"""
        for errorhandler in self.errorhandlers:
            errorhandler.logaudit(message, recorddict)

class MailErrorHandler(ErrorHandler):
    def __init__(self, mailoptions, errorhandler=None):
        """constructs the emailing error handler"""
        if mailer is None:
          raise ImportError("Error importing jToolkit.mailer, so mailer not available")
        self.mailoptions = mailoptions
        self.waitingmail = []
        self.oldestmember = None
        self.sleepperiod = getattr(self.mailoptions, "sleepperiod", 10)
        self.gatherperiod = getattr(self.mailoptions, "gatherperiod", 60)
        self.logmailerrors = getattr(self.mailoptions, "logmailerrors", 1)
        self.logmailinfo = getattr(self.mailoptions, "logmailinfo", 0)
        self.errorhandler = errorhandler
        self.sendloopwaiting = 0
        self.shuttingdown = threading.Event()
        self.waitingLock = threading.Lock()

    def logerror(self, message):
        """logs an error"""
        if getattr(self.mailoptions, "senderrors", 1):
            self.mailmessage(message, "Error")

    def logtrace(self, message):
        """logs a trace"""
        if getattr(self.mailoptions, "sendtraces", 0):
            self.mailmessage(message, "Trace")

    def mailmessage(self, message, subject):
        """emails a message"""
        self.addmessage("%s\n%s\n\n%s" % (subject, "-"*len(subject), message))

    def shutdown(self):
        """shut down now ... no more waiting"""
        self.waitingLock.acquire()
        self.shuttingdown.set()
        self.waitingLock.release()

    def addmessage(self, message):
        """add the message to the queue"""
        self.waitingLock.acquire()
        if self.oldestmember is None:
            self.oldestmember = time.time()
        self.waitingmail.append(message)
        if not self.sendloopwaiting:
            self.sendloopwaiting = 1
            sendloopthread = threading.Thread(target=self.sendloop)
            sendloopthread.start()
        self.waitingLock.release()

    def sendloop(self):
        """wait around until we have gathered some other messages, then send them all together"""
        while 1:
            self.shuttingdown.wait(self.sleepperiod)
            self.waitingLock.acquire()
            expired = (self.oldestmember is not None and time.time() - self.oldestmember >= self.gatherperiod)
            if expired or self.shuttingdown.isSet():
                message = "\n\n".join(self.waitingmail)
                messagecount = len(self.waitingmail)
                self.waitingmail = []
                self.oldestmember = None
                self.sendloopwaiting = 0
            else:
                messagecount =  0
            self.waitingLock.release()
            if messagecount:
                self.dosend(message, "(%d messages)" % messagecount)
                return

    def dosend(self, message, subject):
        """send off any messages waiting in the queue (via chosen transport)"""
        mailengine = getattr(self.mailoptions, 'mailengine', '').lower()
        if not mailengine:
            if hasattr(self.mailoptions, 'smtpserver'):
                mailengine = 'smtp'
            elif hasattr(self.mailoptions, 'mapiprofile'):
                mailengine = 'mapi'
        if mailengine == 'smtp':
            self.dosmtpsend(message, subject)
        elif mailengine == 'simplemapi':
            self.dosimplemapisend(message, subject)
        elif mailengine == 'mapi':
            self.domapisend(message, subject)
        else:
            self.errorhandler.logerror("mailengine not specified or invalid mailengine: %r" % mailengine)

    def dosmtpsend(self, message, subject):
        """send off any messages waiting in the queue (via SMTP)"""
        toaddresses = self.mailoptions.toaddresses
        smtpserver = self.mailoptions.smtpserver
        subject = self.mailoptions.subject + " " + subject
        fromlabel = self.mailoptions.fromlabel
        fromaddress = self.mailoptions.fromaddress
        sendfrom = (fromlabel, fromaddress)
        if self.errorhandler is not None and self.logmailinfo:
            self.errorhandler.logtrace("sending message from %s to %r, length %d" % (fromaddress, toaddresses, len(message)))
        try:
            if self.errorhandler is not None and self.logmailerrors:
              mailererrorhandler = self.errorhandler
            else:
              mailererrorhandler = None
            mailer.SendSMTPMail(subject, message, SendTo=toaddresses, SendFrom=sendfrom, 
                                SMTPServer=smtpserver, errorhandler=mailererrorhandler)
        except:
            if self.errorhandler is not None and self.logmailerrors:
                self.errorhandler.logerror("error sending message over SMTP: %r" % ((fromaddress, toaddresses, message, self.mailoptions.smtpserver),))
                self.errorhandler.logerror(self.errorhandler.traceback_str())

    def dosimplemapisend(self, message, subject):
        """send off any messages waiting in the queue (via MAPI)"""
        toaddresses = self.mailoptions.toaddresses
        profile = getattr(self.mailoptions, "mapiprofile", None)
        subject = self.mailoptions.subject + " " + subject
        if self.errorhandler is not None and self.logmailinfo:
            self.errorhandler.logtrace("sending message with profile %r to %r, length %d" % (profile, toaddresses, len(message)))
        try:
            # TODO: alter mapi in mailer to be able to log its own error messages...
            mailer.SendMAPIMail(Subject=subject, Message=message, SendTo=toaddresses, MAPIProfile=profile)
        except:
            if self.errorhandler is not None and self.logmailerrors:
                self.errorhandler.logerror("error sending message over simple MAPI: %r" % ((profile, toaddresses, message),))
                self.errorhandler.logerror(self.errorhandler.traceback_str())

    def domapisend(self, message, subject):
        """send off any messages waiting in the queue (via MAPI)"""
        toaddresses = self.mailoptions.toaddresses
        profile = getattr(self.mailoptions, "mapiprofile", None)
        subject = self.mailoptions.subject + " " + subject
        if self.errorhandler is not None and self.logmailinfo:
            self.errorhandler.logtrace("sending message with profile %r to %r, length %d" % (profile, toaddresses, len(message)))
        try:
            # TODO: alter mapi in mailer to be able to log its own error messages...a
            mailer.SendEMAPIMail(Subject=subject, Message=message, SendTo=toaddresses, MAPIProfile=profile)
        except:
            if self.errorhandler is not None and self.logmailerrors:
                self.errorhandler.logerror("error sending message over MAPI: %r" % ((profile, toaddresses, message),))
                self.errorhandler.logerror(self.errorhandler.traceback_str())