Skip to content

Commit e4d2c02

Browse files
authored
Announce Hardiness (#225)
* Cast a larger exception capture net * Improved log messages. * Only one place for host/port * pylint improvements
1 parent 0b1586e commit e4d2c02

File tree

4 files changed

+75
-44
lines changed

4 files changed

+75
-44
lines changed

instana/agent.py

Lines changed: 61 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
""" The in-process Instana agent that manages monitoring state and reporting that data. """
12
from __future__ import absolute_import
23

34
import json
@@ -8,16 +9,17 @@
89
import requests
910

1011
import instana.singletons
12+
from instana.collector import Collector
1113

1214
from .fsm import TheMachine
1315
from .log import logger
1416
from .sensor import Sensor
1517
from .util import to_json, get_py_source, package_version
1618
from .options import StandardOptions, AWSLambdaOptions
17-
from instana.collector import Collector
1819

1920

2021
class AnnounceData(object):
22+
""" The Announce Payload """
2123
pid = 0
2224
agentUuid = ""
2325

@@ -26,6 +28,7 @@ def __init__(self, **kwds):
2628

2729

2830
class AWSLambdaFrom(object):
31+
""" The source identifier for AWSLambdaAgent """
2932
hl = True
3033
cp = "aws"
3134
e = "qualifiedARN"
@@ -35,6 +38,7 @@ def __init__(self, **kwds):
3538

3639

3740
class BaseAgent(object):
41+
""" Base class for all agent flavors """
3842
client = requests.Session()
3943
sensor = None
4044

@@ -108,13 +112,22 @@ def reset(self):
108112
self.machine.reset()
109113

110114
def is_timed_out(self):
115+
"""
116+
If we haven't heard from the Instana host agent in 60 seconds, this
117+
method will return True.
118+
@return: Boolean
119+
"""
111120
if self.last_seen and self.can_send:
112121
diff = datetime.now() - self.last_seen
113122
if diff.seconds > 60:
114123
return True
115124
return False
116125

117126
def can_send(self):
127+
"""
128+
Are we in a state where we can send data?
129+
@return: Boolean
130+
"""
118131
# Watch for pid change (fork)
119132
current_pid = os.getpid()
120133
if self._boot_pid != current_pid:
@@ -129,6 +142,11 @@ def can_send(self):
129142
return False
130143

131144
def set_from(self, json_string):
145+
"""
146+
Sets the source identifiers given to use by the Instana Host agent.
147+
@param json_string: source identifiers
148+
@return: None
149+
"""
132150
if type(json_string) is bytes:
133151
raw_json = json_string.decode("UTF-8")
134152
else:
@@ -147,33 +165,37 @@ def set_from(self, json_string):
147165
self.announce_data = AnnounceData(pid=res_data['pid'], agentUuid=res_data['agentUuid'])
148166

149167
def get_from_structure(self):
168+
"""
169+
Retrieves the From data that is reported alongside monitoring data.
170+
@return: dict()
171+
"""
150172
if os.environ.get("INSTANA_TEST", False):
151-
fs = {'e': os.getpid(), 'h': 'fake'}
173+
from_data = {'e': os.getpid(), 'h': 'fake'}
152174
else:
153-
fs = {'e': self.announce_data.pid, 'h': self.announce_data.agentUuid}
154-
return fs
175+
from_data = {'e': self.announce_data.pid, 'h': self.announce_data.agentUuid}
176+
return from_data
155177

156178
def is_agent_listening(self, host, port):
157179
"""
158180
Check if the Instana Agent is listening on <host> and <port>.
181+
@return: Boolean
159182
"""
160-
rv = False
183+
result = False
161184
try:
162185
url = "http://%s:%s/" % (host, port)
163186
response = self.client.get(url, timeout=0.8)
164187

165188
server_header = response.headers["Server"]
166189
if server_header == self.AGENT_HEADER:
167190
logger.debug("Instana host agent found on %s:%d", host, port)
168-
rv = True
191+
result = True
169192
else:
170193
logger.debug("...something is listening on %s:%d but it's not the Instana Host Agent: %s",
171194
host, port, server_header)
172-
except (requests.ConnectTimeout, requests.ConnectionError):
195+
except:
173196
logger.debug("Instana Host Agent not found on %s:%d", host, port)
174-
rv = False
175197
finally:
176-
return rv
198+
return result
177199

178200
def announce(self, discovery):
179201
"""
@@ -190,23 +212,25 @@ def announce(self, discovery):
190212

191213
if response.status_code == 200:
192214
self.last_seen = datetime.now()
193-
except (requests.ConnectTimeout, requests.ConnectionError):
194-
logger.debug("announce", exc_info=True)
215+
except:
216+
logger.debug("announce: ", exc_info=True)
195217
finally:
196218
return response
197219

198220
def is_agent_ready(self):
199221
"""
200222
Used after making a successful announce to test when the agent is ready to accept data.
201223
"""
224+
ready = False
202225
try:
203226
response = self.client.head(self.__data_url(), timeout=0.8)
204227

205228
if response.status_code == 200:
206-
return True
207-
return False
208-
except (requests.ConnectTimeout, requests.ConnectionError):
209-
logger.debug("is_agent_ready: Instana host agent connection error")
229+
ready = True
230+
except:
231+
logger.debug("is_agent_ready: ", exc_info=True)
232+
finally:
233+
return ready
210234

211235
def report_data_payload(self, entity_data):
212236
"""
@@ -219,12 +243,12 @@ def report_data_payload(self, entity_data):
219243
headers={"Content-Type": "application/json"},
220244
timeout=0.8)
221245

222-
# logger.warn("report_data: response.status_code is %s" % response.status_code)
246+
# logger.warning("report_data: response.status_code is %s" % response.status_code)
223247

224248
if response.status_code == 200:
225249
self.last_seen = datetime.now()
226-
except (requests.ConnectTimeout, requests.ConnectionError):
227-
logger.debug("report_data: Instana host agent connection error")
250+
except:
251+
logger.debug("report_data: Instana host agent connection error", exc_info=True)
228252
finally:
229253
return response
230254

@@ -244,12 +268,12 @@ def report_traces(self, spans):
244268
headers={"Content-Type": "application/json"},
245269
timeout=0.8)
246270

247-
# logger.warn("report_traces: response.status_code is %s" % response.status_code)
271+
# logger.debug("report_traces: response.status_code is %s" % response.status_code)
248272

249273
if response.status_code == 200:
250274
self.last_seen = datetime.now()
251-
except (requests.ConnectTimeout, requests.ConnectionError):
252-
logger.debug("report_traces: Instana host agent connection error")
275+
except:
276+
logger.debug("report_traces: ", exc_info=True)
253277
finally:
254278
return response
255279

@@ -286,10 +310,8 @@ def __task_response(self, message_id, data):
286310
data=payload,
287311
headers={"Content-Type": "application/json"},
288312
timeout=0.8)
289-
except (requests.ConnectTimeout, requests.ConnectionError):
290-
logger.debug("task_response", exc_info=True)
291-
except Exception:
292-
logger.debug("task_response Exception", exc_info=True)
313+
except:
314+
logger.debug("task_response: ", exc_info=True)
293315
finally:
294316
return response
295317

@@ -322,6 +344,7 @@ def __response_url(self, message_id):
322344

323345

324346
class AWSLambdaAgent(BaseAgent):
347+
""" In-process agent for AWS Lambda """
325348
def __init__(self):
326349
super(AWSLambdaAgent, self).__init__()
327350

@@ -340,13 +363,21 @@ def __init__(self):
340363
self.collector = Collector(self)
341364
self.collector.start()
342365
else:
343-
logger.warn("Required INSTANA_AGENT_KEY and/or INSTANA_ENDPOINT_URL environment variables not set. "
344-
"We will not be able monitor this function.")
366+
logger.warning("Required INSTANA_AGENT_KEY and/or INSTANA_ENDPOINT_URL environment variables not set. "
367+
"We will not be able monitor this function.")
345368

346369
def can_send(self):
370+
"""
371+
Are we in a state where we can send data?
372+
@return: Boolean
373+
"""
347374
return self._can_send
348375

349376
def get_from_structure(self):
377+
"""
378+
Retrieves the From data that is reported alongside monitoring data.
379+
@return: dict()
380+
"""
350381
return {'hl': True, 'cp': 'aws', 'e': self.collector.context.invoked_function_arn}
351382

352383
def report_data_payload(self, payload):
@@ -363,7 +394,7 @@ def report_data_payload(self, payload):
363394
self.report_headers["X-Instana-Key"] = self.options.agent_key
364395
self.report_headers["X-Instana-Time"] = str(round(time.time() * 1000))
365396

366-
logger.debug("using these headers: %s" % self.report_headers)
397+
logger.debug("using these headers: %s", self.report_headers)
367398

368399
if 'INSTANA_DISABLE_CA_CHECK' in os.environ:
369400
ssl_verify = False
@@ -376,9 +407,7 @@ def report_data_payload(self, payload):
376407
timeout=self.options.timeout,
377408
verify=ssl_verify)
378409

379-
logger.debug("report_data_payload: response.status_code is %s" % response.status_code)
380-
except (requests.ConnectTimeout, requests.ConnectionError):
381-
logger.debug("report_data_payload: ", exc_info=True)
410+
logger.debug("report_data_payload: response.status_code is %s", response.status_code)
382411
except:
383412
logger.debug("report_data_payload: ", exc_info=True)
384413
finally:
@@ -394,4 +423,4 @@ def __data_bundle_url(self):
394423
"""
395424
URL for posting metrics to the host agent. Only valid when announced.
396425
"""
397-
return "%s/bundle" % self.options.endpoint_url
426+
return "%s/bundle" % self.options.endpoint_url

instana/fsm.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -99,16 +99,15 @@ def lookup_agent_host(self, e):
9999
port = self.agent.options.agent_port
100100

101101
if self.agent.is_agent_listening(host, port):
102-
self.agent.host = host
103-
self.agent.port = port
104102
self.fsm.announce()
105103
return True
106-
elif os.path.exists("/proc/"):
104+
105+
if os.path.exists("/proc/"):
107106
host = get_default_gateway()
108107
if host:
109108
if self.agent.is_agent_listening(host, port):
110-
self.agent.host = host
111-
self.agent.port = port
109+
self.agent.options.agent_host = host
110+
self.agent.options.agent_port = port
112111
self.fsm.announce()
113112
return True
114113

@@ -120,7 +119,8 @@ def lookup_agent_host(self, e):
120119
return False
121120

122121
def announce_sensor(self, e):
123-
logger.debug("Announcing sensor to the agent")
122+
logger.debug("Attempting to make an announcement to the agent on %s:%d",
123+
self.agent.options.agent_host, self.agent.options.agent_port)
124124
pid = os.getpid()
125125

126126
try:
@@ -135,7 +135,7 @@ def announce_sensor(self, e):
135135
# psutil which requires dev packages, gcc etc...
136136
proc = subprocess.Popen(["ps", "-p", str(pid), "-o", "command"],
137137
stdout=subprocess.PIPE)
138-
(out, err) = proc.communicate()
138+
(out, _) = proc.communicate()
139139
parts = out.split(b'\n')
140140
cmdline = [parts[1].decode("utf-8")]
141141
except Exception:
@@ -149,7 +149,7 @@ def announce_sensor(self, e):
149149
# If we're on a system with a procfs
150150
if os.path.exists("/proc/"):
151151
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
152-
sock.connect((self.agent.host, 42699))
152+
sock.connect((self.agent.options.agent_host, self.agent.options.agent_port))
153153
path = "/proc/%d/fd/%d" % (pid, sock.fileno())
154154
d.fd = sock.fileno()
155155
d.inode = os.readlink(path)
@@ -162,9 +162,9 @@ def announce_sensor(self, e):
162162
logger.debug("Announced pid: %s (true pid: %s). Waiting for Agent Ready...",
163163
str(pid), str(self.agent.announce_data.pid))
164164
return True
165-
else:
166-
logger.debug("Cannot announce sensor. Scheduling retry.")
167-
self.schedule_retry(self.announce_sensor, e, self.THREAD_NAME + ": announce")
165+
166+
logger.debug("Cannot announce sensor. Scheduling retry.")
167+
self.schedule_retry(self.announce_sensor, e, self.THREAD_NAME + ": announce")
168168
return False
169169

170170
def schedule_retry(self, fun, e, name):

instana/options.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
""" Options for the in-process Instana agent """
12
import logging
23
import os
34

45

56
class StandardOptions(object):
7+
""" Configurable option bits for this package """
68
service = None
79
service_name = None
810
agent_host = None

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def check_setuptools():
2727
' and then try the install again.\n'
2828
'Also:\n'
2929
' `pip show setuptools` - shows the current version\n'
30-
' To see the setuptool releases: \n'
30+
' To see the setuptools releases: \n'
3131
' https://setuptools.readthedocs.io/en/latest/history.html')
3232

3333

0 commit comments

Comments
 (0)