Skip to content

Commit 2f67f22

Browse files
author
Peter Giacomo Lombardo
authored
RCA Helpers (#265)
* Better package version management * New snapshot fields; fix package version processing * Log boot message to host agent * Package init cleanup * Fix double import
1 parent a4f0345 commit 2f67f22

File tree

8 files changed

+116
-63
lines changed

8 files changed

+116
-63
lines changed

instana/__init__.py

Lines changed: 35 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -22,27 +22,38 @@
2222
from threading import Timer
2323
import pkg_resources
2424

25+
from .version import VERSION
26+
2527
__author__ = 'Instana Inc.'
2628
__copyright__ = 'Copyright 2020 Instana Inc.'
2729
__credits__ = ['Pavlo Baron', 'Peter Giacomo Lombardo', 'Andrey Slotin']
2830
__license__ = 'MIT'
2931
__maintainer__ = 'Peter Giacomo Lombardo'
3032
__email__ = '[email protected]'
33+
__version__ = VERSION
34+
35+
# User configurable EUM API key for instana.helpers.eum_snippet()
36+
# pylint: disable=invalid-name
37+
eum_api_key = ''
3138

32-
try:
33-
__version__ = pkg_resources.get_distribution('instana').version
34-
except pkg_resources.DistributionNotFound:
35-
__version__ = 'unknown'
39+
# This Python package can be loaded into Python processes one of three ways:
40+
# 1. manual import statement
41+
# 2. autowrapt hook
42+
# 3. dynamically injected remotely
43+
#
44+
# With such magic, we may get pulled into Python processes that we have no interest being in.
45+
# As a safety measure, we maintain a "do not load list" and if this process matches something
46+
# in that list, then we go sit in a corner quietly and don't load anything at all.
47+
do_not_load_list = ["pip", "pip2", "pip3", "pipenv", "docker-compose", "easy_install", "easy_install-2.7",
48+
"smtpd.py", "twine", "ufw", "unattended-upgrade"]
3649

3750

3851
def load(_):
3952
"""
4053
Method used to activate the Instana sensor via AUTOWRAPT_BOOTSTRAP
4154
environment variable.
4255
"""
43-
if "INSTANA_DEBUG" in os.environ:
44-
print("Instana: activated via AUTOWRAPT_BOOTSTRAP")
45-
56+
return None
4657

4758
def get_lambda_handler_or_default():
4859
"""
@@ -95,7 +106,7 @@ def lambda_handler(event, context):
95106
def boot_agent_later():
96107
""" Executes <boot_agent> in the future! """
97108
if 'gevent' in sys.modules:
98-
import gevent
109+
import gevent # pylint: disable=import-outside-toplevel
99110
gevent.spawn_later(2.0, boot_agent)
100111
else:
101112
Timer(2.0, boot_agent).start()
@@ -148,42 +159,20 @@ def boot_agent():
148159
# Hooks
149160
from .hooks import hook_uwsgi
150161

151-
152-
if "INSTANA_MAGIC" in os.environ:
153-
pkg_resources.working_set.add_entry("/tmp/.instana/python")
154-
# The following path is deprecated: To be removed at a future date
155-
pkg_resources.working_set.add_entry("/tmp/instana/python")
156-
157-
if "INSTANA_DEBUG" in os.environ:
158-
print("Instana: activated via AutoTrace")
159-
else:
160-
if ("INSTANA_DEBUG" in os.environ) and ("AUTOWRAPT_BOOTSTRAP" not in os.environ):
161-
print("Instana: activated via manual import")
162-
163-
# User configurable EUM API key for instana.helpers.eum_snippet()
164-
# pylint: disable=invalid-name
165-
eum_api_key = ''
166-
167-
# This Python package can be loaded into Python processes one of three ways:
168-
# 1. manual import statement
169-
# 2. autowrapt hook
170-
# 3. dynamically injected remotely
171-
#
172-
# With such magic, we may get pulled into Python processes that we have no interest being in.
173-
# As a safety measure, we maintain a "do not load list" and if this process matches something
174-
# in that list, then we go sit in a corner quietly and don't load anything at all.
175-
do_not_load_list = ["pip", "pip2", "pip3", "pipenv", "docker-compose", "easy_install", "easy_install-2.7",
176-
"smtpd.py", "twine", "ufw", "unattended-upgrade"]
177-
178-
# There are cases when sys.argv may not be defined at load time. Seems to happen in embedded Python,
179-
# and some Pipenv installs. If this is the case, it's best effort.
180-
if hasattr(sys, 'argv') and len(sys.argv) > 0 and (os.path.basename(sys.argv[0]) in do_not_load_list):
181-
if "INSTANA_DEBUG" in os.environ:
182-
print("Instana: No use in monitoring this process type (%s). "
183-
"Will go sit in a corner quietly." % os.path.basename(sys.argv[0]))
184-
else:
185-
if "INSTANA_MAGIC" in os.environ:
186-
# If we're being loaded into an already running process, then delay agent initialization
187-
boot_agent_later()
162+
if 'INSTANA_DISABLE' not in os.environ:
163+
# There are cases when sys.argv may not be defined at load time. Seems to happen in embedded Python,
164+
# and some Pipenv installs. If this is the case, it's best effort.
165+
if hasattr(sys, 'argv') and len(sys.argv) > 0 and (os.path.basename(sys.argv[0]) in do_not_load_list):
166+
if "INSTANA_DEBUG" in os.environ:
167+
print("Instana: No use in monitoring this process type (%s). "
168+
"Will go sit in a corner quietly." % os.path.basename(sys.argv[0]))
188169
else:
189-
boot_agent()
170+
if "INSTANA_MAGIC" in os.environ:
171+
pkg_resources.working_set.add_entry("/tmp/.instana/python")
172+
# The following path is deprecated: To be removed at a future date
173+
pkg_resources.working_set.add_entry("/tmp/instana/python")
174+
175+
# If we're being loaded into an already running process, then delay agent initialization
176+
boot_agent_later()
177+
else:
178+
boot_agent()

instana/agent/aws_fargate.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
from instana.options import AWSFargateOptions
77
from instana.collector.aws_fargate import AWSFargateCollector
88
from ..log import logger
9-
from ..util import to_json, package_version
9+
from ..util import to_json
1010
from .base import BaseAgent
11+
from ..version import VERSION
1112

1213

1314
class AWSFargateFrom(object):
@@ -34,7 +35,7 @@ def __init__(self):
3435
# Update log level (if INSTANA_LOG_LEVEL was set)
3536
self.update_log_level()
3637

37-
logger.info("Stan is on the AWS Fargate scene. Starting Instana instrumentation version: %s", package_version())
38+
logger.info("Stan is on the AWS Fargate scene. Starting Instana instrumentation version: %s", VERSION)
3839

3940
if self._validate_options():
4041
self._can_send = True

instana/agent/aws_lambda.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
"""
55
import time
66
from ..log import logger
7-
from ..util import to_json, package_version
7+
from ..util import to_json
88
from .base import BaseAgent
9+
from ..version import VERSION
910
from ..collector.aws_lambda import AWSLambdaCollector
1011
from ..options import AWSLambdaOptions
1112

@@ -34,7 +35,7 @@ def __init__(self):
3435
# Update log level from what Options detected
3536
self.update_log_level()
3637

37-
logger.info("Stan is on the AWS Lambda scene. Starting Instana instrumentation version: %s", package_version())
38+
logger.info("Stan is on the AWS Lambda scene. Starting Instana instrumentation version: %s", VERSION)
3839

3940
if self._validate_options():
4041
self._can_send = True

instana/agent/host.py

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@
1414
from ..log import logger
1515
from .base import BaseAgent
1616
from ..fsm import TheMachine
17+
from ..version import VERSION
1718
from ..options import StandardOptions
1819
from ..collector.host import HostCollector
19-
from ..util import to_json, get_py_source, package_version
20+
from ..util import to_json, get_py_source
2021

2122

2223
class AnnounceData(object):
@@ -50,8 +51,8 @@ def __init__(self):
5051

5152
# Update log level from what Options detected
5253
self.update_log_level()
53-
54-
logger.info("Stan is on the scene. Starting Instana instrumentation version: %s", package_version())
54+
55+
logger.info("Stan is on the scene. Starting Instana instrumentation version: %s", VERSION)
5556

5657
self.collector = HostCollector(self)
5758
self.machine = TheMachine(self)
@@ -186,6 +187,27 @@ def announce(self, discovery):
186187
logger.debug("announce: connection error (%s)", type(exc))
187188
return response
188189

190+
def log_message_to_host_agent(self, message):
191+
"""
192+
Log a message to the discovered host agent
193+
"""
194+
response = None
195+
try:
196+
payload = dict()
197+
payload["m"] = message
198+
199+
url = self.__agent_logger_url()
200+
response = self.client.post(url,
201+
data=to_json(payload),
202+
headers={"Content-Type": "application/json",
203+
"X-Log-Level": "INFO"},
204+
timeout=0.8)
205+
206+
if 200 <= response.status_code <= 204:
207+
self.last_seen = datetime.now()
208+
except Exception as exc:
209+
logger.debug("agent logging: connection error (%s)", type(exc))
210+
189211
def is_agent_ready(self):
190212
"""
191213
Used after making a successful announce to test when the agent is ready to accept data.
@@ -214,7 +236,7 @@ def report_data_payload(self, payload):
214236
data=to_json(payload['spans']),
215237
headers={"Content-Type": "application/json"},
216238
timeout=0.8)
217-
239+
218240
if response is not None and 200 <= response.status_code <= 204:
219241
self.last_seen = datetime.now()
220242

@@ -251,7 +273,7 @@ def handle_agent_tasks(self, task):
251273
payload = get_py_source(task["args"]["file"])
252274
else:
253275
message = "Unrecognized action: %s. An newer Instana package may be required " \
254-
"for this. Current version: %s" % (task["action"], package_version())
276+
"for this. Current version: %s" % (task["action"], VERSION)
255277
payload = {"error": message}
256278
else:
257279
payload = {"error": "Instana Python: No action specified in request."}
@@ -303,3 +325,9 @@ def __response_url(self, message_id):
303325
"""
304326
path = "com.instana.plugin.python/response.%d?messageId=%s" % (int(self.announce_data.pid), message_id)
305327
return "http://%s:%s/%s" % (self.options.agent_host, self.options.agent_port, path)
328+
329+
def __agent_logger_url(self):
330+
"""
331+
URL for logging messages to the discovered host agent.
332+
"""
333+
return "http://%s:%s/%s" % (self.options.agent_host, self.options.agent_port, "com.instana.agent.logger")

instana/collector/helpers/runtime.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from pkg_resources import DistributionNotFound, get_distribution
1010

1111
from instana.log import logger
12+
from instana.version import VERSION
1213
from instana.util import DictionaryOfStan, determine_service_name
1314

1415
from .base import BaseHelper
@@ -166,6 +167,14 @@ def _collect_runtime_snapshot(self,plugin_data):
166167
snapshot_payload['f'] = platform.python_implementation() # flavor
167168
snapshot_payload['a'] = platform.architecture()[0] # architecture
168169
snapshot_payload['versions'] = self.gather_python_packages()
170+
snapshot_payload['iv'] = VERSION
171+
172+
if 'AUTOWRAPT_BOOTSTRAP' in os.environ:
173+
snapshot_payload['m'] = 'Autowrapt'
174+
elif 'INSTANA_MAGIC' in os.environ:
175+
snapshot_payload['m'] = 'AutoTrace'
176+
else:
177+
snapshot_payload['m'] = 'Manual'
169178

170179
try:
171180
from django.conf import settings # pylint: disable=import-outside-toplevel
@@ -191,23 +200,30 @@ def gather_python_packages(self):
191200
# Skip modules that begin with underscore
192201
if ('.' in pkg_name) or pkg_name[0] == '_':
193202
continue
203+
204+
# Skip builtins
205+
if pkg_name in ["sys", "curses"]:
206+
continue
207+
194208
if sys_packages[pkg_name]:
195209
try:
196210
pkg_info = sys_packages[pkg_name].__dict__
197-
if "version" in pkg_info:
198-
versions[pkg_name] = self.jsonable(pkg_info["version"])
199-
elif "__version__" in pkg_info:
211+
if "__version__" in pkg_info:
200212
if isinstance(pkg_info["__version__"], str):
201213
versions[pkg_name] = pkg_info["__version__"]
202214
else:
203215
versions[pkg_name] = self.jsonable(pkg_info["__version__"])
216+
elif "version" in pkg_info:
217+
versions[pkg_name] = self.jsonable(pkg_info["version"])
204218
else:
205219
versions[pkg_name] = get_distribution(pkg_name).version
206220
except DistributionNotFound:
207221
pass
208222
except Exception:
209223
logger.debug("gather_python_packages: could not process module: %s", pkg_name)
210-
224+
225+
# Manually set our package version
226+
versions['instana'] = VERSION
211227
except Exception:
212228
logger.debug("gather_python_packages", exc_info=True)
213229

instana/fsm.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
from .log import logger
1313
from .util import get_default_gateway
14+
from .version import VERSION
1415

1516

1617
class Discovery(object):
@@ -58,7 +59,8 @@ def __init__(self, agent):
5859
# "onchangestate": self.print_state_change,
5960
"onlookup": self.lookup_agent_host,
6061
"onannounce": self.announce_sensor,
61-
"onpending": self.on_ready}})
62+
"onpending": self.on_ready,
63+
"ongood2go": self.on_good2go}})
6264

6365
self.timer = threading.Timer(1, self.fsm.lookup)
6466
self.timer.daemon = True
@@ -173,8 +175,17 @@ def schedule_retry(self, fun, e, name):
173175

174176
def on_ready(self, _):
175177
self.agent.start()
176-
logger.info("Instana host agent available. We're in business. Announced pid: %s (true pid: %s)",
177-
str(os.getpid()), str(self.agent.announce_data.pid))
178+
179+
ns_pid = str(os.getpid())
180+
true_pid = str(self.agent.announce_data.pid)
181+
182+
logger.info("Instana host agent available. We're in business. Announced PID: %s (true pid: %s)", ns_pid, true_pid)
183+
184+
def on_good2go(self, _):
185+
ns_pid = str(os.getpid())
186+
true_pid = str(self.agent.announce_data.pid)
187+
188+
self.agent.log_message_to_host_agent("Instana Python Package %s: PID %s (true pid: %s) is now online and reporting" % (VERSION, ns_pid, true_pid))
178189

179190
def __get_real_pid(self):
180191
"""

instana/version.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Module version file. Used by setup.py and snapshot reporting.
2+
3+
VERSION = '1.25.6dev1'

setup.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
# coding: utf-8
2+
import os
23
import sys
34
from os import path
45
from distutils.version import LooseVersion
56
from setuptools import find_packages, setup
67

7-
VERSION = '1.25.5'
8+
os.environ["INSTANA_DISABLE"] = "true"
9+
10+
# pylint: disable=wrong-import-position
11+
from instana.version import VERSION
812

913
# Import README.md into long_description
1014
pwd = path.abspath(path.dirname(__file__))

0 commit comments

Comments
 (0)