Skip to content

Commit

Permalink
improved logging
Browse files Browse the repository at this point in the history
  • Loading branch information
pinhead84 committed Jul 13, 2023
1 parent bc898f9 commit efedb3b
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 114 deletions.
62 changes: 29 additions & 33 deletions src/config-test.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
# limitations under the License.
#

import traceback
from datetime import datetime

from imapclient import IMAPClient
Expand All @@ -23,24 +22,34 @@
from lib import get_envelope_subject, \
get_envelope_sender_first, \
get_envelope_from_first, \
get_envelope_date
get_envelope_date, \
create_logger
from lib.config import get_config, get_imap_folder, create_imap_connector
from lib.connector import ImapConnector

if __name__ == '__main__':
config = get_config()
root_logger = create_logger()

config = get_config(logger=root_logger)
if not config:
exit(1)

sections = config.sections()
if not sections:
print('No IMAP servers configured. Nothing to do.')
root_logger.warning('No IMAP servers configured. Nothing to do.')
exit(0)

root_logger.info('Testing %s configured IMAP connections...', len(sections))
for section in sections:
connector: ImapConnector = create_imap_connector(config=config, section=section)
logger = create_logger(section)

try:
connector: ImapConnector = create_imap_connector(config=config, section=section)
except Exception as ex:
logger.exception('Invalid configuration. %s', str(ex))
continue

print('[%s] Testing connection...' % section)
logger.info('Testing connection...')
client: IMAPClient | None = None
try:
folder = get_imap_folder(config=config, section=section)
Expand All @@ -51,63 +60,50 @@
select_folder_readonly=True,
)
except Exception as ex:
print('[%s] ERROR: Connection failed! %s\n%s' % (
section,
str(ex),
'\n'.join(traceback.format_exception(ex)),
))
logger.exception('Connection failed. %s', str(ex))
continue

print('[%s] Connection successful.' % section)
logger.info('Connection successful.')

try:
capabilities = []
for cap in client.capabilities():
capabilities.append(cap.decode('utf-8'))

print('[%s] Capabilities: %s' % (section, ', '.join(sorted(capabilities)),))
logger.info('Capabilities: %s', ', '.join(sorted(capabilities)))
except Exception as ex:
print('[%s] ERROR: Can\'t load server capabilities! %s\n%s' % (
section,
str(ex),
'\n'.join(traceback.format_exception(ex)),
))
logger.exception('Can\'t load server capabilities. %s', str(ex))
continue

print('[%s] Fetch latest message from "%s".' % (section, folder,))
logger.info('Fetch latest message from "%s".', folder)
message_numbers = client.search()
if len(message_numbers) < 1:
print('[%s] No messages found in "%s".' % (section, folder,))
logger.info('No messages found in "%s".', folder)
continue

last_message_number = message_numbers[len(message_numbers) - 1]
# print('[%s] Fetching message number #%s...' % (section, last_message_number,))

result = client.fetch([last_message_number], ['ENVELOPE'])
if last_message_number not in result:
print('[%s] ERROR: No envelope data found for message nr %s in "%s".' % (
section,
last_message_number,
folder,
))
logger.error('No envelope data found for message nr %s in "%s".', last_message_number, folder)
continue

print('[%s] Latest message in "%s":' % (section, folder,))
message_info = ['Latest message in "%s":' % folder]

last_message_envelope: Envelope = result[last_message_number][b'ENVELOPE']
# pprint(last_message_envelope)

msg_date: datetime | None = get_envelope_date(last_message_envelope)
print('-> Date : %s' % msg_date)
message_info.append('-> Date : %s' % msg_date)

subject: str | None = get_envelope_subject(last_message_envelope)
print('-> Subject : %s' % subject)
message_info.append('-> Subject : %s' % subject)

from_address: Address | None = get_envelope_from_first(last_message_envelope)
print('-> From : %s' % str(from_address))
message_info.append('-> From : %s' % str(from_address))

sender_address: Address | None = get_envelope_sender_first(last_message_envelope)
print('-> Sender : %s' % str(sender_address))
message_info.append('-> Sender : %s' % str(sender_address))

logger.info('\n'.join(message_info))

finally:
# noinspection PyBroadException
Expand Down
30 changes: 29 additions & 1 deletion src/lib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
# limitations under the License.
#

import logging
import sys
from datetime import datetime
from enum import Enum

Expand All @@ -32,6 +34,32 @@ class EncryptionCertificateCheck(Enum):
REQUIRED = 'required'


__LOGGERS: dict[str, logging.Logger] = {}


def create_logger(name: str = 'app', level: int = logging.INFO) -> logging.Logger:
if name in __LOGGERS:
return __LOGGERS[name]

logger: logging.Logger = logging.getLogger(name)
logger.setLevel(level)

# noinspection SpellCheckingInspection
logger_format = '[%(levelname)s] %(asctime)s | %(message)s' if name == 'app' \
else '[%(levelname)s:%(name)s] %(asctime)s | %(message)s'
formatter: logging.Formatter = logging.Formatter(
fmt=logger_format,
datefmt='%Y-%m-%d %H:%M:%S'
)

handler: logging.StreamHandler = logging.StreamHandler(stream=sys.stdout)
handler.setFormatter(formatter)
logger.addHandler(handler)

__LOGGERS[name] = logger
return logger


def get_address_mail(address: Address, charset='utf-8') -> str | None:
"""
Extracts mail address from an address.
Expand Down Expand Up @@ -59,7 +87,7 @@ def get_address_name(address: Address, charset='utf-8') -> str | None:
:return: mail name or None, if invalid
"""

if address.name:
if not address.name:
return None

return address.name.decode(charset).strip()
Expand Down
35 changes: 17 additions & 18 deletions src/lib/callback.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
#

import subprocess
import traceback
from datetime import datetime
from os import getcwd
from threading import Thread
Expand All @@ -30,7 +29,8 @@
get_envelope_sender_first, \
get_envelope_to_first, \
get_address_name, \
get_address_mail
get_address_mail, \
create_logger


class CallbackHandler:
Expand Down Expand Up @@ -115,6 +115,14 @@ def __init__(
self.__command = command
self.__environment = {**environment}
self.__thread = Thread(target=self.__run)
self.__logger = create_logger(self.__name)

# make sure, that environment dict does not contain None values
# as it might lead to errors on execution
for key, value in self.__environment.items():
if value is None:
self.__logger.warning('Environment variable "%s" has None value.', key)
self.__environment[key] = ''

def start(self):
"""
Expand All @@ -136,31 +144,22 @@ def __run(self):
"""

try:
print('[%s] Running "%s" from working directory "%s"...' % (
self.__name,
self.__command,
getcwd(),
))
self.__logger.info('Running "%s" from working directory "%s"...', self.__command, getcwd())

result: subprocess.CompletedProcess = subprocess.run(
self.__command,
shell=True,
env=self.__environment,
cwd=getcwd(),
text=True
cwd=getcwd()
)

if result.returncode != 0:
print('[%s] ERROR: Callback script "%s" returned non-zero exit code (%s)!' % (
self.__name,
self.__logger.warning(
'Callback script "%s" returned non-zero exit code (%s)!',
self.__command,
result.returncode,
))
result.returncode
)
return

except Exception as ex:
print('[%s] ERROR: Callback script error! %s\n%s' % (
self.__name,
str(ex),
'\n'.join(traceback.format_exception(ex)),
))
self.__logger.exception('Unexpected callback error. %s', str(ex))
20 changes: 13 additions & 7 deletions src/lib/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# limitations under the License.
#

import logging
import os
import sys
from configparser import ConfigParser
Expand All @@ -24,14 +25,14 @@
from .idle import ImapIdleHandler


def get_config() -> ConfigParser | None:
def get_config(logger: logging.Logger) -> ConfigParser | None:
if len(sys.argv) < 2:
print('ERROR: Please provide a config file as first argument!')
logger.error('Please provide a config file as first argument!')
return None

config_path = sys.argv[1]
if not os.path.isfile(config_path):
print('ERROR: Can\'t find config file at "%s"!' % config_path)
logger.error('Can\'t find config file at "%s"!' % config_path)
return None

config = ConfigParser()
Expand Down Expand Up @@ -74,15 +75,20 @@ def create_imap_connector(
section: str,
use_uid=False
) -> ImapConnector:
try:
port: int = int(config.get(
section, 'port',
fallback='143',
).strip())
except ValueError:
raise Exception('Can\'t read port number "%s".' % config.get(section, 'port'))

return ImapConnector(
host=config.get(
section, 'host',
fallback='localhost',
),
port=int(config.get(
section, 'port',
fallback='143',
).strip()),
port=port,
username=config.get(
section, 'username',
fallback=None,
Expand Down
Loading

0 comments on commit efedb3b

Please sign in to comment.