Skip to content

Commit 996b5d8

Browse files
Alex2ndrgmelikovpyctrlphantomii
committed
Initial opensource release
Co-authored-by: George Melikov <mail@gmelikov.ru> Co-authored-by: Dmitry Burmistrov <pyctrl.dev@gmail.com> Co-authored-by: Eugene Frolov <eugene@frolov.net.ru>
0 parents  commit 996b5d8

28 files changed

Lines changed: 1454 additions & 0 deletions

.gitignore

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
6+
# C extensions
7+
*.so
8+
9+
# IDE
10+
.idea/
11+
12+
# Distribution / packaging
13+
.Python
14+
build/
15+
develop-eggs/
16+
dist/
17+
downloads/
18+
eggs/
19+
.eggs/
20+
lib/
21+
lib64/
22+
parts/
23+
sdist/
24+
var/
25+
wheels/
26+
*.egg-info/
27+
.installed.cfg
28+
*.egg
29+
MANIFEST
30+
ChangeLog
31+
cover/
32+
33+
# OS
34+
.DS_Store
35+
36+
# Installer logs
37+
pip-log.txt
38+
pip-delete-this-directory.txt
39+
40+
# Unit test / coverage reports
41+
htmlcov/
42+
.tox/
43+
.tox
44+
.coverage
45+
.coverage.*
46+
.cache
47+
nosetests.xml
48+
coverage.xml
49+
*.cover
50+
.hypothesis/
51+
.pytest_cache/
52+
53+
# Translations
54+
*.mo
55+
*.pot
56+
57+
# Django stuff:
58+
*.log
59+
local_settings.py
60+
db.sqlite3
61+
62+
# Flask stuff:
63+
instance/
64+
.webassets-cache
65+
66+
# Scrapy stuff:
67+
.scrapy
68+
69+
# Sphinx documentation
70+
docs/_build/
71+
72+
# PyBuilder
73+
target/
74+
75+
# Jupyter Notebook
76+
.ipynb_checkpoints
77+
78+
# pyenv
79+
.python-version
80+
81+
# celery beat schedule file
82+
celerybeat-schedule
83+
84+
# SageMath parsed files
85+
*.sage.py
86+
87+
# Environments
88+
.env
89+
.venv
90+
env/
91+
venv/
92+
ENV/
93+
env.bak/
94+
venv.bak/
95+
96+
# Spyder project settings
97+
.spyderproject
98+
.spyproject
99+
100+
# Rope project settings
101+
.ropeproject
102+
103+
# mkdocs documentation
104+
/site
105+
106+
# mypy
107+
.mypy_cache/
108+
109+
# Documentation
110+
docs/build
111+
docs/source/*.png
112+
113+
*~

AUTHORS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Alexandr Popov <popov2al@gmail.com>
2+
George Melikov <mail@gmelikov.ru>
3+
Dmitry Burmistrov <pyctrl.dev@gmail.com>
4+
Eugene Frolov <eugene@frolov.net.ru>

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Awesome metric sending library for nice guys!

obsender/__init__.py

Whitespace-only changes.

obsender/common.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import contextlib
2+
import datetime
3+
import inspect
4+
import json
5+
import logging
6+
import numbers
7+
import uuid
8+
try:
9+
from time import monotonic
10+
except ImportError:
11+
from monotonic import monotonic
12+
13+
import six
14+
15+
from obsender import exceptions
16+
17+
18+
class DurationMixin(object):
19+
"""Mixin to meter timedelta and send its metric for wrapped code"""
20+
21+
@contextlib.contextmanager
22+
def send_duration(self, metric_id, method_name='send_metric', **kwargs):
23+
if not hasattr(self, method_name):
24+
raise exceptions.SendMetricException(
25+
'Method %s not found' % method_name
26+
)
27+
method = getattr(self, method_name)
28+
start_time = monotonic()
29+
try:
30+
yield
31+
finally:
32+
method(
33+
metric_id,
34+
(monotonic() - start_time),
35+
**kwargs
36+
)
37+
38+
39+
class SenderLoggerAdapter(logging.LoggerAdapter):
40+
"""Writes Sender info for each log entry"""
41+
42+
def __init__(self, logger, sender):
43+
super(SenderLoggerAdapter, self).__init__(logger, {})
44+
self._sender = sender
45+
46+
def process(self, msg, kwargs):
47+
_msg = ("[obsender_sender=%s host=%s, app_name=%s] %s"
48+
% (self._sender.__class__.__name__,
49+
self._sender.host,
50+
self._sender.app_name,
51+
msg))
52+
return _msg, kwargs
53+
54+
55+
def jsonify(obj):
56+
if isinstance(obj, uuid.UUID):
57+
return str(obj)
58+
elif isinstance(obj, datetime.datetime) and obj.tzinfo is None:
59+
# TODO(d.burmistrov): make TZ conversion
60+
return obj.isoformat(sep='T') + 'Z'
61+
elif isinstance(obj, datetime.timedelta):
62+
return str(obj.total_seconds())
63+
else:
64+
raise TypeError(obj)
65+
66+
67+
def stringify(obj):
68+
if isinstance(obj, six.string_types):
69+
return obj
70+
elif obj is None:
71+
raise TypeError("can't convert ambiguous value %r" % obj)
72+
elif isinstance(obj, bool):
73+
return str(obj).lower()
74+
elif isinstance(obj, numbers.Number):
75+
return str(obj)
76+
elif isinstance(obj, uuid.UUID):
77+
return jsonify(obj)
78+
elif isinstance(obj, (datetime.datetime, datetime.timedelta)):
79+
return jsonify(obj)
80+
elif isinstance(obj, (list, tuple, dict)):
81+
return json.dumps(obj, default=jsonify)
82+
elif inspect.isclass(obj):
83+
return '%s.%s' % (obj.__module__, obj.__name__)
84+
elif isinstance(obj, BaseException):
85+
return repr(obj)
86+
else:
87+
raise TypeError("can't convert unsupported value %r" % obj)
88+
89+
90+
def floatify(obj):
91+
if isinstance(obj, numbers.Number):
92+
return float(obj)
93+
else:
94+
raise TypeError("Can't convert unsupported value %r" % obj)

obsender/config.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
from oslo_config import cfg
2+
3+
from obsender import constants
4+
5+
# Config example
6+
#
7+
# [victoria_metrics]
8+
# enabled = False
9+
# host = "localhost"
10+
# port = 8125
11+
# global_tags = {}
12+
# prefix = "apps.application_name.dev"
13+
# obsender_host = "localhost"
14+
15+
16+
victoria_metrics_opts = [
17+
cfg.BoolOpt('enabled',
18+
default=False,
19+
help="Enable sending of metrics to Victoria Metrics"),
20+
cfg.StrOpt('host',
21+
default=None,
22+
help="Destination host to send metrics"),
23+
cfg.StrOpt('prefix',
24+
default='',
25+
help=("Prefix of metrics. Should be "
26+
"'apps.<application_name>.<environment>'")),
27+
cfg.IntOpt('port',
28+
default=8125,
29+
help="Destination port to send metrics"),
30+
cfg.DictOpt('global_tags',
31+
default=None,
32+
help="Global tags for metrics in Victoria Metrics"),
33+
cfg.StrOpt('obsender_host',
34+
default=None,
35+
help="Specifies host to send metrics for"),
36+
]
37+
38+
# dpp_events_opts = [
39+
# cfg.BoolOpt('enabled',
40+
# default=False,
41+
# help="Enable sending of data to DPP"),
42+
# cfg.StrOpt('src_host',
43+
# default=None,
44+
# help="Determines host to send data for"),
45+
# cfg.StrOpt('dpp_url',
46+
# default='',
47+
# help="Determines Data Platform endpoint for data push"),
48+
# cfg.FloatOpt('timeout',
49+
# default=constants.DEFAULT_TIMEOUT,
50+
# help="Determines timeout for waiting data upload"),
51+
# ]
52+
53+
54+
CONF = cfg.CONF
55+
CONF.register_opts(victoria_metrics_opts, constants.VICTORIA_METRICS_DOMAIN)
56+
#CONF.register_opts(dpp_events_opts, constants.DPP_EVENTS_DOMAIN)

obsender/constants.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# VictoriaMetrics config section name
2+
VICTORIA_METRICS_DOMAIN = 'victoria_metrics'
3+
4+
# DPP for send events config section name
5+
DPP_EVENTS_DOMAIN = 'dpp_events'
6+
7+
# Default timeout for send event
8+
DEFAULT_TIMEOUT = 3

0 commit comments

Comments
 (0)