Skip to content

Commit 28167ab

Browse files
committed
Added cube
Signed-off-by: Vishal Rana <vr@labstack.com>
1 parent be98fa1 commit 28167ab

File tree

5 files changed

+152
-4
lines changed

5 files changed

+152
-4
lines changed

labstack/cube.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import os
2+
import time
3+
import socket
4+
import threading
5+
from datetime import datetime
6+
from apscheduler.schedulers.background import BackgroundScheduler
7+
import requests
8+
import psutil
9+
10+
sched = BackgroundScheduler()
11+
12+
class Cube():
13+
def __init__(self, api_key, node=socket.gethostname(), batch_size=60,
14+
dispatch_interval=60, tags=None):
15+
self.api_key = api_key
16+
self.node = node
17+
self.batch_size = batch_size
18+
self.dispatch_interval = dispatch_interval
19+
self.tags = tags
20+
self.start_time = time.time()
21+
self.uptime = 0
22+
self.cpu = 0.0
23+
self.memory = 0
24+
self.active_requests = 0
25+
self.requests = []
26+
self.lock = threading.Lock()
27+
28+
def system():
29+
p = psutil.Process(os.getpid())
30+
self.uptime = int(time.time() - self.start_time)
31+
self.cpu = p.cpu_percent()
32+
self.memory = p.memory_full_info().rss
33+
34+
sched.add_job(self._dispatch, 'interval', seconds=dispatch_interval)
35+
sched.add_job(system, 'interval', seconds=1)
36+
sched.start()
37+
38+
def _dispatch(self):
39+
if not self.requests:
40+
return
41+
42+
r = requests.post('https://api.labstack.com/cube', headers={
43+
'User-Agent': 'labstack/cube',
44+
'Authorization': 'Bearer ' + self.api_key
45+
}, json=self.requests)
46+
if not 200 <= r.status_code < 300:
47+
# TOTO: handler error
48+
print('cube error', r.json())
49+
50+
# Reset requests
51+
self.requests.clear()
52+
53+
def start(self, request):
54+
with self.lock:
55+
self.active_requests += 1
56+
57+
request['time'] = int(datetime.now().timestamp() * 1000000)
58+
request['active'] = self.active_requests
59+
request['node'] = self.node
60+
request['uptime'] = self.uptime
61+
request['cpu'] = self.cpu
62+
request['memory'] = self.memory
63+
request['tags'] = self.tags
64+
self.requests.append(request)
65+
66+
return request
67+
68+
def stop(self, request):
69+
with self.lock:
70+
self.active_requests -= 1
71+
request['latency'] = int(datetime.now().timestamp() * 1000000) - request['time']
72+
73+
# Dispatch batch
74+
if len(self.requests) >= self.batch_size:
75+
threading.Thread(target=self._dispatch).start()
76+

labstack/django.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from django.conf import settings
2+
from .cube import Cube
3+
from .util import strip_port
4+
5+
def cube(get_response):
6+
options = settings.CUBE
7+
c = Cube(**options)
8+
9+
def middleware(request):
10+
r = c.start({
11+
'id': request.META.get('HTTP_X_REQUEST_ID'),
12+
'host': strip_port(request.get_host()),
13+
'path': request.path,
14+
'method': request.method,
15+
'bytes_in': int(request.META.get('CONTENT_LENGTH') or 0),
16+
# TODO: revisit
17+
'remote_ip': request.META.get('HTTP_X_FORWARDED_FOR') or request.META.get('REMOTE_ADDR'),
18+
'client_id': request.META.get('HTTP_X_CLIENT_ID'),
19+
'user_agent': request.META.get('HTTP_USER_AGENT')
20+
})
21+
22+
response = get_response(request)
23+
24+
# https://docs.djangoproject.com/en/2.0/_modules/django/middleware/common/#CommonMiddleware
25+
if not response.streaming and not response.has_header('Content-Length'):
26+
response['Content-Length'] = str(len(response.content))
27+
28+
r['id'] = r['id'] or response.get('X-Request-ID')
29+
r['status'] = response.status_code
30+
r['bytes_out'] = int(response.get('Content-Length') or 0)
31+
c.stop(r)
32+
33+
return response
34+
35+
return middleware

labstack/flask.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from flask import request, g
2+
from .cube import Cube
3+
from .util import strip_port
4+
5+
def cube(app, api_key, **kwargs):
6+
c = Cube(api_key, **kwargs)
7+
8+
@app.before_request
9+
def before_request():
10+
g._r = c.start({
11+
'id': request.headers.get('X-Request-ID'),
12+
'host': strip_port(request.host),
13+
'path': request.path,
14+
'method': request.method,
15+
'bytes_in': int(request.headers.get('Content-Length') or 0),
16+
# TODO: revisit
17+
'remote_ip': request.headers.get('X-Forwarded-For', request.remote_addr),
18+
'client_id': request.headers.get('X-Client-ID'),
19+
'user_agent': request.headers.get('User-Agent')
20+
})
21+
22+
@app.after_request
23+
def after_request(response):
24+
r = g._r
25+
r['id'] = r['id'] or response.headers.get('X-Request-ID')
26+
r['status'] = response.status_code
27+
r['bytes_out'] = int(response.headers.get('Content-Length') or 0)
28+
c.stop(r)
29+
return response

labstack/util.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
def strip_port(host):
2+
colon = host.find(':')
3+
if colon == -1:
4+
return host
5+
i = host.find(']')
6+
if i != -1:
7+
return host[host.find('(')+1:i]
8+
return host[:colon]

setup.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
setup(
44
name='labstack',
5-
version='0.31.3',
6-
description='Official Python client library for the LabStack API',
5+
version='0.31.4',
6+
description='Official Python client library for the LabStack platform',
77
long_description='`<https://github.com/labstack/labstack-python>`_',
88
keywords='image compress, image resize, text summary, barcode generate, barcode scan',
99
url='https://github.com/labstack/labstack-python',
@@ -15,7 +15,7 @@
1515
'requests==2.18.1'
1616
],
1717
classifiers=[
18-
'Programming Language :: Python :: 3.5',
19-
'Programming Language :: Python :: 3.6'
18+
'Programming Language :: Python :: 3.5',
19+
'Programming Language :: Python :: 3.6'
2020
]
2121
)

0 commit comments

Comments
 (0)