Skip to content

Commit

Permalink
Feature/process resource detector (open-telemetry#3472)
Browse files Browse the repository at this point in the history
  • Loading branch information
bourbonkk authored Nov 22, 2023
1 parent f399195 commit de89b22
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 16 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#3251](https://github.com/open-telemetry/opentelemetry-python/pull/3251))
- Prometheus exporter support for auto instrumentation
([#3413](https://github.com/open-telemetry/opentelemetry-python/pull/3413))
- Implement Process Resource detector
([#3472](https://github.com/open-telemetry/opentelemetry-python/pull/3472))


## Version 1.20.0/0.41b0 (2023-09-04)

Expand Down
44 changes: 33 additions & 11 deletions opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import abc
import concurrent.futures
import logging
import os
import sys
import typing
from json import dumps
Expand All @@ -74,11 +75,15 @@
from opentelemetry.util._importlib_metadata import entry_points, version
from opentelemetry.util.types import AttributeValue

try:
import psutil
except ImportError:
psutil = None

LabelValue = AttributeValue
Attributes = typing.Dict[str, LabelValue]
logger = logging.getLogger(__name__)


CLOUD_PROVIDER = ResourceAttributes.CLOUD_PROVIDER
CLOUD_ACCOUNT_ID = ResourceAttributes.CLOUD_ACCOUNT_ID
CLOUD_REGION = ResourceAttributes.CLOUD_REGION
Expand Down Expand Up @@ -117,6 +122,7 @@
OS_TYPE = ResourceAttributes.OS_TYPE
OS_DESCRIPTION = ResourceAttributes.OS_DESCRIPTION
PROCESS_PID = ResourceAttributes.PROCESS_PID
PROCESS_PARENT_PID = ResourceAttributes.PROCESS_PARENT_PID
PROCESS_EXECUTABLE_NAME = ResourceAttributes.PROCESS_EXECUTABLE_NAME
PROCESS_EXECUTABLE_PATH = ResourceAttributes.PROCESS_EXECUTABLE_PATH
PROCESS_COMMAND = ResourceAttributes.PROCESS_COMMAND
Expand All @@ -135,7 +141,6 @@
TELEMETRY_AUTO_VERSION = ResourceAttributes.TELEMETRY_AUTO_VERSION
TELEMETRY_SDK_LANGUAGE = ResourceAttributes.TELEMETRY_SDK_LANGUAGE


_OPENTELEMETRY_SDK_VERSION = version("opentelemetry-sdk")


Expand Down Expand Up @@ -180,7 +185,6 @@ def create(
otel_experimental_resource_detectors.append("otel")

for resource_detector in otel_experimental_resource_detectors:

resource_detectors.append(
next(
iter(
Expand Down Expand Up @@ -337,14 +341,32 @@ def detect(self) -> "Resource":
else sys.version_info,
)
)

return Resource(
{
PROCESS_RUNTIME_DESCRIPTION: sys.version,
PROCESS_RUNTIME_NAME: sys.implementation.name,
PROCESS_RUNTIME_VERSION: _runtime_version,
}
)
_process_pid = os.getpid()
_process_executable_name = sys.executable
_process_executable_path = os.path.dirname(_process_executable_name)
_process_command = sys.argv[0]
_process_command_line = " ".join(sys.argv)
_process_command_args = sys.argv[1:]
resource_info = {
PROCESS_RUNTIME_DESCRIPTION: sys.version,
PROCESS_RUNTIME_NAME: sys.implementation.name,
PROCESS_RUNTIME_VERSION: _runtime_version,
PROCESS_PID: _process_pid,
PROCESS_EXECUTABLE_NAME: _process_executable_name,
PROCESS_EXECUTABLE_PATH: _process_executable_path,
PROCESS_COMMAND: _process_command,
PROCESS_COMMAND_LINE: _process_command_line,
PROCESS_COMMAND_ARGS: _process_command_args,
}
if hasattr(os, "getppid"):
# pypy3 does not have getppid()
resource_info[PROCESS_PARENT_PID] = os.getppid()

if psutil is not None:
process = psutil.Process()
resource_info[PROCESS_OWNER] = process.username()

return Resource(resource_info)


def get_aggregated_resources(
Expand Down
61 changes: 56 additions & 5 deletions opentelemetry-sdk/tests/resources/test_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@
# 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=protected-access

import os
import sys
import unittest
import uuid
from logging import ERROR, WARNING
Expand All @@ -30,7 +29,14 @@
_OPENTELEMETRY_SDK_VERSION,
OTEL_RESOURCE_ATTRIBUTES,
OTEL_SERVICE_NAME,
PROCESS_COMMAND,
PROCESS_COMMAND_ARGS,
PROCESS_COMMAND_LINE,
PROCESS_EXECUTABLE_NAME,
PROCESS_EXECUTABLE_PATH,
PROCESS_OWNER,
PROCESS_PARENT_PID,
PROCESS_PID,
PROCESS_RUNTIME_DESCRIPTION,
PROCESS_RUNTIME_NAME,
PROCESS_RUNTIME_VERSION,
Expand All @@ -45,6 +51,11 @@
get_aggregated_resources,
)

try:
import psutil
except ImportError:
psutil = None


class TestResources(unittest.TestCase):
def setUp(self) -> None:
Expand Down Expand Up @@ -524,6 +535,10 @@ def test_service_name_env_precedence(self):
Resource({"service.name": "from-service-name"}),
)

@patch(
"sys.argv",
["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"],
)
def test_process_detector(self):
initial_resource = Resource({"foo": "bar"})
aggregated_resource = get_aggregated_resources(
Expand All @@ -543,8 +558,42 @@ def test_process_detector(self):
aggregated_resource.attributes.keys(),
)

def test_resource_detector_entry_points_default(self):
self.assertEqual(
aggregated_resource.attributes[PROCESS_PID], os.getpid()
)
if hasattr(os, "getppid"):
self.assertEqual(
aggregated_resource.attributes[PROCESS_PARENT_PID],
os.getppid(),
)

if psutil is not None:
self.assertEqual(
aggregated_resource.attributes[PROCESS_OWNER],
psutil.Process().username(),
)

self.assertEqual(
aggregated_resource.attributes[PROCESS_EXECUTABLE_NAME],
sys.executable,
)
self.assertEqual(
aggregated_resource.attributes[PROCESS_EXECUTABLE_PATH],
os.path.dirname(sys.executable),
)
self.assertEqual(
aggregated_resource.attributes[PROCESS_COMMAND], sys.argv[0]
)
self.assertEqual(
aggregated_resource.attributes[PROCESS_COMMAND_LINE],
" ".join(sys.argv),
)
self.assertEqual(
aggregated_resource.attributes[PROCESS_COMMAND_ARGS],
tuple(sys.argv[1:]),
)

def test_resource_detector_entry_points_default(self):
resource = Resource({}).create()

self.assertEqual(
Expand Down Expand Up @@ -644,7 +693,9 @@ def test_resource_detector_entry_points_otel(self):
resource.attributes["telemetry.sdk.name"], "opentelemetry"
)
self.assertEqual(
resource.attributes["service.name"], "unknown_service"
resource.attributes["service.name"],
"unknown_service:"
+ resource.attributes["process.executable.name"],
)
self.assertEqual(resource.attributes["a"], "b")
self.assertEqual(resource.attributes["c"], "d")
Expand Down

0 comments on commit de89b22

Please sign in to comment.