Hoạt động ghi nhật ký và giám sát diễn ra song song để giúp bạn hiểu và tối ưu hoá hiệu suất ứ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ả cá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 dịch vụ hỗ trợ kỹ thuật.
Ghi nhật ký thư viện ứng dụng
Thư viện ứng dụng Google Ads API có tính năng ghi nhật ký tích hợp. Để biết thông tin chi tiết về nhật ký theo nền tảng cụ thể, 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.
Ngôn ngữ | Hướng dẫn |
---|---|
Java | Tài liệu ghi nhật ký cho Java |
.NET | Tài liệu ghi nhật ký cho .NET |
PHP | Tài liệu ghi nhật ký cho PHP |
Python | Tài liệu ghi nhật ký cho Python |
Ruby | Tài liệu ghi nhật ký cho Ruby |
Perl | Tài liệu ghi nhật ký cho Perl |
Định dạng nhật ký
Các thư viện ứng dụng API Google Ads tạo ra 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 đó nhật ký được rút gọn 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 đi và đến. Bạn nên ghi nhật ký í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 khi 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 trên Google Cloud. Điều này giúp 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.
Cloud Logging cung cấp các thư viện ứng dụng cho tất cả các ngôn ngữ thư viện ứng dụng Google Ads API đượ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 Cloud Logging ngay từ quá trình 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, Cloud Logging cũng cung cấp một API REST.
Có một số lựa 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 Google Ads API. Mỗi lựa chọn đều có những điểm đánh đổi riêng về thời gian triển khai, độ phức tạp và hiệu suất. Hãy suy nghĩ kỹ về những điểm đá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 nền
Bạn có thể ghi nhật ký thư viện ứng dụng vào một 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 xuất nhật ký vào một tệp cục bộ, bạn có thể thiết lập một trình nền để thu thập nhật ký và gửi nhật ký đó lê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 ghi lại 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ừ phi bạn thực hiện các thay đổi bổ sung để ghi nhật ký các chỉ số này.
Lựa chọn 2: Chạy ứng dụng trên Compute Engine và cài đặt Ops Agent
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 Ops Agent. Bạn có thể định cấu hình Ops Agent để gửi nhật ký ứng dụng của bạn đến Cloud Logging, 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ột môi trường Google Cloud hoặc nếu bạn đang cân nhắc việc di chuyển ứng dụng của mình sang Google Cloud, thì đây là một lựa chọn đáng 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:
Kết hợp các phép tính chỉ số và câu lệnh nhật ký ở mọi vị trí có thể áp dụng trong mã của bạn. Lựa chọn này phù hợp hơn 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 có thể trừu tượng hoá logic ứng dụng để 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ì bạn có thể triển khai logic ghi nhật ký trong lớp cơ sở đó. Nhìn chung, bạn nên chọn lựa chọn này thay vì kết hợp các câu lệnh nhật ký trong mã ứng dụng, vì lựa chọn này dễ duy trì và mở rộng hơn. Đối với các cơ sở mã lớn hơn, khả năng duy trì và khả năng mở rộng của giải pháp này càng trở nên phù hợp hơn.
Một hạn chế của phương pháp này là nhật ký đầy đủ về yêu cầu và phản hồi không có sẵn trong mã ứng dụng. Bạn có thể truy cập vào toàn bộ đối tượng yêu cầu và phản hồi từ các trình chặn gRPC; đây là cách tính năng 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 vào mã yêu cầu cho một yêu cầu thành công từ các đối tượng phản hồi của Google Ads API.
Cách 4: Triển khai trình chặn ghi nhật ký gRPC tuỳ chỉnh
gRPC hỗ trợ các trình chặn truyền trực tuyến và đơn phương có thể truy cập vào các đối tượng yêu cầu và phản hồi khi chúng truyền giữa máy khách và máy chủ. Thư viện ứng dụng Google Ads API sử dụng các 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 một 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í mà 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 một trình chặn gRPC tuỳ chỉnh mang lại cho bạn sự 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 thêm logic để 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 chỉ số vào Google Cloud Logging để cung cấp chỉ số này cho hoạt động giám sát độ 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 bằng Python. Trình chặn tuỳ chỉnh được tạo và truyền vào ứng dụng khách dịch vụ. Sau đó, nó sẽ truy cập vào các đối tượng yêu cầu và phản hồi truyền qua trên 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 còn 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 hữu ích cho mục đích giám sát, chẳng hạn như 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ề việc giám sát nói chung và cụ thể là khi kết hợp Google Cloud Logging và Google Cloud Monitoring), hãy xem Hướng dẫn giám sát.
# 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