כתיבה והצגה של יומנים

רישום ביומן הוא כלי חשוב לניפוי באגים ולמעקב אחר קוד. ‫Cloud Functions מאפשר לכם להשתמש ב-SDK של כלי הרישום עבור Node.js או Python, או באובייקט console הרגיל לפיתוח לאינטרנט.

‫Cloud Logging הוא שירות בתשלום, ויכול להיות שתחויבו אם תחרגו מהמכסה ללא עלות. מידע נוסף זמין במאמר תמחור של Cloud Logging.

כתיבת יומנים

שימוש ב-SDK של כלי הרישום Cloud Functions

ערכת ה-SDK של Cloud Functions logger מספקת ממשק סטנדרטי לדיווח על סטטוס מפונקציות אל Cloud Logging. אתם יכולים להשתמש ב-SDK הזה כדי לתעד אירועים עם נתונים מובנים, וכך לנתח ולנטר את הנתונים בקלות רבה יותר.

ייבוא מחבילת המשנה logger:

Node.js

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

Python

from firebase_functions import logger
  • לפקודות logger.log() יש את רמת היומן INFO.

  • לפקודות logger.info() יש את רמת היומן INFO.

  • פקודות logger.warn() הן ברמת היומן WARNING.

  • פקודות logger.error() הן ברמת היומן ERROR.

  • לפקודות logger.debug() יש את רמת היומן DEBUG.

  • הודעות מערכת פנימיות הן ברמת היומן DEBUG.

בדוגמה הזו מוצגת פונקציה שכותבת יומן בסיסי:

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

משתמשים ברמות שונות של רישום ביומן לסוגים שונים של יומנים בקוד הפונקציה. אפשר לצרף נתונים מובְנים ליומן כארגומנט האחרון. הנה דוגמה לאופן שבו פונקציה יכולה להשתמש בכל סוג של יומן:

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

באמצעות logger.write(), אפשר לכתוב רשומות ביומן עם רמות חומרה נוספות של יומן: CRITICAL,‏ ALERT ו-EMERGENCY. מידע נוסף זמין במאמר בנושא 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)

שימוש ב-console.log

הפתרון המומלץ לרישום ביומן מתוך פונקציה הוא שימוש ב-SDK של כלי הרישום ביומן לפלטפורמה שלכם. ב-Node.js, אפשר להשתמש במקום זאת בקריאות סטנדרטיות לרישום ביומן של JavaScript, כמו console.log ו-console.error, אבל קודם צריך לדרוש מודול מיוחד כדי לתקן את השיטות הסטנדרטיות כך שיפעלו בצורה תקינה:

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

אחרי שדורשים את מודול התאימות של כלי הרישום, אפשר להשתמש בשיטות console.log() כרגיל בקוד:

exports.helloError = functions.https.onRequest((request, response) => {
  console.log('I am a log entry!');
  response.send('Hello World...');
});
  • לפקודות console.log() יש את רמת היומן INFO.
  • לפקודות console.info() יש את רמת היומן INFO.
  • פקודות console.warn() הן ברמת היומן ERROR.
  • פקודות console.error() הן ברמת היומן ERROR.
  • הודעות מערכת פנימיות הן ברמת היומן DEBUG.

צפייה ביומנים

אפשר לראות את היומנים של Cloud Functions במסוף Google Cloud, בממשק המשתמש של Cloud Logging או באמצעות כלי שורת הפקודה של firebase.

שימוש ב-Firebase CLI

כדי להציג יומנים באמצעות הכלי firebase, משתמשים בפקודה functions:log:

firebase functions:log

כדי להציג יומנים של פונקציה ספציפית, צריך לציין את שם הפונקציה כארגומנט:

firebase functions:log --only <FUNCTION_NAME>

כדי לראות את כל האפשרויות לצפייה ביומנים, אפשר להציג את העזרה עבור functions:log:

firebase help functions:log

שימוש במסוף Google Cloud

אפשר לראות את היומנים של הפונקציות במסוף Google Cloud.

שימוש בממשק המשתמש של Cloud Logging

אפשר לראות את היומנים של Cloud Functions בממשק המשתמש של Cloud Logging.

ניתוח יומנים

Cloud Logging מציע חבילה מתקדמת של כלים לניתוח יומנים שבעזרתם אפשר לעקוב אחרי Cloud Functions.

תרשימים והתראות

אחרי שיוצרים מדדים מבוססי-יומנים כדי לעקוב אחרי הפונקציות, אפשר ליצור תרשימים והתראות על סמך המדדים האלה. לדוגמה, אפשר ליצור תרשים כדי להציג את זמן האחזור לאורך זמן, או ליצור התראה שתעדכן אתכם אם שגיאה מסוימת מתרחשת לעיתים קרובות מדי.

במאמר יצירת תרשימים והתראות מוסבר בפירוט איך להשתמש במדדים מבוססי-יומנים בתרשימים ובמדיניות התראות.

הסבר על מזהי הרצה ואיך משתמשים בהם

כברירת מחדל, פונקציות Cloud Run (דור שני) תומכות בהפעלה בו-זמנית של כמה בקשות בתוך מופע פונקציה יחיד. המשמעות היא שיומנים מבקשות שונות יכולים להיות משולבים, ולכן קשה יותר לעקוב אחרי התהליך של ביצוע יחיד.

כדי לעזור בכך, פונקציות שנפרסות באמצעות Firebase CLI בגרסה 13.33.0 ואילך נפרסות באופן אוטומטי עם אפשרות לשייך מזהה ביצוע לכל רשומה ביומן שנוצרת במהלך הטיפול בביצוע הזה.

מזהה הביצוע מזהה באופן ייחודי את כל היומנים שמשויכים לבקשה יחידה שהפונקציה מטפלת בה. אין צורך לבצע שינויים בקוד. מזהה הביצוע יתווסף ליומנים באופן אוטומטי.

כדי להשבית את הרישום של מזהה ההפעלה בערכי היומן, צריך להגדיר את משתנה הסביבה LOG_EXECUTION_ID כ-false בקובץ dotenv.

חיפוש יומנים וביצוע קורלציה ביניהם לפי מזהה הביצוע

אפשר לבדוק את היומנים ולבצע קורלציה ביניהם לפי מזהה הביצוע ב-Cloud Logs Explorer.

  1. מרחיבים את רשומת היומן מהפונקציה. מזהה ההפעלה נמצא בנתוני היומן המובנים, והוא מוטמע בתוויות בתור labels.execution_id.

  2. לוחצים על הערך של execution_id ובוחרים באפשרות 'הצגת רשומות תואמות' בתפריט הנפתח כדי לראות את כל היומנים האחרים שמשויכים לאותו ביצוע של הפונקציה.

באמצעות מזהה הביצוע, אפשר לקבץ את כל הודעות היומן שקשורות לבקשה אחת, גם אם הפונקציה מטפלת בכמה בקשות בו-זמנית.

שיפור הנראות של היומן באמצעות שדות סיכום בהתאמה אישית

כדי שמזהה ההפעלה יהיה גלוי יותר ב-Logs Explorer, אפשר להוסיף אותו כ[שדה סיכום מותאם אישית][cloud-logging-preference]. אחרי שמוסיפים את מזהה ההפעלה כשדה סיכום, כל רשומה ביומן תציג את מזהה ההפעלה כצ'יפ בתחילת שורת היומן, בדומה לאופן שבו פונקציות מדור ראשון הציגו את מזהה ההפעלה לכל הרשומות ביומן.

כדי להוסיף את מזהה ההרצה לשדה הסיכום:

  1. לוחצים על הערך של מזהה ההפעלה ברשומה המובנית ביומן בקטע labels.execution_id.

  2. בתפריט הנפתח, בוחרים באפשרות 'הוספת שדה לשורת הסיכום'.

כל רשומה ביומן מציגה עכשיו את executionId באופן בולט בשדה הסיכום, מה שמקל על זיהוי וקיבוץ של יומנים שמשויכים למזהה ביצוע ספציפי.