Skip to content

Commit 99cea9d

Browse files
authored
Merge pull request #1 from instana/final_leg
First Release
2 parents 7676e7b + d265919 commit 99cea9d

20 files changed

+667
-108
lines changed

.travis.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
language: python
2+
python:
3+
- "2.7"
4+
- "3.6"
5+
install: "pip install -r requirements.txt"
6+
script: nosetests

README.md

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,37 @@
1-
# python-sensor
1+
<div align="center">
2+
<img src="https://disznc.s3.amazonaws.com/Python-1-2017-06-29-at-22.34.00.png"/>
3+
</div>
4+
5+
# Instana
6+
7+
The instana package provides Python metrics and traces (request, queue & cross-host) for [Instana](https://www.instana.com/).
8+
9+
## Note
10+
11+
This package supports Python 2.7 or greater.
12+
13+
Any and all feedback is welcome. Happy Python visibility.
14+
15+
## Installation
16+
17+
$ pip install instana
18+
19+
## Usage
20+
21+
The instana package is a zero configuration tool that will automatically collect key metrics from your Python processes. Just install and go.
22+
23+
## Tracing
24+
25+
This Python package supports [OpenTracing](http://opentracing.io/).
26+
27+
## Documentation
28+
29+
You can find more documentation covering supported components and minimum versions in the Instana [documentation portal](https://instana.atlassian.net/wiki/display/DOCS/Python).
30+
31+
## Contributing
32+
33+
Bug reports and pull requests are welcome on GitHub at https://github.com/instana/python-sensor.
34+
35+
## More
36+
37+
Want to instrument other languages? See our [Nodejs instrumentation](https://github.com/instana/nodejs-sensor), [Go instrumentation](https://github.com/instana/golang-sensor), [Ruby instrumentation](https://github.com/instana/ruby-sensor) or [many other supported technologies](https://www.instana.com/supported-technologies/).

instana/agent.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
except ImportError:
99
import urllib2
1010

11+
1112
class From(object):
1213
pid = ""
1314
hostId = ""
@@ -42,10 +43,11 @@ def __init__(self, sensor):
4243
self.reset()
4344

4445
def to_json(self, o):
45-
return json.dumps(o, default=lambda o: o.__dict__, sort_keys=False,separators=(',', ':')).encode()
46+
return json.dumps(o, default=lambda o: o.__dict__,
47+
sort_keys=False, separators=(',', ':')).encode()
4648

4749
def can_send(self):
48-
return self.fsm.fsm.current == "ready"
50+
return self.fsm.fsm.current == "good2go"
4951

5052
def head(self, url):
5153
return self.request(url, "HEAD", None)
@@ -94,7 +96,7 @@ def full_request_response(self, url, method, o, body, header):
9496
if method == "HEAD":
9597
b = True
9698
except Exception as e:
97-
l.error(str(e))
99+
l.error("full_request_response: " + str(e))
98100

99101
return (b, h)
100102

instana/custom.py

Lines changed: 0 additions & 6 deletions
This file was deleted.

instana/data.py

Lines changed: 0 additions & 8 deletions
This file was deleted.

instana/fsm.py

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import subprocess
22
import os
3-
import sys
3+
import psutil
44
import threading as t
55
import fysom as f
66
import instana.log as l
@@ -17,35 +17,34 @@ def __init__(self, **kwds):
1717

1818

1919
class Fsm(object):
20-
E_START = "start"
21-
E_LOOKUP = "lookup"
22-
E_ANNOUNCE = "announce"
23-
E_TEST = "test"
24-
2520
RETRY_PERIOD = 30
2621

2722
agent = None
2823
fsm = None
24+
timer = None
2925

3026
def __init__(self, agent):
3127
l.debug("initializing fsm")
3228

3329
self.agent = agent
3430
self.fsm = f.Fysom({
35-
"initial": "none",
31+
"initial": "lostandalone",
3632
"events": [
37-
{"name": self.E_START, "src": [
38-
"none", "unannounced", "announced", "ready"], "dst": "init"},
39-
{"name": self.E_LOOKUP, "src": "init", "dst": "unannounced"},
40-
{"name": self.E_ANNOUNCE, "src": "unannounced", "dst": "announced"},
41-
{"name": self.E_TEST, "src": "announced", "dst": "ready"}],
33+
("startup", "*", "lostandalone"),
34+
("lookup", "lostandalone", "found"),
35+
("announce", "found", "announced"),
36+
("ready", "announced", "good2go")],
4237
"callbacks": {
43-
"onstart": self.lookup_agent_host,
44-
"onenterunannounced": self.announce_sensor,
45-
"onenterannounced": self.test_agent}})
38+
"onlookup": self.lookup_agent_host,
39+
"onannounce": self.announce_sensor,
40+
"onchangestate": self.printstatechange}})
41+
42+
def printstatechange(self, e):
43+
l.debug('========= (%i#%s) FSM event: %s, src: %s, dst: %s ==========' % \
44+
(os.getpid(), t.current_thread().name, e.event, e.src, e.dst))
4645

4746
def reset(self):
48-
self.fsm.start()
47+
self.fsm.lookup()
4948

5049
def lookup_agent_host(self, e):
5150
if self.agent.sensor.options.agent_host != "":
@@ -56,17 +55,19 @@ def lookup_agent_host(self, e):
5655
h = self.check_host(host)
5756
if h == a.AGENT_HEADER:
5857
self.agent.set_host(host)
59-
self.fsm.lookup()
58+
self.fsm.announce()
6059
else:
6160
host = self.get_default_gateway()
6261
if host:
6362
self.check_host(host)
6463
if h == a.AGENT_HEADER:
6564
self.agent.set_host(host)
66-
self.fsm.lookup()
65+
self.fsm.announce()
6766
else:
6867
l.error("Cannot lookup agent host. Scheduling retry.")
69-
self.schedule_retry(self.lookup_agent_host, e)
68+
self.schedule_retry(self.lookup_agent_host, e, "agent_lookup")
69+
return False
70+
return True
7071

7172
def get_default_gateway(self):
7273
l.debug("checking default gateway")
@@ -91,29 +92,36 @@ def check_host(self, host):
9192

9293
def announce_sensor(self, e):
9394
l.debug("announcing sensor to the agent")
94-
95-
d = Discovery(pid=os.getpid(),
96-
name=sys.executable,
97-
args=sys.argv[0:])
95+
p = psutil.Process(os.getpid())
96+
d = Discovery(pid=p.pid,
97+
name=p.cmdline()[0],
98+
args=p.cmdline()[1:])
9899

99100
(b, _) = self.agent.request_response(
100101
self.agent.make_url(a.AGENT_DISCOVERY_URL), "PUT", d)
101102
if not b:
102103
l.error("Cannot announce sensor. Scheduling retry.")
103-
self.schedule_retry(self.announce_sensor, e)
104+
self.schedule_retry(self.announce_sensor, e, "announce")
105+
return False
104106
else:
105107
self.agent.set_from(b)
106-
self.fsm.announce()
108+
self.fsm.ready()
109+
return True
107110

108-
def schedule_retry(self, fun, e):
109-
t.Timer(self.RETRY_PERIOD, fun, [e]).start()
111+
def schedule_retry(self, fun, e, name):
112+
l.error("Scheduling: " + name)
113+
self.timer = t.Timer(self.RETRY_PERIOD, fun, [e])
114+
self.timer.daemon = True
115+
self.timer.name = name
116+
self.timer.start()
117+
l.debug('Threadlist: %s', str(t.enumerate()))
110118

111119
def test_agent(self, e):
112120
l.debug("testing communication with the agent")
113121

114122
(b, _) = self.agent.head(self.agent.make_url(a.AGENT_DATA_URL))
115123

116124
if not b:
117-
self.schedule_retry(self.test_agent, e)
125+
self.schedule_retry(self.test_agent, e, "agent test")
118126
else:
119127
self.fsm.test()

instana/http.py

Lines changed: 0 additions & 11 deletions
This file was deleted.

instana/log.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import logging as l
2+
import os
23

3-
logger = l.getLogger('instana')
4+
logger = l.getLogger('instana(' + str(os.getpid()) + ')')
45

56

67
def init(level):

instana/meter.py

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import gc as gc_
66
import sys
77
import instana.agent_const as a
8+
import copy
9+
import time
810

911

1012
class Snapshot(object):
@@ -63,6 +65,17 @@ class Metrics(object):
6365
def __init__(self, **kwds):
6466
self.__dict__.update(kwds)
6567

68+
def delta_data(self, delta):
69+
data = self.__dict__
70+
if delta is None:
71+
return data
72+
73+
unchanged_items = set(data.items()) & set(delta.items())
74+
for x in unchanged_items:
75+
data.pop(x[0])
76+
77+
return data
78+
6679

6780
class EntityData(object):
6881
pid = 0
@@ -75,33 +88,44 @@ def __init__(self, **kwds):
7588

7689
class Meter(object):
7790
SNAPSHOT_PERIOD = 600
78-
snapshot_countdown = 1
91+
snapshot_countdown = 30
7992
sensor = None
8093
last_usage = None
8194
last_collect = None
95+
timer = None
96+
last_metrics = None
8297

8398
def __init__(self, sensor):
8499
self.sensor = sensor
85-
self.tick()
100+
self.run()
101+
102+
def run(self):
103+
self.timer = t.Thread(target=self.collect_and_report)
104+
self.timer.daemon = True
105+
self.timer.name = "Instana Metric Collection"
106+
self.timer.start()
86107

87-
def tick(self):
88-
t.Timer(1, self.process).start()
108+
def collect_and_report(self):
109+
while 1:
110+
self.process()
111+
time.sleep(1)
89112

90113
def process(self):
91114
if self.sensor.agent.can_send():
92115
self.snapshot_countdown = self.snapshot_countdown - 1
93116
s = None
117+
cm = self.collect_metrics()
94118
if self.snapshot_countdown == 0:
95119
self.snapshot_countdown = self.SNAPSHOT_PERIOD
96120
s = self.collect_snapshot()
97-
98-
m = self.collect_metrics()
99-
d = EntityData(pid=os.getpid(), snapshot=s, metrics=m)
100-
101-
t.Thread(target=self.sensor.agent.request,
102-
args=(self.sensor.agent.make_url(a.AGENT_DATA_URL), "POST", d)).start()
103-
104-
self.tick()
121+
md = cm.delta_data(None)
122+
else:
123+
md = copy.deepcopy(cm).delta_data(self.last_metrics)
124+
125+
d = EntityData(pid=os.getpid(), snapshot=s, metrics=md)
126+
self.sensor.agent.request(
127+
self.sensor.agent.make_url(a.AGENT_DATA_URL), "POST", d)
128+
self.last_metrics = cm.__dict__
105129

106130
def collect_snapshot(self):
107131
try:

0 commit comments

Comments
 (0)