Skip to content

Commit efd6b4c

Browse files
umaannamalaihmstepanekmergify[bot]lrafeeiTimPansino
committed
Add profile_trace testing (#858)
* Include isort stdlibs for determining stdlib modules * Use isort & sys to eliminate std & builtin modules Previously, the logic would fail to identify third party modules installed within the local user socpe. This fixes that issue by skipping builtin and stdlib modules by name, instead of attempting to identify third party modules based on file paths. * Handle importlib_metadata.version being a callable * Add isort into third party notices * [Mega-Linter] Apply linters fixes * Remove Python 2.7 and pypy2 testing (#835) * Change setup-python to @v2 for py2.7 * Remove py27 and pypy testing * Fix syntax errors * Fix comma related syntax errors * Fix more issues in tox * Remove gearman test * Containerized CI Pipeline (#836) * Revert "Remove Python 2.7 and pypy2 testing (#835)" This reverts commit abb6405. * Containerize CI process * Publish new docker container for CI images * Rename github actions job * Copyright tag scripts * Drop debug line * Swap to new CI image * Move pip install to just main python * Remove libcurl special case from tox * Install special case packages into main image * Remove unused packages * Remove all other triggers besides manual * Add make run command * Cleanup small bugs * Fix CI Image Tagging (#838) * Correct templated CI image name * Pin pypy2.7 in image * Fix up scripting * Temporarily Restore Old CI Pipeline (#841) * Restore old pipelines * Remove python 2 from setup-python * Rework CI Pipeline (#839) Change pypy to pypy27 in tox. Fix checkout logic Pin tox requires * Fix Tests on New CI (#843) * Remove non-root user * Test new CI image * Change pypy to pypy27 in tox. * Fix checkout logic * Fetch git tags properly * Pin tox requires * Adjust default db settings for github actions * Rename elasticsearch services * Reset to new pipelines * [Mega-Linter] Apply linters fixes * Fix timezone * Fix docker networking * Pin dev image to new sha * Standardize gearman DB settings * Fix elasticsearch settings bug * Fix gearman bug * Add missing odbc headers * Add more debug messages * Swap out dev ci image * Fix required virtualenv version * Swap out dev ci image * Swap out dev ci image * Remove aioredis v1 for EOL * Add coverage paths for docker container * Unpin ci container --------- Co-authored-by: TimPansino <[email protected]> * Trigger tests * Add testing for profile trace. * [Mega-Linter] Apply linters fixes * Ignore __call__ from coverage on profile_trace. * [Mega-Linter] Apply linters fixes --------- Co-authored-by: Hannah Stepanek <[email protected]> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: hmstepanek <[email protected]> Co-authored-by: Lalleh Rafeei <[email protected]> Co-authored-by: Timothy Pansino <[email protected]> Co-authored-by: TimPansino <[email protected]> Co-authored-by: umaannamalai <[email protected]>
1 parent d84c1b2 commit efd6b4c

File tree

2 files changed

+107
-31
lines changed

2 files changed

+107
-31
lines changed

newrelic/api/profile_trace.py

+19-31
Original file line numberDiff line numberDiff line change
@@ -13,31 +13,27 @@
1313
# limitations under the License.
1414

1515
import functools
16-
import sys
1716
import os
17+
import sys
1818

19-
from newrelic.packages import six
20-
21-
from newrelic.api.time_trace import current_trace
19+
from newrelic import __file__ as AGENT_PACKAGE_FILE
2220
from newrelic.api.function_trace import FunctionTrace
23-
from newrelic.common.object_wrapper import FunctionWrapper, wrap_object
21+
from newrelic.api.time_trace import current_trace
2422
from newrelic.common.object_names import callable_name
23+
from newrelic.common.object_wrapper import FunctionWrapper, wrap_object
24+
from newrelic.packages import six
2525

26-
from newrelic import __file__ as AGENT_PACKAGE_FILE
27-
AGENT_PACKAGE_DIRECTORY = os.path.dirname(AGENT_PACKAGE_FILE) + '/'
26+
AGENT_PACKAGE_DIRECTORY = os.path.dirname(AGENT_PACKAGE_FILE) + "/"
2827

2928

3029
class ProfileTrace(object):
31-
3230
def __init__(self, depth):
3331
self.function_traces = []
3432
self.maximum_depth = depth
3533
self.current_depth = 0
3634

37-
def __call__(self, frame, event, arg):
38-
39-
if event not in ['call', 'c_call', 'return', 'c_return',
40-
'exception', 'c_exception']:
35+
def __call__(self, frame, event, arg): # pragma: no cover
36+
if event not in ["call", "c_call", "return", "c_return", "exception", "c_exception"]:
4137
return
4238

4339
parent = current_trace()
@@ -49,8 +45,7 @@ def __call__(self, frame, event, arg):
4945
# coroutine systems based on greenlets so don't run
5046
# if we detect may be using greenlets.
5147

52-
if (hasattr(sys, '_current_frames') and
53-
parent.thread_id not in sys._current_frames()):
48+
if hasattr(sys, "_current_frames") and parent.thread_id not in sys._current_frames():
5449
return
5550

5651
co = frame.f_code
@@ -84,7 +79,7 @@ def _callable():
8479
except Exception:
8580
pass
8681

87-
if event in ['call', 'c_call']:
82+
if event in ["call", "c_call"]:
8883
# Skip the outermost as we catch that with the root
8984
# function traces for the profile trace.
9085

@@ -100,19 +95,17 @@ def _callable():
10095
self.function_traces.append(None)
10196
return
10297

103-
if event == 'call':
98+
if event == "call":
10499
func = _callable()
105100
if func:
106101
name = callable_name(func)
107102
else:
108-
name = '%s:%s#%s' % (func_filename, func_name,
109-
func_line_no)
103+
name = "%s:%s#%s" % (func_filename, func_name, func_line_no)
110104
else:
111105
func = arg
112106
name = callable_name(arg)
113107
if not name:
114-
name = '%s:@%s#%s' % (func_filename, func_name,
115-
func_line_no)
108+
name = "%s:@%s#%s" % (func_filename, func_name, func_line_no)
116109

117110
function_trace = FunctionTrace(name=name, parent=parent)
118111
function_trace.__enter__()
@@ -127,7 +120,7 @@ def _callable():
127120
self.function_traces.append(function_trace)
128121
self.current_depth += 1
129122

130-
elif event in ['return', 'c_return', 'c_exception']:
123+
elif event in ["return", "c_return", "c_exception"]:
131124
if not self.function_traces:
132125
return
133126

@@ -143,9 +136,7 @@ def _callable():
143136
self.current_depth -= 1
144137

145138

146-
def ProfileTraceWrapper(wrapped, name=None, group=None, label=None,
147-
params=None, depth=3):
148-
139+
def ProfileTraceWrapper(wrapped, name=None, group=None, label=None, params=None, depth=3):
149140
def wrapper(wrapped, instance, args, kwargs):
150141
parent = current_trace()
151142

@@ -192,7 +183,7 @@ def wrapper(wrapped, instance, args, kwargs):
192183
_params = params
193184

194185
with FunctionTrace(_name, _group, _label, _params, parent=parent, source=wrapped):
195-
if not hasattr(sys, 'getprofile'):
186+
if not hasattr(sys, "getprofile"):
196187
return wrapped(*args, **kwargs)
197188

198189
profiler = sys.getprofile()
@@ -212,11 +203,8 @@ def wrapper(wrapped, instance, args, kwargs):
212203

213204

214205
def profile_trace(name=None, group=None, label=None, params=None, depth=3):
215-
return functools.partial(ProfileTraceWrapper, name=name,
216-
group=group, label=label, params=params, depth=depth)
206+
return functools.partial(ProfileTraceWrapper, name=name, group=group, label=label, params=params, depth=depth)
217207

218208

219-
def wrap_profile_trace(module, object_path, name=None,
220-
group=None, label=None, params=None, depth=3):
221-
return wrap_object(module, object_path, ProfileTraceWrapper,
222-
(name, group, label, params, depth))
209+
def wrap_profile_trace(module, object_path, name=None, group=None, label=None, params=None, depth=3):
210+
return wrap_object(module, object_path, ProfileTraceWrapper, (name, group, label, params, depth))
+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Copyright 2010 New Relic, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
from testing_support.validators.validate_transaction_metrics import (
17+
validate_transaction_metrics,
18+
)
19+
20+
from newrelic.api.background_task import background_task
21+
from newrelic.api.profile_trace import ProfileTraceWrapper, profile_trace
22+
23+
24+
def test_profile_trace_wrapper():
25+
def _test():
26+
def nested_fn():
27+
pass
28+
29+
nested_fn()
30+
31+
wrapped_test = ProfileTraceWrapper(_test)
32+
wrapped_test()
33+
34+
35+
@validate_transaction_metrics("test_profile_trace:test_profile_trace_empty_args", background_task=True)
36+
@background_task()
37+
def test_profile_trace_empty_args():
38+
@profile_trace()
39+
def _test():
40+
pass
41+
42+
_test()
43+
44+
45+
_test_profile_trace_defined_args_scoped_metrics = [("Custom/TestTrace", 1)]
46+
47+
48+
@validate_transaction_metrics(
49+
"test_profile_trace:test_profile_trace_defined_args",
50+
scoped_metrics=_test_profile_trace_defined_args_scoped_metrics,
51+
background_task=True,
52+
)
53+
@background_task()
54+
def test_profile_trace_defined_args():
55+
@profile_trace(name="TestTrace", group="Custom", label="Label", params={"key": "value"}, depth=7)
56+
def _test():
57+
pass
58+
59+
_test()
60+
61+
62+
_test_profile_trace_callable_args_scoped_metrics = [("Function/TestProfileTrace", 1)]
63+
64+
65+
@validate_transaction_metrics(
66+
"test_profile_trace:test_profile_trace_callable_args",
67+
scoped_metrics=_test_profile_trace_callable_args_scoped_metrics,
68+
background_task=True,
69+
)
70+
@background_task()
71+
def test_profile_trace_callable_args():
72+
def name_callable():
73+
return "TestProfileTrace"
74+
75+
def group_callable():
76+
return "Function"
77+
78+
def label_callable():
79+
return "HSM"
80+
81+
def params_callable():
82+
return {"account_id": "12345"}
83+
84+
@profile_trace(name=name_callable, group=group_callable, label=label_callable, params=params_callable, depth=0)
85+
def _test():
86+
pass
87+
88+
_test()

0 commit comments

Comments
 (0)