Make logger fully configurable (#1087)

This commit is contained in:
Tulir Asokan
2019-01-11 15:52:30 +01:00
committed by Lonami
parent eda4178333
commit f271316d7d
15 changed files with 141 additions and 137 deletions
+5 -7
View File
@@ -1,6 +1,5 @@
import datetime
import io
import logging
import os
import pathlib
@@ -14,9 +13,6 @@ except ImportError:
aiohttp = None
__log__ = logging.getLogger(__name__)
class DownloadMethods(UserMethods):
# region Public methods
@@ -239,7 +235,8 @@ class DownloadMethods(UserMethods):
# The used sender will also change if ``FileMigrateError`` occurs
sender = self._sender
__log__.info('Downloading file in chunks of %d bytes', part_size)
self._log[__name__].info('Downloading file in chunks of %d bytes',
part_size)
try:
offset = 0
while True:
@@ -251,7 +248,7 @@ class DownloadMethods(UserMethods):
# TODO Implement
raise NotImplementedError
except errors.FileMigrateError as e:
__log__.info('File lives in another DC')
self._log[__name__].info('File lives in another DC')
sender = await self._borrow_exported_sender(e.new_dc)
exported = True
continue
@@ -264,7 +261,8 @@ class DownloadMethods(UserMethods):
else:
return getattr(result, 'type', '')
__log__.debug('Saving %d more bytes', len(result.bytes))
self._log[__name__].debug('Saving %d more bytes',
len(result.bytes))
f.write(result.bytes)
if progress_callback:
progress_callback(f.tell(), file_size)
-3
View File
@@ -1,6 +1,5 @@
import asyncio
import itertools
import logging
import time
from async_generator import async_generator, yield_
@@ -11,8 +10,6 @@ from .buttons import ButtonMethods
from .. import helpers, utils, errors
from ..tl import types, functions
__log__ = logging.getLogger(__name__)
class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods):
+35 -7
View File
@@ -6,7 +6,7 @@ import sys
import time
from datetime import datetime
from .. import version
from .. import version, __name__ as __base_name__
from ..crypto import rsa
from ..extensions import markdown
from ..network import MTProtoSender, ConnectionTcpFull
@@ -19,7 +19,8 @@ DEFAULT_IPV4_IP = '149.154.167.51'
DEFAULT_IPV6_IP = '[2001:67c:4e8:f002::a]'
DEFAULT_PORT = 443
__log__ = logging.getLogger(__name__)
__default_log__ = logging.getLogger(__base_name__)
__default_log__.addHandler(logging.NullHandler())
class TelegramBaseClient(abc.ABC):
@@ -133,6 +134,15 @@ class TelegramBaseClient(abc.ABC):
system_lang_code (`str`, optional):
"System lang code" to be sent when creating the initial connection.
Defaults to `lang_code`.
loop (`asyncio.AbstractEventLoop`, optional):
Asyncio event loop to use. Defaults to `asyncio.get_event_loop()`
base_logger (`str` | `logging.Logger`, optional):
Base logger name or instance to use.
If a `str` is given, it'll be passed to `logging.getLogger()`. If a
`logging.Logger` is given, it'll be used directly. If something
else or nothing is given, the default logger will be used.
"""
# Current TelegramClient version
@@ -161,7 +171,8 @@ class TelegramBaseClient(abc.ABC):
app_version=None,
lang_code='en',
system_lang_code='en',
loop=None):
loop=None,
base_logger=None):
if not api_id or not api_hash:
raise ValueError(
"Your API ID or Hash cannot be empty or None. "
@@ -170,6 +181,19 @@ class TelegramBaseClient(abc.ABC):
self._use_ipv6 = use_ipv6
self._loop = loop or asyncio.get_event_loop()
if isinstance(base_logger, str):
base_logger = logging.getLogger(base_logger)
elif not isinstance(base_logger, logging.Logger):
base_logger = __default_log__
class _Loggers(dict):
def __missing__(self, key):
if key.startswith("telethon."):
key = key[len("telethon."):]
return base_logger.getChild(key)
self._log = _Loggers()
# Determine what session object we have
if isinstance(session, str) or session is None:
try:
@@ -240,6 +264,7 @@ class TelegramBaseClient(abc.ABC):
self._connection = connection
self._sender = MTProtoSender(
self.session.auth_key, self._loop,
loggers=self._log,
retries=self._connection_retries,
delay=self._retry_delay,
auto_reconnect=self._auto_reconnect,
@@ -317,7 +342,8 @@ class TelegramBaseClient(abc.ABC):
"""
await self._sender.connect(self._connection(
self.session.server_address, self.session.port,
loop=self._loop, proxy=self._proxy
loop=self._loop, loggers=self._log,
proxy=self._proxy
))
self.session.auth_key = self._sender.auth_key
self.session.save()
@@ -387,7 +413,7 @@ class TelegramBaseClient(abc.ABC):
"""
Permanently switches the current connection to the new data center.
"""
__log__.info('Reconnecting to new data center %s', new_dc)
self._log[__name__].info('Reconnecting to new data center %s', new_dc)
dc = await self._get_dc(new_dc)
self.session.set_dc(dc.id, dc.ip_address, dc.port)
@@ -443,7 +469,8 @@ class TelegramBaseClient(abc.ABC):
sender = MTProtoSender(None, self._loop)
await sender.connect(self._connection(
dc.ip_address, dc.port, loop=self._loop, proxy=self._proxy))
__log__.info('Exporting authorization for data center %s', dc)
self._log[__name__].info('Exporting authorization for data center %s',
dc)
auth = await self(functions.auth.ExportAuthorizationRequest(dc_id))
req = self._init_with(functions.auth.ImportAuthorizationRequest(
id=auth.id, bytes=auth.bytes
@@ -486,7 +513,8 @@ class TelegramBaseClient(abc.ABC):
n -= 1
self._borrowed_senders[dc_id] = (n, sender)
if not n:
__log__.info('Disconnecting borrowed sender for DC %d', dc_id)
self._log[__name__].info(
'Disconnecting borrowed sender for DC %d', dc_id)
sender.disconnect()
async def _get_cdn_client(self, cdn_redirect):
+10 -11
View File
@@ -1,7 +1,5 @@
import asyncio
import inspect
import itertools
import logging
import random
import time
@@ -9,8 +7,6 @@ from .users import UserMethods
from .. import events, utils, errors
from ..tl import types, functions
__log__ = logging.getLogger(__name__)
class UpdateMethods(UserMethods):
@@ -281,28 +277,31 @@ class UpdateMethods(UserMethods):
await callback(event)
except errors.AlreadyInConversationError:
name = getattr(callback, '__name__', repr(callback))
__log__.debug('Event handler "%s" already has an open '
'conversation, ignoring new one', name)
self._log[__name__].debug(
'Event handler "%s" already has an open conversation, '
'ignoring new one', name)
except events.StopPropagation:
name = getattr(callback, '__name__', repr(callback))
__log__.debug(
self._log[__name__].debug(
'Event handler "%s" stopped chain of propagation '
'for event %s.', name, type(event).__name__
)
break
except Exception:
name = getattr(callback, '__name__', repr(callback))
__log__.exception('Unhandled exception on %s', name)
self._log[__name__].exception('Unhandled exception on %s',
name)
async def _handle_auto_reconnect(self):
# Upon reconnection, we want to send getState
# for Telegram to keep sending us updates.
try:
__log__.info('Asking for the current state after reconnect...')
self._log[__name__].info(
'Asking for the current state after reconnect...')
state = await self(functions.updates.GetStateRequest())
__log__.info('Got new state! %s', state)
self._log[__name__].info('Got new state! %s', state)
except errors.RPCError as e:
__log__.info('Failed to get current state: %r', e)
self._log[__name__].info('Failed to get current state: %r', e)
# endregion
+7 -10
View File
@@ -1,6 +1,5 @@
import hashlib
import io
import logging
import os
import pathlib
import re
@@ -12,8 +11,6 @@ from .users import UserMethods
from .. import utils, helpers
from ..tl import types, functions, custom
__log__ = logging.getLogger(__name__)
class _CacheType:
"""Like functools.partial but pretends to be the wrapped class."""
@@ -196,8 +193,8 @@ class UploadMethods(ButtonMethods, MessageParseMethods, UserMethods):
return msg
async def _send_album(self, entity, files, caption='',
progress_callback=None, reply_to=None,
parse_mode=(), silent=None):
progress_callback=None, reply_to=None,
parse_mode=(), silent=None):
"""Specialized version of .send_file for albums"""
# We don't care if the user wants to avoid cache, we will use it
# anyway. Why? The cached version will be exactly the same thing
@@ -350,8 +347,8 @@ class UploadMethods(ButtonMethods, MessageParseMethods, UserMethods):
return cached
part_count = (file_size + part_size - 1) // part_size
__log__.info('Uploading file of %d bytes in %d chunks of %d',
file_size, part_count, part_size)
self._log[__name__].info('Uploading file of %d bytes in %d chunks of %d',
file_size, part_count, part_size)
with open(file, 'rb') if isinstance(file, str) else BytesIO(file)\
as stream:
@@ -370,8 +367,8 @@ class UploadMethods(ButtonMethods, MessageParseMethods, UserMethods):
result = await self(request)
if result:
__log__.debug('Uploaded %d/%d', part_index + 1,
part_count)
self._log[__name__].debug('Uploaded %d/%d',
part_index + 1, part_count)
if progress_callback:
progress_callback(stream.tell(), file_size)
else:
@@ -464,7 +461,7 @@ class UploadMethods(ButtonMethods, MessageParseMethods, UserMethods):
return file_handle, media
async def _cache_media(self, msg, file, file_handle,
force_document=False):
force_document=False):
if file and msg and isinstance(file_handle,
custom.InputSizedFile):
# There was a response message and we didn't use cached
+9 -8
View File
@@ -1,6 +1,5 @@
import asyncio
import itertools
import logging
import time
from .telegrambaseclient import TelegramBaseClient
@@ -8,7 +7,6 @@ from .. import errors, utils
from ..errors import MultiError, RPCError
from ..tl import TLObject, TLRequest, types, functions
__log__ = logging.getLogger(__name__)
_NOT_A_REQUEST = TypeError('You can only invoke requests, not types!')
@@ -27,7 +25,8 @@ class UserMethods(TelegramBaseClient):
if diff <= 3: # Flood waits below 3 seconds are "ignored"
self._flood_waited_requests.pop(r.CONSTRUCTOR_ID, None)
elif diff <= self.flood_sleep_threshold:
__log__.info('Sleeping early for %ds on flood wait', diff)
self._log[__name__].info(
'Sleeping early for %ds on flood wait', diff)
await asyncio.sleep(diff, loop=self._loop)
self._flood_waited_requests.pop(r.CONSTRUCTOR_ID, None)
else:
@@ -61,8 +60,9 @@ class UserMethods(TelegramBaseClient):
self.session.process_entities(result)
return result
except (errors.ServerError, errors.RpcCallFailError) as e:
__log__.warning('Telegram is having internal issues %s: %s',
e.__class__.__name__, e)
self._log[__name__].warning(
'Telegram is having internal issues %s: %s',
e.__class__.__name__, e)
except (errors.FloodWaitError, errors.FloodTestPhoneWaitError) as e:
if utils.is_list_like(request):
request = request[request_index]
@@ -71,15 +71,16 @@ class UserMethods(TelegramBaseClient):
[request.CONSTRUCTOR_ID] = time.time() + e.seconds
if e.seconds <= self.flood_sleep_threshold:
__log__.info('Sleeping for %ds on flood wait', e.seconds)
self._log[__name__].info('Sleeping for %ds on flood wait',
e.seconds)
await asyncio.sleep(e.seconds, loop=self._loop)
else:
raise
except (errors.PhoneMigrateError, errors.NetworkMigrateError,
errors.UserMigrateError) as e:
__log__.info('Phone migrated to %d', e.new_dc)
self._log[__name__].info('Phone migrated to %d', e.new_dc)
should_raise = isinstance(e, (
errors.PhoneMigrateError, errors.NetworkMigrateError
errors.PhoneMigrateError, errors.NetworkMigrateError
))
if should_raise and await self.is_user_authorized():
raise