Api 4XX error msg customization #1236 (#11527)

* Adding API_400_ERROR_LOG_FORMAT setting
* Adding functional tests for API_400_ERROR_LOG_FORMAT
Co-authored-by: nixocio <nixocio@gmail.com>
This commit is contained in:
John Westcott IV 2022-01-19 11:16:21 -05:00 committed by GitHub
parent 60831cae88
commit e63ce9ed08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 99 additions and 11 deletions

View File

@ -44,6 +44,7 @@ from awx.main.views import ApiErrorView
from awx.api.serializers import ResourceAccessListElementSerializer, CopySerializer, UserSerializer
from awx.api.versioning import URLPathVersioning
from awx.api.metadata import SublistAttachDetatchMetadata, Metadata
from awx.conf import settings_registry
__all__ = [
'APIView',
@ -208,12 +209,20 @@ class APIView(views.APIView):
return response
if response.status_code >= 400:
status_msg = "status %s received by user %s attempting to access %s from %s" % (
response.status_code,
request.user,
request.path,
request.META.get('REMOTE_ADDR', None),
)
msg_data = {
'status_code': response.status_code,
'user_name': request.user,
'url_path': request.path,
'remote_addr': request.META.get('REMOTE_ADDR', None),
'error': response.data.get('error', response.status_text),
}
try:
status_msg = getattr(settings, 'API_400_ERROR_LOG_FORMAT').format(**msg_data)
except Exception as e:
if getattr(settings, 'API_400_ERROR_LOG_FORMAT', None):
logger.error("Unable to format API_400_ERROR_LOG_FORMAT setting, defaulting log message: {}".format(e))
status_msg = settings_registry.get_setting_field('API_400_ERROR_LOG_FORMAT').get_default().format(**msg_data)
if hasattr(self, '__init_request_error__'):
response = self.handle_exception(self.__init_request_error__)
if response.status_code == 401:
@ -221,6 +230,7 @@ class APIView(views.APIView):
logger.info(status_msg)
else:
logger.warning(status_msg)
response = super(APIView, self).finalize_response(request, response, *args, **kwargs)
time_started = getattr(self, 'time_started', None)
response['X-API-Product-Version'] = get_awx_version()

View File

@ -674,6 +674,24 @@ register(
category=_('Logging'),
category_slug='logging',
)
register(
'API_400_ERROR_LOG_FORMAT',
field_class=fields.CharField,
default='status {status_code} received by user {user_name} attempting to access {url_path} from {remote_addr}',
label=_('Log Format For API 4XX Errors'),
help_text=_(
'The format of logged messages when an API 4XX error occurs, '
'the following variables will be substituted: \n'
'status_code - The HTTP status code of the error\n'
'user_name - The user name attempting to use the API\n'
'url_path - The URL path to the API endpoint called\n'
'remote_addr - The remote address seen for the user\n'
'error - The error set by the api endpoint\n'
'Variables need to be in the format {<variable name>}.'
),
category=_('Logging'),
category_slug='logging',
)
register(

View File

@ -0,0 +1,26 @@
import pytest
from django.test.utils import override_settings
from awx.api.versioning import reverse
@pytest.mark.django_db
def test_change_400_error_log(caplog, post, admin_user):
with override_settings(API_400_ERROR_LOG_FORMAT='Test'):
post(url=reverse('api:setting_logging_test'), data={}, user=admin_user, expect=409)
assert 'Test' in caplog.text
@pytest.mark.django_db
def test_bad_400_error_log(caplog, post, admin_user):
with override_settings(API_400_ERROR_LOG_FORMAT="Not good {junk}"):
post(url=reverse('api:setting_logging_test'), data={}, user=admin_user, expect=409)
assert "Unable to format API_400_ERROR_LOG_FORMAT setting, defaulting log message: 'junk'" in caplog.text
assert 'status 409 received by user admin attempting to access /api/v2/settings/logging/test/ from 127.0.0.1' in caplog.text
@pytest.mark.django_db
def test_custom_400_error_log(caplog, post, admin_user):
with override_settings(API_400_ERROR_LOG_FORMAT="{status_code} {error}"):
post(url=reverse('api:setting_logging_test'), data={}, user=admin_user, expect=409)
assert '409 Logging not enabled' in caplog.text

View File

@ -761,6 +761,7 @@ LOG_AGGREGATOR_MAX_DISK_USAGE_GB = 1
LOG_AGGREGATOR_MAX_DISK_USAGE_PATH = '/var/lib/awx'
LOG_AGGREGATOR_RSYSLOGD_DEBUG = False
LOG_AGGREGATOR_RSYSLOGD_ERROR_LOG_FILE = '/var/log/tower/rsyslog.err'
API_400_ERROR_LOG_FORMAT = 'status {status_code} received by user {user_name} attempting to access {url_path} from {remote_addr}'
# The number of retry attempts for websocket session establishment
# If you're encountering issues establishing websockets in a cluster,

View File

@ -32,6 +32,8 @@ SettingsAPI.readCategory.mockResolvedValue({
LOG_AGGREGATOR_MAX_DISK_USAGE_GB: 1,
LOG_AGGREGATOR_MAX_DISK_USAGE_PATH: '/var/lib/awx',
LOG_AGGREGATOR_RSYSLOGD_DEBUG: false,
API_400_ERROR_LOG_FORMAT:
'status {status_code} received by user {user_name} attempting to access {url_path} from {remote_addr}',
},
});

View File

@ -42,7 +42,8 @@ function LoggingDetail() {
'LOG_AGGREGATOR_TCP_TIMEOUT',
'LOG_AGGREGATOR_TYPE',
'LOG_AGGREGATOR_USERNAME',
'LOG_AGGREGATOR_VERIFY_CERT'
'LOG_AGGREGATOR_VERIFY_CERT',
'API_400_ERROR_LOG_FORMAT'
);
const mergedData = {};

View File

@ -59,6 +59,7 @@ describe('<LoggingDetail />', () => {
assertDetail(wrapper, 'Logging Aggregator Protocol', 'https');
assertDetail(wrapper, 'TCP Connection Timeout', '5 seconds');
assertDetail(wrapper, 'Logging Aggregator Level Threshold', 'INFO');
assertDetail(wrapper, 'Log Format For API 4XX Errors', 'Test Log Line');
assertDetail(
wrapper,
'Enable/disable HTTPS certificate verification',

View File

@ -8,7 +8,7 @@ import { CardBody } from 'components/Card';
import ContentError from 'components/ContentError';
import ContentLoading from 'components/ContentLoading';
import { FormSubmitError } from 'components/FormField';
import { FormColumnLayout } from 'components/FormLayout';
import { FormColumnLayout, FormFullWidthLayout } from 'components/FormLayout';
import { useSettings } from 'contexts/Settings';
import useModal from 'hooks/useModal';
import useRequest from 'hooks/useRequest';
@ -71,6 +71,7 @@ function LoggingEdit() {
LOG_AGGREGATOR_LOGGERS: formatJson(form.LOG_AGGREGATOR_LOGGERS),
LOG_AGGREGATOR_HOST: form.LOG_AGGREGATOR_HOST || null,
LOG_AGGREGATOR_TYPE: form.LOG_AGGREGATOR_TYPE || null,
API_400_ERROR_LOG_FORMAT: form.API_400_ERROR_LOG_FORMAT || null,
});
};
@ -196,6 +197,12 @@ function LoggingEdit() {
name="LOG_AGGREGATOR_LOGGERS"
config={logging.LOG_AGGREGATOR_LOGGERS}
/>
<FormFullWidthLayout>
<InputField
name="API_400_ERROR_LOG_FORMAT"
config={logging.API_400_ERROR_LOG_FORMAT}
/>
</FormFullWidthLayout>
{submitError && <FormSubmitError error={submitError} />}
{revertError && <FormSubmitError error={revertError} />}
<RevertFormActionGroup

View File

@ -34,6 +34,8 @@ const mockSettings = {
LOG_AGGREGATOR_MAX_DISK_USAGE_GB: 1,
LOG_AGGREGATOR_MAX_DISK_USAGE_PATH: '/var/lib/awx',
LOG_AGGREGATOR_RSYSLOGD_DEBUG: false,
API_400_ERROR_LOG_FORMAT:
'status {status_code} received by user {user_name} attempting to access {url_path} from {remote_addr}',
};
describe('<LoggingEdit />', () => {

View File

@ -564,6 +564,15 @@
"category_slug": "logging",
"default": false
},
"API_400_ERROR_LOG_FORMAT": {
"type": "string",
"required": false,
"label": "Log Format For API 4XX Errors",
"help_text": "The format of logged messages when an API 4XX error occurs, the following variables will be substituted: \nstatus_code - The HTTP status code of the error\nuser_name - The user name attempting to use the API\nurl_path - The URL path to the API endpoint called\nremote_addr - The remote address seen for the user\nerror - The error set by the api endpoint\nVariables need to be in the format {<variable name>}.",
"category": "Logging",
"category_slug": "logging",
"default": "status {status_code} received by user {user_name} attempting to access {url_path} from {remote_addr}"
},
"AUTOMATION_ANALYTICS_LAST_GATHER": {
"type": "datetime",
"required": true,
@ -4221,6 +4230,15 @@
"category_slug": "logging",
"defined_in_file": false
},
"API_400_ERROR_LOG_FORMAT": {
"type": "string",
"required": false,
"label": "Log Format For API 4XX Errors",
"help_text": "The format of logged messages when an API 4XX error occurs, the following variables will be substituted: \nstatus_code - The HTTP status code of the error\nuser_name - The user name attempting to use the API\nurl_path - The URL path to the API endpoint called\nremote_addr - The remote address seen for the user\nerror - The error set by the api endpoint\nVariables need to be in the format {<variable name>}.",
"category": "Logging",
"category_slug": "logging",
"default": "status {status_code} received by user {user_name} attempting to access {url_path} from {remote_addr}"
},
"AUTOMATION_ANALYTICS_LAST_GATHER": {
"type": "datetime",
"label": "Last gather date for Insights for Ansible Automation Platform.",
@ -6329,7 +6347,7 @@
},
"SOCIAL_AUTH_SAML_USER_FLAGS_BY_ATTR": {
"type": "nested object",
"label": "SAML User Flags Attribute Mapping",
"label": "SAML User Flags Attribute Mapping",
"help_text": "Used to map super users and system auditors from SAML.",
"category": "SAML",
"category_slug": "saml",

View File

@ -70,6 +70,7 @@
"LOG_AGGREGATOR_MAX_DISK_USAGE_GB":1,
"LOG_AGGREGATOR_MAX_DISK_USAGE_PATH":"/var/lib/awx",
"LOG_AGGREGATOR_RSYSLOGD_DEBUG":false,
"API_400_ERROR_LOG_FORMAT":"status {status_code} received by user {user_name} attempting to access {url_path} from {remote_addr}",
"AUTOMATION_ANALYTICS_LAST_GATHER":null,
"AUTOMATION_ANALYTICS_GATHER_INTERVAL":14400,
"SESSION_COOKIE_AGE":1800,

View File

@ -17,5 +17,6 @@
"LOG_AGGREGATOR_LEVEL": "INFO",
"LOG_AGGREGATOR_MAX_DISK_USAGE_GB": 1,
"LOG_AGGREGATOR_MAX_DISK_USAGE_PATH": "/var/lib/awx",
"LOG_AGGREGATOR_RSYSLOGD_DEBUG": false
}
"LOG_AGGREGATOR_RSYSLOGD_DEBUG": false,
"API_400_ERROR_LOG_FORMAT": "Test Log Line"
}