22import time
33import logging
44import threading
5+ from pyramid .response import Response
56from pyramid .session import SignedCookieSessionFactory
67from pyramid .renderers import JSONP
78from pyramid .config import Configurator
89
910from analytics import controller
1011from analytics import charts_config
12+ from analytics import monitoring
1113
1214from analytics .views_website import cache_region as views_website_cache_region
1315from analytics .views_ajax import cache_region as views_ajax_cache_region
1719logger = logging .getLogger (__name__ )
1820
1921
20- def _start_cache_prewarm (settings ):
22+ def _metrics_view (_request ):
23+ body , content_type = monitoring .metrics_response ()
24+ return Response (body = body , content_type = content_type )
25+
26+
27+ def _request_metrics_tween_factory (handler , registry ):
28+ def _tween (request ):
29+ route_name = "unknown"
30+ if request .matched_route is not None :
31+ route_name = request .matched_route .name
32+ method = request .method
33+ timer = monitoring .HTTP_REQUEST_DURATION_SECONDS .labels (method = method , route = route_name ).time ()
34+ monitoring .HTTP_REQUESTS_IN_PROGRESS .labels (method = method , route = route_name ).inc ()
35+ try :
36+ with timer :
37+ response = handler (request )
38+ status_code = str (getattr (response , "status_code" , "200" ))
39+ monitoring .HTTP_REQUESTS_TOTAL .labels (
40+ method = method , route = route_name , status_code = status_code
41+ ).inc ()
42+ return response
43+ except Exception :
44+ monitoring .HTTP_REQUESTS_TOTAL .labels (
45+ method = method , route = route_name , status_code = "500"
46+ ).inc ()
47+ raise
48+ finally :
49+ monitoring .HTTP_REQUESTS_IN_PROGRESS .labels (method = method , route = route_name ).dec ()
50+
51+ return _tween
52+
53+
54+ def _start_cache_prewarm (settings , usage_api_base_url = None ):
2155 if os .environ .get ("ENABLE_CACHE_PREWARM" , "1" ).lower () in ("0" , "false" , "no" ):
2256 return
2357
@@ -32,7 +66,7 @@ def _prewarm():
3266 settings .get ('articlemeta' , None ),
3367 settings .get ('publicationstats' , None ),
3468 settings .get ('citedby' , None ),
35- settings . get ( 'usage' , None ) ,
69+ usage_api_base_url ,
3670 request = None
3771 )
3872 stats .articlemeta .certified_collections ()
@@ -71,13 +105,14 @@ def main(global_config, **settings):
71105 """
72106 config = Configurator (settings = settings )
73107 config .add_renderer ('jsonp' , JSONP (param_name = 'callback' , indent = 4 ))
108+ usage_api_base_url = os .environ .get ('USAGE_API_BASE_URL' , settings .get ('usage' , None ))
74109
75110 def add_stats (request ):
76111 return controller .Stats (
77112 settings .get ('articlemeta' , None ),
78113 settings .get ('publicationstats' , None ),
79114 settings .get ('citedby' , None ),
80- settings . get ( 'usage' , None ) ,
115+ usage_api_base_url ,
81116 request
82117 )
83118
@@ -89,6 +124,7 @@ def add_chartsconfig(request):
89124 config .add_route ('index_web' , '/' )
90125 config .add_route ('faq_web' , '/w/faq' )
91126 config .add_route ('reports' , '/w/reports' )
127+ config .add_route ('metrics' , '/metrics' )
92128 config .add_route ('usage_report_chart' , '/ajx/usage/usage_report_chart' )
93129 config .add_route ('usage_report_yearly_chart' , '/ajx/usage/usage_report_yearly_chart' )
94130 config .add_route ('accesses_web' , '/w/accesses' )
@@ -138,6 +174,8 @@ def add_chartsconfig(request):
138174 config .add_subscriber ('analytics.subscribers.add_localizer' ,
139175 'pyramid.events.NewRequest' )
140176 config .add_translation_dirs ('analytics:locale' )
177+ config .add_tween ('analytics._request_metrics_tween_factory' , over = 'MAIN' )
178+ config .add_view (_metrics_view , route_name = 'metrics' , request_method = 'GET' )
141179
142180 # Cache Settings Config
143181 memcached_host = (
@@ -162,6 +200,8 @@ def add_chartsconfig(request):
162200 views_website_cache_region .configure ('dogpile.cache.pylibmc' , ** cache_config )
163201 controller_cache_region .configure ('dogpile.cache.pylibmc' , ** cache_config )
164202 control_manager_cache_region .configure ('dogpile.cache.pylibmc' , ** cache_config )
203+ monitoring .CACHE_BACKEND_INFO .labels (backend = 'pylibmc' ).set (1 )
204+ monitoring .CACHE_BACKEND_INFO .labels (backend = 'memory' ).set (0 )
165205 else :
166206 # Fallback to in-process cache when memcached is unavailable.
167207 # This avoids recomputing heavy remote calls on every request.
@@ -170,11 +210,13 @@ def add_chartsconfig(request):
170210 controller_cache_region .configure ('dogpile.cache.memory' , ** cache_config )
171211 control_manager_cache_region .configure ('dogpile.cache.memory' , ** cache_config )
172212 views_ajax_cache_region .configure ('dogpile.cache.memory' , ** cache_config )
213+ monitoring .CACHE_BACKEND_INFO .labels (backend = 'memory' ).set (1 )
214+ monitoring .CACHE_BACKEND_INFO .labels (backend = 'pylibmc' ).set (0 )
173215
174216 # Session config
175217 navegation_session_factory = SignedCookieSessionFactory ('sses_navegation' )
176218 config .set_session_factory (navegation_session_factory )
177- _start_cache_prewarm (settings )
219+ _start_cache_prewarm (settings , usage_api_base_url = usage_api_base_url )
178220
179221 config .scan ()
180222
0 commit comments