Hi Hoa,
Please find my comments marked with [Quyen]
Thanks,
Quyen
On 10/26/2017 3:33 PM, Hoa Le wrote:
Improved implementation of LOG pyosaf utils
The following comments shall be removed when the patch is pushed.
- This is the 3rd patch in the series of patches to improve the
implementation of the pyosaf utils of OpenSAF services.
- This patch is based on and uses the code change of the
pyosaf/utils/__init__.py file in the following patch for CLM:
[devel] [PATCH 1/1] pyosaf: High level python interfaces for CLM [#2602]
https://sourceforge.net/p/opensaf/mailman/message/36088562/
---
python/pyosaf/utils/log/logger.py | 437 +++++++++++++++++++++++++++++++++-----
1 file changed, 378 insertions(+), 59 deletions(-)
diff --git a/python/pyosaf/utils/log/logger.py
b/python/pyosaf/utils/log/logger.py
index fc7739c..27115ea 100644
--- a/python/pyosaf/utils/log/logger.py
+++ b/python/pyosaf/utils/log/logger.py
@@ -15,95 +15,414 @@
# Author(s): Ericsson
#
############################################################################
+# pylint: disable=unused-argument
""" SAF Logger utility """
import ctypes
+from copy import deepcopy
+from select import select
-from pyosaf import saLog, saAis
-from pyosaf.utils import log
+from pyosaf.saAis import eSaAisErrorT, SaVersionT, SaSelectionObjectT, \
+ eSaDispatchFlagsT, SaNameT, saAis, SaStringT
+from pyosaf.saNtf import saNtf, eSaNtfEventTypeT, SaNtfClassIdT
+from pyosaf.saLog import saLog, eSaLogFileFullActionT, \
+ SaLogFileCreateAttributesT_2, eSaLogHeaderTypeT, SaLogNtfLogHeaderT, \
+ SaLogGenericLogHeaderT, SaLogRecordT, SaLogCallbacksT, \
+ SaLogWriteLogCallbackT, SaLogHandleT, SaLogStreamHandleT, SaLogHeaderT, \
+ SaLogBufferT
+from pyosaf.utils import log, bad_handle_retry, log_err
+
+SYSTEM_STREAM = saLog.SA_LOG_STREAM_SYSTEM
+NOTIF_STREAM = saLog.SA_LOG_STREAM_NOTIFICATION
+ALARM_STREAM = saLog.SA_LOG_STREAM_ALARM
+
+
+class LoggerInfo(object):
+ """ This class encapsulates data structures used when opening a specific
+ log stream, or writing a log record to a log stream """
+ def __init__(self, exec_name=""):
+ self.stream_name = SYSTEM_STREAM
+ # log file create attributes
+ self.log_file_name = "saLogApplication"
+ self.log_file_path_name = "."
+ self.max_log_file_size = 5000000
+ self.max_log_record_size = 150
+ self.ha_property = True
+ self.log_file_full_action = \
+ eSaLogFileFullActionT.SA_LOG_FILE_FULL_ACTION_ROTATE
+ self.max_files_rotated = 4
+ self.log_file_format = "@Cr @Ch:@Cn:@Cs @Sv @Sl \"@Cb\""
+ # notification header info
+ self.event_type = eSaNtfEventTypeT.SA_NTF_ALARM_QOS
+ self.notification_object = ""
+ self.notifying_object = ""
+ self.ntf_class_id = SaNtfClassIdT(0, 0, 0)
+ self.event_time = saAis.SA_TIME_UNKNOWN
+ # generic info
+ self.log_severity = saLog.SA_LOG_SEV_INFO
+ self.log_service_user_name = exec_name
class SafLogger(object):
- """ SafLogger class handling log record write to the system log stream """
- @staticmethod
- def write_log_callback(invocation, error):
- """ This callback is triggered when the operation requested by the
- invocation of saLogWriteLogAsync() completes
+ """ This class provides logging function of the LOG service """
+ def __init__(self, exec_name="", version=None):
[Quyen] Please consider change a better name for "exec_name" as this
argument is to initialize the service_user_name
+ """ Constructor for SafLogger class
+
+ Args:
+ exec_name (str): Logger's service user name
+ """
+ self.exec_name = exec_name
+ self.init_version = version if version else SaVersionT('A', 2, 3)
+ self.version = None
+ self.invocation = 0
+ self.log_handle = None
+ self.stream_handle = None
+ self.sel_obj = SaSelectionObjectT()
+ self.record = SaLogRecordT()
+ self.log_write_error = eSaAisErrorT.SA_AIS_OK
+ self.callbacks = SaLogCallbacksT()
+ self.callbacks.saLogWriteLogCallback = \
+ SaLogWriteLogCallbackT(self._log_write_callback)
+ self.logger_info = LoggerInfo(exec_name)
+
+ def __enter__(self):
+ """ Enter method for SafLogger class """
+ return self
+
+ def __exit__(self, exception_type, exception_value, traceback):
+ """ Exit method for SafLogger class """
+ self._finalize()
+
+ def __del__(self):
+ """ Destructor for SafLogger class """
+ self._finalize()
+
+ def _log_write_callback(self, invocation, error):
+ """ This callback is invoked by the LOG service to acknowledge a
+ previous invocation of the saLogWriteLogAsync() function with the
+ SA_LOG_RECORD_WRITE_ACK flag, and also provides the result of the
+ respective log write operation.
Args:
invocation (SaInvocationT): Invocation associated with an
invocation of saLogWriteLogAsync()
- error (SaAisErrorT): Return code of the log write operation
+ error (SaAisErrorT): Result of the log write operation
"""
- pass
+ self.log_write_error = error
- def __init__(self, exec_name=""):
- """ Constructor for SafLogger class
+ def _finalize(self):
+ """ Finalize the log handle
+ The log stream associated with the handle will be closed as a result.
+ """
+ if self.log_handle:
[Quyen] General comment: Should use is or is not for checking with None
type. Please update all the similar places
PEP8:
*
Comparisons to singletons like None should always be done with is or
is not, never the equality operators.
Also, beware of writing if x when you really mean if x is not None
-- e.g. when testing whether a variable or argument that defaults to
None was set to some other value. The other value might have a type
(such as a container) that could be false in a boolean context!
+ rc = log.saLogFinalize(self.log_handle)
+ if rc != eSaAisErrorT.SA_AIS_OK:
+ log_err("saLogFinalize FAILED - %s" %
+ eSaAisErrorT.whatis(rc))
+ if rc == eSaAisErrorT.SA_AIS_OK or \
+ rc == eSaAisErrorT.SA_AIS_ERR_BAD_HANDLE:
+ self.log_handle = None
+ self.stream_handle = None
+
+ @bad_handle_retry
+ def init(self):
[Quyen] Should split this method into 2 methods as it does 2 things
initialize and open log stream. Beside that I don't see the close method
to close the opened stream. one more method should be added to support
closing the stream explicitly.
+ """ Initialize the LOG agent library and open the specified log stream
+
+ Returns:
+ SaAisErrorT: Return code of the corresponding LOG API call(s)
+ """
+ # Clean up resources left from previous initialization (if any)
+ self._finalize()
+
+ self.log_handle = SaLogHandleT()
+ self.stream_handle = SaLogStreamHandleT()
+ self.version = deepcopy(self.init_version)
+
+ rc = log.saLogInitialize(self.log_handle, self.callbacks, self.version)
+ if rc != eSaAisErrorT.SA_AIS_OK:
+ log_err("saLogInitialize FAILED - %s" %
+ eSaAisErrorT.whatis(rc))
+ else:
+ rc = log.saLogSelectionObjectGet(self.log_handle, self.sel_obj)
+ if rc != eSaAisErrorT.SA_AIS_OK:
+ log_err("saLogSelectionObjectGet FAILED - %s" %
+ eSaAisErrorT.whatis(rc))
+ if rc == eSaAisErrorT.SA_AIS_OK:
+ stream_name = SaNameT(self.logger_info.stream_name)
+ create_attrs, open_flag = self._set_log_stream_open_config()
+ rc = log.saLogStreamOpen_2(self.log_handle, stream_name,
+ create_attrs, open_flag,
+ saAis.SA_TIME_ONE_SECOND,
+ self.stream_handle)
+ if rc != eSaAisErrorT.SA_AIS_OK:
+ log_err("saLogStreamOpen_2 FAILED - %s" %
+ eSaAisErrorT.whatis(rc))
+ return rc
+
+ def _get_invocation(self):
+ """ Generate and return a new unique invocation number for use
+
+ Returns:
+ int: The new invocation number
+ """
+ self.invocation += 1
+ return self.invocation
+
+ def _set_log_stream_open_config(self):
[Quyen] The method name is "set" but what this method is to create the
attribute structure for later use. please rename the method to better
match with what it does.
+ """ Configure the attributes and flag used to open (or create, if not
+ yet existed) an application log stream
+
+ If the stream to open is a config log stream, setting of attributes and
+ flag is not allowed, and default values are enforced.
+
+ Returns:
+ SaLogFileCreateAttributesT_2: Log file create attributes
+ SaLogStreamOpenFlagsT: Log stream open flag
+ """
+ # Return default values of 'None' for log file create attributes and
+ # '0' for log stream open flag if the stream to open is a config stream
+ if "safLgStrCfg" in self.logger_info.stream_name:
+ return None, 0
+
+ create_attrs = SaLogFileCreateAttributesT_2()
+ create_attrs.logFileName = SaStringT(self.logger_info.log_file_name)
+ create_attrs.logFilePathName = SaStringT(
+ self.logger_info.log_file_path_name)
+ create_attrs.maxLogFileSize = self.logger_info.max_log_file_size
+ create_attrs.maxLogRecordSize = self.logger_info.max_log_record_size
+ create_attrs.haProperty = self.logger_info.ha_property
+ create_attrs.logFileFullAction = \
+ self.logger_info.log_file_full_action
+ create_attrs.maxFilesRotated = self.logger_info.max_files_rotated
+ create_attrs.logFileFmt = SaStringT(self.logger_info.log_file_format)
+
+ return create_attrs, saLog.SA_LOG_STREAM_CREATE
+
+ def _set_log_header(self):
[Quyen] The method name is "set" but what this method is to create the
log header structure for later use. please rename the method to better
match with what it does.
+ """ Fill in the header of the log record to be written with user-
+ provided information
+
+ Returns:
+ SaLogHeaderTypeT: Log header type
+ SaLogHeaderT: Log header structure filled with information
+ """
+ if self.logger_info.stream_name == ALARM_STREAM or \
+ self.logger_info.stream_name == NOTIF_STREAM:
+ header_type = eSaLogHeaderTypeT.SA_LOG_NTF_HEADER
+ ntf_header = SaLogNtfLogHeaderT()
+ ntf_header.notificationId = saNtf.SA_NTF_IDENTIFIER_UNUSED
+ ntf_header.eventType = self.logger_info.event_type
+ ntf_header.notificationObject.contents = SaNameT(
+ self.logger_info.notification_object)
+ ntf_header.notifyingObject.contents = SaNameT(
+ self.logger_info.notifying_object)
+ if self.logger_info.ntf_class_id:
+ ntf_header.notificationClassId.contents = \
+ self.logger_info.ntf_class_id
+ ntf_header.eventTime = self.logger_info.event_time
+ log_header = SaLogHeaderT()
+ log_header.ntfHdr = ntf_header
+ else:
+ header_type = eSaLogHeaderTypeT.SA_LOG_GENERIC_HEADER
+ generic_header = SaLogGenericLogHeaderT()
+ if self.logger_info.ntf_class_id:
+ generic_header.notificationClassId.contents = \
+ self.logger_info.ntf_class_id
+ generic_header.logSvcUsrName.contents = \
+ SaNameT(self.logger_info.log_service_user_name)
+ generic_header.logSeverity = self.logger_info.log_severity
+ log_header = SaLogHeaderT()
+ log_header.genericHdr = generic_header
+ return header_type, log_header
+
+ def set_log_stream_name(self, stream_name):
+ """ Set the name of the log stream to open/create
Args:
- exec_name (str): User name
+ stream_name (str): DN of the log stream; must be one of the below:
+ 1) saLog.SA_LOG_STREAM_SYSTEM
+ 2) saLog.SA_LOG_STREAM_NOTIFICATION
+ 3) saLog.SA_LOG_STREAM_ALARM
+ 4) User-defined application stream name with DN format:
+ safLgStr=<stream_name>,safApp=safLogService
"""
- self.invocation = 0
- self.log_handle = saLog.SaLogHandleT()
- self.sel_obj = saAis.SaSelectionObjectT()
- self.callbacks = saLog.SaLogCallbacksT()
- self.stream_handle = saLog.SaLogStreamHandleT()
- self.record = saLog.SaLogRecordT()
+ self.logger_info.stream_name = stream_name
- self.callbacks.saLogWriteLogCallback = \
- saLog.SaLogWriteLogCallbackT(self.write_log_callback)
+ def set_log_file_name(self, file_name):
+ """ Set the logFileName field of SaLogFileCreateAttributesT_2
- log.saLogInitialize(self.log_handle, self.callbacks, log.LOG_VERSION)
- log.saLogSelectionObjectGet(self.log_handle, self.sel_obj)
+ Args:
+ file_name (str): Log file name
+ """
+ self.logger_info.log_file_name = file_name
- stream_name = saAis.SaNameT(saLog.saLog.SA_LOG_STREAM_SYSTEM)
- log.saLogStreamOpen_2(self.log_handle, stream_name, None, 0,
- saAis.saAis.SA_TIME_ONE_SECOND,
- self.stream_handle)
+ def set_log_file_path_name(self, path_name):
+ """ Set the logFilePathName field of SaLogFileCreateAttributesT_2
- user_name = ctypes.pointer(saAis.SaNameT(exec_name))
- header_type = saLog.eSaLogHeaderTypeT.SA_LOG_GENERIC_HEADER
- self.record.logTimeStamp = saAis.saAis.SA_TIME_UNKNOWN
- self.record.logHdrType = header_type
- self.record.logHeader.genericHdr.logSvcUsrName = user_name
+ Args:
+ path_name (str): Log file path name
+ """
+ self.logger_info.log_file_path_name = path_name
- def finalize(self):
- """ Close a log stream and finalize the log handle corresponding to
- the log stream
+ def set_max_log_file_size(self, max_file_size):
+ """ Set the maxLogFileSize field of SaLogFileCreateAttributesT_2
+
+ Args:
+ max_file_size (int): Max log file size in bytes
"""
- log.saLogStreamClose(self.stream_handle)
- log.saLogFinalize(self.log_handle)
+ self.logger_info.max_log_file_size = max_file_size
- def get_invocation(self):
- """ Increase the invocation number and return it """
- self.invocation += 1
- return self.invocation
+ def set_max_log_record_size(self, max_record_size):
+ """ Set the maxLogRecordSize field of SaLogFileCreateAttributesT_2
- def __del__(self):
- """ Destructor to automatically finalize log handle """
- self.finalize()
+ Args:
+ max_record_size (int): Max log record size in bytes
+ """
+ self.logger_info.max_log_file_size = max_record_size
+
+ def set_ha_property(self, ha_property):
+ """ Set the haProperty field of SaLogFileCreateAttributesT_2
+
+ Args:
+ ha_property (bool): HA property of the log file
+ """
+ self.logger_info.ha_property = ha_property
+
+ def set_log_file_full_action(self, file_full_action):
+ """ Set the logFileFullAction field of SaLogFileCreateAttributesT_2
+
+ Args:
+ file_full_action (SaLogFileFullActionT): Log file-full action
+ """
+ self.logger_info.log_file_full_action = file_full_action
+
+ def set_max_files_rotated(self, max_files_rotated):
+ """ Set the maxFilesRotated field of SaLogFileCreateAttributesT_2
+
+ Args:
+ max_files_rotated (int): Max number of rotated log files
+ """
+ self.logger_info.max_files_rotated = max_files_rotated
+
+ def set_log_file_fmt(self, log_file_format):
+ """ Set the logFileFmt field of SaLogFileCreateAttributesT_2
+
+ Args:
+ log_file_format (str): Log record format expression
+ """
+ self.logger_info.log_file_format = log_file_format
+
+ def set_event_type(self, event_type):
+ """ Set the eventType field of SaLogNtfLogHeaderT
+
+ Args:
+ event_type (SaNtfEventTypeT): Alarm/Notification log event type
+ """
+ self.logger_info.event_type = event_type
+
+ def set_notification_object(self, notification_object):
+ """ Set the notificationObject field of SaLogNtfLogHeaderT
+
+ Args:
+ notification_object (str): Notification object's dn
+ """
+ self.logger_info.notification_object = notification_object
+
+ def set_notifying_object(self, notifying_object):
+ """ Set the notifyingObject field of SaLogNtfLogHeaderT
+ Args:
+ notifying_object (str): Notifying object's dn
+ """
+ self.logger_info.notifying_object = notifying_object
+
+ def set_ntf_class_id(self, ntf_class_id):
+ """ Set the notificationClassId field of SaLogNtfLogHeaderT and
+ SaLogGenericLogHeaderT
+
+ Args:
+ ntf_class_id (SaNtfClassIdT): Notification class id
+ """
+ self.logger_info.ntf_class_id = ntf_class_id
+
+ def set_ntf_event_time(self, event_time):
+ """ Set the eventTime field of SaLogNtfLogHeaderT
+
+ Args:
+ event_time (SaTimeT): Alarm/Notification log event time
+ """
+ self.logger_info.event_time = event_time
+
+ def set_log_severity(self, log_severity):
+ """ Set the logSeverity field of SaLogGenericLogHeaderT
+
+ Args:
+ log_severity (SaLogSeverityT): Log severity
+ """
+ self.logger_info.log_severity = log_severity
+
+ def set_service_user_name(self, service_user_name):
+ """ Set the logSvcUsrName field of SaLogGenericLogHeaderT
+
+ Args:
+ service_user_name (str): Logger's service user name
+ """
+ self.logger_info.log_service_user_name = service_user_name
+
+ @bad_handle_retry
def log(self, message):
- """ Write a log record to the system log stream
+ """ Write a message (log record) to a log stream
Args:
- message (str): Message to write to system log stream
+ message (str): Message to write to the log stream
+
+ Returns:
+ SaAisErrorT: Return code of the corresponding LOG API call(s)
"""
+ header_type, log_header = self._set_log_header()
+ self.record.logTimeStamp = saAis.SA_TIME_UNKNOWN
+ self.record.logHdrType = header_type
+ self.record.logHeader = log_header
- log_buffer = ctypes.pointer(saLog.SaLogBufferT(message))
- notice = saLog.saLog.SA_LOG_SEV_NOTICE
+ log_buffer = ctypes.pointer(SaLogBufferT(message))
self.record.logBuffer = log_buffer
- self.record.logHeader.genericHdr.logSeverity = notice
- log.saLogWriteLogAsync(self.stream_handle, self.get_invocation(),
- saLog.saLog.SA_LOG_RECORD_WRITE_ACK,
- self.record)
+ rc = log.saLogWriteLogAsync(self.stream_handle,
+ self._get_invocation(),
+ saLog.SA_LOG_RECORD_WRITE_ACK,
+ self.record)
+ if rc != eSaAisErrorT.SA_AIS_OK:
+ log_err("saLogWriteLogAsync FAILED - %s" %
+ eSaAisErrorT.whatis(rc))
+ else:
+ fds = [self.sel_obj.value]
+ io_evt = select(fds, [], [], 10)
+ if self.sel_obj.value in io_evt[0]:
+ rc = log.saLogDispatch(self.log_handle,
+ eSaDispatchFlagsT.SA_DISPATCH_ALL)
+ if rc != eSaAisErrorT.SA_AIS_OK:
+ log_err("saLogDispatch FAILED - %s" %
+ eSaAisErrorT.whatis(rc))
+ else:
+ self.log_write_error = eSaAisErrorT.SA_AIS_ERR_TIMEOUT
- def dispatch(self, read_fds):
- """ Invoke pending callbacks
+ if rc == eSaAisErrorT.SA_AIS_OK and self.log_write_error == \
+ eSaAisErrorT.SA_AIS_ERR_BAD_HANDLE:
+ rc = eSaAisErrorT.SA_AIS_ERR_BAD_HANDLE
- Args:
- read_fds (list): List of file descriptors associated with the
- log handle
+ if rc == eSaAisErrorT.SA_AIS_ERR_BAD_HANDLE:
+ init_rc = self.init()
+ # If the re-initialization of agent handle succeeds, we still need
+ # to return BAD_HANDLE to the function decorator, so that it would
+ # re-try the failed operation. Otherwise, the true error code is
+ # returned to the user to decide further actions.
+ if init_rc != eSaAisErrorT.SA_AIS_OK:
+ rc = init_rc
+
+ return rc
+
+ def get_log_write_result(self):
+ """ Return the result of the log write operation to the user
+
+ Returns:
+ SaAisErrorT: Result of the log write operation
"""
- if self.sel_obj.value in read_fds:
- log.saLogDispatch(self.log_handle,
- saAis.eSaDispatchFlagsT.SA_DISPATCH_ALL)
+ return self.log_write_error
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Opensaf-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/opensaf-devel