로그 작성 및 보기


로깅은 코드 디버깅 및 모니터링을 하는 데 중요한 수단입니다. Cloud Functions에서는 Node.js 또는 Python용 로거 SDK 또는 웹 개발용 console 객체 표준을 사용할 수 있는 옵션을 제공합니다.

Cloud Logging은 청구 가능한 서비스로서 무료 할당량을 초과하면 요금이 청구될 수 있습니다. 자세한 내용은 Cloud Logging 가격 책정을 참조하세요.

로그 작성

Cloud Functions 로거 SDK 사용

Cloud Functions 로거 SDK는 함수에서 Cloud Logging으로 상태를 보고하는 표준 인터페이스를 제공합니다. 이 SDK를 사용해 구조화된 데이터로 이벤트를 로깅하면 보다 쉽게 분석 및 모니터링할 수 있습니다.

logger 하위 패키지에서 가져옵니다.

Node.jsPython
// All available logging functions
const {
  log,
  info,
  debug,
  warn,
  error,
  write,
} = require("firebase-functions/logger");
from firebase_functions import logger
  • logger.log() 명령어의 로그 수준은 INFO입니다.

  • logger.info() 명령어의 로그 수준은 정보입니다.

  • logger.warn() 명령어의 로그 수준은 경고입니다.

  • logger.error() 명령어의 로그 수준은 오류입니다.

  • logger.debug() 명령어의 로그 수준은 디버그입니다.

  • 내부 시스템 메시지의 로그 수준은 디버그입니다.

이 예시에서는 기본 로그를 작성하는 함수를 보여줍니다.

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

  response.send("Hello from Firebase!");
});
@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.jsPython
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});
});
@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.jsPython
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,
  });
});
@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를 사용하면 대신 console.logconsole.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() 명령어의 로그 수준은 정보입니다.
  • console.info() 명령어의 로그 수준은 정보입니다.
  • console.warn() 명령어의 로그 수준은 오류입니다.
  • console.error() 명령어의 로그 수준은 오류입니다.
  • 내부 시스템 메시지의 로그 수준은 디버그입니다.

로그 보기

Cloud Functions에 대한 로그는 Google Cloud 콘솔, Cloud Logging UI 또는 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 UI 사용

Cloud Logging UI에서 Cloud Functions의 로그를 확인할 수 있습니다.

로그 분석

Cloud LoggingCloud Functions를 모니터링할 때 사용할 수 있는 강력한 로그 분석 도구 모음을 제공합니다.

차트 및 알림

로그 기반 측정항목을 만들어 함수를 모니터링한 후에는 이 측정항목을 바탕으로 차트와 알림을 만들 수 있습니다. 예를 들어 시간에 따른 지연 시간을 시각화하는 차트를 만들거나 특정 오류가 과도하게 발생할 경우 알려주는 알림을 만들 수 있습니다.

차트 및 알림 정책에서 로그 기반 측정항목을 사용하는 자세한 방법은 차트 및 알림 만들기를 참조하세요.

실행 ID 이해 및 사용

기본적으로 Cloud Run Functions(2세대)는 단일 함수 인스턴스 내에서 여러 요청의 동시 실행을 지원합니다. 즉, 여러 요청의 로그가 인터리브 처리될 수 있으므로 단일 실행의 흐름을 추적하기가 더 어려워집니다.

이를 위해 Firebase CLI 버전 13.33.0 이상을 사용하여 배포된 함수는 해당 실행을 처리하는 동안 내보낸 각 로그 항목과 실행 ID를 연결하는 옵션이 포함된 상태로 자동 배포됩니다.

실행 ID는 함수에서 처리한 단일 요청과 연결된 모든 로그를 고유하게 식별합니다. 코드를 변경할 필요가 없습니다. 실행 ID가 자동으로 로그에 추가됩니다.

로그 항목에서 실행 ID 로깅을 사용 중지하려면 dotenv 파일에서 환경 변수 LOG_EXECUTION_ID를 false로 설정합니다.

실행 ID별로 로그 찾기 및 상관관계 파악

Cloud 로그 탐색기에서 실행 ID별로 로그를 검사하고 상관관계를 파악할 수 있습니다.

  1. 함수의 로그 항목을 펼칩니다. 실행 ID는 구조화된 로그 데이터 내에 있으며 라벨 아래에 labels.execution_id로 중첩됩니다.

  2. execution_id 값을 클릭하고 드롭다운 메뉴에서 '일치하는 항목 표시'를 선택하여 동일한 함수 실행과 연결된 다른 모든 로그를 확인합니다.

실행 ID를 사용하면 함수가 여러 요청을 동시에 처리하는 경우에도 단일 요청과 관련된 모든 로그 메시지를 그룹화할 수 있습니다.

커스텀 요약 필드로 로그 가시성 개선

로그 탐색기에 실행 ID를 더 쉽게 표시하려면 커스텀 요약 필드로 추가하면 됩니다. 이렇게 하면 1세대 함수가 모든 로그 항목의 실행 ID를 표시하는 방식과 마찬가지로 실행 ID가 각 로그 항목 줄의 시작 부분에 칩으로 표시됩니다.

요약 필드에 실행 ID를 추가하려면 다음 단계를 따르세요.

  1. labels.execution_id 아래의 구조화된 로그 항목에서 실행 ID 값을 클릭합니다.

  2. 드롭다운 메뉴에서 '요약 줄에 필드 추가'를 선택합니다.

이제 각 로그 항목의 요약 필드에 executionId가 눈에 띄게 표시되므로 특정 실행 ID와 연결된 로그를 더 쉽게 식별하고 그룹화할 수 있습니다.