1#Licensed to the Apache Software Foundation (ASF) under one
2#or more contributor license agreements.  See the NOTICE file
3#distributed with this work for additional information
4#regarding copyright ownership.  The ASF licenses this file
5#to you under the Apache License, Version 2.0 (the
6#"License"); you may not use this file except in compliance
7#with the License.  You may obtain a copy of the License at
8
9#     http://www.apache.org/licenses/LICENSE-2.0
10
11#Unless required by applicable law or agreed to in writing, software
12#distributed under the License is distributed on an "AS IS" BASIS,
13#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14#See the License for the specific language governing permissions and
15#limitations under the License.
16"""hodLogger provides a customized interface to Python's core logging package.
17"""
18
19import sys, os, re, logging, logging.handlers, inspect, pprint, types
20from tcp import get_address_tuple
21
22fileFormatString    = '[%(asctime)s] %(levelname)s/%(levelno)s \
23%(module)s:%(lineno)s - %(message)s'
24
25streamFormatString  = '%(levelname)s - %(message)s'
26
27debugStreamFormatString = '[%(asctime)s] %(levelname)s/%(levelno)s \
28%(module)s:%(lineno)s - %(message)s'
29
30syslogFormatString = '(%(process)d) %(levelname)s/%(levelno)s \
31%(module)s:%(lineno)s - %(message)s'
32
33smtpFormatString    = '[%(asctime)s] %(levelname)s/%(levelno)s \
34%(module)s:%(lineno)s\n\n%(message)s'
35
36fileFormater = logging.Formatter(fileFormatString)
37streamFormater = logging.Formatter(streamFormatString)
38debugStreamFormater = logging.Formatter(debugStreamFormatString)
39syslogFormater = logging.Formatter(syslogFormatString)
40smtpFormater = logging.Formatter(smtpFormatString)
41
42defaultFileLevel = 3
43defaultStreamLevel = 4
44defaultSyslogLevel = 3
45defaultSmtpLevel = 0
46
47hodLogLevelMap = { 0 : logging.CRITICAL,
48                   1 : logging.ERROR,
49                   2 : logging.WARNING,
50                   3 : logging.INFO,
51                   4 : logging.DEBUG    }
52
53hodStreamFormatMap = { 0 : streamFormater,
54                       1 : streamFormater,
55                       2 : streamFormater,
56                       3 : streamFormater,
57                       4 : debugStreamFormater }
58
59rehodLogLevelMap = {}
60for key in hodLogLevelMap.keys():
61    rehodLogLevelMap[hodLogLevelMap[key]] = key
62
63
64reModule = re.compile("^(.*)\..*$")
65
66hodLogs = {}
67
68class hodRotatingFileHandler(logging.handlers.RotatingFileHandler):
69    """ This class needs to be used in place of RotatingFileHandler when
70        the 2.4.0 Python interpreter is used."""
71
72    def emit(self, record):
73        """
74        Emit a record.
75
76        If a formatter is specified, it is used to format the record.
77        The record is then written to the stream with a trailing newline
78        [N.B. this may be removed depending on feedback]. If exception
79        information is present, it is formatted using
80        traceback.print_exception and appended to the stream.
81
82        *****
83
84        THIS IS A HACK, when instances of hodLogger get passed to the child of
85        a child thread for some reason self.stream gets closed.  This version
86        of emit re-opens self.stream if it is closed.  After testing it appears
87        that self.stream is only closed once after the second thread is
88        initialized so there is not performance penalty to this hack.  This
89        problem only exists in python 2.4.
90
91        *****
92        """
93        try:
94            if self.shouldRollover(record):
95                self.doRollover()
96            try:
97                msg = self.format(record)
98                fs = "%s\n"
99                if not hasattr(types, "UnicodeType"): #if no unicode support...
100                    self.stream.write(fs % msg)
101                else:
102                    try:
103                        self.stream.write(fs % msg)
104                    except UnicodeError:
105                        self.stream.write(fs % msg.encode("UTF-8"))
106                    except ValueError:
107                        self.stream = open(self.baseFilename, self.mode)
108                        self.stream.write(fs % msg)
109
110                self.flush()
111            except:
112                self.handleError(record)
113        except:
114            self.handleError(record)
115
116    def shouldRollover(self, record):
117        """
118        Determine if rollover should occur.
119
120        Basically, see if the supplied record would cause the file to exceed
121        the size limit we have.
122
123        *****
124
125        THIS IS A HACK, when instances of hodLogger get passed to the child of
126        a child thread for some reason self.stream gets closed.  This version
127        of emit re-opens self.stream if it is closed.  After testing it appears
128        that self.stream is only closed once after the second thread is
129        initialized so there is not performance penalty to this hack. This
130        problem only exists in python 2.4.
131
132        *****
133        """
134        if self.maxBytes > 0:                   # are we rolling over?
135            msg = "%s\n" % self.format(record)
136
137            try:
138                #due to non-posix-compliant Windows feature
139                self.stream.seek(0, 2)
140            except ValueError:
141                self.stream = open(self.baseFilename, self.mode)
142                self.stream.seek(0, 2)
143
144            if self.stream.tell() + len(msg) >= self.maxBytes:
145                return 1
146        return 0
147
148class hodCustomLoggingLogger(logging.Logger):
149    """ Slight extension of the logging.Logger class used by the hodLog class.
150    """
151    def findCaller(self):
152        """ findCaller() is supposed to return the callers file name and line
153            number of the caller. This was broken when the logging package was
154            wrapped by hodLog.  We should return much more relevant info now.
155            """
156
157        callerModule = ''
158        callerLine = 0
159
160        currentModule = os.path.basename(__file__)
161        currentModule = reModule.sub("\g<1>", currentModule)
162
163        frames = inspect.stack()
164        for i in range(len(frames)):
165            frameModule = os.path.basename(frames[i][1])
166            frameModule = reModule.sub("\g<1>", frameModule)
167            if frameModule == currentModule:
168                previousFrameModule = os.path.basename(frames[i+1][1])
169                previousFrameModule = reModule.sub("\g<1>",
170                    previousFrameModule)
171                callerFile = frames[i+1][1]
172                callerLine = frames[i+1][2]
173                continue
174
175        returnValues = (callerFile, callerLine)
176        if sys.version.startswith('2.4.4') or sys.version.startswith('2.5'):
177            returnValues = (callerFile, callerLine, None)
178
179        return returnValues
180
181class hodLog:
182    """ Cluster management logging class.
183
184        logging levels: 0 - log only critical messages
185                        1 - log critical and error messages
186                        2 - log critical, error, and warning messages
187                        3 - log critical, error, warning, and info messages
188                        4 - log critical, error, warning, info, and debug
189                            messages"""
190
191    def __init__(self, appName):
192        """Constructs a hodLogger object.
193
194        appName      - name of logging application, log filenames will be
195                       prepended with this name"""
196
197        self.__appName = appName
198
199        # initialize a dictionary to hold loggerNames
200        self.__loggerNames = {}
201
202        # initialize a dictionary to track log handlers and handler classes
203        self.__logObjs = { 'file' : {}, 'smtp' : {},
204                           'syslog' : {}, 'strm' : {} }
205
206        # use a custom logging.Logger class
207        logging.setLoggerClass(hodCustomLoggingLogger)
208
209        # get the root app logger
210        self.__logger = logging.getLogger(appName)
211        self.__logger.setLevel(logging.DEBUG)
212
213        hodLogs[self.__appName] = self
214
215    def __attr__(self, attrname):
216        """loggerNames  - list of defined logger names"""
217
218        if attrname   == "loggerNames":  return self.__loggerNames.keys()
219        else: raise AttributeError, attrname
220
221    def __repr__(self):
222        """Returns a string representation of a hodLog object of the form:
223
224           LOG_NAME
225                file: FILENAME (level LEVEL)
226                smtp: SMTP_SERVER from FROM_ADDRESS (level LEVEL)
227                strm: STRM_OBJECT (level LEVEL)
228                ... """
229
230        hodLogString = "hodLog: %s\n\n" % self.__appName
231        for loggerName in self.__loggerNames.keys():
232            hodLogString = "%s    logger: %s\n" % (hodLogString, loggerName)
233            handlerClasses = self.__logObjs.keys()
234            handlerClasses.sort()
235            for handlerClass in handlerClasses:
236                try:
237                    loggerLevelName = logging.getLevelName(
238                        self.__logObjs[handlerClass][loggerName]['level'])
239                    hodLogString = "%s        %s: %s (level %s)\n" % (
240                        hodLogString, handlerClass,
241                        self.__logObjs[handlerClass][loggerName]['data'],
242                        loggerLevelName)
243                except:
244                    hodLogString = "%s        %s: none\n" % (
245                        hodLogString, handlerClass)
246            hodLogString = "%s\n" % hodLogString
247
248        return hodLogString
249
250    # 'private' method which adds handlers to self.__logObjs
251    def __add_to_handlers(self, handlerClass, loggerName, handler, data,
252        level):
253        self.__logObjs[handlerClass][loggerName] = {}
254        self.__logObjs[handlerClass][loggerName]['handler'] = handler
255        self.__logObjs[handlerClass][loggerName]['data'] = data
256        self.__logObjs[handlerClass][loggerName]['level'] = level
257
258    # 'private' method which determines whether a hod log level is valid and
259    #   returns a valid logging.Logger level
260    def __get_logging_level(self, level, defaultLevel):
261        loggingLevel = ''
262        try:
263            loggingLevel = hodLogLevelMap[int(level)]
264        except:
265            loggingLevel = hodLogLevelMap[defaultLevel]
266
267        return loggingLevel
268
269    # make a logging.logger name rootLogger.childLogger in our case the
270    #   appName.componentName
271    def __get_logging_logger_name(self, loggerName):
272        return "%s.%s" % (self.__appName, loggerName)
273
274    def add_logger(self, loggerName):
275        """Adds a logger of name loggerName.
276
277           loggerName    - name of component of a given application doing the
278                           logging
279
280           Returns a hodLogger object for the just added logger."""
281
282        try:
283            self.__loggerNames[loggerName]
284        except:
285            loggingLoggerName = self.__get_logging_logger_name(loggerName)
286            logging.getLogger(loggingLoggerName)
287
288            self.__loggerNames[loggerName] = 1
289
290            return hodLogger(self.__appName, loggingLoggerName)
291
292    def add_file(self, logDirectory, maxBytes=0, backupCount=0,
293        level=defaultFileLevel, addToLoggerNames=None):
294        """Adds a file handler to all defined loggers or a specified set of
295           loggers.  Each log file will be located in logDirectory and have a
296           name of the form appName-loggerName.log.
297
298           logDirectory     - logging directory
299           maxBytes         - maximum log size to write in bytes before rotate
300           backupCount      - number of rotated logs to keep
301           level            - cluster management log level
302           addToLoggerNames - list of logger names to which stream handling
303                              will be added"""
304
305        def add_file_handler(loggerName):
306            if not self.__logObjs['file'].has_key(loggerName):
307                loggingLevel = self.__get_logging_level(level,
308                    defaultFileLevel)
309
310                logFile = os.path.join(logDirectory, "%s-%s.log" % (
311                    self.__appName, loggerName))
312
313                logFilePresent = False
314                if(os.path.exists(logFile)):
315                  logFilePresent = True
316
317                if sys.version.startswith('2.4'):
318                    fileHandler = hodRotatingFileHandler(logFile,
319                        maxBytes=maxBytes, backupCount=backupCount)
320                else:
321                    fileHandler = logging.handlers.RotatingFileHandler(logFile,
322                        maxBytes=maxBytes, backupCount=backupCount)
323                if logFilePresent and backupCount:
324                  fileHandler.doRollover()
325
326                fileHandler.setLevel(loggingLevel)
327                fileHandler.setFormatter(fileFormater)
328
329                loggingLoggerName = self.__get_logging_logger_name(loggerName)
330                aLogger = logging.getLogger(loggingLoggerName)
331                aLogger.addHandler(fileHandler)
332
333                fileData = "%s" % logFile
334                self.__add_to_handlers('file', loggerName, fileHandler,
335                    fileData, loggingLevel)
336
337        if addToLoggerNames:
338            for loggerName in addToLoggerNames:
339                add_file_handler(loggerName)
340        else:
341            for loggerName in self.__loggerNames:
342                add_file_handler(loggerName)
343
344    def add_stream(self, stream=sys.stderr, level=defaultStreamLevel,
345        addToLoggerNames=None):
346        """Adds a stream handler to all defined loggers or a specified set of
347           loggers.
348
349           stream           - a stream such as sys.stderr or sys.stdout
350           level            - cluster management log level
351           addToLoggerNames - tupple of logger names to which stream handling
352                              will be added"""
353
354        def add_stream_handler(loggerName):
355            if not self.__logObjs['strm'].has_key(loggerName):
356                loggingLevel = self.__get_logging_level(level,
357                    defaultStreamLevel)
358
359                streamHandler = logging.StreamHandler(stream)
360
361                streamHandler.setLevel(loggingLevel)
362
363                streamHandler.setFormatter(hodStreamFormatMap[int(level)])
364
365                loggingLoggerName = self.__get_logging_logger_name(loggerName)
366                aLogger = logging.getLogger(loggingLoggerName)
367                aLogger.addHandler(streamHandler)
368
369                streamData = "%s" % stream
370                self.__add_to_handlers('strm', loggerName, streamHandler,
371                    streamData, loggingLevel)
372
373        if addToLoggerNames:
374            for loggerName in addToLoggerNames:
375                add_stream_handler(loggerName)
376        else:
377            for loggerName in self.__loggerNames:
378                add_stream_handler(loggerName)
379
380    def add_syslog(self, address, level=defaultSyslogLevel,
381                   addToLoggerNames=None):
382        def add_syslog_handler(loggerName):
383            if not self.__logObjs['syslog'].has_key(loggerName):
384                loggingLevel = self.__get_logging_level(level,
385                    defaultSyslogLevel)
386
387                address[1] = int(address[1])
388                syslogHandler = logging.handlers.SysLogHandler(tuple(address),
389                                                               9)
390
391                syslogHandler.setLevel(loggingLevel)
392
393                syslogHandler.setFormatter(syslogFormater)
394
395                loggingLoggerName = self.__get_logging_logger_name(loggerName)
396                aLogger = logging.getLogger(loggingLoggerName)
397                aLogger.addHandler(syslogHandler)
398
399                syslogData = "%s:%s" % (address[0], address[1])
400                self.__add_to_handlers('syslog', loggerName, syslogHandler,
401                    syslogData, loggingLevel)
402
403        if addToLoggerNames:
404            for loggerName in addToLoggerNames:
405                add_syslog_handler(loggerName)
406        else:
407            for loggerName in self.__loggerNames:
408                add_syslog_handler(loggerName)
409
410
411    def add_smtp(self, mailHost, fromAddress, toAddresses,
412        level=defaultSmtpLevel, addToLoggerNames=None):
413        """Adds an SMTP handler to all defined loggers or a specified set of
414           loggers.
415
416           mailHost         - SMTP server to used when sending mail
417           fromAddress      - email address to use as the from address when
418                              sending mail
419           toAdresses       - comma seperated list of email address to which
420                              mail will be sent
421           level            - cluster management log level
422           addToLoggerNames - tupple of logger names to which smtp handling
423                              will be added"""
424
425        def add_email_handler(loggerName):
426            if not self.__logObjs['smtp'].has_key(loggerName):
427                loggingLevel = self.__get_logging_level(level,
428                    defaultSmtpLevel)
429
430                subject = loggerName
431                if   loggingLevel == 50:
432                    subject = "%s - a critical error has occured." % subject
433                elif loggingLevel == 40:
434                    subject = "%s - an error has occured."         % subject
435                elif loggingLevel == 30:
436                    subject = "%s - warning message."              % subject
437                elif loggingLevel == 20:
438                    subject = "%s - information message."          % subject
439                elif loggingLevel == 10:
440                    subject = "%s - debugging message."            % subject
441
442                mailHostTuple = get_address_tuple(mailHost)
443                emailHandler = logging.handlers.SMTPHandler(mailHostTuple,
444                    fromAddress, toAddresses, subject)
445
446                emailHandler.setFormatter(smtpFormater)
447                emailHandler.setLevel(loggingLevel)
448
449                loggingLoggerName = self.__get_logging_logger_name(loggerName)
450                aLogger = logging.getLogger(loggingLoggerName)
451                aLogger.addHandler(emailHandler)
452
453                emailData = "%s from %s" % (mailHost, fromAddress)
454                self.__add_to_handlers('smtp', loggerName, emailHandler,
455                    emailData, loggingLevel)
456
457        if addToLoggerNames:
458            for loggerName in addToLoggerNames:
459                add_email_handler(loggerName)
460        else:
461            for loggerName in self.__loggerNames:
462                add_email_handler(loggerName)
463
464    def status(self):
465        statusStruct = {}
466        for loggerName in self.__loggerNames.keys():
467            statusStruct[loggerName] = []
468            for handlerClass in self.__logObjs.keys():
469                loggerDict = {}
470                try:
471                    level = self.__logObjs[handlerClass][loggerName]['level']
472                    level = rehodLogLevelMap[level]
473
474                    loggerDict['handler'] = handlerClass
475                    loggerDict['level'] = level
476                    loggerDict['data'] = \
477                        self.__logObjs[handlerClass][loggerName]['data']
478                except:
479                    pass
480                else:
481                    statusStruct[loggerName].append(loggerDict)
482
483        return statusStruct
484
485    def lock_handlers(self):
486        for handlerClass in self.__logObjs.keys():
487            for loggerName in self.__logObjs[handlerClass].keys():
488                self.__logObjs[handlerClass][loggerName]['handler'].acquire()
489
490    def release_handlers(self):
491        for handlerClass in self.__logObjs.keys():
492            for loggerName in self.__logObjs[handlerClass].keys():
493                self.__logObjs[handlerClass][loggerName]['handler'].release()
494
495    def get_level(self, handler, loggerName):
496        return rehodLogLevelMap[self.__logObjs[handler][loggerName]['level']]
497
498    def set_level(self, handler, loggerName, level):
499        """Sets the logging level of a particular logger and logger handler.
500
501           handler    - handler (smtp, file, or stream)
502           loggerName - logger to set level on
503           level      - level to set logger
504        """
505
506        level = self.__get_logging_level(level, defaultFileLevel)
507        self.__logObjs[handler][loggerName]['handler'].setLevel(level)
508        self.__logObjs[handler][loggerName]['level'] = level
509
510        if handler == 'stream':
511            self.__logObjs[handler][loggerName]['handler'].setFormatter(
512                hodStreamFormatMap[int(level)])
513
514    def set_logger_level(self, loggerName, level):
515        status = 0
516        for handlerClass in self.__logObjs.keys():
517            if self.__logObjs[handlerClass].has_key(loggerName):
518                self.set_level(handlerClass, loggerName, level)
519            else:
520                status = 1
521
522        return status
523
524    def rollover(self, loggerName):
525        status = 0
526        if self.__logObjs['file'].has_key(loggerName):
527            if self.__logObjs['file'][loggerName]['handler'].shouldRollover():
528                self.__logObjs['file'][loggerName]['handler'].doRollover()
529        else:
530            status = 1
531
532        return status
533
534    def set_max_bytes(self, maxBytes):
535        status = 0
536        if self.__logObjs.has_key('file'):
537            for loggerName in self.__logObjs['file'].keys():
538                self.__logObjs['file'][loggerName]['handler'].maxBytes = 0
539        else:
540            status = 1
541
542        return status
543
544    def get_logger(self, loggerName):
545        """ Returns a hodLogger object for a logger by name. """
546
547        loggingLoggerName = self.__get_logging_logger_name(loggerName)
548        return hodLogger(self.__appName, loggingLoggerName)
549
550    def critical(self, loggerName, msg):
551        """Logs a critical message and flushes log buffers.  This method really
552           should only be called upon a catastrophic failure.
553
554           loggerName - logger to use
555           msg        - message to be logged"""
556
557        loggingLoggerName = self.__get_logging_logger_name(loggerName)
558        logger = logging.getLogger(loggingLoggerName)
559        logger.critical(msg)
560        self.flush()
561
562    def error(self, loggerName, msg):
563        """Logs an error message and flushes log buffers.
564
565           loggerName - logger to use
566           msg        - message to be logged"""
567
568        loggingLoggerName = self.__get_logging_logger_name(loggerName)
569        logger = logging.getLogger(loggingLoggerName)
570        logger.error(msg)
571        self.flush()
572
573    def warn(self, loggerName, msg):
574        """Logs a warning message.
575
576           loggerName - logger to use
577           msg        - message to be logged"""
578
579        loggingLoggerName = self.__get_logging_logger_name(loggerName)
580        logger = logging.getLogger(loggingLoggerName)
581        logger.warn(msg)
582
583    def info(self, loggerName, msg):
584        """Logs an information message.
585
586           loggerName - logger to use
587           msg        - message to be logged"""
588
589        loggingLoggerName = self.__get_logging_logger_name(loggerName)
590        logger = logging.getLogger(loggingLoggerName)
591        logger.info(msg)
592
593    def debug(self, loggerName, msg):
594        """Logs a debugging message.
595
596           loggerName - logger to use
597           msg        - message to be logged"""
598
599        loggingLoggerName = self.__get_logging_logger_name(loggerName)
600        logger = logging.getLogger(loggingLoggerName)
601        logger.debug(msg)
602
603    def flush(self):
604        """Flush all log handlers."""
605
606        for handlerClass in self.__logObjs.keys():
607            for loggerName in self.__logObjs[handlerClass].keys():
608                self.__logObjs[handlerClass][loggerName]['handler'].flush()
609
610    def shutdown(self):
611        """Shutdown all logging, flushing all buffers."""
612
613        for handlerClass in self.__logObjs.keys():
614            for loggerName in self.__logObjs[handlerClass].keys():
615                self.__logObjs[handlerClass][loggerName]['handler'].flush()
616                # Causes famous 'ValueError: I/O operation on closed file'
617                # self.__logObjs[handlerClass][loggerName]['handler'].close()
618
619class hodLogger:
620    """ Encapsulates a particular logger from a hodLog object. """
621    def __init__(self, appName, loggingLoggerName):
622        """Constructs a hodLogger object (a particular logger in a hodLog
623           object).
624
625           loggingLoggerName - name of a logger in hodLog object"""
626
627        self.__appName = appName
628        self.__loggerName = loggingLoggerName
629        self.__logger = logging.getLogger(self.__loggerName)
630
631    def __repr__(self):
632        """Returns a string representation of a hodComponentLog object."""
633
634        return "%s hodLog" % self.__loggerName
635
636    def __call__(self):
637        pass
638
639    def set_logger_level(self, loggerName, level):
640
641        return hodLogs[self.__appName].set_logger_level(loggerName, level)
642
643    def set_max_bytes(self, maxBytes):
644
645        return hodLogs[self.__appName].set_max_bytes(maxBytes)
646
647    def rollover(self):
648        return hodLogs[self.__appName].rollover(self.__loggerName)
649
650    def get_level(self, handler, loggerName):
651
652        return hodLogs[self.__appName].get_level(handler, loggerName)
653
654    def critical(self, msg):
655        """Logs a critical message and calls sys.exit(1).
656
657           msg     - message to be logged"""
658
659        self.__logger.critical(msg)
660
661    def error(self, msg):
662        """Logs an error message.
663
664           msg     - message to be logged"""
665
666        self.__logger.error(msg)
667
668    def warn(self, msg):
669        """Logs a warning message.
670
671           msg     - message to be logged"""
672
673        self.__logger.warn(msg)
674
675    def info(self, msg):
676        """Logs an information message.
677
678           msg     - message to be logged"""
679
680        self.__logger.info(msg)
681
682    def debug(self, msg):
683        """Logs a debugging message.
684
685           msg     - message to be logged"""
686
687        self.__logger.debug(msg)
688
689class hodDummyLogger:
690    """ Dummy hodLogger class.  Other hod classes requiring a hodLogger default
691        to this hodLogger if no logger is passed."""
692
693    def __init__(self):
694        """pass"""
695
696        pass
697
698    def __repr__(self):
699        return "dummy hodLogger"
700
701    def __call__(self):
702        """pass"""
703
704        pass
705
706    def set_logger_level(self, loggerName, level):
707
708        return 0
709
710    def set_max_bytes(self, loggerName, maxBytes):
711
712        return 0
713
714    def get_level(self, handler, loggerName):
715
716        return 4
717
718    def rollover(self):
719
720        return 0
721
722    def critical(self, msg):
723        """pass"""
724
725        pass
726
727    def error(self, msg):
728        """pass"""
729
730        pass
731
732    def warn(self, msg):
733        """pass"""
734
735        pass
736
737    def info(self, msg):
738        """pass"""
739
740        pass
741
742    def debug(self, msg):
743        """pass"""
744
745        pass
746
747def ensureLogDir(logDir):
748  """Verify that the passed in log directory exists, and if it doesn't
749  create it."""
750  if not os.path.exists(logDir):
751    try:
752      old_mask = os.umask(0)
753      os.makedirs(logDir, 01777)
754      os.umask(old_mask)
755    except Exception, e:
756      print >>sys.stderr, "Could not create log directories %s. Exception: %s. Stack Trace: %s" % (logDir, get_exception_error_string(), get_exception_string())
757      raise e
758
759def getLogger(cfg, logName):
760  if cfg['debug'] > 0:
761    user = cfg['userid']
762    baseLogger = hodLog(logName)
763    log = baseLogger.add_logger('main')
764
765    if cfg.has_key('log-dir'):
766      serviceId = os.getenv('PBS_JOBID')
767      if serviceId:
768        logDir = os.path.join(cfg['log-dir'], "%s.%s" % (user, serviceId))
769      else:
770        logDir = os.path.join(cfg['log-dir'], user)
771      if not os.path.exists(logDir):
772        os.mkdir(logDir)
773
774      baseLogger.add_file(logDirectory=logDir, level=cfg['debug'],
775               addToLoggerNames=('main',))
776
777    try:
778      if cfg.has_key('stream') and cfg['stream']:
779        baseLogger.add_stream(level=cfg['debug'], addToLoggerNames=('main',))
780
781      if cfg.has_key('syslog-address'):
782        baseLogger.add_syslog(cfg['syslog-address'],
783          level=cfg['debug'], addToLoggerNames=('main',))
784    except Exception,e:
785      # Caught an exception while initialising logger
786      log.critical("%s Logger failed to initialise. Reason : %s" % (logName, e))
787      pass
788    return log
789