Skip to content

Commit

Permalink
calculate_metrics|logs_enabled simplified and only used by Distro to …
Browse files Browse the repository at this point in the history
…load_instrumentor
  • Loading branch information
tammy-baylis-swi committed Feb 5, 2025
1 parent ed56890 commit b2b2222
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 137 deletions.
88 changes: 54 additions & 34 deletions solarwinds_apm/apm_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

# pylint: disable=too-many-lines

import json
import logging
import os
Expand Down Expand Up @@ -169,10 +171,6 @@ def __init__(
# (Re-)Calculate config if AppOptics
self.metric_format = self._calculate_metric_format()
self.certificates = self._calculate_certificates()
self.__config["export_logs_enabled"] = self._calculate_logs_enabled()
self.__config["export_metrics_enabled"] = (
self._calculate_metrics_enabled()
)

logger.debug("Set ApmConfig as: %s", self)

Expand Down Expand Up @@ -279,6 +277,58 @@ def calculate_is_lambda(cls) -> bool:
return True
return False

# TODO Make cnf_dict required
@classmethod
def calculate_logs_enabled(
cls,
cnf_dict: dict = None,
) -> bool:
"""Return if export of instrumentor logs telemetry enabled.
Invalid boolean values are ignored.
Order of precedence: Environment Variable > config file > default True.
Optional cnf_dict is presumably already from a config file, else a call
to get_cnf_dict() is made for a fresh read."""
logs_enabled = True
if cnf_dict is None:
cnf_dict = cls.get_cnf_dict()
if cnf_dict:
cnf_enabled = cls.convert_to_bool(
cnf_dict.get("export_logs_enabled")
)
logs_enabled = (
cnf_enabled if cnf_enabled is not None else logs_enabled
)
env_enabled = cls.convert_to_bool(
os.environ.get("SW_APM_EXPORT_LOGS_ENABLED")
)
return env_enabled if env_enabled is not None else logs_enabled

# TODO Make cnf_dict required
@classmethod
def calculate_metrics_enabled(
cls,
cnf_dict: dict = None,
) -> bool:
"""Return if export of instrumentor metrics telemetry enabled.
Invalid boolean values are ignored.
Order of precedence: Environment Variable > config file > default True.
Optional cnf_dict is presumably already from a config file, else a call
to get_cnf_dict() is made for a fresh read."""
metrics_enabled = True
if cnf_dict is None:
cnf_dict = cls.get_cnf_dict()
if cnf_dict:
cnf_enabled = cls.convert_to_bool(
cnf_dict.get("export_metrics_enabled")
)
metrics_enabled = (
cnf_enabled if cnf_enabled is not None else metrics_enabled
)
env_enabled = cls.convert_to_bool(
os.environ.get("SW_APM_EXPORT_metrics_ENABLED")
)
return env_enabled if env_enabled is not None else metrics_enabled

def _calculate_agent_enabled_platform(self) -> bool:
"""Checks if agent is enabled/disabled based on platform"""
if not sys.platform.startswith("linux"):
Expand Down Expand Up @@ -649,36 +699,6 @@ def _calculate_certificates(self) -> str:
)
return certs

def _calculate_logs_enabled(self) -> bool:
"""Return if export of logs telemetry enabled, based on collector.
Always False if AO collector, else use current config."""
host = self.get("collector")
if host:
if (
INTL_SWO_AO_COLLECTOR in host
or INTL_SWO_AO_STG_COLLECTOR in host
):
logger.warning(
"AO collector detected. Defaulting to disabled logs export."
)
return False
return self.get("export_logs_enabled")

def _calculate_metrics_enabled(self) -> bool:
"""Return if export of metrics telemetry enabled, based on collector.
Always False if AO collector, else use current config."""
host = self.get("collector")
if host:
if (
INTL_SWO_AO_COLLECTOR in host
or INTL_SWO_AO_STG_COLLECTOR in host
):
logger.warning(
"AO collector detected. Defaulting to disabled OTLP metrics export."
)
return False
return self.get("export_metrics_enabled")

def mask_service_key(self) -> str:
"""Return masked service key except first 4 and last 4 chars"""
service_key = self.__config.get("service_key")
Expand Down
15 changes: 15 additions & 0 deletions solarwinds_apm/distro.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from os import environ
from typing import Any

from opentelemetry._logs import NoOpLoggerProvider
from opentelemetry.environment_variables import (
OTEL_PROPAGATORS,
OTEL_TRACES_EXPORTER,
Expand All @@ -22,6 +23,7 @@
OTEL_PYTHON_LOG_FORMAT,
)
from opentelemetry.instrumentation.version import __version__ as inst_version
from opentelemetry.metrics import NoOpMeterProvider
from opentelemetry.sdk.environment_variables import (
_OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED,
OTEL_EXPORTER_OTLP_PROTOCOL,
Expand Down Expand Up @@ -283,6 +285,19 @@ def load_instrumentor(self, entry_point: EntryPoint, **kwargs):
kwargs,
)

# If SW_APM_EXPORT_(METRICS|LOGS)_ENABLED is set to false,
# then we load the instrumentor with NoOp providers to disable
# Otel instrumentor-based metrics/logs export if supported.
# Assumes kwargs are ignored if not implemented
# for the current instrumentation library.
if (
SolarWindsApmConfig.calculate_metrics_enabled(self._cnf_dict)
is False
):
kwargs["meter_provider"] = NoOpMeterProvider()
if SolarWindsApmConfig.calculate_logs_enabled(self._cnf_dict) is False:
kwargs["logger_provider"] = NoOpLoggerProvider()

try:
instrumentor: BaseInstrumentor = entry_point.load()
except Exception as ex: # pylint: disable=broad-except
Expand Down
103 changes: 0 additions & 103 deletions tests/unit/test_apm_config/test_apm_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,14 +379,9 @@ def test__init_ao_settings_helpers_called(self, mocker):
mock_certs = mocker.patch(
"solarwinds_apm.apm_config.SolarWindsApmConfig._calculate_certificates"
)
mock_logs_enabled = mocker.patch(
"solarwinds_apm.apm_config.SolarWindsApmConfig._calculate_logs_enabled"
)

apm_config.SolarWindsApmConfig()
mock_metric_format.assert_called_once()
mock_certs.assert_called_once()
mock_logs_enabled.assert_called_once()

def test_calculate_metric_format_no_collector(self, mocker):
assert apm_config.SolarWindsApmConfig()._calculate_metric_format() == 2
Expand Down Expand Up @@ -539,104 +534,6 @@ def test_calculate_certificates_ao_prod_trustedpath_file_present(self, mocker):
mock_get_public_cert.configure_mock(return_value="foo")
assert apm_config.SolarWindsApmConfig()._calculate_certificates() == "bar"

def test_calculate_logs_enabled_no_collector_enabled(self, mocker):
mocker.patch.dict(os.environ, {
"SW_APM_COLLECTOR": "",
"SW_APM_EXPORT_LOGS_ENABLED": "true",
})
assert apm_config.SolarWindsApmConfig()._calculate_logs_enabled() == True

def test_calculate_logs_enabled_no_collector_disabled(self, mocker):
mocker.patch.dict(os.environ, {
"SW_APM_COLLECTOR": "",
"SW_APM_EXPORT_LOGS_ENABLED": "false",
})
assert apm_config.SolarWindsApmConfig()._calculate_logs_enabled() == False

def test_calculate_logs_enabled_not_ao_enabled(self, mocker):
mocker.patch.dict(os.environ, {
"SW_APM_COLLECTOR": "some-other-collector",
"SW_APM_EXPORT_LOGS_ENABLED": "true",
})
assert apm_config.SolarWindsApmConfig()._calculate_logs_enabled() == True

def test_calculate_logs_enabled_not_ao_disabled(self, mocker):
mocker.patch.dict(os.environ, {
"SW_APM_COLLECTOR": "some-other-collector",
"SW_APM_EXPORT_LOGS_ENABLED": "false",
})
assert apm_config.SolarWindsApmConfig()._calculate_logs_enabled() == False

def test_calculate_logs_enabled_ao_prod(self, mocker):
mocker.patch.dict(os.environ, {
"SW_APM_COLLECTOR": INTL_SWO_AO_COLLECTOR,
"SW_APM_EXPORT_LOGS_ENABLED": "true",
})
assert apm_config.SolarWindsApmConfig()._calculate_logs_enabled() == False

def test_calculate_logs_enabled_ao_staging(self, mocker):
mocker.patch.dict(os.environ, {
"SW_APM_COLLECTOR": INTL_SWO_AO_STG_COLLECTOR,
"SW_APM_EXPORT_LOGS_ENABLED": "true",
})
assert apm_config.SolarWindsApmConfig()._calculate_logs_enabled() == False

def test_calculate_logs_enabled_ao_prod_with_port(self, mocker):
mocker.patch.dict(os.environ, {
"SW_APM_COLLECTOR": "{}:123".format(INTL_SWO_AO_COLLECTOR),
"SW_APM_EXPORT_LOGS_ENABLED": "true",
})
assert apm_config.SolarWindsApmConfig()._calculate_logs_enabled() == False

def test_calculate_metrics_enabled_no_collector_enabled(self, mocker):
mocker.patch.dict(os.environ, {
"SW_APM_COLLECTOR": "",
"SW_APM_EXPORT_METRICS_ENABLED": "true",
})
assert apm_config.SolarWindsApmConfig()._calculate_metrics_enabled() == True

def test_calculate_metrics_enabled_no_collector_disabled(self, mocker):
mocker.patch.dict(os.environ, {
"SW_APM_COLLECTOR": "",
"SW_APM_EXPORT_METRICS_ENABLED": "false",
})
assert apm_config.SolarWindsApmConfig()._calculate_metrics_enabled() == False

def test_calculate_metrics_enabled_not_ao_enabled(self, mocker):
mocker.patch.dict(os.environ, {
"SW_APM_COLLECTOR": "some-other-collector",
"SW_APM_EXPORT_METRICS_ENABLED": "true",
})
assert apm_config.SolarWindsApmConfig()._calculate_metrics_enabled() == True

def test_calculate_metrics_enabled_not_ao_disabled(self, mocker):
mocker.patch.dict(os.environ, {
"SW_APM_COLLECTOR": "some-other-collector",
"SW_APM_EXPORT_METRICS_ENABLED": "false",
})
assert apm_config.SolarWindsApmConfig()._calculate_metrics_enabled() == False

def test_calculate_metrics_enabled_ao_prod(self, mocker):
mocker.patch.dict(os.environ, {
"SW_APM_COLLECTOR": INTL_SWO_AO_COLLECTOR,
"SW_APM_EXPORT_METRICS_ENABLED": "true",
})
assert apm_config.SolarWindsApmConfig()._calculate_metrics_enabled() == False

def test_calculate_metrics_enabled_ao_staging(self, mocker):
mocker.patch.dict(os.environ, {
"SW_APM_COLLECTOR": INTL_SWO_AO_STG_COLLECTOR,
"SW_APM_EXPORT_METRICS_ENABLED": "true",
})
assert apm_config.SolarWindsApmConfig()._calculate_metrics_enabled() == False

def test_calculate_metrics_enabled_ao_prod_with_port(self, mocker):
mocker.patch.dict(os.environ, {
"SW_APM_COLLECTOR": "{}:123".format(INTL_SWO_AO_COLLECTOR),
"SW_APM_EXPORT_METRICS_ENABLED": "true",
})
assert apm_config.SolarWindsApmConfig()._calculate_metrics_enabled() == False

def test_mask_service_key_no_key_empty_default(self, mocker):
mock_entry_points = mocker.patch(
"solarwinds_apm.apm_config.entry_points"
Expand Down

0 comments on commit b2b2222

Please sign in to comment.