Skip to content

Commit da7cb86

Browse files
truetechmiguelgrinberg
authored andcommitted
Logging improvements for write-only connections (#197)
* Make logger a property of BaseManager, configurable separately from server for write-only situations. * Shorten line lengths for recent changes.
1 parent f2d28ad commit da7cb86

File tree

9 files changed

+64
-23
lines changed

9 files changed

+64
-23
lines changed

socketio/asyncio_manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ async def trigger_callback(self, sid, namespace, id, data):
4444
callback = self.callbacks[sid][namespace][id]
4545
except KeyError:
4646
# if we get an unknown callback we just ignore it
47-
self.server.logger.warning('Unknown callback received, ignoring.')
47+
self._get_logger().warning('Unknown callback received, ignoring.')
4848
else:
4949
del self.callbacks[sid][namespace][id]
5050
if callback is not None:

socketio/asyncio_pubsub_manager.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,18 @@ class AsyncPubSubManager(AsyncManager):
2424
"""
2525
name = 'asyncpubsub'
2626

27-
def __init__(self, channel='socketio', write_only=False):
27+
def __init__(self, channel='socketio', write_only=False, logger=None):
2828
super().__init__()
2929
self.channel = channel
3030
self.write_only = write_only
3131
self.host_id = uuid.uuid4().hex
32+
self.logger = logger
3233

3334
def initialize(self):
3435
super().initialize()
3536
if not self.write_only:
3637
self.thread = self.server.start_background_task(self._thread)
37-
self.server.logger.info(self.name + ' backend initialized.')
38+
self._get_logger().info(self.name + ' backend initialized.')
3839

3940
async def emit(self, event, data, namespace=None, room=None, skip_sid=None,
4041
callback=None, **kwargs):

socketio/asyncio_redis_manager.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import asyncio
2-
import logging
32
import pickle
43
from urllib.parse import urlparse
54

@@ -10,8 +9,6 @@
109

1110
from .asyncio_pubsub_manager import AsyncPubSubManager
1211

13-
logger = logging.getLogger('socketio')
14-
1512

1613
def _parse_redis_url(url):
1714
p = urlparse(url)
@@ -52,15 +49,15 @@ class AsyncRedisManager(AsyncPubSubManager): # pragma: no cover
5249
name = 'aioredis'
5350

5451
def __init__(self, url='redis://localhost:6379/0', channel='socketio',
55-
write_only=False):
52+
write_only=False, logger=None):
5653
if aioredis is None:
5754
raise RuntimeError('Redis package is not installed '
5855
'(Run "pip install aioredis" in your '
5956
'virtualenv).')
6057
self.host, self.port, self.password, self.db = _parse_redis_url(url)
6158
self.pub = None
6259
self.sub = None
63-
super().__init__(channel=channel, write_only=write_only)
60+
super().__init__(channel=channel, write_only=write_only, logger=logger)
6461

6562
async def _publish(self, data):
6663
retry = True
@@ -74,11 +71,13 @@ async def _publish(self, data):
7471
pickle.dumps(data))
7572
except (aioredis.RedisError, OSError):
7673
if retry:
77-
logger.error('Cannot publish to redis... retrying')
74+
self._get_logger().error('Cannot publish to redis... '
75+
'retrying')
7876
self.pub = None
7977
retry = False
8078
else:
81-
logger.error('Cannot publish to redis... giving up')
79+
self._get_logger().error('Cannot publish to redis... '
80+
'giving up')
8281
break
8382

8483
async def _listen(self):
@@ -92,8 +91,9 @@ async def _listen(self):
9291
self.ch = (await self.sub.subscribe(self.channel))[0]
9392
return await self.ch.get()
9493
except (aioredis.RedisError, OSError):
95-
logger.error('Cannot receive from redis... '
96-
'retrying in {} secs'.format(retry_sleep))
94+
self._get_logger().error('Cannot receive from redis... '
95+
'retrying in '
96+
'{} secs'.format(retry_sleep))
9797
self.sub = None
9898
await asyncio.sleep(retry_sleep)
9999
retry_sleep *= 2

socketio/base_manager.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import itertools
2+
import logging
23

34
import six
45

6+
default_logger = logging.getLogger('socketio')
7+
58

69
class BaseManager(object):
710
"""Manage client connections.
@@ -13,6 +16,7 @@ class BaseManager(object):
1316
subclasses.
1417
"""
1518
def __init__(self):
19+
self.logger = None
1620
self.server = None
1721
self.rooms = {}
1822
self.callbacks = {}
@@ -141,7 +145,7 @@ def trigger_callback(self, sid, namespace, id, data):
141145
callback = self.callbacks[sid][namespace][id]
142146
except KeyError:
143147
# if we get an unknown callback we just ignore it
144-
self.server.logger.warning('Unknown callback received, ignoring.')
148+
self._get_logger().warning('Unknown callback received, ignoring.')
145149
else:
146150
del self.callbacks[sid][namespace][id]
147151
if callback is not None:
@@ -157,3 +161,16 @@ def _generate_ack_id(self, sid, namespace, callback):
157161
id = six.next(self.callbacks[sid][namespace][0])
158162
self.callbacks[sid][namespace][id] = callback
159163
return id
164+
165+
def _get_logger(self):
166+
"""Get the appropriate logger
167+
168+
Prevents uninitialized servers in write-only mode from failing.
169+
"""
170+
171+
if self.logger:
172+
return self.logger
173+
elif self.server:
174+
return self.server.logger
175+
else:
176+
return default_logger

socketio/kombu_manager.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,14 @@ class KombuManager(PubSubManager): # pragma: no cover
3838
name = 'kombu'
3939

4040
def __init__(self, url='amqp://guest:guest@localhost:5672//',
41-
channel='socketio', write_only=False):
41+
channel='socketio', write_only=False, logger=None):
4242
if kombu is None:
4343
raise RuntimeError('Kombu package is not installed '
4444
'(Run "pip install kombu" in your '
4545
'virtualenv).')
46-
super(KombuManager, self).__init__(channel=channel)
46+
super(KombuManager, self).__init__(channel=channel,
47+
write_only=write_only,
48+
logger=logger)
4749
self.url = url
4850
self.producer = self._producer()
4951

@@ -78,7 +80,7 @@ def _producer(self):
7880
return self._connection().Producer(exchange=self._exchange())
7981

8082
def __error_callback(self, exception, interval):
81-
self.server.logger.exception('Sleeping {}s'.format(interval))
83+
self._get_logger().exception('Sleeping {}s'.format(interval))
8284

8385
def _publish(self, data):
8486
connection = self._connection()
@@ -99,5 +101,5 @@ def _listen(self):
99101
message.ack()
100102
yield message.payload
101103
except connection.connection_errors:
102-
self.server.logger.exception("Connection error "
104+
self._get_logger().exception("Connection error "
103105
"while reading from queue")

socketio/pubsub_manager.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,18 @@ class PubSubManager(BaseManager):
2424
"""
2525
name = 'pubsub'
2626

27-
def __init__(self, channel='socketio', write_only=False):
27+
def __init__(self, channel='socketio', write_only=False, logger=None):
2828
super(PubSubManager, self).__init__()
2929
self.channel = channel
3030
self.write_only = write_only
3131
self.host_id = uuid.uuid4().hex
32+
self.logger = logger
3233

3334
def initialize(self):
3435
super(PubSubManager, self).initialize()
3536
if not self.write_only:
3637
self.thread = self.server.start_background_task(self._thread)
37-
self.server.logger.info(self.name + ' backend initialized.')
38+
self._get_logger().info(self.name + ' backend initialized.')
3839

3940
def emit(self, event, data, namespace=None, room=None, skip_sid=None,
4041
callback=None, **kwargs):

socketio/redis_manager.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,16 @@ class RedisManager(PubSubManager): # pragma: no cover
3737
name = 'redis'
3838

3939
def __init__(self, url='redis://localhost:6379/0', channel='socketio',
40-
write_only=False):
40+
write_only=False, logger=None):
4141
if redis is None:
4242
raise RuntimeError('Redis package is not installed '
4343
'(Run "pip install redis" in your '
4444
'virtualenv).')
4545
self.redis_url = url
4646
self._redis_connect()
4747
super(RedisManager, self).__init__(channel=channel,
48-
write_only=write_only)
48+
write_only=write_only,
49+
logger=logger)
4950

5051
def initialize(self):
5152
super(RedisManager, self).initialize()

socketio/zmq_manager.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ class ZmqManager(PubSubManager): # pragma: no cover
5050

5151
def __init__(self, url='zmq+tcp://localhost:5555+5556',
5252
channel='socketio',
53-
write_only=False):
53+
write_only=False,
54+
logger=None):
5455
if zmq is None:
5556
raise RuntimeError('zmq package is not installed '
5657
'(Run "pip install pyzmq" in your '
@@ -76,7 +77,8 @@ def __init__(self, url='zmq+tcp://localhost:5555+5556',
7677
self.sub = sub
7778
self.channel = channel
7879
super(ZmqManager, self).__init__(channel=channel,
79-
write_only=write_only)
80+
write_only=write_only,
81+
logger=logger)
8082

8183
def _publish(self, data):
8284
pickled_data = pickle.dumps(

tests/test_pubsub_manager.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import functools
22
import unittest
3+
import logging
34

45
import six
56
if six.PY3:
@@ -39,6 +40,22 @@ def test_write_only_init(self):
3940
self.assertEqual(len(pm.host_id), 32)
4041
self.assertEqual(pm.server.start_background_task.call_count, 0)
4142

43+
def test_write_only_default_logger(self):
44+
pm = pubsub_manager.PubSubManager(write_only=True)
45+
pm.initialize()
46+
self.assertEqual(pm.channel, 'socketio')
47+
self.assertEqual(len(pm.host_id), 32)
48+
self.assertEqual(pm._get_logger(), logging.getLogger('socketio'))
49+
50+
def test_write_only_with_provided_logger(self):
51+
test_logger = logging.getLogger('new_logger')
52+
pm = pubsub_manager.PubSubManager(write_only=True,
53+
logger=test_logger)
54+
pm.initialize()
55+
self.assertEqual(pm.channel, 'socketio')
56+
self.assertEqual(len(pm.host_id), 32)
57+
self.assertEqual(pm._get_logger(), test_logger)
58+
4259
def test_emit(self):
4360
self.pm.emit('foo', 'bar')
4461
self.pm._publish.assert_called_once_with(

0 commit comments

Comments
 (0)