##// END OF EJS Templates
gunicorn: fixed imports for configparser
marcink -
r984:107d305d python3
parent child Browse files
Show More
@@ -1,265 +1,262 b''
1 1 """
2 2 Gunicorn config extension and hooks. This config file adds some extra settings and memory management.
3 3 Gunicorn configuration should be managed by .ini files entries of RhodeCode or VCSServer
4 4 """
5 5
6 6 import gc
7 7 import os
8 8 import sys
9 9 import math
10 10 import time
11 11 import threading
12 12 import traceback
13 13 import random
14 14 from gunicorn.glogging import Logger
15 15
16 16
17 17 def get_workers():
18 18 import multiprocessing
19 19 return multiprocessing.cpu_count() * 2 + 1
20 20
21 21 # GLOBAL
22 22 errorlog = '-'
23 23 accesslog = '-'
24 24
25 25
26 26 # SERVER MECHANICS
27 27 # None == system temp dir
28 28 # worker_tmp_dir is recommended to be set to some tmpfs
29 29 worker_tmp_dir = None
30 30 tmp_upload_dir = None
31 31
32 32 # Custom log format
33 33 access_log_format = (
34 34 '%(t)s %(p)s INFO [GNCRN] %(h)-15s rqt:%(L)s %(s)s %(b)-6s "%(m)s:%(U)s %(q)s" usr:%(u)s "%(f)s" "%(a)s"')
35 35
36 36 # self adjust workers based on CPU count
37 37 # workers = get_workers()
38 38
39 39
40 40 def _get_process_rss(pid=None):
41 41 try:
42 42 import psutil
43 43 if pid:
44 44 proc = psutil.Process(pid)
45 45 else:
46 46 proc = psutil.Process()
47 47 return proc.memory_info().rss
48 48 except Exception:
49 49 return None
50 50
51 51
52 52 def _get_config(ini_path):
53 import configparser
53 54
54 55 try:
55 import configparser
56 except ImportError:
57 import ConfigParser as configparser
58 try:
59 56 config = configparser.RawConfigParser()
60 57 config.read(ini_path)
61 58 return config
62 59 except Exception:
63 60 return None
64 61
65 62
66 63 def _time_with_offset(memory_usage_check_interval):
67 64 return time.time() - random.randint(0, memory_usage_check_interval/2.0)
68 65
69 66
70 67 def pre_fork(server, worker):
71 68 pass
72 69
73 70
74 71 def post_fork(server, worker):
75 72
76 73 # memory spec defaults
77 74 _memory_max_usage = 0
78 75 _memory_usage_check_interval = 60
79 76 _memory_usage_recovery_threshold = 0.8
80 77
81 78 ini_path = os.path.abspath(server.cfg.paste)
82 79 conf = _get_config(ini_path)
83 80
84 81 section = 'server:main'
85 82 if conf and conf.has_section(section):
86 83
87 84 if conf.has_option(section, 'memory_max_usage'):
88 85 _memory_max_usage = conf.getint(section, 'memory_max_usage')
89 86
90 87 if conf.has_option(section, 'memory_usage_check_interval'):
91 88 _memory_usage_check_interval = conf.getint(section, 'memory_usage_check_interval')
92 89
93 90 if conf.has_option(section, 'memory_usage_recovery_threshold'):
94 91 _memory_usage_recovery_threshold = conf.getfloat(section, 'memory_usage_recovery_threshold')
95 92
96 93 worker._memory_max_usage = _memory_max_usage
97 94 worker._memory_usage_check_interval = _memory_usage_check_interval
98 95 worker._memory_usage_recovery_threshold = _memory_usage_recovery_threshold
99 96
100 97 # register memory last check time, with some random offset so we don't recycle all
101 98 # at once
102 99 worker._last_memory_check_time = _time_with_offset(_memory_usage_check_interval)
103 100
104 101 if _memory_max_usage:
105 102 server.log.info("[%-10s] WORKER spawned with max memory set at %s", worker.pid,
106 103 _format_data_size(_memory_max_usage))
107 104 else:
108 105 server.log.info("[%-10s] WORKER spawned", worker.pid)
109 106
110 107
111 108 def pre_exec(server):
112 109 server.log.info("Forked child, re-executing.")
113 110
114 111
115 112 def on_starting(server):
116 113 server_lbl = '{} {}'.format(server.proc_name, server.address)
117 114 server.log.info("Server %s is starting.", server_lbl)
118 115
119 116
120 117 def when_ready(server):
121 118 server.log.info("Server %s is ready. Spawning workers", server)
122 119
123 120
124 121 def on_reload(server):
125 122 pass
126 123
127 124
128 125 def _format_data_size(size, unit="B", precision=1, binary=True):
129 126 """Format a number using SI units (kilo, mega, etc.).
130 127
131 128 ``size``: The number as a float or int.
132 129
133 130 ``unit``: The unit name in plural form. Examples: "bytes", "B".
134 131
135 132 ``precision``: How many digits to the right of the decimal point. Default
136 133 is 1. 0 suppresses the decimal point.
137 134
138 135 ``binary``: If false, use base-10 decimal prefixes (kilo = K = 1000).
139 136 If true, use base-2 binary prefixes (kibi = Ki = 1024).
140 137
141 138 ``full_name``: If false (default), use the prefix abbreviation ("k" or
142 139 "Ki"). If true, use the full prefix ("kilo" or "kibi"). If false,
143 140 use abbreviation ("k" or "Ki").
144 141
145 142 """
146 143
147 144 if not binary:
148 145 base = 1000
149 146 multiples = ('', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
150 147 else:
151 148 base = 1024
152 149 multiples = ('', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi')
153 150
154 151 sign = ""
155 152 if size > 0:
156 153 m = int(math.log(size, base))
157 154 elif size < 0:
158 155 sign = "-"
159 156 size = -size
160 157 m = int(math.log(size, base))
161 158 else:
162 159 m = 0
163 160 if m > 8:
164 161 m = 8
165 162
166 163 if m == 0:
167 164 precision = '%.0f'
168 165 else:
169 166 precision = '%%.%df' % precision
170 167
171 168 size = precision % (size / math.pow(base, m))
172 169
173 170 return '%s%s %s%s' % (sign, size.strip(), multiples[m], unit)
174 171
175 172
176 173 def _check_memory_usage(worker):
177 174 memory_max_usage = worker._memory_max_usage
178 175 if not memory_max_usage:
179 176 return
180 177
181 178 memory_usage_check_interval = worker._memory_usage_check_interval
182 179 memory_usage_recovery_threshold = memory_max_usage * worker._memory_usage_recovery_threshold
183 180
184 181 elapsed = time.time() - worker._last_memory_check_time
185 182 if elapsed > memory_usage_check_interval:
186 183 mem_usage = _get_process_rss()
187 184 if mem_usage and mem_usage > memory_max_usage:
188 185 worker.log.info(
189 186 "memory usage %s > %s, forcing gc",
190 187 _format_data_size(mem_usage), _format_data_size(memory_max_usage))
191 188 # Try to clean it up by forcing a full collection.
192 189 gc.collect()
193 190 mem_usage = _get_process_rss()
194 191 if mem_usage > memory_usage_recovery_threshold:
195 192 # Didn't clean up enough, we'll have to terminate.
196 193 worker.log.warning(
197 194 "memory usage %s > %s after gc, quitting",
198 195 _format_data_size(mem_usage), _format_data_size(memory_max_usage))
199 196 # This will cause worker to auto-restart itself
200 197 worker.alive = False
201 198 worker._last_memory_check_time = time.time()
202 199
203 200
204 201 def worker_int(worker):
205 202 worker.log.info("[%-10s] worker received INT or QUIT signal", worker.pid)
206 203
207 204 # get traceback info, on worker crash
208 205 id2name = dict([(th.ident, th.name) for th in threading.enumerate()])
209 206 code = []
210 207 for thread_id, stack in sys._current_frames().items():
211 208 code.append(
212 209 "\n# Thread: %s(%d)" % (id2name.get(thread_id, ""), thread_id))
213 210 for fname, lineno, name, line in traceback.extract_stack(stack):
214 211 code.append('File: "%s", line %d, in %s' % (fname, lineno, name))
215 212 if line:
216 213 code.append(" %s" % (line.strip()))
217 214 worker.log.debug("\n".join(code))
218 215
219 216
220 217 def worker_abort(worker):
221 218 worker.log.info("[%-10s] worker received SIGABRT signal", worker.pid)
222 219
223 220
224 221 def worker_exit(server, worker):
225 222 worker.log.info("[%-10s] worker exit", worker.pid)
226 223
227 224
228 225 def child_exit(server, worker):
229 226 worker.log.info("[%-10s] worker child exit", worker.pid)
230 227
231 228
232 229 def pre_request(worker, req):
233 230 worker.start_time = time.time()
234 231 worker.log.debug(
235 232 "GNCRN PRE WORKER [cnt:%s]: %s %s", worker.nr, req.method, req.path)
236 233
237 234
238 235 def post_request(worker, req, environ, resp):
239 236 total_time = time.time() - worker.start_time
240 237 # Gunicorn sometimes has problems with reading the status_code
241 238 status_code = getattr(resp, 'status_code', '')
242 239 worker.log.debug(
243 240 "GNCRN POST WORKER [cnt:%s]: %s %s resp: %s, Load Time: %.4fs",
244 241 worker.nr, req.method, req.path, status_code, total_time)
245 242 _check_memory_usage(worker)
246 243
247 244
248 245 class RhodeCodeLogger(Logger):
249 246 """
250 247 Custom Logger that allows some customization that gunicorn doesn't allow
251 248 """
252 249
253 250 datefmt = r"%Y-%m-%d %H:%M:%S"
254 251
255 252 def __init__(self, cfg):
256 253 Logger.__init__(self, cfg)
257 254
258 255 def now(self):
259 256 """ return date in RhodeCode Log format """
260 257 now = time.time()
261 258 msecs = int((now - long(now)) * 1000)
262 259 return time.strftime(self.datefmt, time.localtime(now)) + '.{0:03d}'.format(msecs)
263 260
264 261
265 262 logger_class = RhodeCodeLogger
General Comments 0
You need to be logged in to leave comments. Login now