Accelerating observability adoption by automating OpenTelemetry instrumentation across heterogeneous codebases
Years ago, I was tasked with building OpenTelemetry metric collection for a client who wanted IBM Instana's capabilities without deploying the Instana agent to their production servers. Achieving that required meticulous, time-consuming effort to instrument their Go and Python applications manually. Recently, I faced almost the exact same challenge - except this time, I had Bob as an AI collaborator. Leveraging Bob, I set out to build an industrialized, universal instrumentation solution to solve the problem at scale.
Adding telemetry to existing applications is often tedious, manual, and error-prone. To solve this, we designed the OpenTelemetry (OTel) Instrumentation Wizard - a tool built with Streamlit and Python that automatically injects production-grade OTel traces, metrics, and logs across multiple programming languages while strictly preserving existing business logic. What follows is the implementation and the components.
The wizard operates as a structured multi-stage pipeline. Source files pass from initial language detection through AST/Regex analysis, safety validation, user review, and code injection before returning fully instrumented outputs.
OTel-Wizard: injected
] markers, guaranteeing zero disruption to original runtime logic.
otel-wizard/
โโโ app.py Streamlit UI (3-step wizard)
โโโ injectors/
โ โโโ base_injector.py Abstract base + InjectionResult dataclass
โ โโโ python_injector.py AST-aware Python injector
โ โโโ go_injector.py Go import + initTracer() injector
โ โโโ java_injector.py Java try-with-resources span injector
โ โโโ javascript_injector.py Node.js SDK setup + span injector
โ โโโ typescript_injector.py ES import syntax + typed Tracer injector
โ โโโ c_injector.py C header + otel_start/end_span injector
โโโ utils/
โ โโโ language_detector.py Extension + heuristic language detection
โ โโโ diff_viewer.py Unified text diff + colour-coded HTML diff
โโโ samples/ Six hello-world files (pre-injection)
โโโ tests/ pytest suite (8 test files, 128 assertions)
โโโ requirements.txt
โโโ .env.example
โโโ run_tests.sh
The wizard targets official OpenTelemetry semantic conventions across 7 key languages, applying tailored injection strategies according to each language's SDK maturity:
| Language | Traces | Metrics | Logs | Injection Strategy |
| ----------------------- | -------- | -------- | -------- | --------------------------------------------------- |
| Python (Generic) | โ
Stable | โ
Stable | โ
Dev | AST-based (`ast` module) |
| Python - Flask | โ
Stable | โ
Stable | โ
Dev | AST + `FlaskInstrumentor` middleware |
| Python - FastAPI | โ
Stable | โ
Stable | โ
Dev | AST + `FastAPIInstrumentor` + ASGI hook |
| Python - Django | โ
Stable | โ
Stable | โ
Dev | AST + `DjangoInstrumentor` + `Psycopg2Instrumentor` |
| JavaScript / TypeScript | โ
Stable | โ
Stable | โ ๏ธ Dev | Regex + template |
| Go | โ
Stable | โ
Stable | โ ๏ธ Beta | Regex + template |
| Java | โ
Stable | โ
Stable | โ
Stable | Template overlay |
| C# / .NET | โ
Stable | โ
Stable | โ
Stable | ActivitySource template |
| Ruby | โ
Stable | โ ๏ธ Dev | โ ๏ธ Dev | Regex + template |
| PHP | โ
Stable | โ
Stable | โ
Stable | Regex + template |
> Signal status reflects the official [OpenTelemetry Language Status](https://opentelemetry.io/docs/languages/) as of 2025.
Python Supported Frameworks
Special Python based frameworks are also implemented in the wizard;
| Framework | What Gets Injected |
| ----------- | ------------------------------------------------------------ |
| **Generic** | SDK imports, `TracerProvider` init, span context managers around all functions |
| **Flask** | All of the above + `FlaskInstrumentor().instrument_app(app, excluded_urls=...)` + `RequestsInstrumentor().instrument()` |
| **FastAPI** | All of the above + `FastAPIInstrumentor.instrument_app(app, excluded_urls=..., exclude_spans=["receive"])` + `HTTPXClientInstrumentor().instrument()` |
| **Django** | All of the above + `DjangoInstrumentor().instrument(request_hook=..., is_sql_commentor_enabled=True)` + `Psycopg2Instrumentor().instrument()` - inserted **before** `get_wsgi_application()` / `get_asgi_application()` |
Framework-specific
requirements.txt
entries are also generated automatically.
Users retain full control over how telemetry is applied across their services through the wizard's configuration engine:
service.name
and service.version
resource attributes dynamically.otlp-http
, otlp-grpc
, console
, and prometheus
endpoints.The architecture behind the OpenTelemetry Instrumentation Wizard follows a clean four-phase pipeline: Detection โ Analysis โ Injection โ Reporting.
wizard/core/models.py
)
@dataclass
class WizardConfig:
service_name: str = "my-service"
service_version: str = "1.0.0"
exporter: str = "otlp-http"
otlp_endpoint: str = "http://localhost:4318"
signals: list = field(default_factory=lambda: ["traces", "metrics", "logs"])
depth: str = "medium" # shallow | medium | deep
sampling_ratio: float = 1.0
@dataclass
class InjectionPoint:
signal: str # "trace" | "metric" | "log"
kind: str # "span" | "counter" | "provider_init" ...
location: str # "function:<name>" | "file_top"
reason: str
code_preview: str
enabled: bool = True
wizard/core/detector.p
y)
def detect_language(filename: str, source: str = "") -> dict:
_, ext = os.path.splitext(filename.lower())
if ext in EXTENSION_MAP:
lang = EXTENSION_MAP[ext]
return {"language": "javascript" if lang == "typescript" else lang, "confidence": "high"}
if source:
first_line = source.splitlines()[0] if source.splitlines() else ""
if first_line.startswith("#!"):
for pattern, lang in SHEBANG_PATTERNS:
if re.search(pattern, first_line, re.IGNORECASE):
return {"language": lang, "confidence": "high"}
- AST & Pattern Analysis ( wizard/core/analyzer.py)
def generate_plan(source: str, language: str, filename: str, config: WizardConfig) -> InstrumentationPlan:
plan = InstrumentationPlan(language=language, config=config)
check = check_existing_instrumentation(source, language)
if check["already_instrumented"]:
plan.already_instrumented = True
return plan
plan.dependencies = _get_dependencies(language, config.signals)
functions = _detect_functions(source, language, config.depth)
for line_no, func_name, kind in functions:
if "traces" in config.signals:
plan.injection_points.append(InjectionPoint(
signal="trace",
kind="span",
location=f"function:{func_name}",
reason=f"Wrap '{func_name}' with a trace span for distributed tracing",
code_preview=f"span '{func_name}' (line {line_no})"
))
return plan
wizard/core/injector.py
)
def inject(source: str, plan: InstrumentationPlan, filename: str = "") -> InjectionResult:
language = plan.language
if language == "python":
from wizard.languages.python_injector import PythonInjector
injector = PythonInjector(plan)
elif language == "javascript":
from wizard.languages.js_injector import JSInjector
injector = JSInjector(plan)
return injector.inject(source)
wizard/app.py
) The main entry point ties the interactive user workflow together across four clear user steps:
detect_language()
.generate_plan()
to output an itemized data frame of target span hooks and missing dependencies.inject()
upon user confirmation.generate_report()
and packs the modified source, package metadata (requirements.txt
, package.json
, etc.), and markdown report into an in-memory ZIP archive.The core wizard module includes these language-specific injector implementations:
wizard/core/injector.py
) maps source languages to these concrete injector classes:
| Language | Injector Class | Key Strategy & Tracing Mechanism |
| ------------------------------- | -------------------- | ------------------------------------------------------------ |
| **Python** PY | `PythonInjector` PY | Uses standard `ast` parsing to inject OTel imports, provider initializers, and wrap function bodies using `with _otel_tracer.start_as_current_span(...)` context managers. PY |
| **JavaScript / TypeScript** PY | `JSInjector` PY | Prepends tracer setup headers, generates an accompanying `instrumentation.js` file, and wraps functions with `_otelTracer.startActiveSpan(...)`. PY |
| **Go** PY | `GoInjector` PY | Injects `go.opentelemetry.io/otel` imports, adds an `initOtelProvider` initialization function, and inserts `ctx, _otelSpan := _otelTracer.Start(...)` with `defer _otelSpan.End()`. PY |
| **Java** PY | `JavaInjector` PY | Appends static `OpenTelemetry` class initializers and wraps public/protected method bodies using `try (Scope _ = ...)` and `finally { _otelSpan.end(); }` blocks. PY |
| **C# / .NET** PY | `DotNetInjector` PY | Injects native `System.Diagnostics.ActivitySource` fields and wraps methods using `using var _otelActivity = _otelSource.StartActivity(...)` with `try/catch` exception recording. PY |
| **C** PY | `CInjector` PY | Scans line-by-line using regex for C function signatures, injects `otel_init()` inside `main()`, and inserts macro/pointer span tracking blocks at function entry points. PY |
| **PHP** PY | `PHPInjector` PY | Prepends a PHP OTel bootstrap block and wraps function bodies in `startSpan()`, `activate()`, and `try/catch/finally` blocks. PY |
| **Ruby** PY | `RubyInjector` PY | Prepends `require 'opentelemetry/sdk'`, configures the global SDK, and wraps method bodies inside `_OTEL_TRACER.in_span(...) do ... end` blocks. PY |
// [OTel-Wizard: injected]
(or # [OTel-Wizard: injected])
for easy auditing and tracing.requirements.txt
, package.json
, go.mod
, pom.xml
, CMakeLists.txt
, Gemfile
, or CLI package commands.In the OTel Instrumentation Wizard, injectors are specialized components responsible for parsing source code in a target programming language, inserting required OpenTelemetry SDK setup/imports, and wrapping key execution blocks (such as entry points or main functions) with span tracking logic.
Base Abstraction & Lifecycle Control ( BaseInjector): Establishes a common
inject(source_code, filename)
contract and returns an InjectionResult
dataclass containing the modified source code, language metadata, and a human-readable list of changes. It provides shared helpers to enforce non-empty source checks (_require_non_empty
), detect pre-existing OTel instrumentation (_already_has_otel
), and scan for common entry points across languages (_find_main_function
). Tracer Provider Initialisation: Prepends or injects global tracer configuration, SDK setups, or SDK bootstrap calls suitable for the target runtime environment.
Span Wrapping & Lifecycle Management: Identifies entry functions (e.g., main()
, if __name__ == "__main__":
) and injects code to start spans at entry and guarantee proper span finalisation/teardown via native language constructs (such as try-with-resources
, defer
, try/finally
, or with
blocks).
Idempotency Safeguards: Checks for pre-existing OTel markers (e.g., opentelemetry
, go.opentelemetry.io/otel
) prior to modifying files, skipping injection if the source code is already instrumented.
PythonInjector
)
Inserts OTel imports, sets up a TracerProvider
and ConsoleSpanExporter
, and wraps the body of def main():
using Python's with tracer.start_as_current_span(...)
context manager while handling indentation.
Code Excerpt (injectors/python_injector.py
):
_OTEL_IMPORTS = """\
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import (
BatchSpanProcessor,
ConsoleSpanExporter,
)
from opentelemetry.sdk.resources import Resource, SERVICE_NAME
"""
_OTEL_PROVIDER_INIT = """\
_otel_resource = Resource(attributes={SERVICE_NAME: __name__})
_otel_provider = TracerProvider(resource=_otel_resource)
_otel_provider.add_span_processor(
BatchSpanProcessor(ConsoleSpanExporter())
)
trace.set_tracer_provider(_otel_provider)
tracer = trace.get_tracer(__name__)
"""
GoInjector
)
Injects go.opentelemetry.io/otel
packages into import (...)
blocks, appends an initTracer()
helper function, and modifies func main()
to initialize the provider with defer tp.Shutdown(ctx)
and defer span completion via defer span.End()
.
Code Excerpt (injectors/go_injector.py
):
_MAIN_PREAMBLE = """\
// [OTel] Initialise tracer
ctx := context.Background()
tp, err := initTracer()
if err != nil {
log.Fatalf("failed to initialise tracer: %v", err)
}
defer func() {
if shutdownErr := tp.Shutdown(ctx); shutdownErr != nil {
log.Printf("tracer shutdown error: %v", shutdownErr)
}
}()
tracer := otel.Tracer("main")
ctx, span := tracer.Start(ctx, "main") // [OTel] root span
defer span.End()
_ = ctx // use ctx in downstream calls
"""
JavaInjector
)
Inserts io.opentelemetry.api
imports after package or existing import statements and wraps public static void main(String[] args)
with a try-with-resources
block using Scope scope = span.makeCurrent()
and explicit error reporting in a catch
block.
Code Excerpt (injectors/java_injector.py
):
otel_preamble = (
f"\n{body_indent}// [OTel] Initialise tracer\n"
f"{body_indent}OpenTelemetry openTelemetry = GlobalOpenTelemetry.get();\n"
f"{body_indent}Tracer tracer = openTelemetry.getTracer(\"main\");\n"
f"{body_indent}Span span = tracer.spanBuilder(\"main\").startSpan();\n"
f"{body_indent}try (Scope scope = span.makeCurrent()) {{\n"
)
otel_postamble = (
f"{body_indent}}} catch (Exception e) {{\n"
f"{body_indent} span.setStatus(StatusCode.ERROR, e.getMessage());\n"
f"{body_indent} throw e;\n"
f"{body_indent}}} finally {{\n"
f"{body_indent} span.end();\n"
f"{body_indent}}}\n"
)
JavaScriptInjector
/ TypeScriptInjector
)
Prepends a Node.js NodeSDK
configuration block and tracer acquisition (_otelTracer.startSpan
). TypeScript uses ES module imports and type declarations (const _otelTracer: Tracer
), wrapping function bodies with standard try/catch/finally
blocks.
Code Excerpt (injectors/typescript_injector.py
):
_OTEL_SETUP_BLOCK = """\
// [OTel] โโ OpenTelemetry setup โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
import { NodeSDK } from '@opentelemetry/sdk-node';
import { ConsoleSpanExporter } from '@opentelemetry/sdk-trace-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { Resource } from '@opentelemetry/resources';
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
import { trace, Tracer, context, SpanStatusCode } from '@opentelemetry/api';
const _otelSdk: NodeSDK = new NodeSDK({
resource: new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: 'otel-wizard-service',
}),
traceExporter: new ConsoleSpanExporter(),
instrumentations: [getNodeAutoInstrumentations()],
});
_otelSdk.start();
process.on('SIGTERM', (): void => { _otelSdk.shutdown(); });
const _otelTracer: Tracer = trace.getTracer('main');
// [OTel] โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
"""
CInjector
)
Appends C OTel headers (#include "opentelemetry_c/otel_api.h"
), inserts otel_init()
and otel_start_span("main")
at the beginning of int main()
, injects otel_end_span()
prior to any return
statement inside main()
, and appends otel_shutdown()
before closing the function.
Code Excerpt (injectors/c_injector.py
):
_OTEL_MAIN_INIT = """\
/* [OTel] Initialise tracer */
otel_init("otel-wizard-service");
otel_span_t *_otel_span = otel_start_span("main");
"""
_OTEL_MAIN_CLOSE = """\
/* [OTel] End root span and shutdown */
otel_end_span(_otel_span);
otel_shutdown();
"""
The utils
directory provides critical supporting functionality for the OTel Instrumentation Wizard, focusing on file analysis and difference visualization:
Language Detection ( utils/language_detector.py): The
LanguageDetector
class identifies programming languages through explicit extension maps and keyword content heuristics. It offers two primary mechanisms: detect()
, which strictly validates whether a file has an active injector available (raising UnsupportedLanguageError
otherwise), and detect_any()
, a non-raising alternative designed for fluid UI workflows that gracefully flags recognized but non-instrumentable languages. Diff Generation & Visualisation ( utils/diff_viewer.py): Leverages Python's native
difflib
module to compute line-by-line differences between original and instrumented source code. It provides generate_unified_diff()
to output standard text diffs (diff -u
) and generate_html_diff()
to build sanitised, color-coded HTML views with styled line highlights (green for additions, red for deletions, and blue for hunk headers). To validate the code injection process across all supported target environments, the project includes clean, minimal "Hello World" benchmark samples free of any initial OpenTelemetry instrumentation:
Language Coverage: Standardized samples are provided for C (helloworld.c
), Go (helloworld.go
), Java (HelloWorld.java
), JavaScript (helloworld.js
), Python (helloworld.py
), and TypeScript (helloworld.ts
).
Canonical Code Structure: Each file implements a simple main entry point and helper greeting function (such as greet()
/ main()
). This standardized pattern allows unit and integration tests to reliably verify header/import additions, tracer setup placements, and entry-point span wrapping across every supported language.
// tests/samples/go/sample_app.go
// A plain Go HTTP server WITHOUT OTel instrumentation.
// Used to validate the wizard's Go injection.
package main
import (
"encoding/json"
"fmt"
"log"
"math/rand"
"net/http"
"time"
)
type Item struct {
Name string `json:"name"`
Price float64 `json:"price"`
Quantity int `json:"quantity"`
}
type OrderRequest struct {
UserID int `json:"user_id"`
Items []Item `json:"items"`
}
type OrderResponse struct {
User string `json:"user"`
Total float64 `json:"total"`
Status string `json:"status"`
}
func calculateTotal(items []Item) float64 {
total := 0.0
for _, item := range items {
qty := item.Quantity
if qty == 0 {
qty = 1
}
total += item.Price * float64(qty)
}
return total
}
func fetchUser(userID int) (map[string]interface{}, error) {
time.Sleep(time.Duration(rand.Intn(50)) * time.Millisecond)
if userID <= 0 {
return nil, fmt.Errorf("invalid userID: %d", userID)
}
return map[string]interface{}{
"id": userID,
"name": fmt.Sprintf("User_%d", userID),
}, nil
}
func processOrder(userID int, items []Item) (*OrderResponse, error) {
user, err := fetchUser(userID)
if err != nil {
return nil, err
}
total := calculateTotal(items)
log.Printf("Order processed for %v: $%.2f", user["name"], total)
return &OrderResponse{
User: user["name"].(string),
Total: total,
Status: "confirmed",
}, nil
}
func orderHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
var req OrderRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
resp, err := processOrder(req.UserID, req.Items)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}
func healthHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
fmt.Fprintf(w, `{"status":"ok"}`)
}
func main() {
http.HandleFunc("/order", orderHandler)
http.HandleFunc("/health", healthHandler)
log.Println("Server listening on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
Step 4: Download the modified Code
// tests/samples/go/sample_app.go
// A plain Go HTTP server WITHOUT OTel instrumentation.
// Used to validate the wizard's Go injection.
package main
import (
"context" // [OTel-Wizard: injected]
"os" // [OTel-Wizard: injected]
"go.opentelemetry.io/otel" // [OTel-Wizard: injected]
"go.opentelemetry.io/otel/attribute" // [OTel-Wizard: injected]
"go.opentelemetry.io/otel/codes" // [OTel-Wizard: injected]
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" // [OTel-Wizard: injected]
"go.opentelemetry.io/otel/sdk/resource" // [OTel-Wizard: injected]
sdktrace "go.opentelemetry.io/otel/sdk/trace" // [OTel-Wizard: injected]
semconv "go.opentelemetry.io/otel/semconv/v1.26.0" // [OTel-Wizard: injected]
"go.opentelemetry.io/otel/trace" // [OTel-Wizard: injected]
// initOtelProvider initialises the TracerProvider. // [OTel-Wizard: injected]
func initOtelProvider(ctx context.Context) (func(context.Context) error, error) {
exporter, err := otlptracehttp.New(ctx,
otlptracehttp.WithEndpoint(getEnv("OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:4318")),
otlptracehttp.WithInsecure(),
)
if err != nil {
return nil, err
}
res, err := resource.Merge(
resource.Default(),
resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceName(getEnv("OTEL_SERVICE_NAME", "my-service")),
semconv.ServiceVersion(getEnv("OTEL_SERVICE_VERSION", "1.0.0")),
),
)
if err != nil {
return nil, err
}
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(res),
)
otel.SetTracerProvider(tp)
return tp.Shutdown, nil
}
// getEnv returns env var value or fallback. // [OTel-Wizard: injected]
func getEnv(key, fallback string) string {
if v := os.Getenv(key); v != "" {
return v
}
return fallback
}
var _otelTracer = otel.Tracer("my-service") // [OTel-Wizard: injected]
"encoding/json"
"fmt"
"log"
"math/rand"
"net/http"
"time"
)
type Item struct {
Name string `json:"name"`
Price float64 `json:"price"`
Quantity int `json:"quantity"`
}
type OrderRequest struct {
UserID int `json:"user_id"`
Items []Item `json:"items"`
}
type OrderResponse struct {
User string `json:"user"`
Total float64 `json:"total"`
Status string `json:"status"`
}
func calculateTotal(items []Item) float64 {
// [OTel-Wizard: injected]
ctx, _otelSpan := _otelTracer.Start(ctx, "calculateTotal",
trace.WithAttributes(attribute.String("code.function", "calculateTotal")))
defer func() {
if r := recover(); r != nil {
_otelSpan.SetStatus(codes.Error, "panic")
_otelSpan.End()
panic(r)
}
_otelSpan.End()
}() // [OTel-Wizard: injected]
_ = ctx // [OTel-Wizard: injected]
total := 0.0
for _, item := range items {
qty := item.Quantity
if qty == 0 {
qty = 1
}
total += item.Price * float64(qty)
}
return total
}
func fetchUser(userID int) (map[string]interface{}, error) {
// [OTel-Wizard: injected]
ctx, _otelSpan := _otelTracer.Start(ctx, "fetchUser",
trace.WithAttributes(attribute.String("code.function", "fetchUser")))
defer func() {
if r := recover(); r != nil {
_otelSpan.SetStatus(codes.Error, "panic")
_otelSpan.End()
panic(r)
}
_otelSpan.End()
}() // [OTel-Wizard: injected]
_ = ctx // [OTel-Wizard: injected]
time.Sleep(time.Duration(rand.Intn(50)) * time.Millisecond)
if userID <= 0 {
return nil, fmt.Errorf("invalid userID: %d", userID)
}
return map[string]interface{}{
"id": userID,
"name": fmt.Sprintf("User_%d", userID),
}, nil
}
func processOrder(userID int, items []Item) (*OrderResponse, error) {
// [OTel-Wizard: injected]
ctx, _otelSpan := _otelTracer.Start(ctx, "processOrder",
trace.WithAttributes(attribute.String("code.function", "processOrder")))
defer func() {
if r := recover(); r != nil {
_otelSpan.SetStatus(codes.Error, "panic")
_otelSpan.End()
panic(r)
}
_otelSpan.End()
}() // [OTel-Wizard: injected]
_ = ctx // [OTel-Wizard: injected]
user, err := fetchUser(userID)
if err != nil {
return nil, err
}
total := calculateTotal(items)
log.Printf("Order processed for %v: $%.2f", user["name"], total)
return &OrderResponse{
User: user["name"].(string),
Total: total,
Status: "confirmed",
}, nil
}
func orderHandler(w http.ResponseWriter, r *http.Request) {
// [OTel-Wizard: injected]
ctx, _otelSpan := _otelTracer.Start(ctx, "orderHandler",
trace.WithAttributes(attribute.String("code.function", "orderHandler")))
defer func() {
if r := recover(); r != nil {
_otelSpan.SetStatus(codes.Error, "panic")
_otelSpan.End()
panic(r)
}
_otelSpan.End()
}() // [OTel-Wizard: injected]
_ = ctx // [OTel-Wizard: injected]
if r.Method != http.MethodPost {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
var req OrderRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
resp, err := processOrder(req.UserID, req.Items)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}
func healthHandler(w http.ResponseWriter, r *http.Request) {
// [OTel-Wizard: injected]
ctx, _otelSpan := _otelTracer.Start(ctx, "healthHandler",
trace.WithAttributes(attribute.String("code.function", "healthHandler")))
defer func() {
if r := recover(); r != nil {
_otelSpan.SetStatus(codes.Error, "panic")
_otelSpan.End()
panic(r)
}
_otelSpan.End()
}() // [OTel-Wizard: injected]
_ = ctx // [OTel-Wizard: injected]
w.Header().Set("Content-Type", "application/json")
fmt.Fprintf(w, `{"status":"ok"}`)
}
func main() {
http.HandleFunc("/order", orderHandler)
http.HandleFunc("/health", healthHandler)
log.Println("Server listening on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
โฆ and the codeโs dependencies;
go.opentelemetry.io/otel v1.33.0
go.opentelemetry.io/otel/sdk v1.33.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0
go.opentelemetry.io/otel/sdk/metric v1.33.0
The OTel Instrumentation Wizard provides an automated, end-to-end solution for introducing OpenTelemetry tracing into multi-language source code without requiring manual boilerplate construction. By leveraging a streamlined three-step workflow (Upload โ Preview Diff โ Download), the application abstracts the complexity of observability integration:
Intelligent Language Detection & Flexibility: Through the LanguageDetector
module, the wizard automatically identifies file types via explicit extension matching and keyword-based content heuristics. It provides broad recognition across many ecosystem languages while enforcing clear guardrails for supported injection targets.
Robust Code Injection Engine: Built upon a extensible BaseInjector
contract, concrete injectors for Python, Go, Java, JavaScript, TypeScript, and C automatically handle import injections, tracer provider setup, and entry-point span wrapping. Native language constructs (such as try-with-resources
, defer
, with
blocks, or explicit return-wrapping) guarantee safe span lifecycle management and error propagation across diverse runtime models.
Safety, Transparency, & Idempotency: Built-in safeguards check for pre-existing OTel markers to prevent duplicate instrumentation. The interactive UI offers full transparency via unified and color-coded HTML diff viewers (utils/diff_viewer.py
), ensuring developers can inspect and accept all injected modifications with total confidence.
Empirically Validated Architecture: Accompanied by standardized, clean test samples across all target languages, the architecture ensures reproducible and dependable AST/text transformations across real-world codebases.
Ultimately, the application bridges the gap between raw source code and modern observability practices, lowering the barrier to entry for distributed tracing across heterogeneous developer stacks.
Thanks for reading ๐ฎ