##// END OF EJS Templates
logging: skip certain very large args/kwargs reporting in logs of called methods
super-admin -
r1073:176bb96a python3
parent child Browse files
Show More
@@ -1,741 +1,751 b''
1 # RhodeCode VCSServer provides access to different vcs backends via network.
1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 # Copyright (C) 2014-2020 RhodeCode GmbH
2 # Copyright (C) 2014-2020 RhodeCode GmbH
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
7 # (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software Foundation,
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
18 import io
18 import io
19 import os
19 import os
20 import sys
20 import sys
21 import base64
21 import base64
22 import locale
22 import locale
23 import logging
23 import logging
24 import uuid
24 import uuid
25 import time
25 import time
26 import wsgiref.util
26 import wsgiref.util
27 import traceback
27 import traceback
28 import tempfile
28 import tempfile
29 import psutil
29 import psutil
30
30
31 from itertools import chain
31 from itertools import chain
32
32
33 import msgpack
33 import msgpack
34 import configparser
34 import configparser
35
35
36 from pyramid.config import Configurator
36 from pyramid.config import Configurator
37 from pyramid.wsgi import wsgiapp
37 from pyramid.wsgi import wsgiapp
38 from pyramid.response import Response
38 from pyramid.response import Response
39
39
40 from vcsserver.lib.rc_json import json
40 from vcsserver.lib.rc_json import json
41 from vcsserver.config.settings_maker import SettingsMaker
41 from vcsserver.config.settings_maker import SettingsMaker
42 from vcsserver.str_utils import safe_int, safe_bytes, safe_str
42 from vcsserver.str_utils import safe_int, safe_bytes, safe_str
43 from vcsserver.lib.statsd_client import StatsdClient
43 from vcsserver.lib.statsd_client import StatsdClient
44
44
45 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
46
46
47 # due to Mercurial/glibc2.27 problems we need to detect if locale settings are
47 # due to Mercurial/glibc2.27 problems we need to detect if locale settings are
48 # causing problems and "fix" it in case they do and fallback to LC_ALL = C
48 # causing problems and "fix" it in case they do and fallback to LC_ALL = C
49
49
50 try:
50 try:
51 locale.setlocale(locale.LC_ALL, '')
51 locale.setlocale(locale.LC_ALL, '')
52 except locale.Error as e:
52 except locale.Error as e:
53 log.error(
53 log.error(
54 'LOCALE ERROR: failed to set LC_ALL, fallback to LC_ALL=C, org error: %s', e)
54 'LOCALE ERROR: failed to set LC_ALL, fallback to LC_ALL=C, org error: %s', e)
55 os.environ['LC_ALL'] = 'C'
55 os.environ['LC_ALL'] = 'C'
56
56
57
57
58 import vcsserver
58 import vcsserver
59 from vcsserver import remote_wsgi, scm_app, settings, hgpatches
59 from vcsserver import remote_wsgi, scm_app, settings, hgpatches
60 from vcsserver.git_lfs.app import GIT_LFS_CONTENT_TYPE, GIT_LFS_PROTO_PAT
60 from vcsserver.git_lfs.app import GIT_LFS_CONTENT_TYPE, GIT_LFS_PROTO_PAT
61 from vcsserver.echo_stub import remote_wsgi as remote_wsgi_stub
61 from vcsserver.echo_stub import remote_wsgi as remote_wsgi_stub
62 from vcsserver.echo_stub.echo_app import EchoApp
62 from vcsserver.echo_stub.echo_app import EchoApp
63 from vcsserver.exceptions import HTTPRepoLocked, HTTPRepoBranchProtected
63 from vcsserver.exceptions import HTTPRepoLocked, HTTPRepoBranchProtected
64 from vcsserver.lib.exc_tracking import store_exception
64 from vcsserver.lib.exc_tracking import store_exception
65 from vcsserver.server import VcsServer
65 from vcsserver.server import VcsServer
66
66
67 strict_vcs = True
67 strict_vcs = True
68
68
69 git_import_err = None
69 git_import_err = None
70 try:
70 try:
71 from vcsserver.remote.git import GitFactory, GitRemote
71 from vcsserver.remote.git import GitFactory, GitRemote
72 except ImportError as e:
72 except ImportError as e:
73 GitFactory = None
73 GitFactory = None
74 GitRemote = None
74 GitRemote = None
75 git_import_err = e
75 git_import_err = e
76 if strict_vcs:
76 if strict_vcs:
77 raise
77 raise
78
78
79
79
80 hg_import_err = None
80 hg_import_err = None
81 try:
81 try:
82 from vcsserver.remote.hg import MercurialFactory, HgRemote
82 from vcsserver.remote.hg import MercurialFactory, HgRemote
83 except ImportError as e:
83 except ImportError as e:
84 MercurialFactory = None
84 MercurialFactory = None
85 HgRemote = None
85 HgRemote = None
86 hg_import_err = e
86 hg_import_err = e
87 if strict_vcs:
87 if strict_vcs:
88 raise
88 raise
89
89
90
90
91 svn_import_err = None
91 svn_import_err = None
92 try:
92 try:
93 from vcsserver.remote.svn import SubversionFactory, SvnRemote
93 from vcsserver.remote.svn import SubversionFactory, SvnRemote
94 except ImportError as e:
94 except ImportError as e:
95 SubversionFactory = None
95 SubversionFactory = None
96 SvnRemote = None
96 SvnRemote = None
97 svn_import_err = e
97 svn_import_err = e
98 if strict_vcs:
98 if strict_vcs:
99 raise
99 raise
100
100
101
101
102 def _is_request_chunked(environ):
102 def _is_request_chunked(environ):
103 stream = environ.get('HTTP_TRANSFER_ENCODING', '') == 'chunked'
103 stream = environ.get('HTTP_TRANSFER_ENCODING', '') == 'chunked'
104 return stream
104 return stream
105
105
106
106
107 def log_max_fd():
107 def log_max_fd():
108 try:
108 try:
109 maxfd = psutil.Process().rlimit(psutil.RLIMIT_NOFILE)[1]
109 maxfd = psutil.Process().rlimit(psutil.RLIMIT_NOFILE)[1]
110 log.info('Max file descriptors value: %s', maxfd)
110 log.info('Max file descriptors value: %s', maxfd)
111 except Exception:
111 except Exception:
112 pass
112 pass
113
113
114
114
115 class VCS(object):
115 class VCS(object):
116 def __init__(self, locale_conf=None, cache_config=None):
116 def __init__(self, locale_conf=None, cache_config=None):
117 self.locale = locale_conf
117 self.locale = locale_conf
118 self.cache_config = cache_config
118 self.cache_config = cache_config
119 self._configure_locale()
119 self._configure_locale()
120
120
121 log_max_fd()
121 log_max_fd()
122
122
123 if GitFactory and GitRemote:
123 if GitFactory and GitRemote:
124 git_factory = GitFactory()
124 git_factory = GitFactory()
125 self._git_remote = GitRemote(git_factory)
125 self._git_remote = GitRemote(git_factory)
126 else:
126 else:
127 log.error("Git client import failed: %s", git_import_err)
127 log.error("Git client import failed: %s", git_import_err)
128
128
129 if MercurialFactory and HgRemote:
129 if MercurialFactory and HgRemote:
130 hg_factory = MercurialFactory()
130 hg_factory = MercurialFactory()
131 self._hg_remote = HgRemote(hg_factory)
131 self._hg_remote = HgRemote(hg_factory)
132 else:
132 else:
133 log.error("Mercurial client import failed: %s", hg_import_err)
133 log.error("Mercurial client import failed: %s", hg_import_err)
134
134
135 if SubversionFactory and SvnRemote:
135 if SubversionFactory and SvnRemote:
136 svn_factory = SubversionFactory()
136 svn_factory = SubversionFactory()
137
137
138 # hg factory is used for svn url validation
138 # hg factory is used for svn url validation
139 hg_factory = MercurialFactory()
139 hg_factory = MercurialFactory()
140 self._svn_remote = SvnRemote(svn_factory, hg_factory=hg_factory)
140 self._svn_remote = SvnRemote(svn_factory, hg_factory=hg_factory)
141 else:
141 else:
142 log.error("Subversion client import failed: %s", svn_import_err)
142 log.error("Subversion client import failed: %s", svn_import_err)
143
143
144 self._vcsserver = VcsServer()
144 self._vcsserver = VcsServer()
145
145
146 def _configure_locale(self):
146 def _configure_locale(self):
147 if self.locale:
147 if self.locale:
148 log.info('Settings locale: `LC_ALL` to %s', self.locale)
148 log.info('Settings locale: `LC_ALL` to %s', self.locale)
149 else:
149 else:
150 log.info('Configuring locale subsystem based on environment variables')
150 log.info('Configuring locale subsystem based on environment variables')
151 try:
151 try:
152 # If self.locale is the empty string, then the locale
152 # If self.locale is the empty string, then the locale
153 # module will use the environment variables. See the
153 # module will use the environment variables. See the
154 # documentation of the package `locale`.
154 # documentation of the package `locale`.
155 locale.setlocale(locale.LC_ALL, self.locale)
155 locale.setlocale(locale.LC_ALL, self.locale)
156
156
157 language_code, encoding = locale.getlocale()
157 language_code, encoding = locale.getlocale()
158 log.info(
158 log.info(
159 'Locale set to language code "%s" with encoding "%s".',
159 'Locale set to language code "%s" with encoding "%s".',
160 language_code, encoding)
160 language_code, encoding)
161 except locale.Error:
161 except locale.Error:
162 log.exception('Cannot set locale, not configuring the locale system')
162 log.exception('Cannot set locale, not configuring the locale system')
163
163
164
164
165 class WsgiProxy(object):
165 class WsgiProxy(object):
166 def __init__(self, wsgi):
166 def __init__(self, wsgi):
167 self.wsgi = wsgi
167 self.wsgi = wsgi
168
168
169 def __call__(self, environ, start_response):
169 def __call__(self, environ, start_response):
170 input_data = environ['wsgi.input'].read()
170 input_data = environ['wsgi.input'].read()
171 input_data = msgpack.unpackb(input_data)
171 input_data = msgpack.unpackb(input_data)
172
172
173 error = None
173 error = None
174 try:
174 try:
175 data, status, headers = self.wsgi.handle(
175 data, status, headers = self.wsgi.handle(
176 input_data['environment'], input_data['input_data'],
176 input_data['environment'], input_data['input_data'],
177 *input_data['args'], **input_data['kwargs'])
177 *input_data['args'], **input_data['kwargs'])
178 except Exception as e:
178 except Exception as e:
179 data, status, headers = [], None, None
179 data, status, headers = [], None, None
180 error = {
180 error = {
181 'message': str(e),
181 'message': str(e),
182 '_vcs_kind': getattr(e, '_vcs_kind', None)
182 '_vcs_kind': getattr(e, '_vcs_kind', None)
183 }
183 }
184
184
185 start_response(200, {})
185 start_response(200, {})
186 return self._iterator(error, status, headers, data)
186 return self._iterator(error, status, headers, data)
187
187
188 def _iterator(self, error, status, headers, data):
188 def _iterator(self, error, status, headers, data):
189 initial_data = [
189 initial_data = [
190 error,
190 error,
191 status,
191 status,
192 headers,
192 headers,
193 ]
193 ]
194
194
195 for d in chain(initial_data, data):
195 for d in chain(initial_data, data):
196 yield msgpack.packb(d)
196 yield msgpack.packb(d)
197
197
198
198
199 def not_found(request):
199 def not_found(request):
200 return {'status': '404 NOT FOUND'}
200 return {'status': '404 NOT FOUND'}
201
201
202
202
203 class VCSViewPredicate(object):
203 class VCSViewPredicate(object):
204 def __init__(self, val, config):
204 def __init__(self, val, config):
205 self.remotes = val
205 self.remotes = val
206
206
207 def text(self):
207 def text(self):
208 return 'vcs view method = %s' % (list(self.remotes.keys()),)
208 return 'vcs view method = %s' % (list(self.remotes.keys()),)
209
209
210 phash = text
210 phash = text
211
211
212 def __call__(self, context, request):
212 def __call__(self, context, request):
213 """
213 """
214 View predicate that returns true if given backend is supported by
214 View predicate that returns true if given backend is supported by
215 defined remotes.
215 defined remotes.
216 """
216 """
217 backend = request.matchdict.get('backend')
217 backend = request.matchdict.get('backend')
218 return backend in self.remotes
218 return backend in self.remotes
219
219
220
220
221 class HTTPApplication(object):
221 class HTTPApplication(object):
222 ALLOWED_EXCEPTIONS = ('KeyError', 'URLError')
222 ALLOWED_EXCEPTIONS = ('KeyError', 'URLError')
223
223
224 remote_wsgi = remote_wsgi
224 remote_wsgi = remote_wsgi
225 _use_echo_app = False
225 _use_echo_app = False
226
226
227 def __init__(self, settings=None, global_config=None):
227 def __init__(self, settings=None, global_config=None):
228
228
229 self.config = Configurator(settings=settings)
229 self.config = Configurator(settings=settings)
230 # Init our statsd at very start
230 # Init our statsd at very start
231 self.config.registry.statsd = StatsdClient.statsd
231 self.config.registry.statsd = StatsdClient.statsd
232
232
233 self.global_config = global_config
233 self.global_config = global_config
234 self.config.include('vcsserver.lib.rc_cache')
234 self.config.include('vcsserver.lib.rc_cache')
235
235
236 settings_locale = settings.get('locale', '') or 'en_US.UTF-8'
236 settings_locale = settings.get('locale', '') or 'en_US.UTF-8'
237 vcs = VCS(locale_conf=settings_locale, cache_config=settings)
237 vcs = VCS(locale_conf=settings_locale, cache_config=settings)
238 self._remotes = {
238 self._remotes = {
239 'hg': vcs._hg_remote,
239 'hg': vcs._hg_remote,
240 'git': vcs._git_remote,
240 'git': vcs._git_remote,
241 'svn': vcs._svn_remote,
241 'svn': vcs._svn_remote,
242 'server': vcs._vcsserver,
242 'server': vcs._vcsserver,
243 }
243 }
244 if settings.get('dev.use_echo_app', 'false').lower() == 'true':
244 if settings.get('dev.use_echo_app', 'false').lower() == 'true':
245 self._use_echo_app = True
245 self._use_echo_app = True
246 log.warning("Using EchoApp for VCS operations.")
246 log.warning("Using EchoApp for VCS operations.")
247 self.remote_wsgi = remote_wsgi_stub
247 self.remote_wsgi = remote_wsgi_stub
248
248
249 self._configure_settings(global_config, settings)
249 self._configure_settings(global_config, settings)
250
250
251 self._configure()
251 self._configure()
252
252
253 def _configure_settings(self, global_config, app_settings):
253 def _configure_settings(self, global_config, app_settings):
254 """
254 """
255 Configure the settings module.
255 Configure the settings module.
256 """
256 """
257 settings_merged = global_config.copy()
257 settings_merged = global_config.copy()
258 settings_merged.update(app_settings)
258 settings_merged.update(app_settings)
259
259
260 git_path = app_settings.get('git_path', None)
260 git_path = app_settings.get('git_path', None)
261 if git_path:
261 if git_path:
262 settings.GIT_EXECUTABLE = git_path
262 settings.GIT_EXECUTABLE = git_path
263 binary_dir = app_settings.get('core.binary_dir', None)
263 binary_dir = app_settings.get('core.binary_dir', None)
264 if binary_dir:
264 if binary_dir:
265 settings.BINARY_DIR = binary_dir
265 settings.BINARY_DIR = binary_dir
266
266
267 # Store the settings to make them available to other modules.
267 # Store the settings to make them available to other modules.
268 vcsserver.PYRAMID_SETTINGS = settings_merged
268 vcsserver.PYRAMID_SETTINGS = settings_merged
269 vcsserver.CONFIG = settings_merged
269 vcsserver.CONFIG = settings_merged
270
270
271 def _configure(self):
271 def _configure(self):
272 self.config.add_renderer(name='msgpack', factory=self._msgpack_renderer_factory)
272 self.config.add_renderer(name='msgpack', factory=self._msgpack_renderer_factory)
273
273
274 self.config.add_route('service', '/_service')
274 self.config.add_route('service', '/_service')
275 self.config.add_route('status', '/status')
275 self.config.add_route('status', '/status')
276 self.config.add_route('hg_proxy', '/proxy/hg')
276 self.config.add_route('hg_proxy', '/proxy/hg')
277 self.config.add_route('git_proxy', '/proxy/git')
277 self.config.add_route('git_proxy', '/proxy/git')
278
278
279 # rpc methods
279 # rpc methods
280 self.config.add_route('vcs', '/{backend}')
280 self.config.add_route('vcs', '/{backend}')
281
281
282 # streaming rpc remote methods
282 # streaming rpc remote methods
283 self.config.add_route('vcs_stream', '/{backend}/stream')
283 self.config.add_route('vcs_stream', '/{backend}/stream')
284
284
285 # vcs operations clone/push as streaming
285 # vcs operations clone/push as streaming
286 self.config.add_route('stream_git', '/stream/git/*repo_name')
286 self.config.add_route('stream_git', '/stream/git/*repo_name')
287 self.config.add_route('stream_hg', '/stream/hg/*repo_name')
287 self.config.add_route('stream_hg', '/stream/hg/*repo_name')
288
288
289 self.config.add_view(self.status_view, route_name='status', renderer='json')
289 self.config.add_view(self.status_view, route_name='status', renderer='json')
290 self.config.add_view(self.service_view, route_name='service', renderer='msgpack')
290 self.config.add_view(self.service_view, route_name='service', renderer='msgpack')
291
291
292 self.config.add_view(self.hg_proxy(), route_name='hg_proxy')
292 self.config.add_view(self.hg_proxy(), route_name='hg_proxy')
293 self.config.add_view(self.git_proxy(), route_name='git_proxy')
293 self.config.add_view(self.git_proxy(), route_name='git_proxy')
294 self.config.add_view(self.vcs_view, route_name='vcs', renderer='msgpack',
294 self.config.add_view(self.vcs_view, route_name='vcs', renderer='msgpack',
295 vcs_view=self._remotes)
295 vcs_view=self._remotes)
296 self.config.add_view(self.vcs_stream_view, route_name='vcs_stream',
296 self.config.add_view(self.vcs_stream_view, route_name='vcs_stream',
297 vcs_view=self._remotes)
297 vcs_view=self._remotes)
298
298
299 self.config.add_view(self.hg_stream(), route_name='stream_hg')
299 self.config.add_view(self.hg_stream(), route_name='stream_hg')
300 self.config.add_view(self.git_stream(), route_name='stream_git')
300 self.config.add_view(self.git_stream(), route_name='stream_git')
301
301
302 self.config.add_view_predicate('vcs_view', VCSViewPredicate)
302 self.config.add_view_predicate('vcs_view', VCSViewPredicate)
303
303
304 self.config.add_notfound_view(not_found, renderer='json')
304 self.config.add_notfound_view(not_found, renderer='json')
305
305
306 self.config.add_view(self.handle_vcs_exception, context=Exception)
306 self.config.add_view(self.handle_vcs_exception, context=Exception)
307
307
308 self.config.add_tween(
308 self.config.add_tween(
309 'vcsserver.tweens.request_wrapper.RequestWrapperTween',
309 'vcsserver.tweens.request_wrapper.RequestWrapperTween',
310 )
310 )
311 self.config.add_request_method(
311 self.config.add_request_method(
312 'vcsserver.lib.request_counter.get_request_counter',
312 'vcsserver.lib.request_counter.get_request_counter',
313 'request_count')
313 'request_count')
314
314
315 def wsgi_app(self):
315 def wsgi_app(self):
316 return self.config.make_wsgi_app()
316 return self.config.make_wsgi_app()
317
317
318 def _vcs_view_params(self, request):
318 def _vcs_view_params(self, request):
319 remote = self._remotes[request.matchdict['backend']]
319 remote = self._remotes[request.matchdict['backend']]
320 payload = msgpack.unpackb(request.body, use_list=True)
320 payload = msgpack.unpackb(request.body, use_list=True)
321
321
322 method = payload.get('method')
322 method = payload.get('method')
323 params = payload['params']
323 params = payload['params']
324 wire = params.get('wire')
324 wire = params.get('wire')
325 args = params.get('args')
325 args = params.get('args')
326 kwargs = params.get('kwargs')
326 kwargs = params.get('kwargs')
327 context_uid = None
327 context_uid = None
328
328
329 if wire:
329 if wire:
330 try:
330 try:
331 wire['context'] = context_uid = uuid.UUID(wire['context'])
331 wire['context'] = context_uid = uuid.UUID(wire['context'])
332 except KeyError:
332 except KeyError:
333 pass
333 pass
334 args.insert(0, wire)
334 args.insert(0, wire)
335 repo_state_uid = wire.get('repo_state_uid') if wire else None
335 repo_state_uid = wire.get('repo_state_uid') if wire else None
336
336
337 # NOTE(marcink): trading complexity for slight performance
337 # NOTE(marcink): trading complexity for slight performance
338 if log.isEnabledFor(logging.DEBUG):
338 if log.isEnabledFor(logging.DEBUG):
339 no_args_methods = [
339 # also we SKIP printing out any of those methods args since they maybe excessive
340
340 just_args_methods = {
341 ]
341 'commitctx': ('content', 'removed', 'updated')
342 if method in no_args_methods:
342 }
343 if method in just_args_methods:
344 skip_args = just_args_methods[method]
343 call_args = ''
345 call_args = ''
346 call_kwargs = {}
347 for k in kwargs:
348 if k in skip_args:
349 # replace our skip key with dummy
350 call_kwargs[k] = f'RemovedParam({k})'
351 else:
352 call_kwargs[k] = kwargs[k]
344 else:
353 else:
345 call_args = args[1:]
354 call_args = args[1:]
355 call_kwargs = kwargs
346
356
347 log.debug('Method requested:`%s` with args:%s kwargs:%s context_uid: %s, repo_state_uid:%s',
357 log.debug('Method requested:`%s` with args:%s kwargs:%s context_uid: %s, repo_state_uid:%s',
348 method, call_args, kwargs, context_uid, repo_state_uid)
358 method, call_args, call_kwargs, context_uid, repo_state_uid)
349
359
350 statsd = request.registry.statsd
360 statsd = request.registry.statsd
351 if statsd:
361 if statsd:
352 statsd.incr(
362 statsd.incr(
353 'vcsserver_method_total', tags=[
363 'vcsserver_method_total', tags=[
354 "method:{}".format(method),
364 "method:{}".format(method),
355 ])
365 ])
356 return payload, remote, method, args, kwargs
366 return payload, remote, method, args, kwargs
357
367
358 def vcs_view(self, request):
368 def vcs_view(self, request):
359
369
360 payload, remote, method, args, kwargs = self._vcs_view_params(request)
370 payload, remote, method, args, kwargs = self._vcs_view_params(request)
361 payload_id = payload.get('id')
371 payload_id = payload.get('id')
362
372
363 try:
373 try:
364 resp = getattr(remote, method)(*args, **kwargs)
374 resp = getattr(remote, method)(*args, **kwargs)
365 except Exception as e:
375 except Exception as e:
366 exc_info = list(sys.exc_info())
376 exc_info = list(sys.exc_info())
367 exc_type, exc_value, exc_traceback = exc_info
377 exc_type, exc_value, exc_traceback = exc_info
368
378
369 org_exc = getattr(e, '_org_exc', None)
379 org_exc = getattr(e, '_org_exc', None)
370 org_exc_name = None
380 org_exc_name = None
371 org_exc_tb = ''
381 org_exc_tb = ''
372 if org_exc:
382 if org_exc:
373 org_exc_name = org_exc.__class__.__name__
383 org_exc_name = org_exc.__class__.__name__
374 org_exc_tb = getattr(e, '_org_exc_tb', '')
384 org_exc_tb = getattr(e, '_org_exc_tb', '')
375 # replace our "faked" exception with our org
385 # replace our "faked" exception with our org
376 exc_info[0] = org_exc.__class__
386 exc_info[0] = org_exc.__class__
377 exc_info[1] = org_exc
387 exc_info[1] = org_exc
378
388
379 should_store_exc = True
389 should_store_exc = True
380 if org_exc:
390 if org_exc:
381 def get_exc_fqn(_exc_obj):
391 def get_exc_fqn(_exc_obj):
382 module_name = getattr(org_exc.__class__, '__module__', 'UNKNOWN')
392 module_name = getattr(org_exc.__class__, '__module__', 'UNKNOWN')
383 return module_name + '.' + org_exc_name
393 return module_name + '.' + org_exc_name
384
394
385 exc_fqn = get_exc_fqn(org_exc)
395 exc_fqn = get_exc_fqn(org_exc)
386
396
387 if exc_fqn in ['mercurial.error.RepoLookupError',
397 if exc_fqn in ['mercurial.error.RepoLookupError',
388 'vcsserver.exceptions.RefNotFoundException']:
398 'vcsserver.exceptions.RefNotFoundException']:
389 should_store_exc = False
399 should_store_exc = False
390
400
391 if should_store_exc:
401 if should_store_exc:
392 store_exception(id(exc_info), exc_info, request_path=request.path)
402 store_exception(id(exc_info), exc_info, request_path=request.path)
393
403
394 tb_info = ''.join(
404 tb_info = ''.join(
395 traceback.format_exception(exc_type, exc_value, exc_traceback))
405 traceback.format_exception(exc_type, exc_value, exc_traceback))
396
406
397 type_ = e.__class__.__name__
407 type_ = e.__class__.__name__
398 if type_ not in self.ALLOWED_EXCEPTIONS:
408 if type_ not in self.ALLOWED_EXCEPTIONS:
399 type_ = None
409 type_ = None
400
410
401 resp = {
411 resp = {
402 'id': payload_id,
412 'id': payload_id,
403 'error': {
413 'error': {
404 'message': str(e),
414 'message': str(e),
405 'traceback': tb_info,
415 'traceback': tb_info,
406 'org_exc': org_exc_name,
416 'org_exc': org_exc_name,
407 'org_exc_tb': org_exc_tb,
417 'org_exc_tb': org_exc_tb,
408 'type': type_
418 'type': type_
409 }
419 }
410 }
420 }
411
421
412 try:
422 try:
413 resp['error']['_vcs_kind'] = getattr(e, '_vcs_kind', None)
423 resp['error']['_vcs_kind'] = getattr(e, '_vcs_kind', None)
414 except AttributeError:
424 except AttributeError:
415 pass
425 pass
416 else:
426 else:
417 resp = {
427 resp = {
418 'id': payload_id,
428 'id': payload_id,
419 'result': resp
429 'result': resp
420 }
430 }
421
431 log.debug('Serving data for method %s', method)
422 return resp
432 return resp
423
433
424 def vcs_stream_view(self, request):
434 def vcs_stream_view(self, request):
425 payload, remote, method, args, kwargs = self._vcs_view_params(request)
435 payload, remote, method, args, kwargs = self._vcs_view_params(request)
426 # this method has a stream: marker we remove it here
436 # this method has a stream: marker we remove it here
427 method = method.split('stream:')[-1]
437 method = method.split('stream:')[-1]
428 chunk_size = safe_int(payload.get('chunk_size')) or 4096
438 chunk_size = safe_int(payload.get('chunk_size')) or 4096
429
439
430 try:
440 try:
431 resp = getattr(remote, method)(*args, **kwargs)
441 resp = getattr(remote, method)(*args, **kwargs)
432 except Exception as e:
442 except Exception as e:
433 raise
443 raise
434
444
435 def get_chunked_data(method_resp):
445 def get_chunked_data(method_resp):
436 stream = io.BytesIO(method_resp)
446 stream = io.BytesIO(method_resp)
437 while 1:
447 while 1:
438 chunk = stream.read(chunk_size)
448 chunk = stream.read(chunk_size)
439 if not chunk:
449 if not chunk:
440 break
450 break
441 yield chunk
451 yield chunk
442
452
443 response = Response(app_iter=get_chunked_data(resp))
453 response = Response(app_iter=get_chunked_data(resp))
444 response.content_type = 'application/octet-stream'
454 response.content_type = 'application/octet-stream'
445
455
446 return response
456 return response
447
457
448 def status_view(self, request):
458 def status_view(self, request):
449 import vcsserver
459 import vcsserver
450 return {'status': 'OK', 'vcsserver_version': safe_str(vcsserver.__version__),
460 return {'status': 'OK', 'vcsserver_version': safe_str(vcsserver.__version__),
451 'pid': os.getpid()}
461 'pid': os.getpid()}
452
462
453 def service_view(self, request):
463 def service_view(self, request):
454 import vcsserver
464 import vcsserver
455
465
456 payload = msgpack.unpackb(request.body, use_list=True)
466 payload = msgpack.unpackb(request.body, use_list=True)
457 server_config, app_config = {}, {}
467 server_config, app_config = {}, {}
458
468
459 try:
469 try:
460 path = self.global_config['__file__']
470 path = self.global_config['__file__']
461 config = configparser.RawConfigParser()
471 config = configparser.RawConfigParser()
462
472
463 config.read(path)
473 config.read(path)
464
474
465 if config.has_section('server:main'):
475 if config.has_section('server:main'):
466 server_config = dict(config.items('server:main'))
476 server_config = dict(config.items('server:main'))
467 if config.has_section('app:main'):
477 if config.has_section('app:main'):
468 app_config = dict(config.items('app:main'))
478 app_config = dict(config.items('app:main'))
469
479
470 except Exception:
480 except Exception:
471 log.exception('Failed to read .ini file for display')
481 log.exception('Failed to read .ini file for display')
472
482
473 environ = list(os.environ.items())
483 environ = list(os.environ.items())
474
484
475 resp = {
485 resp = {
476 'id': payload.get('id'),
486 'id': payload.get('id'),
477 'result': dict(
487 'result': dict(
478 version=safe_str(vcsserver.__version__),
488 version=safe_str(vcsserver.__version__),
479 config=server_config,
489 config=server_config,
480 app_config=app_config,
490 app_config=app_config,
481 environ=environ,
491 environ=environ,
482 payload=payload,
492 payload=payload,
483 )
493 )
484 }
494 }
485 return resp
495 return resp
486
496
487 def _msgpack_renderer_factory(self, info):
497 def _msgpack_renderer_factory(self, info):
488 def _render(value, system):
498 def _render(value, system):
489 request = system.get('request')
499 request = system.get('request')
490 if request is not None:
500 if request is not None:
491 response = request.response
501 response = request.response
492 ct = response.content_type
502 ct = response.content_type
493 if ct == response.default_content_type:
503 if ct == response.default_content_type:
494 response.content_type = 'application/x-msgpack'
504 response.content_type = 'application/x-msgpack'
495
505
496 return msgpack.packb(value, use_bin_type=False)
506 return msgpack.packb(value, use_bin_type=False)
497 return _render
507 return _render
498
508
499 def set_env_from_config(self, environ, config):
509 def set_env_from_config(self, environ, config):
500 dict_conf = {}
510 dict_conf = {}
501 try:
511 try:
502 for elem in config:
512 for elem in config:
503 if elem[0] == 'rhodecode':
513 if elem[0] == 'rhodecode':
504 dict_conf = json.loads(elem[2])
514 dict_conf = json.loads(elem[2])
505 break
515 break
506 except Exception:
516 except Exception:
507 log.exception('Failed to fetch SCM CONFIG')
517 log.exception('Failed to fetch SCM CONFIG')
508 return
518 return
509
519
510 username = dict_conf.get('username')
520 username = dict_conf.get('username')
511 if username:
521 if username:
512 environ['REMOTE_USER'] = username
522 environ['REMOTE_USER'] = username
513 # mercurial specific, some extension api rely on this
523 # mercurial specific, some extension api rely on this
514 environ['HGUSER'] = username
524 environ['HGUSER'] = username
515
525
516 ip = dict_conf.get('ip')
526 ip = dict_conf.get('ip')
517 if ip:
527 if ip:
518 environ['REMOTE_HOST'] = ip
528 environ['REMOTE_HOST'] = ip
519
529
520 if _is_request_chunked(environ):
530 if _is_request_chunked(environ):
521 # set the compatibility flag for webob
531 # set the compatibility flag for webob
522 environ['wsgi.input_terminated'] = True
532 environ['wsgi.input_terminated'] = True
523
533
524 def hg_proxy(self):
534 def hg_proxy(self):
525 @wsgiapp
535 @wsgiapp
526 def _hg_proxy(environ, start_response):
536 def _hg_proxy(environ, start_response):
527 app = WsgiProxy(self.remote_wsgi.HgRemoteWsgi())
537 app = WsgiProxy(self.remote_wsgi.HgRemoteWsgi())
528 return app(environ, start_response)
538 return app(environ, start_response)
529 return _hg_proxy
539 return _hg_proxy
530
540
531 def git_proxy(self):
541 def git_proxy(self):
532 @wsgiapp
542 @wsgiapp
533 def _git_proxy(environ, start_response):
543 def _git_proxy(environ, start_response):
534 app = WsgiProxy(self.remote_wsgi.GitRemoteWsgi())
544 app = WsgiProxy(self.remote_wsgi.GitRemoteWsgi())
535 return app(environ, start_response)
545 return app(environ, start_response)
536 return _git_proxy
546 return _git_proxy
537
547
538 def hg_stream(self):
548 def hg_stream(self):
539 if self._use_echo_app:
549 if self._use_echo_app:
540 @wsgiapp
550 @wsgiapp
541 def _hg_stream(environ, start_response):
551 def _hg_stream(environ, start_response):
542 app = EchoApp('fake_path', 'fake_name', None)
552 app = EchoApp('fake_path', 'fake_name', None)
543 return app(environ, start_response)
553 return app(environ, start_response)
544 return _hg_stream
554 return _hg_stream
545 else:
555 else:
546 @wsgiapp
556 @wsgiapp
547 def _hg_stream(environ, start_response):
557 def _hg_stream(environ, start_response):
548 log.debug('http-app: handling hg stream')
558 log.debug('http-app: handling hg stream')
549 repo_path = environ['HTTP_X_RC_REPO_PATH']
559 repo_path = environ['HTTP_X_RC_REPO_PATH']
550 repo_name = environ['HTTP_X_RC_REPO_NAME']
560 repo_name = environ['HTTP_X_RC_REPO_NAME']
551 packed_config = base64.b64decode(
561 packed_config = base64.b64decode(
552 environ['HTTP_X_RC_REPO_CONFIG'])
562 environ['HTTP_X_RC_REPO_CONFIG'])
553 config = msgpack.unpackb(packed_config)
563 config = msgpack.unpackb(packed_config)
554 app = scm_app.create_hg_wsgi_app(
564 app = scm_app.create_hg_wsgi_app(
555 repo_path, repo_name, config)
565 repo_path, repo_name, config)
556
566
557 # Consistent path information for hgweb
567 # Consistent path information for hgweb
558 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
568 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
559 environ['REPO_NAME'] = repo_name
569 environ['REPO_NAME'] = repo_name
560 self.set_env_from_config(environ, config)
570 self.set_env_from_config(environ, config)
561
571
562 log.debug('http-app: starting app handler '
572 log.debug('http-app: starting app handler '
563 'with %s and process request', app)
573 'with %s and process request', app)
564 return app(environ, ResponseFilter(start_response))
574 return app(environ, ResponseFilter(start_response))
565 return _hg_stream
575 return _hg_stream
566
576
567 def git_stream(self):
577 def git_stream(self):
568 if self._use_echo_app:
578 if self._use_echo_app:
569 @wsgiapp
579 @wsgiapp
570 def _git_stream(environ, start_response):
580 def _git_stream(environ, start_response):
571 app = EchoApp('fake_path', 'fake_name', None)
581 app = EchoApp('fake_path', 'fake_name', None)
572 return app(environ, start_response)
582 return app(environ, start_response)
573 return _git_stream
583 return _git_stream
574 else:
584 else:
575 @wsgiapp
585 @wsgiapp
576 def _git_stream(environ, start_response):
586 def _git_stream(environ, start_response):
577 log.debug('http-app: handling git stream')
587 log.debug('http-app: handling git stream')
578 repo_path = environ['HTTP_X_RC_REPO_PATH']
588 repo_path = environ['HTTP_X_RC_REPO_PATH']
579 repo_name = environ['HTTP_X_RC_REPO_NAME']
589 repo_name = environ['HTTP_X_RC_REPO_NAME']
580 packed_config = base64.b64decode(
590 packed_config = base64.b64decode(
581 environ['HTTP_X_RC_REPO_CONFIG'])
591 environ['HTTP_X_RC_REPO_CONFIG'])
582 config = msgpack.unpackb(packed_config)
592 config = msgpack.unpackb(packed_config)
583
593
584 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
594 environ['PATH_INFO'] = environ['HTTP_X_RC_PATH_INFO']
585 self.set_env_from_config(environ, config)
595 self.set_env_from_config(environ, config)
586
596
587 content_type = environ.get('CONTENT_TYPE', '')
597 content_type = environ.get('CONTENT_TYPE', '')
588
598
589 path = environ['PATH_INFO']
599 path = environ['PATH_INFO']
590 is_lfs_request = GIT_LFS_CONTENT_TYPE in content_type
600 is_lfs_request = GIT_LFS_CONTENT_TYPE in content_type
591 log.debug(
601 log.debug(
592 'LFS: Detecting if request `%s` is LFS server path based '
602 'LFS: Detecting if request `%s` is LFS server path based '
593 'on content type:`%s`, is_lfs:%s',
603 'on content type:`%s`, is_lfs:%s',
594 path, content_type, is_lfs_request)
604 path, content_type, is_lfs_request)
595
605
596 if not is_lfs_request:
606 if not is_lfs_request:
597 # fallback detection by path
607 # fallback detection by path
598 if GIT_LFS_PROTO_PAT.match(path):
608 if GIT_LFS_PROTO_PAT.match(path):
599 is_lfs_request = True
609 is_lfs_request = True
600 log.debug(
610 log.debug(
601 'LFS: fallback detection by path of: `%s`, is_lfs:%s',
611 'LFS: fallback detection by path of: `%s`, is_lfs:%s',
602 path, is_lfs_request)
612 path, is_lfs_request)
603
613
604 if is_lfs_request:
614 if is_lfs_request:
605 app = scm_app.create_git_lfs_wsgi_app(
615 app = scm_app.create_git_lfs_wsgi_app(
606 repo_path, repo_name, config)
616 repo_path, repo_name, config)
607 else:
617 else:
608 app = scm_app.create_git_wsgi_app(
618 app = scm_app.create_git_wsgi_app(
609 repo_path, repo_name, config)
619 repo_path, repo_name, config)
610
620
611 log.debug('http-app: starting app handler '
621 log.debug('http-app: starting app handler '
612 'with %s and process request', app)
622 'with %s and process request', app)
613
623
614 return app(environ, start_response)
624 return app(environ, start_response)
615
625
616 return _git_stream
626 return _git_stream
617
627
618 def handle_vcs_exception(self, exception, request):
628 def handle_vcs_exception(self, exception, request):
619 _vcs_kind = getattr(exception, '_vcs_kind', '')
629 _vcs_kind = getattr(exception, '_vcs_kind', '')
620 if _vcs_kind == 'repo_locked':
630 if _vcs_kind == 'repo_locked':
621 # Get custom repo-locked status code if present.
631 # Get custom repo-locked status code if present.
622 status_code = request.headers.get('X-RC-Locked-Status-Code')
632 status_code = request.headers.get('X-RC-Locked-Status-Code')
623 return HTTPRepoLocked(
633 return HTTPRepoLocked(
624 title=exception.message, status_code=status_code)
634 title=exception.message, status_code=status_code)
625
635
626 elif _vcs_kind == 'repo_branch_protected':
636 elif _vcs_kind == 'repo_branch_protected':
627 # Get custom repo-branch-protected status code if present.
637 # Get custom repo-branch-protected status code if present.
628 return HTTPRepoBranchProtected(title=exception.message)
638 return HTTPRepoBranchProtected(title=exception.message)
629
639
630 exc_info = request.exc_info
640 exc_info = request.exc_info
631 store_exception(id(exc_info), exc_info)
641 store_exception(id(exc_info), exc_info)
632
642
633 traceback_info = 'unavailable'
643 traceback_info = 'unavailable'
634 if request.exc_info:
644 if request.exc_info:
635 exc_type, exc_value, exc_tb = request.exc_info
645 exc_type, exc_value, exc_tb = request.exc_info
636 traceback_info = ''.join(traceback.format_exception(exc_type, exc_value, exc_tb))
646 traceback_info = ''.join(traceback.format_exception(exc_type, exc_value, exc_tb))
637
647
638 log.error(
648 log.error(
639 'error occurred handling this request for path: %s, \n tb: %s',
649 'error occurred handling this request for path: %s, \n tb: %s',
640 request.path, traceback_info)
650 request.path, traceback_info)
641
651
642 statsd = request.registry.statsd
652 statsd = request.registry.statsd
643 if statsd:
653 if statsd:
644 exc_type = "{}.{}".format(exception.__class__.__module__, exception.__class__.__name__)
654 exc_type = "{}.{}".format(exception.__class__.__module__, exception.__class__.__name__)
645 statsd.incr('vcsserver_exception_total',
655 statsd.incr('vcsserver_exception_total',
646 tags=["type:{}".format(exc_type)])
656 tags=["type:{}".format(exc_type)])
647 raise exception
657 raise exception
648
658
649
659
650 class ResponseFilter(object):
660 class ResponseFilter(object):
651
661
652 def __init__(self, start_response):
662 def __init__(self, start_response):
653 self._start_response = start_response
663 self._start_response = start_response
654
664
655 def __call__(self, status, response_headers, exc_info=None):
665 def __call__(self, status, response_headers, exc_info=None):
656 headers = tuple(
666 headers = tuple(
657 (h, v) for h, v in response_headers
667 (h, v) for h, v in response_headers
658 if not wsgiref.util.is_hop_by_hop(h))
668 if not wsgiref.util.is_hop_by_hop(h))
659 return self._start_response(status, headers, exc_info)
669 return self._start_response(status, headers, exc_info)
660
670
661
671
662 def sanitize_settings_and_apply_defaults(global_config, settings):
672 def sanitize_settings_and_apply_defaults(global_config, settings):
663 global_settings_maker = SettingsMaker(global_config)
673 global_settings_maker = SettingsMaker(global_config)
664 settings_maker = SettingsMaker(settings)
674 settings_maker = SettingsMaker(settings)
665
675
666 settings_maker.make_setting('logging.autoconfigure', False, parser='bool')
676 settings_maker.make_setting('logging.autoconfigure', False, parser='bool')
667
677
668 logging_conf = os.path.join(os.path.dirname(global_config.get('__file__')), 'logging.ini')
678 logging_conf = os.path.join(os.path.dirname(global_config.get('__file__')), 'logging.ini')
669 settings_maker.enable_logging(logging_conf)
679 settings_maker.enable_logging(logging_conf)
670
680
671 # Default includes, possible to change as a user
681 # Default includes, possible to change as a user
672 pyramid_includes = settings_maker.make_setting('pyramid.includes', [], parser='list:newline')
682 pyramid_includes = settings_maker.make_setting('pyramid.includes', [], parser='list:newline')
673 log.debug("Using the following pyramid.includes: %s", pyramid_includes)
683 log.debug("Using the following pyramid.includes: %s", pyramid_includes)
674
684
675 settings_maker.make_setting('__file__', global_config.get('__file__'))
685 settings_maker.make_setting('__file__', global_config.get('__file__'))
676
686
677 settings_maker.make_setting('pyramid.default_locale_name', 'en')
687 settings_maker.make_setting('pyramid.default_locale_name', 'en')
678 settings_maker.make_setting('locale', 'en_US.UTF-8')
688 settings_maker.make_setting('locale', 'en_US.UTF-8')
679
689
680 settings_maker.make_setting('core.binary_dir', '')
690 settings_maker.make_setting('core.binary_dir', '')
681
691
682 temp_store = tempfile.gettempdir()
692 temp_store = tempfile.gettempdir()
683 default_cache_dir = os.path.join(temp_store, 'rc_cache')
693 default_cache_dir = os.path.join(temp_store, 'rc_cache')
684 # save default, cache dir, and use it for all backends later.
694 # save default, cache dir, and use it for all backends later.
685 default_cache_dir = settings_maker.make_setting(
695 default_cache_dir = settings_maker.make_setting(
686 'cache_dir',
696 'cache_dir',
687 default=default_cache_dir, default_when_empty=True,
697 default=default_cache_dir, default_when_empty=True,
688 parser='dir:ensured')
698 parser='dir:ensured')
689
699
690 # exception store cache
700 # exception store cache
691 settings_maker.make_setting(
701 settings_maker.make_setting(
692 'exception_tracker.store_path',
702 'exception_tracker.store_path',
693 default=os.path.join(default_cache_dir, 'exc_store'), default_when_empty=True,
703 default=os.path.join(default_cache_dir, 'exc_store'), default_when_empty=True,
694 parser='dir:ensured'
704 parser='dir:ensured'
695 )
705 )
696
706
697 # repo_object cache defaults
707 # repo_object cache defaults
698 settings_maker.make_setting(
708 settings_maker.make_setting(
699 'rc_cache.repo_object.backend',
709 'rc_cache.repo_object.backend',
700 default='dogpile.cache.rc.file_namespace',
710 default='dogpile.cache.rc.file_namespace',
701 parser='string')
711 parser='string')
702 settings_maker.make_setting(
712 settings_maker.make_setting(
703 'rc_cache.repo_object.expiration_time',
713 'rc_cache.repo_object.expiration_time',
704 default=30 * 24 * 60 * 60, # 30days
714 default=30 * 24 * 60 * 60, # 30days
705 parser='int')
715 parser='int')
706 settings_maker.make_setting(
716 settings_maker.make_setting(
707 'rc_cache.repo_object.arguments.filename',
717 'rc_cache.repo_object.arguments.filename',
708 default=os.path.join(default_cache_dir, 'vcsserver_cache_repo_object.db'),
718 default=os.path.join(default_cache_dir, 'vcsserver_cache_repo_object.db'),
709 parser='string')
719 parser='string')
710
720
711 # statsd
721 # statsd
712 settings_maker.make_setting('statsd.enabled', False, parser='bool')
722 settings_maker.make_setting('statsd.enabled', False, parser='bool')
713 settings_maker.make_setting('statsd.statsd_host', 'statsd-exporter', parser='string')
723 settings_maker.make_setting('statsd.statsd_host', 'statsd-exporter', parser='string')
714 settings_maker.make_setting('statsd.statsd_port', 9125, parser='int')
724 settings_maker.make_setting('statsd.statsd_port', 9125, parser='int')
715 settings_maker.make_setting('statsd.statsd_prefix', '')
725 settings_maker.make_setting('statsd.statsd_prefix', '')
716 settings_maker.make_setting('statsd.statsd_ipv6', False, parser='bool')
726 settings_maker.make_setting('statsd.statsd_ipv6', False, parser='bool')
717
727
718 settings_maker.env_expand()
728 settings_maker.env_expand()
719
729
720
730
721 def main(global_config, **settings):
731 def main(global_config, **settings):
722 start_time = time.time()
732 start_time = time.time()
723 log.info('Pyramid app config starting')
733 log.info('Pyramid app config starting')
724
734
725 if MercurialFactory:
735 if MercurialFactory:
726 hgpatches.patch_largefiles_capabilities()
736 hgpatches.patch_largefiles_capabilities()
727 hgpatches.patch_subrepo_type_mapping()
737 hgpatches.patch_subrepo_type_mapping()
728
738
729 # Fill in and sanitize the defaults & do ENV expansion
739 # Fill in and sanitize the defaults & do ENV expansion
730 sanitize_settings_and_apply_defaults(global_config, settings)
740 sanitize_settings_and_apply_defaults(global_config, settings)
731
741
732 # init and bootstrap StatsdClient
742 # init and bootstrap StatsdClient
733 StatsdClient.setup(settings)
743 StatsdClient.setup(settings)
734
744
735 pyramid_app = HTTPApplication(settings=settings, global_config=global_config).wsgi_app()
745 pyramid_app = HTTPApplication(settings=settings, global_config=global_config).wsgi_app()
736 total_time = time.time() - start_time
746 total_time = time.time() - start_time
737 log.info('Pyramid app `%s` created and configured in %.2fs',
747 log.info('Pyramid app `%s` created and configured in %.2fs',
738 getattr(pyramid_app, 'func_name', 'pyramid_app'), total_time)
748 getattr(pyramid_app, 'func_name', 'pyramid_app'), total_time)
739 return pyramid_app
749 return pyramid_app
740
750
741
751
General Comments 0
You need to be logged in to leave comments. Login now