1# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
15"""Log helper functions."""
16
17import functools
18import inspect
19import logging
20
21
22def _get_full_class_name(cls):
23    return '%s.%s' % (cls.__module__,
24                      getattr(cls, '__qualname__', cls.__name__))
25
26
27def _is_method(obj, method):
28    """Returns True if a given method is obj's method.
29
30    You can not simply test a given method like:
31
32    return inspect.ismethod(method)
33
34    This is because functools.wraps converts the method to a function
35    in log_method_call function.
36    """
37    return inspect.ismethod(getattr(obj, method.__name__, None))
38
39
40def log_method_call(method):
41    """Decorator helping to log method calls.
42
43    :param method: Method to decorate to be logged.
44    :type method: method definition
45    """
46    log = logging.getLogger(method.__module__)
47
48    @functools.wraps(method)
49    def wrapper(*args, **kwargs):
50        args_start_pos = 0
51        if args:
52            first_arg = args[0]
53            if _is_method(first_arg, method):
54                cls = (first_arg if isinstance(first_arg, type)
55                       else first_arg.__class__)
56                caller = _get_full_class_name(cls)
57                args_start_pos = 1
58            else:
59                caller = 'static'
60        else:
61            caller = 'static'
62        data = {'caller': caller,
63                'method_name': method.__name__,
64                'args': args[args_start_pos:], 'kwargs': kwargs}
65        log.debug('%(caller)s method %(method_name)s '
66                  'called with arguments %(args)s %(kwargs)s', data)
67        return method(*args, **kwargs)
68    return wrapper
69