mirror of
https://github.com/craigerl/aprsd.git
synced 2026-01-04 23:38:44 -05:00
Reworked listen command log output
This patch makes portions of the log format optional, so the main output of the listen command now is the timestamp and the packet.
This commit is contained in:
parent
0ef131678f
commit
61126289df
@ -1,3 +1,4 @@
|
||||
import cProfile
|
||||
import logging
|
||||
import typing as t
|
||||
from functools import update_wrapper
|
||||
@ -46,6 +47,15 @@ common_options = [
|
||||
default=False,
|
||||
help="Don't log to stdout",
|
||||
),
|
||||
click.option(
|
||||
'--profile',
|
||||
'profile_output',
|
||||
default=None,
|
||||
required=False,
|
||||
metavar='FILENAME',
|
||||
help='Enable profiling and save results to FILENAME. '
|
||||
'If FILENAME is not provided, defaults to aprsd_profile.prof',
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@ -129,10 +139,30 @@ def process_standard_options(f: F) -> F:
|
||||
LOG = logging.getLogger('APRSD') # noqa: N806
|
||||
LOG.error("No config file found!! run 'aprsd sample-config'")
|
||||
|
||||
profile_output = kwargs.pop('profile_output', None)
|
||||
del kwargs['loglevel']
|
||||
del kwargs['config_file']
|
||||
del kwargs['quiet']
|
||||
return f(*args, **kwargs)
|
||||
|
||||
# Enable profiling if requested
|
||||
if profile_output is not None:
|
||||
# If profile_output is empty string, use default filename
|
||||
if not profile_output or profile_output == '':
|
||||
profile_output = 'aprsd_profile.prof'
|
||||
profiler = cProfile.Profile()
|
||||
profiler.enable()
|
||||
try:
|
||||
result = f(*args, **kwargs)
|
||||
finally:
|
||||
profiler.disable()
|
||||
profiler.dump_stats(profile_output)
|
||||
LOG = logging.getLogger('APRSD') # noqa: N806
|
||||
LOG.info(f'Profile data saved to {profile_output}')
|
||||
LOG.info(f'Analyze with: python -m pstats {profile_output}')
|
||||
LOG.info(f'Or visualize with: snakeviz {profile_output}')
|
||||
return result
|
||||
else:
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return update_wrapper(t.cast(F, new_func), f)
|
||||
|
||||
@ -151,9 +181,29 @@ def process_standard_options_no_config(f: F) -> F:
|
||||
ctx.obj['quiet'],
|
||||
)
|
||||
|
||||
profile_output = kwargs.pop('profile_output', None)
|
||||
del kwargs['loglevel']
|
||||
del kwargs['config_file']
|
||||
del kwargs['quiet']
|
||||
return f(*args, **kwargs)
|
||||
|
||||
# Enable profiling if requested
|
||||
if profile_output is not None:
|
||||
# If profile_output is empty string, use default filename
|
||||
if not profile_output or profile_output == '':
|
||||
profile_output = 'aprsd_profile.prof'
|
||||
profiler = cProfile.Profile()
|
||||
profiler.enable()
|
||||
try:
|
||||
result = f(*args, **kwargs)
|
||||
finally:
|
||||
profiler.disable()
|
||||
profiler.dump_stats(profile_output)
|
||||
LOG = logging.getLogger('APRSD') # noqa: N806
|
||||
LOG.info(f'Profile data saved to {profile_output}')
|
||||
LOG.info(f'Analyze with: python -m pstats {profile_output}')
|
||||
LOG.info(f'Or visualize with: snakeviz {profile_output}')
|
||||
return result
|
||||
else:
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return update_wrapper(t.cast(F, new_func), f)
|
||||
|
||||
@ -18,6 +18,7 @@ from rich.console import Console
|
||||
import aprsd
|
||||
from aprsd import cli_helper, packets, plugin, threads, utils
|
||||
from aprsd.client.client import APRSDClient
|
||||
from aprsd.log import log
|
||||
from aprsd.main import cli
|
||||
from aprsd.packets import collector as packet_collector
|
||||
from aprsd.packets import core, seen_list
|
||||
@ -116,6 +117,101 @@ class ListenStatsThread(APRSDThread):
|
||||
return True
|
||||
|
||||
|
||||
def process_listen_options(f):
|
||||
"""Custom decorator for listen command that modifies log format before setup_logging."""
|
||||
import cProfile
|
||||
import typing as t
|
||||
from functools import update_wrapper
|
||||
|
||||
def new_func(*args, **kwargs):
|
||||
ctx = args[0]
|
||||
ctx.ensure_object(dict)
|
||||
config_file_found = True
|
||||
|
||||
# Extract show_thread and show_level
|
||||
show_thread = kwargs.get('show_thread', False)
|
||||
show_level = kwargs.get('show_level', False)
|
||||
|
||||
if kwargs['config_file']:
|
||||
default_config_files = [kwargs['config_file']]
|
||||
else:
|
||||
default_config_files = None
|
||||
|
||||
try:
|
||||
CONF(
|
||||
[],
|
||||
project='aprsd',
|
||||
version=aprsd.__version__,
|
||||
default_config_files=default_config_files,
|
||||
)
|
||||
except cfg.ConfigFilesNotFoundError:
|
||||
config_file_found = False
|
||||
|
||||
# NOW modify config AFTER CONF is initialized but BEFORE setup_logging
|
||||
# Build custom log format for listen command
|
||||
# By default, disable thread, level, and location for cleaner output
|
||||
from aprsd.conf import log as conf_log
|
||||
|
||||
parts = []
|
||||
# Timestamp is always included
|
||||
parts.append(conf_log.DEFAULT_LOG_FORMAT_TIMESTAMP)
|
||||
|
||||
if show_thread:
|
||||
parts.append(conf_log.DEFAULT_LOG_FORMAT_THREAD)
|
||||
|
||||
if show_level:
|
||||
parts.append(conf_log.DEFAULT_LOG_FORMAT_LEVEL)
|
||||
|
||||
# Message is always included
|
||||
parts.append(conf_log.DEFAULT_LOG_FORMAT_MESSAGE)
|
||||
|
||||
# Location is never included for listen command
|
||||
|
||||
# Set the custom log format
|
||||
CONF.logging.logformat = ' | '.join(parts)
|
||||
|
||||
ctx.obj['loglevel'] = kwargs['loglevel']
|
||||
ctx.obj['quiet'] = kwargs['quiet']
|
||||
|
||||
# Now call setup_logging with our modified config
|
||||
log.setup_logging(ctx.obj['loglevel'], ctx.obj['quiet'])
|
||||
|
||||
if CONF.trace_enabled:
|
||||
from aprsd.utils import trace
|
||||
|
||||
trace.setup_tracing(['method', 'api'])
|
||||
|
||||
if not config_file_found:
|
||||
LOG = logging.getLogger('APRSD') # noqa: N806
|
||||
LOG.error("No config file found!! run 'aprsd sample-config'")
|
||||
|
||||
profile_output = kwargs.pop('profile_output', None)
|
||||
del kwargs['loglevel']
|
||||
del kwargs['config_file']
|
||||
del kwargs['quiet']
|
||||
|
||||
# Enable profiling if requested
|
||||
if profile_output is not None:
|
||||
if not profile_output or profile_output == '':
|
||||
profile_output = 'aprsd_profile.prof'
|
||||
profiler = cProfile.Profile()
|
||||
profiler.enable()
|
||||
try:
|
||||
result = f(*args, **kwargs)
|
||||
finally:
|
||||
profiler.disable()
|
||||
profiler.dump_stats(profile_output)
|
||||
LOG = logging.getLogger('APRSD') # noqa: N806
|
||||
LOG.info(f'Profile data saved to {profile_output}')
|
||||
LOG.info(f'Analyze with: python -m pstats {profile_output}')
|
||||
LOG.info(f'Or visualize with: snakeviz {profile_output}')
|
||||
return result
|
||||
else:
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return update_wrapper(t.cast(t.Callable, new_func), f)
|
||||
|
||||
|
||||
@cli.command()
|
||||
@cli_helper.add_options(cli_helper.common_options)
|
||||
@click.option(
|
||||
@ -180,8 +276,20 @@ class ListenStatsThread(APRSDThread):
|
||||
is_flag=True,
|
||||
help='Enable packet stats periodic logging.',
|
||||
)
|
||||
@click.option(
|
||||
'--show-thread',
|
||||
default=False,
|
||||
is_flag=True,
|
||||
help='Show thread name in log format (disabled by default for listen).',
|
||||
)
|
||||
@click.option(
|
||||
'--show-level',
|
||||
default=False,
|
||||
is_flag=True,
|
||||
help='Show log level in log format (disabled by default for listen).',
|
||||
)
|
||||
@click.pass_context
|
||||
@cli_helper.process_standard_options
|
||||
@process_listen_options
|
||||
def listen(
|
||||
ctx,
|
||||
aprs_login,
|
||||
@ -192,6 +300,8 @@ def listen(
|
||||
filter,
|
||||
log_packets,
|
||||
enable_packet_stats,
|
||||
show_thread,
|
||||
show_level,
|
||||
):
|
||||
"""Listen to packets on the APRS-IS Network based on FILTER.
|
||||
|
||||
|
||||
@ -15,17 +15,23 @@ LOG_LEVELS = {
|
||||
}
|
||||
|
||||
DEFAULT_DATE_FORMAT = '%m/%d/%Y %I:%M:%S %p'
|
||||
DEFAULT_LOG_FORMAT = (
|
||||
'[%(asctime)s] [%(threadName)-20.20s] [%(levelname)-5.5s]'
|
||||
' %(message)s - [%(pathname)s:%(lineno)d]'
|
||||
|
||||
# Default log format parts
|
||||
DEFAULT_LOG_FORMAT_TIMESTAMP = '<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green>'
|
||||
DEFAULT_LOG_FORMAT_THREAD = '<yellow>{thread.name: <18}</yellow>'
|
||||
DEFAULT_LOG_FORMAT_LEVEL = '<level>{level: <8}</level>'
|
||||
DEFAULT_LOG_FORMAT_MESSAGE = '<level>{message}</level>'
|
||||
DEFAULT_LOG_FORMAT_LOCATION = (
|
||||
'<cyan>{name}</cyan>:<cyan>{function:}</cyan>:<magenta>{line:}</magenta>'
|
||||
)
|
||||
|
||||
# Build default format from parts
|
||||
DEFAULT_LOG_FORMAT = (
|
||||
'<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | '
|
||||
'<yellow>{thread.name: <18}</yellow> | '
|
||||
'<level>{level: <8}</level> | '
|
||||
'<level>{message}</level> | '
|
||||
'<cyan>{name}</cyan>:<cyan>{function:}</cyan>:<magenta>{line:}</magenta>'
|
||||
f'{DEFAULT_LOG_FORMAT_TIMESTAMP} | '
|
||||
f'{DEFAULT_LOG_FORMAT_THREAD} | '
|
||||
f'{DEFAULT_LOG_FORMAT_LEVEL} | '
|
||||
f'{DEFAULT_LOG_FORMAT_MESSAGE} | '
|
||||
f'{DEFAULT_LOG_FORMAT_LOCATION}'
|
||||
)
|
||||
|
||||
logging_group = cfg.OptGroup(
|
||||
|
||||
@ -12,6 +12,16 @@ CONF = cfg.CONF
|
||||
LOG = logger
|
||||
|
||||
|
||||
def build_log_format():
|
||||
"""Build log format from configurable parts."""
|
||||
# If logformat is explicitly set, use it
|
||||
if CONF.logging.logformat:
|
||||
return CONF.logging.logformat
|
||||
|
||||
# Otherwise, use the default format
|
||||
return conf_log.DEFAULT_LOG_FORMAT
|
||||
|
||||
|
||||
class QueueLatest(queue.Queue):
|
||||
"""Custom Queue to keep only the latest N items.
|
||||
|
||||
@ -84,13 +94,16 @@ def setup_logging(loglevel=None, quiet=False, custom_handler=None):
|
||||
logging.getLogger(name).handlers = []
|
||||
logging.getLogger(name).propagate = name not in disable_list
|
||||
|
||||
# Build the log format from configurable parts
|
||||
log_format = build_log_format()
|
||||
|
||||
handlers = []
|
||||
if CONF.logging.enable_console_stdout and not quiet:
|
||||
handlers.append(
|
||||
{
|
||||
'sink': sys.stdout,
|
||||
'serialize': False,
|
||||
'format': CONF.logging.logformat,
|
||||
'format': log_format,
|
||||
'colorize': CONF.logging.enable_color,
|
||||
'level': log_level,
|
||||
},
|
||||
@ -101,13 +114,16 @@ def setup_logging(loglevel=None, quiet=False, custom_handler=None):
|
||||
{
|
||||
'sink': CONF.logging.logfile,
|
||||
'serialize': False,
|
||||
'format': CONF.logging.logformat,
|
||||
'format': log_format,
|
||||
'colorize': False,
|
||||
'level': log_level,
|
||||
},
|
||||
)
|
||||
|
||||
if custom_handler:
|
||||
# If custom_handler doesn't have a format set, use the built format
|
||||
if 'format' not in custom_handler:
|
||||
custom_handler['format'] = log_format
|
||||
handlers.append(custom_handler)
|
||||
|
||||
# configure loguru
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user