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