CM/tool/cm_tool/CMLog.py

399 lines
13 KiB
Python
Executable File

# -*- coding:utf-8 -*-
#############################################################################
# Portions Copyright (c) 2022 Huawei Technologies Co.,Ltd.
#
# openGauss is licensed under Mulan PSL v2.
# You can use this software according to the terms
# and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#
# http://license.coscl.org.cn/MulanPSL2
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
# WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
# ----------------------------------------------------------------------------
# Description : CMLog.py is utility to handle the log
#############################################################################
import os
import sys
import datetime
import subprocess
import _thread as thread
import re
import logging.handlers as _handlers
import time
sys.path.append(sys.path[0] + "/../../")
from ErrorCode import ErrorCode
# import typing for comment.
try:
from typing import Dict
from typing import List
except ImportError:
Dict = dict
List = list
# max log file size
# 16M
MAXLOGFILESIZE = 16 * 1024 * 1024
LOG_DEBUG = 1
LOG_INFO = 2
LOG_WARNING = 2.1
LOG_ERROR = 3
LOG_FATAL = 4
class CMLog:
"""
Class to handle log file
"""
def __init__(self, logPath, module, prefix, suffix = ".log", expectLevel=LOG_DEBUG, traceId=None):
"""
function: Constructor
input : NA
output: NA
"""
self.logFile = ""
self.expectLevel = expectLevel
self.moduleName = module
self.fp = None
self.size = 0
self.suffix = suffix
self.prefix = prefix
self.logPath = logPath
self.pid = os.getpid()
self.step = 0
self.lock = thread.allocate_lock()
self.tmpFile = None
self.ignoreErr = False
self.traceId = traceId
try:
if not os.path.isdir(logPath):
print(ErrorCode.GAUSS_502["GAUSS_50211"] % logPath)
sys.exit(1)
# check log path
if not os.path.exists(logPath):
try:
os.makedirs(logPath, 0o700)
except Exception as e:
raise Exception(ErrorCode.GAUSS_502["GAUSS_50208"] %
logPath + " Error:\n%s" % str(e))
# create new log file
self.__openLogFile()
except Exception as ex:
print(str(ex))
sys.exit(1)
def __checkLink(self):
"""
function: check log file is link
input : NA
output: list of
"""
if os.path.islink(self.logFile):
raise Exception(ErrorCode.GAUSS_502["GAUSS_50206"] % self.logFile)
def __checkLogFileExist(self):
"""
check whether log file exists, if exist, get log file name
log file name format:
prefix-YYYY-mm-DD_HHMMSSsuffix = cm_install-YYYY-mm-DD_HHMMSS.log
"""
logFileList = "%s/logFileList_%s.dat" % (self.logPath, self.pid)
cmd = "ls %s | grep '^%s-.*%s$' > %s" % (
self.logPath, self.prefix, self.suffix, logFileList)
(status, output) = subprocess.getstatusoutput(cmd)
if status != 0:
if os.path.exists(logFileList):
os.remove(logFileList)
return False
with open(logFileList, "r") as fp:
filenameList = []
while True:
# get real file name
filename = (fp.readline()).strip()
if not filename:
break
existedResList = filename.split(".")
if len(existedResList) > 2:
continue
(existedPrefix, existedSuffix) = \
os.path.splitext(filename)
if existedSuffix != self.suffix:
continue
if len(filename) != len(self.prefix) + \
len(self.suffix) + 18:
continue
timeStamp = existedPrefix[-17:]
# check log file name
if self.__isValidDate(timeStamp):
filenameList.append(filename)
# cleanup logFileList
if os.path.exists(logFileList):
os.remove(logFileList)
if len(filenameList) == 0:
return False
# get logFile
fileName = max(filenameList)
self.logFile = os.path.join(self.logPath, fileName)
self.__checkLink()
return True
def __openLogFile(self):
"""
function: open log file
input : NA
output: NA
"""
try:
if self.__checkLogFileExist():
self.fp = open(self.logFile, "a")
return
# get current time
currentTime = time.strftime("%Y-%m-%d_%H%M%S")
# init log file
self.logFile = os.path.join(self.logPath, self.prefix + "-" + currentTime + self.suffix)
# Re-create the log file to add a retry 3 times mechanism,
# in order to call concurrently between multiple processes
retryTimes = 3
count = 0
while (True):
(status, output) = self.__createLogFile()
if status == 0:
break
count = count + 1
time.sleep(1)
if (count > retryTimes):
raise Exception(output)
# open log file
self.__checkLink()
self.fp = open(self.logFile, "a")
except Exception as e:
raise Exception(ErrorCode.GAUSS_502["GAUSS_50206"]
% self.logFile + " Error:\n%s" % str(e))
def __createLogFile(self):
"""
function: create log file
input : NA
output: (status, output)
"""
try:
if (not os.path.exists(self.logFile)):
os.mknod(self.logFile)
return (0, "")
except Exception as e:
return (1, str(e))
def __isValidDate(self, datastr):
"""
function: Judge if date valid
input : datastr
output: bool
"""
try:
time.strptime(datastr, "%Y-%m-%d_%H%M%S")
return True
except Exception as ex:
return False
def closeLog(self):
"""
function: Function to close log file
input : NA
output: NA
"""
try:
if self.fp:
self.fp.flush()
self.fp.close()
self.fp = None
except Exception as ex:
if self.fp:
self.fp.close()
raise Exception(str(ex))
# print the flow message to console window and log file
# AddInfo: constant represent step constant, addStep represent step
# plus, None represent no step
def log(self, msg, stepFlag=""):
"""
function:print the flow message to console window and log file
input: msg,stepFlag
control: when stepFlag="", the OM background log does not display
step information.
when stepFlag="addStep", the OM background log step will
add 1.
when stepFlag="constant", the OM background log step
defaults to the current step.
output: NA
"""
if (LOG_INFO >= self.expectLevel):
print(msg)
self.__writeLog("LOG", msg, stepFlag)
# print the flow message to log file only
def debug(self, msg, stepFlag=""):
"""
function:print the flow message to log file only
input: msg,stepFlag
control: when stepFlag="", the OM background log does not display
step information.
when stepFlag="addStep", the OM background log step will
add 1.
when stepFlag="constant", the OM background log step
defaults to the current step.
output: NA
"""
if (LOG_DEBUG >= self.expectLevel):
self.__writeLog("DEBUG", msg, stepFlag)
def warn(self, msg, stepFlag=""):
"""
function:print the flow message to log file only
input: msg,stepFlag
control: when stepFlag="", the OM background log does not display
step information.
when stepFlag="addStep", the OM background log step will
add 1.
when stepFlag="constant", the OM background log step
defaults to the current step.
output: NA
"""
if (LOG_WARNING >= self.expectLevel):
print(msg)
self.__writeLog("WARNING", msg, stepFlag)
# print the error message to console window and log file
def error(self, msg):
"""
function: print the error message to console window and log file
input : msg
output: NA
"""
if (LOG_ERROR >= self.expectLevel):
print(msg)
self.__writeLog("ERROR", msg)
# print the error message to console window and log file,then exit
def logExit(self, msg):
"""
function: print the error message to console window and log file,
then exit
input : msg
output: NA
"""
if (LOG_FATAL >= self.expectLevel):
print(msg)
try:
self.__writeLog("ERROR", msg)
except Exception as ex:
print(str(ex))
self.closeLog()
sys.exit(1)
def Step(self, stepFlag):
"""
function: return Step number info
input: add
output: step number
"""
if (stepFlag == "constant"):
return self.step
else:
self.step = self.step + 1
return self.step
def __getLogFileLine(self):
f = sys._getframe().f_back.f_back.f_back
return "%s(%s:%s)" % (os.path.basename(f.f_code.co_filename), f.f_code.co_name,
str(f.f_lineno))
def __writeLog(self, level, msg, stepFlag=""):
"""
function: Write log to file
input: level, msg, stepFlag
output: NA
"""
if self.fp is None:
return
try:
self.lock.acquire()
# if the log file does not exits, create it
if (not os.path.exists(self.logFile)):
self.__openLogFile()
else:
logPer = oct(os.stat(self.logFile).st_mode)[-3:]
self.__checkLink()
if not logPer == "600":
os.chmod(self.logFile, 0o600)
# check if need switch to an new log file
self.size = os.path.getsize(self.logFile)
if self.size >= MAXLOGFILESIZE and os.getuid() != 0:
self.closeLog()
self.__openLogFile()
replaceReg = re.compile(r'-W[ ]*[^ ]*[ ]*')
msg = replaceReg.sub('-W *** ', str(msg))
if msg.find("gs_redis") >= 0:
replaceReg = re.compile(r'-A[ ]*[^ ]*[ ]*')
msg = replaceReg.sub('-A *** ', str(msg))
strTime = datetime.datetime.now()
fileLine = self.__getLogFileLine()
if stepFlag == "":
if self.traceId:
print("[%s][%s][%d][%s][%s]:%s"
% (self.traceId, strTime, self.pid, self.moduleName,
level, msg), file=self.fp)
else:
print("[%s][%d][%s][%s]:%s" % (
strTime, self.pid, self.moduleName, level, msg),
file=self.fp)
else:
stepnum = self.Step(stepFlag)
print("[%s][%d][%s][%s][%s][Step%d]:%s" % (
strTime, self.pid, fileLine, self.moduleName, level, stepnum, msg),
file=self.fp)
self.fp.flush()
self.lock.release()
except Exception as ex:
self.lock.release()
if self.ignoreErr:
return
raise Exception(ErrorCode.GAUSS_502["GAUSS_50205"]
% (("log file %s") % self.logFile) +
" Error:\n%s" % str(ex))
@staticmethod
def exitWithError(msg, status=1):
"""
function: Exit with error message
input: msg, status=1
output: NA
"""
sys.stderr.write("%s\n" % msg)
sys.exit(status)
@staticmethod
def printMessage(msg):
"""
function: Print the String message
input: msg
output: NA
"""
sys.stdout.write("%s\n" % msg)