diff --git a/sdk/monitor/azure-monitor-query/CHANGELOG.md b/sdk/monitor/azure-monitor-query/CHANGELOG.md
index b509ac76fa83..bd8f683f9cd2 100644
--- a/sdk/monitor/azure-monitor-query/CHANGELOG.md
+++ b/sdk/monitor/azure-monitor-query/CHANGELOG.md
@@ -9,6 +9,7 @@
- `workspaces`, `workspace_ids`, `qualified_names` and `azure_resource_ids` are now merged into a single `additional_workspaces` list in the query API.
- The `LogQueryRequest` object now takes in a `workspace_id` and `additional_workspaces` instead of `workspace`.
- `aggregation` param is now a list instead of a string in the `query` method.
+- `duration` must now be provided as a timedelta instead of a string.
### Key Bugs Fixed
diff --git a/sdk/monitor/azure-monitor-query/README.md b/sdk/monitor/azure-monitor-query/README.md
index 4bf44092d18e..719db95b887d 100644
--- a/sdk/monitor/azure-monitor-query/README.md
+++ b/sdk/monitor/azure-monitor-query/README.md
@@ -153,6 +153,7 @@ This sample shows sending multiple queries at the same time using batch query AP
```Python
import os
+from datetime import timedelta
import pandas as pd
from azure.monitor.query import LogsQueryClient, LogsQueryRequest
from azure.identity import ClientSecretCredential
@@ -169,13 +170,13 @@ client = LogsQueryClient(credential)
requests = [
LogsQueryRequest(
query="AzureActivity | summarize count()",
- duration="PT1H",
+ duration=timedelta(hours=1),
workspace= os.environ['LOG_WORKSPACE_ID']
),
LogsQueryRequest(
query= """AppRequests | take 10 |
summarize avgRequestDuration=avg(DurationMs) by bin(TimeGenerated, 10m), _ResourceId""",
- duration="PT1H",
+ duration=timedelta(hours=1),
start_time=datetime(2021, 6, 2),
workspace= os.environ['LOG_WORKSPACE_ID']
),
@@ -228,6 +229,7 @@ This example shows getting the metrics for an EventGrid subscription. The resour
```Python
import os
+from datetime import timedelta
from azure.monitor.query import MetricsQueryClient
from azure.identity import ClientSecretCredential
@@ -245,7 +247,7 @@ response = client.query(
metrics_uri,
metric_names=["PublishSuccessCount"],
start_time=datetime(2021, 5, 25),
- duration='P1D'
+ duration=timedelta(days=1),
)
for metric in response.metrics:
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 7ee12be0a0be..727906c27494 100644
--- a/sdk/monitor/azure-monitor-query/azure/monitor/query/_helpers.py
+++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/_helpers.py
@@ -50,17 +50,23 @@ def order_results(request_order, responses):
return ordered
def construct_iso8601(start=None, end=None, duration=None):
+ if duration is not None:
+ duration = 'PT{}S'.format(duration.total_seconds())
iso_str = None
if start is not None:
start = Serializer.serialize_iso(start)
+ if end and duration:
+ raise ValueError("start_time can only be provided with duration or end_time, but not both.")
if end is not None:
end = Serializer.serialize_iso(end)
iso_str = start + '/' + end
elif duration is not None:
iso_str = start + '/' + duration
else:
- raise ValueError("Start time must be provided aling with duration or end time.")
+ raise ValueError("Start time must be provided along with duration or end time.")
elif end is not None:
+ if not duration:
+ raise ValueError("End time must be provided along with duration or start time.")
end = Serializer.serialize_iso(end)
iso_str = duration + '/' + end
else:
diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/_log_query_client.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/_log_query_client.py
index 79f14744b53d..63f19cda2837 100644
--- a/sdk/monitor/azure-monitor-query/azure/monitor/query/_log_query_client.py
+++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/_log_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, Optional
from azure.core.exceptions import HttpResponseError
from ._generated._monitor_query_client import MonitorQueryClient
@@ -16,6 +16,7 @@
if TYPE_CHECKING:
from azure.core.credentials import TokenCredential
+ from datetime import timedelta
class LogsQueryClient(object):
@@ -49,7 +50,7 @@ def __init__(self, credential, **kwargs):
self._query_op = self._client.query
def query(self, workspace_id, query, duration=None, **kwargs):
- # type: (str, str, str, Any) -> LogsQueryResults
+ # type: (str, str, Optional[timedelta], Any) -> LogsQueryResults
"""Execute an Analytics query.
Executes an Analytics query for data.
@@ -63,9 +64,9 @@ def query(self, workspace_id, query, duration=None, **kwargs):
:param query: The Analytics query. Learn more about the `Analytics query syntax
`_.
:type query: str
- :param str duration: The duration for which to query the data. This can also be accompanied
+ :param ~datetime.timedelta duration: The duration for which to query the data. This can also be accompanied
with either start_time or end_time. If start_time or end_time is not provided, the current time is
- taken as the end time. This should be provided in a ISO8601 string format like 'PT1H', 'P1Y2M10DT2H30M'.
+ taken as the end time.
:keyword datetime start_time: The start time from which to query the data. This should be accompanied
with either end_time or duration.
:keyword datetime end_time: The end time till which to query the data. This should be accompanied
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 a72d926ab377..1d39b7adeb0f 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
@@ -7,7 +7,7 @@
# pylint: disable=anomalous-backslash-in-string
-from typing import TYPE_CHECKING, Any
+from typing import TYPE_CHECKING, Any, Optional
from ._generated._monitor_query_client import (
MonitorQueryClient,
@@ -17,6 +17,7 @@
from ._helpers import get_metrics_authentication_policy, construct_iso8601
if TYPE_CHECKING:
+ from datetime import timedelta
from azure.core.credentials import TokenCredential
from azure.core.paging import ItemPaged
@@ -53,7 +54,7 @@ def __init__(self, credential, **kwargs):
self._definitions_op = self._client.metric_definitions
def query(self, resource_uri, metric_names, duration=None, **kwargs):
- # type: (str, list, str, Any) -> MetricsResult
+ # 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
@@ -63,9 +64,9 @@ def query(self, resource_uri, metric_names, duration=None, **kwargs):
:type resource_uri: str
:param metric_names: The names of the metrics to retrieve.
:type metric_names: list[str]
- :param str duration: The duration for which to query the data. This can also be accompanied
+ :param ~datetime.timedelta duration: The duration for which to query the data. This can also be accompanied
with either start_time or end_time. If start_time or end_time is not provided, the current time is
- taken as the end time. This should be provided in a ISO8601 string format like 'PT1H', 'P1Y2M10DT2H30M'.
+ taken as the end time.
:keyword datetime start_time: The start time from which to query the data. This should be accompanied
with either end_time or duration.
:keyword datetime end_time: The end time till which to query the data. This should be accompanied
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 c477de7909c1..d584623754b9 100644
--- a/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py
+++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py
@@ -151,9 +151,9 @@ class LogsQueryRequest(InternalLogQueryRequest):
:param query: The Analytics query. Learn more about the `Analytics query syntax
`_.
:type query: str
- :param str duration: The duration for which to query the data. This can also be accompanied
+ :param ~datetime.timedelta duration: The duration for which to query the data. This can also be accompanied
with either start_time or end_time. If start_time or end_time is not provided, the current time is
- taken as the end time. This should be provided in a ISO8601 string format like 'PT1H', 'P1Y2M10DT2H30M'.
+ taken as the end time.
:keyword datetime start_time: The start time from which to query the data. This should be accompanied
with either end_time or duration.
:keyword datetime end_time: The end time till which to query the data. This should be accompanied
diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_log_query_client_async.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_log_query_client_async.py
index c100c5791355..f840472815dd 100644
--- a/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_log_query_client_async.py
+++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_log_query_client_async.py
@@ -5,7 +5,8 @@
# license information.
# --------------------------------------------------------------------------
-from typing import Any, Union, Sequence, Dict, TYPE_CHECKING
+from datetime import timedelta
+from typing import Any, Union, Sequence, Dict, Optional, TYPE_CHECKING
from azure.core.exceptions import HttpResponseError
from .._generated.aio._monitor_query_client import MonitorQueryClient
@@ -41,7 +42,7 @@ async def query(
self,
workspace_id: str,
query: str,
- duration: str = None,
+ duration: Optional[timedelta] = None,
**kwargs: Any) -> LogsQueryResults:
"""Execute an Analytics query.
@@ -56,9 +57,9 @@ async def query(
:param query: The Analytics query. Learn more about the `Analytics query syntax
`_.
:type query: str
- :param str duration: The duration for which to query the data. This can also be accompanied
+ :param ~datetime.timedelta duration: The duration for which to query the data. This can also be accompanied
with either start_time or end_time. If start_time or end_time is not provided, the current time is
- taken as the end time. This should be provided in a ISO8601 string format like 'PT1H', 'P1Y2M10DT2H30M'.
+ taken as the end time.
:keyword datetime start_time: The start time from which to query the data. This should be accompanied
with either end_time or duration.
:keyword datetime end_time: The end time till which to query the data. This should be accompanied
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 7d6d3423d80e..28124ac7635a 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
@@ -7,7 +7,8 @@
# pylint: disable=anomalous-backslash-in-string
-from typing import TYPE_CHECKING, Any, List
+from datetime import timedelta
+from typing import TYPE_CHECKING, Any, List, Optional
from azure.core.async_paging import AsyncItemPaged
@@ -42,7 +43,13 @@ def __init__(self, credential: "AsyncTokenCredential", **kwargs: Any) -> None:
self._namespace_op = self._client.metric_namespaces
self._definitions_op = self._client.metric_definitions
- async def query(self, resource_uri: str, metric_names: List, duration: str = None, **kwargs: Any) -> MetricsResult:
+ async def query(
+ self,
+ resource_uri: str,
+ metric_names: List,
+ duration: Optional[timedelta] = None,
+ **kwargs: Any
+ ) -> MetricsResult:
"""Lists the metric values for a resource.
**Note**: Although the start_time, end_time, duration are optional parameters, it is highly
@@ -52,9 +59,9 @@ async def query(self, resource_uri: str, metric_names: List, duration: str = Non
:type resource_uri: str
:param metric_names: The names of the metrics to retrieve.
:type metric_names: list
- :param str duration: The duration for which to query the data. This can also be accompanied
+ :param ~datetime.timedelta duration: The duration for which to query the data. This can also be accompanied
with either start_time or end_time. If start_time or end_time is not provided, the current time is
- taken as the end time. This should be provided in a ISO8601 string format like 'PT1H', 'P1Y2M10DT2H30M'.
+ taken as the end time.
:keyword datetime start_time: The start time from which to query the data. This should be accompanied
with either end_time or duration.
:keyword datetime end_time: The end time till which to query the data. This should be accompanied
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 0b3e673caa61..ae1da2593ea3 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
@@ -3,7 +3,7 @@
import os
import asyncio
-from datetime import datetime
+from datetime import datetime, timedelta
import urllib3
from azure.monitor.query.aio import MetricsQueryClient
from azure.identity.aio import ClientSecretCredential
@@ -25,7 +25,7 @@ async def query_metrics():
metrics_uri,
metric_names=["PublishSuccessCount"],
start_time=datetime(2021, 5, 25),
- duration='P1D'
+ duration=timedelta(days=1)
)
for metric in response.metrics:
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 eff925ea8a44..50917ce33ab8 100644
--- a/sdk/monitor/azure-monitor-query/samples/sample_batch_query.py
+++ b/sdk/monitor/azure-monitor-query/samples/sample_batch_query.py
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
-from datetime import datetime
+from datetime import datetime, timedelta
import os
import pandas as pd
from azure.monitor.query import LogsQueryClient, LogsQueryRequest
@@ -20,13 +20,13 @@
requests = [
LogsQueryRequest(
query="AzureActivity | summarize count()",
- duration="PT1H",
+ duration=timedelta(hours=1),
workspace_id= os.environ['LOG_WORKSPACE_ID']
),
LogsQueryRequest(
query= """AppRequests | take 10 |
summarize avgRequestDuration=avg(DurationMs) by bin(TimeGenerated, 10m), _ResourceId""",
- duration="PT1H",
+ duration=timedelta(hours=1),
start_time=datetime(2021, 6, 2),
workspace_id= os.environ['LOG_WORKSPACE_ID']
),
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 10a160f71bed..b3e978fe37d9 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,7 +3,7 @@
import os
import pandas as pd
-from datetime import datetime
+from datetime import datetime, timedelta
from msrest.serialization import UTC
from azure.monitor.query import LogsQueryClient
from azure.identity import ClientSecretCredential
@@ -27,7 +27,7 @@
end_time = datetime.now(UTC())
# returns LogsQueryResults
-response = client.query(os.environ['LOG_WORKSPACE_ID'], query, duration='PT1H', end_time=end_time)
+response = client.query(os.environ['LOG_WORKSPACE_ID'], query, duration=timedelta(days=1), end_time=end_time)
if not response.tables:
print("No results for the query")
diff --git a/sdk/monitor/azure-monitor-query/samples/sample_log_query_client_without_pandas.py b/sdk/monitor/azure-monitor-query/samples/sample_log_query_client_without_pandas.py
index fa74b229e3ed..19a089db2159 100644
--- a/sdk/monitor/azure-monitor-query/samples/sample_log_query_client_without_pandas.py
+++ b/sdk/monitor/azure-monitor-query/samples/sample_log_query_client_without_pandas.py
@@ -2,7 +2,7 @@
# Licensed under the MIT License.
import os
-from datetime import datetime
+from datetime import datetime, timedelta
from msrest.serialization import UTC
from azure.monitor.query import LogsQueryClient
from azure.identity import ClientSecretCredential
@@ -23,7 +23,7 @@
end_time = datetime.now(UTC())
# returns LogsQueryResults
-response = client.query(os.environ['LOG_WORKSPACE_ID'], query, duration='PT1H', end_time=end_time)
+response = client.query(os.environ['LOG_WORKSPACE_ID'], query, duration=timedelta(hours=1), end_time=end_time)
if not response.tables:
print("No results for the query")
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 2230ee736f1d..fd38bb66a355 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
@@ -25,7 +25,7 @@
metrics_uri,
metric_names=["MatchedEventCount"],
start_time=datetime(2021, 6, 21),
- duration='P1D',
+ duration=timedelta(days=1),
aggregation=['Count']
)
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 11e08aa4908b..b58d866ffae0 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
@@ -1,4 +1,4 @@
-from datetime import datetime
+from datetime import datetime, time, timedelta
import pytest
import os
from azure.identity.aio import ClientSecretCredential
@@ -20,7 +20,7 @@ async def test_metrics_auth():
os.environ['METRICS_RESOURCE_URI'],
metric_names=["MatchedEventCount"],
start_time=datetime(2021, 6, 21),
- duration='P1D',
+ duration=timedelta(days=1),
aggregation=['Count']
)
assert response
diff --git a/sdk/monitor/azure-monitor-query/tests/test_logs_timespans.py b/sdk/monitor/azure-monitor-query/tests/test_logs_timespans.py
index 85af60795414..5a584edd93ac 100644
--- a/sdk/monitor/azure-monitor-query/tests/test_logs_timespans.py
+++ b/sdk/monitor/azure-monitor-query/tests/test_logs_timespans.py
@@ -1,4 +1,4 @@
-from datetime import datetime, time, timedelta
+from datetime import datetime, timedelta
import pytest
import json
import os
@@ -8,6 +8,8 @@
from azure.core.exceptions import HttpResponseError
from azure.monitor.query import LogsQueryClient, LogsQueryRequest
+from azure.monitor.query._helpers import construct_iso8601
+
def _credential():
credential = ClientSecretCredential(
client_id = os.environ['AZURE_CLIENT_ID'],
@@ -52,11 +54,11 @@ def test_query_duration_and_end_time():
query = "AppRequests | take 5"
end_time = datetime.now(UTC())
- duration = 'P3D'
+ duration = timedelta(days=3)
def callback(request):
dic = json.loads(request.http_request.body)
- assert 'P3D/' in dic.get('timespan')
+ assert 'PT259200.0S/' in dic.get('timespan')
client.query(os.environ['LOG_WORKSPACE_ID'], query, duration=duration, end_time=end_time, raw_request_hook=callback)
@@ -68,11 +70,11 @@ def test_query_duration_and_start_time():
end_time = datetime.now(UTC())
start_time = end_time - timedelta(days=3)
- duration = 'P3D'
+ duration = timedelta(days=3)
def callback(request):
dic = json.loads(request.http_request.body)
- assert '/P3D' in dic.get('timespan')
+ assert '/PT259200.0S' in dic.get('timespan')
client.query(os.environ['LOG_WORKSPACE_ID'], query, duration=duration, start_time=start_time, raw_request_hook=callback)
@@ -83,10 +85,36 @@ def test_query_duration_only():
client = LogsQueryClient(credential)
query = "AppRequests | take 5"
- duration = 'P3D'
+ duration = timedelta(days=3)
def callback(request):
dic = json.loads(request.http_request.body)
- assert 'P3D' in dic.get('timespan')
+ assert 'PT259200.0S' in dic.get('timespan')
client.query(os.environ['LOG_WORKSPACE_ID'], query, duration=duration, raw_request_hook=callback)
+
+def test_duration_to_iso8601():
+ d1 = timedelta(days=1)
+ d2 = timedelta(weeks=1)
+ d3 = timedelta(weeks=3, days=4)
+ d4 = timedelta(seconds=10)
+ d5 = timedelta(microseconds=1000)
+ d6 = timedelta(milliseconds=100000)
+ d7 = timedelta(hours=24, days=1)
+
+ assert construct_iso8601(duration=d1) == 'PT86400.0S'
+ assert construct_iso8601(duration=d2) == 'PT604800.0S'
+ assert construct_iso8601(duration=d3) == 'PT2160000.0S'
+ assert construct_iso8601(duration=d4) == 'PT10.0S'
+ assert construct_iso8601(duration=d5) == 'PT0.001S'
+ assert construct_iso8601(duration=d5) == 'PT0.001S'
+ assert construct_iso8601(duration=d7) == 'PT172800.0S'
+
+ with pytest.raises(ValueError, match="End time must be provided along with duration or start time."):
+ construct_iso8601(end=datetime.now(UTC()))
+
+ with pytest.raises(ValueError, match="Start time must be provided along with duration or end time."):
+ construct_iso8601(start=datetime.now(UTC()))
+
+ with pytest.raises(ValueError, match="start_time can only be provided with duration or end_time, but not both."):
+ construct_iso8601(end=datetime.now(UTC()), start=datetime(2020, 10, 10), duration=d3)
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 8f5a69ede392..d12e7aa28e9f 100644
--- a/sdk/monitor/azure-monitor-query/tests/test_metrics_client.py
+++ b/sdk/monitor/azure-monitor-query/tests/test_metrics_client.py
@@ -1,6 +1,6 @@
import pytest
import os
-from datetime import datetime
+from datetime import datetime, timedelta
from azure.identity import ClientSecretCredential
from azure.monitor.query import MetricsQueryClient
@@ -20,7 +20,7 @@ def test_metrics_auth():
os.environ['METRICS_RESOURCE_URI'],
metric_names=["MatchedEventCount"],
start_time=datetime(2021, 6, 21),
- duration='P1D',
+ duration=timedelta(days=1),
aggregation=['Count']
)
assert response