From a350ca2b4995df6d75c9021dd537589896dccff6 Mon Sep 17 00:00:00 2001 From: liangke Date: Thu, 6 Sep 2018 15:37:38 +0800 Subject: Support Marker in python logging 1 Add 'Marker' feature which carry extra contextual information for loggers, can be used to mark a single log statement 2 Make MDC Formatter have "marker" label. Change-Id: I21ac547b97c49190393d57ff290dcbc3ae20da2a Issue-ID: MULTICLOUD-328 Signed-off-by: liangke --- pylog/onaplogging/__init__.py | 8 +- pylog/onaplogging/logWatchDog.py | 9 +- pylog/onaplogging/marker/__init__.py | 27 ++++++ pylog/onaplogging/marker/marker.py | 142 ++++++++++++++++++++++++++++++ pylog/onaplogging/marker/markerFactory.py | 74 ++++++++++++++++ pylog/onaplogging/marker/markerFilter.py | 32 +++++++ pylog/onaplogging/marker/markerHandler.py | 51 +++++++++++ pylog/onaplogging/markerFormatter.py | 72 +++++++++++++++ pylog/onaplogging/markerLogAdaptor.py | 85 ++++++++++++++++++ pylog/onaplogging/mdcContext.py | 30 +++++-- pylog/onaplogging/mdcformatter.py | 43 +++++---- pylog/onaplogging/monkey.py | 9 +- 12 files changed, 540 insertions(+), 42 deletions(-) create mode 100644 pylog/onaplogging/marker/__init__.py create mode 100644 pylog/onaplogging/marker/marker.py create mode 100644 pylog/onaplogging/marker/markerFactory.py create mode 100644 pylog/onaplogging/marker/markerFilter.py create mode 100644 pylog/onaplogging/marker/markerHandler.py create mode 100644 pylog/onaplogging/markerFormatter.py create mode 100644 pylog/onaplogging/markerLogAdaptor.py (limited to 'pylog/onaplogging') diff --git a/pylog/onaplogging/__init__.py b/pylog/onaplogging/__init__.py index 1f2f9d9..204d7d1 100644 --- a/pylog/onaplogging/__init__.py +++ b/pylog/onaplogging/__init__.py @@ -1,11 +1,13 @@ -# Copyright (c) 2018 VMware, Inc. +# Copyright 2018 ke liang . # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: +# You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/pylog/onaplogging/logWatchDog.py b/pylog/onaplogging/logWatchDog.py index 154dc41..f93dd12 100644 --- a/pylog/onaplogging/logWatchDog.py +++ b/pylog/onaplogging/logWatchDog.py @@ -1,14 +1,16 @@ -# Copyright (c) 2018 VMware, Inc. +# Copyright 2018 ke liang . # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: +# You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import os import yaml @@ -89,7 +91,6 @@ def _yamlConfig(filepath=None, watchDog=None): def patch_loggingYaml(): - # The patch to add yam config forlogginf and runtime # reload logging when modify yaml file config.yamlConfig = _yamlConfig diff --git a/pylog/onaplogging/marker/__init__.py b/pylog/onaplogging/marker/__init__.py new file mode 100644 index 0000000..a1b3d89 --- /dev/null +++ b/pylog/onaplogging/marker/__init__.py @@ -0,0 +1,27 @@ +# Copyright 2018 ke liang . +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import + +from marker.marker import ( + Marker, MARKER_TAG, + BaseMarker, matchMarkerHelp) + +from marker.markerFactory import MarkerFactory +from marker.markerHandler import MarkerNotifyHandler +from marker.markerFilter import MarkerFilter + +__all__ = ("Marker", "MARKER_TAG", "BaseMarker", + "matchMarkerHelp", "MarkerFactory", + "MarkerNotifyHandler", "MarkerFilter") diff --git a/pylog/onaplogging/marker/marker.py b/pylog/onaplogging/marker/marker.py new file mode 100644 index 0000000..5414e21 --- /dev/null +++ b/pylog/onaplogging/marker/marker.py @@ -0,0 +1,142 @@ +# Copyright 2018 ke liang . +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import abc + +MARKER_TAG = "marker" + + +class Marker(object): + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def getName(self): + raise NotImplementedError() + + @abc.abstractmethod + def contains(self, item=None): + raise NotImplementedError() + + @abc.abstractmethod + def addChild(self, item): + raise NotImplementedError() + + @abc.abstractmethod + def removeChild(self, item): + raise NotImplementedError() + + @abc.abstractmethod + def __eq__(self, other): + raise NotImplementedError() + + @abc.abstractmethod + def __hash__(self): + raise NotImplementedError() + + @abc.abstractmethod + def __iter__(self): + raise NotImplementedError() + + +class BaseMarker(Marker): + + def __init__(self, name): + super(BaseMarker, self).__init__() + if not isinstance(name, str): + raise TypeError("not str type") + if name == "": + raise ValueError("empty value") + self.__name = name + self.__childs = [] + + def getName(self): + return self.__name + + def __iter__(self): + return iter(self.__childs) + + def __eq__(self, other): + + if not isinstance(other, Marker): + return False + if id(self) == id(other): + return True + + return self.__name == other.getName() + + def __hash__(self): + return hash(self.__name) + + def contains(self, item=None): + + if isinstance(item, Marker): + if item == self: + return True + return len(list(filter( + lambda x: x == item, self.__childs))) > 0 + + elif isinstance(item, str): + if item == self.__name: + return True + + return len(list(filter( + lambda x: x.__name == item, self.__childs))) > 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 addChilds(self, childs): + try: + iter(childs) + except Exception as e: + raise e + + for item in childs: + self.addChild(item) + + 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) + + +def matchMarkerHelp(record, markerToMatch): + + marker = getattr(record, MARKER_TAG, None) + + if marker is None or markerToMatch is None: + return False + + if not isinstance(marker, Marker): + return False + + try: + if isinstance(markerToMatch, list): + return len(list(filter( + lambda x: marker.contains(x), markerToMatch))) > 0 + + return marker.contains(markerToMatch) + except Exception as e: + raise e diff --git a/pylog/onaplogging/marker/markerFactory.py b/pylog/onaplogging/marker/markerFactory.py new file mode 100644 index 0000000..deb9566 --- /dev/null +++ b/pylog/onaplogging/marker/markerFactory.py @@ -0,0 +1,74 @@ +# Copyright 2018 ke liang . +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import abc +import threading +from marker import BaseMarker + +lock = threading.RLock() + + +class IMarkerFactory(object): + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def getMarker(self, marker_name=None): + raise NotImplementedError() + + @abc.abstractmethod + def deleteMarker(self, marker_name=None): + raise NotImplementedError() + + @abc.abstractmethod + def exist(self, marker_name=None): + raise NotImplementedError() + + +class MarkerFactory(IMarkerFactory): + + _instance = None + _marker_map = {} + + def __new__(cls, *args, **kwargs): + + if cls._instance is None: + cls._instance = super(MarkerFactory, cls).__new__(cls) + + return cls._instance + + def getMarker(self, marker_name=None): + if marker_name is None: + raise ValueError("not empty") + + marker = self._marker_map.get(marker_name, None) + if marker is None: + lock.acquire() + marker = BaseMarker(name=marker_name) + self._marker_map[marker_name] = marker + lock.release() + + return marker + + def deleteMarker(self, marker_name=None): + lock.acquire() + if self.exist(marker_name): + del self._marker_map[marker_name] + return True + lock.release() + return False + + def exist(self, marker_name=None): + + return self._marker_map.get( + marker_name, None) is not None diff --git a/pylog/onaplogging/marker/markerFilter.py b/pylog/onaplogging/marker/markerFilter.py new file mode 100644 index 0000000..4ae5478 --- /dev/null +++ b/pylog/onaplogging/marker/markerFilter.py @@ -0,0 +1,32 @@ +# Copyright 2018 ke liang . +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys +from logging import Filter +from marker import matchMarkerHelp + + +class MarkerFilter(Filter): + + def __init__(self, name="", markers=None): + if sys.version_info > (2, 7): + super(MarkerFilter, self).__init__(name) + else: + Filter.__init__(self, name) + + self.markerToMatch = markers + + def filter(self, record): + # compare filter's markers with record's marker + return matchMarkerHelp(record, self.markerToMatch) diff --git a/pylog/onaplogging/marker/markerHandler.py b/pylog/onaplogging/marker/markerHandler.py new file mode 100644 index 0000000..ca5f483 --- /dev/null +++ b/pylog/onaplogging/marker/markerHandler.py @@ -0,0 +1,51 @@ +# Copyright 2018 ke liang . +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys +from logging.handlers import SMTPHandler +from marker import matchMarkerHelp + + +class MarkerNotifyHandler(SMTPHandler): + + 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) + + self.markers = markers + + def handle(self, record): + + if self.markers is None: + return False + + if matchMarkerHelp(record, self.markers): + if sys.version_info > (2, 7): + return super(SMTPHandler, self).handle(record) + else: + return SMTPHandler.handle(self, record) + + return False diff --git a/pylog/onaplogging/markerFormatter.py b/pylog/onaplogging/markerFormatter.py new file mode 100644 index 0000000..f33bfc2 --- /dev/null +++ b/pylog/onaplogging/markerFormatter.py @@ -0,0 +1,72 @@ +# Copyright 2018 ke liang . +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys +import logging +from marker import MARKER_TAG +from marker import Marker + + +class MarkerFormatter(logging.Formatter): + + def __init__(self, fmt=None, datefmt=None, style='%'): + + if sys.version_info > (3, 2): + super(MarkerFormatter, self).__init__( + fmt=fmt, datefmt=datefmt, style=style) + elif sys.version_info > (2, 7): + super(MarkerFormatter, self).__init__( + fmt=fmt, datefmt=datefmt) + else: + logging.Formatter.__init__(self, fmt, datefmt) + + self.style = style + self._marker_tag = "%(marker)s" + + if sys.version_info > (3, 2): + if self.style not in logging._STYLES: + raise ValueError('Style must be one of: %s' % + ','.join(logging._STYLES.keys())) + if self.style == "{": + self._marker_tag = "{marker}" + elif self.style == "$": + self._marker_tag = "${marker}" + + self._tmpFmt = self._fmt + + def format(self, record): + + try: + if self._fmt.find(self._marker_tag) != -1 \ + and hasattr(record, MARKER_TAG): + marker = getattr(record, MARKER_TAG) + + if isinstance(marker, Marker): + self._fmt = self._fmt.replace( + self._marker_tag, marker.getName()) + elif self._fmt.find(self._marker_tag) != -1 \ + and not hasattr(record, MARKER_TAG): + + self._fmt = self._fmt.replace(self._marker_tag, "") + + if sys.version_info > (3, 2): + self._style = logging._STYLES[self.style][0](self._fmt) + + if sys.version_info > (2, 7): + return super(MarkerFormatter, self).format(record) + else: + return logging.Formatter.format(self, record) + + finally: + self._fmt = self._tmpFmt diff --git a/pylog/onaplogging/markerLogAdaptor.py b/pylog/onaplogging/markerLogAdaptor.py new file mode 100644 index 0000000..d787f2b --- /dev/null +++ b/pylog/onaplogging/markerLogAdaptor.py @@ -0,0 +1,85 @@ +# Copyright 2018 ke liang . +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys +from logging import LoggerAdapter +from threading import RLock +from functools import wraps +from marker import MARKER_TAG +from marker import Marker +from mdcContext import _getmdcs + +lock = RLock() + + +def addMarker(func): + + @wraps(func) + def wrapper(self, marker, msg, *args, **kwargs): + lock.acquire() + if not isinstance(marker, Marker): + raise TypeError("not marker type %s" + % type(marker)) + + if self.extra and MARKER_TAG in self.extra: + raise Exception("cann't add 'marker' in extra") + setattr(self.logger, MARKER_TAG, marker) + func(self, marker, msg, *args, **kwargs) + if hasattr(self.logger, MARKER_TAG): + delattr(self.logger, MARKER_TAG) + lock.release() + return wrapper + + +class MarkerLogAdaptor(LoggerAdapter): + + def process(self, msg, kwargs): + + if sys.version_info > (3, 2): + kwargs['extra'] = _getmdcs(self.extra) + else: + kwargs['extra'] = self.extra + return msg, kwargs + + @addMarker + def infoMarker(self, marker, msg, *args, **kwargs): + + self.info(msg, *args, **kwargs) + + @addMarker + def debugMarker(self, marker, msg, *args, **kwargs): + + self.debug(msg, *args, **kwargs) + + @addMarker + def warningMarker(self, marker, msg, *args, **kwargs): + + self.warning(msg, *args, **kwargs) + + @addMarker + def errorMarker(self, marker, msg, *args, **kwargs): + + self.error(msg, *args, **kwargs) + + @addMarker + def exceptionMarker(self, marker, msg, *arg, **kwargs): + self.exception(msg, *arg, **kwargs) + + @addMarker + def criticalMarker(self, marker, msg, *arg, **kwargs): + self.critical(msg, *arg, **kwargs) + + @addMarker + def logMarker(self, marker, level, msg, *arg, **kwargs): + self.log(level, msg, *arg, **kwargs) diff --git a/pylog/onaplogging/mdcContext.py b/pylog/onaplogging/mdcContext.py index 60075b4..4fdc99c 100644 --- a/pylog/onaplogging/mdcContext.py +++ b/pylog/onaplogging/mdcContext.py @@ -1,15 +1,16 @@ -# Copyright (c) 2018 VMware, Inc. +# Copyright 2018 ke liang . # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: +# You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - +# See the License for the specific language governing permissions and +# limitations under the License. import logging import threading @@ -18,12 +19,14 @@ import os import traceback import sys import functools - +from marker import Marker +from marker import MARKER_TAG __all__ = ['patch_loggingMDC', 'MDC'] _replace_func_name = ['info', 'critical', 'fatal', 'debug', - 'error', 'warn', 'warning', 'log', 'findCaller'] + 'error', 'warn', 'warning', 'log', + 'handle', 'findCaller'] class MDCContext(threading.local): @@ -80,7 +83,7 @@ def _getmdcs(extra=None): :return: mdc dict """ if MDC.isEmpty(): - return + return extra mdc = MDC.result() @@ -93,6 +96,7 @@ def _getmdcs(extra=None): extra = {} extra['mdc'] = mdc + del mdc return extra @@ -106,7 +110,6 @@ def info(self, msg, *args, **kwargs): @fetchkeys def debug(self, msg, *args, **kwargs): - if self.isEnabledFor(logging.DEBUG): self._log(logging.DEBUG, msg, args, **kwargs) @@ -150,6 +153,17 @@ def log(self, level, msg, *args, **kwargs): self._log(level, msg, args, **kwargs) +def handle(self, record): + + cmarker = getattr(self, MARKER_TAG, None) + + if isinstance(cmarker, Marker): + setattr(record, MARKER_TAG, cmarker) + + if (not self.disabled) and self.filter(record): + self.callHandlers(record) + + def findCaller(self, stack_info=False): f = logging.currentframe() diff --git a/pylog/onaplogging/mdcformatter.py b/pylog/onaplogging/mdcformatter.py index 01056a4..087497d 100644 --- a/pylog/onaplogging/mdcformatter.py +++ b/pylog/onaplogging/mdcformatter.py @@ -1,19 +1,23 @@ -# Copyright (c) 2018 VMware, Inc. +# Copyright 2018 ke liang . # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: +# You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + import sys import logging +from markerFormatter import MarkerFormatter -class MDCFormatter(logging.Formatter): +class MDCFormatter(MarkerFormatter): """ A custom MDC formatter to prepare Mapped Diagnostic Context to enrich log message. @@ -33,7 +37,7 @@ class MDCFormatter(logging.Formatter): elif sys.version_info > (2, 7): super(MDCFormatter, self).__init__(fmt=fmt, datefmt=datefmt) else: - logging.Formatter.__init__(self, fmt, datefmt) + MarkerFormatter.__init__(self, fmt, datefmt) self.style = style self._mdc_tag = "%(mdc)s" @@ -46,7 +50,6 @@ class MDCFormatter(logging.Formatter): elif self.style == "$": self._mdc_tag = "${mdc}" - self._tmpfmt = self._fmt if mdcfmt: self._mdcFmt = mdcfmt else: @@ -111,20 +114,19 @@ class MDCFormatter(logging.Formatter): if sys.version_info > (2, 7): return super(MDCFormatter, self).format(record) else: - return logging.Formatter.format(self, record) + return MarkerFormatter.format(self, record) mdcFmtkeys, mdcFmtWords = self._mdcfmtKey() if mdcFmtWords is None: + self._fmt = self._fmt.replace(self._mdc_tag, "") if sys.version_info > (3, 2): - self._style = logging._STYLES[self.style][0]( - self._fmt.replace(self._mdc_tag, "")) - else: - self._fmt = self._fmt.replace(self._mdc_tag, "") + self._style = logging._STYLES[self.style][0](self._fmt) + if sys.version_info > (2, 7): return super(MDCFormatter, self).format(record) else: - return logging.Formatter.format(self, record) + return MarkerFormatter.format(self, record) mdc = record.__dict__.get('mdc', None) res = {} @@ -137,22 +139,17 @@ class MDCFormatter(logging.Formatter): del mdc try: mdcstr = self._replaceStr(keys=mdcFmtkeys).format(**res) + self._fmt = self._fmt.replace(self._mdc_tag, mdcstr) + if sys.version_info > (3, 2): - self._style = logging._STYLES[self.style][0]( - self._fmt.replace(self._mdc_tag, mdcstr)) - else: - self._fmt = self._fmt.replace(self._mdc_tag, mdcstr) + self._style = logging._STYLES[self.style][0](self._fmt) + if sys.version_info > (2, 7): - s = super(MDCFormatter, self).format(record) + return super(MDCFormatter, self).format(record) else: - s = logging.Formatter.format(self, record) - return s + return MarkerFormatter.format(self, record) except KeyError as e: print("The mdc key %s format is wrong" % str(e)) except Exception: raise - - finally: - # reset fmt format - self._fmt = self._tmpfmt diff --git a/pylog/onaplogging/monkey.py b/pylog/onaplogging/monkey.py index fcf8fdf..00659c7 100644 --- a/pylog/onaplogging/monkey.py +++ b/pylog/onaplogging/monkey.py @@ -1,15 +1,16 @@ -# Copyright (c) 2018 VMware, Inc. +# Copyright 2018 ke liang . # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: +# You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - +# See the License for the specific language governing permissions and +# limitations under the License. from mdcContext import patch_loggingMDC from logWatchDog import patch_loggingYaml -- cgit 1.2.3-korg