From 07052ca3a60108370974fabf57ba634cf3793340 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Thu, 10 Dec 2020 14:44:55 -0800 Subject: [PATCH 1/2] changes without context autosynth cannot find the source of changes triggered by earlier changes in this repository, or by version upgrades to tools such as linters. --- samples/snippets/README.rst | 191 ------------------------------------ samples/snippets/noxfile.py | 2 - synth.metadata | 3 +- 3 files changed, 1 insertion(+), 195 deletions(-) delete mode 100644 samples/snippets/README.rst diff --git a/samples/snippets/README.rst b/samples/snippets/README.rst deleted file mode 100644 index 9a38dca7d..000000000 --- a/samples/snippets/README.rst +++ /dev/null @@ -1,191 +0,0 @@ - -.. This file is automatically generated. Do not edit this file directly. - -Cloud Logging Python Samples -=============================================================================== - -.. image:: https://gstatic.com/cloudssh/images/open-btn.png - :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor=logging/cloud-client/README.rst - - -This directory contains samples for Cloud Logging. `Cloud Logging`_ allows you to store, search, analyze, monitor, and alert on log data and events from Google Cloud Platform and Amazon Web Services. - - - - -.. _Cloud Logging: https://cloud.google.com/logging/docs - - -Setup -------------------------------------------------------------------------------- - - - -Authentication -++++++++++++++ - -This sample requires you to have authentication setup. Refer to the -`Authentication Getting Started Guide`_ for instructions on setting up -credentials for applications. - -.. _Authentication Getting Started Guide: - https://cloud.google.com/docs/authentication/getting-started - - - - -Install Dependencies -++++++++++++++++++++ - -#. Clone python-docs-samples and change directory to the sample directory you want to use. - - .. code-block:: bash - - $ git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git - -#. Install `pip`_ and `virtualenv`_ if you do not already have them. You may want to refer to the `Python Development Environment Setup Guide`_ for Google Cloud Platform for instructions. - - .. _Python Development Environment Setup Guide: - https://cloud.google.com/python/setup - -#. Create a virtualenv. Samples are compatible with Python 3.6+. - - .. code-block:: bash - - $ virtualenv env - $ source env/bin/activate - -#. Install the dependencies needed to run the samples. - - .. code-block:: bash - - $ pip install -r requirements.txt - -.. _pip: https://pip.pypa.io/ -.. _virtualenv: https://virtualenv.pypa.io/ - - - - - - -Samples -------------------------------------------------------------------------------- - - -Quickstart -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -.. image:: https://gstatic.com/cloudssh/images/open-btn.png - :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor=logging/cloud-client/quickstart.py,logging/cloud-client/README.rst - - - - -To run this sample: - -.. code-block:: bash - - $ python quickstart.py - - - - -Snippets -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -.. image:: https://gstatic.com/cloudssh/images/open-btn.png - :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor=logging/cloud-client/snippets.py,logging/cloud-client/README.rst - - - - -To run this sample: - -.. code-block:: bash - - $ python snippets.py - - - usage: snippets.py [-h] logger_name {list,write,delete} ... - - This application demonstrates how to perform basic operations on logs and - log entries with Cloud Logging. - - For more information, see the README.md under /logging and the - documentation at https://cloud.google.com/logging/docs. - - positional arguments: - logger_name Logger name - {list,write,delete} - list Lists the most recent entries for a given logger. - write Writes log entries to the given logger. - delete Deletes a logger and all its entries. Note that a - deletion can take several minutes to take effect. - - optional arguments: - -h, --help show this help message and exit - - - - - -Export -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -.. image:: https://gstatic.com/cloudssh/images/open-btn.png - :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor=logging/cloud-client/export.py,logging/cloud-client/README.rst - - - - -To run this sample: - -.. code-block:: bash - - $ python export.py - - - usage: export.py [-h] {list,create,update,delete} ... - - positional arguments: - {list,create,update,delete} - list Lists all sinks. - create Lists all sinks. - update Changes a sink's filter. The filter determines which - logs this sink matches and will be exported to the - destination. For example a filter of 'severity>=INFO' - will send all logs that have a severity of INFO or - greater to the destination. See https://cloud.google.c - om/logging/docs/view/advanced_filters for more filter - information. - delete Deletes a sink. - - optional arguments: - -h, --help show this help message and exit - - - - - - - - - -The client library -------------------------------------------------------------------------------- - -This sample uses the `Google Cloud Client Library for Python`_. -You can read the documentation for more details on API usage and use GitHub -to `browse the source`_ and `report issues`_. - -.. _Google Cloud Client Library for Python: - https://googlecloudplatform.github.io/google-cloud-python/ -.. _browse the source: - https://github.com/GoogleCloudPlatform/google-cloud-python -.. _report issues: - https://github.com/GoogleCloudPlatform/google-cloud-python/issues - - - -.. _Google Cloud SDK: https://cloud.google.com/sdk/ diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py index 2a0629078..b90eef00f 100644 --- a/samples/snippets/noxfile.py +++ b/samples/snippets/noxfile.py @@ -148,8 +148,6 @@ def lint(session): "." ] session.run("flake8", *args) - - # # Black # diff --git a/synth.metadata b/synth.metadata index 4e26d4877..5a3b3f49e 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,7 +4,7 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/python-logging.git", - "sha": "4e24b3c360adef8d7761573d789867857586337d" + "sha": "7eaa5853f3a45e3db015a09841b98aeab461e6f3" } }, { @@ -134,7 +134,6 @@ "renovate.json", "samples/AUTHORING_GUIDE.md", "samples/CONTRIBUTING.md", - "samples/snippets/README.rst", "samples/snippets/noxfile.py", "scripts/decrypt-secrets.sh", "scripts/readme-gen/readme_gen.py", From 816ae6f0a684789ab2b29aa922514429bcc106e1 Mon Sep 17 00:00:00 2001 From: yoshi-automation Date: Thu, 10 Dec 2020 14:47:51 -0800 Subject: [PATCH 2/2] feat: add the Tailing API to get a live stream of the tail end of filtered logs PiperOrigin-RevId: 344435830 Source-Author: Google APIs Source-Date: Thu Nov 26 09:56:05 2020 -0800 Source-Repo: googleapis/googleapis Source-Sha: e8857c4c36948e7e0500377cd7fcecbf2459afc8 Source-Link: https://github.com/googleapis/googleapis/commit/e8857c4c36948e7e0500377cd7fcecbf2459afc8 --- google/cloud/logging_v2/proto/logging.proto | 99 +++++++++++++++ .../logging_service_v2/async_client.py | 67 +++++++++- .../services/logging_service_v2/client.py | 54 +++++++- .../logging_service_v2/transports/base.py | 27 ++++ .../logging_service_v2/transports/grpc.py | 28 +++++ .../transports/grpc_asyncio.py | 30 +++++ google/cloud/logging_v2/types/__init__.py | 4 + google/cloud/logging_v2/types/logging.py | 118 ++++++++++++++++++ synth.metadata | 4 +- .../logging_v2/test_logging_service_v2.py | 76 +++++++++++ 10 files changed, 503 insertions(+), 4 deletions(-) diff --git a/google/cloud/logging_v2/proto/logging.proto b/google/cloud/logging_v2/proto/logging.proto index 58647b92f..f8b01a71e 100644 --- a/google/cloud/logging_v2/proto/logging.proto +++ b/google/cloud/logging_v2/proto/logging.proto @@ -125,6 +125,15 @@ service LoggingServiceV2 { }; option (google.api.method_signature) = "parent"; } + + // Streaming read of log entries as they are ingested. Until the stream is + // terminated, it will continue reading logs. + rpc TailLogEntries(stream TailLogEntriesRequest) returns (stream TailLogEntriesResponse) { + option (google.api.http) = { + post: "/v2/entries:tail" + body: "*" + }; + } } // The parameters to DeleteLog. @@ -254,6 +263,11 @@ message ListLogEntriesRequest { // "billingAccounts/[BILLING_ACCOUNT_ID]" // "folders/[FOLDER_ID]" // + // May alternatively be one or more views + // projects/[PROJECT_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID] + // organization/[ORGANIZATION_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID] + // billingAccounts/[BILLING_ACCOUNT_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID] + // folders/[FOLDER_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID] // // Projects listed in the `project_ids` field are added to this list. repeated string resource_names = 8 [ @@ -363,6 +377,19 @@ message ListLogsRequest { // `nextPageToken` from the previous response. The values of other method // parameters should be identical to those in the previous call. string page_token = 3 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. The resource name that owns the logs: + // projects/[PROJECT_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID] + // organization/[ORGANIZATION_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID] + // billingAccounts/[BILLING_ACCOUNT_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID] + // folders/[FOLDER_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID] + // + // To support legacy queries, it could also be: + // "projects/[PROJECT_ID]" + // "organizations/[ORGANIZATION_ID]" + // "billingAccounts/[BILLING_ACCOUNT_ID]" + // "folders/[FOLDER_ID]" + repeated string resource_names = 8 [(google.api.field_behavior) = OPTIONAL]; } // Result returned from ListLogs. @@ -377,3 +404,75 @@ message ListLogsResponse { // method again using the value of `nextPageToken` as `pageToken`. string next_page_token = 2; } + +// The parameters to `TailLogEntries`. +message TailLogEntriesRequest { + // Required. Name of a parent resource from which to retrieve log entries: + // + // "projects/[PROJECT_ID]" + // "organizations/[ORGANIZATION_ID]" + // "billingAccounts/[BILLING_ACCOUNT_ID]" + // "folders/[FOLDER_ID]" + // + // May alternatively be one or more views: + // "projects/[PROJECT_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID]" + // "organization/[ORGANIZATION_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID]" + // "billingAccounts/[BILLING_ACCOUNT_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID]" + // "folders/[FOLDER_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID]" + repeated string resource_names = 1 [(google.api.field_behavior) = REQUIRED]; + + // Optional. A filter that chooses which log entries to return. See [Advanced + // Logs Filters](https://cloud.google.com/logging/docs/view/advanced_filters). + // Only log entries that match the filter are returned. An empty filter + // matches all log entries in the resources listed in `resource_names`. + // Referencing a parent resource that is not in `resource_names` will cause + // the filter to return no results. The maximum length of the filter is 20000 + // characters. + string filter = 2 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. The amount of time to buffer log entries at the server before + // being returned to prevent out of order results due to late arriving log + // entries. Valid values are between 0-60000 milliseconds. Defaults to 2000 + // milliseconds. + google.protobuf.Duration buffer_window = 3 [(google.api.field_behavior) = OPTIONAL]; +} + +// Result returned from `TailLogEntries`. +message TailLogEntriesResponse { + // Information about entries that were omitted from the session. + message SuppressionInfo { + // An indicator of why entries were omitted. + enum Reason { + // Unexpected default. + REASON_UNSPECIFIED = 0; + + // Indicates suppression occurred due to relevant entries being + // received in excess of rate limits. For quotas and limits, see + // [Logging API quotas and + // limits](https://cloud.google.com/logging/quotas#api-limits). + RATE_LIMIT = 1; + + // Indicates suppression occurred due to the client not consuming + // responses quickly enough. + NOT_CONSUMED = 2; + } + + // The reason that entries were omitted from the session. + Reason reason = 1; + + // A lower bound on the count of entries omitted due to `reason`. + int32 suppressed_count = 2; + } + + // A list of log entries. Each response in the stream will order entries with + // increasing values of `LogEntry.timestamp`. Ordering is not guaranteed + // between separate responses. + repeated LogEntry entries = 1; + + // If entries that otherwise would have been included in the session were not + // sent back to the client, counts of relevant entries omitted from the + // session with the reason that they were not included. There will be at most + // one of each reason per response. The counts represent the number of + // suppressed entries since the last streamed response. + repeated SuppressionInfo suppression_info = 2; +} diff --git a/google/cloud/logging_v2/services/logging_service_v2/async_client.py b/google/cloud/logging_v2/services/logging_service_v2/async_client.py index e6dd57247..82ee957a3 100644 --- a/google/cloud/logging_v2/services/logging_service_v2/async_client.py +++ b/google/cloud/logging_v2/services/logging_service_v2/async_client.py @@ -18,7 +18,16 @@ from collections import OrderedDict import functools import re -from typing import Dict, Sequence, Tuple, Type, Union +from typing import ( + Dict, + AsyncIterable, + Awaitable, + AsyncIterator, + Sequence, + Tuple, + Type, + Union, +) import pkg_resources import google.api_core.client_options as ClientOptions # type: ignore @@ -430,6 +439,12 @@ async def list_log_entries( "billingAccounts/[BILLING_ACCOUNT_ID]" "folders/[FOLDER_ID]" + May alternatively be one or more views + projects/[PROJECT_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID] + organization/[ORGANIZATION_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID] + billingAccounts/[BILLING_ACCOUNT_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID] + folders/[FOLDER_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID] + Projects listed in the ``project_ids`` field are added to this list. This corresponds to the ``resource_names`` field @@ -690,6 +705,56 @@ async def list_logs( # Done; return the response. return response + def tail_log_entries( + self, + requests: AsyncIterator[logging.TailLogEntriesRequest] = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> Awaitable[AsyncIterable[logging.TailLogEntriesResponse]]: + r"""Streaming read of log entries as they are ingested. + Until the stream is terminated, it will continue reading + logs. + + Args: + requests (AsyncIterator[`~.logging.TailLogEntriesRequest`]): + The request object AsyncIterator. The parameters to `TailLogEntries`. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + AsyncIterable[~.logging.TailLogEntriesResponse]: + Result returned from ``TailLogEntries``. + """ + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.tail_log_entries, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, + exceptions.InternalServerError, + exceptions.ServiceUnavailable, + ), + ), + default_timeout=3600.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Send the request. + response = rpc(requests, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + try: DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( diff --git a/google/cloud/logging_v2/services/logging_service_v2/client.py b/google/cloud/logging_v2/services/logging_service_v2/client.py index 79a9ed1af..a54252bf7 100644 --- a/google/cloud/logging_v2/services/logging_service_v2/client.py +++ b/google/cloud/logging_v2/services/logging_service_v2/client.py @@ -19,7 +19,17 @@ from distutils import util import os import re -from typing import Callable, Dict, Optional, Sequence, Tuple, Type, Union +from typing import ( + Callable, + Dict, + Optional, + Iterable, + Iterator, + Sequence, + Tuple, + Type, + Union, +) import pkg_resources from google.api_core import client_options as client_options_lib # type: ignore @@ -598,6 +608,12 @@ def list_log_entries( "billingAccounts/[BILLING_ACCOUNT_ID]" "folders/[FOLDER_ID]" + May alternatively be one or more views + projects/[PROJECT_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID] + organization/[ORGANIZATION_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID] + billingAccounts/[BILLING_ACCOUNT_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID] + folders/[FOLDER_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID] + Projects listed in the ``project_ids`` field are added to this list. This corresponds to the ``resource_names`` field @@ -833,6 +849,42 @@ def list_logs( # Done; return the response. return response + def tail_log_entries( + self, + requests: Iterator[logging.TailLogEntriesRequest] = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> Iterable[logging.TailLogEntriesResponse]: + r"""Streaming read of log entries as they are ingested. + Until the stream is terminated, it will continue reading + logs. + + Args: + requests (Iterator[`~.logging.TailLogEntriesRequest`]): + The request object iterator. The parameters to `TailLogEntries`. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + Iterable[~.logging.TailLogEntriesResponse]: + Result returned from ``TailLogEntries``. + """ + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.tail_log_entries] + + # Send the request. + response = rpc(requests, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + try: DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( diff --git a/google/cloud/logging_v2/services/logging_service_v2/transports/base.py b/google/cloud/logging_v2/services/logging_service_v2/transports/base.py index c8bcbcbf9..be9dcdbfe 100644 --- a/google/cloud/logging_v2/services/logging_service_v2/transports/base.py +++ b/google/cloud/logging_v2/services/logging_service_v2/transports/base.py @@ -186,6 +186,21 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), + self.tail_log_entries: gapic_v1.method.wrap_method( + self.tail_log_entries, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, + exceptions.InternalServerError, + exceptions.ServiceUnavailable, + ), + ), + default_timeout=3600.0, + client_info=client_info, + ), } @property @@ -244,5 +259,17 @@ def list_logs( ]: raise NotImplementedError() + @property + def tail_log_entries( + self, + ) -> typing.Callable[ + [logging.TailLogEntriesRequest], + typing.Union[ + logging.TailLogEntriesResponse, + typing.Awaitable[logging.TailLogEntriesResponse], + ], + ]: + raise NotImplementedError() + __all__ = ("LoggingServiceV2Transport",) diff --git a/google/cloud/logging_v2/services/logging_service_v2/transports/grpc.py b/google/cloud/logging_v2/services/logging_service_v2/transports/grpc.py index 4c0636e47..d774281b9 100644 --- a/google/cloud/logging_v2/services/logging_service_v2/transports/grpc.py +++ b/google/cloud/logging_v2/services/logging_service_v2/transports/grpc.py @@ -380,5 +380,33 @@ def list_logs( ) return self._stubs["list_logs"] + @property + def tail_log_entries( + self, + ) -> Callable[[logging.TailLogEntriesRequest], logging.TailLogEntriesResponse]: + r"""Return a callable for the tail log entries method over gRPC. + + Streaming read of log entries as they are ingested. + Until the stream is terminated, it will continue reading + logs. + + Returns: + Callable[[~.TailLogEntriesRequest], + ~.TailLogEntriesResponse]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "tail_log_entries" not in self._stubs: + self._stubs["tail_log_entries"] = self.grpc_channel.stream_stream( + "/google.logging.v2.LoggingServiceV2/TailLogEntries", + request_serializer=logging.TailLogEntriesRequest.serialize, + response_deserializer=logging.TailLogEntriesResponse.deserialize, + ) + return self._stubs["tail_log_entries"] + __all__ = ("LoggingServiceV2GrpcTransport",) diff --git a/google/cloud/logging_v2/services/logging_service_v2/transports/grpc_asyncio.py b/google/cloud/logging_v2/services/logging_service_v2/transports/grpc_asyncio.py index 8a26a078e..686eb52e0 100644 --- a/google/cloud/logging_v2/services/logging_service_v2/transports/grpc_asyncio.py +++ b/google/cloud/logging_v2/services/logging_service_v2/transports/grpc_asyncio.py @@ -390,5 +390,35 @@ def list_logs( ) return self._stubs["list_logs"] + @property + def tail_log_entries( + self, + ) -> Callable[ + [logging.TailLogEntriesRequest], Awaitable[logging.TailLogEntriesResponse] + ]: + r"""Return a callable for the tail log entries method over gRPC. + + Streaming read of log entries as they are ingested. + Until the stream is terminated, it will continue reading + logs. + + Returns: + Callable[[~.TailLogEntriesRequest], + Awaitable[~.TailLogEntriesResponse]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "tail_log_entries" not in self._stubs: + self._stubs["tail_log_entries"] = self.grpc_channel.stream_stream( + "/google.logging.v2.LoggingServiceV2/TailLogEntries", + request_serializer=logging.TailLogEntriesRequest.serialize, + response_deserializer=logging.TailLogEntriesResponse.deserialize, + ) + return self._stubs["tail_log_entries"] + __all__ = ("LoggingServiceV2GrpcAsyncIOTransport",) diff --git a/google/cloud/logging_v2/types/__init__.py b/google/cloud/logging_v2/types/__init__.py index b24bf3b8c..4c85fbb46 100644 --- a/google/cloud/logging_v2/types/__init__.py +++ b/google/cloud/logging_v2/types/__init__.py @@ -66,6 +66,8 @@ ListMonitoredResourceDescriptorsResponse, ListLogsRequest, ListLogsResponse, + TailLogEntriesRequest, + TailLogEntriesResponse, ) from .logging_metrics import ( LogMetric, @@ -125,6 +127,8 @@ "ListMonitoredResourceDescriptorsResponse", "ListLogsRequest", "ListLogsResponse", + "TailLogEntriesRequest", + "TailLogEntriesResponse", "LogMetric", "ListLogMetricsRequest", "ListLogMetricsResponse", diff --git a/google/cloud/logging_v2/types/logging.py b/google/cloud/logging_v2/types/logging.py index 0d44439ab..cec8993f5 100644 --- a/google/cloud/logging_v2/types/logging.py +++ b/google/cloud/logging_v2/types/logging.py @@ -20,6 +20,7 @@ from google.api import monitored_resource_pb2 as monitored_resource # type: ignore from google.cloud.logging_v2.types import log_entry +from google.protobuf import duration_pb2 as duration # type: ignore from google.rpc import status_pb2 as status # type: ignore @@ -36,6 +37,8 @@ "ListMonitoredResourceDescriptorsResponse", "ListLogsRequest", "ListLogsResponse", + "TailLogEntriesRequest", + "TailLogEntriesResponse", }, ) @@ -208,6 +211,12 @@ class ListLogEntriesRequest(proto.Message): "billingAccounts/[BILLING_ACCOUNT_ID]" "folders/[FOLDER_ID]" + May alternatively be one or more views + projects/[PROJECT_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID] + organization/[ORGANIZATION_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID] + billingAccounts/[BILLING_ACCOUNT_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID] + folders/[FOLDER_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID] + Projects listed in the ``project_ids`` field are added to this list. filter (str): @@ -358,6 +367,16 @@ class ListLogsRequest(proto.Message): ``pageToken`` must be the value of ``nextPageToken`` from the previous response. The values of other method parameters should be identical to those in the previous call. + resource_names (Sequence[str]): + Optional. The resource name that owns the logs: + projects/[PROJECT_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID] + organization/[ORGANIZATION_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID] + billingAccounts/[BILLING_ACCOUNT_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID] + folders/[FOLDER_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID] + + To support legacy queries, it could also be: + "projects/[PROJECT_ID]" "organizations/[ORGANIZATION_ID]" + "billingAccounts/[BILLING_ACCOUNT_ID]" "folders/[FOLDER_ID]". """ parent = proto.Field(proto.STRING, number=1) @@ -366,6 +385,8 @@ class ListLogsRequest(proto.Message): page_token = proto.Field(proto.STRING, number=3) + resource_names = proto.RepeatedField(proto.STRING, number=8) + class ListLogsResponse(proto.Message): r"""Result returned from ListLogs. @@ -391,4 +412,101 @@ def raw_page(self): next_page_token = proto.Field(proto.STRING, number=2) +class TailLogEntriesRequest(proto.Message): + r"""The parameters to ``TailLogEntries``. + + Attributes: + resource_names (Sequence[str]): + Required. Name of a parent resource from which to retrieve + log entries: + + :: + + "projects/[PROJECT_ID]" + "organizations/[ORGANIZATION_ID]" + "billingAccounts/[BILLING_ACCOUNT_ID]" + "folders/[FOLDER_ID]" + + May alternatively be one or more views: + "projects/[PROJECT_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID]" + "organization/[ORGANIZATION_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID]" + "billingAccounts/[BILLING_ACCOUNT_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID]" + "folders/[FOLDER_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID]". + filter (str): + Optional. A filter that chooses which log entries to return. + See `Advanced Logs + Filters `__. + Only log entries that match the filter are returned. An + empty filter matches all log entries in the resources listed + in ``resource_names``. Referencing a parent resource that is + not in ``resource_names`` will cause the filter to return no + results. The maximum length of the filter is 20000 + characters. + buffer_window (~.duration.Duration): + Optional. The amount of time to buffer log + entries at the server before being returned to + prevent out of order results due to late + arriving log entries. Valid values are between + 0-60000 milliseconds. Defaults to 2000 + milliseconds. + """ + + resource_names = proto.RepeatedField(proto.STRING, number=1) + + filter = proto.Field(proto.STRING, number=2) + + buffer_window = proto.Field(proto.MESSAGE, number=3, message=duration.Duration,) + + +class TailLogEntriesResponse(proto.Message): + r"""Result returned from ``TailLogEntries``. + + Attributes: + entries (Sequence[~.log_entry.LogEntry]): + A list of log entries. Each response in the stream will + order entries with increasing values of + ``LogEntry.timestamp``. Ordering is not guaranteed between + separate responses. + suppression_info (Sequence[~.logging.TailLogEntriesResponse.SuppressionInfo]): + If entries that otherwise would have been + included in the session were not sent back to + the client, counts of relevant entries omitted + from the session with the reason that they were + not included. There will be at most one of each + reason per response. The counts represent the + number of suppressed entries since the last + streamed response. + """ + + class SuppressionInfo(proto.Message): + r"""Information about entries that were omitted from the session. + + Attributes: + reason (~.logging.TailLogEntriesResponse.SuppressionInfo.Reason): + The reason that entries were omitted from the + session. + suppressed_count (int): + A lower bound on the count of entries omitted due to + ``reason``. + """ + + class Reason(proto.Enum): + r"""An indicator of why entries were omitted.""" + REASON_UNSPECIFIED = 0 + RATE_LIMIT = 1 + NOT_CONSUMED = 2 + + reason = proto.Field( + proto.ENUM, number=1, enum="TailLogEntriesResponse.SuppressionInfo.Reason", + ) + + suppressed_count = proto.Field(proto.INT32, number=2) + + entries = proto.RepeatedField(proto.MESSAGE, number=1, message=log_entry.LogEntry,) + + suppression_info = proto.RepeatedField( + proto.MESSAGE, number=2, message=SuppressionInfo, + ) + + __all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/synth.metadata b/synth.metadata index 5a3b3f49e..de6f5186a 100644 --- a/synth.metadata +++ b/synth.metadata @@ -11,8 +11,8 @@ "git": { "name": "googleapis", "remote": "https://github.com/googleapis/googleapis.git", - "sha": "be0bdf86cd31aa7c1a7b30a9a2e9f2fd53ee3d91", - "internalRef": "342353190" + "sha": "e8857c4c36948e7e0500377cd7fcecbf2459afc8", + "internalRef": "344435830" } }, { diff --git a/tests/unit/gapic/logging_v2/test_logging_service_v2.py b/tests/unit/gapic/logging_v2/test_logging_service_v2.py index 2c08f63b2..f6cb5d7a1 100644 --- a/tests/unit/gapic/logging_v2/test_logging_service_v2.py +++ b/tests/unit/gapic/logging_v2/test_logging_service_v2.py @@ -1698,6 +1698,81 @@ async def test_list_logs_async_pages(): assert page_.raw_page.next_page_token == token +def test_tail_log_entries( + transport: str = "grpc", request_type=logging.TailLogEntriesRequest +): + client = LoggingServiceV2Client( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + requests = [request] + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.tail_log_entries), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = iter([logging.TailLogEntriesResponse()]) + + response = client.tail_log_entries(iter(requests)) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert next(args[0]) == request + + # Establish that the response is the type that we expect. + for message in response: + assert isinstance(message, logging.TailLogEntriesResponse) + + +def test_tail_log_entries_from_dict(): + test_tail_log_entries(request_type=dict) + + +@pytest.mark.asyncio +async def test_tail_log_entries_async( + transport: str = "grpc_asyncio", request_type=logging.TailLogEntriesRequest +): + client = LoggingServiceV2AsyncClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + requests = [request] + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.tail_log_entries), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = mock.Mock(aio.StreamStreamCall, autospec=True) + call.return_value.read = mock.AsyncMock( + side_effect=[logging.TailLogEntriesResponse()] + ) + + response = await client.tail_log_entries(iter(requests)) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert next(args[0]) == request + + # Establish that the response is the type that we expect. + message = await response.read() + assert isinstance(message, logging.TailLogEntriesResponse) + + +@pytest.mark.asyncio +async def test_tail_log_entries_async_from_dict(): + await test_tail_log_entries_async(request_type=dict) + + def test_credentials_transport_error(): # It is an error to provide credentials and a transport instance. transport = transports.LoggingServiceV2GrpcTransport( @@ -1800,6 +1875,7 @@ def test_logging_service_v2_base_transport(): "list_log_entries", "list_monitored_resource_descriptors", "list_logs", + "tail_log_entries", ) for method in methods: with pytest.raises(NotImplementedError):