firebase-tools
Version:
Command-Line Interface for Firebase
154 lines (153 loc) • 6.25 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.get_logs = void 0;
const zod_1 = require("zod");
const tool_1 = require("../../tool");
const util_1 = require("../../util");
const functionslog_1 = require("../../../functions/functionslog");
const cloudlogging_1 = require("../../../gcp/cloudlogging");
const error_1 = require("../../../error");
const SEVERITY_LEVELS = [
"DEFAULT",
"DEBUG",
"INFO",
"NOTICE",
"WARNING",
"ERROR",
"CRITICAL",
"ALERT",
"EMERGENCY",
];
function normalizeFunctionSelectors(selectors) {
if (!selectors)
return undefined;
if (Array.isArray(selectors)) {
const cleaned = selectors.map((name) => name.trim()).filter(Boolean);
return cleaned.length ? cleaned.join(",") : undefined;
}
const cleaned = selectors
.split(/[,\s]+/)
.map((name) => name.trim())
.filter(Boolean);
return cleaned.length ? cleaned.join(",") : undefined;
}
function validateTimestamp(label, value) {
const parsed = Date.parse(value);
if (Number.isNaN(parsed)) {
return `${label} must be an RFC3339/ISO 8601 timestamp, received '${value}'.`;
}
return null;
}
exports.get_logs = (0, tool_1.tool)("functions", {
name: "get_logs",
description: "Use this to retrieve a page of Cloud Functions log entries using Google Cloud Logging advanced filters.",
inputSchema: zod_1.z.object({
function_names: zod_1.z
.array(zod_1.z.string())
.min(1)
.optional()
.describe("Optional list of deployed Cloud Function IDs to filter logs (e.g. ['fnA','fnB'])."),
page_size: zod_1.z
.number()
.int()
.min(1)
.max(1000)
.default(50)
.describe("Maximum number of log entries to return."),
order: zod_1.z.enum(["asc", "desc"]).default("desc").describe("Sort order by timestamp"),
page_token: zod_1.z
.string()
.optional()
.describe("Opaque page token returned from a previous call to continue pagination."),
min_severity: zod_1.z
.enum(SEVERITY_LEVELS)
.optional()
.describe("Filters results to entries at or above the provided severity level."),
start_time: zod_1.z
.string()
.optional()
.describe("RFC3339 timestamp (YYYY-MM-DDTHH:MM:SSZ). Only entries with timestamp greater than or equal to this are returned."),
end_time: zod_1.z
.string()
.optional()
.describe("RFC3339 timestamp (YYYY-MM-DDTHH:MM:SSZ). Only entries with timestamp less than or equal to this are returned."),
filter: zod_1.z
.string()
.optional()
.describe("Additional Google Cloud Logging advanced filter text that will be AND'ed with the generated filter."),
}),
annotations: {
title: "Get Functions Logs from Cloud Logging",
readOnlyHint: true,
openWorldHint: true,
},
_meta: {
requiresAuth: true,
requiresProject: true,
},
}, async ({ function_names, page_size, order, page_token, min_severity, start_time, end_time, filter }, { projectId }) => {
const resolvedOrder = order?.toLowerCase() === "asc" ? "asc" : "desc";
const resolvedPageSize = page_size ?? 50;
const normalizedSelectors = normalizeFunctionSelectors(function_names);
const filterParts = [(0, functionslog_1.getApiFilter)(normalizedSelectors)];
if (min_severity) {
filterParts.push(`severity>="${min_severity}"`);
}
if (start_time) {
const error = validateTimestamp("start_time", start_time);
if (error)
return (0, util_1.mcpError)(error);
filterParts.push(`timestamp>="${start_time}"`);
}
if (end_time) {
const error = validateTimestamp("end_time", end_time);
if (error)
return (0, util_1.mcpError)(error);
filterParts.push(`timestamp<="${end_time}"`);
}
if (start_time && end_time && Date.parse(start_time) > Date.parse(end_time)) {
return (0, util_1.mcpError)("start_time must be less than or equal to end_time.");
}
if (filter) {
filterParts.push(`(${filter})`);
}
const combinedFilter = filterParts.join("\n");
try {
const { entries, nextPageToken } = await (0, cloudlogging_1.listEntries)(projectId, combinedFilter, resolvedPageSize, resolvedOrder, page_token);
const formattedEntries = entries.map((entry) => {
const functionName = entry.resource?.labels?.function_name ?? entry.resource?.labels?.service_name ?? null;
const payload = entry.textPayload ?? entry.jsonPayload ?? entry.protoPayload ?? entry.labels ?? null;
return {
timestamp: entry.timestamp ?? entry.receiveTimestamp ?? null,
severity: entry.severity ?? "DEFAULT",
function: functionName,
message: entry.textPayload ??
(entry.jsonPayload ? JSON.stringify(entry.jsonPayload) : undefined) ??
(entry.protoPayload ? JSON.stringify(entry.protoPayload) : undefined) ??
"",
payload,
log_name: entry.logName,
trace: entry.trace ?? null,
span_id: entry.spanId ?? null,
};
});
const response = {
filter: combinedFilter,
order: resolvedOrder,
page_size: resolvedPageSize,
entries: resolvedOrder === "asc" ? formattedEntries : formattedEntries.reverse(),
next_page_token: nextPageToken ?? null,
has_more: Boolean(nextPageToken),
};
if (!entries.length) {
return (0, util_1.toContent)(response, {
contentPrefix: "No log entries matched the provided filters.\n\n",
});
}
return (0, util_1.toContent)(response);
}
catch (err) {
const errMsg = (0, error_1.getErrMsg)(err?.original || err, "Failed to retrieve Cloud Logging entries.");
return (0, util_1.mcpError)(errMsg);
}
});