feat(analytics): log request_id, account_number, org_id from ingress API response (#16512)

* feat(analytics): ANSTRAT-2268 log request_id, account_number, org_id from ingress API response

Log the ingress API success response fields (request_id, account_number,
org_id) in the controller task log when gather_analytics tarballs are
uploaded. This enables support engineers to trace uploads through to
Kibana without source code modifications.

* style(analytics): use f-strings in _log_shipping_response to match codebase conventions

* style(analytics): fix black formatting in test assertions
This commit is contained in:
Dave
2026-06-22 15:03:57 +01:00
committed by GitHub
parent 23b7ad5067
commit 67048a609a
2 changed files with 51 additions and 1 deletions

View File

@@ -420,6 +420,18 @@ def gather(dest=None, module=None, subset=None, since=None, until=None, collecti
return tarfiles
def _log_shipping_response(response, path):
filename = os.path.basename(path)
try:
data = response.json()
request_id = data.get('request_id', 'unknown')
account_number = data.get('account_number', 'unknown')
org_id = data.get('org_id', 'unknown')
logger.info(f"Analytics upload successful: file={filename} request_id={request_id} account_number={account_number} org_id={org_id}")
except Exception:
logger.info(f"Analytics upload successful: file={filename} status={response.status_code}")
def ship(path):
"""
Ship gathered metrics to the Insights API
@@ -468,6 +480,7 @@ def ship(path):
cert_url, files=files, cert=(cert_path, key_path), verify=settings.INSIGHTS_CERT_PATH, headers=s.headers, timeout=(31, 31)
)
if response.status_code < 300:
_log_shipping_response(response, path)
return True
else:
logger.warning(
@@ -484,6 +497,7 @@ def ship(path):
response = client.make_request("POST", url, headers=s.headers, files=files, verify=settings.INSIGHTS_CERT_PATH, timeout=(31, 31))
if response.status_code < 300:
_log_shipping_response(response, path)
return True
else:
logger.error(f'OIDC authentication failed with status {response.status_code}, {response.text}')

View File

@@ -9,7 +9,7 @@ from unittest import mock
from django.test.utils import override_settings
from awx.main.analytics.core import ship, _get_cert_upload_url
from awx.main.analytics.core import ship, _get_cert_upload_url, _log_shipping_response
class TestGetCertUploadUrl:
@@ -70,6 +70,7 @@ class TestShipMTLS:
# Mock successful mTLS response
mock_response = mock.Mock()
mock_response.status_code = 200
mock_response.json.return_value = {'request_id': 'abc-123', 'account_number': '12345', 'org_id': '67890'}
mock_session = mock.Mock()
mock_session.headers = {}
mock_session.post.return_value = mock_response
@@ -81,6 +82,7 @@ class TestShipMTLS:
mock_get_cert.assert_called_once()
mock_temp_files.assert_called_once_with('cert-pem-data', 'key-pem-data')
mock_session.post.assert_called_once()
mock_response.json.assert_called_once()
# Verify cert URL is used (cert. subdomain added)
call_args = mock_session.post.call_args
@@ -126,6 +128,7 @@ class TestShipMTLS:
# Mock successful OIDC response
mock_oidc_response = mock.Mock()
mock_oidc_response.status_code = 200
mock_oidc_response.json.return_value = {'request_id': 'oidc-456', 'account_number': '12345', 'org_id': '67890'}
mock_oidc_instance = mock.Mock()
mock_oidc_instance.make_request.return_value = mock_oidc_response
mock_oidc_client.return_value = mock_oidc_instance
@@ -172,6 +175,7 @@ class TestShipMTLS:
# Mock successful OIDC response
mock_oidc_response = mock.Mock()
mock_oidc_response.status_code = 200
mock_oidc_response.json.return_value = {'request_id': 'oidc-789', 'account_number': '12345', 'org_id': '67890'}
mock_oidc_instance = mock.Mock()
mock_oidc_instance.make_request.return_value = mock_oidc_response
mock_oidc_client.return_value = mock_oidc_instance
@@ -209,6 +213,7 @@ class TestShipMTLS:
# Mock successful OIDC response
mock_oidc_response = mock.Mock()
mock_oidc_response.status_code = 200
mock_oidc_response.json.return_value = {'request_id': 'oidc-no-cert', 'account_number': '12345', 'org_id': '67890'}
mock_oidc_instance = mock.Mock()
mock_oidc_instance.make_request.return_value = mock_oidc_response
mock_oidc_client.return_value = mock_oidc_instance
@@ -269,3 +274,34 @@ class TestShipMTLS:
assert result is False
mock_session.post.assert_called_once()
mock_oidc_instance.make_request.assert_called_once()
class TestLogShippingResponse:
"""Test _log_shipping_response() helper function."""
def test_logs_response_fields(self):
"""Test that request_id, account_number, and org_id are logged."""
response = mock.Mock()
response.json.return_value = {'request_id': 'req-abc', 'account_number': '99999', 'org_id': '11111'}
with mock.patch('awx.main.analytics.core.logger') as mock_logger:
_log_shipping_response(response, '/tmp/analytics.tar.gz')
mock_logger.info.assert_called_once_with("Analytics upload successful: file=analytics.tar.gz request_id=req-abc account_number=99999 org_id=11111")
def test_logs_unknown_for_missing_fields(self):
"""Test fallback to 'unknown' when response fields are absent."""
response = mock.Mock()
response.json.return_value = {}
with mock.patch('awx.main.analytics.core.logger') as mock_logger:
_log_shipping_response(response, '/tmp/analytics.tar.gz')
mock_logger.info.assert_called_once_with(
"Analytics upload successful: file=analytics.tar.gz request_id=unknown account_number=unknown org_id=unknown"
)
def test_graceful_fallback_on_json_error(self):
"""Test fallback log when response body is not valid JSON."""
response = mock.Mock()
response.json.side_effect = ValueError("No JSON")
response.status_code = 202
with mock.patch('awx.main.analytics.core.logger') as mock_logger:
_log_shipping_response(response, '/tmp/analytics.tar.gz')
mock_logger.info.assert_called_once_with("Analytics upload successful: file=analytics.tar.gz status=202")