Skip to content

Commit

Permalink
Add a new row type in query (Azure#20685)
Browse files Browse the repository at this point in the history
* Add row type

* Add a new row type

* add test

* lint

* Apply suggestions from code review

* changes
  • Loading branch information
Rakshith Bhyravabhotla authored Sep 14, 2021
1 parent 5016742 commit 279e7eb
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 8 deletions.
2 changes: 2 additions & 0 deletions sdk/monitor/azure-monitor-query/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
- Added `QueryPartialErrorException` and `LogsQueryError` to handle errors.
- Added `partial_error` and `is_error` attributes to `LogsQueryResult`.
- Added an option `allow_partial_errors` that defaults to False, which can be set to not throw if there are any partial errors.
- Added a new `LogsTableRow` type that represents a single row in a table.

### Breaking Changes

- `LogsQueryResult` now iterates over the tables directly as a convinience.
- `metric_namespace` is renamed to `namespace` and is a keyword-only argument in `list_metric_definitions` API.

### Bugs Fixed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
MetricAggregationType,
LogsQueryResult,
LogsTable,
LogsTableRow,
MetricsResult,
LogsBatchQuery,
MetricNamespace,
Expand All @@ -38,6 +39,7 @@
"LogsQueryError",
"QueryPartialErrorException",
"LogsTable",
"LogsTableRow",
"LogsBatchQuery",
"MetricsQueryClient",
"MetricNamespace",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ def list_metric_namespaces(self, resource_uri, **kwargs):
**kwargs)

@distributed_trace
def list_metric_definitions(self, resource_uri, metric_namespace=None, **kwargs):
# type: (str, str, Any) -> ItemPaged[MetricDefinition]
def list_metric_definitions(self, resource_uri, **kwargs):
# type: (str, Any) -> ItemPaged[MetricDefinition]
"""Lists the metric definitions for the resource.
:param resource_uri: The identifier of the resource.
Expand Down
45 changes: 43 additions & 2 deletions sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,22 @@ class LogsTable(object):
: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[object]]
:vartype rows: list[~azure.monitor.query.LogsTableRow]
"""
def __init__(self, **kwargs):
# type: (Any) -> None
self.name = kwargs.pop('name', None) # type: str
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_types, row) for row in _rows]
self.rows = [
LogsTableRow(
row=row,
row_index=ind,
col_types=self.columns_types,
columns=self.columns
) for ind, row in enumerate(_rows)
]

@classmethod
def _from_generated(cls, generated):
Expand All @@ -48,6 +55,40 @@ def _from_generated(cls, generated):
)


class LogsTableRow(object):
"""Represents a single row in logs table.
ivar list row: The collection of values in the row.
ivar int row_index: The index of the row in the table
"""
def __init__(self, **kwargs):
# type: (Any) -> None
_col_types = kwargs['col_types']
row = kwargs['row']
self.row = process_row(_col_types, row)
self.row_index = kwargs['row_index']
_columns = kwargs['columns']
self._row_dict = {
_columns[i]: self.row[i] for i in range(len(self.row))
}

def __iter__(self):
"""This will iterate over the row directly.
"""
return iter(self.row)

def __getitem__(self, column):
"""This type must be subscriptable directly to row.
Must be gettableby both column name and row index
Example: row[0] -> returns the first element of row and
row[column_name] -> returns the row element against the given column name.
"""
try:
return self._row_dict[column]
except KeyError:
return self.row[column]


class MetricsResult(object):
"""The response to a metrics query.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@
# Response time trend
# request duration over the last 12 hours.
# [START send_logs_query]
query = """AppRwequests | take 5"""
query = """AppRequests | take 5"""

# returns LogsQueryResult
try:
response = client.query(os.environ['LOG_WORKSPACE_ID'], query, timespan=timedelta(days=1))
for table in response:
print(table)
df = pd.DataFrame(data=table.rows, columns=table.columns)
print(df)
except QueryPartialErrorException as err:
print("this is a partial error")
print(err.details)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import os
from azure.identity.aio import ClientSecretCredential
from azure.core.exceptions import HttpResponseError
from azure.monitor.query import LogsBatchQuery, LogsQueryError, LogsTable, LogsQueryResult
from azure.monitor.query import LogsBatchQuery, LogsQueryError, LogsTable, LogsQueryResult, LogsTableRow
from azure.monitor.query.aio import LogsQueryClient

def _credential():
Expand Down Expand Up @@ -202,3 +202,23 @@ async def test_logs_query_result_iterate_over_tables():
assert response.visualization is not None
assert len(response.tables) == 2
assert response.__class__ == LogsQueryResult

@pytest.mark.live_test_only
@pytest.mark.asyncio
async def test_logs_query_result_row_type():
client = LogsQueryClient(_credential())

query = "AppRequests | take 5"

response = await client.query(
os.environ['LOG_WORKSPACE_ID'],
query,
timespan=None,
)

## should iterate over tables
for table in response:
assert table.__class__ == LogsTable

for row in table.rows:
assert row.__class__ == LogsTableRow
21 changes: 20 additions & 1 deletion sdk/monitor/azure-monitor-query/tests/test_logs_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import os
from azure.identity import ClientSecretCredential
from azure.core.exceptions import HttpResponseError
from azure.monitor.query import LogsQueryClient, LogsBatchQuery, LogsQueryError, LogsTable, LogsQueryResult
from azure.monitor.query import LogsQueryClient, LogsBatchQuery, LogsQueryError, LogsTable, LogsQueryResult, LogsTableRow

def _credential():
credential = ClientSecretCredential(
Expand Down Expand Up @@ -245,3 +245,22 @@ def test_logs_query_result_iterate_over_tables():
assert response.visualization is not None
assert len(response.tables) == 2
assert response.__class__ == LogsQueryResult

@pytest.mark.live_test_only
def test_logs_query_result_row_type():
client = LogsQueryClient(_credential())

query = "AppRequests | take 5"

response = client.query(
os.environ['LOG_WORKSPACE_ID'],
query,
timespan=None,
)

## should iterate over tables
for table in response:
assert table.__class__ == LogsTable

for row in table.rows:
assert row.__class__ == LogsTableRow

0 comments on commit 279e7eb

Please sign in to comment.