logging_utils.py 9.0 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
# 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

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

28 29
LOG_AllTransformer = 100

30 31 32 33 34 35 36 37 38

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

    return wrapper


39
class TranslatorLogger:
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
    """
    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
57
        self.logger_name = "Dynamic-to-Static"
58
        self._logger = log_helper.get_logger(
59 60
            self.logger_name,
            1,
61 62
            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
    def check_level(self, level):
119
        if isinstance(level, (int, type(None))):
120 121 122 123 124 125 126 127 128 129
            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):
0
0x45f 已提交
146 147 148 149
        if self.verbosity_level != -1:
            self.logger.warning(msg, *args, **kwargs)
            if self.need_to_echo_log_to_stdout:
                self._output_to_stdout('WARNING: ' + msg, *args)
150 151 152

    def log(self, level, msg, *args, **kwargs):
        if self.has_verbosity(level):
153 154 155 156
            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)
157

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

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

175 176 177 178 179 180 181
            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)

182 183 184 185

_TRANSLATOR_LOGGER = TranslatorLogger()


186
def set_verbosity(level=0, also_to_stdout=False):
187
    """
188 189
    Sets the verbosity level of log for dygraph to static graph. Logs can be output to stdout by setting `also_to_stdout`.

190
    There are two means to set the logging verbosity:
191 192 193 194 195

    1. Call function `set_verbosity`

    2. Set environment variable `TRANSLATOR_VERBOSITY`

196 197 198 199 200 201 202

    **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.
203
        also_to_stdout(bool): Whether to also output log messages to `sys.stdout`.
204 205 206 207 208 209 210 211 212 213 214 215 216 217

    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
218
    _TRANSLATOR_LOGGER.need_to_echo_log_to_stdout = also_to_stdout
219 220 221 222 223 224


def get_verbosity():
    return _TRANSLATOR_LOGGER.verbosity_level


225
def set_code_level(level=LOG_AllTransformer, also_to_stdout=False):
226
    """
227 228
    Sets the level to print code from specific level Ast Transformer. Code can be output to stdout by setting `also_to_stdout`.

229
    There are two means to set the code level:
230 231 232 233 234

    1. Call function `set_code_level`

    2. Set environment variable `TRANSLATOR_CODE_LEVEL`

235 236 237 238 239 240

    **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.
241
        also_to_stdout(bool): Whether to also output code to `sys.stdout`.
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256

    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
257
    _TRANSLATOR_LOGGER.need_to_echo_code_to_stdout = also_to_stdout
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276


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):
277 278 279
    _TRANSLATOR_LOGGER.log_transformed_code(
        level, ast_node, transformer_name, *args, **kwargs
    )