0001-misc-remove-statsd-support-37587.patch
help/fr/settings-monitoring.page | ||
---|---|---|
1 |
<page xmlns="http://projectmallard.org/1.0/" |
|
2 |
type="topic" id="settings-monitoring" xml:lang="fr"> |
|
3 | ||
4 |
<info> |
|
5 |
<link type="guide" xref="index#system-settings" /> |
|
6 |
<revision docversion="0.1" date="2013-07-06" status="draft"/> |
|
7 |
<credit type="author"> |
|
8 |
<name>Frédéric Péters</name> |
|
9 |
<email>fpeters@entrouvert.com</email> |
|
10 |
</credit> |
|
11 |
<desc>Intégration avec un serveur StatsD</desc> |
|
12 |
</info> |
|
13 | ||
14 |
<title>Surveillance et suivi applicatif</title> |
|
15 | ||
16 |
<section id="statsd"> |
|
17 |
<title>StatsD</title> |
|
18 | ||
19 |
<p> |
|
20 |
<app>w.c.s.</app> peut communiquer à un serveur <app>StatsD</app> une série |
|
21 |
d'informations concernant le traitement des requêtes HTTP et |
|
22 |
l'authentification des utilisateurs. |
|
23 |
</p> |
|
24 | ||
25 |
<p> |
|
26 |
L'adresse du serveur <app>StatsD</app> est récupérée d'une variable |
|
27 |
d'environnement nommée <var>QOMMON_STATSD_HOSTNAME</var> qui sera |
|
28 |
communément positionnée au niveau du serveur Web; par défaut |
|
29 |
<var>localhost</var> sera utilisé. |
|
30 |
</p> |
|
31 | ||
32 |
<listing> |
|
33 |
<title>Exemple de configuration Apache</title> |
|
34 |
<code>SetEnv QOMMON_STATSD_HOSTNAME statsd.example.net</code> |
|
35 |
</listing> |
|
36 | ||
37 |
<p> |
|
38 |
Les clés positionnées sont de la forme |
|
39 |
<code>wcs.<var>domaine</var>.<var>clé</var></code>, les points présents dans le |
|
40 |
nom de domaine remplacés par des tirets; par exemple |
|
41 |
<code>wcs.teleservices-example-net.login-error</code>. |
|
42 |
</p> |
|
43 | ||
44 |
<p> |
|
45 |
Les informations suivantes sont communiquées : |
|
46 |
</p> |
|
47 | ||
48 |
<table shade="rows"> |
|
49 |
<tr> |
|
50 |
<td><p><code>login-submit</code></p></td> |
|
51 |
<td><p>Compteur incrémenté lors de la validation d'une page |
|
52 |
d'authentification</p></td> |
|
53 |
</tr> |
|
54 |
<tr> |
|
55 |
<td><p><code>login-error</code></p></td> |
|
56 |
<td><p>Compteur incrémenté lors d'une tentative d'authentification |
|
57 |
infructueuse.</p></td> |
|
58 |
</tr> |
|
59 |
<tr> |
|
60 |
<td><p><code>login-success</code></p></td> |
|
61 |
<td><p>Compteur incrémenté lors d'une tentative d'authentification |
|
62 |
réussie</p></td> |
|
63 |
</tr> |
|
64 |
<tr> |
|
65 |
<td><p><code>successful-request</code></p></td> |
|
66 |
<td><p>Compteur incrémenté à la fin d'une requête HTTP réussie</p></td> |
|
67 |
</tr> |
|
68 |
<tr> |
|
69 |
<td><p><code>failed-request</code></p></td> |
|
70 |
<td><p>Compteur incrémenté à la fin d'une requête HTTP avortée par |
|
71 |
une erreur interne (erreur HTTP 500)</p></td> |
|
72 |
</tr> |
|
73 |
</table> |
|
74 |
</section> |
|
75 | ||
76 |
</page> |
wcs/publisher.py | ||
---|---|---|
351 | 351 |
sql.reindex() |
352 | 352 | |
353 | 353 |
def cleanup(self): |
354 |
if self.statsd: |
|
355 |
self.statsd.udp_sock.close() |
|
356 |
self.statsd = None |
|
357 | 354 |
if self.is_using_postgresql(): |
358 | 355 |
from . import sql |
359 | 356 |
sql.cleanup_connection() |
wcs/qommon/ident/password.py | ||
---|---|---|
252 | 252 |
username = form.get_widget('username').parse() |
253 | 253 |
password = form.get_widget('password').parse() |
254 | 254 | |
255 |
get_publisher().statsd.increment('login-submit') |
|
256 | ||
257 | 255 |
try: |
258 | 256 |
user = PasswordAccount.get_with_credentials(username, password) |
259 | 257 |
except: |
260 | 258 |
form.set_error('username', _('Invalid credentials')) |
261 |
get_publisher().statsd.increment('login-error') |
|
262 | 259 |
return |
263 | 260 | |
264 |
get_publisher().statsd.increment('login-success') |
|
265 | ||
266 | 261 |
account = PasswordAccount.get(username) |
267 | 262 |
return self.login_submit_account_user(account, user, form) |
268 | 263 |
wcs/qommon/publisher.py | ||
---|---|---|
92 | 92 |
auto_create_appdir = True |
93 | 93 |
missing_appdir_redirect = None |
94 | 94 |
use_sms_feature = True |
95 |
statsd = None |
|
96 | 95 | |
97 | 96 |
gettext = lambda self, message: message |
98 | 97 |
ngettext = lambda self, msgid1, msgid2, n: msgid1 |
... | ... | |
249 | 248 |
def finish_successful_request(self): |
250 | 249 |
if not self.get_request().ignore_session: |
251 | 250 |
self.session_manager.finish_successful_request() |
252 |
self.statsd.increment('successful-request') |
|
253 | 251 | |
254 | 252 |
def finish_failed_request(self): |
255 | 253 |
# duplicate at lot from parent class, just to use our own HTTPResponse |
... | ... | |
257 | 255 |
original_response = request.response |
258 | 256 |
request.response = HTTPResponse() |
259 | 257 | |
260 |
if self.statsd: # maybe unset if very early failure |
|
261 |
self.statsd.increment('failed-request') |
|
262 | ||
263 | 258 |
(exc_type, exc_value, tb) = sys.exc_info() |
264 | 259 | |
265 | 260 |
if exc_type is NotImplementedError: |
... | ... | |
465 | 460 |
def init_publish(self, request): |
466 | 461 |
self.set_app_dir(request) |
467 | 462 | |
468 |
from vendor import pystatsd |
|
469 |
self.statsd = pystatsd.Client( |
|
470 |
host=request.get_environ('QOMMON_STATSD_HOSTNAME', 'localhost'), |
|
471 |
prefix='%s.%s' % ( |
|
472 |
self.APP_NAME, |
|
473 |
os.path.split(self.app_dir)[-1].replace('+', '-').replace('.', '-'))) |
|
474 | ||
475 | 463 |
self.initialize_app_dir() |
476 | 464 |
self.set_config(request) |
477 | 465 |
wcs/qommon/vendor/pystatsd.py | ||
---|---|---|
1 |
# w.c.s. - web application for online forms |
|
2 |
# Copyright (C) 2005-2012 Entr'ouvert |
|
3 |
# |
|
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 |
|
6 |
# the Free Software Foundation; either version 2 of the License, or |
|
7 |
# (at your option) any later version. |
|
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 General Public License |
|
15 |
# along with this program; if not, see <http://www.gnu.org/licenses/>. |
|
16 |
# |
|
17 |
# |
|
18 |
# statsd.py - Python implementation of the statsd client |
|
19 |
# |
|
20 |
# Initially released (as License :: OSI Approved :: BSD License, from setup.py) |
|
21 |
# by Steve Ivy <steveivy@gmail.com> - <https://github.com/sivy/py-statsd> |
|
22 | ||
23 |
import random |
|
24 |
import socket |
|
25 |
import time |
|
26 | ||
27 | ||
28 |
# Sends statistics to the stats daemon over UDP |
|
29 |
class Client(object): |
|
30 | ||
31 |
def __init__(self, host='localhost', port=8125, prefix=None): |
|
32 |
""" |
|
33 |
Create a new Statsd client. |
|
34 |
* host: the host where statsd is listening, defaults to localhost |
|
35 |
* port: the port where statsd is listening, defaults to 8125 |
|
36 | ||
37 |
>>> from pystatsd import statsd |
|
38 |
>>> stats_client = statsd.Statsd(host, port) |
|
39 |
""" |
|
40 |
self.addr = (socket.gethostbyname(host), port) |
|
41 |
self.prefix = prefix |
|
42 |
self.udp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) |
|
43 | ||
44 |
def timing_since(self, stat, start, sample_rate=1): |
|
45 |
""" |
|
46 |
Log timing information as the number of microseconds since the provided time float |
|
47 |
>>> start = time.time() |
|
48 |
>>> # do stuff |
|
49 |
>>> statsd_client.timing_since('some.time', start) |
|
50 |
""" |
|
51 |
self.timing(stat, int((time.time() - start) * 1000000), sample_rate) |
|
52 | ||
53 |
def timing(self, stat, time, sample_rate=1): |
|
54 |
""" |
|
55 |
Log timing information for a single stat |
|
56 |
>>> statsd_client.timing('some.time',500) |
|
57 |
""" |
|
58 |
stats = {stat: "%f|ms" % time} |
|
59 |
self.send(stats, sample_rate) |
|
60 | ||
61 |
def gauge(self, stat, value, sample_rate=1): |
|
62 |
""" |
|
63 |
Log gauge information for a single stat |
|
64 |
>>> statsd_client.gauge('some.gauge',42) |
|
65 |
""" |
|
66 |
stats = {stat: "%f|g" % value} |
|
67 |
self.send(stats, sample_rate) |
|
68 | ||
69 |
def increment(self, stats, sample_rate=1): |
|
70 |
""" |
|
71 |
Increments one or more stats counters |
|
72 |
>>> statsd_client.increment('some.int') |
|
73 |
>>> statsd_client.increment('some.int',0.5) |
|
74 |
""" |
|
75 |
self.update_stats(stats, 1, sample_rate=sample_rate) |
|
76 | ||
77 |
def decrement(self, stats, sample_rate=1): |
|
78 |
""" |
|
79 |
Decrements one or more stats counters |
|
80 |
>>> statsd_client.decrement('some.int') |
|
81 |
""" |
|
82 |
self.update_stats(stats, -1, sample_rate=sample_rate) |
|
83 | ||
84 |
def update_stats(self, stats, delta, sample_rate=1): |
|
85 |
""" |
|
86 |
Updates one or more stats counters by arbitrary amounts |
|
87 |
>>> statsd_client.update_stats('some.int',10) |
|
88 |
""" |
|
89 |
if not isinstance(stats, list): |
|
90 |
stats = [stats] |
|
91 | ||
92 |
data = dict((stat, "%s|c" % delta) for stat in stats) |
|
93 |
self.send(data, sample_rate) |
|
94 | ||
95 |
def send(self, data, sample_rate=1): |
|
96 |
""" |
|
97 |
Squirt the metrics over UDP |
|
98 |
""" |
|
99 | ||
100 |
if self.prefix: |
|
101 |
data = dict((".".join((self.prefix, stat)), value) for stat, value in data.iteritems()) |
|
102 | ||
103 |
if sample_rate < 1: |
|
104 |
if random.random() > sample_rate: |
|
105 |
return |
|
106 |
sampled_data = dict((stat, "%s|@%s" % (value, sample_rate)) for stat, value in data.iteritems()) |
|
107 |
else: |
|
108 |
sampled_data = data |
|
109 | ||
110 |
try: |
|
111 |
[self.udp_sock.sendto("%s:%s" % (stat, value), self.addr) for stat, value in sampled_data.iteritems()] |
|
112 |
except: |
|
113 |
pass |
|
114 |
- |