Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add instrumentation to generator functions #106

Merged
merged 1 commit into from
Mar 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions beeline/aiotrace.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import asyncio
import contextvars # pylint: disable=import-error
import functools
import inspect

from beeline.trace import Tracer

Expand Down Expand Up @@ -77,7 +78,14 @@ async def async_inner(*args, **kwargs):
return await fn(*args, **kwargs)

return async_inner
elif inspect.isgeneratorfunction(fn):
@functools.wraps(fn)
def inner(*args, **kwargs):
inner_generator = fn(*args, **kwargs)
with tracer_fn(name=name, trace_id=trace_id, parent_id=parent_id):
yield from inner_generator

return inner
else:
@functools.wraps(fn)
def inner(*args, **kwargs):
Expand Down
37 changes: 36 additions & 1 deletion beeline/test_beeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,41 @@ def my_sum(a, b):
# check that an event was sent, from which we can infer that the function was wrapped
self.assertTrue(_beeline.tracer_impl._run_hooks_and_send.called)

def test_generator_wrapper(self):
''' ensure that the trace wrapper decorates a generator function and starts a trace
also ensure that child traces get the parent trace correctly set
'''

_beeline = beeline.Beeline()

with patch('beeline.get_beeline') as m_gbl:
m_gbl.return_value = _beeline
_beeline.tracer_impl._run_hooks_and_send = Mock()

@beeline.traced(name="return_integer_n")
def return_integer(n):
return n

@beeline.traced(name="output_integers_to")
def output_integers_to(n):
for i in range(n):
yield return_integer(i)

# should accept the function's arguments normally and yield the items from the
# generator
self.assertEqual(list(output_integers_to(3)), [0, 1, 2])

self.assertTrue(_beeline.tracer_impl._run_hooks_and_send.called)

spans = [x[0][0] for x in _beeline.tracer_impl._run_hooks_and_send.call_args_list]

# check the child spans now
parent_span = spans[-1]
child_spans = spans[:-1]

for child_span in child_spans:
self.assertEqual(child_span.parent_id, parent_span.id)

@staticmethod
def raising_run_in_thread(target):
closure_dict = {}
Expand Down Expand Up @@ -246,4 +281,4 @@ def my_sum(a, b):

# this should not crash if the beeline isn't initialized
# it should also accept arguments normally and return the function's value
self.assertEqual(my_sum(1, 2), 3)
self.assertEqual(my_sum(1, 2), 3)
22 changes: 15 additions & 7 deletions beeline/trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import struct
import threading
import uuid
import inspect
from collections import defaultdict

from contextlib import contextmanager
Expand Down Expand Up @@ -359,11 +360,18 @@ def unmarshal_trace_context(trace_context):
def traced_impl(tracer_fn, name, trace_id, parent_id):
"""Implementation of the traced decorator without async support."""
def wrapped(fn):
@functools.wraps(fn)
def inner(*args, **kwargs):
with tracer_fn(name=name, trace_id=trace_id, parent_id=parent_id):
return fn(*args, **kwargs)

return inner

if inspect.isgeneratorfunction(fn):
@functools.wraps(fn)
def inner(*args, **kwargs):
inner_generator = fn(*args, **kwargs)
with tracer_fn(name=name, trace_id=trace_id, parent_id=parent_id):
for value in inner_generator:
yield value
return inner
else:
@functools.wraps(fn)
def inner(*args, **kwargs):
with tracer_fn(name=name, trace_id=trace_id, parent_id=parent_id):
return fn(*args, **kwargs)
return inner
return wrapped