~launchpad-pqm/launchpad/devel

10637.3.15 by Guilherme Salgado
update the shebang lines of a few scripts that still had python2.5 hard-coded
1
#!/usr/bin/python -S
9590.1.135 by Michael Hudson
add files from launchpad-loggerhead tree to launchpad tree
2
#
3
# Copyright 2009, 2010 Canonical Ltd.  This software is licensed under the
4
# GNU Affero General Public License version 3 (see the file LICENSE).
5
6
import _pythonpath
7
8
import logging
9
import os
10
import sys
11
12
from paste import httpserver
13
from paste.deploy.config import PrefixMiddleware
14
from paste.httpexceptions import HTTPExceptionHandler
15
from paste.request import construct_url
16
from paste.translogger import TransLogger
17
18
from canonical.config import config
19
import lp.codehosting
20
21
LISTEN_HOST = '0.0.0.0'
22
LISTEN_PORT = 8080
23
THREADPOOL_WORKERS = 10
24
25
26
class NoLockingFileHandler(logging.FileHandler):
27
    """A version of logging.FileHandler that doesn't do it's own locking.
28
29
    We experienced occasional hangs in production where gdb-ery on the server
30
    revealed that we sometimes end up with many threads blocking on the RLock
31
    held by the logging file handler, and log reading finds that an exception
32
    managed to kill a thread in an unsafe window for RLock's.
33
34
    Luckily, there's no real reason for us to take a lock during logging as
35
    each log message translates to one call to .write on a file object, which
36
    translates to one fwrite call, and it seems that this does enough locking
37
    itself for our purposes.
38
39
    So this handler just doesn't lock in log message handling.
40
    """
41
42
    def acquire(self):
43
        pass
44
45
    def release(self):
46
        pass
47
48
49
def setup_logging(home, foreground):
50
    # i hate that stupid logging config format, so just set up logging here.
51
52
    log_folder = config.codebrowse.log_folder
53
    if not log_folder:
54
        log_folder = os.path.join(home, 'logs')
55
    if not os.path.exists(log_folder):
56
        os.mkdir(log_folder)
57
58
    f = logging.Formatter(
59
        '%(levelname)-.3s [%(asctime)s.%(msecs)03d] [%(thread)d] %(name)s: %(message)s',
60
        '%Y%m%d-%H:%M:%S')
61
    debug_log = NoLockingFileHandler(os.path.join(log_folder, 'debug.log'))
62
    debug_log.setLevel(logging.DEBUG)
63
    debug_log.setFormatter(f)
64
    if foreground:
65
        stdout_log = logging.StreamHandler(sys.stdout)
66
        stdout_log.setLevel(logging.DEBUG)
67
        stdout_log.setFormatter(f)
68
    f = logging.Formatter('[%(asctime)s.%(msecs)03d] %(message)s',
69
                          '%Y%m%d-%H:%M:%S')
70
    access_log = NoLockingFileHandler(os.path.join(log_folder, 'access.log'))
71
    access_log.setLevel(logging.INFO)
72
    access_log.setFormatter(f)
73
74
    logging.getLogger('').setLevel(logging.DEBUG)
75
    logging.getLogger('').addHandler(debug_log)
76
    logging.getLogger('wsgi').addHandler(access_log)
77
78
    if foreground:
79
        logging.getLogger('').addHandler(stdout_log)
80
    else:
81
        class S(object):
82
            def write(self, str):
83
                logging.getLogger().error(str.rstrip('\n'))
84
            def flush(self):
85
                pass
86
        sys.stderr = S()
87
88
89
90
foreground = False
91
if len(sys.argv) > 1:
92
    if sys.argv[1] == '-f':
93
        foreground = True
94
95
home = os.path.realpath(os.path.dirname(__file__))
96
pidfile = os.path.join(home, 'loggerhead.pid')
97
98
if not foreground:
99
    sys.stderr.write('\n')
100
    sys.stderr.write('Launching loggerhead into the background.\n')
101
    sys.stderr.write('PID file: %s\n' % (pidfile,))
102
    sys.stderr.write('\n')
103
104
    from loggerhead.daemon import daemonize
105
    daemonize(pidfile, home)
106
107
setup_logging(home, foreground=foreground)
108
109
log = logging.getLogger('loggerhead')
110
log.info('Starting up...')
111
112
log.info('Loading the bzr plugins...')
113
from bzrlib.plugin import load_plugins
114
load_plugins()
115
116
import bzrlib.plugins
117
if getattr(bzrlib.plugins, 'loom', None) is None:
118
    log.error('Loom plugin loading failed.')
119
120
from launchpad_loggerhead.debug import (
121
    change_kill_thread_criteria, threadpool_debug)
122
from launchpad_loggerhead.app import RootApp
123
from launchpad_loggerhead.session import SessionHandler
124
125
SESSION_VAR = 'lh.session'
126
127
secret = open(os.path.join(config.root, config.codebrowse.secret_path)).read()
128
129
app = RootApp(SESSION_VAR)
130
app = HTTPExceptionHandler(app)
131
app = SessionHandler(app, SESSION_VAR, secret)
132
def log_on_request_start(app):
133
    def wrapped(environ, start_response):
134
        log = logging.getLogger('loggerhead')
135
        log.info("Starting to process %s", construct_url(environ))
136
        return app(environ, start_response)
137
    return wrapped
138
app = log_on_request_start(app)
139
app = PrefixMiddleware(app)
140
app = TransLogger(app)
141
app = threadpool_debug(app)
142
143
def set_scheme(app):
144
    """Set wsgi.url_scheme in the environment correctly.
145
146
    We serve requests that originated from both http and https, and
147
    distinguish between them by adding a header in the https Apache config.
148
    """
149
    def wrapped(environ, start_response):
150
        environ['wsgi.url_scheme'] = environ.pop(
151
            'HTTP_X_FORWARDED_SCHEME', 'http')
152
        return app(environ, start_response)
153
    return wrapped
154
app = set_scheme(app)
155
app = change_kill_thread_criteria(app)
156
157
try:
158
    httpserver.serve(
159
        app, host=LISTEN_HOST, port=LISTEN_PORT,
160
        threadpool_workers=THREADPOOL_WORKERS,
161
        threadpool_options={
162
            # Kill threads after 300 seconds.  This is insanely high, but
163
            # lower enough than the default (1800 seconds!) that evidence
164
            # suggests it will be hit occasionally, and there's very little
165
            # chance of it having negative consequences.
166
            'kill_thread_limit': 300,
167
            # Check for threads that should be killed every 10 requests.  The
168
            # default is every 100, which is easily long enough for things to
169
            # gum up completely in between checks.
170
            'hung_check_period': 10,
171
            })
172
finally:
173
    log.info('Shutdown.')
174
    try:
175
        os.remove(pidfile)
176
    except OSError:
177
        pass