Why Warnings Disappeared in GitLab Runner's Instance Executor: Watch Your Logger Initialization Order in Go

2026-11-12来源 dev.toEnglish

A warning can be present in your code, execute under the expected condition, and still never appear in the logs.

In Go services, one possible reason is initialization order: a method attempts to write through a struct field before that field has been initialized.

That was the problem in GitLab Runner's instance executor. When users enabled use_common_build_dir = true without the required VM isolation or max_use_count = 1 configuration, the expected warning did not appear in their CI job logs.

In GitLab Runner MR !7813, addressing issue #40194, I traced the missing warning to the order in which the executor initialized its logger and fixed the logging path.

The Problem: A Warning That Never Reached the Job Trace

The affected code was in:

executors/instance/instance.go

Inside Prepare(options common.ExecutorPrepareOptions), the executor checked whether UseCommonBuildDir could be used with the current autoscaler configuration.

The relevant logic was:

if options.Config.Autoscaler.VMIsolation.Enabled || options.Config.Autoscaler.MaxUseCount == 1 { e.SharedBuildsDir = false } else { e.BuildLogger.Warningln("use_common_build_dir has no effect: requires vm isolation or max_use_count = 1") } 

The warning was intended to tell operators that use_common_build_dir had no effect unless VM isolation was enabled or max_use_count was set to 1.

The condition itself was correct. The problem was the logger being used to emit the warning.

Root Cause: Logger Initialization Happened Later

The Prepare method accessed:

e.BuildLogger.Warningln(...) 

But e.BuildLogger was assigned inside:

e.AbstractExecutor.Prepare(options) 

That initialization happened later in the method.

Consequently, when the warning branch executed, e.BuildLogger did not yet have its initialized writer. The warning was silently dropped instead of being written to the active CI job trace.

This is an initialization-order issue rather than a problem with the warning condition.

The code was attempting to use a logger owned by the executor before the executor's preparation process had initialized it.

The Fix: Use the Logger Already Available in Prepare Options

The fix was small but important.

Instead of using the executor's not-yet-initialized logger, I changed the warning to use the logger supplied through options:

} else { options.BuildLogger.Warningln("use_common_build_dir has no effect: requires vm isolation or max_use_count = 1") } 

options.BuildLogger was already available to the Prepare method, so it could write the warning directly to the active job trace.

This avoids depending on the later initialization of e.BuildLogger.

The key distinction is:

  • e.BuildLogger: an executor field initialized during AbstractExecutor.Prepare.
  • options.BuildLogger: the logger already supplied to the current preparation operation.

Using the latter makes the warning independent of the executor field's initialization order.

Testing the Configuration Combinations

A logging fix should verify more than whether a message can be written. It should also verify that the warning appears under the correct configuration conditions.

I added table-driven tests in:

executors/instance/instance_test.go

The test, TestPrepare_UseCommonBuildDir, covers five configuration permutations:

Configuration Expected behavior
UseCommonBuildDir: false Common build directory behavior is not enabled
VMIsolation.Enabled: true VM isolation satisfies the required condition
MaxUseCount: 1 Single-use VM configuration satisfies the required condition
MaxUseCount: 3 The warning condition applies when VM isolation is disabled
MaxUseCount: 0 The warning condition applies when VM isolation is disabled

The tests use buildlogger.New backed by a capture trace, allowing the test to inspect the output written to the job trace.

This verifies the behavior at the logging destination rather than merely checking that a logging method was called.

Running the Targeted Test

The focused test can be run with:

go test -v ./executors/instance -run '^TestPrepare_UseCommonBuildDir$' 

This keeps verification targeted to the executor configuration and warning behavior changed in the merge request.

Engineering Takeaways

Initialization order matters. A struct field may exist but still be unusable if its internal dependencies have not been initialized.

Use dependencies that are already available. When a method receives a logger through its options, using that logger can be safer than relying on a field initialized by a later lifecycle step.

A correct condition does not guarantee correct observability. The warning condition can execute as intended while the message never reaches the destination operators inspect.

Test the output, not just the call. Capturing the trace verifies that the warning reaches the actual logging destination.

Table-driven tests make configuration behavior easier to maintain. Testing the relevant combinations helps protect both the warning condition and the behavior of supported configurations.

Conclusion

The missing warning in GitLab Runner's instance executor was caused by a subtle ordering problem: the code used e.BuildLogger before AbstractExecutor.Prepare initialized it.

Switching to options.BuildLogger ensured that the warning was written to the active CI job trace. The accompanying table-driven tests covered five configuration permutations and verified the logging behavior.

The broader lesson is straightforward: in Go, pay attention not only to which dependency a method uses, but also to when that dependency becomes ready.

References

Author: Manish Kumar

GitLab: @manishpatel00

原文:https://www.a8q9.cn/en/article/why-warnings-disappeared-in-gitlab-runner-s-instance-executor-watch-your-logger--a248bb.html

相关文章