aboutsummaryrefslogtreecommitdiffstats
path: root/pylog/onaplogging/marker
diff options
context:
space:
mode:
Diffstat (limited to 'pylog/onaplogging/marker')
-rw-r--r--pylog/onaplogging/marker/marker.py257
-rw-r--r--pylog/onaplogging/marker/markerFactory.py126
-rw-r--r--pylog/onaplogging/marker/markerFilter.py52
-rw-r--r--pylog/onaplogging/marker/markerHandler.py118
4 files changed, 461 insertions, 92 deletions
diff --git a/pylog/onaplogging/marker/marker.py b/pylog/onaplogging/marker/marker.py
index 5414e21..17a3328 100644
--- a/pylog/onaplogging/marker/marker.py
+++ b/pylog/onaplogging/marker/marker.py
@@ -14,10 +14,30 @@
import abc
+from typing import Iterable, List, Optional, Union, Iterator
+from deprecated import deprecated
+from warnings import warn
+from logging import LogRecord
+
MARKER_TAG = "marker"
class Marker(object):
+ """Abstract class for defining the marker structure.
+
+ TODO:
+ after deprecated child methods are removed, rename them here.
+ Extends:
+ object
+ Method list:
+ getName
+ addChild
+ addChilds
+ removeChild
+ contains
+ Raises:
+ NotImplementedError
+ """
__metaclass__ = abc.ABCMeta
@@ -51,92 +71,235 @@ class Marker(object):
class BaseMarker(Marker):
+ """Basic marker class.
+
+ It is a marker with base functionalities that add sub-level markers and
+ check if another marker exists as the parent itself or as its child.
+
+ Extends:
+ Marker
+ Properties:
+ name : The name of the marker.
+ children (list) : The list of all children (sub-level) markers.
+ Arguments:
+ name (str) : The name of the marker.
+ Methods:
+ getName : Returns the name of the marker.
+ addChild : Adds a sub-level marker.
+ addChilds : Adds a list of sub-level markers.
+ removeChild : Removes a specified sub-level marker.
+ contains : Checks if a sub-level marker exists.
+ """
+
+ @property
+ def name(self):
+ # type: () -> str
+ """Name of the parent marker."""
+ return self.__name
+
+ @property
+ def children(self):
+ # type: () -> List[Marker]
+ """Child markers of one parent marker."""
+ return self.__childs
+
+ @name.setter
+ def name(self, value):
+ # type: (str) -> None
+ self.__name = value
+
+ @children.setter
+ def children(self, value):
+ # type: (List[Marker]) -> None
+ self.__childs = value
+
+ def __init__(self, name): # type: (str)
+ """
+ Raises:
+ TypeError : If the `name` parameter is not a string.
+ ValueError : If the `name` parameter is an empty string.
+ """
- def __init__(self, name):
super(BaseMarker, self).__init__()
+
if not isinstance(name, str):
raise TypeError("not str type")
+
if name == "":
raise ValueError("empty value")
+
+ warn("Attribute `__childs` is replaced by the property `children`."
+ "Use children instead.", DeprecationWarning)
+
self.__name = name
self.__childs = []
- def getName(self):
- return self.__name
+ def add_child(self, marker):
+ # type: (Marker) -> None
+ """Append a marker to child markers.
- def __iter__(self):
- return iter(self.__childs)
+ Use this method to describe a different level of logs. For example,
+ error log would use the ERROR marker. However it's possible to
+ create a, for instance, TYPE_ERROR to mark type related events.
+ In this case TYPE_ERROR will be a child of parent ERROR.
- def __eq__(self, other):
+ Args:
+ marker : marker describing a different log level.
+ Raises:
+ TypeError : if the marker object has different type.
+ """
- if not isinstance(other, Marker):
- return False
- if id(self) == id(other):
- return True
+ if not isinstance(marker, Marker):
+ raise TypeError("Bad marker type. \
+ Can only add markers of type Marker. \
+ Type %s was passed." % type(marker))
- return self.__name == other.getName()
+ if self == marker:
+ return
- def __hash__(self):
- return hash(self.__name)
+ if marker not in self.children:
+ self.children.append(marker)
- def contains(self, item=None):
+ def add_children(self, markers):
+ # type: (Iterable[List]) -> None
+ """ Append a list of markers to child markers.
+
+ Args:
+ markers : An iterable object, containing markers.
+ Raises:
+ Exception : If `marker` parameter is not iterable.
+ """
+
+ try:
+ iter(markers)
+ except Exception as e:
+ raise e
+
+ for marker in markers:
+ self.children.append(marker)
+
+ def remove_child(self, marker):
+ # type: (Marker) -> None
+ """Use this method to remove a marker from the children list.
- if isinstance(item, Marker):
- if item == self:
+ Args:
+ marker : A child marker object.
+ Raises:
+ TypeError: if the marker object has different type.
+ """
+
+ if not isinstance(marker, Marker):
+ raise TypeError("Bad marker type. \
+ Can only add markers of type Marker. \
+ Type %s was passed." % type(marker))
+
+ if marker in self.children:
+ self.children.remove(marker)
+
+ def contains(self, item=None):
+ # type: (Optional[Union[Marker, str]]) -> bool
+ """
+ Use it to check if a marker exists as a parent itself or its chidren.
+
+ Args:
+ item : A child marker object. Defaults to None.
+ Returns:
+ bool : True if the marker exists.
+ """
+
+ warn("`item` argument will be replaced with `marker`. "
+ "Default value None will be removed.",
+ DeprecationWarning)
+ marker = item
+
+ if isinstance(marker, Marker):
+ if marker == self:
return True
return len(list(filter(
- lambda x: x == item, self.__childs))) > 0
+ lambda x: x == marker, self.children))) > 0
- elif isinstance(item, str):
- if item == self.__name:
+ elif isinstance(marker, str):
+ if marker == self.name:
return True
return len(list(filter(
- lambda x: x.__name == item, self.__childs))) > 0
+ lambda x: x.name == marker, self.children))) > 0
return False
- def addChild(self, item):
- if not isinstance(item, Marker):
- raise TypeError("can only add (not %s) marker type"
- % type(item))
- if self == item:
- return
- if item not in self.__childs:
- self.__childs.append(item)
+ def __iter__(self):
+ # type: () -> Iterator[List[Marker]]
+ return iter(self.__childs)
+
+ def __hash__(self):
+ # type (): -> int
+ return hash(self.__name)
+ def __eq__(self, other):
+ # type: (Marker) -> bool
+ if not isinstance(other, Marker):
+ return False
+ if id(self) == id(other):
+ return True
+
+ return self.__name == other.getName()
+
+ @deprecated(reason="Will be removed. Call the `name` property instead.")
+ def getName(self):
+ """Class attribute getter."""
+ return self.name
+
+ @deprecated(reason="Will be removed. Call add_children(markers) instead.")
def addChilds(self, childs):
- try:
- iter(childs)
- except Exception as e:
- raise e
+ """Add a list of sub-level markers. See add_children(markers)"""
+ self.add_children(childs)
- for item in childs:
- self.addChild(item)
+ @deprecated(reason="Will be removed. Call add_child(marker) instead.")
+ def addChild(self, item):
+ """Add a sub-level marker. See add_child(marker)"""
+ self.add_child(item)
+ @deprecated(reason="Will be removed. Call remove_child(marker) instead.")
def removeChild(self, item):
- if not isinstance(item, Marker):
- raise TypeError("can only add (not %s) marker type"
- % type(item))
- if item in self.__childs:
- self.__childs.remove(item)
+ """Remove a sub-level marker. See remove_child(marker)"""
+ self.remove_child(item)
+@deprecated(reason="Will be removed. "
+ "Call match_marker(record, marker_to_match) instead.")
def matchMarkerHelp(record, markerToMatch):
-
- marker = getattr(record, MARKER_TAG, None)
-
- if marker is None or markerToMatch is None:
+ """See match_marker(record, marker_to_match)."""
+ return match_markers(record, markerToMatch)
+
+
+def match_markers(record, marker_to_match):
+ # type: (LogRecord, Union[Marker, List]) -> bool
+ """
+ Use this method to match a marker (or a list of markers) with a LogRecord
+ record.
+
+ Args:
+ record : a record that may contain a marker.
+ markerToMatch : a marker or a list of markers.
+ Raises:
+ Exception : if match check went wrong.
+ Returns:
+ bool : whether the check can be done or the marker is found.
+ """
+ record_marker = getattr(record, MARKER_TAG, None)
+
+ if record_marker is None or \
+ marker_to_match is None:
return False
- if not isinstance(marker, Marker):
+ if not isinstance(record_marker, Marker):
return False
try:
- if isinstance(markerToMatch, list):
+ if isinstance(marker_to_match, list):
return len(list(filter(
- lambda x: marker.contains(x), markerToMatch))) > 0
+ lambda x: record_marker.contains(x), marker_to_match))) > 0
- return marker.contains(markerToMatch)
+ return record_marker.contains(marker_to_match)
except Exception as e:
raise e
diff --git a/pylog/onaplogging/marker/markerFactory.py b/pylog/onaplogging/marker/markerFactory.py
index 0705235..a0e9887 100644
--- a/pylog/onaplogging/marker/markerFactory.py
+++ b/pylog/onaplogging/marker/markerFactory.py
@@ -14,12 +14,32 @@
import abc
import threading
+
+from deprecated import deprecated
+from warnings import warn
+from typing import Dict, Optional
+
+from .marker import Marker
from .marker import BaseMarker
lock = threading.RLock()
class IMarkerFactory(object):
+ """Abstract marker factory for defining structure.
+
+ TODO:
+ after deprecated child methods are removed, rename them here.
+ Extends:
+ object
+ Method list:
+ getMarker
+ deleteMarker
+ exist
+ Raises:
+ NotImplementedError
+ """
+
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
@@ -36,39 +56,111 @@ class IMarkerFactory(object):
class MarkerFactory(IMarkerFactory):
+ """A factory class maganing every marker.
+
+ It is designed to check the existance, create and remove single markers.
+ This class follows a singleton pattern - only one instance can be created.
+
+ Extends:
+ IMarkerFactory
+ Properties:
+ marker_map : a map of existing markers.
+ Attributes:
+ _instance : a marker factory instance.
+ Methods:
+ getMarker : creates a new marker or returns an available one.
+ deleteMarker : removes a specific marker.
+ exist : checks if a specific marker exists.
+ """
_instance = None
_marker_map = {}
- def __new__(cls, *args, **kwargs):
-
- if cls._instance is None:
- cls._instance = super(MarkerFactory, cls).__new__(cls)
+ @property
+ def marker_map(self):
+ # type: () -> Dict
+ if not hasattr(self, '_marker_map'):
+ self._marker_map = {}
+ return self._marker_map
+
+ def get_marker(self, name=None):
+ # type: (Optional[str]) -> Marker
+ """
+ Use it to get any marker by its name. If it doesn't exist - it
+ will create a new marker that will be added to the factory.
+ Blocks the thread while executing.
+
+ Args:
+ name : A marker name. Defaults to None.
+ Raises:
+ ValueError : If `name` is None.
+ Returns:
+ Marker : A found or just newly created marker.
+ """
+
+ if name is None:
+ raise ValueError("Marker name is None. Must have a str value.")
- return cls._instance
+ lock.acquire()
- def getMarker(self, marker_name=None):
- if marker_name is None:
- raise ValueError("not empty")
+ marker = self.marker_map.get(name, None)
- lock.acquire()
- marker = self._marker_map.get(marker_name, None)
if marker is None:
- marker = BaseMarker(name=marker_name)
- self._marker_map[marker_name] = marker
+ marker = BaseMarker(name)
+ self.marker_map[name] = marker
+
lock.release()
return marker
- def deleteMarker(self, marker_name=None):
+ def delete_marker(self, name=None):
+ # type: (Optional[str]) -> bool
+ """
+ Args:
+ name: A marker name. Defaults to None.
+ Returns:
+ bool: The status of deletion.
+ """
+
lock.acquire()
- if self.exist(marker_name):
- del self._marker_map[marker_name]
+ exists = self.exists(name)
+ if exists:
+ del self.marker_map[name]
return True
lock.release()
+
return False
+ def exists(self, name=None):
+ # type: (Optional[str]) -> bool
+ """
+ Checks whether the search for a marker returns None and returns the
+ status of the operation.
+
+ Args:
+ name: marker name. Defaults to None.
+ Returns:
+ bool: status of whether the marker was found.
+ """
+ marker = self.marker_map.get(name, None)
+ return marker is not None
+
+ def __new__(cls, *args, **kwargs):
+ if cls._instance is None:
+ cls._instance = super(MarkerFactory, cls).__new__(cls)
+
+ warn("_marker_map attribute will be replaced by marker_map property.",
+ DeprecationWarning)
+ return cls._instance
+
+ @deprecated(reason="Will be removed. Call exists(name) instead.")
def exist(self, marker_name=None):
+ return self.exists(marker_name)
- return self._marker_map.get(
- marker_name, None) is not None
+ @deprecated(reason="Will be removed. Call get_marker(name) instead.")
+ def getMarker(self, marker_name=None):
+ return self.get_marker(marker_name)
+
+ @deprecated(reason="Will be removed. Call delete_marker(name) instead.")
+ def deleteMarker(self, marker_name=None):
+ return self.delete_marker(marker_name)
diff --git a/pylog/onaplogging/marker/markerFilter.py b/pylog/onaplogging/marker/markerFilter.py
index 4f49884..a381d8e 100644
--- a/pylog/onaplogging/marker/markerFilter.py
+++ b/pylog/onaplogging/marker/markerFilter.py
@@ -12,21 +12,57 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import sys
-from logging import Filter
-from .marker import matchMarkerHelp
+from logging import Filter, LogRecord
+from warnings import warn
+from typing import List, Optional, Union
+
+from onaplogging.utils.system import is_above_python_2_7
+
+from .marker import match_markers, Marker
class MarkerFilter(Filter):
+ """Marker filtering.
+
+ Extends:
+ logging.Filter
+ Properties:
+ marker_to_match (Marker/list): a marker of list of markers.
+ Methods
+ filter: Filter records by the current filter marker(s).
+ """
+
+ @property
+ def markers_to_match(self):
+ # type: () -> Union[Marker, List[Marker]]
+ return self.markersToMatch # TODO renamed - deprecated
- def __init__(self, name="", markers=None):
- if sys.version_info > (2, 7):
+ @markers_to_match.setter
+ def markers_to_match(self, value):
+ # type: ( Union[Marker, List[Marker]] ) -> None
+ self.markersToMatch = value
+
+ def __init__(self,
+ name="", # type: str
+ markers=None): # type: Optional[Union[Marker, List[Marker]]]
+
+ if is_above_python_2_7():
super(MarkerFilter, self).__init__(name)
+
else:
Filter.__init__(self, name)
- self.markerToMatch = markers
+ warn("markersToMatch attribute will be replaced by a property. "
+ "Use markers_to_match property instead.", DeprecationWarning)
+ self.markers_to_match = markers
def filter(self, record):
- # compare filter's markers with record's marker
- return matchMarkerHelp(record, self.markerToMatch)
+ # type: (LogRecord) -> bool
+ """Filter by looking for a marker match.
+
+ Args:
+ record: A record to match with the filter(s).
+ Returns:
+ bool: Whether the record matched with the filter(s)
+ """
+ return match_markers(record, self.markers_to_match)
diff --git a/pylog/onaplogging/marker/markerHandler.py b/pylog/onaplogging/marker/markerHandler.py
index e9ce810..36934a8 100644
--- a/pylog/onaplogging/marker/markerHandler.py
+++ b/pylog/onaplogging/marker/markerHandler.py
@@ -12,40 +12,118 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import sys
+from logging import LogRecord
from logging.handlers import SMTPHandler
-from .marker import matchMarkerHelp
+from typing import Tuple, List, Optional, Union
+
+from onaplogging.utils.system import is_above_python_2_7, is_above_python_3_2
+
+from .marker import match_markers, Marker
class MarkerNotifyHandler(SMTPHandler):
+ """Handler for email notification.
+
+ Wraps logging.handler.SMTPHandler and extends it by sending only such
+ notifications which contain certain markers.
+
+ Extends:
+ SMTPHandler
+ Property:
+ markers: A marker or a list of markers.
+ Args:
+ mailhost: A (host, port) tuple.
+ fromaddr: The sender of the email notification.
+ toaddrs: Email notification recepient(s).
+ subject: Email subject.
+ credentials: A (username, password) tuple.
+ secure: For example (TLS). It is used when the
+ credentials are supplied.
+ timout: Default is 5.0 seconds. Python version 3.2+
+ markers: A marker or a list of markers.
+ """
+
+ @property
+ def markers(self):
+ # type: () -> Union[Marker, List[Marker]]
+ return self._markers
+
+ @markers.setter
+ def markers(self, value):
+ # type: ( Union[Marker, List[Marker]] ) - None
+ self._markers = value
+
+ def __init__(self,
+ mailhost, # type: Tuple
+ fromaddr, # type: str
+ toaddrs, # type: Union[List[str], str]
+ subject, # type: Tuple
+ credentials=None, # type: Tuple
+ secure=None, # type: Optional[Tuple]
+ timeout=5.0, # type: Optional[float]
+ markers=None # type: Optional[Union[Marker, List[Marker]]]
+ ):
+
+ if is_above_python_3_2():
+ super(MarkerNotifyHandler, self). \
+ __init__( # noqa: E122
+ mailhost,
+ fromaddr,
+ toaddrs,
+ subject,
+ credentials,
+ secure,
+ timeout)
+
+ elif is_above_python_2_7():
+ super(MarkerNotifyHandler, self). \
+ __init__( # noqa: E122
+ mailhost,
+ fromaddr,
+ toaddrs,
+ subject,
+ credentials,
+ secure)
- def __init__(self, mailhost, fromaddr, toaddrs, subject,
- credentials=None, secure=None, timeout=5.0, markers=None):
-
- if sys.version_info > (3, 2):
- super(MarkerNotifyHandler, self).__init__(
- mailhost, fromaddr, toaddrs, subject,
- credentials, secure, timeout)
- elif sys.version_info > (2, 7):
- super(MarkerNotifyHandler, self).__init__(
- mailhost, fromaddr, toaddrs, subject,
- credentials, secure)
else:
SMTPHandler.__init__(self,
- mailhost, fromaddr, toaddrs, subject,
- credentials, secure)
+ mailhost,
+ fromaddr,
+ toaddrs,
+ subject,
+ credentials,
+ secure)
self.markers = markers
def handle(self, record):
+ # type: (LogRecord) -> bool
+ """
+ Handle a LogRecord record. Send an email notification.
+ """
+ return self.send_notification(record)
+
+ def send_notification(self, record):
+ # type: (LogRecord) -> bool
+ """Email notification handler.
- if self.markers is None:
+ Matches the record with the specific markers set for email
+ notifications. Sends an email notification if that marker(s) matched.
+
+ Args:
+ record (LogRecord): A record that might contain a marker.
+ Returns:
+ bool: Whether a record was passed for emission (to be sent).
+ """
+
+ if hasattr(self, "markers") and \
+ self.markers is None:
return False
- if matchMarkerHelp(record, self.markers):
- if sys.version_info > (2, 7):
+ if match_markers(record, self.markers):
+
+ if is_above_python_2_7():
return super(MarkerNotifyHandler, self).handle(record)
- else:
- return SMTPHandler.handle(self, record)
+ return SMTPHandler.handle(self, record)
return False