def test_sentinel_exited_complete_root_exception(): """ This test forces a transaction to exit while it still has an active trace this causes an exception to be raised in TraceCache complete_root(). It verifies that the sentinel.exited property is set to true if an exception is raised in complete_root() """ expected_error = "not the current trace" try: txn = None sentinel = None txn = BackgroundTask(application_instance(), "Parent") txn.__enter__() sentinel = txn.root_span trace = FunctionTrace("trace") trace.__enter__() txn.__exit__(None, None, None) assert False, "Did not raise exception" except RuntimeError as e: assert str(e) == expected_error finally: assert sentinel.exited # Make sure to exit properly so cleanup is performed trace.__exit__(None, None, None) txn.__exit__(None, None, None)
def test_async_trace_overlapping_children(): parent_trace = FunctionTrace('parent') with parent_trace: child_trace_1 = FunctionTrace('child_1', parent=parent_trace) child_trace_2 = FunctionTrace('child_2', parent=parent_trace) child_trace_1.__enter__() child_trace_2.__enter__() time.sleep(0.01) child_trace_1.__exit__(None, None, None) child_trace_2.__exit__(None, None, None) assert parent_trace.start_time < child_trace_1.start_time assert child_trace_1.start_time < child_trace_2.start_time assert child_trace_1.end_time < child_trace_2.end_time assert child_trace_2.end_time < parent_trace.end_time
def test_sentinel_exited_complete_root_exception(): """ This test forces a transaction to exit while it still has an active trace this causes an exception to be raised in TraceCache complete_root(). It verifies that the sentinel.exited property is set to true if an exception is raised in complete_root(), and that the exception is caught. """ txn = None sentinel = None txn = BackgroundTask(application_instance(), "Parent") txn.__enter__() sentinel = txn.root_span trace = FunctionTrace("trace") trace.__enter__() txn.__exit__(None, None, None) assert sentinel.exited # Make sure to exit properly so cleanup is performed trace.__exit__(None, None, None) txn.__exit__(None, None, None)
class NRFunctionTraceCoroutineWrapper(ObjectProxy): def __init__(self, wrapped, transaction, name): super(NRFunctionTraceCoroutineWrapper, self).__init__(wrapped) self._nr_transaction = transaction self._nr_trace = FunctionTrace(transaction, name) def __iter__(self): return self def __await__(self): return self def __next__(self): return self.send(None) next = __next__ def send(self, value): if not self._nr_trace.activated: self._nr_trace.__enter__() with TransactionContext(self._nr_transaction): try: return self.__wrapped__.send(value) except: self._nr_trace.__exit__(None, None, None) raise def throw(self, *args, **kwargs): with TransactionContext(self._nr_transaction): try: return self.__wrapped__.throw(*args, **kwargs) except: self._nr_trace.__exit__(None, None, None) raise def close(self): try: return self.__wrapped__.close() finally: self._nr_trace.__exit__(None, None, None)
class _WSGIApplicationIterable(object): def __init__(self, transaction, generator): self.transaction = transaction self.generator = generator self.response_trace = None self.closed = False def __iter__(self): self.start_trace() try: for item in self.generator: try: self.transaction._calls_yield += 1 self.transaction._bytes_sent += len(item) except Exception: pass yield item except GeneratorExit: raise except: # Catch all self.transaction.record_exception(*sys.exc_info()) raise finally: self.close() def start_trace(self): if not self.transaction._sent_start: self.transaction._sent_start = time.time() if not self.response_trace: self.response_trace = FunctionTrace( name='Response', group='Python/WSGI') self.response_trace.__enter__() def close(self): if self.closed: return if self.response_trace: self.response_trace.__exit__(None, None, None) self.response_trace = None try: with FunctionTrace( name='Finalize', group='Python/WSGI'): if isinstance(self.generator, _WSGIApplicationMiddleware): self.generator.close() elif hasattr(self.generator, 'close'): name = callable_name(self.generator.close) with FunctionTrace(name): self.generator.close() except: # Catch all self.transaction.__exit__(*sys.exc_info()) raise else: self.transaction.__exit__(None, None, None) self.transaction._sent_end = time.time() finally: self.closed = True
def __call__(self, frame, event, arg): if event not in [ 'call', 'c_call', 'return', 'c_return', 'exception', 'c_exception' ]: return parent = current_trace() if not parent: return # Not sure if setprofile() is reliable in the face of # coroutine systems based on greenlets so don't run # if we detect may be using greenlets. if (hasattr(sys, '_current_frames') and parent.thread_id not in sys._current_frames()): return co = frame.f_code func_name = co.co_name func_line_no = frame.f_lineno func_filename = co.co_filename def _callable_name(): # A stack frame doesn't provide any information about the # original callable object. We thus need to try and # deduce what it is by searching through the stack # frame globals. This will still not work in many # cases, including lambdas, generator expressions, # and decorated attributes such as properties of # classes. try: if func_name in frame.f_globals: if frame.f_globals[func_name].func_code is co: return callable_name(frame.f_globals[func_name]) except Exception: pass for name, obj in six.iteritems(frame.f_globals): try: if obj.__dict__[func_name].func_code is co: return callable_name(obj.__dict__[func_name]) except Exception: pass if event in ['call', 'c_call']: # Skip the outermost as we catch that with the root # function traces for the profile trace. if len(self.function_traces) == 0: self.function_traces.append(None) return if self.current_depth >= self.maximum_depth: self.function_traces.append(None) return if func_filename.startswith(AGENT_PACKAGE_DIRECTORY): self.function_traces.append(None) return if event == 'call': name = _callable_name() if not name: name = '%s:%s#%s' % (func_filename, func_name, func_line_no) else: name = callable_name(arg) if not name: name = '%s:@%s#%s' % (func_filename, func_name, func_line_no) function_trace = FunctionTrace(name=name, parent=parent) function_trace.__enter__() self.function_traces.append(function_trace) self.current_depth += 1 elif event in ['return', 'c_return', 'c_exception']: if not self.function_traces: return try: function_trace = self.function_traces.pop() except IndexError: pass else: if function_trace: function_trace.__exit__(None, None, None) self.current_depth -= 1
def __call__(self, frame, event, arg): if event not in ['call', 'c_call', 'return', 'c_return', 'exception', 'c_exception']: return transaction = current_transaction() if not transaction: return # Not sure if setprofile() is reliable in the face of # coroutine systems based on greenlets so don't run # if we detect may be using greenlets. if (hasattr(sys, '_current_frames') and not transaction.thread_id in sys._current_frames()): return co = frame.f_code func_name = co.co_name func_line_no = frame.f_lineno func_filename = co.co_filename def _callable_name(): # This is pretty ugly and inefficient, but a stack # frame doesn't provide any information about the # original callable object. We thus need to try and # deduce what it is by searching through the stack # frame globals. This will still not work in many # cases, including lambdas, generator expressions, # and decoratored attributes such as properties of # classes. try: if func_name in frame.f_globals: if frame.f_globals[func_name].func_code is co: return callable_name(frame.f_globals[func_name]) except Exception: pass for name, obj in six.iteritems(frame.f_globals): try: if obj.__dict__[func_name].func_code is co: return callable_name(obj.__dict__[func_name]) except Exception: pass if event in ['call', 'c_call']: # Skip the outermost as we catch that with the root # function traces for the profile trace. if len(self.function_traces) == 0: self.function_traces.append(None) return if self.current_depth >= self.maximum_depth: self.function_traces.append(None) return if func_filename.startswith(AGENT_PACKAGE_DIRECTORY): self.function_traces.append(None) return if event == 'call': name = _callable_name() if not name: name = '%s:%s#%s' % (func_filename, func_name, func_line_no) else: name = callable_name(arg) if not name: name = '%s:@%s#%s' % (func_filename, func_name, func_line_no) function_trace = FunctionTrace(transaction, name=name) function_trace.__enter__() self.function_traces.append(function_trace) self.current_depth += 1 elif event in ['return', 'c_return', 'c_exception']: if not self.function_traces: return try: function_trace = self.function_traces.pop() except IndexError: pass else: if function_trace: function_trace.__exit__(None, None, None) self.current_depth -= 1