ثبت

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

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

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

زبان راهنما
جاوا اسناد ثبت گزارش برای Java
دات نت اسناد ثبت گزارش برای 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 خود استفاده کنید. این کار راه‌اندازی داشبوردها و هشدارها را در Google Cloud Monitoring برای استفاده از سنجه‌های ثبت‌شده ممکن می‌سازد.

‫Cloud Logging برای همه زبان‌های کتابخانه مشتری «میانای برنامه‌سازی کاربردی Google Ads» پشتیبانی‌شده، به‌جز 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» از رهگیرهای gRPC برای ارائه پشتیبانی گزارش‌گیری داخلی استفاده می‌کنند. به‌همین ترتیب، می‌توانید میان‌گیر gRPC سفارشی را برای دسترسی به اشیای درخواست و پاسخ، استخراج اطلاعات برای اهداف گزارش‌گیری و نظارت، و نوشتن آن داده‌ها در مکان انتخابی خود پیاده‌سازی کنید.

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

رهگیر Cloud Logging سفارشی در 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