logging_utils.py 8.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
#
# 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 os
import threading

import six
from paddle.fluid import log_helper
from paddle.fluid.dygraph.dygraph_to_static.utils import ast_to_source_code

__all__ = ["TranslatorLogger", "set_verbosity", "set_code_level"]

VERBOSITY_ENV_NAME = 'TRANSLATOR_VERBOSITY'
CODE_LEVEL_ENV_NAME = 'TRANSLATOR_CODE_LEVEL'
DEFAULT_VERBOSITY = -1
DEFAULT_CODE_LEVEL = -1

29 30
LOG_AllTransformer = 100

31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57

def synchronized(func):
    def wrapper(*args, **kwargs):
        with threading.Lock():
            return func(*args, **kwargs)

    return wrapper


class TranslatorLogger(object):
    """
    class for Logging and debugging during the tranformation from dygraph to static graph.
    The object of this class is a singleton.
    """

    @synchronized
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):
            cls._instance = object.__new__(cls, *args, **kwargs)
            cls._instance._initialized = False
        return cls._instance

    def __init__(self):
        if self._initialized:
            return

        self._initialized = True
58
        self.logger_name = "Dynamic-to-Static"
59
        self._logger = log_helper.get_logger(
60 61 62
            self.logger_name,
            1,
            fmt='%(asctime)s %(name)s %(levelname)s: %(message)s')
63 64
        self._verbosity_level = None
        self._transformed_code_level = None
65 66
        self._need_to_echo_log_to_stdout = None
        self._need_to_echo_code_to_stdout = None
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95

    @property
    def logger(self):
        return self._logger

    @property
    def verbosity_level(self):
        if self._verbosity_level is not None:
            return self._verbosity_level
        else:
            return int(os.getenv(VERBOSITY_ENV_NAME, DEFAULT_VERBOSITY))

    @verbosity_level.setter
    def verbosity_level(self, level):
        self.check_level(level)
        self._verbosity_level = level

    @property
    def transformed_code_level(self):
        if self._transformed_code_level is not None:
            return self._transformed_code_level
        else:
            return int(os.getenv(CODE_LEVEL_ENV_NAME, DEFAULT_CODE_LEVEL))

    @transformed_code_level.setter
    def transformed_code_level(self, level):
        self.check_level(level)
        self._transformed_code_level = level

96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
    @property
    def need_to_echo_log_to_stdout(self):
        if self._need_to_echo_log_to_stdout is not None:
            return self._need_to_echo_log_to_stdout
        return False

    @need_to_echo_log_to_stdout.setter
    def need_to_echo_log_to_stdout(self, log_to_stdout):
        assert isinstance(log_to_stdout, (bool, type(None)))
        self._need_to_echo_log_to_stdout = log_to_stdout

    @property
    def need_to_echo_code_to_stdout(self):
        if self._need_to_echo_code_to_stdout is not None:
            return self._need_to_echo_code_to_stdout
        return False

    @need_to_echo_code_to_stdout.setter
    def need_to_echo_code_to_stdout(self, code_to_stdout):
        assert isinstance(code_to_stdout, (bool, type(None)))
        self._need_to_echo_code_to_stdout = code_to_stdout

118 119 120 121 122 123 124 125 126 127 128 129
    def check_level(self, level):
        if isinstance(level, (six.integer_types, type(None))):
            rv = level
        else:
            raise TypeError("Level is not an integer: {}".format(level))
        return rv

    def has_code_level(self, level):
        level = self.check_level(level)
        return level == self.transformed_code_level

    def has_verbosity(self, level):
130 131 132 133 134 135 136
        """
        Checks whether the verbosity level set by the user is greater than or equal to the log level.
        Args:
            level(int): The level of log.
        Returns:
            True if the verbosity level set by the user is greater than or equal to the log level, otherwise False.
        """
137
        level = self.check_level(level)
138
        return self.verbosity_level >= level
139 140 141

    def error(self, msg, *args, **kwargs):
        self.logger.error(msg, *args, **kwargs)
142 143
        if self.need_to_echo_log_to_stdout:
            self._output_to_stdout('ERROR: ' + msg, *args)
144 145

    def warn(self, msg, *args, **kwargs):
146 147 148
        self.logger.warning(msg, *args, **kwargs)
        if self.need_to_echo_log_to_stdout:
            self._output_to_stdout('WARNING: ' + msg, *args)
149 150 151

    def log(self, level, msg, *args, **kwargs):
        if self.has_verbosity(level):
152 153 154 155
            msg_with_level = '(Level {}) {}'.format(level, msg)
            self.logger.info(msg_with_level, *args, **kwargs)
            if self.need_to_echo_log_to_stdout:
                self._output_to_stdout('INFO: ' + msg_with_level, *args)
156 157 158 159 160

    def log_transformed_code(self, level, ast_node, transformer_name, *args,
                             **kwargs):
        if self.has_code_level(level):
            source_code = ast_to_source_code(ast_node)
161 162 163 164 165 166
            if level == LOG_AllTransformer:
                header_msg = "After the last level ast transformer: '{}', the transformed code:\n" \
                    .format(transformer_name)
            else:
                header_msg = "After the level {} ast transformer: '{}', the transformed code:\n"\
                    .format(level, transformer_name)
167 168 169 170

            msg = header_msg + source_code
            self.logger.info(msg, *args, **kwargs)

171 172 173 174 175 176 177
            if self.need_to_echo_code_to_stdout:
                self._output_to_stdout('INFO: ' + msg, *args)

    def _output_to_stdout(self, msg, *args):
        msg = self.logger_name + ' ' + msg
        print(msg % args)

178 179 180 181

_TRANSLATOR_LOGGER = TranslatorLogger()


182
def set_verbosity(level=0, also_to_stdout=False):
183
    """
184 185
    Sets the verbosity level of log for dygraph to static graph. Logs can be output to stdout by setting `also_to_stdout`.

186
    There are two means to set the logging verbosity:
187 188 189 190 191

    1. Call function `set_verbosity`

    2. Set environment variable `TRANSLATOR_VERBOSITY`

192 193 194 195 196 197 198

    **Note**:
    `set_verbosity` has a higher priority than the environment variable.

    Args:
        level(int): The verbosity level. The larger value idicates more verbosity.
            The default value is 0, which means no logging.
199
        also_to_stdout(bool): Whether to also output log messages to `sys.stdout`.
200 201 202 203 204 205 206 207 208 209 210 211 212 213

    Examples:
        .. code-block:: python

            import os
            import paddle

            paddle.jit.set_verbosity(1)
            # The verbosity level is now 1

            os.environ['TRANSLATOR_VERBOSITY'] = '3'
            # The verbosity level is now 3, but it has no effect because it has a lower priority than `set_verbosity`
    """
    _TRANSLATOR_LOGGER.verbosity_level = level
214
    _TRANSLATOR_LOGGER.need_to_echo_log_to_stdout = also_to_stdout
215 216 217 218 219 220


def get_verbosity():
    return _TRANSLATOR_LOGGER.verbosity_level


221
def set_code_level(level=LOG_AllTransformer, also_to_stdout=False):
222
    """
223 224
    Sets the level to print code from specific level Ast Transformer. Code can be output to stdout by setting `also_to_stdout`.

225
    There are two means to set the code level:
226 227 228 229 230

    1. Call function `set_code_level`

    2. Set environment variable `TRANSLATOR_CODE_LEVEL`

231 232 233 234 235 236

    **Note**:
    `set_code_level` has a higher priority than the environment variable.

    Args:
        level(int): The level to print code. Default is 100, which means to print the code after all AST Transformers.
237
        also_to_stdout(bool): Whether to also output code to `sys.stdout`.
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252

    Examples:
        .. code-block:: python

            import paddle

            paddle.jit.set_code_level(2)
            # It will print the transformed code at level 2, which means to print the code after second transformer,
            # as the date of August 28, 2020, it is CastTransformer.

            os.environ['TRANSLATOR_CODE_LEVEL'] = '3'
            # The code level is now 3, but it has no effect because it has a lower priority than `set_code_level`

    """
    _TRANSLATOR_LOGGER.transformed_code_level = level
253
    _TRANSLATOR_LOGGER.need_to_echo_code_to_stdout = also_to_stdout
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274


def get_code_level():
    return _TRANSLATOR_LOGGER.transformed_code_level


def error(msg, *args, **kwargs):
    _TRANSLATOR_LOGGER.error(msg, *args, **kwargs)


def warn(msg, *args, **kwargs):
    _TRANSLATOR_LOGGER.warn(msg, *args, **kwargs)


def log(level, msg, *args, **kwargs):
    _TRANSLATOR_LOGGER.log(level, msg, *args, **kwargs)


def log_transformed_code(level, ast_node, transformer_name, *args, **kwargs):
    _TRANSLATOR_LOGGER.log_transformed_code(level, ast_node, transformer_name,
                                            *args, **kwargs)