Ведение журналов – важный инструмент для отладки и мониторинга кода.
Cloud Functions позволяет использовать SDK для ведения журналов для Node.js или Python, а также стандартный объект console для веб-разработки.
Cloud Logging – это платный сервис. Если вы превысите квоту на бесплатное использование, с вас может взиматься плата. Подробнее о ценах на Cloud Logging…
Запись журналов
Как использовать SDK для ведения журнала Cloud Functions
SDK регистратора Cloud Functions предоставляет стандартный интерфейс для передачи данных о статусе из функций в 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
Чтобы посмотреть журналы с помощью инструмента 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 версии 13.33.0 и более поздних, автоматически развертываются с возможностью связать идентификатор выполнения с каждой записью в журнале, созданной во время обработки этого выполнения.
Идентификатор выполнения уникальным образом определяет все журналы, связанные с одним запросом, обработанным вашей функцией. Изменять код не нужно. Идентификатор выполнения будет автоматически добавлен в журналы.
Чтобы отключить регистрацию идентификатора выполнения в записях журнала, задайте для переменной среды LOG_EXECUTION_ID значение false в файле dotenv.
Как найти и сопоставить журналы по идентификатору выполнения
Вы можете просматривать и сопоставлять журналы по идентификатору выполнения в Cloud Logs Explorer.
Разверните запись в журнале из вашей функции. Идентификатор выполнения находится в структурированных данных журнала, вложенных в ярлыки как
labels.execution_id.Нажмите на значение
execution_idи выберите "Показать совпадающие записи" в раскрывающемся меню, чтобы увидеть все остальные журналы, связанные с выполнением этой функции.
Используя идентификатор выполнения, вы можете сгруппировать все сообщения журнала, относящиеся к одному запросу, даже если ваша функция обрабатывает несколько запросов одновременно.
Как улучшить видимость журналов с помощью специальных полей сводки
Чтобы идентификатор выполнения был более заметен в интерфейсе просмотра журналов, вы можете добавить его в качестве [специального поля сводки][cloud-logging-preference]. После того как вы добавите идентификатор выполнения в качестве поля сводки, в начале каждой записи в журнале будет показываться чип с идентификатором выполнения. Это похоже на то, как функции первого поколения показывали идентификатор выполнения для всех записей в журнале.
Чтобы добавить идентификатор выполнения в поле сводки:
Нажмите на значение идентификатора выполнения в записи структурированного журнала под элементом
labels.execution_id.В раскрывающемся меню выберите "Добавить поле в строку сводки".
Теперь в каждой записи журнала в поле "Сводка" будет указан executionId, чтобы вам было проще находить и группировать записи в журнале, связанные с определенным идентификатором выполнения.