add opt in docker logs flag
This commit is contained in:
1 parent
e012910332
commit
431cada08e
7 files changed
+54
-7
No files matched your search
@@ -177,6 +177,7 @@ export const createMonitorBodyValidation = z
|
||||
geoCheckEnabled: z.boolean().optional(),
|
||||
geoCheckLocations: z.array(z.enum(GeoContinents)).optional(),
|
||||
geoCheckInterval: z.number().min(300000).optional(),
|
||||
dockerLogsEnabled: z.boolean().optional(),
|
||||
dnsServer: dnsServerValidation.optional(),
|
||||
dnsRecordType: z.enum(DnsRecordTypes).optional(),
|
||||
})
|
||||
@@ -222,6 +223,7 @@ export const editMonitorBodyValidation = z
|
||||
geoCheckEnabled: z.boolean().optional(),
|
||||
geoCheckLocations: z.array(z.enum(GeoContinents)).optional(),
|
||||
geoCheckInterval: z.number().min(300000).optional(),
|
||||
dockerLogsEnabled: z.boolean().optional(),
|
||||
dnsServer: dnsServerValidation.optional(),
|
||||
dnsRecordType: z.enum(DnsRecordTypes).optional(),
|
||||
})
|
||||
@@ -298,6 +300,8 @@ const importedMonitorSchema = z
|
||||
geoCheckEnabled: z.boolean().default(false),
|
||||
geoCheckLocations: z.array(z.enum(GeoContinents)).default([]),
|
||||
geoCheckInterval: z.number().min(300000).default(300000),
|
||||
dockerLogsEnabled: z.boolean().default(false),
|
||||
|
||||
dnsServer: dnsServerValidation.optional(),
|
||||
dnsRecordType: z.enum(DnsRecordTypes).optional(),
|
||||
createdAt: z.string().optional(),
|
||||
@@ -387,6 +391,7 @@ export const monitorResponseSchema = z
|
||||
geoCheckEnabled: z.boolean(),
|
||||
geoCheckLocations: z.array(z.enum(GeoContinents)),
|
||||
geoCheckInterval: z.number(),
|
||||
dockerLogsEnabled: z.boolean(),
|
||||
dnsServer: z.string().optional(),
|
||||
dnsRecordType: z.enum(DnsRecordTypes).optional(),
|
||||
teamId: z.string(),
|
||||
|
||||
@@ -306,6 +306,10 @@ const MonitorSchema = new Schema<MonitorDocument>(
|
||||
type: Number,
|
||||
default: 300000,
|
||||
},
|
||||
dockerLogsEnabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
dnsServer: {
|
||||
type: String,
|
||||
},
|
||||
|
||||
@@ -454,6 +454,7 @@ class MongoMonitorsRepository implements IMonitorsRepository {
|
||||
geoCheckEnabled: doc.geoCheckEnabled ?? false,
|
||||
geoCheckLocations: doc.geoCheckLocations ?? [],
|
||||
geoCheckInterval: doc.geoCheckInterval ?? 300000,
|
||||
dockerLogsEnabled: doc.dockerLogsEnabled ?? false,
|
||||
dnsServer: doc.dnsServer ?? undefined,
|
||||
dnsRecordType: doc.dnsRecordType ?? undefined,
|
||||
createdAt: toDateString(doc.createdAt),
|
||||
|
||||
@@ -109,6 +109,7 @@ export interface Monitor {
|
||||
geoCheckEnabled?: boolean;
|
||||
geoCheckLocations?: GeoContinent[];
|
||||
geoCheckInterval?: number;
|
||||
dockerLogsEnabled?: boolean;
|
||||
dnsServer?: string;
|
||||
dnsRecordType?: DnsRecordType;
|
||||
recentChecks: CheckSnapshot[];
|
||||
|
||||
@@ -279,6 +279,11 @@ export class DockerProvider implements IStatusProvider<DockerStatusPayload> {
|
||||
}
|
||||
};
|
||||
|
||||
private collectLogs = async (docker: Dockerode, summaries: Dockerode.ContainerInfo[]): Promise<DockerContainerLogs[]> => {
|
||||
const rawLogs = await this.mapWithConcurrency(summaries, STATS_CONCURRENCY, (s) => this.toContainerLogs(docker, s));
|
||||
return rawLogs.filter((log) => log !== null);
|
||||
};
|
||||
|
||||
handle = async (monitor: Monitor): Promise<MonitorStatusResponse<DockerStatusPayload>> => {
|
||||
try {
|
||||
const docker = new this.DockerLib(this.toDockerOptions(monitor));
|
||||
@@ -307,8 +312,8 @@ export class DockerProvider implements IStatusProvider<DockerStatusPayload> {
|
||||
|
||||
const summaries = await docker.listContainers({ all: true });
|
||||
const containers = await this.mapWithConcurrency(summaries, STATS_CONCURRENCY, (s) => this.toContainerInfo(docker, s));
|
||||
const rawLogs = await this.mapWithConcurrency(summaries, STATS_CONCURRENCY, (s) => this.toContainerLogs(docker, s));
|
||||
const logs = rawLogs.filter((log) => log !== null);
|
||||
const logs = monitor.dockerLogsEnabled ? await this.collectLogs(docker, summaries) : undefined;
|
||||
|
||||
const summary: DockerContainerSummary = {
|
||||
total: containers.length,
|
||||
running: containers.filter((c) => c.state === "running").length,
|
||||
|
||||
@@ -297,7 +297,7 @@ describe("DockerProvider", () => {
|
||||
);
|
||||
const { provider } = setup({ logs });
|
||||
|
||||
const result = await provider.handle(makeMonitor());
|
||||
const result = await provider.handle(makeMonitor({ dockerLogsEnabled: true }));
|
||||
|
||||
expect(result.payload?.logs?.[0]?.lines).toEqual([
|
||||
{ ts: "2026-01-01T00:00:37.100000000Z", stream: "stdout", text: "ready" },
|
||||
@@ -309,7 +309,7 @@ describe("DockerProvider", () => {
|
||||
const logs = jest.fn().mockResolvedValue(Buffer.from("noise\n2026-01-01T00:00:00Z first\n2026-01-01T00:00:01.12Z second\n"));
|
||||
const { provider } = setup({ logs });
|
||||
|
||||
const result = await provider.handle(makeMonitor());
|
||||
const result = await provider.handle(makeMonitor({ dockerLogsEnabled: true }));
|
||||
|
||||
expect(result.payload?.logs?.[0]?.lines).toEqual([
|
||||
{ ts: "2026-01-01T00:00:00.000000000Z", stream: "stdout", text: "first" },
|
||||
@@ -321,7 +321,7 @@ describe("DockerProvider", () => {
|
||||
const logs = jest.fn().mockResolvedValue(Buffer.from(`2026-01-01T00:00:00Z ${"x".repeat(5000)}\n`));
|
||||
const { provider } = setup({ logs });
|
||||
|
||||
const result = await provider.handle(makeMonitor());
|
||||
const result = await provider.handle(makeMonitor({ dockerLogsEnabled: true }));
|
||||
const text = result.payload?.logs?.[0]?.lines[0]?.text;
|
||||
expect(text).toEqual(expect.stringMatching(/ …\[truncated\]$/));
|
||||
expect(Buffer.byteLength(text?.replace(" …[truncated]", "") ?? "", "utf8")).toBeLessThanOrEqual(4096);
|
||||
@@ -330,7 +330,7 @@ describe("DockerProvider", () => {
|
||||
it("isolates a logs failure and warns", async () => {
|
||||
const { provider, logger } = setup({ logs: jest.fn().mockRejectedValue(new Error("logs failed")) });
|
||||
|
||||
const result = await provider.handle(makeMonitor());
|
||||
const result = await provider.handle(makeMonitor({ dockerLogsEnabled: true }));
|
||||
|
||||
expect(result.payload?.logs).toEqual([]);
|
||||
expect(logger.warn).toHaveBeenCalledWith(expect.objectContaining({ message: "Failed to read logs for container my-container" }));
|
||||
@@ -340,11 +340,29 @@ describe("DockerProvider", () => {
|
||||
const containers = [makeContainer({ Id: "a".repeat(64) }), makeContainer({ Id: "b".repeat(64), State: "exited" })];
|
||||
const { provider, logs } = setup({ listContainers: jest.fn().mockResolvedValue(containers) });
|
||||
|
||||
await provider.handle(makeMonitor());
|
||||
await provider.handle(makeMonitor({ dockerLogsEnabled: true }));
|
||||
|
||||
expect(logs).toHaveBeenCalledTimes(2);
|
||||
expect(logs).toHaveBeenCalledWith({ follow: false, stdout: true, stderr: true, timestamps: true, tail: DOCKER_LOG_TAIL_LINES });
|
||||
});
|
||||
|
||||
it("does not read logs when dockerLogsEnabled is false", async () => {
|
||||
const { provider, logs } = setup();
|
||||
|
||||
const result = await provider.handle(makeMonitor({ dockerLogsEnabled: false }));
|
||||
|
||||
expect(logs).not.toHaveBeenCalled();
|
||||
expect(result.payload?.logs).toBeUndefined();
|
||||
});
|
||||
|
||||
it("does not read logs when dockerLogsEnabled is unset", async () => {
|
||||
const { provider, logs } = setup();
|
||||
|
||||
const result = await provider.handle(makeMonitor());
|
||||
|
||||
expect(logs).not.toHaveBeenCalled();
|
||||
expect(result.payload?.logs).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
// ── Enrichment fault isolation ───────────────────────────────────────
|
||||
|
||||
@@ -233,6 +233,19 @@ describe("monitorValidation — strategy gating", () => {
|
||||
expect(parsed.monitors[0].strategy).toBeUndefined();
|
||||
});
|
||||
|
||||
it("defaults dockerLogsEnabled to false on imported docker monitors", () => {
|
||||
const parsed = importMonitorsBodyValidation.parse({
|
||||
monitors: [
|
||||
{
|
||||
name: "Imported Docker",
|
||||
type: "docker",
|
||||
url: "unix:///var/run/docker.sock",
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(parsed.monitors[0].dockerLogsEnabled).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects strategy on imported non-pagespeed monitors", () => {
|
||||
expect(() =>
|
||||
importMonitorsBodyValidation.parse({
|
||||
|
||||
Reference in new issue
Block a user