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

NH-65075 Add support for SW_APM_TRANSACTION_NAME #206

Merged
merged 6 commits into from
Oct 20, 2023
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased](https://github.com/solarwindscloud/solarwinds-apm-python/compare/rel-0.17.0...HEAD)

### Added
- Add support for `SW_APM_TRANSACTION_NAME` ([#206](https://github.com/solarwindscloud/solarwinds-apm-python/pull/206))

## [0.17.0](https://github.com/solarwindscloud/solarwinds-apm-python/releases/tag/rel-0.17.0) - 2023-09-20

### Changed
Expand Down
2 changes: 2 additions & 0 deletions solarwinds_apm/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ def set_transaction_name(custom_name: str) -> bool:
Assign a custom transaction name to a current request. If multiple
transaction names are set on the same trace, then the last one is used.
Overrides default, out-of-the-box naming based on URL/controller/action.
Takes precedence over transaction_name set in environment variable or
config file.

Any uppercase to lowercase conversions or special character replacements
are done by the platform. Any truncations are done by the core extension.
Expand Down
3 changes: 3 additions & 0 deletions solarwinds_apm/apm_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ def __init__(
"reporter_file_single": 0,
"proxy": "",
"transaction_filters": [],
"transaction_name": None,
}
self.agent_enabled = True
self.update_with_cnf_file()
Expand Down Expand Up @@ -708,6 +709,8 @@ def _set_config_value(self, keys_str: str, val: Any) -> Any:
self.__config[key] = val
# update logging level of agent logger
apm_logging.set_sw_log_level(val)
elif keys == ["transaction_name"]:
self.__config[key] = val
elif isinstance(sub_dict, dict) and keys[-1] in sub_dict:
if isinstance(sub_dict[keys[-1]], bool):
val = self.convert_to_bool(val)
Expand Down
4 changes: 4 additions & 0 deletions solarwinds_apm/inbound_metrics_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@


class SolarWindsInboundMetricsSpanProcessor(SpanProcessor):
_TRANSACTION_NAME = "transaction_name"
_HTTP_METHOD = SpanAttributes.HTTP_METHOD # "http.method"
_HTTP_ROUTE = SpanAttributes.HTTP_ROUTE # "http.route"
_HTTP_STATUS_CODE = SpanAttributes.HTTP_STATUS_CODE # "http.status_code"
Expand All @@ -40,6 +41,7 @@ def __init__(
) -> None:
self.apm_txname_manager = apm_txname_manager
self._span = apm_config.extension.Span
self.config_transaction_name = apm_config.get(self._TRANSACTION_NAME)

def on_start(
self,
Expand Down Expand Up @@ -163,6 +165,8 @@ def calculate_transaction_names(
http_route = span.attributes.get(self._HTTP_ROUTE, None)
trans_name = None
custom_trans_name = self.calculate_custom_transaction_name(span)
if not custom_trans_name:
custom_trans_name = self.config_transaction_name

if custom_trans_name:
trans_name = custom_trans_name
Expand Down
81 changes: 80 additions & 1 deletion tests/unit/test_inbound_metrics_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -876,9 +876,16 @@ def test_calculate_transaction_names_span_name_default(self, mocker):
}
}
)
mock_get = mocker.Mock(return_value=None)
mock_apm_config = mocker.Mock()
mock_apm_config.configure_mock(
**{
"get": mock_get
}
)
processor = SolarWindsInboundMetricsSpanProcessor(
mocker.Mock(),
mocker.Mock(),
mock_apm_config,
)
assert ("foo", None) == processor.calculate_transaction_names(mock_span)

Expand Down Expand Up @@ -911,6 +918,78 @@ def test_calculate_transaction_names_custom(self, mocker):
result = processor.calculate_transaction_names(mock_span)
assert "foo", "bar" == result

def test_calculate_transaction_names_yes_custom_yes_config(self, mocker):
mock_spanattributes = mocker.patch(
"solarwinds_apm.inbound_metrics_processor.SpanAttributes"
)
mock_spanattributes.configure_mock(
**{
"HTTP_URL": "http.url",
"HTTP_ROUTE": "http.route"
}
)
mock_calculate_custom = mocker.patch(
"solarwinds_apm.inbound_metrics_processor.SolarWindsInboundMetricsSpanProcessor.calculate_custom_transaction_name"
)
mock_calculate_custom.configure_mock(return_value="foo")
mock_span = mocker.Mock()
mock_span.configure_mock(
**{
"attributes": {
"http.url": "bar"
}
}
)
mock_get = mocker.Mock(return_value="not-used")
mock_apm_config = mocker.Mock()
mock_apm_config.configure_mock(
**{
"get": mock_get
}
)
processor = SolarWindsInboundMetricsSpanProcessor(
mocker.Mock(),
mock_apm_config,
)
result = processor.calculate_transaction_names(mock_span)

Check notice

Code scanning / CodeQL

Unused local variable

Variable result is not used.
assert "foo", "bar" == result

def test_calculate_transaction_names_no_custom_yes_config(self, mocker):
mock_spanattributes = mocker.patch(
"solarwinds_apm.inbound_metrics_processor.SpanAttributes"
)
mock_spanattributes.configure_mock(
**{
"HTTP_URL": "http.url",
"HTTP_ROUTE": "http.route"
}
)
mock_calculate_custom = mocker.patch(
"solarwinds_apm.inbound_metrics_processor.SolarWindsInboundMetricsSpanProcessor.calculate_custom_transaction_name"
)
mock_calculate_custom.configure_mock(return_value=None)
mock_span = mocker.Mock()
mock_span.configure_mock(
**{
"attributes": {
"http.url": "bar"
}
}
)
mock_get = mocker.Mock(return_value="foo")
mock_apm_config = mocker.Mock()
mock_apm_config.configure_mock(
**{
"get": mock_get
}
)
processor = SolarWindsInboundMetricsSpanProcessor(
mocker.Mock(),
mock_apm_config,
)
result = processor.calculate_transaction_names(mock_span)

Check notice

Code scanning / CodeQL

Unused local variable

Variable result is not used.
assert "foo", "bar" == result

def test_calculate_transaction_names_http_route(self, mocker):
mock_spanattributes = mocker.patch(
"solarwinds_apm.inbound_metrics_processor.SpanAttributes"
Expand Down