logtail-python 0.4.1__py2.py3-none-any.whl → 0.4.3__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- logtail/__init__.py +1 -1
- logtail/flusher.py +23 -4
- logtail/frame.py +6 -1
- logtail/handler.py +15 -3
- logtail/uploader.py +21 -1
- {logtail_python-0.4.1.dist-info → logtail_python-0.4.3.dist-info}/METADATA +3 -2
- logtail_python-0.4.3.dist-info/RECORD +14 -0
- logtail_python-0.4.1.dist-info/RECORD +0 -14
- {logtail_python-0.4.1.dist-info → logtail_python-0.4.3.dist-info}/WHEEL +0 -0
- {logtail_python-0.4.1.dist-info → logtail_python-0.4.3.dist-info}/licenses/LICENSE.md +0 -0
- {logtail_python-0.4.1.dist-info → logtail_python-0.4.3.dist-info}/top_level.txt +0 -0
logtail/__init__.py
CHANGED
logtail/flusher.py
CHANGED
|
@@ -100,11 +100,30 @@ class FlushWorker(threading.Thread):
|
|
|
100
100
|
if shutdown and self.pipe.empty():
|
|
101
101
|
self.should_run = False
|
|
102
102
|
|
|
103
|
-
def flush(self):
|
|
103
|
+
def flush(self, timeout=None):
|
|
104
|
+
"""Block until the worker has drained the queue.
|
|
105
|
+
|
|
106
|
+
If ``timeout`` is given (in seconds), return after at most that much
|
|
107
|
+
wall time has elapsed even if the queue is still non-empty. Returns
|
|
108
|
+
``True`` if the queue was drained, ``False`` if the timeout fired.
|
|
109
|
+
|
|
110
|
+
The bound matters when the caller holds the global logging lock, as
|
|
111
|
+
``logging.config.dictConfig()`` does while it flushes and closes the
|
|
112
|
+
handlers it replaces: if this worker is inside an upload whose urllib3
|
|
113
|
+
``debug()`` call needs that same lock, an unbounded wait deadlocks the
|
|
114
|
+
process. It also keeps a slow or unreachable endpoint from stalling a
|
|
115
|
+
shutdown forever.
|
|
116
|
+
"""
|
|
104
117
|
self._flushing = True
|
|
105
|
-
|
|
106
|
-
time.
|
|
107
|
-
|
|
118
|
+
try:
|
|
119
|
+
deadline = None if timeout is None else time.time() + timeout
|
|
120
|
+
while not self._clean or not self.pipe.empty():
|
|
121
|
+
if deadline is not None and time.time() >= deadline:
|
|
122
|
+
return False
|
|
123
|
+
time.sleep(self.check_interval)
|
|
124
|
+
return True
|
|
125
|
+
finally:
|
|
126
|
+
self._flushing = False
|
|
108
127
|
|
|
109
128
|
def in_flush_worker():
|
|
110
129
|
return isinstance(threading.current_thread(), FlushWorker)
|
logtail/frame.py
CHANGED
|
@@ -93,4 +93,9 @@ def _relative_to_main_module_if_possible(pathname):
|
|
|
93
93
|
return _relative_to_main_module(pathname) if has_main_module else pathname
|
|
94
94
|
|
|
95
95
|
def _relative_to_main_module(pathname):
|
|
96
|
-
|
|
96
|
+
try:
|
|
97
|
+
return path.relpath(pathname, path.dirname(__main__.__file__))
|
|
98
|
+
except ValueError:
|
|
99
|
+
# Windows has no relative path between drives, and a PyInstaller build unpacks the
|
|
100
|
+
# script to the system drive while the application may run from another one.
|
|
101
|
+
return pathname
|
logtail/handler.py
CHANGED
|
@@ -19,6 +19,8 @@ DEFAULT_RAISE_EXCEPTIONS = False
|
|
|
19
19
|
DEFAULT_DROP_EXTRA_EVENTS = True
|
|
20
20
|
DEFAULT_INCLUDE_EXTRA_ATTRIBUTES = True
|
|
21
21
|
DEFAULT_TIMEOUT = 30
|
|
22
|
+
DEFAULT_FLUSH_TIMEOUT = 30
|
|
23
|
+
|
|
22
24
|
|
|
23
25
|
_handlers = weakref.WeakSet()
|
|
24
26
|
|
|
@@ -35,6 +37,7 @@ class LogtailHandler(logging.Handler):
|
|
|
35
37
|
include_extra_attributes=DEFAULT_INCLUDE_EXTRA_ATTRIBUTES,
|
|
36
38
|
context=DEFAULT_CONTEXT,
|
|
37
39
|
timeout=DEFAULT_TIMEOUT,
|
|
40
|
+
flush_timeout=DEFAULT_FLUSH_TIMEOUT,
|
|
38
41
|
level=logging.NOTSET):
|
|
39
42
|
super(LogtailHandler, self).__init__(level=level)
|
|
40
43
|
self.source_token = source_token
|
|
@@ -51,6 +54,7 @@ class LogtailHandler(logging.Handler):
|
|
|
51
54
|
self.flush_interval = flush_interval
|
|
52
55
|
self.check_interval = check_interval
|
|
53
56
|
self.raise_exceptions = raise_exceptions
|
|
57
|
+
self.flush_timeout = flush_timeout
|
|
54
58
|
self.dropcount = 0
|
|
55
59
|
# Do not initialize the flush thread yet because it causes issues on Render.
|
|
56
60
|
self.flush_thread = None
|
|
@@ -91,8 +95,16 @@ class LogtailHandler(logging.Handler):
|
|
|
91
95
|
raise e
|
|
92
96
|
|
|
93
97
|
def flush(self):
|
|
94
|
-
if self.flush_thread and self.flush_thread.is_alive():
|
|
95
|
-
|
|
98
|
+
if not (self.flush_thread and self.flush_thread.is_alive()):
|
|
99
|
+
return
|
|
100
|
+
if logging._lock._is_owned():
|
|
101
|
+
# logging.config.dictConfig() flushes the handlers it replaces while holding the global
|
|
102
|
+
# logging lock, and the worker's upload needs that lock whenever urllib3's logger has a
|
|
103
|
+
# level-cache miss, so waiting here could only deadlock. Nothing is lost: the worker
|
|
104
|
+
# keeps running and sends what is queued as soon as the lock is released.
|
|
105
|
+
return
|
|
106
|
+
if not self.flush_thread.flush(timeout=self.flush_timeout):
|
|
107
|
+
print('Gave up waiting for Better Stack uploads after {}s, logs are still buffered'.format(self.flush_timeout))
|
|
96
108
|
|
|
97
109
|
def _reset_after_fork(self):
|
|
98
110
|
# A forked child inherits the parent's queue with whatever was still buffered in it,
|
|
@@ -100,7 +112,7 @@ class LogtailHandler(logging.Handler):
|
|
|
100
112
|
# whose socket it shares with the parent, so it starts over with fresh ones.
|
|
101
113
|
self.pipe = queue.Queue(maxsize=self.buffer_capacity)
|
|
102
114
|
self.flush_thread = None
|
|
103
|
-
self.uploader
|
|
115
|
+
self.uploader.reset()
|
|
104
116
|
|
|
105
117
|
|
|
106
118
|
def _reset_handlers_after_fork():
|
logtail/uploader.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# coding: utf-8
|
|
2
2
|
from __future__ import print_function, unicode_literals
|
|
3
|
+
import os
|
|
3
4
|
import msgpack
|
|
4
5
|
import requests
|
|
6
|
+
import requests.utils
|
|
5
7
|
|
|
6
8
|
class Fake500(object):
|
|
7
9
|
def __init__(self, exception):
|
|
@@ -13,11 +15,17 @@ class Uploader(object):
|
|
|
13
15
|
self.source_token = source_token
|
|
14
16
|
self.host = host
|
|
15
17
|
self.timeout = timeout
|
|
16
|
-
|
|
18
|
+
# requests would otherwise ask the environment, and on macOS SystemConfiguration, for
|
|
19
|
+
# proxy settings on every request. Resolving them once here keeps the flush thread out
|
|
20
|
+
# of CoreFoundation, which refuses to run in a child forked from a multithreaded
|
|
21
|
+
# process, such as an RQ work horse. The CA bundle variables follow the same rule.
|
|
22
|
+
self.proxies = requests.utils.get_environ_proxies(host)
|
|
23
|
+
self.verify = os.environ.get('REQUESTS_CA_BUNDLE') or os.environ.get('CURL_CA_BUNDLE') or True
|
|
17
24
|
self.headers = {
|
|
18
25
|
'Authorization': 'Bearer %s' % source_token,
|
|
19
26
|
'Content-Type': 'application/msgpack',
|
|
20
27
|
}
|
|
28
|
+
self.session = self._new_session()
|
|
21
29
|
|
|
22
30
|
def __call__(self, frame):
|
|
23
31
|
data = msgpack.packb(frame, use_bin_type=True)
|
|
@@ -25,3 +33,15 @@ class Uploader(object):
|
|
|
25
33
|
return self.session.post(self.host, data=data, headers=self.headers, timeout=self.timeout)
|
|
26
34
|
except requests.RequestException as e:
|
|
27
35
|
return Fake500(e)
|
|
36
|
+
|
|
37
|
+
def reset(self):
|
|
38
|
+
# A forked child shares the parent's pooled socket, so it needs a session of its own,
|
|
39
|
+
# built from the settings resolved above rather than looked up again.
|
|
40
|
+
self.session = self._new_session()
|
|
41
|
+
|
|
42
|
+
def _new_session(self):
|
|
43
|
+
session = requests.Session()
|
|
44
|
+
session.trust_env = False
|
|
45
|
+
session.proxies = self.proxies
|
|
46
|
+
session.verify = self.verify
|
|
47
|
+
return session
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: logtail-python
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.3
|
|
4
4
|
Summary: Better Stack client library
|
|
5
5
|
Home-page: https://github.com/logtail/logtail-python
|
|
6
|
-
Download-URL: https://github.com/logtail/logtail-python/tarball/0.4.
|
|
6
|
+
Download-URL: https://github.com/logtail/logtail-python/tarball/0.4.3
|
|
7
7
|
Author: Logtail
|
|
8
8
|
Author-email: hello@logtail.com
|
|
9
9
|
License: ISC
|
|
@@ -17,6 +17,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.13
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
20
21
|
Classifier: Programming Language :: Python
|
|
21
22
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
23
|
Requires-Python: >=3.10
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
logtail/__init__.py,sha256=s4LOmVNtxWLWS5XoL0bE-d7v8zZAqP_d-xh77sTZ_z8,252
|
|
2
|
+
logtail/compat.py,sha256=KbZmcmDpj_vbMotp16pADPwJ1FkEHGDNjKbOfCy-c10,141
|
|
3
|
+
logtail/flusher.py,sha256=bPSCvKNniXBZWYY5AVZ0w6lFkxswe739Ooa3ZAj608s,5987
|
|
4
|
+
logtail/formatter.py,sha256=nk4W1G95P19ijylPBJ8D9FlO_qbh3tfbv7xzCSfpDN8,968
|
|
5
|
+
logtail/frame.py,sha256=Vmh6UZWz86i16faH1fYqeCS6UUy7-29-XHDgMPwGfB0,3571
|
|
6
|
+
logtail/handler.py,sha256=Cuz8QmFGzBVTpMb6DZZV6VEmJ9smlM3RaqTTBei3GXY,4952
|
|
7
|
+
logtail/helpers.py,sha256=ezDTLsTBAtCtIh_XptSYOiBrZFa-E9wo2SWTnjKgOOU,1156
|
|
8
|
+
logtail/rq.py,sha256=SEjVwjp8lRLab2F0StdssBQ0-vFK1CekEgYwkSICBeo,1023
|
|
9
|
+
logtail/uploader.py,sha256=5oeNxmnyogq1w9MoK2YkxQGxQy_bJ-yS7RqWzltQrKY,1873
|
|
10
|
+
logtail_python-0.4.3.dist-info/licenses/LICENSE.md,sha256=aCBMqxfm7QNyFR9JiJ2GSMvhAURXN2E0yTcYNQloqI0,737
|
|
11
|
+
logtail_python-0.4.3.dist-info/METADATA,sha256=ry2U2ZRaMnPu10JiI7AaSjB2BSu5K-45Q7QfVe4UBTM,2480
|
|
12
|
+
logtail_python-0.4.3.dist-info/WHEEL,sha256=4YBfCYNH4wlLpv3pzq1hbEuIlXA4WJabKLFurZ7eTL0,109
|
|
13
|
+
logtail_python-0.4.3.dist-info/top_level.txt,sha256=QZIbQogN1yFBgjiJ-8NlYArVCL00gBoYbEUT_Ew8l-Q,8
|
|
14
|
+
logtail_python-0.4.3.dist-info/RECORD,,
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
logtail/__init__.py,sha256=9PQmXLJchEiOwtJ6WfZuo9AT1aL3rl6ZPD7TxLKwuz0,252
|
|
2
|
-
logtail/compat.py,sha256=KbZmcmDpj_vbMotp16pADPwJ1FkEHGDNjKbOfCy-c10,141
|
|
3
|
-
logtail/flusher.py,sha256=EjLbAgooBjYs0KKCMVqxRddDpMD8qnVuhXSEMZTISQw,5017
|
|
4
|
-
logtail/formatter.py,sha256=nk4W1G95P19ijylPBJ8D9FlO_qbh3tfbv7xzCSfpDN8,968
|
|
5
|
-
logtail/frame.py,sha256=p7vVYcajZvjH4FJDt4ADLfYljVbQXKTGKk3kFec5JUs,3335
|
|
6
|
-
logtail/handler.py,sha256=CruppN_XD0W-Ifsl1iu99T0yWw66utkl7_un6V6OO1Y,4265
|
|
7
|
-
logtail/helpers.py,sha256=ezDTLsTBAtCtIh_XptSYOiBrZFa-E9wo2SWTnjKgOOU,1156
|
|
8
|
-
logtail/rq.py,sha256=SEjVwjp8lRLab2F0StdssBQ0-vFK1CekEgYwkSICBeo,1023
|
|
9
|
-
logtail/uploader.py,sha256=Ilhh8wuAGlz1YJcQpzXtFqwD-2RYmuamKv3dFNoTWUg,868
|
|
10
|
-
logtail_python-0.4.1.dist-info/licenses/LICENSE.md,sha256=aCBMqxfm7QNyFR9JiJ2GSMvhAURXN2E0yTcYNQloqI0,737
|
|
11
|
-
logtail_python-0.4.1.dist-info/METADATA,sha256=F29t1-2304x3Ofa8b1cASyECF4V85ef2Ndo5yovJzmI,2429
|
|
12
|
-
logtail_python-0.4.1.dist-info/WHEEL,sha256=4YBfCYNH4wlLpv3pzq1hbEuIlXA4WJabKLFurZ7eTL0,109
|
|
13
|
-
logtail_python-0.4.1.dist-info/top_level.txt,sha256=QZIbQogN1yFBgjiJ-8NlYArVCL00gBoYbEUT_Ew8l-Q,8
|
|
14
|
-
logtail_python-0.4.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|