From b0ad13a7cbf13c64d5aa002ff532ca9607674b68 Mon Sep 17 00:00:00 2001 From: James Pogran Date: Wed, 12 Aug 2026 15:20:59 -0400 Subject: [PATCH] Add log search API Define POST /api/logging/logs/getLogs/v1 in the OpenAPI specification with API-token authentication and open JSON request and response bodies. Regenerate LoggingAnalyzeApi and its documentation, and cover the serialized URL, JSON payload, and Authorization header. --- README.md | 2 +- docs/LoggingAnalyzeApi.md | 82 +++++++ instana_client/api/logging_analyze_api.py | 285 +++++++++++++++++++++- openapi.yaml | 30 +++ test/test_logging_analyze_api.py | 28 +++ 5 files changed, 425 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 819c8272..de9dc79c 100644 --- a/README.md +++ b/README.md @@ -397,6 +397,7 @@ Class | Method | HTTP request | Description *LogAlertConfigurationApi* | [**restore_log_alert_config**](docs/LogAlertConfigurationApi.md#restore_log_alert_config) | **PUT** /api/events/settings/global-alert-configs/logs/{id}/restore/{created} | Restore Log Alert Config *LogAlertConfigurationApi* | [**update_log_alert_config**](docs/LogAlertConfigurationApi.md#update_log_alert_config) | **POST** /api/events/settings/global-alert-configs/logs/{id} | Update Log Alert Config *LoggingAnalyzeApi* | [**get_log_volume_usage**](docs/LoggingAnalyzeApi.md#get_log_volume_usage) | **GET** /api/logging/logs/getLogVolumeUsage | Get Log Volume Usage +*LoggingAnalyzeApi* | [**search_logs**](docs/LoggingAnalyzeApi.md#search_logs) | **POST** /api/logging/logs/getLogs/v1 | Search logs *MaintenanceConfigurationApi* | [**delete_maintenance_config**](docs/MaintenanceConfigurationApi.md#delete_maintenance_config) | **DELETE** /api/settings/maintenance/{id} | Delete maintenance configuration *MaintenanceConfigurationApi* | [**delete_maintenance_config_v2**](docs/MaintenanceConfigurationApi.md#delete_maintenance_config_v2) | **DELETE** /api/settings/v2/maintenance/{id} | Delete maintenance configuration *MaintenanceConfigurationApi* | [**get_maintenance_config**](docs/MaintenanceConfigurationApi.md#get_maintenance_config) | **GET** /api/settings/maintenance/{id} | Maintenance configuration @@ -1160,4 +1161,3 @@ Authentication schemes defined for the API: support@instana.com - diff --git a/docs/LoggingAnalyzeApi.md b/docs/LoggingAnalyzeApi.md index 5b7ac7f7..6a11518a 100644 --- a/docs/LoggingAnalyzeApi.md +++ b/docs/LoggingAnalyzeApi.md @@ -5,6 +5,7 @@ All URIs are relative to *https://unit-tenant.instana.io* Method | HTTP request | Description ------------- | ------------- | ------------- [**get_log_volume_usage**](LoggingAnalyzeApi.md#get_log_volume_usage) | **GET** /api/logging/logs/getLogVolumeUsage | Get Log Volume Usage +[**search_logs**](LoggingAnalyzeApi.md#search_logs) | **POST** /api/logging/logs/getLogs/v1 | Search logs # **get_log_volume_usage** @@ -95,3 +96,84 @@ Name | Type | Description | Notes [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) +# **search_logs** +> Dict[str, object] search_logs(request_body) + +Search logs + +Search logs using a time range, requested tags, optional tag-filter expression, and pagination settings. + +### Example + +* Api Key Authentication (ApiKeyAuth): + +```python +import instana_client +from instana_client.rest import ApiException +from pprint import pprint + +# Defining the host is optional and defaults to https://unit-tenant.instana.io +# See configuration.py for a list of all supported configuration parameters. +configuration = instana_client.Configuration( + host = "https://unit-tenant.instana.io" +) + +# The client must configure the authentication and authorization parameters +# in accordance with the API server security policy. +# Examples for each auth method are provided below, use the example that +# satisfies your auth use case. + +# Configure API key authorization: ApiKeyAuth +configuration.api_key['ApiKeyAuth'] = os.environ["API_KEY"] + +# Uncomment below to setup prefix (e.g. Bearer) for API key, if needed +# configuration.api_key_prefix['ApiKeyAuth'] = 'Bearer' + +# Enter a context with an instance of the API client +with instana_client.ApiClient(configuration) as api_client: + # Create an instance of the API class + api_instance = instana_client.LoggingAnalyzeApi(api_client) + request_body = None # Dict[str, object] | + + try: + # Search logs + api_response = api_instance.search_logs(request_body) + print("The response of LoggingAnalyzeApi->search_logs:\n") + pprint(api_response) + except Exception as e: + print("Exception when calling LoggingAnalyzeApi->search_logs: %s\n" % e) +``` + + + +### Parameters + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **request_body** | [**Dict[str, object]**](object.md)| | + +### Return type + +**Dict[str, object]** + +### Authorization + +[ApiKeyAuth](../README.md#ApiKeyAuth) + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +### HTTP response details + +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**200** | Log search results. | - | +**400** | Invalid log search request. | - | +**401** | Unauthorized access. | - | +**500** | Internal server error. | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/instana_client/api/logging_analyze_api.py b/instana_client/api/logging_analyze_api.py index b1e29e5a..df5e7b12 100644 --- a/instana_client/api/logging_analyze_api.py +++ b/instana_client/api/logging_analyze_api.py @@ -18,7 +18,7 @@ from typing_extensions import Annotated from pydantic import Field, StrictInt, StrictStr -from typing import Optional +from typing import Any, Dict, Optional from typing_extensions import Annotated from instana_client.models.log_volume_usage_result import LogVolumeUsageResult @@ -341,3 +341,286 @@ def _get_log_volume_usage_serialize( ) + + + @validate_call + def search_logs( + self, + request_body: Dict[str, Any], + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> Dict[str, object]: + """Search logs + + Search logs using a time range, requested tags, optional tag-filter expression, and pagination settings. + + :param request_body: (required) + :type request_body: Dict[str, object] + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._search_logs_serialize( + request_body=request_body, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "Dict[str, object]", + '400': None, + '401': None, + '500': None, + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + + @validate_call + def search_logs_with_http_info( + self, + request_body: Dict[str, Any], + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[Dict[str, object]]: + """Search logs + + Search logs using a time range, requested tags, optional tag-filter expression, and pagination settings. + + :param request_body: (required) + :type request_body: Dict[str, object] + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._search_logs_serialize( + request_body=request_body, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "Dict[str, object]", + '400': None, + '401': None, + '500': None, + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + + @validate_call + def search_logs_without_preload_content( + self, + request_body: Dict[str, Any], + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """Search logs + + Search logs using a time range, requested tags, optional tag-filter expression, and pagination settings. + + :param request_body: (required) + :type request_body: Dict[str, object] + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._search_logs_serialize( + request_body=request_body, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "Dict[str, object]", + '400': None, + '401': None, + '500': None, + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + + def _search_logs_serialize( + self, + request_body, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: Dict[str, str] = { + } + + _path_params: Dict[str, str] = {} + _query_params: List[Tuple[str, str]] = [] + _header_params: Dict[str, Optional[str]] = _headers or {} + _form_params: List[Tuple[str, str]] = [] + _files: Dict[ + str, Union[str, bytes, List[str], List[bytes], List[Tuple[str, bytes]]] + ] = {} + _body_params: Optional[bytes] = None + + # process the path parameters + # process the query parameters + # process the header parameters + # process the form parameters + # process the body parameter + if request_body is not None: + _body_params = request_body + + + # set the HTTP header `Accept` + if 'Accept' not in _header_params: + _header_params['Accept'] = self.api_client.select_header_accept( + [ + 'application/json' + ] + ) + + # set the HTTP header `Content-Type` + if _content_type: + _header_params['Content-Type'] = _content_type + else: + _default_content_type = ( + self.api_client.select_header_content_type( + [ + 'application/json' + ] + ) + ) + if _default_content_type is not None: + _header_params['Content-Type'] = _default_content_type + + # authentication setting + _auth_settings: List[str] = [ + 'ApiKeyAuth' + ] + + return self.api_client.param_serialize( + method='POST', + resource_path='/api/logging/logs/getLogs/v1', + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + diff --git a/openapi.yaml b/openapi.yaml index 315d1e06..56a8381b 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -14890,6 +14890,36 @@ paths: tags: - AI Management x-ibm-ahub-byok: true + /api/logging/logs/getLogs/v1: + post: + description: Search logs using a time range, requested tags, optional tag-filter expression, and pagination settings. + operationId: searchLogs + requestBody: + content: + application/json: + schema: + type: object + additionalProperties: true + required: true + responses: + '200': + content: + application/json: + schema: + type: object + additionalProperties: true + description: Log search results. + '400': + description: Invalid log search request. + '401': + description: Unauthorized access. + '500': + description: Internal server error. + security: + - ApiKeyAuth: [] + summary: Search logs + tags: + - Logging Analyze /api/logging/logs/getLogVolumeUsage: get: description: | diff --git a/test/test_logging_analyze_api.py b/test/test_logging_analyze_api.py index 5082307a..c42ca63d 100644 --- a/test/test_logging_analyze_api.py +++ b/test/test_logging_analyze_api.py @@ -15,7 +15,9 @@ import unittest +from instana_client.api_client import ApiClient from instana_client.api.logging_analyze_api import LoggingAnalyzeApi +from instana_client.configuration import Configuration class TestLoggingAnalyzeApi(unittest.TestCase): @@ -34,6 +36,32 @@ def test_get_log_volume_usage(self) -> None: """ pass + def test_search_logs_serializes_payload_and_api_token_auth(self) -> None: + configuration = Configuration() + configuration.api_key['ApiKeyAuth'] = 'token' + configuration.api_key_prefix['ApiKeyAuth'] = 'apiToken' + api = LoggingAnalyzeApi(api_client=ApiClient(configuration=configuration)) + payload = { + 'timeConfig': {'to': 1_700_000_000_000, 'windowSize': 3_600_000}, + 'requestedTags': ['log.message'], + 'retrievalSize': 10, + } + + method, url, headers, body, post_params = api._search_logs_serialize( + request_body=payload, + _request_auth=None, + _content_type=None, + _headers=None, + _host_index=0, + ) + + self.assertEqual('POST', method) + self.assertEqual('https://unit-tenant.instana.io/api/logging/logs/getLogs/v1', url) + self.assertEqual('apiToken token', headers['authorization']) + self.assertEqual('application/json', headers['Content-Type']) + self.assertEqual(payload, body) + self.assertEqual([], post_params) + if __name__ == '__main__': unittest.main()