1818import threading
1919import warnings
2020
21- from newrelic .api .application import register_application
21+ from newrelic .api .application import application_settings , register_application
2222from newrelic .api .background_task import BackgroundTaskWrapper
2323from newrelic .api .error_trace import wrap_error_trace
2424from newrelic .api .function_trace import FunctionTrace , FunctionTraceWrapper , wrap_function_trace
@@ -238,77 +238,12 @@ def wrapper(wrapped, instance, args, kwargs):
238238 return FunctionWrapper (wrapped , wrapper )
239239
240240 for wrapped in middleware :
241- yield wrapper (wrapped )
242-
243-
244- # Because this is not being used in any version of Django that is
245- # within New Relic's support window, no tests will be added
246- # for this. However, value exists to keeping backwards compatible
247- # functionality, so instead of removing this instrumentation, this
248- # will be excluded from the coverage analysis.
249- def wrap_view_middleware (middleware ): # pragma: no cover
250- # This is no longer being used. The changes to strip the
251- # wrapper from the view handler when passed into the function
252- # urlresolvers.reverse() solves most of the problems. To back
253- # that up, the object wrapper now proxies various special
254- # methods so that comparisons like '==' will work. The object
255- # wrapper can even be used as a standin for the wrapped object
256- # when used as a key in a dictionary and will correctly match
257- # the original wrapped object.
258-
259- # Wrapper to be applied to view middleware. Records the time
260- # spent in the middleware as separate function node and also
261- # attempts to name the web transaction after the name of the
262- # middleware with success being determined by the priority.
263- # This wrapper is special in that it must strip the wrapper
264- # from the view handler when being passed to the view
265- # middleware to avoid issues where middleware wants to do
266- # comparisons between the passed middleware and some other
267- # value. It is believed that the view handler should never
268- # actually be called from the view middleware so not an
269- # issue that no longer wrapped at this point.
241+ do_not_wrap = is_denied_middleware (callable_name (wrapped ))
270242
271- def wrapper (wrapped ):
272- # The middleware if a class method would already be
273- # bound at this point, so is safe to determine the name
274- # when it is being wrapped rather than on each
275- # invocation.
276-
277- name = callable_name (wrapped )
278-
279- def wrapper (wrapped , instance , args , kwargs ):
280- transaction = current_transaction ()
281-
282- def _wrapped (request , view_func , view_args , view_kwargs ):
283- # This strips the view handler wrapper before call.
284-
285- if hasattr (view_func , "_nr_last_object" ):
286- view_func = view_func ._nr_last_object
287-
288- return wrapped (request , view_func , view_args , view_kwargs )
289-
290- if transaction is None :
291- return _wrapped (* args , ** kwargs )
292-
293- before = (transaction .name , transaction .group )
294-
295- with FunctionTrace (name = name , source = wrapped ):
296- try :
297- return _wrapped (* args , ** kwargs )
298-
299- finally :
300- # We want to name the transaction after this
301- # middleware but only if the transaction wasn't
302- # named from within the middleware itself explicitly.
303-
304- after = (transaction .name , transaction .group )
305- if before == after :
306- transaction .set_transaction_name (name , priority = 2 )
307-
308- return FunctionWrapper (wrapped , wrapper )
309-
310- for wrapped in middleware :
311- yield wrapper (wrapped )
243+ if do_not_wrap :
244+ yield wrapped
245+ else :
246+ yield wrapper (wrapped )
312247
313248
314249def wrap_trailing_middleware (middleware ):
@@ -323,7 +258,13 @@ def wrap_trailing_middleware(middleware):
323258 # invocation.
324259
325260 for wrapped in middleware :
326- yield FunctionTraceWrapper (wrapped , name = callable_name (wrapped ))
261+ name = callable_name (wrapped )
262+ do_not_wrap = is_denied_middleware (name )
263+
264+ if do_not_wrap :
265+ yield wrapped
266+ else :
267+ yield FunctionTraceWrapper (wrapped , name = name )
327268
328269
329270def insert_and_wrap_middleware (handler , * args , ** kwargs ):
@@ -1149,17 +1090,126 @@ def _wrapper(wrapped, instance, args, kwargs):
11491090 return _wrapper (middleware )
11501091
11511092
1093+ # For faster logic, could we cache the callable_names to
1094+ # avoid this logic for multiple calls to the same middleware?
1095+ # Also, is that even a scenario we need to worry about?
1096+ def is_denied_middleware (callable_name ):
1097+ settings = application_settings () or global_settings ()
1098+
1099+ # Return True (skip wrapping) if:
1100+ # 1. middleware wrapping is disabled or
1101+ # 2. the callable name is in the exclude list
1102+ if not settings .instrumentation .middleware .django .enabled or (
1103+ callable_name in settings .instrumentation .middleware .django .exclude
1104+ ):
1105+ return True
1106+
1107+ # Return False (middleware will be wrapped) if:
1108+ # 1. If the callable name is in the include list.
1109+ # If we have made it to this point in the logic, that means
1110+ # that the callable name is not explicitly in the exclude
1111+ # list and, whether it is in the exclude list as a wildcard
1112+ # or not, the list of middleware to include explicitly takes
1113+ # precedence over the exclude list's wildcards.
1114+ # 2. enabled=True and len(exclude)==0
1115+ # This scenario is redundant logic, but we utilize it to
1116+ # speed up the logic for the common case where
1117+ # the user has not specified any include or exclude lists.
1118+ if (callable_name in settings .instrumentation .middleware .django .include ) or (
1119+ settings .instrumentation .middleware .django .enabled
1120+ and len (settings .instrumentation .middleware .django .exclude ) == 0
1121+ ):
1122+ return False
1123+
1124+ # =========================================================
1125+ # Wildcard logic for include and exclude lists:
1126+ # =========================================================
1127+ # Returns False if an entry in the include wildcard is more
1128+ # specific than exclude wildcard. Otherwise, it will iterate
1129+ # through the entire include list and return True if no more
1130+ # specific include (than exclude) is found.
1131+ def include_logic (callable_name , exclude_middleware_name ):
1132+ """
1133+ We iterate through the entire include and exclude lists to
1134+ ensure that the user has not included overlapping wildcards
1135+ that would potentially be less specific than the exclude in
1136+ one case and more specific than the exclude in another case.
1137+
1138+ e.g.:
1139+ exclude = middleware.*, middleware.parameters.bar*
1140+ include = middleware.parameters.*
1141+
1142+ Where `middleware.*` is less specific than `middleware.parameters.*`
1143+ but `middleware.parameters.bar*` is more specific than `middleware.parameters.*`
1144+
1145+ In this case, we want to make sure to exclude any middleware that
1146+ matches the `middleware.parameters.bar*` pattern, but include any
1147+ other middleware that matches the `middleware.parameters.*` pattern.
1148+ """
1149+ for include_middleware in settings .instrumentation .middleware .django .include :
1150+ if include_middleware .endswith ("*" ):
1151+ include_middleware_name = include_middleware .rstrip ("*" )
1152+ if callable_name .startswith (include_middleware_name ):
1153+ # Count number of dots in the include and exclude
1154+ # middleware names to determine specificity.
1155+ exclude_dots = exclude_middleware_name .count ("." )
1156+ include_dots = include_middleware_name .count ("." )
1157+ if include_dots > exclude_dots :
1158+ # Include this middleware--include is more specific and takes precedence
1159+ return False
1160+ elif include_dots == exclude_dots :
1161+ # Count number of characters after the last dot
1162+ exclude_post_dot_char_count = len (exclude_middleware_name ) - exclude_middleware_name .rfind ("." )
1163+ include_post_dot_char_count = len (include_middleware_name ) - include_middleware_name .rfind ("." )
1164+ if include_post_dot_char_count > exclude_post_dot_char_count :
1165+ # Include this middleware--include is more specific and takes precedence
1166+ return False
1167+ else :
1168+ # Exclude wildcard has the same or greater specificity than the include wildcard
1169+ # Expect to exclude this middleware--so far, exclude is more specific and takes precedence
1170+ continue
1171+ else :
1172+ # Expect to exclude this middleware--so far, exclude is more specific and takes precedence
1173+ pass
1174+
1175+ # if we have made it to this point, there is no include middleware that is
1176+ # more specific than the exclude middleware, so we can safely return True
1177+ return True
1178+
1179+ # Check if the callable name matches any of the excluded middleware patterns.
1180+ # If middleware name ends with '*', it is a wildcard
1181+ deny = False
1182+ for exclude_middleware in settings .instrumentation .middleware .django .exclude :
1183+ if exclude_middleware .endswith ("*" ):
1184+ name = exclude_middleware .rstrip ("*" )
1185+ if callable_name .startswith (name ):
1186+ include = include_logic (callable_name , name )
1187+ if not include :
1188+ return False
1189+ else :
1190+ deny |= include
1191+
1192+ # If we have made it to this point, there are contents within
1193+ # the exclude list and those contents act as a no-op with
1194+ # respect to the specific middleware being evaluated
1195+ return deny
1196+
1197+
11521198def _nr_wrapper_convert_exception_to_response_ (wrapped , instance , args , kwargs ):
11531199 def _bind_params (original_middleware , * args , ** kwargs ):
11541200 return original_middleware
11551201
11561202 original_middleware = _bind_params (* args , ** kwargs )
11571203 converted_middleware = wrapped (* args , ** kwargs )
11581204 name = callable_name (original_middleware )
1205+ do_not_wrap = is_denied_middleware (name )
11591206
1160- if is_coroutine_function (converted_middleware ) or is_asyncio_coroutine (converted_middleware ):
1161- return _nr_wrap_converted_middleware_async_ (converted_middleware , name )
1162- return _nr_wrap_converted_middleware_ (converted_middleware , name )
1207+ if do_not_wrap :
1208+ return converted_middleware
1209+ else :
1210+ if is_coroutine_function (converted_middleware ) or is_asyncio_coroutine (converted_middleware ):
1211+ return _nr_wrap_converted_middleware_async_ (converted_middleware , name )
1212+ return _nr_wrap_converted_middleware_ (converted_middleware , name )
11631213
11641214
11651215def instrument_django_core_handlers_exception (module ):
0 commit comments