logtail-python 0.4.2__tar.gz → 0.4.3__tar.gz

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.
Files changed (30) hide show
  1. {logtail_python-0.4.2 → logtail_python-0.4.3}/PKG-INFO +2 -2
  2. {logtail_python-0.4.2 → logtail_python-0.4.3}/logtail/__init__.py +1 -1
  3. {logtail_python-0.4.2 → logtail_python-0.4.3}/logtail/frame.py +6 -1
  4. {logtail_python-0.4.2 → logtail_python-0.4.3}/logtail/handler.py +10 -3
  5. {logtail_python-0.4.2 → logtail_python-0.4.3}/logtail_python.egg-info/PKG-INFO +2 -2
  6. {logtail_python-0.4.2 → logtail_python-0.4.3}/setup.py +1 -1
  7. {logtail_python-0.4.2 → logtail_python-0.4.3}/tests/test_frame.py +15 -0
  8. {logtail_python-0.4.2 → logtail_python-0.4.3}/tests/test_handler.py +29 -0
  9. {logtail_python-0.4.2 → logtail_python-0.4.3}/LICENSE.md +0 -0
  10. {logtail_python-0.4.2 → logtail_python-0.4.3}/MANIFEST.in +0 -0
  11. {logtail_python-0.4.2 → logtail_python-0.4.3}/README.md +0 -0
  12. {logtail_python-0.4.2 → logtail_python-0.4.3}/logtail/compat.py +0 -0
  13. {logtail_python-0.4.2 → logtail_python-0.4.3}/logtail/flusher.py +0 -0
  14. {logtail_python-0.4.2 → logtail_python-0.4.3}/logtail/formatter.py +0 -0
  15. {logtail_python-0.4.2 → logtail_python-0.4.3}/logtail/helpers.py +0 -0
  16. {logtail_python-0.4.2 → logtail_python-0.4.3}/logtail/rq.py +0 -0
  17. {logtail_python-0.4.2 → logtail_python-0.4.3}/logtail/uploader.py +0 -0
  18. {logtail_python-0.4.2 → logtail_python-0.4.3}/logtail_python.egg-info/SOURCES.txt +0 -0
  19. {logtail_python-0.4.2 → logtail_python-0.4.3}/logtail_python.egg-info/dependency_links.txt +0 -0
  20. {logtail_python-0.4.2 → logtail_python-0.4.3}/logtail_python.egg-info/requires.txt +0 -0
  21. {logtail_python-0.4.2 → logtail_python-0.4.3}/logtail_python.egg-info/top_level.txt +0 -0
  22. {logtail_python-0.4.2 → logtail_python-0.4.3}/requirements.txt +0 -0
  23. {logtail_python-0.4.2 → logtail_python-0.4.3}/setup.cfg +0 -0
  24. {logtail_python-0.4.2 → logtail_python-0.4.3}/test-requirements.txt +0 -0
  25. {logtail_python-0.4.2 → logtail_python-0.4.3}/tests/__init__.py +0 -0
  26. {logtail_python-0.4.2 → logtail_python-0.4.3}/tests/test_flusher.py +0 -0
  27. {logtail_python-0.4.2 → logtail_python-0.4.3}/tests/test_formatter.py +0 -0
  28. {logtail_python-0.4.2 → logtail_python-0.4.3}/tests/test_helpers.py +0 -0
  29. {logtail_python-0.4.2 → logtail_python-0.4.3}/tests/test_rq.py +0 -0
  30. {logtail_python-0.4.2 → logtail_python-0.4.3}/tests/test_uploader.py +0 -0
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: logtail-python
3
- Version: 0.4.2
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.2
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
@@ -5,6 +5,6 @@ from .handler import LogtailHandler
5
5
  from .helpers import LogtailContext, DEFAULT_CONTEXT
6
6
  from .formatter import LogtailFormatter
7
7
 
8
- __version__ = '0.4.2'
8
+ __version__ = '0.4.3'
9
9
 
10
10
  context = DEFAULT_CONTEXT
@@ -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
- return path.relpath(pathname, path.dirname(__main__.__file__))
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
@@ -95,9 +95,16 @@ class LogtailHandler(logging.Handler):
95
95
  raise e
96
96
 
97
97
  def flush(self):
98
- if self.flush_thread and self.flush_thread.is_alive():
99
- if not self.flush_thread.flush(timeout=self.flush_timeout):
100
- print('Gave up waiting for Better Stack uploads after {}s, logs are still buffered'.format(self.flush_timeout))
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))
101
108
 
102
109
  def _reset_after_fork(self):
103
110
  # A forked child inherits the parent's queue with whatever was still buffered in it,
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: logtail-python
3
- Version: 0.4.2
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.2
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
@@ -3,7 +3,7 @@ import os
3
3
  from setuptools import setup
4
4
 
5
5
 
6
- VERSION = '0.4.2'
6
+ VERSION = '0.4.3'
7
7
  ROOT_DIR = os.path.dirname(__file__)
8
8
 
9
9
  REQUIREMENTS = [
@@ -4,9 +4,12 @@ from logtail.frame import create_frame
4
4
  from logtail.handler import LogtailHandler
5
5
  from logtail.helpers import LogtailContext
6
6
  import datetime
7
+ import ntpath
7
8
  import unittest
8
9
  import logging
9
10
 
11
+ from unittest.mock import patch
12
+
10
13
  class TestLogtailLogEntry(unittest.TestCase):
11
14
  def test_create_frame_happy_path(self):
12
15
  handler = LogtailHandler(source_token="some-source-token")
@@ -36,3 +39,15 @@ class TestLogtailLogEntry(unittest.TestCase):
36
39
 
37
40
  frame = create_frame(log_record, log_record.getMessage(), LogtailContext(), include_extra_attributes=True)
38
41
  self.assertIn('non_dict_key', frame)
42
+
43
+ @patch('logtail.frame.path', ntpath)
44
+ @patch('logtail.frame.__main__')
45
+ def test_create_frame_keeps_the_absolute_path_when_the_file_is_on_another_drive(self, main):
46
+ # PyInstaller unpacks the script to the system drive while the application's files stay
47
+ # on the drive it was started from, and Windows has no relative path between drives.
48
+ main.__file__ = 'C:\\Users\\zi\\AppData\\Local\\Temp\\_MEI61722\\main.py'
49
+ log_record = logging.LogRecord("logtail-test", 20, 'D:\\workspace\\app\\main.py', 10, "Some log message", [], None)
50
+
51
+ frame = create_frame(log_record, log_record.getMessage(), LogtailContext())
52
+
53
+ self.assertEqual(frame['context']['runtime']['file'], 'D:\\workspace\\app\\main.py')
@@ -411,6 +411,35 @@ class TestLogtailHandler(unittest.TestCase):
411
411
  self.assertEqual(uploads, [['hello']])
412
412
  self.assertTrue(handler.pipe.empty())
413
413
 
414
+ def test_flush_does_not_wait_while_the_caller_holds_the_logging_lock(self):
415
+ # logging.config.dictConfig() flushes the handlers it replaces while holding the global
416
+ # logging lock, and the worker's upload needs that lock whenever urllib3's logger has a
417
+ # level-cache miss, so waiting for the worker there can only deadlock.
418
+ logger = logging.getLogger(__name__)
419
+ logger.handlers = []
420
+ logger.setLevel(logging.INFO)
421
+ handler = LogtailHandler(source_token=self.source_token, flush_interval=0.01, check_interval=0.01, flush_timeout=2)
422
+ uploads = []
423
+ in_upload = threading.Event()
424
+
425
+ def upload(frame):
426
+ in_upload.set()
427
+ with logging._lock: # what urllib3's isEnabledFor() does on a level-cache miss
428
+ uploads.append([f['message'] for f in frame])
429
+ return mock.MagicMock(status_code=202)
430
+
431
+ handler.uploader = upload
432
+ logger.addHandler(handler)
433
+ self.addCleanup(self._stop_flush_worker, handler)
434
+
435
+ with logging._lock:
436
+ logger.info('hello')
437
+ self.assertTrue(in_upload.wait(2))
438
+ started = time.time()
439
+ handler.flush()
440
+ self.assertLess(time.time() - started, 0.5, 'flush() waited for a worker that needs the lock we hold')
441
+ self._wait_until(lambda: uploads == [['hello']])
442
+
414
443
  def _wait_until(self, condition):
415
444
  deadline = time.time() + 2
416
445
  while not condition() and time.time() < deadline:
File without changes
File without changes