Skip to content

refactor(orchestrator/buildlogger): implement zapcore.Core directly#2614

Merged
tvi merged 1 commit intomainfrom
t/ctx-todo
May 10, 2026
Merged

refactor(orchestrator/buildlogger): implement zapcore.Core directly#2614
tvi merged 1 commit intomainfrom
t/ctx-todo

Conversation

@tvi
Copy link
Copy Markdown
Contributor

@tvi tvi commented May 10, 2026

LogEntryLogger previously implemented io.Writer and was plugged into a zap pipeline via zapcore.NewCore(jsonEncoder, logEntryLogger, DebugLevel). Each entry was serialized to JSON by the encoder and then parsed back out by Write to recover the message, level, timestamp, and fields. Two of those parse paths could fail and called logger.L().Error(context.TODO(), ...) because Write has no context.

Implement zapcore.Core on LogEntryLogger instead: Check, Write(Entry, []Field), Sync, With, plus an embedded zapcore.LevelEnabler. The Entry struct already carries level, time, and message, and []Field is encoded straight into a map[string]string via zapcore.MapObjectEncoder, so the JSON encode/parse round-trip and its failure modes disappear, and with them the two context.TODO() call sites.

With returns a childCore that shares the parent's capture slice and mu and only carries the accumulated fields, so logger.With(...) chains do not fork the capture buffer and Lines() still observes every entry.

Wire-up in TemplateCreate is simplified: drop the JSON encoder and the zapcore.NewCore wrapper and tee LogEntryLogger directly with the build logger core. Test helper writeTestBuildLogs constructs zapcore.Entry values instead of marshalling synthetic JSON and calling Write([]byte).

Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Code review skipped — your organization has reached its monthly code review spending cap.

An organization admin can view or raise the cap at claude.ai/admin-settings/claude-code. The cap resets at the start of the next billing period.

Once the cap resets or is raised, reopen this pull request to trigger a review.

@cursor
Copy link
Copy Markdown

cursor Bot commented May 10, 2026

PR Summary

Medium Risk
Moderate risk because it changes how build logs are captured and field/level/timestamp extraction now depends on zapcore field encoding; mistakes could drop fields or alter ordering/levels. Concurrency behavior also changes since Write and Sync now share a mutex as a quiescence barrier.

Overview
Reworks LogEntryLogger to implement zapcore.Core directly, capturing zapcore.Entry + fields into TemplateBuildLogEntry without JSON encode/parse, and adds a childCore so With(...) chains share the same capture buffer.

Updates template build logging to tee this core directly with the existing build logger (dropping the JSON encoder/core wrapper) and adjusts the status test helper to write zapcore.Entry values instead of synthetic JSON lines.

Reviewed by Cursor Bugbot for commit ef589b0. Bugbot is set up for automated code reviews on this repo. Configure here.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 10, 2026

❌ 12 Tests Failed:

Tests completed Failed Passed Skipped
2609 12 2597 7
View the full list of 13 ❄️ flaky test(s)
github.com/e2b-dev/infra/tests/integration/internal/tests/api/sandboxes::TestUpdateNetworkConfig

Flake rate in main: 76.03% (Passed 116 times, Failed 368 times)

Stack Traces | 179s run time
=== RUN   TestUpdateNetworkConfig
=== PAUSE TestUpdateNetworkConfig
=== CONT  TestUpdateNetworkConfig
--- FAIL: TestUpdateNetworkConfig (179.28s)
github.com/e2b-dev/infra/tests/integration/internal/tests/api/sandboxes::TestUpdateNetworkConfig/pause_resume_preserves_allow_internet_access_false

Flake rate in main: 76.53% (Passed 111 times, Failed 362 times)

Stack Traces | 3.89s run time
=== RUN   TestUpdateNetworkConfig/pause_resume_preserves_allow_internet_access_false
    sandbox_network_update_test.go:372: Command [curl] output: event:{start:{pid:1366}}
    sandbox_network_update_test.go:372: Command [curl] output: event:{end:{exit_code:35  exited:true  status:"exit status 35"  error:"exit status 35"}}
Executing command curl in sandbox iks27tz390z6ej48xguou
    sandbox_network_update_test.go:372: Command [curl] output: event:{start:{pid:1367}}
    sandbox_network_update_test.go:372: Command [curl] output: event:{end:{exit_code:35  exited:true  status:"exit status 35"  error:"exit status 35"}}
    sandbox_network_update_test.go:391: Command [curl] output: event:{start:{pid:1368}}
    sandbox_network_update_test.go:391: Command [curl] output: event:{data:{stdout:"HTTP/2 302 \r\nx-content-type-options: nosniff\r\nlocation: https://dns.google/\r\ndate: Sun, 10 May 2026 17:40:42 GMT\r\ncontent-type: text/html; charset=UTF-8\r\nserver: HTTP server (unknown)\r\ncontent-length: 216\r\nx-xss-protection: 0\r\nx-frame-options: SAMEORIGIN\r\nalt-svc: h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000\r\n\r\n"}}
    sandbox_network_update_test.go:391: Command [curl] output: event:{end:{exited:true  status:"exit status 0"}}
    sandbox_network_update_test.go:391: Command [curl] completed successfully in sandbox iks27tz390z6ej48xguou
    sandbox_network_update_test.go:391: 
        	Error Trace:	.../api/sandboxes/sandbox_network_out_test.go:74
        	            				.../api/sandboxes/sandbox_network_update_test.go:60
        	            				.../api/sandboxes/sandbox_network_update_test.go:391
        	Error:      	An error is expected but got nil.
        	Test:       	TestUpdateNetworkConfig/pause_resume_preserves_allow_internet_access_false
        	Messages:   	https://8.8.8.8 should be blocked
--- FAIL: TestUpdateNetworkConfig/pause_resume_preserves_allow_internet_access_false (3.89s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestBindLocalhost

Flake rate in main: 57.93% (Passed 191 times, Failed 263 times)

Stack Traces | 0s run time
=== RUN   TestBindLocalhost
=== PAUSE TestBindLocalhost
=== CONT  TestBindLocalhost
--- FAIL: TestBindLocalhost (0.00s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestBindLocalhost/bind_0_0_0_0

Flake rate in main: 63.10% (Passed 107 times, Failed 183 times)

Stack Traces | 7.27s run time
=== RUN   TestBindLocalhost/bind_0_0_0_0
=== PAUSE TestBindLocalhost/bind_0_0_0_0
=== CONT  TestBindLocalhost/bind_0_0_0_0
    localhost_bind_test.go:69: Command [python] output: event:{start:{pid:1258}}
Executing command python in sandbox i5u09tmn24ivczo8dqf8h
    localhost_bind_test.go:90: 
        	Error Trace:	.../tests/envd/localhost_bind_test.go:90
        	Error:      	Not equal: 
        	            	expected: 200
        	            	actual  : 502
        	Test:       	TestBindLocalhost/bind_0_0_0_0
        	Messages:   	Unexpected status code 502 for bind address 0.0.0.0
--- FAIL: TestBindLocalhost/bind_0_0_0_0 (7.27s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestBindLocalhost/bind_::1

Flake rate in main: 64.80% (Passed 107 times, Failed 197 times)

Stack Traces | 7.15s run time
=== RUN   TestBindLocalhost/bind_::1
=== PAUSE TestBindLocalhost/bind_::1
=== CONT  TestBindLocalhost/bind_::1
    localhost_bind_test.go:69: Command [python] output: event:{start:{pid:1257}}
    localhost_bind_test.go:90: 
        	Error Trace:	.../tests/envd/localhost_bind_test.go:90
        	Error:      	Not equal: 
        	            	expected: 200
        	            	actual  : 502
        	Test:       	TestBindLocalhost/bind_::1
        	Messages:   	Unexpected status code 502 for bind address ::1
--- FAIL: TestBindLocalhost/bind_::1 (7.15s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestBindLocalhost/bind_localhost

Flake rate in main: 65.03% (Passed 107 times, Failed 199 times)

Stack Traces | 7.63s run time
=== RUN   TestBindLocalhost/bind_localhost
=== PAUSE TestBindLocalhost/bind_localhost
=== CONT  TestBindLocalhost/bind_localhost
Executing command python in sandbox ig2vtr9grq17xxqy3gk9m
    localhost_bind_test.go:69: Command [python] output: event:{start:{pid:1258}}
Executing command python in sandbox if6gs220mak53pp9y95gl
    localhost_bind_test.go:90: 
        	Error Trace:	.../tests/envd/localhost_bind_test.go:90
        	Error:      	Not equal: 
        	            	expected: 200
        	            	actual  : 502
        	Test:       	TestBindLocalhost/bind_localhost
        	Messages:   	Unexpected status code 502 for bind address localhost
--- FAIL: TestBindLocalhost/bind_localhost (7.63s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir

Flake rate in main: 52.65% (Passed 134 times, Failed 149 times)

Stack Traces | 0.67s run time
=== RUN   TestListDir
=== PAUSE TestListDir
=== CONT  TestListDir
--- FAIL: TestListDir (0.67s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir/depth_0_lists_only_root_directory

Flake rate in main: 56.68% (Passed 107 times, Failed 140 times)

Stack Traces | 0.02s run time
=== RUN   TestListDir/depth_0_lists_only_root_directory
=== PAUSE TestListDir/depth_0_lists_only_root_directory
=== CONT  TestListDir/depth_0_lists_only_root_directory
    filesystem_test.go:97: 
        	Error Trace:	.../tests/envd/filesystem_test.go:97
        	Error:      	Received unexpected error:
        	            	unavailable: 502 Bad Gateway
        	Test:       	TestListDir/depth_0_lists_only_root_directory
--- FAIL: TestListDir/depth_0_lists_only_root_directory (0.02s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir/depth_1_lists_root_directory

Flake rate in main: 56.68% (Passed 107 times, Failed 140 times)

Stack Traces | 0.01s run time
=== RUN   TestListDir/depth_1_lists_root_directory
=== PAUSE TestListDir/depth_1_lists_root_directory
=== CONT  TestListDir/depth_1_lists_root_directory
    filesystem_test.go:97: 
        	Error Trace:	.../tests/envd/filesystem_test.go:97
        	Error:      	Received unexpected error:
        	            	unavailable: 502 Bad Gateway
        	Test:       	TestListDir/depth_1_lists_root_directory
--- FAIL: TestListDir/depth_1_lists_root_directory (0.01s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)

Flake rate in main: 56.68% (Passed 107 times, Failed 140 times)

Stack Traces | 0.02s run time
=== RUN   TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)
=== PAUSE TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)
=== CONT  TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)
    filesystem_test.go:97: 
        	Error Trace:	.../tests/envd/filesystem_test.go:97
        	Error:      	Received unexpected error:
        	            	unavailable: 502 Bad Gateway
        	Test:       	TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory)
--- FAIL: TestListDir/depth_2_lists_first_level_of_subdirectories_(in_this_case_the_root_directory) (0.02s)
github.com/e2b-dev/infra/tests/integration/internal/tests/envd::TestListDir/depth_3_lists_all_directories_and_files

Flake rate in main: 56.68% (Passed 107 times, Failed 140 times)

Stack Traces | 0.01s run time
=== RUN   TestListDir/depth_3_lists_all_directories_and_files
=== PAUSE TestListDir/depth_3_lists_all_directories_and_files
=== CONT  TestListDir/depth_3_lists_all_directories_and_files
    filesystem_test.go:97: 
        	Error Trace:	.../tests/envd/filesystem_test.go:97
        	Error:      	Received unexpected error:
        	            	unavailable: 502 Bad Gateway
        	Test:       	TestListDir/depth_3_lists_all_directories_and_files
--- FAIL: TestListDir/depth_3_lists_all_directories_and_files (0.01s)
github.com/e2b-dev/infra/tests/integration/internal/tests/orchestrator::TestSandboxMemoryIntegrity

Flake rate in main: 65.59% (Passed 117 times, Failed 223 times)

Stack Traces | 83s run time
=== RUN   TestSandboxMemoryIntegrity
=== PAUSE TestSandboxMemoryIntegrity
=== CONT  TestSandboxMemoryIntegrity
    sandbox_memory_integrity_test.go:26: Build completed successfully
--- FAIL: TestSandboxMemoryIntegrity (82.98s)
github.com/e2b-dev/infra/tests/integration/internal/tests/orchestrator::TestSandboxMemoryIntegrity/tmpfs_hash

Flake rate in main: 66.98% (Passed 107 times, Failed 217 times)

Stack Traces | 34s run time
=== RUN   TestSandboxMemoryIntegrity/tmpfs_hash
=== PAUSE TestSandboxMemoryIntegrity/tmpfs_hash
=== CONT  TestSandboxMemoryIntegrity/tmpfs_hash
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{start:{pid:1262}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stdout:"Total memory: 985 MB\nUsed memory before tmpfs mount: 186 MB\nFree memory before tmpfs mount: 798 MB\nMemory to use in integrity test (80% of free, min 64MB): 638 MB\n"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"638+0 records in\n638+0 records out\n668991488 bytes (669 MB, 638 MiB) copied, 8.54688 s, 78.3 MB/s\n"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"\t"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"C"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"ommand being timed: \"dd if=/dev/urandom of=/mnt/testfile bs=1M count=638\"\n\tUser time (seconds): 0.00\n\tSystem time (seconds): 8.40\n\tPercent of CPU this job got: 98%\n\tElapsed (wall clock) time (h:mm:ss or m:ss): 0:08.55\n\tAverage shared text size (kbytes): 0\n\tAverage unshared data size (kbytes): 0\n\tAverage stack size (kbytes): 0\n\tAverage total size (kbytes): 0\n\tMaximum resident set size (kbytes): 2724\n\tAverage res"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"ident set size (kbytes): 0\n\tMajor (requiring I/O) page faul"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"ts: 2\n\tMinor (reclaiming a frame) page faults: 347\n\tVoluntary context swit"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"ches: 3\n\tInvoluntary context switches: 50\n\tSwaps: 0\n\tFile system inputs: 176"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"\n\tFile system outputs: 0\n\tSocket messages sent: 0"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"\n\tSocket messages received: 0\n\tSignals delivered: 0"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"\n\tPage size ("}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:"bytes): 4096\n\tExit"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stderr:" status: 0\n"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{data:{stdout:"Used memory after tmpfs mount and file fill: 831 MB\n"}}
    sandbox_memory_integrity_test.go:70: Command [bash] output: event:{end:{exited:true status:"exit status 0"}}
    sandbox_memory_integrity_test.go:70: Command [bash] completed successfully in sandbox imhz19jtsq3s0uq79b582
Executing command bash in sandbox imhz19jtsq3s0uq79b582 (user: root)
    sandbox_memory_integrity_test.go:74: Command [bash] output: event:{start:{pid:1278}}
    sandbox_memory_integrity_test.go:74: Command [bash] output: event:{data:{stdout:"9d80108aa8443c04ac2de497d76c5f82a7a8b40bf821252d93b1c7531acc3798\n"}}
    sandbox_memory_integrity_test.go:74: Command [bash] output: event:{end:{exited:true status:"exit status 0"}}
    sandbox_memory_integrity_test.go:74: Command [bash] completed successfully in sandbox imhz19jtsq3s0uq79b582
Executing command bash in sandbox imhz19jtsq3s0uq79b582 (user: root)
    sandbox_memory_integrity_test.go:99: Command [bash] output: event:{start:{pid:1281}}
    sandbox_memory_integrity_test.go:100: 
        	Error Trace:	.../tests/orchestrator/sandbox_memory_integrity_test.go:100
        	Error:      	Received unexpected error:
        	            	failed to execute command bash in sandbox imhz19jtsq3s0uq79b582: invalid_argument: protocol error: incomplete envelope: unexpected EOF
        	Test:       	TestSandboxMemoryIntegrity/tmpfs_hash
--- FAIL: TestSandboxMemoryIntegrity/tmpfs_hash (34.03s)

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request refactors the LogEntryLogger to implement the zapcore.Core interface directly, replacing the previous JSON-based io.Writer implementation. This change improves performance by avoiding JSON round-tripping and introduces a childCore to handle field accumulation without duplicating the capture buffer. Corresponding updates were made to the template creation logic and unit tests to support the new interface. I have no feedback to provide.

Comment thread packages/orchestrator/pkg/template/build/buildlogger/log_entry_logger.go Outdated
@tvi tvi force-pushed the t/ctx-todo branch 2 times, most recently from 7ea52e7 to 7567211 Compare May 10, 2026 06:17
@tvi tvi force-pushed the t/ctx-todo branch 2 times, most recently from c0b887f to bc25872 Compare May 10, 2026 06:39
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bc25872543

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit bc25872. Configure here.

LogEntryLogger previously implemented io.Writer and was plugged into a
zap pipeline via zapcore.NewCore(jsonEncoder, logEntryLogger,
DebugLevel). Each entry was serialized to JSON by the encoder and then
parsed back out by Write to recover the message, level, timestamp, and
fields. Two of those parse paths could fail and called
logger.L().Error(context.TODO(), ...) because Write has no context.

Implement zapcore.Core on LogEntryLogger instead: Check, Write(Entry,
[]Field), Sync, With, plus an embedded zapcore.LevelEnabler. The Entry
struct already carries level, time, and message, and []Field is encoded
straight into a map[string]string via zapcore.MapObjectEncoder, so the
JSON encode/parse round-trip and its failure modes disappear, and with
them the two context.TODO() call sites.

With returns a childCore that shares the parent's capture slice and mu
and only carries the accumulated fields, so logger.With(...) chains do
not fork the capture buffer and Lines() still observes every entry.

Wire-up in TemplateCreate is simplified: drop the JSON encoder and the
zapcore.NewCore wrapper and tee LogEntryLogger directly with the build
logger core. Test helper writeTestBuildLogs constructs zapcore.Entry
values instead of marshalling synthetic JSON and calling Write([]byte).
@tvi tvi merged commit 2cd20fb into main May 10, 2026
51 checks passed
@tvi tvi deleted the t/ctx-todo branch May 10, 2026 17:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants