Tính năng ghi nhật ký và giám sát hoạt động song song để giúp bạn hiểu và tối ưu hoá hiệu suất của ứng dụng, cũng như chẩn đoán lỗi và các vấn đề liên quan đến hệ thống. Bạn nên bật nhật ký tóm tắt cho tất cả lệnh gọi API và nhật ký chi tiết cho các lệnh gọi API không thành công để có thể cung cấp nhật ký lệnh gọi API khi cần hỗ trợ kỹ thuật.
Ghi nhật ký thư viện ứng dụng
Thư viện ứng dụng API Google Ads có tính năng ghi nhật ký tích hợp. Để biết thông tin chi tiết về việc ghi nhật ký dành riêng cho nền tảng, hãy tham khảo tài liệu ghi nhật ký trong thư viện ứng dụng mà bạn chọn.
Định dạng nhật ký
Thư viện ứng dụng API Google Ads tạo một nhật ký chi tiết và một nhật ký tóm tắt cho mỗi lệnh gọi API. Nhật ký chi tiết chứa tất cả thông tin chi tiết về lệnh gọi API, trong khi nhật ký tóm tắt chứa thông tin chi tiết tối thiểu về lệnh gọi API. Ví dụ về từng loại nhật ký sẽ xuất hiện, trong đó các nhật ký được cắt bớt và định dạng để dễ đọc.
Nhật ký tóm tắt
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.")
Nhật ký chi tiết
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----------------
Nếu tôi không sử dụng thư viện ứng dụng thì sao?
Nếu bạn không sử dụng thư viện ứng dụng, hãy triển khai tính năng ghi nhật ký của riêng bạn để ghi lại thông tin chi tiết về các lệnh gọi API đến và đi. Bạn nên ghi lại ít nhất giá trị của tiêu đề phản hồi request-id
. Sau đó, bạn có thể chia sẻ giá trị này với nhóm hỗ trợ kỹ thuật nếu cần.
Ghi nhật ký vào đám mây
Bạn có thể sử dụng nhiều công cụ để ghi lại nhật ký và chỉ số hiệu suất cho ứng dụng của mình. Ví dụ: bạn có thể sử dụng Google Cloud Logging để ghi lại các chỉ số hiệu suất vào Dự án Google Cloud. Nhờ đó, bạn có thể thiết lập trang tổng quan và cảnh báo trong Google Cloud Monitoring để sử dụng các chỉ số đã ghi lại.
Tính năng Ghi nhật ký trên đám mây cung cấp thư viện ứng dụng cho tất cả ngôn ngữ thư viện ứng dụng API Google Ads được hỗ trợ, ngoại trừ Perl. Vì vậy, trong hầu hết các trường hợp, bạn có thể ghi nhật ký bằng tính năng Ghi nhật ký trên đám mây ngay từ tính năng tích hợp thư viện ứng dụng. Đối với các ngôn ngữ khác, bao gồm cả Perl, tính năng Ghi nhật ký trên đám mây cũng cung cấp API REST.
Có một số tuỳ chọn để ghi nhật ký vào Cloud Logging hoặc một công cụ khác từ thư viện ứng dụng API Google Ads. Mỗi tuỳ chọn đều có những đánh đổi riêng về thời gian triển khai, độ phức tạp và hiệu suất. Hãy cân nhắc kỹ những sự đánh đổi này trước khi quyết định triển khai giải pháp nào.
Cách 1: Ghi nhật ký cục bộ vào đám mây từ một quy trình trong nền
Bạn có thể ghi nhật ký thư viện ứng dụng khách vào tệp cục bộ trên máy bằng cách sửa đổi cấu hình ghi nhật ký. Sau khi nhật ký được xuất ra tệp cục bộ, bạn có thể thiết lập trình nền để thu thập nhật ký và gửi nhật ký đó đến đám mây.
Một hạn chế của phương pháp này là một số chỉ số hiệu suất sẽ không được thu thập theo mặc định. Nhật ký thư viện ứng dụng bao gồm thông tin chi tiết từ các đối tượng yêu cầu và phản hồi, vì vậy, các chỉ số độ trễ sẽ không được đưa vào trừ khi bạn thực hiện thêm các thay đổi để ghi lại các chỉ số này.
Cách 2: Chạy ứng dụng trên Compute Engine và cài đặt Trình quản lý hoạt động
Nếu ứng dụng của bạn đang chạy trên Compute Engine, bạn có thể gửi nhật ký đến Google Cloud Logging bằng cách cài đặt Trình điều phối hoạt động. Bạn có thể định cấu hình Trình tác nhân Ops để gửi nhật ký ứng dụng đến tính năng Ghi nhật ký trên đám mây, ngoài các chỉ số và nhật ký được gửi theo mặc định.
Nếu ứng dụng của bạn đang chạy trong môi trường Google Cloud hoặc nếu bạn đang cân nhắc chuyển ứng dụng sang Google Cloud, thì đây là một lựa chọn tuyệt vời để cân nhắc.
Cách 3: Triển khai tính năng ghi nhật ký trong mã ứng dụng
Bạn có thể ghi nhật ký trực tiếp từ mã ứng dụng theo một trong hai cách sau:
Kết hợp các phép tính chỉ số và câu lệnh nhật ký ở mọi vị trí áp dụng trong mã. Lựa chọn này khả thi hơn đối với các cơ sở mã nhỏ hơn, trong đó phạm vi và chi phí bảo trì của thay đổi như vậy sẽ ở mức tối thiểu.
Triển khai giao diện ghi nhật ký. Nếu logic ứng dụng có thể được trừu tượng hoá để các phần khác nhau của ứng dụng kế thừa từ cùng một lớp cơ sở, thì logic ghi nhật ký có thể được triển khai trong lớp cơ sở đó. Bạn nên sử dụng tuỳ chọn này thay vì kết hợp các câu lệnh nhật ký trong toàn bộ mã ứng dụng, vì tuỳ chọn này dễ bảo trì và mở rộng hơn. Đối với các cơ sở mã lớn hơn, khả năng bảo trì và khả năng mở rộng của giải pháp này đều phù hợp hơn.
Một hạn chế của phương pháp này là bạn không thể xem nhật ký yêu cầu và phản hồi đầy đủ từ mã xử lý ứng dụng. Bạn có thể truy cập vào đối tượng yêu cầu và phản hồi đầy đủ từ trình chặn gRPC; đây là cách ghi nhật ký thư viện ứng dụng tích hợp sẵn lấy nhật ký yêu cầu và phản hồi. Trong trường hợp xảy ra lỗi, có thể có thêm thông tin trong đối tượng ngoại lệ, nhưng có ít thông tin chi tiết hơn cho các phản hồi thành công trong logic ứng dụng. Ví dụ: trong hầu hết các trường hợp, bạn không thể truy cập mã yêu cầu của một yêu cầu thành công từ các đối tượng phản hồi API Google Ads.
Cách 4: Triển khai trình chặn ghi nhật ký gRPC tuỳ chỉnh
gRPC hỗ trợ trình chặn một chiều và truyền trực tuyến. Các trình chặn này có thể truy cập vào đối tượng yêu cầu và phản hồi khi các đối tượng này truyền giữa máy khách và máy chủ. Thư viện ứng dụng API Google Ads sử dụng trình chặn gRPC để cung cấp tính năng hỗ trợ ghi nhật ký tích hợp. Tương tự, bạn có thể triển khai trình chặn gRPC tuỳ chỉnh để truy cập vào các đối tượng yêu cầu và phản hồi, trích xuất thông tin cho mục đích ghi nhật ký và giám sát, đồng thời ghi dữ liệu đó vào vị trí bạn chọn.
Không giống như một số giải pháp khác được trình bày ở đây, việc triển khai trình chặn gRPC tuỳ chỉnh cho phép bạn linh hoạt ghi lại các đối tượng yêu cầu và phản hồi trên mọi yêu cầu, đồng thời triển khai logic bổ sung để ghi lại thông tin chi tiết về yêu cầu. Ví dụ: bạn có thể tính thời gian đã trôi qua của một yêu cầu bằng cách triển khai logic tính thời gian hiệu suất trong chính trình chặn tuỳ chỉnh, sau đó ghi lại chỉ số này vào Google Cloud Logging để có thể theo dõi độ trễ trong Google Cloud Monitoring.
Trình chặn Google Cloud Logging tuỳ chỉnh trong Python
Để minh hoạ giải pháp này, chúng tôi đã viết một ví dụ về trình chặn ghi nhật ký tuỳ chỉnh trong Python. Trình chặn tuỳ chỉnh được tạo và truyền vào ứng dụng dịch vụ. Sau đó, lớp này truy cập vào các đối tượng yêu cầu và phản hồi truyền qua mọi lệnh gọi phương thức dịch vụ, xử lý dữ liệu từ các đối tượng đó và gửi dữ liệu đến Google Cloud Logging.
Ngoài dữ liệu đến từ các đối tượng yêu cầu và phản hồi, ví dụ này triển khai một số logic bổ sung để ghi lại thời gian đã trôi qua của yêu cầu và một số siêu dữ liệu khác sẽ hữu ích cho mục đích giám sát, chẳng hạn như liệu yêu cầu có thành công hay không. Để biết thêm thông tin về cách thông tin này có thể hữu ích, cả về tổng thể để theo dõi và cụ thể là khi kết hợp Google Cloud Logging với Google Cloud Monitoring, hãy xem Hướng dẫn theo dõi.
# 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