Added basic scaffolding for webclient module
[odoo/odoo.git] / openerp / netsvc.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2014 OpenERP SA (<http://www.openerp.com>)
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 logging
23 import logging.handlers
24 import os
25 import pprint
26 import release
27 import sys
28 import threading
29
30 import psycopg2
31
32 import openerp
33 import sql_db
34 import tools
35
36 _logger = logging.getLogger(__name__)
37
38 def log(logger, level, prefix, msg, depth=None):
39     indent=''
40     indent_after=' '*len(prefix)
41     for line in (prefix + pprint.pformat(msg, depth=depth)).split('\n'):
42         logger.log(level, indent+line)
43         indent=indent_after
44
45 def LocalService(name):
46     """
47     The openerp.netsvc.LocalService() function is deprecated. It still works
48     in two cases: workflows and reports. For workflows, instead of using
49     LocalService('workflow'), openerp.workflow should be used (better yet,
50     methods on openerp.osv.orm.Model should be used). For reports,
51     openerp.report.render_report() should be used (methods on the Model should
52     be provided too in the future).
53     """
54     assert openerp.conf.deprecation.allow_local_service
55     _logger.warning("LocalService() is deprecated since march 2013 (it was called with '%s')." % name)
56
57     if name == 'workflow':
58         return openerp.workflow
59
60     if name.startswith('report.'):
61         report = openerp.report.interface.report_int._reports.get(name)
62         if report:
63             return report
64         else:
65             dbname = getattr(threading.currentThread(), 'dbname', None)
66             if dbname:
67                 registry = openerp.modules.registry.RegistryManager.get(dbname)
68                 with registry.cursor() as cr:
69                     return registry['ir.actions.report.xml']._lookup_report(cr, name[len('report.'):])
70
71 class PostgreSQLHandler(logging.Handler):
72     """ PostgreSQL Loggin Handler will store logs in the database, by default
73     the current database, can be set using --log-db=DBNAME
74     """
75     def emit(self, record):
76         ct = threading.current_thread()
77         ct_db = getattr(ct, 'dbname', None)
78         ct_uid = getattr(ct, 'uid', None)
79         dbname = tools.config['log_db'] or ct_db
80         if not dbname:
81             return
82         with tools.ignore(Exception), sql_db.db_connect(dbname).cursor() as cr:
83             msg = tools.ustr(record.msg)
84             if record.args:
85                 msg = msg % record.args
86             traceback = getattr(record, 'exc_text', '')
87             if traceback:
88                 msg = "%s\n%s" % (msg, traceback)
89             # we do not use record.levelname because it may have been changed by ColoredFormatter.
90             levelname = logging.getLevelName(record.levelno)
91             val = (ct_uid, ct_uid, 'server', ct_db, record.name, levelname, msg, record.pathname, record.lineno, record.funcName)
92             cr.execute("""
93                 INSERT INTO ir_logging(create_date, write_date, create_uid, write_uid, type, dbname, name, level, message, path, line, func)
94                 VALUES (NOW() at time zone 'UTC', NOW() at time zone 'UTC', %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
95             """, val)
96
97 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, _NOTHING, DEFAULT = range(10)
98 #The background is set with 40 plus the number of the color, and the foreground with 30
99 #These are the sequences need to get colored ouput
100 RESET_SEQ = "\033[0m"
101 COLOR_SEQ = "\033[1;%dm"
102 BOLD_SEQ = "\033[1m"
103 COLOR_PATTERN = "%s%s%%s%s" % (COLOR_SEQ, COLOR_SEQ, RESET_SEQ)
104 LEVEL_COLOR_MAPPING = {
105     logging.DEBUG: (BLUE, DEFAULT),
106     logging.INFO: (GREEN, DEFAULT),
107     logging.WARNING: (YELLOW, DEFAULT),
108     logging.ERROR: (RED, DEFAULT),
109     logging.CRITICAL: (WHITE, RED),
110 }
111
112 class DBFormatter(logging.Formatter):
113     def format(self, record):
114         record.pid = os.getpid()
115         record.dbname = getattr(threading.currentThread(), 'dbname', '?')
116         return logging.Formatter.format(self, record)
117
118 class ColoredFormatter(DBFormatter):
119     def format(self, record):
120         fg_color, bg_color = LEVEL_COLOR_MAPPING[record.levelno]
121         record.levelname = COLOR_PATTERN % (30 + fg_color, 40 + bg_color, record.levelname)
122         return DBFormatter.format(self, record)
123
124 _logger_init = False
125 def init_logger():
126     global _logger_init
127     if _logger_init:
128         return
129     _logger_init = True
130
131     from tools.translate import resetlocale
132     resetlocale()
133
134     # create a format for log messages and dates
135     format = '%(asctime)s %(pid)s %(levelname)s %(dbname)s %(name)s: %(message)s'
136
137     if tools.config['syslog']:
138         # SysLog Handler
139         if os.name == 'nt':
140             handler = logging.handlers.NTEventLogHandler("%s %s" % (release.description, release.version))
141         else:
142             handler = logging.handlers.SysLogHandler()
143         format = '%s %s' % (release.description, release.version) \
144                 + ':%(dbname)s:%(levelname)s:%(name)s:%(message)s'
145
146     elif tools.config['logfile']:
147         # LogFile Handler
148         logf = tools.config['logfile']
149         try:
150             # We check we have the right location for the log files
151             dirname = os.path.dirname(logf)
152             if dirname and not os.path.isdir(dirname):
153                 os.makedirs(dirname)
154             if tools.config['logrotate'] is not False:
155                 handler = logging.handlers.TimedRotatingFileHandler(filename=logf, when='D', interval=1, backupCount=30)
156             elif os.name == 'posix':
157                 handler = logging.handlers.WatchedFileHandler(logf)
158             else:
159                 handler = logging.handlers.FileHandler(logf)
160         except Exception:
161             sys.stderr.write("ERROR: couldn't create the logfile directory. Logging to the standard output.\n")
162             handler = logging.StreamHandler(sys.stdout)
163     else:
164         # Normal Handler on standard output
165         handler = logging.StreamHandler(sys.stdout)
166
167     # Check that handler.stream has a fileno() method: when running OpenERP
168     # behind Apache with mod_wsgi, handler.stream will have type mod_wsgi.Log,
169     # which has no fileno() method. (mod_wsgi.Log is what is being bound to
170     # sys.stderr when the logging.StreamHandler is being constructed above.)
171     def is_a_tty(stream):
172         return hasattr(stream, 'fileno') and os.isatty(stream.fileno())
173
174     if isinstance(handler, logging.StreamHandler) and is_a_tty(handler.stream):
175         formatter = ColoredFormatter(format)
176     else:
177         formatter = DBFormatter(format)
178     handler.setFormatter(formatter)
179
180     logging.getLogger().addHandler(handler)
181
182     if tools.config['log_db']:
183         postgresqlHandler = PostgreSQLHandler()
184         postgresqlHandler.setLevel(logging.WARNING)
185         logging.getLogger().addHandler(postgresqlHandler)
186
187     # Configure loggers levels
188     pseudo_config = PSEUDOCONFIG_MAPPER.get(tools.config['log_level'], [])
189
190     logconfig = tools.config['log_handler']
191
192     logging_configurations = DEFAULT_LOG_CONFIGURATION + pseudo_config + logconfig
193     for logconfig_item in logging_configurations:
194         loggername, level = logconfig_item.split(':')
195         level = getattr(logging, level, logging.INFO)
196         logger = logging.getLogger(loggername)
197         logger.setLevel(level)
198
199     for logconfig_item in logging_configurations:
200         _logger.debug('logger level set: "%s"', logconfig_item)
201
202 DEFAULT_LOG_CONFIGURATION = [
203     'openerp.workflow.workitem:WARNING',
204     'openerp.http.rpc.request:INFO',
205     'openerp.http.rpc.response:INFO',
206     'openerp.addons.web.http:INFO',
207     'openerp.sql_db:INFO',
208     ':INFO',
209 ]
210 PSEUDOCONFIG_MAPPER = {
211     'debug_rpc_answer': ['openerp:DEBUG','openerp.http.rpc.request:DEBUG', 'openerp.http.rpc.response:DEBUG'],
212     'debug_rpc': ['openerp:DEBUG','openerp.http.rpc.request:DEBUG'],
213     'debug': ['openerp:DEBUG'],
214     'debug_sql': ['openerp.sql_db:DEBUG'],
215     'info': [],
216     'warn': ['openerp:WARNING'],
217     'error': ['openerp:ERROR'],
218     'critical': ['openerp:CRITICAL'],
219 }
220
221 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: