Ghi và xem nhật ký

Ghi nhật ký là một công cụ quan trọng để gỡ lỗi và giám sát mã. Cloud Functions cho phép bạn sử dụng SDK trình ghi nhật ký cho Node.js hoặc Python, hoặc đối tượng console tiêu chuẩn để phát triển cho web.

Cloud Logging là một dịch vụ có tính phí. Bạn có thể bị tính phí nếu vượt quá hạn mức miễn phí. Để biết thêm thông tin, hãy xem bài viết Giá của Cloud Logging.

Viết nhật ký

Sử dụng SDK trình ghi nhật ký Cloud Functions

SDK trình ghi nhật ký Cloud Functions cung cấp một giao diện tiêu chuẩn để báo cáo trạng thái từ các hàm đến Cloud Logging. Bạn có thể sử dụng SDK này để ghi nhật ký các sự kiện bằng dữ liệu có cấu trúc, giúp phân tích và giám sát dễ dàng hơn.

Nhập từ gói con logger:

Node.js

// All available logging functions
const {
  log,
  info,
  debug,
  warn,
  error,
  write,
} = require("firebase-functions/logger");

Python

from firebase_functions import logger
  • Lệnh logger.log() có cấp độ nhật ký INFO.

  • Lệnh logger.info() có cấp độ nhật ký INFO.

  • Lệnh logger.warn() có cấp độ nhật ký WARNING.

  • Lệnh logger.error() có cấp độ nhật ký ERROR.

  • Lệnh logger.debug() có cấp độ nhật ký DEBUG.

  • Thông báo hệ thống nội bộ có cấp độ nhật ký DEBUG.

Ví dụ này minh hoạ một hàm ghi nhật ký cơ bản:

Node.js

exports.helloWorld = onRequest((request, response) => {
  // sends a log to Cloud Logging
  log("Hello logs!");

  response.send("Hello from Firebase!");
});

Python

@https_fn.on_request()
def hello_world(req: https_fn.Request) -> https_fn.Response:
    # sends a log to Cloud Logging
    logger.log("Hello logs!")

    return https_fn.Response("Hello from Firebase!")

Sử dụng các cấp độ nhật ký khác nhau cho các loại nhật ký khác nhau trong mã hàm. Bạn có thể đính kèm dữ liệu có cấu trúc vào nhật ký dưới dạng đối số cuối cùng. Dưới đây là ví dụ về cách một hàm có thể sử dụng từng loại nhật ký:

Node.js

exports.getInspirationalQuote = onRequest(async (request, response) => {
  const db = getFirestore();
  const today = new Date();
  const quoteOfTheMonthRef = db
      .collection("quotes")
      .doc(`${today.getFullYear()}`)
      .collection("months")
      .doc(`${today.getMonth()}`);

  const DEFAULT_QUOTE =
      "You miss 100% of the shots you don't take. -Wayne Gretzky";
  let quote;
  try {
    const quoteOfTheMonthDocSnap = await quoteOfTheMonthRef.get();

    // Attach relevant debugging information with debug()
    debug("Monthly quote fetch result", {
      docRef: quoteOfTheMonthRef.path,
      exists: quoteOfTheMonthDocSnap.exists,
      createTime: quoteOfTheMonthDocSnap.createTime,
    });

    if (quoteOfTheMonthDocSnap.exists) {
      quote = quoteOfTheMonthDocSnap.data().text;
    } else {
      // Use warn() for lower-severity issues than error()
      warn("Quote not found for month, sending default instead", {
        docRef: quoteOfTheMonthRef.path,
        dateRequested: today.toLocaleDateString("en-US"),
      });

      quote = DEFAULT_QUOTE;
    }
  } catch (err) {
    // Attach an error object as the second argument
    error("Unable to read quote from Firestore, sending default instead",
        err);

    quote = DEFAULT_QUOTE;
  }

  // Attach relevant structured data to any log
  info("Sending a quote!", {quote: quote});
  response.json({inspirationalQuote: quote});
});

Python

@https_fn.on_request()
def get_inspirational_quote(req: https_fn.Request) -> https_fn.Response:
    firestore_client = firestore.client()
    today = datetime.date.today()
    quote_of_the_month_ref = (firestore_client.collection("quotes").doc(str(
        today.year)).collection("months").doc(str(today.month)))

    default_quote = "Python has been an important part of Google since the beginning, and remains so as the system grows and evolves."

    quote = None
    try:
        quote_of_the_month = quote_of_the_month_ref.get()

        # Attach relevant debugging information with debug()
        logger.debug(
            "Monthly quote fetch result",
            docRef=quote_of_the_month.path,
            exists=quote_of_the_month.exists,
            createTime=quote_of_the_month.createTime,
        )

        if quote_of_the_month.exists:
            quote = quote_of_the_month.to_dict()["text"]
        else:
            # Use warn() for lower-severity issues than error()
            logger.warn(
                "Quote not found for month, sending default instead",
                doc_reference=quote_of_the_month.path,
                date_requested=today.strftime("%Y-%m-%d"),
            )
            quote = default_quote
    except:
        e = sys.exc_info()[0]
        # Attach an error object as the second argument
        logger.error("Unable to read quote from Firestore, sending default instead", error=e)
        quote = default_quote

    # Attach relevant structured data to any log
    logger.info("Sending a quote!", quote=quote)
    return https_fn.Response("Hello from Firebase!")

Với logger.write(), bạn có thể ghi các mục nhập nhật ký với các cấp độ nghiêm trọng bổ sung của nhật ký là CRITICAL, ALERT, và EMERGENCY. Xem LogSeverity.

Node.js

exports.appHasARegression = onRegressionAlertPublished((event) => {
  write({
    // write() lets you set additional severity levels
    // beyond the built-in logger functions
    severity: "EMERGENCY",
    message: "Regression in production app",
    issue: event.data.payload.issue,
    lastOccurred: event.data.payload.resolveTime,
  });
});

Python

@crashlytics_fn.on_regression_alert_published()
def app_has_regression(alert: crashlytics_fn.CrashlyticsRegressionAlertEvent) -> None:
    logger.write(
        severity="EMERGENCY",
        message="Regression in production app",
        issue=alert.data.payload.issue,
        last_occurred=alert.data.payload.resolve_time,
    )
    print(alert)

Sử dụng console.log

Giải pháp được đề xuất để ghi nhật ký từ một hàm là sử dụng SDK trình ghi nhật ký cho nền tảng của bạn. Với Node.js, bạn có thể sử dụng các lệnh gọi ghi nhật ký JavaScript tiêu chuẩn như console.logconsole.error, nhưng trước tiên, bạn cần yêu cầu một mô-đun đặc biệt để vá các phương thức tiêu chuẩn nhằm hoạt động chính xác:

require("firebase-functions/logger/compat");

Sau khi yêu cầu mô-đun tương thích với trình ghi nhật ký, bạn có thể sử dụng các phương thức console.log() như bình thường trong mã của mình:

exports.helloError = functions.https.onRequest((request, response) => {
  console.log('I am a log entry!');
  response.send('Hello World...');
});
  • Lệnh console.log() có cấp độ nhật ký INFO.
  • Lệnh console.info() có cấp độ nhật ký INFO.
  • Lệnh console.warn() có cấp độ nhật ký ERROR.
  • Lệnh console.error() có cấp độ nhật ký ERROR.
  • Thông báo hệ thống nội bộ có cấp độ nhật ký DEBUG.

Xem nhật ký

Bạn có thể xem nhật ký cho Cloud Functions trong Google Cloud console, Cloud Logging UI hoặc thông qua công cụ dòng lệnh firebase.

Sử dụng Firebase CLI

Để xem nhật ký bằng công cụ firebase, hãy sử dụng lệnh functions:log:

firebase functions:log

Để xem nhật ký cho một hàm cụ thể, hãy cung cấp tên hàm dưới dạng đối số:

firebase functions:log --only <FUNCTION_NAME>

Để xem toàn bộ các lựa chọn xem nhật ký, hãy xem phần trợ giúp cho functions:log:

firebase help functions:log

Sử dụng bảng điều khiển Google Cloud

Bạn có thể xem nhật ký cho các hàm trong Google Cloud bảng điều khiển.

Sử dụng giao diện người dùng Cloud Logging

Bạn có thể xem nhật ký cho Cloud Functions trong giao diện người dùng Cloud Logging.

Phân tích nhật ký

Cloud Logging cung cấp một bộ công cụ phân tích nhật ký mạnh mẽ mà bạn có thể sử dụng để giám sát Cloud Functions.

Biểu đồ và cảnh báo

Sau khi tạo các chỉ số dựa trên nhật ký để giám sát các hàm, bạn có thể tạo biểu đồ và cảnh báo dựa trên các chỉ số này. Ví dụ: bạn có thể tạo biểu đồ để hình dung độ trễ theo thời gian hoặc tạo cảnh báo để cho bạn biết nếu một lỗi nhất định xảy ra quá thường xuyên.

Hãy xem bài viết Tạo biểu đồ và cảnh báo để biết thông tin chi tiết về cách sử dụng các chỉ số dựa trên nhật ký trong biểu đồ và chính sách cảnh báo.

Tìm hiểu và sử dụng mã thực thi

Theo mặc định, Cloud Run functions (thế hệ thứ 2) hỗ trợ thực thi đồng thời nhiều yêu cầu trong một thực thể hàm. Điều này có nghĩa là nhật ký từ các yêu cầu khác nhau có thể được xen kẽ, khiến bạn khó theo dõi luồng của một lần thực thi.

Để giúp bạn giải quyết vấn đề này, các hàm được triển khai bằng Firebase CLI phiên bản 13.33.0 trở lên sẽ tự động triển khai với một lựa chọn để liên kết mã thực thi với mỗi mục nhập nhật ký được phát ra trong quá trình xử lý lần thực thi đó.

Mã thực thi xác định duy nhất tất cả nhật ký được liên kết với một yêu cầu do hàm của bạn xử lý. Bạn không cần thay đổi mã; mã thực thi sẽ tự động được thêm vào nhật ký của bạn.

Để tắt tính năng ghi nhật ký mã thực thi trong các mục nhập nhật ký, hãy đặt biến môi trường LOG_EXECUTION_ID thành false trong tệp dotenv.

Tìm và tương quan nhật ký theo mã thực thi

Bạn có thể kiểm tra và tương quan nhật ký theo mã thực thi trong Cloud Logs Explorer.

  1. Mở rộng mục nhập nhật ký từ hàm của bạn. Mã thực thi nằm trong dữ liệu nhật ký có cấu trúc, được lồng trong nhãn dưới dạng labels.execution_id.

  2. Nhấp vào giá trị của execution_id rồi chọn "Hiển thị các mục nhập phù hợp" trong trình đơn thả xuống để xem tất cả nhật ký khác được liên kết với cùng một lần thực thi hàm.

Bằng cách sử dụng mã thực thi, bạn có thể nhóm tất cả thông báo nhật ký liên quan đến một yêu cầu duy nhất, ngay cả khi hàm của bạn đang xử lý nhiều yêu cầu đồng thời.

Nâng cao khả năng hiển thị nhật ký bằng các trường tóm tắt tuỳ chỉnh

Để giúp mã thực thi hiển thị rõ ràng hơn trong Trình khám phá nhật ký, bạn có thể thêm mã thực thi dưới dạng [trường tóm tắt tuỳ chỉnh][cloud-logging-preference]. Sau khi bạn thêm mã thực thi dưới dạng trường tóm tắt, mọi mục nhập nhật ký sẽ hiển thị mã thực thi dưới dạng một khối ở đầu dòng nhật ký. tương tự như cách các hàm thế hệ thứ nhất hiển thị mã thực thi cho tất cả mục nhập nhật ký.

Cách thêm mã thực thi vào trường tóm tắt:

  1. Nhấp vào giá trị của mã thực thi trong mục nhập nhật ký có cấu trúc trong labels.execution_id.

  2. Chọn "Thêm trường vào dòng tóm tắt" trong trình đơn thả xuống.

Mỗi mục nhập nhật ký hiện hiển thị executionId một cách nổi bật trong trường tóm tắt, giúp bạn dễ dàng xác định và nhóm các nhật ký được liên kết với một mã thực thi cụ thể.