Skip to content

Commit 1075364

Browse files
committed
refactor(agent): ServerlessAgent with Span Filter
Introduce a new ServerlessAgent abstract base class to eliminate code duplication across serverless platforms and add support for the Span Filtering feature. This refactoring follows the Template Method pattern, allowing platform-specific customization through abstract methods while maintaining a single source of truth for the common serverless agent behavior. Signed-off-by: Paulo Vital <paulo.vital@ibm.com>
1 parent c60f7c9 commit 1075364

9 files changed

Lines changed: 574 additions & 434 deletions

File tree

Lines changed: 19 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,105 +1,39 @@
1-
# (c) Copyright IBM Corp. 2023
1+
# (c) Copyright IBM Corp. 2023, 2026
22

33
"""
44
The Instana agent (for AWS EKS Fargate) that manages
55
monitoring state and reporting that data.
66
"""
77

8-
from instana.agent.base import BaseAgent
8+
from instana.agent.serverless import ServerlessAgent
99
from instana.collector.aws_eks_fargate import EKSFargateCollector
1010
from instana.collector.helpers.eks.process import get_pod_name
11-
from instana.log import logger
1211
from instana.options import EKSFargateOptions
13-
from instana.util import to_json
14-
from instana.version import VERSION
1512

1613

17-
class EKSFargateAgent(BaseAgent):
18-
"""In-process agent for AWS Fargate"""
19-
20-
def __init__(self):
21-
super(EKSFargateAgent, self).__init__()
14+
class EKSFargateAgent(ServerlessAgent):
15+
"""In-process agent for AWS EKS Fargate"""
2216

17+
def _initialize_platform(self) -> None:
18+
"""Initialize EKS Fargate specific options and pod name."""
2319
self.options = EKSFargateOptions()
24-
self.collector = None
25-
self.report_headers = None
26-
self._can_send = False
2720
self.podname = get_pod_name()
2821

29-
# Update log level (if INSTANA_LOG_LEVEL was set)
30-
self.update_log_level()
31-
32-
logger.info(
33-
"Stan is on the EKS Pod on AWS Fargate scene. Starting Instana instrumentation version: %s",
34-
VERSION,
35-
)
36-
37-
if self._validate_options():
38-
self._can_send = True
39-
self.collector = EKSFargateCollector(self)
40-
self.collector.start()
41-
else:
42-
logger.warning(
43-
"Required INSTANA_AGENT_KEY and/or INSTANA_ENDPOINT_URL environment variables not set. "
44-
"We will not be able to monitor this Pod."
45-
)
46-
47-
def can_send(self):
48-
"""
49-
Are we in a state where we can send data?
50-
@return: Boolean
51-
"""
52-
return self._can_send
53-
54-
def get_from_structure(self):
55-
"""
56-
Retrieves the From data that is reported alongside monitoring data.
57-
@return: dict()
58-
"""
59-
60-
return {"hl": True, "cp": "k8s", "e": self.podname}
22+
def _create_collector(self) -> EKSFargateCollector:
23+
"""Create EKS Fargate collector."""
24+
return EKSFargateCollector(self)
6125

62-
def report_data_payload(self, payload):
63-
"""
64-
Used to report metrics and span data to the endpoint URL in self.options.endpoint_url
65-
"""
66-
response = None
67-
try:
68-
if self.report_headers is None:
69-
# Prepare request headers
70-
self.report_headers = dict()
71-
self.report_headers["Content-Type"] = "application/json"
72-
self.report_headers["X-Instana-Host"] = self.podname
73-
self.report_headers["X-Instana-Key"] = self.options.agent_key
26+
def _get_entity_id(self) -> str:
27+
"""Get Kubernetes pod name."""
28+
return self.podname
7429

75-
response = self.client.post(
76-
self.__data_bundle_url(),
77-
data=to_json(payload),
78-
headers=self.report_headers,
79-
timeout=self.options.timeout,
80-
verify=self.options.ssl_verify,
81-
proxies=self.options.endpoint_proxy,
82-
)
30+
def _get_cloud_provider(self) -> str:
31+
"""Kubernetes cloud provider."""
32+
return "k8s"
8333

84-
if not 200 <= response.status_code < 300:
85-
logger.info(
86-
"report_data_payload: Instana responded with status code %s",
87-
response.status_code,
88-
)
89-
except Exception as exc:
90-
logger.debug("report_data_payload: connection error (%s)", type(exc))
91-
return response
34+
def _get_platform_name(self) -> str:
35+
"""Platform name for logging."""
36+
return "EKS Pod on AWS Fargate"
9237

93-
def _validate_options(self):
94-
"""
95-
Validate that the options used by this Agent are valid. e.g. can we report data?
96-
"""
97-
return (
98-
self.options.endpoint_url is not None and self.options.agent_key is not None
99-
)
10038

101-
def __data_bundle_url(self):
102-
"""
103-
URL for posting metrics to the host agent. Only valid when announced.
104-
"""
105-
return f"{self.options.endpoint_url}/bundle"
39+
# Made with Bob

src/instana/agent/aws_fargate.py

Lines changed: 18 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,38 @@
1-
# (c) Copyright IBM Corp. 2021
1+
# (c) Copyright IBM Corp. 2021, 2026
22
# (c) Copyright Instana Inc. 2020
33

44
"""
55
The Instana agent (for AWS Fargate) that manages
66
monitoring state and reporting that data.
77
"""
88

9+
from instana.agent.serverless import ServerlessAgent
910
from instana.collector.aws_fargate import AWSFargateCollector
1011
from instana.options import AWSFargateOptions
1112

12-
from ..log import logger
13-
from ..util import to_json
14-
from ..version import VERSION
15-
from .base import BaseAgent
1613

17-
18-
class AWSFargateAgent(BaseAgent):
14+
class AWSFargateAgent(ServerlessAgent):
1915
"""In-process agent for AWS Fargate"""
2016

21-
def __init__(self):
22-
super(AWSFargateAgent, self).__init__()
23-
17+
def _initialize_platform(self) -> None:
18+
"""Initialize AWS Fargate specific options."""
2419
self.options = AWSFargateOptions()
25-
self.collector = None
26-
self.report_headers = None
27-
self._can_send = False
28-
29-
# Update log level (if INSTANA_LOG_LEVEL was set)
30-
self.update_log_level()
31-
32-
logger.info(
33-
"Stan is on the AWS Fargate scene. Starting Instana instrumentation version: %s",
34-
VERSION,
35-
)
36-
37-
if self._validate_options():
38-
self._can_send = True
39-
self.collector = AWSFargateCollector(self)
40-
self.collector.start()
41-
else:
42-
logger.warning(
43-
"Required INSTANA_AGENT_KEY and/or INSTANA_ENDPOINT_URL environment variables not set. "
44-
"We will not be able monitor this AWS Fargate cluster."
45-
)
46-
47-
def can_send(self):
48-
"""
49-
Are we in a state where we can send data?
50-
@return: Boolean
51-
"""
52-
return self._can_send
5320

54-
def get_from_structure(self):
55-
"""
56-
Retrieves the From data that is reported alongside monitoring data.
57-
@return: dict()
58-
"""
59-
return {"hl": True, "cp": "aws", "e": self.collector.get_fq_arn()}
21+
def _create_collector(self) -> AWSFargateCollector:
22+
"""Create AWS Fargate collector."""
23+
return AWSFargateCollector(self)
6024

61-
def report_data_payload(self, payload):
62-
"""
63-
Used to report metrics and span data to the endpoint URL in self.options.endpoint_url
64-
"""
65-
response = None
66-
try:
67-
if self.report_headers is None:
68-
# Prepare request headers
69-
self.report_headers = dict()
70-
self.report_headers["Content-Type"] = "application/json"
71-
self.report_headers["X-Instana-Host"] = self.collector.get_fq_arn()
72-
self.report_headers["X-Instana-Key"] = self.options.agent_key
25+
def _get_entity_id(self) -> str:
26+
"""Get Fargate task ARN."""
27+
return self.collector.get_fq_arn()
7328

74-
response = self.client.post(
75-
self.__data_bundle_url(),
76-
data=to_json(payload),
77-
headers=self.report_headers,
78-
timeout=self.options.timeout,
79-
verify=self.options.ssl_verify,
80-
proxies=self.options.endpoint_proxy,
81-
)
29+
def _get_cloud_provider(self) -> str:
30+
"""AWS cloud provider."""
31+
return "aws"
8232

83-
if not 200 <= response.status_code < 300:
84-
logger.info(
85-
"report_data_payload: Instana responded with status code %s",
86-
response.status_code,
87-
)
88-
except Exception as exc:
89-
logger.debug("report_data_payload: connection error (%s)", type(exc))
90-
return response
33+
def _get_platform_name(self) -> str:
34+
"""Platform name for logging."""
35+
return "AWS Fargate"
9136

92-
def _validate_options(self):
93-
"""
94-
Validate that the options used by this Agent are valid. e.g. can we report data?
95-
"""
96-
return (
97-
self.options.endpoint_url is not None and self.options.agent_key is not None
98-
)
9937

100-
def __data_bundle_url(self):
101-
"""
102-
URL for posting metrics to the host agent. Only valid when announced.
103-
"""
104-
return f"{self.options.endpoint_url}/bundle"
38+
# Made with Bob

src/instana/agent/aws_lambda.py

Lines changed: 18 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,38 @@
1-
# (c) Copyright IBM Corp. 2021
1+
# (c) Copyright IBM Corp. 2021, 2026
22
# (c) Copyright Instana Inc. 2020
33

44
"""
55
The Instana Agent for AWS Lambda functions that manages
66
monitoring state and reporting that data.
77
"""
88

9-
from typing import Any, Dict
10-
from instana.agent.base import BaseAgent
9+
from instana.agent.serverless import ServerlessAgent
1110
from instana.collector.aws_lambda import AWSLambdaCollector
12-
from instana.log import logger
1311
from instana.options import AWSLambdaOptions
14-
from instana.util import to_json
15-
from instana.version import VERSION
1612

1713

18-
class AWSLambdaAgent(BaseAgent):
14+
class AWSLambdaAgent(ServerlessAgent):
1915
"""In-process Agent for AWS Lambda"""
2016

21-
def __init__(self) -> None:
22-
super(AWSLambdaAgent, self).__init__()
23-
24-
self.collector = None
17+
def _initialize_platform(self) -> None:
18+
"""Initialize AWS Lambda specific options."""
2519
self.options = AWSLambdaOptions()
26-
self.report_headers = None
27-
self._can_send = False
28-
29-
# Update log level from what Options detected
30-
self.update_log_level()
31-
32-
logger.info(
33-
f"Stan is on the AWS Lambda scene. Starting Instana instrumentation version: {VERSION}",
34-
)
35-
36-
if self._validate_options():
37-
self._can_send = True
38-
self.collector = AWSLambdaCollector(self)
39-
self.collector.start()
40-
else:
41-
logger.warning(
42-
"Required INSTANA_AGENT_KEY and/or INSTANA_ENDPOINT_URL environment variables not set. "
43-
"We will not be able monitor this function."
44-
)
45-
46-
def can_send(self) -> bool:
47-
"""
48-
Are we in a state where we can send data?
49-
@return: Boolean
50-
"""
51-
return self._can_send
52-
53-
def get_from_structure(self) -> Dict[str, Any]:
54-
"""
55-
Retrieves the From data that is reported alongside monitoring data.
56-
@return: dict()
57-
"""
58-
return {"hl": True, "cp": "aws", "e": self.collector.get_fq_arn()}
5920

60-
def report_data_payload(self, payload):
61-
"""
62-
Used to report metrics and span data to the endpoint URL in self.options.endpoint_url
63-
"""
64-
response = None
65-
try:
66-
if self.report_headers is None:
67-
# Prepare request headers
68-
self.report_headers = dict()
69-
self.report_headers["Content-Type"] = "application/json"
70-
self.report_headers["X-Instana-Host"] = self.collector.get_fq_arn()
71-
self.report_headers["X-Instana-Key"] = self.options.agent_key
21+
def _create_collector(self) -> AWSLambdaCollector:
22+
"""Create AWS Lambda collector."""
23+
return AWSLambdaCollector(self)
7224

73-
response = self.client.post(
74-
self.__data_bundle_url(),
75-
data=to_json(payload),
76-
headers=self.report_headers,
77-
timeout=self.options.timeout,
78-
verify=self.options.ssl_verify,
79-
proxies=self.options.endpoint_proxy,
80-
)
25+
def _get_entity_id(self) -> str:
26+
"""Get Lambda function ARN."""
27+
return self.collector.get_fq_arn()
8128

82-
if 200 <= response.status_code < 300:
83-
logger.debug(
84-
"report_data_payload: Instana responded with status code %s",
85-
response.status_code,
86-
)
87-
else:
88-
logger.info(
89-
"report_data_payload: Instana responded with status code %s",
90-
response.status_code,
91-
)
92-
except Exception as exc:
93-
logger.debug("report_data_payload: connection error (%s)", type(exc))
29+
def _get_cloud_provider(self) -> str:
30+
"""AWS cloud provider."""
31+
return "aws"
9432

95-
return response
33+
def _get_platform_name(self) -> str:
34+
"""Platform name for logging."""
35+
return "AWS Lambda"
9636

97-
def _validate_options(self) -> bool:
98-
"""
99-
Validate that the options used by this Agent are valid. e.g. can we report data?
100-
"""
101-
return self.options.endpoint_url and self.options.agent_key
10237

103-
def __data_bundle_url(self) -> str:
104-
"""
105-
URL for posting metrics to the host agent. Only valid when announced.
106-
"""
107-
return f"{self.options.endpoint_url}/bundle"
38+
# Made with Bob

0 commit comments

Comments
 (0)