##// END OF EJS Templates
request-wrapper: add request counters
marcink -
r3874:e2b5c030 default
parent child Browse files
Show More
@@ -0,0 +1,27 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2017-2019 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 counter = 0
22
23
24 def get_request_counter(request):
25 global counter
26 counter += 1
27 return counter
@@ -1,746 +1,750 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import os
22 22 import sys
23 23 import logging
24 24 import collections
25 25 import tempfile
26 26 import time
27 27
28 28 from paste.gzipper import make_gzip_middleware
29 29 import pyramid.events
30 30 from pyramid.wsgi import wsgiapp
31 31 from pyramid.authorization import ACLAuthorizationPolicy
32 32 from pyramid.config import Configurator
33 33 from pyramid.settings import asbool, aslist
34 34 from pyramid.httpexceptions import (
35 35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound, HTTPNotFound)
36 36 from pyramid.renderers import render_to_response
37 37
38 38 from rhodecode.model import meta
39 39 from rhodecode.config import patches
40 40 from rhodecode.config import utils as config_utils
41 41 from rhodecode.config.environment import load_pyramid_environment
42 42
43 43 import rhodecode.events
44 44 from rhodecode.lib.middleware.vcs import VCSMiddleware
45 45 from rhodecode.lib.request import Request
46 46 from rhodecode.lib.vcs import VCSCommunicationError
47 47 from rhodecode.lib.exceptions import VCSServerUnavailable
48 48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
49 49 from rhodecode.lib.middleware.https_fixup import HttpsFixup
50 50 from rhodecode.lib.celerylib.loader import configure_celery
51 51 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
52 52 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
53 53 from rhodecode.lib.exc_tracking import store_exception
54 54 from rhodecode.subscribers import (
55 55 scan_repositories_if_enabled, write_js_routes_if_enabled,
56 56 write_metadata_if_needed, inject_app_settings)
57 57
58 58
59 59 log = logging.getLogger(__name__)
60 60
61 61
62 62 def is_http_error(response):
63 63 # error which should have traceback
64 64 return response.status_code > 499
65 65
66 66
67 67 def should_load_all():
68 68 """
69 69 Returns if all application components should be loaded. In some cases it's
70 70 desired to skip apps loading for faster shell script execution
71 71 """
72 72 return True
73 73
74 74
75 75 def make_pyramid_app(global_config, **settings):
76 76 """
77 77 Constructs the WSGI application based on Pyramid.
78 78
79 79 Specials:
80 80
81 81 * The application can also be integrated like a plugin via the call to
82 82 `includeme`. This is accompanied with the other utility functions which
83 83 are called. Changing this should be done with great care to not break
84 84 cases when these fragments are assembled from another place.
85 85
86 86 """
87 87
88 88 # Allows to use format style "{ENV_NAME}" placeholders in the configuration. It
89 89 # will be replaced by the value of the environment variable "NAME" in this case.
90 90 start_time = time.time()
91 91
92 92 debug = asbool(global_config.get('debug'))
93 93 if debug:
94 94 enable_debug()
95 95
96 96 environ = {'ENV_{}'.format(key): value for key, value in os.environ.items()}
97 97
98 98 global_config = _substitute_values(global_config, environ)
99 99 settings = _substitute_values(settings, environ)
100 100
101 101 sanitize_settings_and_apply_defaults(global_config, settings)
102 102
103 103 config = Configurator(settings=settings)
104 104
105 105 # Apply compatibility patches
106 106 patches.inspect_getargspec()
107 107
108 108 load_pyramid_environment(global_config, settings)
109 109
110 110 # Static file view comes first
111 111 includeme_first(config)
112 112
113 113 includeme(config)
114 114
115 115 pyramid_app = config.make_wsgi_app()
116 116 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
117 117 pyramid_app.config = config
118 118
119 119 config.configure_celery(global_config['__file__'])
120 120 # creating the app uses a connection - return it after we are done
121 121 meta.Session.remove()
122 122 total_time = time.time() - start_time
123 123 log.info('Pyramid app `%s` created and configured in %.2fs',
124 124 pyramid_app.func_name, total_time)
125 125
126 126 return pyramid_app
127 127
128 128
129 129 def not_found_view(request):
130 130 """
131 131 This creates the view which should be registered as not-found-view to
132 132 pyramid.
133 133 """
134 134
135 135 if not getattr(request, 'vcs_call', None):
136 136 # handle like regular case with our error_handler
137 137 return error_handler(HTTPNotFound(), request)
138 138
139 139 # handle not found view as a vcs call
140 140 settings = request.registry.settings
141 141 ae_client = getattr(request, 'ae_client', None)
142 142 vcs_app = VCSMiddleware(
143 143 HTTPNotFound(), request.registry, settings,
144 144 appenlight_client=ae_client)
145 145
146 146 return wsgiapp(vcs_app)(None, request)
147 147
148 148
149 149 def error_handler(exception, request):
150 150 import rhodecode
151 151 from rhodecode.lib import helpers
152 152
153 153 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
154 154
155 155 base_response = HTTPInternalServerError()
156 156 # prefer original exception for the response since it may have headers set
157 157 if isinstance(exception, HTTPException):
158 158 base_response = exception
159 159 elif isinstance(exception, VCSCommunicationError):
160 160 base_response = VCSServerUnavailable()
161 161
162 162 if is_http_error(base_response):
163 163 log.exception(
164 164 'error occurred handling this request for path: %s', request.path)
165 165
166 166 error_explanation = base_response.explanation or str(base_response)
167 167 if base_response.status_code == 404:
168 168 error_explanation += " Optionally you don't have permission to access this page."
169 169 c = AttributeDict()
170 170 c.error_message = base_response.status
171 171 c.error_explanation = error_explanation
172 172 c.visual = AttributeDict()
173 173
174 174 c.visual.rhodecode_support_url = (
175 175 request.registry.settings.get('rhodecode_support_url') or
176 176 request.route_url('rhodecode_support')
177 177 )
178 178 c.redirect_time = 0
179 179 c.rhodecode_name = rhodecode_title
180 180 if not c.rhodecode_name:
181 181 c.rhodecode_name = 'Rhodecode'
182 182
183 183 c.causes = []
184 184 if is_http_error(base_response):
185 185 c.causes.append('Server is overloaded.')
186 186 c.causes.append('Server database connection is lost.')
187 187 c.causes.append('Server expected unhandled error.')
188 188
189 189 if hasattr(base_response, 'causes'):
190 190 c.causes = base_response.causes
191 191
192 192 c.messages = helpers.flash.pop_messages(request=request)
193 193
194 194 exc_info = sys.exc_info()
195 195 c.exception_id = id(exc_info)
196 196 c.show_exception_id = isinstance(base_response, VCSServerUnavailable) \
197 197 or base_response.status_code > 499
198 198 c.exception_id_url = request.route_url(
199 199 'admin_settings_exception_tracker_show', exception_id=c.exception_id)
200 200
201 201 if c.show_exception_id:
202 202 store_exception(c.exception_id, exc_info)
203 203
204 204 response = render_to_response(
205 205 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
206 206 response=base_response)
207 207
208 208 return response
209 209
210 210
211 211 def includeme_first(config):
212 212 # redirect automatic browser favicon.ico requests to correct place
213 213 def favicon_redirect(context, request):
214 214 return HTTPFound(
215 215 request.static_path('rhodecode:public/images/favicon.ico'))
216 216
217 217 config.add_view(favicon_redirect, route_name='favicon')
218 218 config.add_route('favicon', '/favicon.ico')
219 219
220 220 def robots_redirect(context, request):
221 221 return HTTPFound(
222 222 request.static_path('rhodecode:public/robots.txt'))
223 223
224 224 config.add_view(robots_redirect, route_name='robots')
225 225 config.add_route('robots', '/robots.txt')
226 226
227 227 config.add_static_view(
228 228 '_static/deform', 'deform:static')
229 229 config.add_static_view(
230 230 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
231 231
232 232
233 233 def includeme(config):
234 234 log.debug('Initializing main includeme from %s', os.path.basename(__file__))
235 235 settings = config.registry.settings
236 236 config.set_request_factory(Request)
237 237
238 238 # plugin information
239 239 config.registry.rhodecode_plugins = collections.OrderedDict()
240 240
241 241 config.add_directive(
242 242 'register_rhodecode_plugin', register_rhodecode_plugin)
243 243
244 244 config.add_directive('configure_celery', configure_celery)
245 245
246 246 if asbool(settings.get('appenlight', 'false')):
247 247 config.include('appenlight_client.ext.pyramid_tween')
248 248
249 249 load_all = should_load_all()
250 250
251 251 # Includes which are required. The application would fail without them.
252 252 config.include('pyramid_mako')
253 253 config.include('rhodecode.lib.rc_beaker')
254 254 config.include('rhodecode.lib.rc_cache')
255 255
256 256 config.include('rhodecode.apps._base.navigation')
257 257 config.include('rhodecode.apps._base.subscribers')
258 258 config.include('rhodecode.tweens')
259 259
260 260 config.include('rhodecode.integrations')
261 261 config.include('rhodecode.authentication')
262 262
263 263 if load_all:
264 264 from rhodecode.authentication import discover_legacy_plugins
265 265 # load CE authentication plugins
266 266 config.include('rhodecode.authentication.plugins.auth_crowd')
267 267 config.include('rhodecode.authentication.plugins.auth_headers')
268 268 config.include('rhodecode.authentication.plugins.auth_jasig_cas')
269 269 config.include('rhodecode.authentication.plugins.auth_ldap')
270 270 config.include('rhodecode.authentication.plugins.auth_pam')
271 271 config.include('rhodecode.authentication.plugins.auth_rhodecode')
272 272 config.include('rhodecode.authentication.plugins.auth_token')
273 273
274 274 # Auto discover authentication plugins and include their configuration.
275 275 discover_legacy_plugins(config)
276 276
277 277 # apps
278 278 config.include('rhodecode.apps._base')
279 279
280 280 if load_all:
281 281 config.include('rhodecode.apps.ops')
282 282 config.include('rhodecode.apps.admin')
283 283 config.include('rhodecode.apps.channelstream')
284 284 config.include('rhodecode.apps.file_store')
285 285 config.include('rhodecode.apps.login')
286 286 config.include('rhodecode.apps.home')
287 287 config.include('rhodecode.apps.journal')
288 288 config.include('rhodecode.apps.repository')
289 289 config.include('rhodecode.apps.repo_group')
290 290 config.include('rhodecode.apps.user_group')
291 291 config.include('rhodecode.apps.search')
292 292 config.include('rhodecode.apps.user_profile')
293 293 config.include('rhodecode.apps.user_group_profile')
294 294 config.include('rhodecode.apps.my_account')
295 295 config.include('rhodecode.apps.svn_support')
296 296 config.include('rhodecode.apps.ssh_support')
297 297 config.include('rhodecode.apps.gist')
298 298 config.include('rhodecode.apps.debug_style')
299 299 config.include('rhodecode.api')
300 300
301 301 config.add_route('rhodecode_support', 'https://rhodecode.com/help/', static=True)
302 302 config.add_translation_dirs('rhodecode:i18n/')
303 303 settings['default_locale_name'] = settings.get('lang', 'en')
304 304
305 305 # Add subscribers.
306 306 config.add_subscriber(inject_app_settings,
307 307 pyramid.events.ApplicationCreated)
308 308 config.add_subscriber(scan_repositories_if_enabled,
309 309 pyramid.events.ApplicationCreated)
310 310 config.add_subscriber(write_metadata_if_needed,
311 311 pyramid.events.ApplicationCreated)
312 312 config.add_subscriber(write_js_routes_if_enabled,
313 313 pyramid.events.ApplicationCreated)
314 314
315 315 # request custom methods
316 316 config.add_request_method(
317 317 'rhodecode.lib.partial_renderer.get_partial_renderer',
318 318 'get_partial_renderer')
319 319
320 config.add_request_method(
321 'rhodecode.lib.request_counter.get_request_counter',
322 'request_count')
323
320 324 # Set the authorization policy.
321 325 authz_policy = ACLAuthorizationPolicy()
322 326 config.set_authorization_policy(authz_policy)
323 327
324 328 # Set the default renderer for HTML templates to mako.
325 329 config.add_mako_renderer('.html')
326 330
327 331 config.add_renderer(
328 332 name='json_ext',
329 333 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
330 334
331 335 # include RhodeCode plugins
332 336 includes = aslist(settings.get('rhodecode.includes', []))
333 337 for inc in includes:
334 338 config.include(inc)
335 339
336 340 # custom not found view, if our pyramid app doesn't know how to handle
337 341 # the request pass it to potential VCS handling ap
338 342 config.add_notfound_view(not_found_view)
339 343 if not settings.get('debugtoolbar.enabled', False):
340 344 # disabled debugtoolbar handle all exceptions via the error_handlers
341 345 config.add_view(error_handler, context=Exception)
342 346
343 347 # all errors including 403/404/50X
344 348 config.add_view(error_handler, context=HTTPError)
345 349
346 350
347 351 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
348 352 """
349 353 Apply outer WSGI middlewares around the application.
350 354 """
351 355 registry = config.registry
352 356 settings = registry.settings
353 357
354 358 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
355 359 pyramid_app = HttpsFixup(pyramid_app, settings)
356 360
357 361 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
358 362 pyramid_app, settings)
359 363 registry.ae_client = _ae_client
360 364
361 365 if settings['gzip_responses']:
362 366 pyramid_app = make_gzip_middleware(
363 367 pyramid_app, settings, compress_level=1)
364 368
365 369 # this should be the outer most middleware in the wsgi stack since
366 370 # middleware like Routes make database calls
367 371 def pyramid_app_with_cleanup(environ, start_response):
368 372 try:
369 373 return pyramid_app(environ, start_response)
370 374 finally:
371 375 # Dispose current database session and rollback uncommitted
372 376 # transactions.
373 377 meta.Session.remove()
374 378
375 379 # In a single threaded mode server, on non sqlite db we should have
376 380 # '0 Current Checked out connections' at the end of a request,
377 381 # if not, then something, somewhere is leaving a connection open
378 382 pool = meta.Base.metadata.bind.engine.pool
379 383 log.debug('sa pool status: %s', pool.status())
380 384 log.debug('Request processing finalized')
381 385
382 386 return pyramid_app_with_cleanup
383 387
384 388
385 389 def sanitize_settings_and_apply_defaults(global_config, settings):
386 390 """
387 391 Applies settings defaults and does all type conversion.
388 392
389 393 We would move all settings parsing and preparation into this place, so that
390 394 we have only one place left which deals with this part. The remaining parts
391 395 of the application would start to rely fully on well prepared settings.
392 396
393 397 This piece would later be split up per topic to avoid a big fat monster
394 398 function.
395 399 """
396 400
397 401 settings.setdefault('rhodecode.edition', 'Community Edition')
398 402
399 403 if 'mako.default_filters' not in settings:
400 404 # set custom default filters if we don't have it defined
401 405 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
402 406 settings['mako.default_filters'] = 'h_filter'
403 407
404 408 if 'mako.directories' not in settings:
405 409 mako_directories = settings.setdefault('mako.directories', [
406 410 # Base templates of the original application
407 411 'rhodecode:templates',
408 412 ])
409 413 log.debug(
410 414 "Using the following Mako template directories: %s",
411 415 mako_directories)
412 416
413 417 # NOTE(marcink): fix redis requirement for schema of connection since 3.X
414 418 if 'beaker.session.type' in settings and settings['beaker.session.type'] == 'ext:redis':
415 419 raw_url = settings['beaker.session.url']
416 420 if not raw_url.startswith(('redis://', 'rediss://', 'unix://')):
417 421 settings['beaker.session.url'] = 'redis://' + raw_url
418 422
419 423 # Default includes, possible to change as a user
420 424 pyramid_includes = settings.setdefault('pyramid.includes', [
421 425 'rhodecode.lib.middleware.request_wrapper',
422 426 ])
423 427 log.debug(
424 428 "Using the following pyramid.includes: %s",
425 429 pyramid_includes)
426 430
427 431 # TODO: johbo: Re-think this, usually the call to config.include
428 432 # should allow to pass in a prefix.
429 433 settings.setdefault('rhodecode.api.url', '/_admin/api')
430 434 settings.setdefault('__file__', global_config.get('__file__'))
431 435
432 436 # Sanitize generic settings.
433 437 _list_setting(settings, 'default_encoding', 'UTF-8')
434 438 _bool_setting(settings, 'is_test', 'false')
435 439 _bool_setting(settings, 'gzip_responses', 'false')
436 440
437 441 # Call split out functions that sanitize settings for each topic.
438 442 _sanitize_appenlight_settings(settings)
439 443 _sanitize_vcs_settings(settings)
440 444 _sanitize_cache_settings(settings)
441 445
442 446 # configure instance id
443 447 config_utils.set_instance_id(settings)
444 448
445 449 return settings
446 450
447 451
448 452 def enable_debug():
449 453 """
450 454 Helper to enable debug on running instance
451 455 :return:
452 456 """
453 457 import tempfile
454 458 import textwrap
455 459 import logging.config
456 460
457 461 ini_template = textwrap.dedent("""
458 462 #####################################
459 463 ### DEBUG LOGGING CONFIGURATION ####
460 464 #####################################
461 465 [loggers]
462 466 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper
463 467
464 468 [handlers]
465 469 keys = console, console_sql
466 470
467 471 [formatters]
468 472 keys = generic, color_formatter, color_formatter_sql
469 473
470 474 #############
471 475 ## LOGGERS ##
472 476 #############
473 477 [logger_root]
474 478 level = NOTSET
475 479 handlers = console
476 480
477 481 [logger_sqlalchemy]
478 482 level = INFO
479 483 handlers = console_sql
480 484 qualname = sqlalchemy.engine
481 485 propagate = 0
482 486
483 487 [logger_beaker]
484 488 level = DEBUG
485 489 handlers =
486 490 qualname = beaker.container
487 491 propagate = 1
488 492
489 493 [logger_rhodecode]
490 494 level = DEBUG
491 495 handlers =
492 496 qualname = rhodecode
493 497 propagate = 1
494 498
495 499 [logger_ssh_wrapper]
496 500 level = DEBUG
497 501 handlers =
498 502 qualname = ssh_wrapper
499 503 propagate = 1
500 504
501 505 [logger_celery]
502 506 level = DEBUG
503 507 handlers =
504 508 qualname = celery
505 509
506 510
507 511 ##############
508 512 ## HANDLERS ##
509 513 ##############
510 514
511 515 [handler_console]
512 516 class = StreamHandler
513 517 args = (sys.stderr, )
514 518 level = DEBUG
515 519 formatter = color_formatter
516 520
517 521 [handler_console_sql]
518 522 # "level = DEBUG" logs SQL queries and results.
519 523 # "level = INFO" logs SQL queries.
520 524 # "level = WARN" logs neither. (Recommended for production systems.)
521 525 class = StreamHandler
522 526 args = (sys.stderr, )
523 527 level = WARN
524 528 formatter = color_formatter_sql
525 529
526 530 ################
527 531 ## FORMATTERS ##
528 532 ################
529 533
530 534 [formatter_generic]
531 535 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
532 536 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
533 537 datefmt = %Y-%m-%d %H:%M:%S
534 538
535 539 [formatter_color_formatter]
536 540 class = rhodecode.lib.logging_formatter.ColorRequestTrackingFormatter
537 541 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
538 542 datefmt = %Y-%m-%d %H:%M:%S
539 543
540 544 [formatter_color_formatter_sql]
541 545 class = rhodecode.lib.logging_formatter.ColorFormatterSql
542 546 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
543 547 datefmt = %Y-%m-%d %H:%M:%S
544 548 """)
545 549
546 550 with tempfile.NamedTemporaryFile(prefix='rc_debug_logging_', suffix='.ini',
547 551 delete=False) as f:
548 552 log.info('Saved Temporary DEBUG config at %s', f.name)
549 553 f.write(ini_template)
550 554
551 555 logging.config.fileConfig(f.name)
552 556 log.debug('DEBUG MODE ON')
553 557 os.remove(f.name)
554 558
555 559
556 560 def _sanitize_appenlight_settings(settings):
557 561 _bool_setting(settings, 'appenlight', 'false')
558 562
559 563
560 564 def _sanitize_vcs_settings(settings):
561 565 """
562 566 Applies settings defaults and does type conversion for all VCS related
563 567 settings.
564 568 """
565 569 _string_setting(settings, 'vcs.svn.compatible_version', '')
566 570 _string_setting(settings, 'git_rev_filter', '--all')
567 571 _string_setting(settings, 'vcs.hooks.protocol', 'http')
568 572 _string_setting(settings, 'vcs.hooks.host', '127.0.0.1')
569 573 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
570 574 _string_setting(settings, 'vcs.server', '')
571 575 _string_setting(settings, 'vcs.server.log_level', 'debug')
572 576 _string_setting(settings, 'vcs.server.protocol', 'http')
573 577 _bool_setting(settings, 'startup.import_repos', 'false')
574 578 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
575 579 _bool_setting(settings, 'vcs.server.enable', 'true')
576 580 _bool_setting(settings, 'vcs.start_server', 'false')
577 581 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
578 582 _int_setting(settings, 'vcs.connection_timeout', 3600)
579 583
580 584 # Support legacy values of vcs.scm_app_implementation. Legacy
581 585 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or
582 586 # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'.
583 587 scm_app_impl = settings['vcs.scm_app_implementation']
584 588 if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']:
585 589 settings['vcs.scm_app_implementation'] = 'http'
586 590
587 591
588 592 def _sanitize_cache_settings(settings):
589 593 temp_store = tempfile.gettempdir()
590 594 default_cache_dir = os.path.join(temp_store, 'rc_cache')
591 595
592 596 # save default, cache dir, and use it for all backends later.
593 597 default_cache_dir = _string_setting(
594 598 settings,
595 599 'cache_dir',
596 600 default_cache_dir, lower=False, default_when_empty=True)
597 601
598 602 # ensure we have our dir created
599 603 if not os.path.isdir(default_cache_dir):
600 604 os.makedirs(default_cache_dir, mode=0o755)
601 605
602 606 # exception store cache
603 607 _string_setting(
604 608 settings,
605 609 'exception_tracker.store_path',
606 610 temp_store, lower=False, default_when_empty=True)
607 611
608 612 # cache_perms
609 613 _string_setting(
610 614 settings,
611 615 'rc_cache.cache_perms.backend',
612 616 'dogpile.cache.rc.file_namespace', lower=False)
613 617 _int_setting(
614 618 settings,
615 619 'rc_cache.cache_perms.expiration_time',
616 620 60)
617 621 _string_setting(
618 622 settings,
619 623 'rc_cache.cache_perms.arguments.filename',
620 624 os.path.join(default_cache_dir, 'rc_cache_1'), lower=False)
621 625
622 626 # cache_repo
623 627 _string_setting(
624 628 settings,
625 629 'rc_cache.cache_repo.backend',
626 630 'dogpile.cache.rc.file_namespace', lower=False)
627 631 _int_setting(
628 632 settings,
629 633 'rc_cache.cache_repo.expiration_time',
630 634 60)
631 635 _string_setting(
632 636 settings,
633 637 'rc_cache.cache_repo.arguments.filename',
634 638 os.path.join(default_cache_dir, 'rc_cache_2'), lower=False)
635 639
636 640 # cache_license
637 641 _string_setting(
638 642 settings,
639 643 'rc_cache.cache_license.backend',
640 644 'dogpile.cache.rc.file_namespace', lower=False)
641 645 _int_setting(
642 646 settings,
643 647 'rc_cache.cache_license.expiration_time',
644 648 5*60)
645 649 _string_setting(
646 650 settings,
647 651 'rc_cache.cache_license.arguments.filename',
648 652 os.path.join(default_cache_dir, 'rc_cache_3'), lower=False)
649 653
650 654 # cache_repo_longterm memory, 96H
651 655 _string_setting(
652 656 settings,
653 657 'rc_cache.cache_repo_longterm.backend',
654 658 'dogpile.cache.rc.memory_lru', lower=False)
655 659 _int_setting(
656 660 settings,
657 661 'rc_cache.cache_repo_longterm.expiration_time',
658 662 345600)
659 663 _int_setting(
660 664 settings,
661 665 'rc_cache.cache_repo_longterm.max_size',
662 666 10000)
663 667
664 668 # sql_cache_short
665 669 _string_setting(
666 670 settings,
667 671 'rc_cache.sql_cache_short.backend',
668 672 'dogpile.cache.rc.memory_lru', lower=False)
669 673 _int_setting(
670 674 settings,
671 675 'rc_cache.sql_cache_short.expiration_time',
672 676 30)
673 677 _int_setting(
674 678 settings,
675 679 'rc_cache.sql_cache_short.max_size',
676 680 10000)
677 681
678 682
679 683 def _int_setting(settings, name, default):
680 684 settings[name] = int(settings.get(name, default))
681 685 return settings[name]
682 686
683 687
684 688 def _bool_setting(settings, name, default):
685 689 input_val = settings.get(name, default)
686 690 if isinstance(input_val, unicode):
687 691 input_val = input_val.encode('utf8')
688 692 settings[name] = asbool(input_val)
689 693 return settings[name]
690 694
691 695
692 696 def _list_setting(settings, name, default):
693 697 raw_value = settings.get(name, default)
694 698
695 699 old_separator = ','
696 700 if old_separator in raw_value:
697 701 # If we get a comma separated list, pass it to our own function.
698 702 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
699 703 else:
700 704 # Otherwise we assume it uses pyramids space/newline separation.
701 705 settings[name] = aslist(raw_value)
702 706 return settings[name]
703 707
704 708
705 709 def _string_setting(settings, name, default, lower=True, default_when_empty=False):
706 710 value = settings.get(name, default)
707 711
708 712 if default_when_empty and not value:
709 713 # use default value when value is empty
710 714 value = default
711 715
712 716 if lower:
713 717 value = value.lower()
714 718 settings[name] = value
715 719 return settings[name]
716 720
717 721
718 722 def _substitute_values(mapping, substitutions):
719 723 result = {}
720 724
721 725 try:
722 726 for key, value in mapping.items():
723 727 # initialize without substitution first
724 728 result[key] = value
725 729
726 730 # Note: Cannot use regular replacements, since they would clash
727 731 # with the implementation of ConfigParser. Using "format" instead.
728 732 try:
729 733 result[key] = value.format(**substitutions)
730 734 except KeyError as e:
731 735 env_var = '{}'.format(e.args[0])
732 736
733 737 msg = 'Failed to substitute: `{key}={{{var}}}` with environment entry. ' \
734 738 'Make sure your environment has {var} set, or remove this ' \
735 739 'variable from config file'.format(key=key, var=env_var)
736 740
737 741 if env_var.startswith('ENV_'):
738 742 raise ValueError(msg)
739 743 else:
740 744 log.warning(msg)
741 745
742 746 except ValueError as e:
743 747 log.warning('Failed to substitute ENV variable: %s', e)
744 748 result = mapping
745 749
746 750 return result
@@ -1,60 +1,60 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21
21 import gc
22 22 import time
23 23 import logging
24 24
25
26 25 from rhodecode.lib.base import get_ip_addr, get_access_path, get_user_agent
27 26 from rhodecode.lib.utils2 import safe_str
28 27
29 28
30 29 log = logging.getLogger(__name__)
31 30
32 31
33 32 class RequestWrapperTween(object):
34 33 def __init__(self, handler, registry):
35 34 self.handler = handler
36 35 self.registry = registry
37 36
38 37 # one-time configuration code goes here
39 38
40 39 def __call__(self, request):
41 40 start = time.time()
42 41 try:
43 42 response = self.handler(request)
44 43 finally:
45 44 end = time.time()
46 45 total = end - start
46 count = request.request_count()
47 47 log.info(
48 'IP: %s %s Request to %s time: %.4fs [%s]',
49 get_ip_addr(request.environ), request.environ.get('REQUEST_METHOD'),
48 'Req[%4s] IP: %s %s Request to %s time: %.4fs [%s]',
49 count, get_ip_addr(request.environ), request.environ.get('REQUEST_METHOD'),
50 50 safe_str(get_access_path(request.environ)), total,
51 51 get_user_agent(request. environ)
52 52 )
53 53
54 54 return response
55 55
56 56
57 57 def includeme(config):
58 58 config.add_tween(
59 59 'rhodecode.lib.middleware.request_wrapper.RequestWrapperTween',
60 60 )
General Comments 0
You need to be logged in to leave comments. Login now