Skip to content

Commit 2c43341

Browse files
committed
feat(commands/util/walkthrough): richer per-platform log guide (server + agent + docs)
1 parent 5b8ba04 commit 2c43341

1 file changed

Lines changed: 124 additions & 24 deletions

File tree

‎src/commands/util/walkthrough.ts‎

Lines changed: 124 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -49,25 +49,102 @@ const productResources: Record<string, ResourceLink[]> = {
4949
],
5050
};
5151

52-
// Where each product writes its logs, keyed by product value then platform
53-
// value. Shown once both are known. Paths come from the Coder docs. Values are
54-
// Markdown; a product/platform without an entry simply shows no hint.
55-
const logLocations: Record<string, Partial<Record<string, string>>> = {
52+
// The log guide for a product/platform is one section per log source, split by
53+
// dividers, then a docs link. `default` covers platforms without a specific
54+
// entry. Paths and commands come from the Coder repo and docs.
55+
type LogSection = { title: string; location: string; command: string };
56+
type LogGuide = { sections: LogSection[]; footer: string };
57+
58+
const architectureDocs =
59+
"https://coder.com/docs/admin/infrastructure/architecture";
60+
const coderFooter = `Learn about the difference between the Coder Server and Agent at ${architectureDocs}`;
61+
62+
const agentSectionUnix: LogSection = {
63+
title: "Coder Workspace Agent/`coder agent`",
64+
location: "`/tmp/coder-agent.log` inside the workspace",
65+
command: "coder ssh <workspace> -- cat /tmp/coder-agent.log",
66+
};
67+
68+
const logGuides: Record<string, Partial<Record<string, LogGuide>>> = {
5669
coder: {
57-
linux:
58-
"Server: `journalctl -u coder` on a VM, or `kubectl logs deployment/coder -n <namespace>` on Kubernetes.\nWorkspace agent: `/tmp/coder-agent.log`.",
59-
macos: "Workspace agent: `/tmp/coder-agent.log`.",
60-
windows:
61-
"Workspace agent logs live wherever your template writes them; check the template's startup script.",
70+
linux: {
71+
sections: [
72+
{
73+
title: "Coder Server/`coderd`",
74+
location: "the systemd journal (`coderd` writes to standard output)",
75+
command:
76+
"sudo journalctl -u coder.service --no-pager\n# Docker: docker logs coder\n# Kubernetes: kubectl logs deployment/coder -n coder",
77+
},
78+
agentSectionUnix,
79+
],
80+
footer: coderFooter,
81+
},
82+
macos: {
83+
sections: [
84+
{
85+
title: "Coder Server/`coderd`",
86+
location:
87+
"standard output (`coderd` has no default log file on macOS)",
88+
command:
89+
'CODER_LOGGING_HUMAN="$HOME/coder.log" coder server\ncat "$HOME/coder.log"',
90+
},
91+
agentSectionUnix,
92+
],
93+
footer: coderFooter,
94+
},
95+
windows: {
96+
sections: [
97+
{
98+
title: "Coder Server/`coderd`",
99+
location:
100+
"standard output (`coderd` has no default log file on Windows)",
101+
command:
102+
'$env:CODER_LOGGING_HUMAN="$HOME\\coder.log"; coder server\nGet-Content "$HOME\\coder.log"',
103+
},
104+
{
105+
title: "Coder Workspace Agent/`coder agent`",
106+
location:
107+
"the path your template sets (the azure-windows example uses `C:\\AzureData\\CoderAgent.log`)",
108+
command:
109+
'coder ssh <workspace> -- powershell -Command "Get-Content C:\\AzureData\\CoderAgent.log"',
110+
},
111+
],
112+
footer: coderFooter,
113+
},
114+
default: {
115+
sections: [
116+
{
117+
title: "Coder Server/`coderd`",
118+
location:
119+
"standard output; capture it to a file with `CODER_LOGGING_HUMAN`",
120+
command: 'CODER_LOGGING_HUMAN="$HOME/coder.log" coder server',
121+
},
122+
agentSectionUnix,
123+
],
124+
footer: coderFooter,
125+
},
62126
},
63127
"code-server": {
64-
linux:
65-
"`~/.local/share/code-server/coder-logs/` (and `~/.vscode-server/data/logs/` for the VS Code server).",
66-
macos:
67-
"`~/.local/share/code-server/coder-logs/` (and `~/.vscode-server/data/logs/` for the VS Code server).",
128+
default: {
129+
sections: [
130+
{
131+
title: "code-server",
132+
location:
133+
"`/tmp/code-server.log` (the Coder code-server module default; code-server's own logs live under `~/.local/share/code-server/`)",
134+
command: "coder ssh <workspace> -- cat /tmp/code-server.log",
135+
},
136+
],
137+
footer:
138+
"code-server is configured by the Coder code-server module: https://registry.coder.com/modules/coder/code-server",
139+
},
68140
},
69141
};
70142

143+
const logGuideFor = (product?: string, platform?: string) => {
144+
const perProduct = product ? logGuides[product] : undefined;
145+
return perProduct?.[platform ?? ""] ?? perProduct?.default;
146+
};
147+
71148
// The walkthrough asks one selector per field, in this order.
72149
const steps = [
73150
{
@@ -135,6 +212,33 @@ async function lifecycleText(client: Client) {
135212
return `When your issue is resolved, use ${close} to close it.\nUse ${reopen} to reopen it if needed.`;
136213
}
137214

215+
// Renders the "where are my logs" container for the chosen product and
216+
// platform: one section per log source separated by dividers, then a docs link.
217+
// Returns undefined when there is no guide for the product.
218+
function logGuideComponent(product?: string, platform?: string) {
219+
const guide = logGuideFor(product, platform);
220+
if (!guide) {
221+
return undefined;
222+
}
223+
224+
const container = new ContainerBuilder().setAccentColor(Colors.Blurple);
225+
226+
guide.sections.forEach((section, index) => {
227+
if (index > 0) {
228+
container.addSeparatorComponents(new SeparatorBuilder());
229+
}
230+
container.addTextDisplayComponents(
231+
text(
232+
`${section.title} logs are located at ${section.location}.\n\nYou can get them easily via:\n\`\`\`\n${section.command}\n\`\`\``,
233+
),
234+
);
235+
});
236+
237+
return container
238+
.addSeparatorComponents(new SeparatorBuilder())
239+
.addTextDisplayComponents(text(guide.footer));
240+
}
241+
138242
// Builds the walkthrough message from the answered values so far: an info
139243
// container with a field row per answer, the current question and selector while
140244
// steps remain, and the selected product's documentation buttons at the bottom.
@@ -189,17 +293,13 @@ async function buildMessage(
189293
);
190294
}
191295

192-
// The log locations depend on the platform, so hide them while that answer is
193-
// being edited. When editing another field, show them above the question.
194-
const logHint =
195-
editIndex === 2 ? undefined : logLocations[values[1]]?.[values[2]];
196-
const logComponent = logHint
197-
? new ContainerBuilder()
198-
.setAccentColor(Colors.Blurple)
199-
.addTextDisplayComponents(
200-
text(`**Where to find your logs**\n${logHint}`),
201-
)
202-
: undefined;
296+
// The log guide depends on the platform, so only show it once the platform is
297+
// known, and hide it while that answer is being edited. When editing another
298+
// field it renders above the question.
299+
const logComponent =
300+
editIndex !== 2 && values[2] !== undefined
301+
? logGuideComponent(values[1], values[2])
302+
: undefined;
203303

204304
if (editIndex !== undefined && logComponent) {
205305
components.push(logComponent, ...question);

0 commit comments

Comments
 (0)