How to Add Monitoring to a Go Project Without Code Changes
Think about how many times you've postponed implementing full tracing in a project just because it was too tedious to pass contexts through every method? Or worse, when you needed to trace a request path inside a third-party library whose source code you don't control. Usually in such cases, you either had to rewrite half the business logic to fit the OpenTelemetry API, or accept "blind spots" in your monitoring.
The OpenTelemetry team seems to have found a way to eliminate this tedious work. The opentelemetry-go-compile-instrumentation repository offers a tool that injects telemetry directly during the application build process.
What is this magic
The project is a utility otelc. It works as a layer on top of the standard Go compiler. Instead of you manually importing OpenTelemetry libraries and placing spans, the tool does this for you at compile time. It finds the right places in your code and dependencies, and then inserts the necessary calls there.
This isn't just "automation"—it's a paradigm shift. You write clean code focused on business tasks, and observability becomes an infrastructure layer that sits on top.
What is this useful for in practice
The main highlight here is the complete absence of changes to the source code. If tomorrow you decide to switch monitoring vendors or abandon tracing altogether, you won't need to clean up hundreds of imports across the entire project.
Interesting capabilities of the tool:
- Working with third-party libraries. You can get traces from the depths of someone else's code that's connected via go.mod.
- Zero runtime overhead. Since code is injected at build time, the program doesn't need to spend resources on dynamic analysis or reflection during execution.
- Flexible integration. The tool easily plugs into CI/CD pipelines. Essentially, you just change the build command.
How it works internally
The tool modifies the build process. Instead of the familiar go build you use otelc go build. Under the hood, the utility analyzes the abstract syntax tree (AST) of your code and its dependencies. Based on pre-described rules (Instrumentation Rules), it injects the necessary code snippets.
The repository contains detailed architecture guides. If you're curious about exactly how call substitution works and how to describe rules for new libraries, take a look at the docs/ folder. Everything is documented there: from API design to semantic conventions.
Where to start
First, you need to build the tool itself. This is a standard procedure for Go projects:
git clone https://github.com/open-telemetry/opentelemetry-go-compile-instrumentation.git
cd opentelemetry-go-compile-instrumentation
make build
After that, you'll have the binary otelc. To test it in action, you can run the demo application from the repository. The entire process boils down to one command:
cd demo/app/basic
../../otelc go build
./basic
If everything went well, your application will start generating telemetry data, although you won't find a single mention of OpenTelemetry in its source code.
Who will benefit from this
The project looks like an excellent tool for those maintaining legacy systems or huge monoliths where manual tracing implementation would drag on for months. It's also a lifesaver for teams that want to keep their code "sterile" from infrastructure dependencies.
However, it's worth considering that the project requires understanding how instrumentation rules work. If you need something specific that isn't covered by the standard rules, you'll need to dig into YAML configs and possibly write your own hooks.
It's definitely worth trying the tool if you're tired of boilerplate around contexts and spans. This is a step toward what the modern developer experience should be: complex things like observability work "out of the box" and don't get in the way of writing code.
Related projects