Skip to content

Commit 4af1466

Browse files
authored
Fix: Gunicorn process detection (#208)
* Check proc cmdline for gunicorn process * Change Strategy to avoid circular import * Remove debug print statements * Python 2.7 compatibility
1 parent 97ca621 commit 4af1466

File tree

5 files changed

+63
-47
lines changed

5 files changed

+63
-47
lines changed

instana/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ def boot_agent():
8585
# Hooks
8686
from .hooks import hook_uwsgi
8787

88+
8889
if "INSTANA_MAGIC" in os.environ:
8990
pkg_resources.working_set.add_entry("/tmp/instana/python")
9091

instana/log.py

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from __future__ import print_function
12
import logging
23
import os
34
import sys
@@ -35,22 +36,36 @@ def running_in_gunicorn():
3536
process_check = False
3637
package_check = False
3738

38-
if hasattr(sys, 'argv'):
39-
for arg in sys.argv:
40-
if arg.find('gunicorn') >= 0:
39+
try:
40+
# Is this a gunicorn process?
41+
if hasattr(sys, 'argv'):
42+
for arg in sys.argv:
43+
if arg.find('gunicorn') >= 0:
44+
process_check = True
45+
elif os.path.isfile("/proc/self/cmdline"):
46+
with open("/proc/self/cmdline") as cmd:
47+
contents = cmd.read()
48+
49+
parts = contents.split('\0')
50+
parts.pop()
51+
cmdline = " ".join(parts)
52+
53+
if cmdline.find('gunicorn') >= 0:
4154
process_check = True
42-
else:
43-
# We have no command line so rely on the gunicorn package presence entirely
44-
process_check = True
4555

46-
try:
47-
from gunicorn import glogging
48-
except ImportError:
49-
pass
50-
else:
51-
package_check = True
56+
# Is the glogging package available?
57+
try:
58+
from gunicorn import glogging
59+
except ImportError:
60+
pass
61+
else:
62+
package_check = True
5263

53-
return process_check and package_check
64+
# Both have to be true for gunicorn logging
65+
return process_check and package_check
66+
except Exception as e:
67+
print("Instana.log.running_in_gunicorn: %s", e, file=sys.stderr)
68+
return False
5469

5570

5671
if running_in_gunicorn():

instana/meter.py

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from pkg_resources import DistributionNotFound, get_distribution
1313

1414
from .log import logger
15-
from .util import get_py_source, package_version, every
15+
from .util import get_py_source, package_version, every, get_proc_cmdline
1616

1717

1818
class Snapshot(object):
@@ -241,33 +241,6 @@ def handle_agent_tasks(self, task):
241241

242242
self.agent.task_response(task["messageId"], payload)
243243

244-
def get_proc_cmdline(self, as_string=False):
245-
"""
246-
Parse the proc file system for the command line of this process. If not available, then return a default.
247-
Return is dependent on the value of `as_string`. If True, return the full command line as a string,
248-
otherwise a list.
249-
"""
250-
name = "python"
251-
if os.path.isfile("/proc/self/cmdline"):
252-
with open("/proc/self/cmdline") as cmd:
253-
name = cmd.read()
254-
else:
255-
# Most likely not on a *nix based OS. Return a default
256-
if as_string is True:
257-
return name
258-
else:
259-
return [name]
260-
261-
# /proc/self/command line will have strings with null bytes such as "/usr/bin/python\0-s\0-d\0". This
262-
# bit will prep the return value and drop the trailing null byte
263-
parts = name.split('\0')
264-
parts.pop()
265-
266-
if as_string is True:
267-
parts = " ".join(parts)
268-
269-
return parts
270-
271244
def get_application_name(self):
272245
""" This function makes a best effort to name this application process. """
273246

@@ -281,15 +254,15 @@ def get_application_name(self):
281254
app_name = "python" # the default name
282255

283256
if not hasattr(sys, 'argv'):
284-
proc_cmdline = self.get_proc_cmdline(as_string=False)
257+
proc_cmdline = get_proc_cmdline(as_string=False)
285258
return os.path.basename(proc_cmdline[0])
286259

287260
basename = os.path.basename(sys.argv[0])
288261
if basename == "gunicorn":
289262
# gunicorn renames their processes to pretty things - we use those by default
290263
# gunicorn: master [djface.wsgi]
291264
# gunicorn: worker [djface.wsgi]
292-
app_name = self.get_proc_cmdline(as_string=True)
265+
app_name = get_proc_cmdline(as_string=True)
293266

294267
if app_name is None:
295268
app_name = basename

instana/util.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,34 @@ def extractor(o):
9393
logger.debug("to_json non-fatal encoding issue: ", exc_info=True)
9494

9595

96+
def get_proc_cmdline(as_string=False):
97+
"""
98+
Parse the proc file system for the command line of this process. If not available, then return a default.
99+
Return is dependent on the value of `as_string`. If True, return the full command line as a string,
100+
otherwise a list.
101+
"""
102+
name = "python"
103+
if os.path.isfile("/proc/self/cmdline"):
104+
with open("/proc/self/cmdline") as cmd:
105+
name = cmd.read()
106+
else:
107+
# Most likely not on a *nix based OS. Return a default
108+
if as_string is True:
109+
return name
110+
else:
111+
return [name]
112+
113+
# /proc/self/command line will have strings with null bytes such as "/usr/bin/python\0-s\0-d\0". This
114+
# bit will prep the return value and drop the trailing null byte
115+
parts = name.split('\0')
116+
parts.pop()
117+
118+
if as_string is True:
119+
parts = " ".join(parts)
120+
121+
return parts
122+
123+
96124
def package_version():
97125
"""
98126
Determine the version of this package.
@@ -234,13 +262,12 @@ def get_py_source(file):
234262
235263
@param file [String] The fully qualified path to a file
236264
"""
265+
response = None
237266
try:
238-
response = None
239-
pysource = ""
240-
241267
if regexp_py.search(file) is None:
242268
response = {"error": "Only Python source files are allowed. (*.py)"}
243269
else:
270+
pysource = ""
244271
with open(file, 'r') as pyfile:
245272
pysource = pyfile.read()
246273

@@ -251,6 +278,7 @@ def get_py_source(file):
251278
finally:
252279
return response
253280

281+
254282
# Used by get_py_source
255283
regexp_py = re.compile('\.py$')
256284

tests/test_secrets.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import unittest
44

5-
from instana.singletons import agent
65
from instana.util import strip_secrets
76

87

0 commit comments

Comments
 (0)