ثبت

ثبت گزارش و نظارت به‌طور هم‌زمان کار می‌کنند تا به شما کمک کنند عملکرد برنامه را درک و بهینه‌سازی کنید، و همچنین خطاها و مشکلات مربوط به سیستم را تشخیص دهید. باید گزارش‌های خلاصه را برای همه فراخوانی‌های میانای برنامه‌سازی کاربردی و گزارش‌های تفصیلی را برای فراخوانی‌های ناموفق میانای برنامه‌سازی کاربردی روشن کنید تا بتوانید گزارش‌های فراخوانی میانای برنامه‌سازی کاربردی را درصورت نیاز به پشتیبانی فنی ارائه دهید.

گزارش‌گیری کتابخانه کارخواه

کتابخانه‌های کارخواه Google Ads API با ثبت گزارش داخلی ارائه می‌شوند. برای جزئیات ثبت گزارش مختص پلاتفرم، به اسناد ثبت گزارش در کتابخانه کارخواه انتخابی‌تان مراجعه کنید.

زبان راهنما
جاوا اسناد ثبت گزارش برای جاوا
دات نت اسناد ثبت گزارش برای NET.
PHP اسناد ثبت گزارش برای PHP
Python اسناد ثبت گزارش برای Python
Ruby اسناد ثبت گزارش برای Ruby
پرل اسناد ثبت گزارش برای Perl

قالب گزارش

کتابخانه‌های کارخواه Google Ads API برای هر فراخوانی میانای برنامه‌سازی کاربردی، یک گزارش دقیق و یک گزارش خلاصه تولید می‌کنند. گزارش دقیق حاوی همه جزئیات فراخوانی میانای برنامه‌سازی کاربردی است، درحالی‌که گزارش خلاصه حاوی حداقل جزئیات فراخوانی میانای برنامه‌سازی کاربردی است. نمونه‌ای از هر نوع گزارش نشان داده شده است، با گزارش‌های کوتاه و قالب‌بندی‌شده برای خوانایی.

گزارش خلاصه

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

گزارش مشروح

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

Request
-------

Method Name: /google.ads.googleads.v26.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",
  "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----------------

اگر از کتابخانه کارخواه استفاده نکنم چه می‌شود؟

اگر از کتابخانه کارخواه استفاده نمی‌کنید، ثبت گزارش خودتان را برای ضبط جزئیات تماس‌های API ورودی و خروجی پیاده‌سازی کنید. باید حداقل مقدار سرایند پاسخ request-id را ثبت کنید، که سپس درصورت نیاز می‌تواند با تیم‌های پشتیبانی فنی هم‌رسانی شود.

درحال ثبت در فضای ابری

ابزارهای زیادی وجود دارد که می‌توانید برای ضبط گزارش‌ها و سنجه‌های عملکرد برای برنامه‌تان استفاده کنید. برای مثال، می‌توانید از Google Cloud Logging برای ثبت سنجه‌های عملکرد در Google Cloud Project استفاده کنید. این امر راه‌اندازی داشبوردها و هشدارها را در Google Cloud Monitoring برای استفاده از سنجه‌های ثبت‌شده ممکن می‌سازد.

Cloud Logging کتابخانه‌های کارخواه را برای همه زبان‌های کتابخانه کارخواه Google Ads API پشتیبانی‌شده به‌جز Perl ارائه می‌دهد، بنابراین در بیشتر موارد امکان ثبت گزارش با Cloud Logging مستقیماً از ادغام کتابخانه کارخواه شما وجود دارد. برای زبان‌های دیگر ازجمله Perl،‏ Cloud Logging همچنین یک REST API ارائه می‌دهد.

چند گزینه برای ورود به سیستم Cloud Logging یا ابزار دیگری از کتابخانه کارخواه Google Ads API وجود دارد. هر گزینه با معاوضه‌های خاص خود ازنظر زمان پیاده‌سازی، پیچیدگی، و عملکرد همراه است. پیش‌از تصمیم‌گیری درباره اینکه کدام راه‌حل را اجرا کنید، به‌دقت درباره این معاوضه‌ها فکر کنید.

گزینه ۱: نوشتن گزارش‌های محلی از فرایند پس‌زمینه‌ای

گزارش‌های کتابخانه مشتری را می‌توان با اصلاح پیکربندی گزارش‌گیری در فایل محلی در دستگاهتان نوشت. وقتی گزارش‌ها در فایل محلی برونداد شدند، می‌توانید یک دیمون راه‌اندازی کنید تا گزارش‌ها را جمع‌آوری کند و به فضای ابری ارسال کند.

یکی‌از محدودیت‌های این رویکرد این است که برخی‌از سنجه‌های عملکرد به‌طور پیش‌فرض ثبت نمی‌شوند. گزارش‌های کتابخانه مشتری شامل جزئیات مربوط به اشیاء درخواست و پاسخ است، بنابراین سنجه‌های تأخیر گنجانده نخواهد شد، مگراینکه تغییرات بیشتری برای ثبت این موارد نیز انجام شود.

گزینه ۲: استفاده از Compute Engine و Ops Agent

اگر برنامه‌تان در Compute Engine اجرا می‌شود، می‌توانید با نصب Ops Agent، گزارش‌هایتان را به Google Cloud Logging ارسال کنید. می‌توانید Ops Agent را پیکربندی کنید تا علاوه‌بر سنجه‌ها و گزارش‌هایی که به‌طور پیش‌فرض ارسال می‌شوند، گزارش‌های برنامه‌تان را به Cloud Logging ارسال کند.

اگر برنامه‌تان ازقبل در محیط Google Cloud اجرا می‌شود، یا اگر قصد دارید برنامه‌تان را به Google Cloud منتقل کنید، این گزینه عالی‌ای برای بررسی است.

گزینه ۳: پیاده‌سازی ثبت گزارش در کد برنامه

ثبت مستقیم از کد برنامه می‌تواند به یکی از دو روش زیر انجام شود:

  1. محاسبات سنجه‌ها و عبارت‌های گزارش را در هر مکان قابل‌اجرا در کدتان بگنجانید. این گزینه برای پایگاه‌های کد کوچک‌تر امکان‌پذیرتر است، زیرا دامنه و هزینه‌های نگهداری چنین تغییری حداقل خواهد بود.

  2. درحال پیاده‌سازی میانای گزارش‌گیری. اگر منطق برنامه را بتوان انتزاعی کرد به‌طوری‌که بخش‌های مختلف برنامه از یک کلاس پایه مشترک ارث‌بری کنند، منطق ثبت گزارش را می‌توان در آن کلاس پایه پیاده‌سازی کرد. این گزینه عموماً بر گنجاندن عبارت‌های گزارش در سراسر کد برنامه ترجیح داده می‌شود، زیرا نگهداری و مقیاس‌بندی آن آسان‌تر است. برای پایگاه‌های کد بزرگ‌تر، قابلیت نگهداری و مقیاس‌پذیری این راه‌حل اهمیت بیشتری پیدا می‌کند.

یکی‌از محدودیت‌های این رویکرد این است که گزارش‌های کامل درخواست و پاسخ از کد برنامه دردسترس نیست. از رهگیرهای gRPC می‌توان به اشیاء کامل درخواست و پاسخ دسترسی پیدا کرد؛ این روشی است که ثبت گزارش کتابخانه مشتری داخلی گزارش‌های درخواست و پاسخ را دریافت می‌کند. درصورت بروز خطا، ممکن است اطلاعات بیشتری در شیء استثنا دردسترس باشد، اما جزئیات کمتری برای پاسخ‌های موفق در منطق برنامه دردسترس است. برای مثال، در اکثر موارد، شناسه درخواست برای درخواست موفق ازطریق اشیای پاسخ Google Ads API قابل‌دسترسی نیست.

گزینه ۴: رهگیر گزارش‌گیری gRPC سفارشی

‫gRPC از رهگیرهای تک‌مقداری و جاری‌سازی پشتیبانی می‌کند که می‌توانند به اشیای درخواست و پاسخ درحین عبور بین کارخواه و سرور دسترسی داشته باشند. کتابخانه‌های کارخواه Google Ads API از رهگیرهای gRPC برای ارائه پشتیبانی ثبت داخلی استفاده می‌کنند. به‌طور مشابه، می‌توانید یک رهگیر gRPC سفارشی را برای دسترسی به اشیاء درخواست و پاسخ، استخراج اطلاعات برای اهداف ثبت و نظارت، و نوشتن آن داده‌ها در مکان انتخابی خود پیاده‌سازی کنید.

برخلاف برخی‌از راه‌حل‌های دیگری که در اینجا ارائه شده است، پیاده‌سازی رهگیر gRPC سفارشی به شما انعطاف‌پذیری می‌دهد تا اشیای درخواست و پاسخ را در هر درخواست ضبط کنید و منطق اضافی را برای ضبط جزئیات درخواست پیاده‌سازی کنید. برای مثال، می‌توانید زمان سپری‌شده درخواست را با پیاده‌سازی منطق زمان‌بندی عملکرد در خود رهگیر سفارشی محاسبه کنید، سپس سنجه را در Google Cloud Logging ثبت کنید تا برای پایش تأخیر در Google Cloud Monitoring دردسترس قرار گیرد.

رهگیر Cloud Logging سفارشی در Python

برای نشان دادن این راه‌حل، نمونه‌ای از رهگیر ثبت سفارشی را در Python نوشته‌ایم. رهگیر سفارشی ایجاد می‌شود و به کارخواه سرویس منتقل می‌شود. سپس به اشیاء درخواست و پاسخ که ازطریق هر فراخوانی روش سرویس عبور می‌کنند دسترسی پیدا می‌کند، داده‌های آن اشیاء را پردازش می‌کند، و داده‌ها را به Google Cloud Logging ارسال می‌کند.

علاوه‌بر داده‌هایی که از اشیای درخواست و پاسخ می‌آید، این نمونه منطق تکمیلی‌ای را برای ضبط زمان سپری‌شده درخواست و برخی فراداده‌های دیگر که برای اهداف نظارت مفید است (مثل موفقیت‌آمیز بودن یا نبودن درخواست) پیاده‌سازی می‌کند. برای اطلاعات بیشتر درباره اینکه این اطلاعات چگونه می‌تواند مفید باشد، هم به‌طور کلی برای پایش و هم به‌طور خاص هنگام ترکیب Google Cloud Logging و Google Cloud Monitoring، به راهنمای پایش مراجعه کنید.

# 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 time
from typing import Any, Callable, Dict, Optional

from google.cloud import logging as google_cloud_logging
from grpc._interceptor import _ClientCallDetails

from google.ads.googleads.interceptors import LoggingInterceptor


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: str):
        """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: google_cloud_logging.Client = google_cloud_logging.Client()
        self.logger: google_cloud_logging.Logger = logging_client.logger("cloud_logging")
        self.rpc_start: float
        self.rpc_end: float

    def log_successful_request(
        self,
        method: str,
        customer_id: Optional[str],
        metadata_json: str,
        request_id: str,
        request: Any,  # google.ads.googleads.vX.services.types.SearchGoogleAdsRequest or SearchGoogleAdsStreamRequest
        trailing_metadata_json: str,
        response: Any,  # grpc.Call or grpc.Future
    ) -> None:
        """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: Any = 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: float = (self.rpc_end - self.rpc_start) * 1000

        debug_log: Dict[str, Any] = {
            "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: Dict[str, Any] = {
            "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: str,
        customer_id: Optional[str],
        metadata_json: str,
        request_id: str,
        request: Any,  # google.ads.googleads.vX.services.types.SearchGoogleAdsRequest or SearchGoogleAdsStreamRequest
        trailing_metadata_json: str,
        response: Any,  # grpc.Call or grpc.Future
    ) -> None:
        """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: Any = self._get_error_from_response(response)
        exception_str: str = self._parse_exception_to_str(exception)
        fault_message: str = self._get_fault_message(exception)

        info_log: Dict[str, Any] = {
            "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: Dict[str, Any] = {
            "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: Callable[[_ClientCallDetails, Any], Any], # Any is request type
        client_call_details: _ClientCallDetails,
        request: Any,  # google.ads.googleads.vX.services.types.SearchGoogleAdsRequest
    ) -> Any:  # grpc.Call or grpc.Future
        """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: Any) -> None: # response_future is grpc.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: Any = continuation(client_call_details, request) # response is grpc.Call or grpc.Future

        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: Callable[[_ClientCallDetails, Any], Any], # Any is request type
        client_call_details: _ClientCallDetails,
        request: Any,  # google.ads.googleads.vX.services.types.SearchGoogleAdsStreamRequest
    ) -> Any:  # grpc.Call or grpc.Future
        """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: Any) -> None: # response_future is grpc.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: Any = continuation(client_call_details, request) # response is grpc.Call or grpc.Future

        # 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