From 7a8e2582b69b8c02fa57f5cf656a5abce0de79cc Mon Sep 17 00:00:00 2001
From: Andreas Battaglia <Andreas.Battaglia@cern.ch>
Date: Thu, 3 Jul 2008 17:14:20 +0000
Subject: [PATCH] * Logging system sends e-mail messages of desired severity
 level to responsibles * Database logging severity level can now be change run
 time

---
 Script/{main.py => CastorScript.py} | 59 ++++++++++++++++++-----------
 Script/Conf.py                      |  9 +++++
 Script/CopyThread.py                | 16 ++++----
 Script/Database.py                  | 28 +++++++++++++-
 Script/DeleteThread.py              | 16 ++++----
 Script/ManagerThread.py             |  2 +
 6 files changed, 91 insertions(+), 39 deletions(-)
 rename Script/{main.py => CastorScript.py} (77%)

diff --git a/Script/main.py b/Script/CastorScript.py
similarity index 77%
rename from Script/main.py
rename to Script/CastorScript.py
index 4b492d1..05f95f9 100755
--- a/Script/main.py
+++ b/Script/CastorScript.py
@@ -12,7 +12,7 @@ from time import sleep
 import threading
 import sys
 import signal
-import logging
+import logging, logging.handlers
 
 
 ##### Global flags for update and exit signals #####
@@ -22,14 +22,6 @@ confFlag = 0
 ##### Get Configuration #####
 conf = Conf.Conf()
 
-##### Set Logger for main thread #####
-logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)-8s %(message)s',
-                        datefmt='%a, %d %b %Y %H:%M:%S',filename= '/dev/null',filemode='w')
-mainLog = logging.FileHandler(conf.LogDir + '/mainLog.out','w')
-formatter = logging.Formatter('%(asctime)s %(levelname)-8s [%(file)s:%(line)s] %(message)s')
-mainLog.setFormatter(formatter)
-logger = logging.getLogger('main')
-
 ##### Set event #####
 event = threading.Event()
 
@@ -49,9 +41,29 @@ delete = DeleteThread(conf,event,dbLock,DeleteQueue,ClearQueue)
 
 def main(conf):
 
-    #### Set Logger for main thread #####
+    ##### Set root Logger #####
+    logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)-8s %(message)s',
+                        datefmt='%a, %d %b %Y %H:%M:%S',filename= '/dev/null',filemode='w')
+    formatter = logging.Formatter('%(asctime)s %(levelname)-8s [%(file)s:%(line)s] %(message)s')
+
+    ##### Add email handler #####
+    mailFlag = False
+    if len(conf.mailList)!=0:
+        mailFlag = True
+        # if we want sender to be the username, do: import getpass, getpass.getuser()
+        email = logging.handlers.SMTPHandler('localhost',conf.mailSender,conf.mailList,'CastorScript message')
+        email.setFormatter(formatter)
+        mailLevel = conf.mailLevel
+        setLogLevel(mailLevel,email)
+        logging.getLogger('').addHandler(email)
+    # end if
+    
+    ##### Set root Logger for main thread #####
+    mainLog = logging.FileHandler(conf.LogDir + '/mainLog.out','w')
+    mainLog.setFormatter(formatter)
+    logger = logging.getLogger('main')
     LogLevel = conf.LogLevel
-    setLogLevel(LogLevel)
+    setLogLevel(LogLevel,mainLog)
     logger.addHandler(mainLog)
          
     ##### Set update and exit signal handlers #####
@@ -65,7 +77,7 @@ def main(conf):
     db = 0
     logInfo = {'file':logger.findCaller()[0],'line':logger.findCaller()[1]}
     logger.info('Check for connection to Metadata Database',extra = logInfo) 
-    db = checkDB(db)
+    db = checkDB(db,logger)
     DBTimeout = conf.DBTimeout
         
     ##### Start the threads #####
@@ -73,25 +85,28 @@ def main(conf):
     copy.start()
     delete.start()
     logInfo = {'file':logger.findCaller()[0],'line':logger.findCaller()[1]}
-    logger.info('Manager,Copy and Delete Threads started',extra = logInfo) 
+    logger.info('Manager,Copy and Delete Threads started',extra = logInfo)
     
     ##### Every DBTimeout check if connection to Oracle database is still good #####
     while not exitFlag:
         #signal.pause()
         logInfo = {'file':logger.findCaller()[0],'line':logger.findCaller()[1]}
         logger.info('Check for connection to Metadata Database',extra = logInfo) 
-        db = checkDB(db)
+        db = checkDB(db,logger)
         event.wait(DBTimeout)
         ##### If update signal, update configuration #####
         if confFlag:
             reload(Conf)
             conf = Conf.Conf()
             DBTimeout = conf.DBTimeout
+            mailLevel = conf.mailLevel
             LogLevel = conf.LogLevel
             manager.managerConf(conf)
             copy.copyConf(conf)
             delete.deleteConf(conf)
-            setLogLevel(LogLevel)
+            db.dbConf(conf)
+            if mailFlag: setLogLevel(mailLevel,email)
+            setLogLevel(LogLevel,mainLog)
             confFlag = 0
             logInfo = {'file':logger.findCaller()[0],'line':logger.findCaller()[1]}
             logger.info('Configuration updated',extra = logInfo) 
@@ -130,7 +145,7 @@ def Exit(signum,frame):
     event.set()
 
 
-def checkDB(db):
+def checkDB(db,logger):
     dbFlag = copy.getDBFlag() and delete.getDBFlag()
     if not dbFlag: db = 0
     if not db:
@@ -169,13 +184,13 @@ def checkDB(db):
 
 
 #### Set Log severity level #####
-def setLogLevel(LogLevel):
+def setLogLevel(LogLevel,Log):
     level = LogLevel.upper()
-    if level == 'DEBUG': mainLog.setLevel(logging.DEBUG)
-    elif level == 'INFO': mainLog.setLevel(logging.INFO)
-    elif level == 'WARNING': mainLog.setLevel(logging.WARNING)
-    elif level == 'ERROR': mainLog.setLevel(logging.ERROR)
-    elif level == 'CRITICAL': mainLog.setLevel(logging.CRITICAL)
+    if level == 'DEBUG': Log.setLevel(logging.DEBUG)
+    elif level == 'INFO': Log.setLevel(logging.INFO)
+    elif level == 'WARNING': Log.setLevel(logging.WARNING)
+    elif level == 'ERROR': Log.setLevel(logging.ERROR)
+    elif level == 'CRITICAL': Log.setLevel(logging.CRITICAL)
 # end setLogLevel()
 
 
diff --git a/Script/Conf.py b/Script/Conf.py
index b992935..4aebde9 100755
--- a/Script/Conf.py
+++ b/Script/Conf.py
@@ -14,6 +14,15 @@ class Conf:
         # Log severity level (DEBUG,INFO,WARNING,ERROR,CRITICAL)
         self.LogLevel = 'debug'
 
+        # Email list which will receive error messages (list of strings)
+        self.mailList = []
+
+        # Email Log severity level (DEBUG,INFO,WARNING,ERROR,CRITICAL)
+        self.mailLevel = 'debug'
+
+        # Email sender
+        self.mailSender = 'andreas.battaglia@cern.ch'
+
 
 
         ########## METADATA DATABASE ##########
diff --git a/Script/CopyThread.py b/Script/CopyThread.py
index 0265316..3bec6d8 100755
--- a/Script/CopyThread.py
+++ b/Script/CopyThread.py
@@ -50,15 +50,15 @@ class CopyThread(threading.Thread):
         ##### Set Logger Level #####
         self.setLogLevel()
 
-               
+                       
         while(1):
             
             ##### When time to exit, do not take new files to be copied, but finish ongoing copy processes #####
             if (self.exitFlag and len(self.CopyList)==0): break
 
             self.logInfo = {'file':self.logger.findCaller()[0],'line':self.logger.findCaller()[1]}        
-            self.logger.debug('Size of CopyQueue: ' + str(self.CopyQueue.qsize()) + '\n\t\t\t\t\t\t Size of DeleteQueue: '
-                              + str(self.DeleteQueue.qsize()) + '\n\t\t\t\t\t\t Size of ClearQueue: ' + str(self.ClearQueue.qsize()),extra = self.logInfo)
+            self.logger.debug('Size of CopyQueue: ' + str(self.CopyQueue.qsize()) + ', Size of DeleteQueue: '
+                              + str(self.DeleteQueue.qsize()) + ', Size of ClearQueue: ' + str(self.ClearQueue.qsize()),extra = self.logInfo)
 
             ##### Launch n = maxCopy of parallel rfcp processes #####
             self.logInfo = {'file':self.logger.findCaller()[0],'line':self.logger.findCaller()[1]}        
@@ -124,7 +124,8 @@ class CopyThread(threading.Thread):
             self.event.wait(self.CopyTimeout)
 
         # end while
-
+        self.logInfo = {'file':self.logger.findCaller()[0],'line':self.logger.findCaller()[1]}        
+        self.logger.info('CopyThread exited',extra = self.logInfo)
     # end def run()
 
     
@@ -176,10 +177,12 @@ class CopyThread(threading.Thread):
                 
                 #SFO_filesize = os.path.getsize(DataFile) 
                 nsls = Popen(['nsls', '-l',CastorFile], stdout = PIPE, stderr = STDOUT, env= castorEnv)
+                w = nsls.wait()
+                nslsOut = nsls.stdout.read()
                 Castor_filesize = 0
-                if nsls.wait()==0: Castor_filesize = long(filter(lambda x: x!='',nsls.stdout.read().split(' '))[4]) 
+                if w==0: Castor_filesize = long(filter(lambda x: x!='',nslsOut.split(' '))[4]) 
                 self.logInfo = {'file':self.logger.findCaller()[0],'line':self.logger.findCaller()[1]}        
-                self.logger.debug(nsls.stdout.read(),extra = self.logInfo)
+                self.logger.debug(nslsOut,extra = self.logInfo)
                 # end if
 
                 if SFO_filesize == Castor_filesize:
@@ -196,7 +199,6 @@ class CopyThread(threading.Thread):
                         self.logger.info('Update Metadata database',extra = self.logInfo)
                         self.dbLock.acquire()
                         self.dbFlag = self.db.Transfer(DataFile,CastorFile)
-                        ## PASS THE LOGGER
                         self.dbLock.release()
                     else:
                         self.logInfo = {'file':self.logger.findCaller()[0],'line':self.logger.findCaller()[1]}        
diff --git a/Script/Database.py b/Script/Database.py
index cf4ce51..2ef64a4 100755
--- a/Script/Database.py
+++ b/Script/Database.py
@@ -77,7 +77,7 @@ class Database:
         try:
             self.filedeletion(sfofile)            
             return True
-        except:
+        except (InterfaceError,DatabaseError):
             self.logInfo = {'file':self.logger.findCaller()[0],'line':self.logger.findCaller()[1]}
             self.logger.error(str(sys.exc_info()),extra = self.logInfo)
             self.logger.error(str(sys.exc_type),extra = self.logInfo)
@@ -85,6 +85,14 @@ class Database:
             self.logger.error(str(self.db.lastOp()),extra = self.logInfo)
             self.logger.error(str(self.db.getLastKeys()),extra = self.logInfo)
             return False
+        except:
+            self.logInfo = {'file':self.logger.findCaller()[0],'line':self.logger.findCaller()[1]}
+            self.logger.error(str(sys.exc_info()),extra = self.logInfo)
+            self.logger.error(str(sys.exc_type),extra = self.logInfo)
+            self.logger.error(str(sys.exc_value),extra = self.logInfo)
+            self.logger.error(str(self.db.lastOp()),extra = self.logInfo)
+            self.logger.error(str(self.db.getLastKeys()),extra = self.logInfo)
+            return True
 
 
     def Transfer(self,sfofile,castorfile):
@@ -101,7 +109,7 @@ class Database:
 
             return True
 
-        except:
+        except (InterfaceError,DatabaseError):
             self.logInfo = {'file':self.logger.findCaller()[0],'line':self.logger.findCaller()[1]}
             self.logger.error(str(sys.exc_info()),extra = self.logInfo)
             self.logger.error(str(sys.exc_type),extra = self.logInfo)
@@ -109,6 +117,15 @@ class Database:
             self.logger.error(str(self.db.lastOp()),extra = self.logInfo)
             self.logger.error(str(self.db.getLastKeys()),extra = self.logInfo)
             return False
+        
+        except:
+            self.logInfo = {'file':self.logger.findCaller()[0],'line':self.logger.findCaller()[1]}
+            self.logger.error(str(sys.exc_info()),extra = self.logInfo)
+            self.logger.error(str(sys.exc_type),extra = self.logInfo)
+            self.logger.error(str(sys.exc_value),extra = self.logInfo)
+            self.logger.error(str(self.db.lastOp()),extra = self.logInfo)
+            self.logger.error(str(self.db.getLastKeys()),extra = self.logInfo)
+            return True
 
     
     def filedeletion(self,sfofile):
@@ -248,3 +265,10 @@ class Database:
         elif level == 'WARNING': self.dbLog.setLevel(logging.WARNING)
         elif level == 'ERROR': self.dbLog.setLevel(logging.ERROR)
         elif level == 'CRITICAL': self.dbLog.setLevel(logging.CRITICAL)
+
+    def dbConf(self,conf):
+        self.logInfo = {'file':self.logger.findCaller()[0],'line':self.logger.findCaller()[1]}        
+        self.logger.info('Update log level',extra = self.logInfo)
+        self.conf = conf
+        self.LogLevel = self.conf.LogLevel
+        self.setLogLevel(self.LogLevel)
diff --git a/Script/DeleteThread.py b/Script/DeleteThread.py
index f5ca5d4..ca3cbdc 100755
--- a/Script/DeleteThread.py
+++ b/Script/DeleteThread.py
@@ -113,7 +113,6 @@ class DeleteThread(threading.Thread):
                     self.logger.info('Update Metadata database',extra = self.logInfo)
                     self.dbLock.acquire()
                     self.dbFlag = self.db.Deletion(deletefile[0])
-                    ## PASS THE LOGGER
                     self.dbLock.release()
                 else:
                     self.logInfo = {'file':self.logger.findCaller()[0],'line':self.logger.findCaller()[1]}        
@@ -134,7 +133,7 @@ class DeleteThread(threading.Thread):
                 # block until room is available in the queue ?????
                 self.event.wait(self.DeleteTimeout)
                 self.logInfo = {'file':self.logger.findCaller()[0],'line':self.logger.findCaller()[1]}        
-                self.logger.warning('File ' + deletefile[0] + ' too recent: delete it later',extra = self.logInfo)
+                self.logger.info('File ' + deletefile[0] + ' too recent: delete it later',extra = self.logInfo)
                 continue
             
             ##### Check the SFO lock, if required #####
@@ -185,7 +184,6 @@ class DeleteThread(threading.Thread):
                     self.logInfo = {'file':self.logger.findCaller()[0],'line':self.logger.findCaller()[1]}        
                     self.logger.info('Update Metadata database',extra = self.logInfo)
                     self.dbFlag = self.db.Deletion(deletefile[0])
-                    ## PASS THE LOGGER
                     self.dbLock.release()
                 else:
                     self.logInfo = {'file':self.logger.findCaller()[0],'line':self.logger.findCaller()[1]}        
@@ -217,12 +215,13 @@ class DeleteThread(threading.Thread):
             self.logInfo = {'file':self.logger.findCaller()[0],'line':self.logger.findCaller()[1]}        
             self.logger.debug('Time to check if file has been migrated',extra = self.logInfo)
             Castor_file = copyDir + deletefile[0][deletefile[0].rindex('/'):] 
-            nsls = Popen(['nsls', '-l',Castor_file], stdout = PIPE, stderr = self.logFile, env= castorEnv)
+            nsls = Popen(['nsls', '-l',Castor_file], stdout = PIPE, stderr = STDOUT, env= castorEnv)
             nsls.wait()
+            nslsOut = nsls.stdout.read()
             self.logInfo = {'file':self.logger.findCaller()[0],'line':self.logger.findCaller()[1]}
-            self.logger.debug(nsls.stdout.read(),extra = self.logInfo)
-            isMig = 'm' in nsls.communicate()[0].split(' ')[0]
-            #isMig = nsls.communicate()[0].split(' ')[0].startswith('m')
+            self.logger.debug(nslsOut,extra = self.logInfo)
+            isMig = 'm' in nslsOut.split(' ')[0]
+            #isMig = nslsOut.split(' ')[0].startswith('m')
                     
             if isMig:
 
@@ -241,7 +240,6 @@ class DeleteThread(threading.Thread):
                     self.logInfo = {'file':self.logger.findCaller()[0],'line':self.logger.findCaller()[1]}        
                     self.logger.info('Update Metadata database',extra = self.logInfo)
                     self.dbFlag = self.db.Deletion(deletefile[0])
-                    ## PASS THE LOGGER
                     self.dbLock.release()
                 else:
                     self.logInfo = {'file':self.logger.findCaller()[0],'line':self.logger.findCaller()[1]}        
@@ -263,6 +261,8 @@ class DeleteThread(threading.Thread):
 
         self.event.wait(self.DeleteTimeout)
         # end while
+        self.logInfo = {'file':self.logger.findCaller()[0],'line':self.logger.findCaller()[1]}        
+        self.logger.info('DeleteThread exited',extra = self.logInfo)
     # end def run()
 
 
diff --git a/Script/ManagerThread.py b/Script/ManagerThread.py
index 830cbe0..a93e80c 100755
--- a/Script/ManagerThread.py
+++ b/Script/ManagerThread.py
@@ -179,6 +179,8 @@ class ManagerThread(threading.Thread):
             self.updateDates()
             
         # end while
+        self.logInfo = {'file':self.logger.findCaller()[0],'line':self.logger.findCaller()[1]}        
+        self.logger.info('ManagerThread exited',extra = self.logInfo)
     # end def run()
 
 
-- 
GitLab