[IMP] float_utils: simplified code, added float_repr
[odoo/odoo.git] / openerp / loglevels.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 import sys
23 import logging
24 import warnings
25
26 LOG_NOTSET = 'notset'
27 LOG_DEBUG_SQL = 'debug_sql'
28 LOG_DEBUG_RPC_ANSWER = 'debug_rpc_answer'
29 LOG_DEBUG_RPC = 'debug_rpc'
30 LOG_DEBUG = 'debug'
31 LOG_TEST = 'test'
32 LOG_INFO = 'info'
33 LOG_WARNING = 'warn'
34 LOG_ERROR = 'error'
35 LOG_CRITICAL = 'critical'
36
37 logging.DEBUG_RPC_ANSWER = logging.DEBUG - 4
38 logging.addLevelName(logging.DEBUG_RPC_ANSWER, 'DEBUG_RPC_ANSWER')
39 logging.DEBUG_RPC = logging.DEBUG - 2
40 logging.addLevelName(logging.DEBUG_RPC, 'DEBUG_RPC')
41 logging.DEBUG_SQL = logging.DEBUG_RPC - 3
42 logging.addLevelName(logging.DEBUG_SQL, 'DEBUG_SQL')
43
44 logging.TEST = logging.INFO - 5
45 logging.addLevelName(logging.TEST, 'TEST')
46
47 class Logger(object):
48     def __init__(self):
49         warnings.warn("The netsvc.Logger API shouldn't be used anymore, please "
50                       "use the standard `logging.getLogger` API instead",
51                       PendingDeprecationWarning, stacklevel=2)
52         super(Logger, self).__init__()
53
54     def notifyChannel(self, name, level, msg):
55         warnings.warn("notifyChannel API shouldn't be used anymore, please use "
56                       "the standard `logging` module instead",
57                       PendingDeprecationWarning, stacklevel=2)
58         from service.web_services import common
59
60         log = logging.getLogger(ustr(name))
61
62         if level in [LOG_DEBUG_RPC, LOG_TEST] and not hasattr(log, level):
63             fct = lambda msg, *args, **kwargs: log.log(getattr(logging, level.upper()), msg, *args, **kwargs)
64             setattr(log, level, fct)
65
66
67         level_method = getattr(log, level)
68
69         if isinstance(msg, Exception):
70             msg = exception_to_unicode(msg)
71
72         try:
73             msg = ustr(msg).strip()
74             if level in (LOG_ERROR, LOG_CRITICAL): # and tools.config.get_misc('debug','env_info',False):
75                 msg = common().exp_get_server_environment() + "\n" + msg
76
77             result = msg.split('\n')
78         except UnicodeDecodeError:
79             result = msg.strip().split('\n')
80         try:
81             if len(result)>1:
82                 for idx, s in enumerate(result):
83                     level_method('[%02d]: %s' % (idx+1, s,))
84             elif result:
85                 level_method(result[0])
86         except IOError:
87             # TODO: perhaps reset the logger streams?
88             #if logrotate closes our files, we end up here..
89             pass
90         except Exception:
91             # better ignore the exception and carry on..
92             pass
93
94     def set_loglevel(self, level, logger=None):
95         if logger is not None:
96             log = logging.getLogger(str(logger))
97         else:
98             log = logging.getLogger()
99         log.setLevel(logging.INFO) # make sure next msg is printed
100         log.info("Log level changed to %s" % logging.getLevelName(level))
101         log.setLevel(level)
102
103     def shutdown(self):
104         logging.shutdown()
105
106 # TODO get_encodings, ustr and exception_to_unicode were originally from tools.misc.
107 # There are here until we refactor tools so that this module doesn't depends on tools.
108
109 def get_encodings(hint_encoding='utf-8'):
110     fallbacks = {
111         'latin1': 'latin9',
112         'iso-8859-1': 'iso8859-15',
113         'cp1252': '1252',
114     }
115     if hint_encoding:
116         yield hint_encoding
117         if hint_encoding.lower() in fallbacks:
118             yield fallbacks[hint_encoding.lower()]
119
120     # some defaults (also taking care of pure ASCII)
121     for charset in ['utf8','latin1']:
122         if not (hint_encoding) or (charset.lower() != hint_encoding.lower()):
123             yield charset
124
125     from locale import getpreferredencoding
126     prefenc = getpreferredencoding()
127     if prefenc and prefenc.lower() != 'utf-8':
128         yield prefenc
129         prefenc = fallbacks.get(prefenc.lower())
130         if prefenc:
131             yield prefenc
132
133 def ustr(value, hint_encoding='utf-8'):
134     """This method is similar to the builtin `str` method, except
135        it will return unicode() string.
136
137     @param value: the value to convert
138     @param hint_encoding: an optional encoding that was detected
139                           upstream and should be tried first to
140                           decode ``value``.
141
142     @rtype: unicode
143     @return: unicode string
144     """
145     if isinstance(value, Exception):
146         return exception_to_unicode(value)
147
148     if isinstance(value, unicode):
149         return value
150
151     if not isinstance(value, basestring):
152         try:
153             return unicode(value)
154         except Exception:
155             raise UnicodeError('unable to convert %r' % (value,))
156
157     for ln in get_encodings(hint_encoding):
158         try:
159             return unicode(value, ln)
160         except Exception:
161             pass
162     raise UnicodeError('unable to convert %r' % (value,))
163
164
165 def exception_to_unicode(e):
166     if (sys.version_info[:2] < (2,6)) and hasattr(e, 'message'):
167         return ustr(e.message)
168     if hasattr(e, 'args'):
169         return "\n".join((ustr(a) for a in e.args))
170     try:
171         return unicode(e)
172     except Exception:
173         return u"Unknown message"
174
175 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: