From f6ac0bda7be83c250ec74745694c2d99d49407ea Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Mon, 23 Aug 2021 13:57:41 -0700 Subject: [PATCH 01/11] logs client --- .../azure/monitor/query/_logs_query_client.py | 12 ++++++------ .../monitor/query/aio/_logs_query_client_async.py | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/_logs_query_client.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/_logs_query_client.py index 7431510fb04d..545a6cc69b2a 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/_logs_query_client.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/_logs_query_client.py @@ -5,7 +5,7 @@ # license information. # -------------------------------------------------------------------------- -from typing import TYPE_CHECKING, Any, Union, Sequence, Dict, Optional +from typing import TYPE_CHECKING, Any, Union, Sequence, Dict from azure.core.exceptions import HttpResponseError from azure.core.tracing.decorator import distributed_trace @@ -17,7 +17,7 @@ if TYPE_CHECKING: from azure.core.credentials import TokenCredential - from datetime import timedelta + from datetime import timedelta, datetime class LogsQueryClient(object): @@ -51,8 +51,8 @@ def __init__(self, credential, **kwargs): self._query_op = self._client.query @distributed_trace - def query(self, workspace_id, query, timespan=None, **kwargs): - # type: (str, str, Optional[timedelta], Any) -> LogsQueryResult + def query(self, workspace_id, query, timespan, **kwargs): + # type: (str, str, Union[timedelta, (datetime, timedelta), (datetime, datetime)], Any) -> LogsQueryResult """Execute an Analytics query. Executes an Analytics query for data. @@ -76,7 +76,7 @@ def query(self, workspace_id, query, timespan=None, **kwargs): :keyword additional_workspaces: A list of workspaces that are included in the query. These can be qualified workspace names, workspace Ids, or Azure resource Ids. :paramtype additional_workspaces: list[str] - :return: QueryResults, or the result of cls(response) + :return: LogsQueryResult, or the result of cls(response) :rtype: ~azure.monitor.query.LogsQueryResult :raises: ~azure.core.exceptions.HttpResponseError @@ -135,7 +135,7 @@ def query_batch(self, queries, **kwargs): :param queries: The list of queries that should be processed :type queries: list[dict] or list[~azure.monitor.query.LogsBatchQuery] :return: List of LogsBatchQueryResult, or the result of cls(response) - :rtype: ~list[~azure.monitor.query.LogsBatchQueryResult] + :rtype: list[~azure.monitor.query.LogsBatchQueryResult] :raises: ~azure.core.exceptions.HttpResponseError .. admonition:: Example: diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_logs_query_client_async.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_logs_query_client_async.py index deb3ba2a99f9..2b883a92dc94 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_logs_query_client_async.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_logs_query_client_async.py @@ -45,7 +45,7 @@ async def query( self, workspace_id: str, query: str, - timespan: Optional[Union[timedelta, Tuple[datetime, timedelta], Tuple[datetime, datetime]]] = None, + timespan: Union[timedelta, Tuple[datetime, timedelta], Tuple[datetime, datetime]], **kwargs: Any) -> LogsQueryResult: """Execute an Analytics query. @@ -123,7 +123,7 @@ async def query_batch( :param queries: The list of queries that should be processed :type queries: list[dict] or list[~azure.monitor.query.LogsBatchQuery] :return: list of LogsBatchQueryResult objects, or the result of cls(response) - :rtype: ~list[~azure.monitor.query.LogsBatchQueryResult] + :rtype: list[~azure.monitor.query.LogsBatchQueryResult] :raises: ~azure.core.exceptions.HttpResponseError """ try: From a299a10e229a5dfb05e75e53a2be181fc983fcc7 Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Mon, 23 Aug 2021 14:01:05 -0700 Subject: [PATCH 02/11] lc-2 --- .../azure/monitor/query/_logs_query_client.py | 4 ++-- .../azure/monitor/query/aio/_logs_query_client_async.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/_logs_query_client.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/_logs_query_client.py index 545a6cc69b2a..854e997b6227 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/_logs_query_client.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/_logs_query_client.py @@ -5,7 +5,7 @@ # license information. # -------------------------------------------------------------------------- -from typing import TYPE_CHECKING, Any, Union, Sequence, Dict +from typing import TYPE_CHECKING, Any, Union, Sequence, Dict, List from azure.core.exceptions import HttpResponseError from azure.core.tracing.decorator import distributed_trace @@ -126,7 +126,7 @@ def query(self, workspace_id, query, timespan, **kwargs): @distributed_trace def query_batch(self, queries, **kwargs): - # type: (Union[Sequence[Dict], Sequence[LogsBatchQuery]], Any) -> Sequence[LogsBatchQueryResult] + # type: (Union[Sequence[Dict], Sequence[LogsBatchQuery]], Any) -> List[LogsBatchQueryResult] """Execute a list of analytics queries. Each request can be either a LogQueryRequest object or an equivalent serialized model. diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_logs_query_client_async.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_logs_query_client_async.py index 2b883a92dc94..c97ea8d04b62 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_logs_query_client_async.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_logs_query_client_async.py @@ -6,7 +6,7 @@ # -------------------------------------------------------------------------- from datetime import datetime, timedelta -from typing import Any, Tuple, Union, Sequence, Dict, Optional, TYPE_CHECKING +from typing import Any, Tuple, Union, Sequence, Dict, List, TYPE_CHECKING from azure.core.exceptions import HttpResponseError from azure.core.tracing.decorator_async import distributed_trace_async @@ -114,7 +114,7 @@ async def query_batch( self, queries: Union[Sequence[Dict], Sequence[LogsBatchQuery]], **kwargs: Any - ) -> Sequence[LogsBatchQueryResult]: + ) -> List[LogsBatchQueryResult]: """Execute a list of analytics queries. Each request can be either a LogQueryRequest object or an equivalent serialized model. From 62d24fc95ca2052520e0d77d7fed895b40df47b4 Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Mon, 23 Aug 2021 14:45:34 -0700 Subject: [PATCH 03/11] models changes --- .../azure/monitor/query/_models.py | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py index 2b3a33672ec6..a7023ae0232e 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py @@ -21,18 +21,19 @@ class LogsTable(object): All required parameters must be populated in order to send to Azure. - :param name: Required. The name of the table. - :type name: str - :param columns: Required. The list of columns in this table. - :type columns: list[~azure.monitor.query.LogsTableColumn] - :param rows: Required. The resulting rows from this query. - :type rows: list[list[str]] + :ivar name: Required. The name of the table. + :vartype name: str + :ivar columns: Required. The list of columns in this table. + :vartype columns: list[~azure.monitor.query.LogsTableColumn] + :ivar rows: Required. The resulting rows from this query. + :vartype rows: list[list[str]] """ - def __init__(self, name, columns, rows): - # type: (str, List[LogsTableColumn], List[List[str]]) -> None - self.name = name - self.columns = columns - self.rows = [process_row(self.columns, row) for row in rows] + def __init__(self, **kwargs): + # type: (Any) -> None + self.name = kwargs.pop('name', None) # type: str + self.columns = kwargs.pop('columns', None) # type: Optional[LogsTableColumn] + _rows = kwargs.pop('rows', None) + self.rows = [process_row(self.columns, row) for row in _rows] @classmethod def _from_generated(cls, generated): @@ -76,7 +77,7 @@ class LogsQueryResult(object): visualization selected by the query and any properties for that visualization. :vartype visualization: object :ivar error: Any error info. - :vartype error: object + :vartype error: ~azure.core.exceptions.HttpResponseError """ def __init__(self, **kwargs): # type: (Any) -> None @@ -530,12 +531,12 @@ class MetricAvailability(object): """Metric availability specifies the time grain (aggregation interval or frequency) and the retention period for that time grain. - :keyword granularity: the time grain specifies the aggregation interval for the metric. Expressed + :ivar granularity: the time grain specifies the aggregation interval for the metric. Expressed as a duration 'PT1M', 'P1D', etc. - :paramtype granularity: ~datetime.timedelta - :keyword retention: the retention period for the metric at the specified timegrain. Expressed as + :vartype granularity: ~datetime.timedelta + :ivar retention: the retention period for the metric at the specified timegrain. Expressed as a duration 'PT1M', 'P1D', etc. - :paramtype retention: ~datetime.timedelta + :vartype retention: ~datetime.timedelta """ def __init__( self, From 1f69843d0e5170f546fbbff0f22b3dafc054fa8e Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Mon, 23 Aug 2021 19:09:19 -0700 Subject: [PATCH 04/11] more changes --- .../monitor/query/_metrics_query_client.py | 15 +- .../azure/monitor/query/_models.py | 20 +- .../query/aio/_metrics_query_client_async.py | 16 +- .../samples/champion_scenarios.md | 288 ++++++++++++++++++ .../samples/sample_batch_query.py | 11 +- .../samples/sample_log_query_client.py | 10 +- .../samples/sample_metrics_query_client.py | 22 +- 7 files changed, 329 insertions(+), 53 deletions(-) create mode 100644 sdk/monitor/azure-monitor-query/samples/champion_scenarios.md diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/_metrics_query_client.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/_metrics_query_client.py index aae99348a479..12b4daea5107 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/_metrics_query_client.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/_metrics_query_client.py @@ -9,6 +9,7 @@ from typing import TYPE_CHECKING, Any, Optional from azure.core.tracing.decorator import distributed_trace +from msrest.serialization import Serializer from ._generated._monitor_query_client import ( MonitorQueryClient, @@ -59,9 +60,6 @@ def query(self, resource_uri, metric_names, **kwargs): # type: (str, list, Optional[timedelta], Any) -> MetricsResult """Lists the metric values for a resource. - **Note**: Although the start_time, end_time, duration are optional parameters, it is highly - recommended to specify the timespan. If not, the entire dataset is queried. - :param resource_uri: The identifier of the resource. :type resource_uri: str :param metric_names: The names of the metrics to retrieve. @@ -93,9 +91,6 @@ def query(self, resource_uri, metric_names, **kwargs): ‘c1’**\ :code:`
`- Return all time series where A = a1:code:`
`\ **$filter=A eq ‘a1’ and B eq ‘\ *’ and C eq ‘*\ ’**. :paramtype filter: str - :keyword result_type: Reduces the set of data collected. The syntax allowed depends on the - operation. See the operation's description for details. - :paramtype result_type: str or ~monitor_query_client.models.ResultType :keyword metric_namespace: Metric namespace to query metric definitions for. :paramtype metric_namespace: str :return: Response, or the result of cls(response) @@ -131,13 +126,15 @@ def list_metric_namespaces(self, resource_uri, **kwargs): :param resource_uri: The identifier of the resource. :type resource_uri: str - :keyword start_time: The ISO 8601 conform Date start time from which to query for metric - namespaces. - :paramtype start_time: str + :keyword start_time: The start time from which to query for metric + namespaces. This should be provided as a datetime object. + :paramtype start_time: ~datetime.datetime :return: An iterator like instance of either MetricNamespace or the result of cls(response) :rtype: ~azure.core.paging.ItemPaged[~azure.monitor.query.MetricNamespace] :raises: ~azure.core.exceptions.HttpResponseError """ + start = Serializer.serialize_iso(kwargs.pop('start_time')) + kwargs.setdefault('start_time', start) return self._namespace_op.list( resource_uri, cls=kwargs.pop( diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py index a7023ae0232e..fb45517c210e 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py @@ -277,16 +277,16 @@ class MetricNamespaceClassification(str, Enum): class MetricNamespace(object): """Metric namespace class specifies the metadata for a metric namespace. - :keyword id: The ID of the metricNamespace. - :paramtype id: str - :keyword type: The type of the namespace. - :paramtype type: str - :keyword name: The name of the namespace. - :paramtype name: str - :keyword fully_qualified_namespace: The fully qualified namespace name. - :paramtype fully_qualified_namespace: str - :keyword namespace_classification: Kind of namespace. Possible values include: "Platform", "Custom", "Qos". - :paramtype namespace_classification: str or ~azure.monitor.query.MetricNamespaceClassification + :ivar id: The ID of the metricNamespace. + :vartype id: str + :ivar type: The type of the namespace. + :vartype type: str + :ivar name: The name of the namespace. + :vartype name: str + :ivar fully_qualified_namespace: The fully qualified namespace name. + :vartype fully_qualified_namespace: str + :ivar namespace_classification: Kind of namespace. Possible values include: "Platform", "Custom", "Qos". + :vartype namespace_classification: str or ~azure.monitor.query.MetricNamespaceClassification """ def __init__( self, diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_metrics_query_client_async.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_metrics_query_client_async.py index dfdc2906f38b..4ca6925298eb 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_metrics_query_client_async.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_metrics_query_client_async.py @@ -13,6 +13,7 @@ from azure.core.async_paging import AsyncItemPaged from azure.core.tracing.decorator import distributed_trace from azure.core.tracing.decorator_async import distributed_trace_async +from msrest.serialization import Serializer from .._generated.aio._monitor_query_client import ( MonitorQueryClient, @@ -88,9 +89,6 @@ async def query( ‘c1’**\ :code:`
`- Return all time series where A = a1:code:`
`\ **$filter=A eq ‘a1’ and B eq ‘\ *’ and C eq ‘*\ ’**. :paramtype filter: str - :keyword result_type: Reduces the set of data collected. The syntax allowed depends on the - operation. See the operation's description for details. - :paramtype result_type: str or ~monitor_query_client.models.ResultType :keyword metric_namespace: Metric namespace to query metric definitions for. :paramtype metric_namespace: str :return: Response, or the result of cls(response) @@ -115,13 +113,15 @@ def list_metric_namespaces(self, resource_uri: str, **kwargs: Any) -> AsyncItemP :param resource_uri: The identifier of the resource. :type resource_uri: str - :keyword start_time: The ISO 8601 conform Date start time from which to query for metric - namespaces. - :paramtype start_time: str + :keyword start_time: The start time from which to query for metric + namespaces. This should be provided as a datetime object. + :paramtype start_time: ~datetime.datetime :return: An iterator like instance of either MetricNamespace or the result of cls(response) - :rtype: ~azure.core.paging.ItemPaged[~azure.monitor.query.MetricNamespace] + :rtype: ~azure.core.paging.AsyncItemPaged[:class: `~azure.monitor.query.MetricNamespace`] :raises: ~azure.core.exceptions.HttpResponseError """ + start = Serializer.serialize_iso(kwargs.pop('start_time')) + kwargs.setdefault('start_time', start) return self._namespace_op.list( resource_uri, cls=kwargs.pop( @@ -146,7 +146,7 @@ def list_metric_definitions( :param metric_namespace: Metric namespace to query metric definitions for. :type metric_namespace: str :return: An iterator like instance of either MetricDefinitionCollection or the result of cls(response) - :rtype: ~azure.core.paging.ItemPaged[~azure.monitor.query.MetricDefinition] + :rtype: ~azure.core.paging.AsyncItemPaged[:class: `~azure.monitor.query.MetricDefinition`] :raises: ~azure.core.exceptions.HttpResponseError """ return self._definitions_op.list( diff --git a/sdk/monitor/azure-monitor-query/samples/champion_scenarios.md b/sdk/monitor/azure-monitor-query/samples/champion_scenarios.md new file mode 100644 index 000000000000..367f568e0c8a --- /dev/null +++ b/sdk/monitor/azure-monitor-query/samples/champion_scenarios.md @@ -0,0 +1,288 @@ +## Azure Monitor Query Champion Scenarios + +This document covers the basic champion Scenarios to use the package. + +### Authenticate the client + +Consider the following example, which creates and authenticates clients for both logs and metrics querying: + +```python +from azure.identity import DefaultAzureCredential +from azure.monitor.query import LogsQueryClient, MetricsQueryClient + +credential = DefaultAzureCredential() +logs_client = LogsQueryClient(credential) +metrics_client = MetricsQueryClient(credential) +``` + +### Make a simple query to the service + +* Each row is converted into a native python data type. For example, time is a datetime object instead of string. + +#### Results in tabular form + +```python +import os +import pandas as pd +from datetime import timedelta +from azure.monitor.query import LogsQueryClient +from azure.identity import DefaultAzureCredential + +def query(): + query = """AppRequests | + summarize avgRequestDuration=avg(DurationMs) by bin(TimeGenerated, 10m), _ResourceId""" + + response = client.query(os.environ['LOG_WORKSPACE_ID'], query, + timespan=timedelta(days=1)) + + if not response.tables: + return None + + primary_table = response.tables[0] + df = pd.DataFrame(table.rows, columns=[col.name for col in table.columns]) + return df + +if __name__ == '__main__': + print(query()) + +""" + TimeGenerated _ResourceId avgRequestDuration +0 2021-05-27T08:40:00Z /subscriptions/... 27.307699999999997 +1 2021-05-27T08:50:00Z /subscriptions/... 18.11655 +2 2021-05-27T09:00:00Z /subscriptions/... 24.5271 +""" + +``` + +#### Results in Key Value form + +```python +import os +import pandas as pd +from datetime import timedelta +from azure.monitor.query import LogsQueryClient +from azure.identity import DefaultAzureCredential + +def query(): + query = """AppRequests | + summarize avgRequestDuration=avg(DurationMs) by bin(TimeGenerated, 10m), _ResourceId""" + + response = client.query(os.environ['LOG_WORKSPACE_ID'], query, + timespan=timedelta(days=1)) + + if not response.tables: + return None + + primary_table = response.tables[0] + df = pd.DataFrame(table.rows, columns=[col.name for col in table.columns]) + return df.to_dict(orient='records') + +if __name__ == '__main__': + print(query()) + + +""" +[ + { + 'TimeGenerated': Timestamp('2021-08-24 01:10:00+0000'), + '_ResourceId': '/subscriptions/faa080af....', + 'avgRequestDuration': 19.7987 + }, + { + 'TimeGenerated': Timestamp('2021-08-24 01:10:00+0000'), + '_ResourceId': '/subscriptions/faa08....', + 'avgRequestDuration': 33.9654 + }, + { + 'TimeGenerated': Timestamp('2021-08-24 01:10:00+0000'), + '_ResourceId': '/subscriptions/faa080....', + 'avgRequestDuration': 44.13115 + } +] +""" + +``` + +### Run multiple queries in 1 api call + +* batch_query returns the results as a list in the same order in which the requests were sent. +* Each item in the result will have an error attribute if there is an error. + +#### Results in tabular form + +```python +from datetime import datetime, timedelta +import os +import pandas as pd +from azure.monitor.query import LogsQueryClient, LogsBatchQuery +from azure.identity import DefaultAzureCredential + + +credential = DefaultAzureCredential() + +client = LogsQueryClient(credential) + +requests = [ + LogsBatchQuery( + query="AzureActivity | summarize count()", + timespan=timedelta(hours=1), + workspace_id= os.environ['LOG_WORKSPACE_ID'] + ), + LogsBatchQuery( + query= """AppRequests | take 5 | + summarize avgRequestDuration=avg(DurationMs) by bin(TimeGenerated, 10m), _ResourceId""", + timespan=(datetime(2021, 6, 2), timedelta(hours=1)), + workspace_id= os.environ['LOG_WORKSPACE_ID'] + ), + LogsBatchQuery( + query= """AppRequests | take 5 | + summarize avgRequestDuration=avg(DurationMs) by bin(TimeGenerated, 10m), _ResourceId""", + workspace_id= os.environ['LOG_WORKSPACE_ID'], + timespan=(datetime(2021, 6, 2), datetime(2021, 6, 3)), + include_statistics=True + ), +] +results = client.query_batch(requests) + +for response in results: + if response.error is not None: + error = response.error.innererror + print(error) + + table = response.tables[0] + df = pd.DataFrame(table.rows, columns=[col.name for col in table.columns]) + print(df) + print("\n\n-------------------------\n\n") + +""" + count_ +0 2 + + +------------------------- + + + TimeGenerated _ResourceId avgRequestDuration +0 2021-06-02 00:20:00+00:00 /subscriptions/... 18.12380 +1 2021-06-02 00:00:00+00:00 /subscriptions/... 20.84805 +2 2021-06-02 00:10:00+00:00 /subscriptions/... 19.72410 +3 2021-06-02 00:30:00+00:00 /subscriptions/... 19.41265 +4 2021-06-02 00:40:00+00:00 /subscriptions/... 19.17145 + + +------------------------- + + + + TimeGenerated _ResourceId avgRequestDuration +0 2021-06-02 00:20:00+00:00 /subscriptions/... 18.12380 +1 2021-06-02 00:00:00+00:00 /subscriptions/... 20.84805 +2 2021-06-02 00:10:00+00:00 /subscriptions/... 19.72410 +3 2021-06-02 00:30:00+00:00 /subscriptions/... 19.41265 +4 2021-06-02 00:40:00+00:00 /subscriptions/... 19.17145 + + + +------------------------- +""" +``` + +#### Results in Key Value form + + +Very Simlar to above: + +```python +for response in results: + if response.error is not None: + error = response.error.innererror + print(error) + + table = response.tables[0] + df = pd.DataFrame(table.rows, columns=[col.name for col in table.columns]) + print(df.to_dict(orient='records')) + print("\n\n-------------------------\n\n") +``` + +### Run a complex query to set server timeout for more than 3 minutes. + +```python +import os +import pandas as pd +from azure.core.serialization import NULL +from azure.monitor.query import LogsQueryClient +from azure.identity import DefaultAzureCredential + + +credential = DefaultAzureCredential() + +client = LogsQueryClient(credential) + +response = client.query( + os.environ['LOG_WORKSPACE_ID'], + "range x from 1 to 10000000000 step 1 | count", + timespan=NULL, # can pass None too + server_timeout=600 + ) + +### results in server timeout +``` + +### Run a metrics Query + +```python +import os +from datetime import timedelta +from azure.monitor.query import MetricsQueryClient, MetricAggregationType +from azure.identity import DefaultAzureCredential + +credential = DefaultAzureCredential() + +client = MetricsQueryClient(credential) + +metrics_uri = os.environ['METRICS_RESOURCE_URI'] +response = client.query( + metrics_uri, + metric_names=["Ingress"], + timespan=timedelta(hours=2), + granularity=timedelta(minutes=5), + aggregations=[MetricAggregationType.AVERAGE], + ) + +for metric in response.metrics: + print(metric.name + ' -- ' + metric.display_description) + for time_series_element in metric.timeseries: + for metric_value in time_series_element.data: + print('The ingress at {} is {}'.format( + metric_value.timestamp, + metric_value.average + )) + +""" +Ingress -- The amount of ingress data, in bytes. This number includes ingress from an external client into Azure Storage as well as ingress within Azure. +The ingress at 2021-08-23 23:58:00+00:00 is 567.4285714285714 +The ingress at 2021-08-24 00:03:00+00:00 is 812.0 +The ingress at 2021-08-24 00:08:00+00:00 is 812.0 +The ingress at 2021-08-24 00:13:00+00:00 is 812.0 +The ingress at 2021-08-24 00:18:00+00:00 is 812.0 +The ingress at 2021-08-24 00:23:00+00:00 is 3623.3333333333335 +The ingress at 2021-08-24 00:28:00+00:00 is 1082.75 +The ingress at 2021-08-24 00:33:00+00:00 is 1160.6666666666667 +The ingress at 2021-08-24 00:38:00+00:00 is 1060.75 +The ingress at 2021-08-24 00:43:00+00:00 is 1081.75 +The ingress at 2021-08-24 00:48:00+00:00 is 1061.25 +The ingress at 2021-08-24 00:53:00+00:00 is 1160.3333333333333 +The ingress at 2021-08-24 00:58:00+00:00 is 1082.0 +The ingress at 2021-08-24 01:03:00+00:00 is 1628.6666666666667 +The ingress at 2021-08-24 01:08:00+00:00 is 794.6666666666666 +The ingress at 2021-08-24 01:13:00+00:00 is 1060.25 +The ingress at 2021-08-24 01:18:00+00:00 is 1160.0 +The ingress at 2021-08-24 01:23:00+00:00 is 1082.0 +The ingress at 2021-08-24 01:28:00+00:00 is 1060.5 +The ingress at 2021-08-24 01:33:00+00:00 is 1630.0 +The ingress at 2021-08-24 01:38:00+00:00 is 795.0 +The ingress at 2021-08-24 01:43:00+00:00 is 827.6 +The ingress at 2021-08-24 01:48:00+00:00 is 1250.5 +The ingress at 2021-08-24 01:53:00+00:00 is 1061.75 +""" +``` \ No newline at end of file diff --git a/sdk/monitor/azure-monitor-query/samples/sample_batch_query.py b/sdk/monitor/azure-monitor-query/samples/sample_batch_query.py index c7e1ec025b93..3705d94cf494 100644 --- a/sdk/monitor/azure-monitor-query/samples/sample_batch_query.py +++ b/sdk/monitor/azure-monitor-query/samples/sample_batch_query.py @@ -16,19 +16,19 @@ requests = [ LogsBatchQuery( query="AzureActivity | summarize count()", - duration=timedelta(hours=1), + timespan=timedelta(hours=1), workspace_id= os.environ['LOG_WORKSPACE_ID'] ), LogsBatchQuery( query= """AppRequests | take 10 | summarize avgRequestDuration=avg(DurationMs) by bin(TimeGenerated, 10m), _ResourceId""", - duration=timedelta(hours=1), - start_time=datetime(2021, 6, 2), + timespan=(datetime(2021, 6, 2), timedelta(hours=1)), workspace_id= os.environ['LOG_WORKSPACE_ID'] ), LogsBatchQuery( - query= "AppRequestss | take 5", + query= "AppRequests | take 5", workspace_id= os.environ['LOG_WORKSPACE_ID'], + timespan=(datetime(2021, 6, 2), datetime(2021, 6, 3)), include_statistics=True ), ] @@ -39,7 +39,8 @@ table = response.tables[0] df = pd.DataFrame(table.rows, columns=[col.name for col in table.columns]) print(df) + print("\n\n-------------------------\n\n") except TypeError: - print(response.error) + print(response.error.innererror) # [END send_query_batch] \ No newline at end of file diff --git a/sdk/monitor/azure-monitor-query/samples/sample_log_query_client.py b/sdk/monitor/azure-monitor-query/samples/sample_log_query_client.py index fd2d13a4b891..8ce26f8a6b28 100644 --- a/sdk/monitor/azure-monitor-query/samples/sample_log_query_client.py +++ b/sdk/monitor/azure-monitor-query/samples/sample_log_query_client.py @@ -3,8 +3,7 @@ import os import pandas as pd -from datetime import datetime, timedelta -from msrest.serialization import UTC +from datetime import timedelta from azure.monitor.query import LogsQueryClient from azure.identity import DefaultAzureCredential @@ -20,13 +19,6 @@ query = """AppRequests | summarize avgRequestDuration=avg(DurationMs) by bin(TimeGenerated, 10m), _ResourceId""" -query = """ -AppRequests -| where TimeGenerated > ago(1h) -| fork - ( summarize avgRequestDuration=avg(DurationMs) by bin(TimeGenerated, 10m), _ResourceId ) -""" - # returns LogsQueryResult response = client.query(os.environ['LOG_WORKSPACE_ID'], query, timespan=timedelta(days=1)) diff --git a/sdk/monitor/azure-monitor-query/samples/sample_metrics_query_client.py b/sdk/monitor/azure-monitor-query/samples/sample_metrics_query_client.py index 224d7f05e8ea..4027ddd1b308 100644 --- a/sdk/monitor/azure-monitor-query/samples/sample_metrics_query_client.py +++ b/sdk/monitor/azure-monitor-query/samples/sample_metrics_query_client.py @@ -7,7 +7,7 @@ from azure.monitor.query import MetricsQueryClient, MetricAggregationType from azure.identity import DefaultAzureCredential -urllib3.disable_warnings() +# urllib3.disable_warnings() # [START metrics_client_auth_with_token_cred] credential = DefaultAzureCredential() @@ -19,20 +19,18 @@ metrics_uri = os.environ['METRICS_RESOURCE_URI'] response = client.query( metrics_uri, - metric_names=["MatchedEventCount", "DeliverySuccesssCount"], - timespan=timedelta(days=1), - aggregations=[MetricAggregationType.COUNT] + metric_names=["Ingress"], + timespan=timedelta(hours=2), + granularity=timedelta(minutes=5), + aggregations=[MetricAggregationType.AVERAGE], ) for metric in response.metrics: - print(metric.name) + print(metric.name + ' -- ' + metric.display_description) for time_series_element in metric.timeseries: for metric_value in time_series_element.data: - if metric_value.count != 0: - print( - "There are {} matched events at {}".format( - metric_value.count, - metric_value.time_stamp - ) - ) + print('The ingress at {} is {}'.format( + metric_value.timestamp, + metric_value.average + )) # [END send_metrics_query] From 49ea2fba223851d5886a3d6306e4dbc7a3e47802 Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Mon, 23 Aug 2021 19:25:40 -0700 Subject: [PATCH 05/11] more changes --- .../azure/monitor/query/__init__.py | 2 + .../azure/monitor/query/_models.py | 71 ++++++++++++------- 2 files changed, 47 insertions(+), 26 deletions(-) diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/__init__.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/__init__.py index 386aec140be1..69379f05dcb4 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/__init__.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/__init__.py @@ -18,6 +18,7 @@ MetricNamespace, MetricNamespaceClassification, MetricDefinition, + MetricUnit, TimeSeriesElement, Metric, MetricValue, @@ -39,6 +40,7 @@ "MetricNamespace", "MetricNamespaceClassification", "MetricDefinition", + "MetricUnit", "MetricsResult", "TimeSeriesElement", "Metric", diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py index fb45517c210e..eacc19dcdd10 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py @@ -125,7 +125,7 @@ class MetricsResult(object): :ivar resource_region: The region of the resource that has been queried for metrics. :vartype resource_region: str :ivar metrics: Required. The value of the collection. - :vartype metrics: list[~monitor_query_client.models.Metric] + :vartype metrics: list[~azure.monitor.query.Metric] """ def __init__(self, **kwargs): # type: (Any) -> None @@ -231,7 +231,7 @@ class LogsBatchQueryResult(object): visualization selected by the query and any properties for that visualization. :vartype visualization: object :ivar error: Any error info. - :vartype error: object + :vartype error: ~azure.core.exceptions.HttpResponseError """ def __init__( self, @@ -328,35 +328,35 @@ class MetricClass(str, Enum): class MetricDefinition(object): #pylint: disable=too-many-instance-attributes """Metric definition class specifies the metadata for a metric. - :keyword dimension_required: Flag to indicate whether the dimension is required. - :paramtype dimension_required: bool - :keyword resource_id: the resource identifier of the resource that emitted the metric. - :paramtype resource_id: str - :keyword namespace: the namespace the metric belongs to. - :paramtype namespace: str - :keyword name: the name and the display name of the metric, i.e. it is a localizable string. - :paramtype name: str - :keyword unit: the unit of the metric. Possible values include: "Count", "Bytes", "Seconds", + :ivar dimension_required: Flag to indicate whether the dimension is required. + :vartype dimension_required: bool + :ivar resource_id: the resource identifier of the resource that emitted the metric. + :vartype resource_id: str + :ivar namespace: the namespace the metric belongs to. + :vartype namespace: str + :ivar name: the name and the display name of the metric, i.e. it is a localizable string. + :vartype name: str + :ivar unit: the unit of the metric. Possible values include: "Count", "Bytes", "Seconds", "CountPerSecond", "BytesPerSecond", "Percent", "MilliSeconds", "ByteSeconds", "Unspecified", "Cores", "MilliCores", "NanoCores", "BitsPerSecond". - :paramtype unit: str or ~monitor_query_client.models.Unit - :keyword primary_aggregation_type: the primary aggregation type value defining how to use the + :vartype unit: str or ~azure.monitor.query.MetricUnit + :ivar primary_aggregation_type: the primary aggregation type value defining how to use the values for display. Possible values include: "None", "Average", "Count", "Minimum", "Maximum", "Total". - :paramtype primary_aggregation_type: str or ~azure.monitor.query.MetricAggregationType - :keyword metric_class: The class of the metric. Possible values include: "Availability", + :vartype primary_aggregation_type: str or ~azure.monitor.query.MetricAggregationType + :ivar metric_class: The class of the metric. Possible values include: "Availability", "Transactions", "Errors", "Latency", "Saturation". - :paramtype metric_class: str or ~azure.monitor.query.MetricClass - :keyword supported_aggregation_types: the collection of what aggregation types are supported. - :paramtype supported_aggregation_types: list[str or ~azure.monitor.query.MetricAggregationType] - :keyword metric_availabilities: the collection of what aggregation intervals are available to be + :vartype metric_class: str or ~azure.monitor.query.MetricClass + :ivar supported_aggregation_types: the collection of what aggregation types are supported. + :vartype supported_aggregation_types: list[str or ~azure.monitor.query.MetricAggregationType] + :ivar metric_availabilities: the collection of what aggregation intervals are available to be queried. - :paramtype metric_availabilities: list[~azure.monitor.query.MetricAvailability] - :keyword id: the resource identifier of the metric definition. - :paramtype id: str - :keyword dimensions: the name and the display name of the dimension, i.e. it is a localizable + :vartype metric_availabilities: list[~azure.monitor.query.MetricAvailability] + :ivar id: the resource identifier of the metric definition. + :vartype id: str + :ivar dimensions: the name and the display name of the dimension, i.e. it is a localizable string. - :paramtype dimensions: list[str] + :vartype dimensions: list[str] """ def __init__( self, @@ -460,7 +460,7 @@ class Metric(object): "Unspecified", "Cores", "MilliCores", "NanoCores", "BitsPerSecond". :vartype unit: str :ivar timeseries: Required. The time series returned when a data query is performed. - :vartype timeseries: list[~monitor_query_client.models.TimeSeriesElement] + :vartype timeseries: list[~azure.monitor.query.TimeSeriesElement] :ivar display_description: Detailed description of this metric. :vartype display_description: str """ @@ -499,7 +499,7 @@ class TimeSeriesElement(object): :vartype metadata_values: dict(str, str) :ivar data: An array of data points representing the metric values. This is only returned if a result type of data is specified. - :vartype data: list[~monitor_query_client.models.MetricValue] + :vartype data: list[~azure.monitor.query.MetricValue] """ _attribute_map = { @@ -566,3 +566,22 @@ class MetricAggregationType(str, Enum): MINIMUM = "Minimum" MAXIMUM = "Maximum" TOTAL = "Total" + + +class MetricUnit(str, Enum): + """The unit of the metric. + """ + + COUNT = "Count" + BYTES = "Bytes" + SECONDS = "Seconds" + COUNT_PER_SECOND = "CountPerSecond" + BYTES_PER_SECOND = "BytesPerSecond" + PERCENT = "Percent" + MILLI_SECONDS = "MilliSeconds" + BYTE_SECONDS = "ByteSeconds" + UNSPECIFIED = "Unspecified" + CORES = "Cores" + MILLI_CORES = "MilliCores" + NANO_CORES = "NanoCores" + BITS_PER_SECOND = "BitsPerSecond" From 6407c1ff28eca4a75171b96f4fd0d7aa066f1260 Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Tue, 24 Aug 2021 11:38:22 -0700 Subject: [PATCH 06/11] more arch changes --- .../azure/monitor/query/__init__.py | 2 - .../azure/monitor/query/_helpers.py | 2 +- .../monitor/query/_metrics_query_client.py | 2 +- .../azure/monitor/query/_models.py | 47 +++++-------------- .../query/aio/_metrics_query_client_async.py | 2 +- .../sample_metrics_query_client_async.py | 17 +++---- .../samples/champion_scenarios.md | 8 ++-- .../samples/sample_log_query_client.py | 3 +- 8 files changed, 27 insertions(+), 56 deletions(-) diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/__init__.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/__init__.py index 69379f05dcb4..d201fbff31bb 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/__init__.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/__init__.py @@ -12,7 +12,6 @@ LogsBatchQueryResult, LogsQueryResult, LogsTable, - LogsTableColumn, MetricsResult, LogsBatchQuery, MetricNamespace, @@ -33,7 +32,6 @@ "LogsQueryClient", "LogsBatchQueryResult", "LogsQueryResult", - "LogsTableColumn", "LogsTable", "LogsBatchQuery", "MetricsQueryClient", diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/_helpers.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/_helpers.py index 205239d1816f..cda750cd845e 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/_helpers.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/_helpers.py @@ -90,4 +90,4 @@ def native_col_type(col_type, value): return value def process_row(col_types, row): - return [native_col_type(col_types[ind].type, val) for ind, val in enumerate(row)] + return [native_col_type(col_types[ind], val) for ind, val in enumerate(row)] diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/_metrics_query_client.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/_metrics_query_client.py index 12b4daea5107..38ecb1c9847d 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/_metrics_query_client.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/_metrics_query_client.py @@ -133,7 +133,7 @@ def list_metric_namespaces(self, resource_uri, **kwargs): :rtype: ~azure.core.paging.ItemPaged[~azure.monitor.query.MetricNamespace] :raises: ~azure.core.exceptions.HttpResponseError """ - start = Serializer.serialize_iso(kwargs.pop('start_time')) + start = Serializer.serialize_iso(kwargs.pop('start_time', None)) kwargs.setdefault('start_time', start) return self._namespace_op.list( resource_uri, diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py index eacc19dcdd10..6bf3135696ed 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py @@ -23,48 +23,31 @@ class LogsTable(object): :ivar name: Required. The name of the table. :vartype name: str - :ivar columns: Required. The list of columns in this table. - :vartype columns: list[~azure.monitor.query.LogsTableColumn] + :ivar columns: The labels of columns in this table. + :vartype columns: list[str] + :ivar column_types: The types of columns in this table. + :vartype columns: list[object] :ivar rows: Required. The resulting rows from this query. - :vartype rows: list[list[str]] + :vartype rows: list[list[object]] """ def __init__(self, **kwargs): # type: (Any) -> None self.name = kwargs.pop('name', None) # type: str - self.columns = kwargs.pop('columns', None) # type: Optional[LogsTableColumn] + self.columns = kwargs.pop('columns', None) # type: Optional[str] + self.columns_types = kwargs.pop('column_types', None) # type: Optional[Any] _rows = kwargs.pop('rows', None) - self.rows = [process_row(self.columns, row) for row in _rows] + self.rows = [process_row(self.columns_types, row) for row in _rows] @classmethod def _from_generated(cls, generated): return cls( name=generated.name, - columns=[LogsTableColumn(name=col.name, type=col.type) for col in generated.columns], + columns=[col.name for col in generated.columns], + column_types=[col.type for col in generated.columns], rows=generated.rows ) -class LogsTableColumn(InternalColumn): - """A column in a table. - - :ivar name: The name of this column. - :vartype name: str - :ivar type: The data type of this column. - :vartype type: str - """ - - _attribute_map = { - "name": {"key": "name", "type": "str"}, - "type": {"key": "type", "type": "str"}, - } - - def __init__(self, **kwargs): - # type: (Any) -> None - super(LogsTableColumn, self).__init__(**kwargs) - self.name = kwargs.get("name", None) - self.type = kwargs.get("type", None) - - class LogsQueryResult(object): """Contains the tables, columns & rows resulting from a query. @@ -172,11 +155,9 @@ class LogsBatchQuery(object): :keyword bool include_visualization: In the query language, it is possible to specify different visualization options. By default, the API does not return information regarding the type of visualization to show. - :keyword headers: Dictionary of :code:``. - :paramtype headers: dict[str, str] """ - def __init__(self, query, workspace_id, timespan, **kwargs): #pylint: disable=super-init-not-called + def __init__(self, workspace_id, query, timespan, **kwargs): #pylint: disable=super-init-not-called # type: (str, str, Optional[str], Any) -> None include_statistics = kwargs.pop("include_statistics", False) include_visualization = kwargs.pop("include_visualization", False) @@ -193,11 +174,7 @@ def __init__(self, query, workspace_id, timespan, **kwargs): #pylint: disable=su prefer += "," prefer += "include-render=true" - headers = kwargs.get("headers", None) - try: - headers['Prefer'] = prefer - except TypeError: - headers = {'Prefer': prefer} + headers = {'Prefer': prefer} timespan = construct_iso8601(timespan) additional_workspaces = kwargs.pop("additional_workspaces", None) self.id = str(uuid.uuid4()) diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_metrics_query_client_async.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_metrics_query_client_async.py index 4ca6925298eb..06e4533cd2f4 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_metrics_query_client_async.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_metrics_query_client_async.py @@ -120,7 +120,7 @@ def list_metric_namespaces(self, resource_uri: str, **kwargs: Any) -> AsyncItemP :rtype: ~azure.core.paging.AsyncItemPaged[:class: `~azure.monitor.query.MetricNamespace`] :raises: ~azure.core.exceptions.HttpResponseError """ - start = Serializer.serialize_iso(kwargs.pop('start_time')) + start = Serializer.serialize_iso(kwargs.pop('start_time', None)) kwargs.setdefault('start_time', start) return self._namespace_op.list( resource_uri, diff --git a/sdk/monitor/azure-monitor-query/samples/async_samples/sample_metrics_query_client_async.py b/sdk/monitor/azure-monitor-query/samples/async_samples/sample_metrics_query_client_async.py index c4578b06747c..4b52d4186807 100644 --- a/sdk/monitor/azure-monitor-query/samples/async_samples/sample_metrics_query_client_async.py +++ b/sdk/monitor/azure-monitor-query/samples/async_samples/sample_metrics_query_client_async.py @@ -4,17 +4,13 @@ import os import asyncio from datetime import datetime, timedelta -import urllib3 from azure.monitor.query.aio import MetricsQueryClient +from azure.monitor.query import MetricAggregationType from azure.identity.aio import DefaultAzureCredential -urllib3.disable_warnings() - async def query_metrics(): credential = DefaultAzureCredential( - client_id = os.environ['AZURE_CLIENT_ID'], - client_secret = os.environ['AZURE_CLIENT_SECRET'], - tenant_id = os.environ['AZURE_TENANT_ID'] + ) client = MetricsQueryClient(credential) @@ -23,16 +19,17 @@ async def query_metrics(): async with client: response = await client.query( metrics_uri, - metric_names=["PublishSuccessCount"], - start_time=datetime(2021, 5, 25), - duration=timedelta(days=1) + metric_names=["Ingress"], + timespan=timedelta(hours=2), + granularity=timedelta(minutes=15), + aggregations=[MetricAggregationType.AVERAGE], ) for metric in response.metrics: print(metric.name) for time_series_element in metric.timeseries: for metric_value in time_series_element.data: - print(metric_value.time_stamp) + print(metric_value.timestamp) if __name__ == '__main__': loop = asyncio.get_event_loop() diff --git a/sdk/monitor/azure-monitor-query/samples/champion_scenarios.md b/sdk/monitor/azure-monitor-query/samples/champion_scenarios.md index 367f568e0c8a..d104a65ead8f 100644 --- a/sdk/monitor/azure-monitor-query/samples/champion_scenarios.md +++ b/sdk/monitor/azure-monitor-query/samples/champion_scenarios.md @@ -39,7 +39,7 @@ def query(): return None primary_table = response.tables[0] - df = pd.DataFrame(table.rows, columns=[col.name for col in table.columns]) + df = pd.DataFrame(table.rows, columns=table.columns) return df if __name__ == '__main__': @@ -74,7 +74,7 @@ def query(): return None primary_table = response.tables[0] - df = pd.DataFrame(table.rows, columns=[col.name for col in table.columns]) + df = pd.DataFrame(table.rows, columns=table.columns) return df.to_dict(orient='records') if __name__ == '__main__': @@ -150,7 +150,7 @@ for response in results: print(error) table = response.tables[0] - df = pd.DataFrame(table.rows, columns=[col.name for col in table.columns]) + df = pd.DataFrame(table.rows, columns=table.columns) print(df) print("\n\n-------------------------\n\n") @@ -199,7 +199,7 @@ for response in results: print(error) table = response.tables[0] - df = pd.DataFrame(table.rows, columns=[col.name for col in table.columns]) + df = pd.DataFrame(table.rows, columns=table.columns) print(df.to_dict(orient='records')) print("\n\n-------------------------\n\n") ``` diff --git a/sdk/monitor/azure-monitor-query/samples/sample_log_query_client.py b/sdk/monitor/azure-monitor-query/samples/sample_log_query_client.py index 8ce26f8a6b28..917f6a16e698 100644 --- a/sdk/monitor/azure-monitor-query/samples/sample_log_query_client.py +++ b/sdk/monitor/azure-monitor-query/samples/sample_log_query_client.py @@ -27,8 +27,7 @@ for table in response.tables: try: - print ([col.name for col in table.columns]) - df = pd.DataFrame(table.rows, columns=[col.name for col in table.columns]) + df = pd.DataFrame(table.rows, columns=table.columns) print(df) except TypeError: print(response.error) From e1537ad202cfefb41e8740dfc55b6dc83c6e3cf0 Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Tue, 24 Aug 2021 19:43:33 -0700 Subject: [PATCH 07/11] changelog --- sdk/monitor/azure-monitor-query/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sdk/monitor/azure-monitor-query/CHANGELOG.md b/sdk/monitor/azure-monitor-query/CHANGELOG.md index ea4ae667b247..f296e45716be 100644 --- a/sdk/monitor/azure-monitor-query/CHANGELOG.md +++ b/sdk/monitor/azure-monitor-query/CHANGELOG.md @@ -8,6 +8,7 @@ - Added a `MetricClass` enum to provide the class of a metric. - Added a `metric_class` attribute to the `MetricDefinition` type. - Added a `MetricNamespaceClassification` enum to support the `namespace_classification` attribute on `MetricNamespace` type. +- Added a `MetricUnit` enum to describe the unit of the metric. ### Breaking Changes @@ -30,6 +31,9 @@ - Removed `LogsBatchResultError` type. - `LogsQueryResultTable` is named to `LogsTable` - `LogsQueryResultColumn` is renamed to `LogsTableColumn` +- `LogsTableColumn` is now removed. Column labels are strings instead. +- `start_time` in `list_metric_namespaces` API is now a datetime. +- The order of params in `LogsBatchQuery` is changed. Also, `headers` is no longer accepted. ### Bugs Fixed From 983b2635b988efe930c34890b1582f7cef5b243d Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Tue, 24 Aug 2021 20:36:16 -0700 Subject: [PATCH 08/11] tests --- .../azure/monitor/query/_metrics_query_client.py | 9 +++++---- .../monitor/query/aio/_metrics_query_client_async.py | 10 +++++----- .../tests/async/test_logs_client_async.py | 2 ++ .../tests/async/test_metrics_client_async.py | 2 ++ .../azure-monitor-query/tests/test_logs_client.py | 4 ++-- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/_metrics_query_client.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/_metrics_query_client.py index 38ecb1c9847d..af31b461d852 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/_metrics_query_client.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/_metrics_query_client.py @@ -133,10 +133,10 @@ def list_metric_namespaces(self, resource_uri, **kwargs): :rtype: ~azure.core.paging.ItemPaged[~azure.monitor.query.MetricNamespace] :raises: ~azure.core.exceptions.HttpResponseError """ - start = Serializer.serialize_iso(kwargs.pop('start_time', None)) - kwargs.setdefault('start_time', start) + start_time = Serializer.serialize_iso(kwargs.pop('start_time', None)) return self._namespace_op.list( resource_uri, + start_time, cls=kwargs.pop( "cls", lambda objs: [ @@ -152,12 +152,13 @@ def list_metric_definitions(self, resource_uri, metric_namespace=None, **kwargs) :param resource_uri: The identifier of the resource. :type resource_uri: str - :param metric_namespace: Metric namespace to query metric definitions for. - :type metric_namespace: str + :keyword namespace: Metric namespace to query metric definitions for. + :paramtype namespace: str :return: An iterator like instance of either MetricDefinitionCollection or the result of cls(response) :rtype: ~azure.core.paging.ItemPaged[~azure.monitor.query.MetricDefinition] :raises: ~azure.core.exceptions.HttpResponseError """ + metric_namespace = kwargs.pop('namespace', None) return self._definitions_op.list( resource_uri, metric_namespace, diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_metrics_query_client_async.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_metrics_query_client_async.py index 06e4533cd2f4..36f75ab5362b 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_metrics_query_client_async.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_metrics_query_client_async.py @@ -120,10 +120,10 @@ def list_metric_namespaces(self, resource_uri: str, **kwargs: Any) -> AsyncItemP :rtype: ~azure.core.paging.AsyncItemPaged[:class: `~azure.monitor.query.MetricNamespace`] :raises: ~azure.core.exceptions.HttpResponseError """ - start = Serializer.serialize_iso(kwargs.pop('start_time', None)) - kwargs.setdefault('start_time', start) + start_time = Serializer.serialize_iso(kwargs.pop('start_time', None)) return self._namespace_op.list( resource_uri, + start_time, cls=kwargs.pop( "cls", lambda objs: [ @@ -136,19 +136,19 @@ def list_metric_namespaces(self, resource_uri: str, **kwargs: Any) -> AsyncItemP def list_metric_definitions( self, resource_uri: str, - metric_namespace: str = None, **kwargs: Any ) -> AsyncItemPaged[MetricDefinition]: """Lists the metric definitions for the resource. :param resource_uri: The identifier of the resource. :type resource_uri: str - :param metric_namespace: Metric namespace to query metric definitions for. - :type metric_namespace: str + :keyword namespace: Metric namespace to query metric definitions for. + :paramtype namespace: str :return: An iterator like instance of either MetricDefinitionCollection or the result of cls(response) :rtype: ~azure.core.paging.AsyncItemPaged[:class: `~azure.monitor.query.MetricDefinition`] :raises: ~azure.core.exceptions.HttpResponseError """ + metric_namespace = kwargs.pop('namespace', None) return self._definitions_op.list( resource_uri, metric_namespace, diff --git a/sdk/monitor/azure-monitor-query/tests/async/test_logs_client_async.py b/sdk/monitor/azure-monitor-query/tests/async/test_logs_client_async.py index 6c9ea9f6fd13..4fb58f16cc4b 100644 --- a/sdk/monitor/azure-monitor-query/tests/async/test_logs_client_async.py +++ b/sdk/monitor/azure-monitor-query/tests/async/test_logs_client_async.py @@ -31,6 +31,7 @@ async def test_logs_auth(): @pytest.mark.skip("https://github.com/Azure/azure-sdk-for-python/issues/19917") @pytest.mark.live_test_only +@pytest.mark.asyncio async def test_logs_server_timeout(): client = LogsQueryClient(_credential()) with pytest.raises(HttpResponseError) as e: @@ -43,6 +44,7 @@ async def test_logs_server_timeout(): assert e.message.contains('Gateway timeout') @pytest.mark.live_test_only +@pytest.mark.asyncio async def test_logs_query_batch(): client = LogsQueryClient(_credential()) diff --git a/sdk/monitor/azure-monitor-query/tests/async/test_metrics_client_async.py b/sdk/monitor/azure-monitor-query/tests/async/test_metrics_client_async.py index e205e67734e2..6c680e9c9760 100644 --- a/sdk/monitor/azure-monitor-query/tests/async/test_metrics_client_async.py +++ b/sdk/monitor/azure-monitor-query/tests/async/test_metrics_client_async.py @@ -43,6 +43,7 @@ async def test_metrics_granularity(): assert response.granularity == timedelta(minutes=5) @pytest.mark.live_test_only +@pytest.mark.asyncio async def test_metrics_namespaces(): client = MetricsQueryClient(_credential()) @@ -51,6 +52,7 @@ async def test_metrics_namespaces(): assert response is not None @pytest.mark.live_test_only +@pytest.mark.asyncio async def test_metrics_definitions(): client = MetricsQueryClient(_credential()) diff --git a/sdk/monitor/azure-monitor-query/tests/test_logs_client.py b/sdk/monitor/azure-monitor-query/tests/test_logs_client.py index e9e91b316dcc..ffff9597486a 100644 --- a/sdk/monitor/azure-monitor-query/tests/test_logs_client.py +++ b/sdk/monitor/azure-monitor-query/tests/test_logs_client.py @@ -22,7 +22,7 @@ def test_logs_single_query(): summarize avgRequestDuration=avg(DurationMs) by bin(TimeGenerated, 10m), _ResourceId""" # returns LogsQueryResult - response = client.query(os.environ['LOG_WORKSPACE_ID'], query) + response = client.query(os.environ['LOG_WORKSPACE_ID'], query, timespan=None) assert response is not None assert response.tables is not None @@ -107,7 +107,7 @@ def test_logs_single_query_with_render(): query = """AppRequests""" # returns LogsQueryResult - response = client.query(os.environ['LOG_WORKSPACE_ID'], query, include_visualization=True) + response = client.query(os.environ['LOG_WORKSPACE_ID'], query, timespan=None, include_visualization=True) assert response.visualization is not None From c246f453cf5120df33d699d23443be3c54d7c554 Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Tue, 24 Aug 2021 20:37:47 -0700 Subject: [PATCH 09/11] lint --- .../azure/monitor/query/_metrics_query_client.py | 2 +- sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py | 1 - .../azure/monitor/query/aio/_metrics_query_client_async.py | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/_metrics_query_client.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/_metrics_query_client.py index af31b461d852..0fafab0139fd 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/_metrics_query_client.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/_metrics_query_client.py @@ -8,8 +8,8 @@ # pylint: disable=anomalous-backslash-in-string from typing import TYPE_CHECKING, Any, Optional -from azure.core.tracing.decorator import distributed_trace from msrest.serialization import Serializer +from azure.core.tracing.decorator import distributed_trace from ._generated._monitor_query_client import ( MonitorQueryClient, diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py index 6bf3135696ed..1ad98aadb1ec 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py @@ -11,7 +11,6 @@ from ._helpers import construct_iso8601, process_row from ._generated.models import ( - Column as InternalColumn, BatchQueryRequest as InternalLogQueryRequest, ) diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_metrics_query_client_async.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_metrics_query_client_async.py index 36f75ab5362b..fc95281654bb 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_metrics_query_client_async.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_metrics_query_client_async.py @@ -9,11 +9,11 @@ from datetime import timedelta from typing import TYPE_CHECKING, Any, List, Optional +from msrest.serialization import Serializer from azure.core.async_paging import AsyncItemPaged from azure.core.tracing.decorator import distributed_trace from azure.core.tracing.decorator_async import distributed_trace_async -from msrest.serialization import Serializer from .._generated.aio._monitor_query_client import ( MonitorQueryClient, From d106dd59f970c79f8a9a622128fcd1d5407a976a Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Tue, 24 Aug 2021 21:02:19 -0700 Subject: [PATCH 10/11] fix tests --- .../azure/monitor/query/_metrics_query_client.py | 4 +++- .../monitor/query/aio/_metrics_query_client_async.py | 4 +++- .../tests/async/test_metrics_client_async.py | 10 ++++++---- .../azure-monitor-query/tests/test_metrics_client.py | 2 +- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/_metrics_query_client.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/_metrics_query_client.py index 0fafab0139fd..30a99f6f82a0 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/_metrics_query_client.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/_metrics_query_client.py @@ -133,7 +133,9 @@ def list_metric_namespaces(self, resource_uri, **kwargs): :rtype: ~azure.core.paging.ItemPaged[~azure.monitor.query.MetricNamespace] :raises: ~azure.core.exceptions.HttpResponseError """ - start_time = Serializer.serialize_iso(kwargs.pop('start_time', None)) + start_time = kwargs.pop('start_time', None) + if start_time: + start_time = Serializer.serialize_iso(start_time) return self._namespace_op.list( resource_uri, start_time, diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_metrics_query_client_async.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_metrics_query_client_async.py index fc95281654bb..0d06cbe8a0b1 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_metrics_query_client_async.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_metrics_query_client_async.py @@ -120,7 +120,9 @@ def list_metric_namespaces(self, resource_uri: str, **kwargs: Any) -> AsyncItemP :rtype: ~azure.core.paging.AsyncItemPaged[:class: `~azure.monitor.query.MetricNamespace`] :raises: ~azure.core.exceptions.HttpResponseError """ - start_time = Serializer.serialize_iso(kwargs.pop('start_time', None)) + start_time = kwargs.pop('start_time', None) + if start_time: + start_time = Serializer.serialize_iso(start_time) return self._namespace_op.list( resource_uri, start_time, diff --git a/sdk/monitor/azure-monitor-query/tests/async/test_metrics_client_async.py b/sdk/monitor/azure-monitor-query/tests/async/test_metrics_client_async.py index 6c680e9c9760..ae9ba2f52b2d 100644 --- a/sdk/monitor/azure-monitor-query/tests/async/test_metrics_client_async.py +++ b/sdk/monitor/azure-monitor-query/tests/async/test_metrics_client_async.py @@ -47,15 +47,17 @@ async def test_metrics_granularity(): async def test_metrics_namespaces(): client = MetricsQueryClient(_credential()) - response = await client.list_metric_namespaces(os.environ['METRICS_RESOURCE_URI']) + async with client: + response = client.list_metric_namespaces(os.environ['METRICS_RESOURCE_URI']) - assert response is not None + assert response is not None @pytest.mark.live_test_only @pytest.mark.asyncio async def test_metrics_definitions(): client = MetricsQueryClient(_credential()) - response = await client.list_metric_definitions(os.environ['METRICS_RESOURCE_URI'], metric_namespace='microsoft.eventgrid/topics') + async with client: + response = client.list_metric_definitions(os.environ['METRICS_RESOURCE_URI'], namespace='microsoft.eventgrid/topics') - assert response is not None + assert response is not None diff --git a/sdk/monitor/azure-monitor-query/tests/test_metrics_client.py b/sdk/monitor/azure-monitor-query/tests/test_metrics_client.py index ecdb7e7c8a3a..17f61ccb812b 100644 --- a/sdk/monitor/azure-monitor-query/tests/test_metrics_client.py +++ b/sdk/monitor/azure-monitor-query/tests/test_metrics_client.py @@ -51,6 +51,6 @@ def test_metrics_namespaces(): def test_metrics_definitions(): client = MetricsQueryClient(_credential()) - response = client.list_metric_definitions(os.environ['METRICS_RESOURCE_URI'], metric_namespace='microsoft.eventgrid/topics') + response = client.list_metric_definitions(os.environ['METRICS_RESOURCE_URI'], namespace='microsoft.eventgrid/topics') assert response is not None From e0c8f5b9a71269c80cc1f0bdad58f135e2fecb9d Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Tue, 24 Aug 2021 22:44:03 -0700 Subject: [PATCH 11/11] timespan --- sdk/monitor/azure-monitor-query/CHANGELOG.md | 1 + .../azure/monitor/query/_logs_query_client.py | 12 ++++++---- .../azure/monitor/query/_models.py | 12 ++++++---- .../query/aio/_logs_query_client_async.py | 1 + .../tests/async/test_logs_client_async.py | 24 +++++++++++++++++++ .../tests/test_logs_client.py | 12 ++++++++++ 6 files changed, 52 insertions(+), 10 deletions(-) diff --git a/sdk/monitor/azure-monitor-query/CHANGELOG.md b/sdk/monitor/azure-monitor-query/CHANGELOG.md index f296e45716be..5805d4278e0b 100644 --- a/sdk/monitor/azure-monitor-query/CHANGELOG.md +++ b/sdk/monitor/azure-monitor-query/CHANGELOG.md @@ -34,6 +34,7 @@ - `LogsTableColumn` is now removed. Column labels are strings instead. - `start_time` in `list_metric_namespaces` API is now a datetime. - The order of params in `LogsBatchQuery` is changed. Also, `headers` is no longer accepted. +- `timespan` is now a required keyword-only argument in logs APIs. ### Bugs Fixed diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/_logs_query_client.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/_logs_query_client.py index 854e997b6227..eaac63c874bc 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/_logs_query_client.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/_logs_query_client.py @@ -51,8 +51,8 @@ def __init__(self, credential, **kwargs): self._query_op = self._client.query @distributed_trace - def query(self, workspace_id, query, timespan, **kwargs): - # type: (str, str, Union[timedelta, (datetime, timedelta), (datetime, datetime)], Any) -> LogsQueryResult + def query(self, workspace_id, query, **kwargs): + # type: (str, str, Any) -> LogsQueryResult """Execute an Analytics query. Executes an Analytics query for data. @@ -63,9 +63,9 @@ def query(self, workspace_id, query, timespan, **kwargs): :param query: The Analytics query. Learn more about the `Analytics query syntax `_. :type query: str - :param timespan: The timespan for which to query the data. This can be a timedelta, + :keyword timespan: The timespan for which to query the data. This can be a timedelta, a timedelta and a start datetime, or a start datetime/end datetime. - :type timespan: ~datetime.timedelta or tuple[~datetime.datetime, ~datetime.timedelta] + :paramtype timespan: ~datetime.timedelta or tuple[~datetime.datetime, ~datetime.timedelta] or tuple[~datetime.datetime, ~datetime.datetime] :keyword int server_timeout: the server timeout in seconds. The default timeout is 3 minutes, and the maximum timeout is 10 minutes. @@ -89,7 +89,9 @@ def query(self, workspace_id, query, timespan, **kwargs): :dedent: 0 :caption: Get a response for a single Log Query """ - timespan = construct_iso8601(timespan) + if 'timespan' not in kwargs: + raise TypeError("query() missing 1 required keyword-only argument: 'timespan'") + timespan = construct_iso8601(kwargs.pop('timespan')) include_statistics = kwargs.pop("include_statistics", False) include_visualization = kwargs.pop("include_visualization", False) server_timeout = kwargs.pop("server_timeout", None) diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py index 1ad98aadb1ec..18cfa1245435 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py @@ -141,9 +141,9 @@ class LogsBatchQuery(object): :param query: The Analytics query. Learn more about the `Analytics query syntax `_. :type query: str - :param timespan: The timespan for which to query the data. This can be a timedelta, + :keyword timespan: The timespan for which to query the data. This can be a timedelta, a timedelta and a start datetime, or a start datetime/end datetime. - :type timespan: ~datetime.timedelta or tuple[~datetime.datetime, ~datetime.timedelta] + :paramtype timespan: ~datetime.timedelta or tuple[~datetime.datetime, ~datetime.timedelta] or tuple[~datetime.datetime, ~datetime.datetime] :keyword additional_workspaces: A list of workspaces that are included in the query. These can be qualified workspace names, workspace Ids, or Azure resource Ids. @@ -156,8 +156,10 @@ class LogsBatchQuery(object): visualization to show. """ - def __init__(self, workspace_id, query, timespan, **kwargs): #pylint: disable=super-init-not-called - # type: (str, str, Optional[str], Any) -> None + def __init__(self, workspace_id, query, **kwargs): #pylint: disable=super-init-not-called + # type: (str, str, Any) -> None + if 'timespan' not in kwargs: + raise TypeError("LogsBatchQuery() missing 1 required keyword-only argument: 'timespan'") include_statistics = kwargs.pop("include_statistics", False) include_visualization = kwargs.pop("include_visualization", False) server_timeout = kwargs.pop("server_timeout", None) @@ -174,7 +176,7 @@ def __init__(self, workspace_id, query, timespan, **kwargs): #pylint: disable=su prefer += "include-render=true" headers = {'Prefer': prefer} - timespan = construct_iso8601(timespan) + timespan = construct_iso8601(kwargs.pop('timespan')) additional_workspaces = kwargs.pop("additional_workspaces", None) self.id = str(uuid.uuid4()) self.body = { diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_logs_query_client_async.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_logs_query_client_async.py index c97ea8d04b62..5039f62402f6 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_logs_query_client_async.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_logs_query_client_async.py @@ -45,6 +45,7 @@ async def query( self, workspace_id: str, query: str, + *, timespan: Union[timedelta, Tuple[datetime, timedelta], Tuple[datetime, datetime]], **kwargs: Any) -> LogsQueryResult: """Execute an Analytics query. diff --git a/sdk/monitor/azure-monitor-query/tests/async/test_logs_client_async.py b/sdk/monitor/azure-monitor-query/tests/async/test_logs_client_async.py index 4fb58f16cc4b..fb49883cff6d 100644 --- a/sdk/monitor/azure-monitor-query/tests/async/test_logs_client_async.py +++ b/sdk/monitor/azure-monitor-query/tests/async/test_logs_client_async.py @@ -16,6 +16,7 @@ def _credential(): return credential @pytest.mark.live_test_only +@pytest.mark.asyncio async def test_logs_auth(): credential = _credential() client = LogsQueryClient(credential) @@ -29,6 +30,20 @@ async def test_logs_auth(): assert response is not None assert response.tables is not None +@pytest.mark.live_test_only +@pytest.mark.asyncio +async def test_logs_auth_no_timespan(): + credential = _credential() + client = LogsQueryClient(credential) + query = """AppRequests | + where TimeGenerated > ago(12h) | + summarize avgRequestDuration=avg(DurationMs) by bin(TimeGenerated, 10m), _ResourceId""" + + # returns LogsQueryResult + with pytest.raises(TypeError): + await client.query(os.environ['LOG_WORKSPACE_ID'], query) + + @pytest.mark.skip("https://github.com/Azure/azure-sdk-for-python/issues/19917") @pytest.mark.live_test_only @pytest.mark.asyncio @@ -43,6 +58,15 @@ async def test_logs_server_timeout(): ) assert e.message.contains('Gateway timeout') +@pytest.mark.live_test_only +@pytest.mark.asyncio +async def test_logs_query_batch_raises_on_no_timespan(): + with pytest.raises(TypeError): + LogsBatchQuery( + query="AzureActivity | summarize count()", + workspace_id= os.environ['LOG_WORKSPACE_ID'] + ) + @pytest.mark.live_test_only @pytest.mark.asyncio async def test_logs_query_batch(): diff --git a/sdk/monitor/azure-monitor-query/tests/test_logs_client.py b/sdk/monitor/azure-monitor-query/tests/test_logs_client.py index ffff9597486a..dba61f5471d1 100644 --- a/sdk/monitor/azure-monitor-query/tests/test_logs_client.py +++ b/sdk/monitor/azure-monitor-query/tests/test_logs_client.py @@ -27,6 +27,18 @@ def test_logs_single_query(): assert response is not None assert response.tables is not None +@pytest.mark.live_test_only +def test_logs_single_query_raises_no_timespan(): + credential = _credential() + client = LogsQueryClient(credential) + query = """AppRequests | + where TimeGenerated > ago(12h) | + summarize avgRequestDuration=avg(DurationMs) by bin(TimeGenerated, 10m), _ResourceId""" + + # returns LogsQueryResult + with pytest.raises(TypeError): + client.query(os.environ['LOG_WORKSPACE_ID'], query) + @pytest.mark.live_test_only def test_logs_single_query_with_non_200(): credential = _credential()