Logowanie

Logowanie i monitorowanie współdziałają ze sobą, ułatwiając zrozumienie i optymalizację wydajności aplikacji, a także diagnozowanie błędów i problemów. Włącz dzienniki podsumowania w przypadku wszystkich wywołań interfejsu API oraz szczegółowe logi nieudanych wywołań interfejsu API, które umożliwiają udostępnienie interfejsu API; rejestry połączeń, gdy potrzebujesz pomocy technicznej.

Logowanie biblioteki klienta

Biblioteki klienta interfejsu Google Ads API mają wbudowaną funkcję rejestrowania. Działanie związane z konkretną platformą należy zapoznać się z dokumentacją logowania w bibliotece klienta wyboru.

Język Przewodnik
Java Logowanie dokumentacji Java
.NET Logowanie dokumentacji .NET
PHP Logowanie dokumentacji PHP
Python Dokumentacja logowania w języku Python
Ruby Dokumentacja logowania w języku Ruby
Perl Logowanie dokumentacji Perl

Format logu

Biblioteki klienta interfejsu Google Ads API generują szczegółowy dziennik i podsumowanie log dla każdego wywołania interfejsu API. Szczegółowy dziennik zawiera wszystkie szczegóły dla wywołania API, podczas gdy dziennik podsumowania zawiera minimum szczegółów tego wywołania. Wyświetlany jest przykład każdego typu logu z przyciętymi i sformatowanymi logami na czytelność.

Dziennik podsumowania

GoogleAds.SummaryRequestLogs Warning: 1 : [2023-09-15 19:58:39Z] -
Request made: Host: , Method: /google.ads.googleads.v14.services.GoogleAdsService/SearchStream,
ClientCustomerID: 5951878031, RequestID: hELhBPNlEDd8mWYcZu7b8g,
IsFault: True, FaultMessage: Status(StatusCode="InvalidArgument",
Detail="Request contains an invalid argument.")

Szczegółowy dziennik

GoogleAds.DetailedRequestLogs Verbose: 1 : [2023-11-02 21:09:36Z] -
---------------BEGIN API CALL---------------

Request
-------

Method Name: /google.ads.googleads.v14.services.GoogleAdsService/SearchStream
Host:
Headers: {
  "x-goog-api-client": "gl-dotnet/5.0.0 gapic/17.0.1 gax/4.2.0 grpc/2.46.3 gccl/3.0.1 pb/3.21.5",
  "developer-token": "REDACTED",
  "login-customer-id": "1234567890",
  "x-goog-request-params": "customer_id=4567890123"
}

{ "customerId": "4567890123", "query": "SELECT ad_group_criterion.type FROM
  ad_group_criterion WHERE ad_group.status IN(ENABLED, PAUSED) AND
  campaign.status IN(ENABLED, PAUSED) ", "summaryRowSetting": "NO_SUMMARY_ROW" }

Response
--------
Headers: {
  "date": "Thu, 02 Nov 2023 21:09:35 GMT",
  "alt-svc": "h3-29=\":443\"; ma=2592000"
}

{
  "results": [ {
    "adGroupCriterion": {
      "resourceName": "customers/4567890123/adGroupCriteria/456789456789~123456123467",
      "type": "KEYWORD"
    } }, {
    "adGroupCriterion": {
      "resourceName": "customers/4567890123/adGroupCriteria/456789456789~56789056788",
      "type": "KEYWORD"
    } } ],
    "fieldMask": "adGroupCriterion.type", "requestId": "VsJ4F00ew6s9heHvAJ-abw"
}
----------------END API CALL----------------

Co zrobić, jeśli nie używam biblioteki klienta?

Jeśli nie korzystasz z biblioteki klienta, zaimplementuj własne rejestrowanie, aby rejestrować szczegóły wychodzących i przychodzących wywołań interfejsu API. Należy zapisać co najmniej wartości nagłówka odpowiedzi request-id, którą można udostępnić usłudze zespołami pomocy technicznej.

Logowanie do chmury

Istnieje wiele narzędzi, za pomocą których możesz rejestrować logi i wskaźniki wydajności Twojej aplikacji. Możesz na przykład użyć Google Cloud Logging do logowania i dane o wydajności do projektu Google Cloud. Dzięki temu można konfigurować panele i alerty w Google Cloud Monitoring. na korzystanie z zarejestrowanych wskaźników.

Usługa Cloud Logging udostępnia biblioteki klienta dla wszystkich obsługiwanych klientów korzystających z interfejsu Google Ads API z wyjątkiem Perl, więc w większości przypadków można zalogować się Cloud Logging bezpośrednio z poziomu integracji z biblioteką klienta. Inne języki takich jak Perl, Cloud Logging zawiera również interfejs API typu REST.

Dostępnych jest kilka opcji logowania do Cloud Logging lub innego narzędzia Biblioteka klienta interfejsu Google Ads API. Każda opcja wiąże się z innymi kompromisami czasu wdrażanie rozwiązań, złożoność i wydajność. Dobrze zastanów się nad tymi kompromisami przed podjęciem decyzji o wdrożeniu rozwiązania.

Opcja 1. Zapisz lokalne logi w chmurze z procesu w tle

Logi biblioteki klienta można zapisywać w pliku lokalnym na komputerze przez zmianę konfiguracji logowania. Po przesłaniu dzienników do pliku lokalnego możesz: skonfiguruj demona, aby zbierał logi i wysyłał je do chmury.

Ograniczeniem tego podejścia jest to, że niektóre dane o skuteczności domyślnie. Logi biblioteki klienta zawierają szczegóły z żądania oraz obiektów odpowiedzi, więc wskaźniki czasu oczekiwania nie będą uwzględniane, chyba że dodatkowe zmiany również te dane.

Opcja 2. Uruchom aplikację w Compute Engine i zainstaluj agenta operacyjnego

Jeśli Twoja aplikacja działa w Compute Engine, możesz wysłać swoją logi w Google Cloud Logging, instalując agenta operacyjnego. Operacje Agenta można skonfigurować tak, aby wysyłał logi aplikacji do Cloud Logowanie, jako uzupełnienie danych i logów wysyłanych domyślnie.

Jeśli Twoja aplikacja działa już w środowisku Google Cloud lub rozważają przeniesienie Twojej aplikacji do Google Cloud, to świetna opcja, i ujawniamy to, co naprawdę ważne.

Opcja 3. Zaimplementuj logowanie w kodzie aplikacji

Logowanie bezpośrednio z kodu aplikacji można wykonać na jeden z dwóch sposobów:

  1. uwzględnianie obliczeń wskaźników i wyciągów dziennika w każdym odpowiednią lokalizację w kodzie. Ta opcja jest bardziej dokładna dla mniejszych baz kodu, przy czym zakres i koszty utrzymania takiej zmiany jest minimalna.

  2. Wdrożenie interfejsu logowania. Czy logikę aplikacji można wyodrębnić tak aby różne części aplikacji dziedziczyły tę samą podstawę klasy podstawowej, można w niej zaimplementować logikę logowania. Ta opcja jest jest ogólnie wyższy od stosowania instrukcji dziennika w funkcji dla aplikacji, ponieważ jest łatwiejszy w utrzymaniu i skalowaniu. Większy bazuje na bazach kodu, łatwość obsługi i skalowalność rozwiązania. jest bardziej trafne.

Jednym z ograniczeń tego podejścia jest to, że pełne logi żądań i odpowiedzi są nie są dostępne w kodzie aplikacji. Obiekty pełnych żądań i odpowiedzi mogą uzyskiwać dostęp z modułów przechwytujących gRPC; tak działa wbudowana biblioteka klienta Logging pobiera logi żądań i odpowiedzi. W przypadku błędu dodatkowe informacje mogą być dostępne w obiekcie wyjątku, ale jest ich mniej dostępnych dla pomyślnych odpowiedzi w ramach logiki aplikacji. Na przykład w polu w większości przypadków identyfikator udanego żądania nie jest dostępny z Obiekty odpowiedzi interfejsu Google Ads API.

Opcja 4. Wdróż niestandardowy element przechwytujący logowanie gRPC

gRPC obsługuje elementy przechwytujące jednoargumentowe i streamingowe, które mają dostęp do obiektów żądań i odpowiedzi, które są przesyłane między klientem a serwerem. Biblioteki klienta interfejsu Google Ads API korzystają z mechanizmów przechwytujących gRPC, aby oferować wbudowane logowanie . W podobny sposób można wdrożyć niestandardowy element przechwytujący gRPC, aby uzyskać dostęp do obiekty żądań i odpowiedzi, wyodrębnianie informacji do logowania i monitorowania i zapisuj je w wybranej przez Ciebie lokalizacji.

W przeciwieństwie do niektórych innych przedstawionych tu rozwiązań implementacja niestandardowego wywołania gRPC umożliwia swobodę wychwytywania obiektów żądań i odpowiedzi każdego żądania i zaimplementować dodatkowe mechanizmy rejestrowania szczegółów żądania. Można np. obliczyć czas trwania żądania, implementując logikę czasu wydajności w niestandardowym elemencie przechwytującym, a następnie zapisz do Google Cloud Logging, aby można było monitorować czas oczekiwania w Google Cloud Monitoring.

Niestandardowy przechwytujący Google Cloud Logging w Pythonie

Aby zademonstrować to rozwiązanie, napisaliśmy przykład niestandardowego rejestrowania danych w Pythonie. Niestandardowy element przechwytujący jest tworzony i przekazywany do klienta usługi. Następnie uzyskuje dostęp do obiektów żądań i odpowiedzi, które przechodzą przy każdym wywołaniu metody usługi, przetwarza dane z tych obiektów oraz wysyła dane do Google Cloud Logging.

Oprócz danych pochodzących z obiektów żądań i odpowiedzi interfejs implementuje dodatkową logikę, aby przechwytywać czas, który upłynął oraz inne metadane, które mogłyby przydać się do monitorowania, np. czy żądanie zostało zrealizowane. Więcej informacji na ten temat mogą być przydatne, zarówno do monitorowania, jak i szczególnie wtedy, łącząc Google Cloud Logging i Google Cloud Monitoring, zapoznaj się z artykułem Monitorowanie .

# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""A custom gRPC Interceptor that logs requests and responses to Cloud Logging.

The custom interceptor object is passed into the get_service method of the
GoogleAdsClient. It intercepts requests and responses, parses them into a
human readable structure and logs them using the logging service instantiated
within the class (in this case, a Cloud Logging client).
"""

import logging
import time

from google.cloud import logging
from grpc import UnaryUnaryClientInterceptor, UnaryStreamClientInterceptor

from google.ads.googleads.interceptors import LoggingInterceptor, mask_message


class CloudLoggingInterceptor(LoggingInterceptor):
    """An interceptor that logs rpc request and response details to Google Cloud Logging.

    This class inherits logic from the LoggingInterceptor, which simplifies the
    implementation here. Some logic is required here in order to make the
    underlying logic work -- comments make note of this where applicable.
    NOTE: Inheriting from the LoggingInterceptor class could yield unexpected side
    effects. For example, if the LoggingInterceptor class is updated, this class would
    inherit the updated logic, which could affect its functionality. One option to avoid
    this is to inherit from the Interceptor class instead, and selectively copy whatever
    logic is needed from the LoggingInterceptor class."""

    def __init__(self, api_version):
        """Initializer for the CloudLoggingInterceptor.

        Args:
            api_version: a str of the API version of the request.
        """
        super().__init__(logger=None, api_version=api_version)
        # Instantiate the Cloud Logging client.
        logging_client = logging.Client()
        self.logger = logging_client.logger("cloud_logging")

    def log_successful_request(
        self,
        method,
        customer_id,
        metadata_json,
        request_id,
        request,
        trailing_metadata_json,
        response,
    ):
        """Handles logging of a successful request.

        Args:
            method: The method of the request.
            customer_id: The customer ID associated with the request.
            metadata_json: A JSON str of initial_metadata.
            request_id: A unique ID for the request provided in the response.
            request: An instance of a request proto message.
            trailing_metadata_json: A JSON str of trailing_metadata.
            response: A grpc.Call/grpc.Future instance.
        """
        # Retrieve and mask the RPC result from the response future.
        # This method is available from the LoggingInterceptor class.
        # Ensure self._cache is set in order for this to work.
        # The response result could contain up to 10,000 rows of data,
        # so consider truncating this value before logging it, to save
        # on data storage costs and maintain readability.
        result = self.retrieve_and_mask_result(response)

        # elapsed_ms is the approximate elapsed time of the RPC, in milliseconds.
        # There are different ways to define and measure elapsed time, so use
        # whatever approach makes sense for your monitoring purposes.
        # rpc_start and rpc_end are set in the intercept_unary_* methods below.
        elapsed_ms = (self.rpc_end - self.rpc_start) * 1000

        debug_log = {
            "method": method,
            "host": metadata_json,
            "request_id": request_id,
            "request": str(request),
            "headers": trailing_metadata_json,
            "response": str(result),
            "is_fault": False,
            "elapsed_ms": elapsed_ms,
        }
        self.logger.log_struct(debug_log, severity="DEBUG")

        info_log = {
            "customer_id": customer_id,
            "method": method,
            "request_id": request_id,
            "is_fault": False,
            # Available from the Interceptor class.
            "api_version": self._api_version,
        }
        self.logger.log_struct(info_log, severity="INFO")

    def log_failed_request(
        self,
        method,
        customer_id,
        metadata_json,
        request_id,
        request,
        trailing_metadata_json,
        response,
    ):
        """Handles logging of a failed request.

        Args:
            method: The method of the request.
            customer_id: The customer ID associated with the request.
            metadata_json: A JSON str of initial_metadata.
            request_id: A unique ID for the request provided in the response.
            request: An instance of a request proto message.
            trailing_metadata_json: A JSON str of trailing_metadata.
            response: A JSON str of the response message.
        """
        exception = self._get_error_from_response(response)
        exception_str = self._parse_exception_to_str(exception)
        fault_message = self._get_fault_message(exception)

        info_log = {
            "method": method,
            "endpoint": self.endpoint,
            "host": metadata_json,
            "request_id": request_id,
            "request": str(request),
            "headers": trailing_metadata_json,
            "exception": exception_str,
            "is_fault": True,
        }
        self.logger.log_struct(info_log, severity="INFO")

        error_log = {
            "method": method,
            "endpoint": self.endpoint,
            "request_id": request_id,
            "customer_id": customer_id,
            "is_fault": True,
            "fault_message": fault_message,
        }
        self.logger.log_struct(error_log, severity="ERROR")

    def intercept_unary_unary(self, continuation, client_call_details, request):
        """Intercepts and logs API interactions.

        Overrides abstract method defined in grpc.UnaryUnaryClientInterceptor.

        Args:
            continuation: a function to continue the request process.
            client_call_details: a grpc._interceptor._ClientCallDetails
                instance containing request metadata.
            request: a SearchGoogleAdsRequest or SearchGoogleAdsStreamRequest
                message class instance.

        Returns:
            A grpc.Call/grpc.Future instance representing a service response.
        """
        # Set the rpc_end value to current time when RPC completes.
        def update_rpc_end(response_future):
            self.rpc_end = time.perf_counter()

        # Capture precise clock time to later calculate approximate elapsed
        # time of the RPC.
        self.rpc_start = time.perf_counter()

        # The below call is REQUIRED.
        response = continuation(client_call_details, request)

        response.add_done_callback(update_rpc_end)

        self.log_request(client_call_details, request, response)

        # The below return is REQUIRED.
        return response

    def intercept_unary_stream(
        self, continuation, client_call_details, request
    ):
        """Intercepts and logs API interactions for Unary-Stream requests.

        Overrides abstract method defined in grpc.UnaryStreamClientInterceptor.

        Args:
            continuation: a function to continue the request process.
            client_call_details: a grpc._interceptor._ClientCallDetails
                instance containing request metadata.
            request: a SearchGoogleAdsRequest or SearchGoogleAdsStreamRequest
                message class instance.

        Returns:
            A grpc.Call/grpc.Future instance representing a service response.
        """

        def on_rpc_complete(response_future):
            self.rpc_end = time.perf_counter()
            self.log_request(client_call_details, request, response_future)

        # Capture precise clock time to later calculate approximate elapsed
        # time of the RPC.
        self.rpc_start = time.perf_counter()

        # The below call is REQUIRED.
        response = continuation(client_call_details, request)

        # Set self._cache to the cache on the response wrapper in order to
        # access the streaming logs. This is REQUIRED in order to log streaming
        # requests.
        self._cache = response.get_cache()

        response.add_done_callback(on_rpc_complete)

        # The below return is REQUIRED.
        return response