feat: add logs:edge-functions command and historical log support#7944
feat: add logs:edge-functions command and historical log support#7944
Conversation
Add edge function log streaming and historical log fetching for both function and edge function logs. The deploy command output now shows CLI hints for accessing logs when functions are deployed. - Add `logs:edge-functions` command with WebSocket streaming and historical mode via `--from`/`--to` - Add `--from`/`--to` date options to `logs:function` for historical log fetching via analytics API - Add `--deploy-id` option to `logs:function` and `logs:edge-functions` to look up functions from a specific deploy - Accept function ID (not just name) in `logs:function` - Show function/edge-function CLI log hints in deploy output - Create shared `log-api.ts` utility for date parsing, API fetching, and log formatting Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The deploy API response doesn't include an edge_functions_count field. Thread the edgeFunctionsCount from deploySite's local hashing step through to the deploy output so edge function log hints display correctly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…g sanitization Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
…g sanitization Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
…g sanitization Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add proper type assertions to eliminate unsafe-any and non-null assertion lint errors. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1019b05 to
bd52070
Compare
📝 WalkthroughSummary by CodeRabbitRelease Notes
WalkthroughThis pull request adds edge function logging support to the Netlify CLI. It introduces a new Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs). Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
♻️ Duplicate comments (1)
tests/integration/commands/logs/edge-functions.test.ts (1)
199-216: URL substring match is less precise thannew URL().hostnameused infunctions.test.ts.The
url.includes('analytics.services.netlify.com')check can match the hostname appearing anywhere in the URL string (e.g., as a query parameter). The companionfunctions.test.tsusesnew URL(url).hostnamefor the equivalent mock routing, which is more precise. Aligning on the hostname approach would be consistent.♻️ Proposed fix (align with functions.test.ts approach)
- const spyFetch = vi.fn().mockImplementation((url: string) => { - if (url.includes('analytics.services.netlify.com')) { + const spyFetch = vi.fn().mockImplementation((url: string) => { + const parsedUrl = new URL(url) + if (parsedUrl.hostname === 'analytics.services.netlify.com') {- const analyticsCall = spyFetch.mock.calls.find((args: string[]) => - args[0].includes('analytics.services.netlify.com'), + const analyticsCall = spyFetch.mock.calls.find((args: string[]) => { + const parsedUrl = new URL(args[0]) + return parsedUrl.hostname === 'analytics.services.netlify.com' + } )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/integration/commands/logs/edge-functions.test.ts` around lines 199 - 216, The host-matching in the fetch mock is too broad; update the mockImplementation in spyFetch to parse the input URL and compare the hostname exactly (e.g., use new URL(url).hostname === 'analytics.services.netlify.com') instead of url.includes(...), keeping the same return behavior for that host; ensure you reference the existing spyFetch/mockImplementation and originalFetch variables and keep global.fetch assignment and subsequent assertions (spyWebsocket, program.parseAsync, spyFetch.mock.calls) unchanged.
🧹 Nitpick comments (4)
src/commands/logs/log-api.ts (1)
12-32: Add a request timeout to prevent hangs.Consider aborting the request after a reasonable timeout to avoid indefinitely blocking the CLI on slow or stalled networks.
♻️ Suggested update with AbortController
export async function fetchHistoricalLogs({ url, accessToken, }: { url: string accessToken: string }): Promise<unknown> { - const response = await fetch(url, { - method: 'GET', - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }) + const controller = new AbortController() + const timeout = setTimeout(() => controller.abort(), 30_000) + const response = await fetch(url, { + method: 'GET', + headers: { + Authorization: `Bearer ${accessToken}`, + }, + signal: controller.signal, + }) - if (!response.ok) { - const errorData = (await response.json().catch(() => ({}))) as { error?: string } - throw new Error(errorData.error ?? `HTTP ${response.status.toString()}: ${response.statusText}`) - } + try { + if (!response.ok) { + const errorData = (await response.json().catch(() => ({}))) as { error?: string } + throw new Error(errorData.error ?? `HTTP ${response.status.toString()}: ${response.statusText}`) + } + } finally { + clearTimeout(timeout) + } return response.json() }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/logs/log-api.ts` around lines 12 - 32, The fetchHistoricalLogs function can hang on slow networks; create an AbortController, pass controller.signal into fetch, and start a timeout (e.g., 10s) with setTimeout that calls controller.abort() to cancel the request; after fetch completes clearTimeout to avoid leaks and handle AbortError by throwing a clear timeout-specific Error (or rethrow) so the CLI doesn't hang; update fetchHistoricalLogs to use the controller/signal and proper cleanup around the existing response.ok/error handling and response.json call.src/commands/logs/edge-functions.ts (1)
27-27: Redundant.toString()—CLI_LOG_LEVEL_CHOICES_STRINGis an array.
CLI_LOG_LEVEL_CHOICES_STRINGisLOG_LEVELS_LIST.map(...)— an array. Template literals coerce arrays to strings automatically (same as calling.toString()), so the explicit call is redundant.functions.tsomits it consistently.♻️ Proposed fix
- log(`Invalid log level. Choices are:${CLI_LOG_LEVEL_CHOICES_STRING.toString()}`) + log(`Invalid log level. Choices are:${CLI_LOG_LEVEL_CHOICES_STRING}`)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/logs/edge-functions.ts` at line 27, The template literal in the log call redundantly calls .toString() on the array CLI_LOG_LEVEL_CHOICES_STRING; remove the explicit .toString() so the line reads log(`Invalid log level. Choices are:${CLI_LOG_LEVEL_CHOICES_STRING}`) and mirror the style used in functions.ts, ensuring you update the usage in edge-functions.ts where the log(...) invocation occurs.src/commands/logs/functions.ts (1)
34-36: MissingsiteIdnull guard — inconsistent withedge-functions.ts.
edge-functions.tsexplicitly checksif (!siteId)and exits with a helpful message.functions.tsusessiteId!non-null assertions at lines 46/50, so if the project isn't linked, the URL at line 85 would containundefinedas a path segment.🛡️ Proposed guard (mirrors edge-functions.ts)
const { id: siteId } = site + + if (!siteId) { + log('You must link a project before attempting to view function logs') + return + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/logs/functions.ts` around lines 34 - 36, Add the same null-guard used in edge-functions.ts to functions.ts: check the extracted siteId (from const { id: siteId } = site) before using it, and if it's falsy call command.out.error with a helpful message and exit/return early so code never uses siteId!; update any places currently using siteId! (e.g., the URL construction around line ~85) to rely on the guarded, non-null siteId instead.src/commands/deploy/deploy.ts (1)
815-818: Update CLI hint placeholder to reflect ID support.The command now accepts function IDs, so the hint should not imply name-only usage.
Proposed tweak
- 'Function CLI': `netlify logs:function --deploy-id ${results.deployId} <function-name>`, + 'Function CLI': `netlify logs:function --deploy-id ${results.deployId} <function-name-or-id>`,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/deploy/deploy.ts` around lines 815 - 818, The CLI hint in the functionLogsData object currently suggests a name-only placeholder ("<function-name>") even though the command accepts function IDs; update the 'Function CLI' string to indicate both ID and name are supported (e.g., use "<function-id|name>" or "<function-id or function-name>") so users aren’t misled. Locate the functionLogsData constant and change the template string that builds `netlify logs:function --deploy-id ${results.deployId} <function-name>` to use the combined placeholder while keeping the --deploy-id ${results.deployId} interpolation intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/commands/logs.md`:
- Line 108: Replace the vision-based phrase "look up" in the `deploy-id` flag
description with a non-vision term; update the line that reads "`deploy-id`
(*string*) - Deploy ID to look up the function from" to use "find" or "retrieve"
(e.g., "Deploy ID to find the function" or "Deploy ID to retrieve the function")
so Vale's accessibility rule base.accessibilityVision is satisfied.
- Line 102: Update the user-facing argument description that currently uses the
raw token functionName so Vale stops flagging it; either enclose the token in
backticks (`functionName - Name or ID of the function to stream logs for`) or
reword it to plain English (e.g., "function name - Name or ID of the function to
stream logs for") in the logs command documentation entry where functionName
appears.
In `@src/commands/logs/functions.ts`:
- Around line 83-85: The URL currently interpolates branch and
resolvedFunctionName directly into the path (see variables branch and
resolvedFunctionName and the url constant), which breaks for branch names
containing slashes; update the URL construction to apply encodeURIComponent to
each path segment (encode branch and resolvedFunctionName) before interpolation
so the path segments are safely encoded while leaving query parameters
(fromMs/toMs) unchanged.
In `@src/commands/logs/log-api.ts`:
- Around line 56-71: printHistoricalLogs currently compares entry.level
lowercased against levelsToPrint as-is, so uppercase filters like "ERROR" will
miss matches; normalize the incoming levelsToPrint once (e.g., map to lowercase
and optionally trim, or build a Set of lowercased values) at the start of
printHistoricalLogs and then compare the lowercased entry.level against that
normalized collection (use the existing local variable level for the entry
comparison) to avoid case-sensitive mismatches.
In `@tests/integration/commands/logs/functions.test.ts`:
- Line 205: The three new tests (including the one with title 'should find
function by ID') use an empty destructured parameter "async ({}) =>", which
triggers Biome's noEmptyPattern lint error; edit each test's declaration (all
tests that currently use async ({}) =>) and replace the empty destructuring with
an empty parameter list "async () =>" so the test functions are declared as
async () => instead of async ({}) => (look for the test titles such as 'should
find function by ID' and the two other newly added tests that use the same
pattern).
---
Duplicate comments:
In `@tests/integration/commands/logs/edge-functions.test.ts`:
- Around line 199-216: The host-matching in the fetch mock is too broad; update
the mockImplementation in spyFetch to parse the input URL and compare the
hostname exactly (e.g., use new URL(url).hostname ===
'analytics.services.netlify.com') instead of url.includes(...), keeping the same
return behavior for that host; ensure you reference the existing
spyFetch/mockImplementation and originalFetch variables and keep global.fetch
assignment and subsequent assertions (spyWebsocket, program.parseAsync,
spyFetch.mock.calls) unchanged.
---
Nitpick comments:
In `@src/commands/deploy/deploy.ts`:
- Around line 815-818: The CLI hint in the functionLogsData object currently
suggests a name-only placeholder ("<function-name>") even though the command
accepts function IDs; update the 'Function CLI' string to indicate both ID and
name are supported (e.g., use "<function-id|name>" or "<function-id or
function-name>") so users aren’t misled. Locate the functionLogsData constant
and change the template string that builds `netlify logs:function --deploy-id
${results.deployId} <function-name>` to use the combined placeholder while
keeping the --deploy-id ${results.deployId} interpolation intact.
In `@src/commands/logs/edge-functions.ts`:
- Line 27: The template literal in the log call redundantly calls .toString() on
the array CLI_LOG_LEVEL_CHOICES_STRING; remove the explicit .toString() so the
line reads log(`Invalid log level. Choices are:${CLI_LOG_LEVEL_CHOICES_STRING}`)
and mirror the style used in functions.ts, ensuring you update the usage in
edge-functions.ts where the log(...) invocation occurs.
In `@src/commands/logs/functions.ts`:
- Around line 34-36: Add the same null-guard used in edge-functions.ts to
functions.ts: check the extracted siteId (from const { id: siteId } = site)
before using it, and if it's falsy call command.out.error with a helpful message
and exit/return early so code never uses siteId!; update any places currently
using siteId! (e.g., the URL construction around line ~85) to rely on the
guarded, non-null siteId instead.
In `@src/commands/logs/log-api.ts`:
- Around line 12-32: The fetchHistoricalLogs function can hang on slow networks;
create an AbortController, pass controller.signal into fetch, and start a
timeout (e.g., 10s) with setTimeout that calls controller.abort() to cancel the
request; after fetch completes clearTimeout to avoid leaks and handle AbortError
by throwing a clear timeout-specific Error (or rethrow) so the CLI doesn't hang;
update fetchHistoricalLogs to use the controller/signal and proper cleanup
around the existing response.ok/error handling and response.json call.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (29)
docs/commands/agents.mddocs/commands/api.mddocs/commands/blobs.mddocs/commands/build.mddocs/commands/clone.mddocs/commands/completion.mddocs/commands/db.mddocs/commands/dev.mddocs/commands/env.mddocs/commands/functions.mddocs/commands/init.mddocs/commands/link.mddocs/commands/login.mddocs/commands/logs.mddocs/commands/open.mddocs/commands/recipes.mddocs/commands/sites.mddocs/commands/status.mddocs/commands/unlink.mddocs/commands/watch.mddocs/index.mdsrc/commands/deploy/deploy.tssrc/commands/logs/edge-functions.tssrc/commands/logs/functions.tssrc/commands/logs/index.tssrc/commands/logs/log-api.tssrc/utils/deploy/deploy-site.tstests/integration/commands/logs/edge-functions.test.tstests/integration/commands/logs/functions.test.ts
| **Arguments** | ||
|
|
||
| - functionName - Name of the function to stream logs for | ||
| - functionName - Name or ID of the function to stream logs for |
There was a problem hiding this comment.
Vale spellcheck warning: functionName in user-facing docs.
The vale linter flags functionName as a misspelling. Either wrap it in backticks or rephrase to "function name" for the argument description.
✏️ Suggested fix
-- functionName - Name or ID of the function to stream logs for
+- `function-name` - Name or ID of the function to stream logs for📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - functionName - Name or ID of the function to stream logs for | |
| - `function-name` - Name or ID of the function to stream logs for |
🧰 Tools
🪛 GitHub Check: lint-docs
[warning] 102-102:
[vale] reported by reviewdog 🐶
[base.spelling] Spellcheck: did you really mean 'functionName'?
Raw Output:
{"message": "[base.spelling] Spellcheck: did you really mean 'functionName'?", "location": {"path": "docs/commands/logs.md", "range": {"start": {"line": 102, "column": 3}}}, "severity": "WARNING"}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/commands/logs.md` at line 102, Update the user-facing argument
description that currently uses the raw token functionName so Vale stops
flagging it; either enclose the token in backticks (`functionName - Name or ID
of the function to stream logs for`) or reword it to plain English (e.g.,
"function name - Name or ID of the function to stream logs for") in the logs
command documentation entry where functionName appears.
| - `level` (*string*) - Log levels to stream. Choices are: trace, debug, info, warn, error, fatal | ||
| - `debug` (*boolean*) - Print debugging information | ||
| - `auth` (*string*) - Netlify auth token - can be used to run this command without logging in | ||
| - `deploy-id` (*string*) - Deploy ID to look up the function from |
There was a problem hiding this comment.
Vale accessibility warning: avoid vision-based term "look".
Vale (base.accessibilityVision) flags "look up" as a vision-based term. Replace with "find" or "retrieve".
✏️ Suggested fix
-- `deploy-id` (*string*) - Deploy ID to look up the function from
+- `deploy-id` (*string*) - Deploy ID to find the function from📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - `deploy-id` (*string*) - Deploy ID to look up the function from | |
| - `deploy-id` (*string*) - Deploy ID to find the function from |
🧰 Tools
🪛 GitHub Check: lint-docs
[warning] 108-108:
[vale] reported by reviewdog 🐶
[base.accessibilityVision] Don't use vision-based terms. Use something inclusive like 'check', 'search', or 'examine' instead of 'look'.
Raw Output:
{"message": "[base.accessibilityVision] Don't use vision-based terms. Use something inclusive like 'check', 'search', or 'examine' instead of 'look'.", "location": {"path": "docs/commands/logs.md", "range": {"start": {"line": 108, "column": 41}}}, "severity": "WARNING"}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/commands/logs.md` at line 108, Replace the vision-based phrase "look up"
in the `deploy-id` flag description with a non-vision term; update the line that
reads "`deploy-id` (*string*) - Deploy ID to look up the function from" to use
"find" or "retrieve" (e.g., "Deploy ID to find the function" or "Deploy ID to
retrieve the function") so Vale's accessibility rule base.accessibilityVision is
satisfied.
| const branch = siteInfo.build_settings?.repo_branch ?? 'main' | ||
|
|
||
| const url = `https://analytics.services.netlify.com/v2/sites/${siteId}/branch/${branch}/function_logs/${resolvedFunctionName}?from=${fromMs.toString()}&to=${toMs.toString()}` |
There was a problem hiding this comment.
Unencoded path segments will break for feature-branch workflows.
branch and resolvedFunctionName are interpolated directly into the URL path. Git branches can legally contain / (e.g. feature/foo), which adds an unintended extra path segment and silently routes to the wrong endpoint. encodeURIComponent should be applied to each variable segment.
🐛 Proposed fix
- const url = `https://analytics.services.netlify.com/v2/sites/${siteId}/branch/${branch}/function_logs/${resolvedFunctionName}?from=${fromMs.toString()}&to=${toMs.toString()}`
+ const url = `https://analytics.services.netlify.com/v2/sites/${encodeURIComponent(siteId!)}/branch/${encodeURIComponent(branch)}/function_logs/${encodeURIComponent(resolvedFunctionName)}?from=${fromMs.toString()}&to=${toMs.toString()}`🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/commands/logs/functions.ts` around lines 83 - 85, The URL currently
interpolates branch and resolvedFunctionName directly into the path (see
variables branch and resolvedFunctionName and the url constant), which breaks
for branch names containing slashes; update the URL construction to apply
encodeURIComponent to each path segment (encode branch and resolvedFunctionName)
before interpolation so the path segments are safely encoded while leaving query
parameters (fromMs/toMs) unchanged.
| export function printHistoricalLogs(data: unknown, levelsToPrint: string[]): void { | ||
| const entries = Array.isArray(data) ? (data as { timestamp?: string; level?: string; message?: string }[]) : [] | ||
|
|
||
| if (entries.length === 0) { | ||
| log('No logs found for the specified time range') | ||
| return | ||
| } | ||
|
|
||
| for (const entry of entries) { | ||
| const level = (entry.level ?? '').toLowerCase() | ||
| if (levelsToPrint.length > 0 && !levelsToPrint.includes(level)) { | ||
| continue | ||
| } | ||
| log(formatLogEntry(entry)) | ||
| } | ||
| } |
There was a problem hiding this comment.
Normalize filter levels to avoid case-sensitive mismatches.
If callers pass uppercase levels (e.g., ERROR), filtering can drop valid entries. Normalize once before comparing.
🐛 Suggested fix
export function printHistoricalLogs(data: unknown, levelsToPrint: string[]): void {
const entries = Array.isArray(data) ? (data as { timestamp?: string; level?: string; message?: string }[]) : []
+ const normalizedLevels = levelsToPrint.map((level) => level.toLowerCase())
if (entries.length === 0) {
log('No logs found for the specified time range')
return
}
for (const entry of entries) {
const level = (entry.level ?? '').toLowerCase()
- if (levelsToPrint.length > 0 && !levelsToPrint.includes(level)) {
+ if (normalizedLevels.length > 0 && !normalizedLevels.includes(level)) {
continue
}
log(formatLogEntry(entry))
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export function printHistoricalLogs(data: unknown, levelsToPrint: string[]): void { | |
| const entries = Array.isArray(data) ? (data as { timestamp?: string; level?: string; message?: string }[]) : [] | |
| if (entries.length === 0) { | |
| log('No logs found for the specified time range') | |
| return | |
| } | |
| for (const entry of entries) { | |
| const level = (entry.level ?? '').toLowerCase() | |
| if (levelsToPrint.length > 0 && !levelsToPrint.includes(level)) { | |
| continue | |
| } | |
| log(formatLogEntry(entry)) | |
| } | |
| } | |
| export function printHistoricalLogs(data: unknown, levelsToPrint: string[]): void { | |
| const entries = Array.isArray(data) ? (data as { timestamp?: string; level?: string; message?: string }[]) : [] | |
| const normalizedLevels = levelsToPrint.map((level) => level.toLowerCase()) | |
| if (entries.length === 0) { | |
| log('No logs found for the specified time range') | |
| return | |
| } | |
| for (const entry of entries) { | |
| const level = (entry.level ?? '').toLowerCase() | |
| if (normalizedLevels.length > 0 && !normalizedLevels.includes(level)) { | |
| continue | |
| } | |
| log(formatLogEntry(entry)) | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/commands/logs/log-api.ts` around lines 56 - 71, printHistoricalLogs
currently compares entry.level lowercased against levelsToPrint as-is, so
uppercase filters like "ERROR" will miss matches; normalize the incoming
levelsToPrint once (e.g., map to lowercase and optionally trim, or build a Set
of lowercased values) at the start of printHistoricalLogs and then compare the
lowercased entry.level against that normalized collection (use the existing
local variable level for the entry comparison) to avoid case-sensitive
mismatches.
| expect(spyLog).toHaveBeenCalledTimes(2) | ||
| }) | ||
|
|
||
| test('should find function by ID', async ({}) => { |
There was a problem hiding this comment.
Biome noEmptyPattern errors — will fail lint CI.
All three new tests use async ({}) => with an empty destructuring pattern. Biome emits an error for each. Change to async () =>.
🐛 Proposed fix
- test('should find function by ID', async ({}) => {
+ test('should find function by ID', async () => {- test('should look up function from deploy when --deploy is specified', async ({}) => {
+ test('should look up function from deploy when --deploy-id is specified', async () => {- test('should fetch historical logs when --from is specified', async ({}) => {
+ test('should fetch historical logs when --from is specified', async () => {Also applies to: 231-231, 267-267
🧰 Tools
🪛 Biome (2.4.4)
[error] 205-205: Unexpected empty object pattern.
(lint/correctness/noEmptyPattern)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/integration/commands/logs/functions.test.ts` at line 205, The three new
tests (including the one with title 'should find function by ID') use an empty
destructured parameter "async ({}) =>", which triggers Biome's noEmptyPattern
lint error; edit each test's declaration (all tests that currently use async
({}) =>) and replace the empty destructuring with an empty parameter list "async
() =>" so the test functions are declared as async () => instead of async ({})
=> (look for the test titles such as 'should find function by ID' and the two
other newly added tests that use the same pattern).
There was a problem hiding this comment.
♻️ Duplicate comments (2)
docs/commands/logs.md (2)
102-102:⚠️ Potential issue | 🟡 MinorWrap
functionNamein code formatting (or rephrase).
functionNameis still flagged by Vale spellcheck in user-facing docs. Use backticks or plain words (function name) to avoid the warning.✏️ Suggested fix
-- functionName - Name or ID of the function to stream logs for +- `functionName` - Name or ID of the function to stream logs for🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/commands/logs.md` at line 102, Update the docs/commands/logs.md line that currently shows "functionName - Name or ID of the function to stream logs for" by wrapping functionName in inline code backticks or rewording it to plain words (e.g., `functionName` or "function name") so Vale spellcheck no longer flags it; locate the occurrence of the identifier "functionName" in that file and replace it with the chosen formatted/rephrased version.
106-106:⚠️ Potential issue | 🟡 MinorReplace vision-based phrase “look up” in flag description.
This wording is still flagged by Vale accessibility rules; switch to “find” or “retrieve”.
✏️ Suggested fix
-- `deploy-id` (*string*) - Deploy ID to look up the function from +- `deploy-id` (*string*) - Deploy ID to find the function from🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/commands/logs.md` at line 106, The flag description for `deploy-id` uses the vision-based phrase "look up"; update the description in the `deploy-id` flag entry to use an alternative such as "find" or "retrieve" (e.g., change "Deploy ID to look up the function from" to "Deploy ID to find the function" or "Deploy ID to retrieve the function") so it no longer uses vision-based language.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@docs/commands/logs.md`:
- Line 102: Update the docs/commands/logs.md line that currently shows
"functionName - Name or ID of the function to stream logs for" by wrapping
functionName in inline code backticks or rewording it to plain words (e.g.,
`functionName` or "function name") so Vale spellcheck no longer flags it; locate
the occurrence of the identifier "functionName" in that file and replace it with
the chosen formatted/rephrased version.
- Line 106: The flag description for `deploy-id` uses the vision-based phrase
"look up"; update the description in the `deploy-id` flag entry to use an
alternative such as "find" or "retrieve" (e.g., change "Deploy ID to look up the
function from" to "Deploy ID to find the function" or "Deploy ID to retrieve the
function") so it no longer uses vision-based language.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
docs/commands/logs.md
| | Subcommand | description | | ||
| |:--------------------------- |:-----| | ||
| | [`logs:deploy`](/commands/logs#logsdeploy) | Stream the logs of deploys currently being built to the console | | ||
| | [`logs:edge-functions`](/commands/logs#logsedge-functions) | Stream netlify edge function logs to the console | |
There was a problem hiding this comment.
I understand why this is plural and logs:function is singular, but it still itches! 😖
In the future, I think we could rename logs:function to logs:functions and start accepting multiple function names, since there's nothing stopping us from listening to different streams and interleaving them, just like we do with edge functions.
| const functionLogsData: Record<string, string> = { | ||
| 'Function logs': terminalLink(results.functionLogsUrl, results.functionLogsUrl, { fallback: false }), | ||
| 'Edge function Logs': terminalLink(results.edgeFunctionLogsUrl, results.edgeFunctionLogsUrl, { fallback: false }), | ||
| 'Function CLI': `netlify logs:function --deploy-id ${results.deployId} <function-name>`, |
There was a problem hiding this comment.
"Function CLI" feels a bit weird? How about:
- Functions logs
- Web: https://(...)
- CLI: `netlify logs:function (...)`
- Edge Functions logs
- Web: https://(...)
- CLI: `netlify logs:edge-functions (...)`
| } | ||
| } | ||
|
|
||
| const ws = getWebSocket('wss://socketeer.services.netlify.com/edge-function/logs') |
There was a problem hiding this comment.
Side note: I hate that we're exposing this. We should set up a customer-facing domain name for this.
| return ms | ||
| } | ||
|
|
||
| export async function fetchHistoricalLogs({ |
There was a problem hiding this comment.
This method doesn't really have anything to do with fetching logs. It's just making a fetch call and returning the response. I would recommend either getting rid of it and making the call directly, or moving the URL generation over here. I would recommend the latter, since we're currently constructing the analytics endpoint in different places and we could centralise that.
Summary
logs:edge-functionscommand for streaming and historical edge function logs--from/--todate options tologs:functionandlogs:edge-functionsfor historical log fetching via the analytics API--deploy-idoption to both commands to look up functions from a specific deploylogs:functionlog-api.tsutility for date parsing, REST API fetching, and log formattingNew files
src/commands/logs/log-api.ts— shared utilities for historical log fetchingsrc/commands/logs/edge-functions.ts— edge function log handlertests/integration/commands/logs/edge-functions.test.ts— tests forlogs:edge-functionsTest plan
npm run typecheck— no new type errorsnpm exec vitest -- run tests/integration/commands/logs/— all 15 tests passnpm exec vitest -- run tests/integration/commands/deploy/— 39/40 pass (1 pre-existing failure)./bin/run.js logs:function --helpshows--from,--to,--deploy-idoptions./bin/run.js logs:edge-functions --helpshows all options🤖 Generated with Claude Code