diff --git a/awx/main/models/unified_jobs.py b/awx/main/models/unified_jobs.py index 0c95d5b3e98c..d8d6936052ae 100644 --- a/awx/main/models/unified_jobs.py +++ b/awx/main/models/unified_jobs.py @@ -1550,7 +1550,7 @@ def log_lifecycle(self, state, blocked_by=None): extra["controller_node"] = self.controller_node or "NOT_SET" elif state == "execution_node_chosen": extra["execution_node"] = self.execution_node or "NOT_SET" - logger_job_lifecycle.debug(msg, extra=extra) + logger_job_lifecycle.info(msg, extra=extra) @property def launched_by(self): diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index a24fe6f090ba..e0d4262fdbe4 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -821,86 +821,25 @@ def IS_TESTING(argv=None): 'dispatcher': {'format': '%(asctime)s %(levelname)-8s [%(guid)s] %(name)s PID:%(process)d %(message)s'}, 'job_lifecycle': {'()': 'awx.main.utils.formatters.JobLifeCycleFormatter'}, }, + # Extended below based on install scenario. You probably don't want to add something directly here. + # See 'handler_config' below. 'handlers': { 'console': { '()': 'logging.StreamHandler', 'level': 'DEBUG', - 'filters': ['require_debug_true_or_test', 'dynamic_level_filter', 'guid'], + 'filters': ['dynamic_level_filter', 'guid'], 'formatter': 'simple', }, 'null': {'class': 'logging.NullHandler'}, 'file': {'class': 'logging.NullHandler', 'formatter': 'simple'}, 'syslog': {'level': 'WARNING', 'filters': ['require_debug_false'], 'class': 'logging.NullHandler', 'formatter': 'simple'}, + 'inventory_import': {'level': 'DEBUG', 'class': 'logging.StreamHandler', 'formatter': 'timed_import'}, 'external_logger': { 'class': 'awx.main.utils.handlers.RSysLogHandler', 'formatter': 'json', 'address': '/var/run/awx-rsyslog/rsyslog.sock', 'filters': ['external_log_enabled', 'dynamic_level_filter', 'guid'], }, - 'tower_warnings': { - # don't define a level here, it's set by settings.LOG_AGGREGATOR_LEVEL - 'class': 'logging.handlers.WatchedFileHandler', - 'filters': ['require_debug_false', 'dynamic_level_filter', 'guid'], - 'filename': os.path.join(LOG_ROOT, 'tower.log'), - 'formatter': 'simple', - }, - 'callback_receiver': { - # don't define a level here, it's set by settings.LOG_AGGREGATOR_LEVEL - 'class': 'logging.handlers.WatchedFileHandler', - 'filters': ['require_debug_false', 'dynamic_level_filter', 'guid'], - 'filename': os.path.join(LOG_ROOT, 'callback_receiver.log'), - 'formatter': 'simple', - }, - 'dispatcher': { - # don't define a level here, it's set by settings.LOG_AGGREGATOR_LEVEL - 'class': 'logging.handlers.WatchedFileHandler', - 'filters': ['require_debug_false', 'dynamic_level_filter', 'guid'], - 'filename': os.path.join(LOG_ROOT, 'dispatcher.log'), - 'formatter': 'dispatcher', - }, - 'wsbroadcast': { - # don't define a level here, it's set by settings.LOG_AGGREGATOR_LEVEL - 'class': 'logging.handlers.WatchedFileHandler', - 'filters': ['require_debug_false', 'dynamic_level_filter', 'guid'], - 'filename': os.path.join(LOG_ROOT, 'wsbroadcast.log'), - 'formatter': 'simple', - }, - 'celery.beat': {'class': 'logging.StreamHandler', 'level': 'ERROR'}, # don't log every celerybeat wakeup - 'inventory_import': {'level': 'DEBUG', 'class': 'logging.StreamHandler', 'formatter': 'timed_import'}, - 'task_system': { - # don't define a level here, it's set by settings.LOG_AGGREGATOR_LEVEL - 'class': 'logging.handlers.WatchedFileHandler', - 'filters': ['require_debug_false', 'dynamic_level_filter', 'guid'], - 'filename': os.path.join(LOG_ROOT, 'task_system.log'), - 'formatter': 'simple', - }, - 'management_playbooks': { - 'level': 'DEBUG', - 'class': 'logging.handlers.WatchedFileHandler', - 'filters': ['require_debug_false'], - 'filename': os.path.join(LOG_ROOT, 'management_playbooks.log'), - 'formatter': 'simple', - }, - 'system_tracking_migrations': { - 'level': 'WARNING', - 'class': 'logging.handlers.WatchedFileHandler', - 'filters': ['require_debug_false'], - 'filename': os.path.join(LOG_ROOT, 'tower_system_tracking_migrations.log'), - 'formatter': 'simple', - }, - 'rbac_migrations': { - 'level': 'WARNING', - 'class': 'logging.handlers.WatchedFileHandler', - 'filters': ['require_debug_false'], - 'filename': os.path.join(LOG_ROOT, 'tower_rbac_migrations.log'), - 'formatter': 'simple', - }, - 'job_lifecycle': { - 'level': 'DEBUG', - 'class': 'logging.handlers.WatchedFileHandler', - 'filename': os.path.join(LOG_ROOT, 'job_lifecycle.log'), - 'formatter': 'job_lifecycle', - }, }, 'loggers': { 'django': {'handlers': ['console']}, @@ -933,6 +872,40 @@ def IS_TESTING(argv=None): }, } +# Log handler configuration. Keys are the name of the handler. Be mindful when renaming things here. +# People might have created custom settings files that augments the behavior of these. +# Specify 'filename' (used if the environment variable AWX_LOGGING_MODE is unset or 'file') +# and an optional 'formatter'. If no formatter is specified, 'simple' is used. +handler_config = { + 'tower_warnings': {'filename': 'tower.log'}, + 'callback_receiver': {'filename': 'callback_receiver.log'}, + 'dispatcher': {'filename': 'dispatcher.log', 'formatter': 'dispatcher'}, + 'wsbroadcast': {'filename': 'wsbroadcast.log'}, + 'task_system': {'filename': 'task_system.log'}, + 'rbac_migrations': {'filename': 'tower_rbac_migrations.log'}, + 'job_lifecycle': {'filename': 'job_lifecycle.log', 'formatter': 'job_lifecycle'}, +} + +# If running on a VM, we log to files. When running in a container, we log to stdout. +logging_mode = os.getenv('AWX_LOGGING_MODE', 'file') +if logging_mode not in ('file', 'stdout'): + raise Exception("AWX_LOGGING_MODE must be 'file' or 'stdout'") + +for name, config in handler_config.items(): + # Common log handler config. Don't define a level here, it's set by settings.LOG_AGGREGATOR_LEVEL + LOGGING['handlers'][name] = {'filters': ['dynamic_level_filter', 'guid'], 'formatter': config.get('formatter', 'simple')} + + if logging_mode == 'file': + LOGGING['handlers'][name]['class'] = 'logging.handlers.WatchedFileHandler' + LOGGING['handlers'][name]['filename'] = os.path.join(LOG_ROOT, config['filename']) + + if logging_mode == 'stdout': + LOGGING['handlers'][name]['class'] = 'logging.NullHandler' + +# Prevents logging to stdout on traditional VM installs +if logging_mode == 'file': + LOGGING['handlers']['console']['filters'].insert(0, 'require_debug_true_or_test') + # Apply coloring to messages logged to the console COLOR_LOGS = False diff --git a/tools/ansible/roles/dockerfile/templates/Dockerfile.j2 b/tools/ansible/roles/dockerfile/templates/Dockerfile.j2 index 232fa6023451..a9891b2b41e1 100644 --- a/tools/ansible/roles/dockerfile/templates/Dockerfile.j2 +++ b/tools/ansible/roles/dockerfile/templates/Dockerfile.j2 @@ -10,6 +10,7 @@ FROM quay.io/centos/centos:stream9 as builder ENV LANG en_US.UTF-8 ENV LANGUAGE en_US:en ENV LC_ALL en_US.UTF-8 +ENV AWX_LOGGING_MODE stdout USER root @@ -88,6 +89,7 @@ FROM quay.io/centos/centos:stream9 ENV LANG en_US.UTF-8 ENV LANGUAGE en_US:en ENV LC_ALL en_US.UTF-8 +ENV AWX_LOGGING_MODE stdout USER root diff --git a/tools/docker-compose/ansible/roles/sources/templates/docker-compose.yml.j2 b/tools/docker-compose/ansible/roles/sources/templates/docker-compose.yml.j2 index 3884a383a536..87e50fbaa82b 100644 --- a/tools/docker-compose/ansible/roles/sources/templates/docker-compose.yml.j2 +++ b/tools/docker-compose/ansible/roles/sources/templates/docker-compose.yml.j2 @@ -21,6 +21,7 @@ services: RECEPTORCTL_SOCKET: {{ receptor_socket_file }} CONTROL_PLANE_NODE_COUNT: {{ control_plane_node_count|int }} EXECUTION_NODE_COUNT: {{ execution_node_count|int }} + AWX_LOGGING_MODE: stdout {% if loop.index == 1 %} RUN_MIGRATIONS: 1 {% endif %}