التسجيل

تعمل ميزة التسجيل والمراقبة معًا لمساعدتك في فهم أداء التطبيق وتحسينه، بالإضافة إلى تشخيص الأخطاء والمشاكل المتعلّقة بالنظام. عليك تفعيل السجلّات التلخيصية لجميع طلبات البيانات من واجهة برمجة التطبيقات والسجلّات التفصيلية لطلبات البيانات من واجهة برمجة التطبيقات التي تعذّر تنفيذها حتى تتمكّن من تقديم سجلّات طلبات البيانات من واجهة برمجة التطبيقات عند الحاجة إلى الدعم الفني.

تسجيل مكتبة العميل

تأتي مكتبات عملاء Google Ads API مزوّدة بميزة تسجيل مدمجة. للحصول على تفاصيل تسجيل البيانات الخاصة بالنظام الأساسي، يُرجى الرجوع إلى مستندات التسجيل ضمن مكتبة العميل المفضّلة لديك.

Language الدليل
Java مستندات تسجيل Java
NET. مستندات التسجيل لنظام ‎ .NET
PHP مستندات تسجيل PHP
Python مستندات التسجيل في Python
Ruby مستندات التسجيل في Ruby
Perl مستندات تسجيل Perl

تنسيق السجلّ

تُنشئ مكتبات عملاء Google Ads API سجلًّا تفصيليًا وسجلّ summary لكل طلب بيانات من واجهة برمجة التطبيقات. يحتوي السجلّ التفصيلي على كل تفاصيل طلب البيانات من واجهة برمجة التطبيقات، في حين يحتوي السجلّ الملخّص على الحد الأدنى من تفاصيل طلب البيانات من واجهة برمجة التطبيقات. يتم عرض مثال على كل نوع من أنواع السجلّات، مع اقتطاع السجلّات وتنسيقها لتسهيل قراءتها.

السجلّ التلخيصي

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.")

السجلّ المفصّل

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----------------

ماذا لو لم أستخدم مكتبة عملاء؟

إذا كنت لا تستخدم مكتبة عملاء، يمكنك تنفيذ عملية تسجيل خاصة بك لتسجيل تفاصيل طلبات البيانات الصادرة والواردة من واجهة برمجة التطبيقات. يجب تسجيل قيمة عنوان الاستجابة request-id على الأقل، والتي يمكن مشاركتها بعد ذلك مع فِرق الدعم الفني حسب الحاجة.

تسجيل البيانات في السحابة الإلكترونية

هناك العديد من الأدوات التي يمكنك استخدامها لتسجيل السجلات ومقاييس الأداء لتطبيقك. على سبيل المثال، يمكنك استخدام Google Cloud Logging لتسجيل مقاييس الأداء في مشروعك على Google Cloud. يتيح ذلك إعداد لوحات البيانات والتنبيهات في مراقبة Google Cloud للاستفادة من المقاييس المسجّلة.

توفّر ميزة "تسجيل في السحابة الإلكترونية" مكتبات عملاء لجميع لغات مكتبة برمجة التطبيقات المتوافقة في "إعلانات Google"، باستثناء Perl، لذا من الممكن في معظم الحالات تسجيل الدخول باستخدام "تسجيل في السحابة الإلكترونية" مباشرةً من عملية دمج مكتبة العميل. بالنسبة إلى اللغات الأخرى، بما في ذلك Perl، يوفّر "تسجيل في السحابة الإلكترونية" أيضًا واجهة برمجة تطبيقات REST.

هناك بضعة خيارات للتسجيل في Cloud Logging أو أداة أخرى من مكتبة عميل Google Ads API. ولكل خيار مزاياه وعيوبه في ما يتعلق بالوقت المستغرَق للقيام بالتكامل والتعقيد والأداء. فكِّر جيدًا في هذه المفاضلات قبل تحديد الحلّ الذي تريد تنفيذه.

الخيار 1: كتابة السجلّات المحلية في السحابة الإلكترونية من عملية في الخلفية

يمكن كتابة سجلات مكتبة العميل في ملف محلي على جهازك من خلال تعديل إعدادات التسجيل. بعد إخراج السجلّات إلى ملف محلي، يمكنك إعداد برنامج تابع لنظام التشغيل لجمع السجلّات وإرسالها إلى السحابة الإلكترونية.

من قيود هذا النهج أنّه لن يتم تسجيل بعض مقاييس الأداء تلقائيًا. تتضمّن سجلّات مكتبة العميل تفاصيل من كائنَي الطلب والردّ، لذا لن يتم تضمين مقاييس وقت الاستجابة ما لم يتم إجراء تغييرات إضافية لتسجّل هذه المقاييس أيضًا.

الخيار 2: تشغيل تطبيقك على Compute Engine وتثبيت "وكيل العمليات"

إذا كان تطبيقك يعمل على Compute Engine، يمكنك إرسال السجلّات إلى أداة "تسجيلات Google Cloud" من خلال تثبيت Ops Agent. يمكن ضبط "أداة Ops Agent" لإرسال سجلّات تطبيقك إلى "تسجيل في السحابة"، بالإضافة إلى المقاييس والسجلّات التي يتم إرسالها تلقائيًا.

إذا كان تطبيقك قيد التشغيل في بيئة Google Cloud، أو إذا كنت تفكر في نقل تطبيقك إلى Google Cloud، هذا خيار رائع.

الخيار 3: تنفيذ التسجيل في رمز تطبيقك البرمجي

يمكن تسجيل الدخول مباشرةً من رمز التطبيق بطريقتَين:

  1. دمج عمليات حساب المقاييس وبيانات السجلّ في كل موقع جغرافي منطبق في الرمز يكون هذا الخيار أكثر ملاءمةً لقواعد برمجية أصغر حجمًا، حيث تكون تكاليف نطاق هذا التغيير وصيانته منخفضة.

  2. تنفيذ واجهة تسجيل إذا كان من الممكن تجريد منطق التطبيق لكي تكتسب أجزاء مختلفة من التطبيق الخصائص من القاعدة نفسها، يمكن تنفيذ منطق التسجيل في هذه القاعدة. ويُفضَّل هذا الخيار عمومًا على دمج عبارات السجلّ في رمز التطبيق، لأنّه من الأسهل صيانته وتوسيع نطاقه. بالنسبة إلى قواعد الرمز البرمجي الأكبر حجمًا، تزداد قابلية هذا الحل للصيانة وقابليته للتوسع.

من قيود هذا النهج أنّ سجلّات الطلبات والردود الكاملة لا تتوفّر من رمز التطبيق. يمكن الوصول إلى كائنَي الطلب والاستجابة الكاملَين من خلال أدوات اعتراض gRPC، وهذه هي الطريقة التي تحصل بها ميزة تسجيل مكتبة العميل المضمّنة على سجلّات الطلبات والاستجابات. في حال حدوث خطأ، قد تتوفّر معلومات إضافية في عنصر الاستثناء، ولكن تتوفّر تفاصيل أقل للاستجابات الناجحة ضمن منطق التطبيق. على سبيل المثال، في معظم الحالات، لا يمكن الوصول إلى معرّف الطلب لطلب ناجح من ملفّات برمجة التطبيقات Google Ads API.

الخيار 4: تنفيذ معرّف بيانات بيني مخصّص لتسجيل gRPC

تتوافق gRPC مع الموانع الأحادية والبث التي يمكنها الوصول إلى كائنَي الطلب والاستجابة أثناء مرورهما بين العميل والخادم. تستخدِم مكتبات العميل لواجهة برمجة التطبيقات في "إعلانات Google" أدوات اعتراض gRPC لتقديم دعم تسجيل مضمّن. وبالمثل، يمكنك تنفيذ معرّف اعتراض مخصّص لبروتوكول gRPC للوصول إلى عناصر الطلب والاستجابة واستخراج المعلومات لأغراض التسجيل والمراقبة وكتابة هذه البيانات في الموقع الجغرافي الذي تختاره.

على عكس بعض الحلول الأخرى المقدَّمة هنا، يمنحك تنفيذ وحدة اعتراض مخصّصة لبروتوكول gRPC مرونة في تسجيل كائنَي الطلب والاستجابة في كل طلب، وتنفيذ منطق إضافي لتسجيل تفاصيل الطلب. على سبيل المثال، يمكنك احتساب الوقت المنقضي للطلب من خلال تنفيذ منطق توقيت الأداء داخل معرّف الجلسة المخصّص نفسه، ثم تسجيل المقياس في "تسجيلات Google Cloud" لإتاحته لمراقبة وقت الاستجابة ضمن "مراقبة Google Cloud".

أداة اعتراض مخصّصة لميزة "تسجيل في Google Cloud" في Python

لتوضيح هذا الحل، كتبنا مثالاً على مثبّت مخصّص للتسجيل في لغة Python. يتم إنشاء معرّف الطلبات المخصّص ونقله إلى العميل الخدمة. بعد ذلك، يصل إلى كائنَي الطلب والاستجابة اللذَين يتم إرسالهما في كلّ طلب لطريقة الخدمة، ويعالج البيانات من هذين الكائنَين، ويرسلها إلى "تسجيلات Google Cloud".

بالإضافة إلى البيانات الواردة من كائنَي الطلب والاستجابة، ينفِّذ المثال بعض المنطق الإضافي لتسجيل الوقت المنقضي للطلب، وبعض البيانات الوصفية الأخرى التي قد تكون مفيدة لأغراض المراقبة، مثل ما إذا كان الطلب ناجحًا أم لا. لمزيد من المعلومات عن كيفية استخدام هذه المعلومات بشكلٍ مفيد، سواءً بشكلٍ عام للتتبّع أو بشكلٍ خاص عند دمج "تسجيل Google Cloud" و"مراقبة Google Cloud"، يُرجى الاطّلاع على دليل التتبّع.

# 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