psa: remove old event feed items
[feedingit] / src / debugging.py
1 # Copyright (c) 2011 Neal H. Walfield
2 #
3 # This software is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 3 of the License, or
6 # (at your option) any later version.
7 #
8 # This software is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 # GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public License
14 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
16 from __future__ import with_statement
17 import os
18 import logging
19 import itertools
20 import sys
21 import string
22 import traceback
23 import time
24 import errno
25 import glob
26
27 logger = None
28 original_excepthook = None
29
30 def my_excepthook(exctype, value, tb):
31     """Log uncaught exceptions."""
32     logger.error(
33         "Uncaught exception: %s"
34         % (''.join(traceback.format_exception(exctype, value, tb)),))
35     original_excepthook(exctype, value, tb)
36
37 def init(dot_directory, debug=False, max_logfiles=1, program_name=None):
38     if not os.path.isabs(dot_directory):
39         dot_directory = os.path.join(os.path.expanduser("~"), dot_directory)
40
41     logging_directory = os.path.join(dot_directory, "logging")
42     try:
43         os.makedirs(logging_directory)
44     except OSError, e:
45         if e.errno != errno.EEXIST:
46             raise
47
48     if program_name is None:
49         program_name = os.path.basename(sys.argv[0])
50     string.translate(program_name, string.maketrans(' .', '__'))
51
52     timestamp = time.strftime("%Y%m%d")
53
54     logfiles = glob.glob(os.path.join(logging_directory,
55                                       program_name + '-*.log'))
56     if len(logfiles) >= max_logfiles:
57         logfiles.sort()
58         for f in logfiles[:-(max_logfiles+1)]:
59             print "Purging old log file %s" % (f,)
60             try:
61                 os.remove(f)
62             except OSError, e:
63                 print "Removing %s: %s" % (f, str(e))
64
65     logfile = os.path.join(logging_directory,
66                            program_name + '-' + timestamp + '.log')
67
68     print "Sending output to %s" % logfile
69
70     global logger
71     logger = logging.getLogger(__name__) 
72
73     if debug:
74         level = logging.DEBUG
75     else:
76         level = logging.INFO
77
78     logging.basicConfig(
79         level=level,
80         format=('%(asctime)s (pid: ' + str(os.getpid()) + ') '
81                 + '%(levelname)-8s %(message)s'),
82         filename=logfile,
83         filemode='a')
84
85     # Log uncaught exceptions.
86     global original_excepthook
87     original_excepthook = sys.excepthook
88     sys.excepthook = my_excepthook
89
90     def redirect(thing):
91         filename = os.path.join(logging_directory, program_name + '.' + thing)
92         try:
93             with open(filename, "r") as fhandle:
94                 contents = fhandle.read()
95         except IOError, e:
96             if e.errno in (errno.ENOENT,):
97                 fhandle = None
98                 contents = ""
99             else:
100                 logging.error("Reading %s: %s" % (filename, str(e)))
101                 raise
102
103         logging.error("std%s of last run: %s" % (thing, contents))
104
105         if fhandle is not None:
106             os.remove(filename)
107
108         print "Redirecting std%s to %s" % (thing, filename)
109         return open(filename, "w", 0)
110             
111     sys.stderr = redirect('err')
112     sys.stdout = redirect('out')
113