logtail-python 0.2.10__tar.gz → 0.3.1__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 (38) hide show
  1. {logtail-python-0.2.10/logtail_python.egg-info → logtail_python-0.3.1}/PKG-INFO +4 -6
  2. {logtail-python-0.2.10 → logtail_python-0.3.1}/logtail/__init__.py +1 -1
  3. {logtail-python-0.2.10 → logtail_python-0.3.1}/logtail/flusher.py +15 -4
  4. {logtail-python-0.2.10 → logtail_python-0.3.1}/logtail/frame.py +8 -2
  5. {logtail-python-0.2.10 → logtail_python-0.3.1}/logtail/handler.py +9 -1
  6. {logtail-python-0.2.10 → logtail_python-0.3.1/logtail_python.egg-info}/PKG-INFO +4 -6
  7. {logtail-python-0.2.10 → logtail_python-0.3.1}/setup.py +3 -5
  8. logtail_python-0.3.1/tests/__pycache__/test_flusher.cpython-311.pyc +0 -0
  9. {logtail-python-0.2.10 → logtail_python-0.3.1}/tests/__pycache__/test_formatter.cpython-311.pyc +0 -0
  10. {logtail-python-0.2.10 → logtail_python-0.3.1}/tests/__pycache__/test_frame.cpython-311.pyc +0 -0
  11. logtail_python-0.3.1/tests/__pycache__/test_handler.cpython-311.pyc +0 -0
  12. {logtail-python-0.2.10 → logtail_python-0.3.1}/tests/__pycache__/test_helpers.cpython-311.pyc +0 -0
  13. logtail_python-0.3.1/tests/__pycache__/test_uploader.cpython-311.pyc +0 -0
  14. {logtail-python-0.2.10 → logtail_python-0.3.1}/tests/test_flusher.py +8 -5
  15. {logtail-python-0.2.10 → logtail_python-0.3.1}/tests/test_handler.py +38 -14
  16. {logtail-python-0.2.10 → logtail_python-0.3.1}/tests/test_uploader.py +3 -1
  17. logtail-python-0.2.10/tests/__pycache__/test_flusher.cpython-311.pyc +0 -0
  18. logtail-python-0.2.10/tests/__pycache__/test_handler.cpython-311.pyc +0 -0
  19. logtail-python-0.2.10/tests/__pycache__/test_uploader.cpython-311.pyc +0 -0
  20. {logtail-python-0.2.10 → logtail_python-0.3.1}/LICENSE.md +0 -0
  21. {logtail-python-0.2.10 → logtail_python-0.3.1}/MANIFEST.in +0 -0
  22. {logtail-python-0.2.10 → logtail_python-0.3.1}/README.md +0 -0
  23. {logtail-python-0.2.10 → logtail_python-0.3.1}/logtail/compat.py +0 -0
  24. {logtail-python-0.2.10 → logtail_python-0.3.1}/logtail/formatter.py +0 -0
  25. {logtail-python-0.2.10 → logtail_python-0.3.1}/logtail/helpers.py +0 -0
  26. {logtail-python-0.2.10 → logtail_python-0.3.1}/logtail/uploader.py +0 -0
  27. {logtail-python-0.2.10 → logtail_python-0.3.1}/logtail_python.egg-info/SOURCES.txt +0 -0
  28. {logtail-python-0.2.10 → logtail_python-0.3.1}/logtail_python.egg-info/dependency_links.txt +0 -0
  29. {logtail-python-0.2.10 → logtail_python-0.3.1}/logtail_python.egg-info/requires.txt +0 -0
  30. {logtail-python-0.2.10 → logtail_python-0.3.1}/logtail_python.egg-info/top_level.txt +0 -0
  31. {logtail-python-0.2.10 → logtail_python-0.3.1}/requirements.txt +0 -0
  32. {logtail-python-0.2.10 → logtail_python-0.3.1}/setup.cfg +0 -0
  33. {logtail-python-0.2.10 → logtail_python-0.3.1}/test-requirements.txt +0 -0
  34. {logtail-python-0.2.10 → logtail_python-0.3.1}/tests/__init__.py +0 -0
  35. {logtail-python-0.2.10 → logtail_python-0.3.1}/tests/__pycache__/__init__.cpython-311.pyc +0 -0
  36. {logtail-python-0.2.10 → logtail_python-0.3.1}/tests/test_formatter.py +0 -0
  37. {logtail-python-0.2.10 → logtail_python-0.3.1}/tests/test_frame.py +0 -0
  38. {logtail-python-0.2.10 → logtail_python-0.3.1}/tests/test_helpers.py +0 -0
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: logtail-python
3
- Version: 0.2.10
3
+ Version: 0.3.1
4
4
  Summary: Logtail.com client library
5
5
  Home-page: https://github.com/logtail/logtail-python
6
- Download-URL: https://github.com/logtail/logtail-python/tarball/0.2.10
6
+ Download-URL: https://github.com/logtail/logtail-python/tarball/0.3.1
7
7
  Author: Logtail
8
8
  Author-email: hello@logtail.com
9
9
  License: ISC
@@ -11,14 +11,12 @@ Keywords: api,logtail,logging,client
11
11
  Classifier: Intended Audience :: Developers
12
12
  Classifier: License :: OSI Approved :: ISC License (ISCL)
13
13
  Classifier: Operating System :: OS Independent
14
- Classifier: Programming Language :: Python :: 2
15
- Classifier: Programming Language :: Python :: 2.7
16
14
  Classifier: Programming Language :: Python :: 3
17
- Classifier: Programming Language :: Python :: 3.5
18
- Classifier: Programming Language :: Python :: 3.6
19
15
  Classifier: Programming Language :: Python :: 3.7
20
16
  Classifier: Programming Language :: Python :: 3.8
21
17
  Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
22
20
  Classifier: Programming Language :: Python
23
21
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
22
  Description-Content-Type: text/markdown
@@ -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.2.10'
8
+ __version__ = '0.3.1'
9
9
 
10
10
  context = DEFAULT_CONTEXT
@@ -10,14 +10,17 @@ RETRY_SCHEDULE = (1, 10, 60) # seconds
10
10
 
11
11
 
12
12
  class FlushWorker(threading.Thread):
13
- def __init__(self, upload, pipe, buffer_capacity, flush_interval):
13
+ def __init__(self, upload, pipe, buffer_capacity, flush_interval, check_interval):
14
14
  threading.Thread.__init__(self)
15
15
  self.parent_thread = threading.current_thread()
16
16
  self.upload = upload
17
17
  self.pipe = pipe
18
18
  self.buffer_capacity = buffer_capacity
19
19
  self.flush_interval = flush_interval
20
+ self.check_interval = check_interval
20
21
  self.should_run = True
22
+ self._flushing = False
23
+ self._clean = True
21
24
 
22
25
  def run(self):
23
26
  while self.should_run:
@@ -27,6 +30,7 @@ class FlushWorker(threading.Thread):
27
30
  last_flush = time.time()
28
31
  time_remaining = _initial_time_remaining(self.flush_interval)
29
32
  frame = []
33
+ self._clean = True
30
34
 
31
35
  # If the parent thread has exited but there are still outstanding
32
36
  # events, attempt to send them before exiting.
@@ -38,16 +42,17 @@ class FlushWorker(threading.Thread):
38
42
  # `flush_interval` seconds have passed without sending any events.
39
43
  while len(frame) < self.buffer_capacity and time_remaining > 0:
40
44
  try:
41
- # Blocks for up to 1.0 seconds for each item to prevent
45
+ # Blocks for up to `check_interval` seconds for each item to prevent
42
46
  # spinning and burning CPU unnecessarily. Could block for the
43
47
  # entire amount of `time_remaining` but then in the case that
44
48
  # the parent thread has exited, that entire amount of time
45
49
  # would be waited before this child worker thread exits.
46
- entry = self.pipe.get(block=(not shutdown), timeout=1.0)
50
+ entry = self.pipe.get(block=(not shutdown), timeout=self.check_interval)
51
+ self._clean = False
47
52
  frame.append(entry)
48
53
  self.pipe.task_done()
49
54
  except queue.Empty:
50
- if shutdown:
55
+ if shutdown or self._flushing:
51
56
  break
52
57
  shutdown = not self.parent_thread.is_alive()
53
58
  time_remaining = _calculate_time_remaining(last_flush, self.flush_interval)
@@ -68,9 +73,15 @@ class FlushWorker(threading.Thread):
68
73
  if response.status_code == 500 and getattr(response, "exception") != None:
69
74
  print('Failed to send logs to Better Stack after {} retries: {}'.format(len(RETRY_SCHEDULE), response.exception))
70
75
 
76
+ self._clean = True
71
77
  if shutdown and self.pipe.empty():
72
78
  self.should_run = False
73
79
 
80
+ def flush(self):
81
+ self._flushing = True
82
+ while not self._clean or not self.pipe.empty():
83
+ time.sleep(self.check_interval)
84
+ self._flushing = False
74
85
 
75
86
  def _initial_time_remaining(flush_interval):
76
87
  return flush_interval
@@ -60,6 +60,9 @@ def _parse_custom_events(record, include_extra_attributes):
60
60
  def _remove_circular_dependencies(obj, memo=None):
61
61
  if memo is None:
62
62
  memo = set()
63
+ # Skip immutable types, which can't contain circular dependencies
64
+ if isinstance(obj, (str, int, float, bool)):
65
+ return obj
63
66
  if id(obj) in memo:
64
67
  return "<omitted circular reference>"
65
68
  memo.add(id(obj))
@@ -69,8 +72,11 @@ def _remove_circular_dependencies(obj, memo=None):
69
72
  new_dict[key] = _remove_circular_dependencies(value, memo)
70
73
  return new_dict
71
74
  elif isinstance(obj, list):
72
- new_list = [_remove_circular_dependencies(item, memo) for item in obj]
73
- return new_list
75
+ return [_remove_circular_dependencies(item, memo) for item in obj]
76
+ elif isinstance(obj, tuple):
77
+ return tuple(_remove_circular_dependencies(item, memo) for item in obj)
78
+ elif isinstance(obj, set):
79
+ return {_remove_circular_dependencies(item, memo) for item in obj}
74
80
  else:
75
81
  return obj
76
82
 
@@ -12,6 +12,7 @@ from .frame import create_frame
12
12
  DEFAULT_HOST = 'https://in.logs.betterstack.com'
13
13
  DEFAULT_BUFFER_CAPACITY = 1000
14
14
  DEFAULT_FLUSH_INTERVAL = 1
15
+ DEFAULT_CHECK_INTERVAL = 0.1
15
16
  DEFAULT_RAISE_EXCEPTIONS = False
16
17
  DEFAULT_DROP_EXTRA_EVENTS = True
17
18
  DEFAULT_INCLUDE_EXTRA_ATTRIBUTES = True
@@ -23,6 +24,7 @@ class LogtailHandler(logging.Handler):
23
24
  host=DEFAULT_HOST,
24
25
  buffer_capacity=DEFAULT_BUFFER_CAPACITY,
25
26
  flush_interval=DEFAULT_FLUSH_INTERVAL,
27
+ check_interval=DEFAULT_CHECK_INTERVAL,
26
28
  raise_exceptions=DEFAULT_RAISE_EXCEPTIONS,
27
29
  drop_extra_events=DEFAULT_DROP_EXTRA_EVENTS,
28
30
  include_extra_attributes=DEFAULT_INCLUDE_EXTRA_ATTRIBUTES,
@@ -38,6 +40,7 @@ class LogtailHandler(logging.Handler):
38
40
  self.include_extra_attributes = include_extra_attributes
39
41
  self.buffer_capacity = buffer_capacity
40
42
  self.flush_interval = flush_interval
43
+ self.check_interval = check_interval
41
44
  self.raise_exceptions = raise_exceptions
42
45
  self.dropcount = 0
43
46
  # Do not initialize the flush thread yet because it causes issues on Render.
@@ -51,7 +54,8 @@ class LogtailHandler(logging.Handler):
51
54
  self.uploader,
52
55
  self.pipe,
53
56
  self.buffer_capacity,
54
- self.flush_interval
57
+ self.flush_interval,
58
+ self.check_interval,
55
59
  )
56
60
  self.flush_thread.start()
57
61
 
@@ -71,3 +75,7 @@ class LogtailHandler(logging.Handler):
71
75
  except Exception as e:
72
76
  if self.raise_exceptions:
73
77
  raise e
78
+
79
+ def flush(self):
80
+ if self.flush_thread and self.flush_thread.is_alive():
81
+ self.flush_thread.flush()
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: logtail-python
3
- Version: 0.2.10
3
+ Version: 0.3.1
4
4
  Summary: Logtail.com client library
5
5
  Home-page: https://github.com/logtail/logtail-python
6
- Download-URL: https://github.com/logtail/logtail-python/tarball/0.2.10
6
+ Download-URL: https://github.com/logtail/logtail-python/tarball/0.3.1
7
7
  Author: Logtail
8
8
  Author-email: hello@logtail.com
9
9
  License: ISC
@@ -11,14 +11,12 @@ Keywords: api,logtail,logging,client
11
11
  Classifier: Intended Audience :: Developers
12
12
  Classifier: License :: OSI Approved :: ISC License (ISCL)
13
13
  Classifier: Operating System :: OS Independent
14
- Classifier: Programming Language :: Python :: 2
15
- Classifier: Programming Language :: Python :: 2.7
16
14
  Classifier: Programming Language :: Python :: 3
17
- Classifier: Programming Language :: Python :: 3.5
18
- Classifier: Programming Language :: Python :: 3.6
19
15
  Classifier: Programming Language :: Python :: 3.7
20
16
  Classifier: Programming Language :: Python :: 3.8
21
17
  Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
22
20
  Classifier: Programming Language :: Python
23
21
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
22
  Description-Content-Type: text/markdown
@@ -3,7 +3,7 @@ import os
3
3
  from setuptools import setup
4
4
 
5
5
 
6
- VERSION = '0.2.10'
6
+ VERSION = '0.3.1'
7
7
  ROOT_DIR = os.path.dirname(__file__)
8
8
 
9
9
  REQUIREMENTS = [
@@ -33,14 +33,12 @@ setup(
33
33
  'Intended Audience :: Developers',
34
34
  'License :: OSI Approved :: ISC License (ISCL)',
35
35
  'Operating System :: OS Independent',
36
- 'Programming Language :: Python :: 2',
37
- 'Programming Language :: Python :: 2.7',
38
36
  'Programming Language :: Python :: 3',
39
- 'Programming Language :: Python :: 3.5',
40
- 'Programming Language :: Python :: 3.6',
41
37
  'Programming Language :: Python :: 3.7',
42
38
  'Programming Language :: Python :: 3.8',
43
39
  'Programming Language :: Python :: 3.9',
40
+ 'Programming Language :: Python :: 3.10',
41
+ 'Programming Language :: Python :: 3.11',
44
42
  'Programming Language :: Python',
45
43
  'Topic :: Software Development :: Libraries :: Python Modules',
46
44
  ],
@@ -6,6 +6,8 @@ import time
6
6
  import threading
7
7
  import unittest
8
8
 
9
+ from unittest.mock import patch
10
+
9
11
  from logtail.compat import queue
10
12
  from logtail.flusher import RETRY_SCHEDULE
11
13
  from logtail.flusher import FlushWorker
@@ -17,11 +19,12 @@ class TestFlushWorker(unittest.TestCase):
17
19
  source_token = 'dummy_source_token'
18
20
  buffer_capacity = 5
19
21
  flush_interval = 2
22
+ check_interval = 0.01
20
23
 
21
24
  def _setup_worker(self, uploader=None):
22
25
  pipe = queue.Queue(maxsize=self.buffer_capacity)
23
26
  uploader = uploader or Uploader(self.source_token, self.host)
24
- fw = FlushWorker(uploader, pipe, self.buffer_capacity, self.flush_interval)
27
+ fw = FlushWorker(uploader, pipe, self.buffer_capacity, self.flush_interval, self.check_interval)
25
28
  return pipe, uploader, fw
26
29
 
27
30
  def test_is_thread(self):
@@ -50,7 +53,7 @@ class TestFlushWorker(unittest.TestCase):
50
53
 
51
54
  self.assertEqual(self.calls, 1)
52
55
 
53
- @mock.patch('logtail.flusher._calculate_time_remaining')
56
+ @patch('logtail.flusher._calculate_time_remaining')
54
57
  def test_flushes_after_interval(self, calculate_time_remaining):
55
58
  self.buffer_capacity = 10
56
59
  num_items = 2
@@ -82,8 +85,8 @@ class TestFlushWorker(unittest.TestCase):
82
85
  self.assertEqual(self.upload_calls, 1)
83
86
  self.assertEqual(self.timeout_calls, 2)
84
87
 
85
- @mock.patch('logtail.flusher._calculate_time_remaining')
86
- @mock.patch('logtail.flusher._initial_time_remaining')
88
+ @patch('logtail.flusher._calculate_time_remaining')
89
+ @patch('logtail.flusher._initial_time_remaining')
87
90
  def test_does_nothing_without_any_items(self, initial_time_remaining, calculate_time_remaining):
88
91
  calculate_time_remaining.side_effect = lambda a,b: 0.0
89
92
  initial_time_remaining.side_effect = lambda a: 0.0001
@@ -95,7 +98,7 @@ class TestFlushWorker(unittest.TestCase):
95
98
  fw.step()
96
99
  self.assertFalse(uploader.called)
97
100
 
98
- @mock.patch('logtail.flusher.time.sleep')
101
+ @patch('logtail.flusher.time.sleep')
99
102
  def test_retries_according_to_schedule(self, mock_sleep):
100
103
  first_frame = list(range(self.buffer_capacity))
101
104
 
@@ -6,20 +6,22 @@ import threading
6
6
  import unittest
7
7
  import logging
8
8
 
9
- from logtail import LogtailHandler, context
9
+ from unittest.mock import patch
10
10
 
11
+ from logtail import LogtailHandler, context
12
+ from logtail.handler import FlushWorker
11
13
 
12
14
  class TestLogtailHandler(unittest.TestCase):
13
15
  source_token = 'dummy_source_token'
14
16
  host = 'dummy_host'
15
17
 
16
- @mock.patch('logtail.handler.FlushWorker')
18
+ @patch('logtail.handler.FlushWorker')
17
19
  def test_handler_creates_uploader_from_args(self, MockWorker):
18
20
  handler = LogtailHandler(source_token=self.source_token, host=self.host)
19
21
  self.assertEqual(handler.uploader.source_token, self.source_token)
20
22
  self.assertEqual(handler.uploader.host, self.host)
21
23
 
22
- @mock.patch('logtail.handler.FlushWorker')
24
+ @patch('logtail.handler.FlushWorker')
23
25
  def test_handler_creates_pipe_from_args(self, MockWorker):
24
26
  buffer_capacity = 9
25
27
  flush_interval = 1
@@ -30,11 +32,12 @@ class TestLogtailHandler(unittest.TestCase):
30
32
  )
31
33
  self.assertTrue(handler.pipe.empty())
32
34
 
33
- @mock.patch('logtail.handler.FlushWorker')
35
+ @patch('logtail.handler.FlushWorker')
34
36
  def test_handler_creates_and_starts_worker_from_args_after_first_log(self, MockWorker):
35
37
  buffer_capacity = 9
36
38
  flush_interval = 9
37
- handler = LogtailHandler(source_token=self.source_token, buffer_capacity=buffer_capacity, flush_interval=flush_interval)
39
+ check_interval = 4
40
+ handler = LogtailHandler(source_token=self.source_token, buffer_capacity=buffer_capacity, flush_interval=flush_interval, check_interval=check_interval)
38
41
 
39
42
  self.assertFalse(MockWorker.called)
40
43
 
@@ -47,11 +50,12 @@ class TestLogtailHandler(unittest.TestCase):
47
50
  handler.uploader,
48
51
  handler.pipe,
49
52
  buffer_capacity,
50
- flush_interval
53
+ flush_interval,
54
+ check_interval,
51
55
  )
52
56
  self.assertEqual(handler.flush_thread.start.call_count, 1)
53
57
 
54
- @mock.patch('logtail.handler.FlushWorker')
58
+ @patch('logtail.handler.FlushWorker')
55
59
  def test_emit_starts_thread_if_not_alive(self, MockWorker):
56
60
  handler = LogtailHandler(source_token=self.source_token)
57
61
 
@@ -67,7 +71,7 @@ class TestLogtailHandler(unittest.TestCase):
67
71
 
68
72
  self.assertEqual(handler.flush_thread.start.call_count, 2)
69
73
 
70
- @mock.patch('logtail.handler.FlushWorker')
74
+ @patch('logtail.handler.FlushWorker')
71
75
  def test_emit_drops_records_if_configured(self, MockWorker):
72
76
  buffer_capacity = 1
73
77
  handler = LogtailHandler(
@@ -87,7 +91,7 @@ class TestLogtailHandler(unittest.TestCase):
87
91
  self.assertTrue(handler.pipe.empty())
88
92
  self.assertEqual(handler.dropcount, 1)
89
93
 
90
- @mock.patch('logtail.handler.FlushWorker')
94
+ @patch('logtail.handler.FlushWorker')
91
95
  def test_emit_does_not_drop_records_if_configured(self, MockWorker):
92
96
  buffer_capacity = 1
93
97
  handler = LogtailHandler(
@@ -118,7 +122,7 @@ class TestLogtailHandler(unittest.TestCase):
118
122
 
119
123
  self.assertEqual(handler.dropcount, 0)
120
124
 
121
- @mock.patch('logtail.handler.FlushWorker')
125
+ @patch('logtail.handler.FlushWorker')
122
126
  def test_error_suppression(self, MockWorker):
123
127
  buffer_capacity = 1
124
128
  handler = LogtailHandler(
@@ -139,7 +143,7 @@ class TestLogtailHandler(unittest.TestCase):
139
143
  handler.raise_exceptions = False
140
144
  logger.critical('hello')
141
145
 
142
- @mock.patch('logtail.handler.FlushWorker')
146
+ @patch('logtail.handler.FlushWorker')
143
147
  def test_can_send_unserializable_extra_data(self, MockWorker):
144
148
  buffer_capacity = 1
145
149
  handler = LogtailHandler(
@@ -158,7 +162,7 @@ class TestLogtailHandler(unittest.TestCase):
158
162
  self.assertRegex(log_entry['data']['unserializable'], r'^<tests\.test_handler\.UnserializableObject object at 0x[0-f]+>$')
159
163
  self.assertTrue(handler.pipe.empty())
160
164
 
161
- @mock.patch('logtail.handler.FlushWorker')
165
+ @patch('logtail.handler.FlushWorker')
162
166
  def test_can_send_unserializable_context(self, MockWorker):
163
167
  buffer_capacity = 1
164
168
  handler = LogtailHandler(
@@ -178,7 +182,7 @@ class TestLogtailHandler(unittest.TestCase):
178
182
  self.assertRegex(log_entry['context']['data']['unserializable'], r'^<tests\.test_handler\.UnserializableObject object at 0x[0-f]+>$')
179
183
  self.assertTrue(handler.pipe.empty())
180
184
 
181
- @mock.patch('logtail.handler.FlushWorker')
185
+ @patch('logtail.handler.FlushWorker')
182
186
  def test_can_send_circular_dependency_in_extra_data(self, MockWorker):
183
187
  buffer_capacity = 1
184
188
  handler = LogtailHandler(
@@ -199,8 +203,28 @@ class TestLogtailHandler(unittest.TestCase):
199
203
  self.assertEqual(log_entry['data']['egg']['chicken'], "<omitted circular reference>")
200
204
  self.assertTrue(handler.pipe.empty())
201
205
 
206
+ @patch('logtail.handler.FlushWorker')
207
+ def test_can_have_multiple_instance_of_same_string_in_extra_data(self, MockWorker):
208
+ buffer_capacity = 1
209
+ handler = LogtailHandler(
210
+ source_token=self.source_token,
211
+ buffer_capacity=buffer_capacity
212
+ )
213
+
214
+ logger = logging.getLogger(__name__)
215
+ logger.handlers = []
216
+ logger.addHandler(handler)
217
+ test_string = 'this is a test string'
218
+ logger.info('hello', extra={'test1': test_string, 'test2': test_string})
219
+
220
+ log_entry = handler.pipe.get()
221
+
222
+ self.assertEqual(log_entry['message'], 'hello')
223
+ self.assertEqual(log_entry['test1'], 'this is a test string')
224
+ self.assertEqual(log_entry['test2'], 'this is a test string')
225
+ self.assertTrue(handler.pipe.empty())
202
226
 
203
- @mock.patch('logtail.handler.FlushWorker')
227
+ @patch('logtail.handler.FlushWorker')
204
228
  def test_can_send_circular_dependency_in_context(self, MockWorker):
205
229
  buffer_capacity = 1
206
230
  handler = LogtailHandler(
@@ -4,6 +4,8 @@ import msgpack
4
4
  import mock
5
5
  import unittest
6
6
 
7
+ from unittest.mock import patch
8
+
7
9
  from logtail.uploader import Uploader
8
10
 
9
11
 
@@ -12,7 +14,7 @@ class TestUploader(unittest.TestCase):
12
14
  source_token = 'dummy_source_token'
13
15
  frame = [1, 2, 3]
14
16
 
15
- @mock.patch('logtail.uploader.requests.Session.post')
17
+ @patch('logtail.uploader.requests.Session.post')
16
18
  def test_call(self, post):
17
19
  def mock_post(endpoint, data=None, headers=None):
18
20
  # Check that the data is sent to ther correct endpoint