Skip to content
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
43 changes: 39 additions & 4 deletions solarwinds_apm/k8s.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@


def _pod_name() -> str:
"""
Get Kubernetes pod name from environment or system hostname.

Returns:
str: The pod name.
"""
env = os.getenv(NAME_ENV)
if env:
logger.debug("read pod name from env")
Expand All @@ -39,6 +45,15 @@ def _pod_name() -> str:


def _pod_uid(mount_info: str) -> str | None:
"""
Get Kubernetes pod UID from environment or mountinfo file.

Parameters:
mount_info (str): Path to mountinfo file.

Returns:
str | None: The pod UID if found, None otherwise.
"""
env = os.getenv(UID_ENV)
if env:
logger.debug("read pod uid from env")
Expand Down Expand Up @@ -70,6 +85,15 @@ def _pod_uid(mount_info: str) -> str | None:


def _pod_namespace(namespace: str) -> str | None:
"""
Get Kubernetes namespace from environment or namespace file.

Parameters:
namespace (str): Path to namespace file.

Returns:
str | None: The namespace if found, None otherwise.
"""
env = os.getenv(NAMESPACE_ENV)
if env:
logger.debug("read pod namespace from env")
Expand All @@ -84,18 +108,29 @@ def _pod_namespace(namespace: str) -> str | None:


class K8sResourceDetector(ResourceDetector):
"""Detects attribute values only available when the app is running on k8s
and returns them in a Resource.
"""
"""Detect Kubernetes resource attributes when running in a Kubernetes environment."""

def __init__(
self, namespace: str = NAMESPACE_FILE, mountinfo: str = MOUNTINFO_FILE
):
) -> None:
"""
Initialize K8s Resource Detector.

Parameters:
namespace (str): Path to namespace file. Defaults to NAMESPACE_FILE.
mountinfo (str): Path to mountinfo file. Defaults to MOUNTINFO_FILE.
"""
super().__init__()
self._namespace = namespace
self._mountinfo = mountinfo

def detect(self) -> Resource:
"""
Detect Kubernetes resource attributes.

Returns:
Resource: Resource with K8s namespace, pod UID, and pod name attributes if available.
"""
attributes = {}

namespace = _pod_namespace(self._namespace)
Expand Down
12 changes: 12 additions & 0 deletions solarwinds_apm/tracer_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,27 @@
#
# 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.

"""SolarWinds TracerProvider with custom shutdown behavior."""

from opentelemetry.sdk.trace import TracerProvider
from typing_extensions import override

from solarwinds_apm.oboe.http_sampler import HttpSampler


class SolarwindsTracerProvider(TracerProvider):
"""Provide custom TracerProvider with HttpSampler shutdown support.

Extends OpenTelemetry TracerProvider to properly shutdown HttpSampler.
"""

@override
def shutdown(self) -> None:
"""
Shutdown the tracer provider and its sampler.

Ensures HttpSampler daemon thread is properly terminated before parent shutdown.
"""
if isinstance(self.sampler, HttpSampler):
self.sampler.shutdown()
super().shutdown()
35 changes: 30 additions & 5 deletions solarwinds_apm/uams.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@


def _read_from_file(uams_file: str) -> dict:
"""
Read UAMS client ID from file.

Parameters:
uams_file (str): Path to UAMS client ID file.

Returns:
dict: Dictionary with UAMS client ID and host ID attributes, or empty dict on error.
"""
try:
with open(uams_file, encoding="utf-8") as file:
uams_id = file.read().strip()
Expand All @@ -46,6 +55,12 @@ def _read_from_file(uams_file: str) -> dict:


def _read_from_api() -> dict:
"""
Read UAMS client ID from local API endpoint.

Returns:
dict: Dictionary with UAMS client ID and host ID attributes, or empty dict on error.
"""
try:
token = attach(set_value(_SUPPRESS_INSTRUMENTATION_KEY, True))
response = requests.get(UAMS_CLIENT_URL, timeout=1)
Expand All @@ -68,14 +83,24 @@ def _read_from_api() -> dict:


class UamsResourceDetector(ResourceDetector):
"""Detects attribute values only available when the app is running on k8s
and returns them in a Resource.
"""
"""Detect UAMS client attributes for SolarWinds APM resource identification."""

def __init__(self, uams=UAMS_CLIENT_PATH):
def __init__(self, uams: str = UAMS_CLIENT_PATH) -> None:
"""
Initialize UAMS Resource Detector.

Parameters:
uams (str): Path to UAMS client ID file. Defaults to UAMS_CLIENT_PATH.
"""
super().__init__()
self._uams = uams

def detect(self) -> "Resource":
def detect(self) -> Resource:
"""
Detect UAMS resource attributes.

Returns:
Resource: Resource with UAMS client ID and host ID attributes if available.
"""
attributes = _read_from_file(self._uams) or _read_from_api() or {}
return Resource(attributes)
2 changes: 2 additions & 0 deletions solarwinds_apm/version.py
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
"""SolarWinds APM version information."""

__version__ = "5.2.0"
Loading