1
- import time
2
1
import inspect
3
2
import sys
3
+ import time
4
4
5
+ from django .conf import settings
5
6
from django .core import cache
7
+ from django .core .cache import get_cache as base_get_cache
6
8
from django .core .cache .backends .base import BaseCache
9
+ from django .dispatch import Signal
10
+ from django .template import Node
11
+ from django .utils .datastructures import SortedDict
7
12
from django .utils .translation import ugettext_lazy as _ , ungettext
13
+
8
14
from debug_toolbar .panels import DebugPanel
15
+ from debug_toolbar .utils import (tidy_stacktrace , render_stacktrace ,
16
+ get_template_info , get_stack )
17
+
18
+
19
+ cache_called = Signal (providing_args = ["time_taken" , "name" , "return_value" , "args" , "kwargs" , "trace" ])
20
+
21
+
22
+ def send_signal (method ):
23
+ def wrapped (self , * args , ** kwargs ):
24
+ t = time .time ()
25
+ value = method (self , * args , ** kwargs )
26
+ t = time .time () - t
27
+
28
+ enable_stacktraces = getattr (settings ,
29
+ 'DEBUG_TOOLBAR_CONFIG' , {}).get ('ENABLE_STACKTRACES' , True )
30
+ if enable_stacktraces :
31
+ stacktrace = tidy_stacktrace (reversed (get_stack ()))
32
+ else :
33
+ stacktrace = []
34
+
35
+ template_info = None
36
+ cur_frame = sys ._getframe ().f_back
37
+ try :
38
+ while cur_frame is not None :
39
+ if cur_frame .f_code .co_name == 'render' :
40
+ node = cur_frame .f_locals ['self' ]
41
+ if isinstance (node , Node ):
42
+ template_info = get_template_info (node .source )
43
+ break
44
+ cur_frame = cur_frame .f_back
45
+ except :
46
+ pass
47
+ del cur_frame
48
+ cache_called .send (sender = self .__class__ , time_taken = t ,
49
+ name = method .__name__ , return_value = value ,
50
+ args = args , kwargs = kwargs , trace = stacktrace ,
51
+ template_info = template_info , backend = self .cache )
52
+ return value
53
+ return wrapped
9
54
10
55
11
56
class CacheStatTracker (BaseCache ):
12
57
"""A small class used to track cache calls."""
13
58
def __init__ (self , cache ):
14
59
self .cache = cache
15
- self .reset ()
16
-
17
- def reset (self ):
18
- self .calls = []
19
- self .hits = 0
20
- self .misses = 0
21
- self .sets = 0
22
- self .gets = 0
23
- self .get_manys = 0
24
- self .deletes = 0
25
- self .total_time = 0
26
60
27
61
def _get_func_info (self ):
28
62
frame = sys ._getframe (3 )
29
63
info = inspect .getframeinfo (frame )
30
64
return (info [0 ], info [1 ], info [2 ], info [3 ])
31
65
32
- def get (self , * args , ** kwargs ):
33
- t = time .time ()
34
- value = self .cache .get (* args , ** kwargs )
35
- this_time = time .time () - t
36
- self .total_time += this_time * 1000
37
- if value is None :
38
- self .misses += 1
39
- else :
40
- self .hits += 1
41
- self .gets += 1
42
- self .calls .append ((this_time , 'get' , args , kwargs , self ._get_func_info ()))
43
- return value
44
-
45
- def set (self , * args , ** kwargs ):
46
- t = time .time ()
47
- self .cache .set (* args , ** kwargs )
48
- this_time = time .time () - t
49
- self .total_time += this_time * 1000
50
- self .sets += 1
51
- self .calls .append ((this_time , 'set' , args , kwargs , self ._get_func_info ()))
52
-
53
- def delete (self , * args , ** kwargs ):
54
- t = time .time ()
55
- self .cache .delete (* args , ** kwargs )
56
- this_time = time .time () - t
57
- self .total_time += this_time * 1000
58
- self .deletes += 1
59
- self .calls .append ((this_time , 'delete' , args , kwargs , self ._get_func_info ()))
60
-
61
- def get_many (self , * args , ** kwargs ):
62
- t = time .time ()
63
- results = self .cache .get_many (* args , ** kwargs )
64
- this_time = time .time () - t
65
- self .total_time += this_time * 1000
66
- self .get_manys += 1
67
- for key , value in results .iteritems ():
68
- if value is None :
69
- self .misses += 1
70
- else :
71
- self .hits += 1
72
- self .calls .append ((this_time , 'get_many' , args , kwargs , self ._get_func_info ()))
73
- return results
66
+ def __contains__ (self , key ):
67
+ return self .cache .__contains__ (key )
74
68
75
69
def make_key (self , * args , ** kwargs ):
76
70
return self .cache .make_key (* args , ** kwargs )
77
71
72
+ def validate_key (self , * args , ** kwargs ):
73
+ self .cache .validate_key (* args , ** kwargs )
74
+
75
+ def clear (self ):
76
+ return self .cache .clear ()
77
+
78
+ @send_signal
78
79
def add (self , * args , ** kwargs ):
79
80
return self .cache .add (* args , ** kwargs )
80
81
82
+ @send_signal
83
+ def get (self , * args , ** kwargs ):
84
+ return self .cache .get (* args , ** kwargs )
85
+
86
+ @send_signal
87
+ def set (self , * args , ** kwargs ):
88
+ return self .cache .set (* args , ** kwargs )
89
+
90
+ @send_signal
91
+ def delete (self , * args , ** kwargs ):
92
+ return self .cache .delete (* args , ** kwargs )
93
+
94
+ @send_signal
81
95
def has_key (self , * args , ** kwargs ):
82
96
return self .cache .has_key (* args , ** kwargs )
83
97
98
+ @send_signal
84
99
def incr (self , * args , ** kwargs ):
85
100
return self .cache .incr (* args , ** kwargs )
86
101
102
+ @send_signal
87
103
def decr (self , * args , ** kwargs ):
88
104
return self .cache .decr (* args , ** kwargs )
89
105
90
- def __contains__ (self , key ):
91
- return self .cache .__contains__ (key )
106
+ @send_signal
107
+ def get_many (self , * args , ** kwargs ):
108
+ return self .cache .get_many (* args , ** kwargs )
92
109
110
+ @send_signal
93
111
def set_many (self , * args , ** kwargs ):
94
112
self .cache .set_many (* args , ** kwargs )
95
113
114
+ @send_signal
96
115
def delete_many (self , * args , ** kwargs ):
97
116
self .cache .delete_many (* args , ** kwargs )
98
117
99
- def clear (self ):
100
- self .cache .clear ()
101
-
102
- def validate_key (self , * args , ** kwargs ):
103
- self .cache .validate_key (* args , ** kwargs )
104
-
118
+ @send_signal
105
119
def incr_version (self , * args , ** kwargs ):
106
120
return self .cache .incr_version (* args , ** kwargs )
107
121
122
+ @send_signal
108
123
def decr_version (self , * args , ** kwargs ):
109
124
return self .cache .decr_version (* args , ** kwargs )
110
125
@@ -119,29 +134,86 @@ class CacheDebugPanel(DebugPanel):
119
134
120
135
def __init__ (self , * args , ** kwargs ):
121
136
super (CacheDebugPanel , self ).__init__ (* args , ** kwargs )
122
- cache .cache .reset ()
137
+ self .total_time = 0
138
+ self .hits = 0
139
+ self .misses = 0
140
+ self .calls = []
141
+ self .counts = SortedDict ((
142
+ ('add' , 0 ),
143
+ ('get' , 0 ),
144
+ ('set' , 0 ),
145
+ ('delete' , 0 ),
146
+ ('get_many' , 0 ),
147
+ ('set_many' , 0 ),
148
+ ('delete_many' , 0 ),
149
+ ('has_key' , 0 ),
150
+ ('incr' , 0 ),
151
+ ('decr' , 0 ),
152
+ ('incr_version' , 0 ),
153
+ ('decr_version' , 0 ),
154
+ ))
155
+ cache_called .connect (self ._store_call_info )
156
+
157
+ def _store_call_info (self , sender , name = None , time_taken = 0 ,
158
+ return_value = None , args = None , kwargs = None , trace = None ,
159
+ template_info = None , backend = None , ** kw ):
160
+ if name == 'get' :
161
+ if return_value is None :
162
+ self .misses += 1
163
+ else :
164
+ self .hits += 1
165
+ elif name == 'get_many' :
166
+ for key , value in return_value .iteritems ():
167
+ if value is None :
168
+ self .misses += 1
169
+ else :
170
+ self .hits += 1
171
+ self .total_time += time_taken * 1000
172
+ self .counts [name ] += 1
173
+ self .calls .append ({
174
+ 'time' : time_taken ,
175
+ 'name' : name ,
176
+ 'args' : args ,
177
+ 'kwargs' : kwargs ,
178
+ 'trace' : render_stacktrace (trace ),
179
+ 'template_info' : template_info ,
180
+ 'backend' : backend
181
+ })
123
182
124
183
def nav_title (self ):
125
184
return _ ('Cache' )
126
185
127
186
def nav_subtitle (self ):
128
- cache_calls = len (cache . cache .calls )
187
+ cache_calls = len (self .calls )
129
188
return ungettext ('%(cache_calls)d call in %(time).2fms' ,
130
189
'%(cache_calls)d calls in %(time).2fms' ,
131
190
cache_calls ) % {'cache_calls' : cache_calls ,
132
- 'time' : cache . cache .total_time }
191
+ 'time' : self .total_time }
133
192
134
193
def title (self ):
135
- return _ ('Cache Usage' )
194
+ count = len (getattr (settings , 'CACHES' , ['default' ]))
195
+ return ungettext ('Cache calls from %(count)d backend' ,
196
+ 'Cache calls from %(count)d backends' ,
197
+ count ) % dict (count = count )
136
198
137
199
def url (self ):
138
200
return ''
139
201
140
202
def process_response (self , request , response ):
141
203
self .record_stats ({
142
- 'cache_calls' : len (cache .cache .calls ),
143
- 'cache_time' : cache .cache .total_time ,
144
- 'cache' : cache .cache ,
204
+ 'total_calls' : len (self .calls ),
205
+ 'calls' : self .calls ,
206
+ 'total_time' : self .total_time ,
207
+ 'hits' : self .hits ,
208
+ 'misses' : self .misses ,
209
+ 'counts' : self .counts ,
145
210
})
146
211
212
+
213
+ def get_cache_debug (* args , ** kwargs ):
214
+ base_cache = base_get_cache (* args , ** kwargs )
215
+ return CacheStatTracker (base_cache )
216
+
217
+
147
218
cache .cache = CacheStatTracker (cache .cache )
219
+ cache .get_cache = get_cache_debug
0 commit comments