⚠ This page is served via a proxy. Original site: https://github.com
This service does not collect credentials or authentication data.
Skip to content

Proposal: Replace YAML rule definitions with Go DSL for hook instrumentation #258

@pdelewski

Description

@pdelewski

This proposal suggests replacing the current YAML-based instrumentation rule definitions with a Go-based internal DSL. This would provide greater flexibility, type safety, and enable advanced use cases that are difficult or impossible to express in YAML.

Motivation

The current YAML approach works well for simple declarative scenarios:

  server_hook:
    target: net/http
    func: ServeHTTP
    recv: serverHandler
    before: BeforeServeHTTP
    after: AfterServeHTTP
    path: "github.com/.../nethttp/server"

However, YAML becomes limiting when more sophisticated rewriting logic is required:

  • No programmatic control: Cannot express conditional logic or dynamic transformations
  • Limited AST manipulation: The raw field allows code injection but cannot modify function signatures, rename parameters, or perform context-sensitive transformations
  • No type safety: YAML is stringly-typed, errors are only caught at runtime
  • Duplication: Similar rules require copy-paste with minor variations

Proposed Solution

Define instrumentation rules as plain Go code using an internal DSL:

  package nethttp_hooks

  import "github.com/open-telemetry/opentelemetry-go-compile-instrumentation/hooks"

  func ProvideHooks() []*hooks.Hook {
      return []*hooks.Hook{
          {
              Target: hooks.InjectTarget{
                  Package:  "net/http",
                  Function: "ServeHTTP",
                  Receiver: "serverHandler",
              },
              Hooks: &hooks.InjectFunctions{
                  Before: "BeforeServeHTTP",
                  After:  "AfterServeHTTP",
                  From:   "github.com/.../nethttp/server",
              },
          },
      }
  }

For advanced cases like runtime instrumentation, the Go DSL enables full AST manipulation:

  func (r *RuntimeHookProvider) ProvideHooks() []*hooks.Hook {
      return []*hooks.Hook{
          {
              Target: hooks.InjectTarget{
                  Package:  "runtime",
                  Function: "newproc1",
              },
              Rewrite: func(node ast.Node) (ast.Node, error) {
                  funcDecl := node.(*ast.FuncDecl)
                  // Rename unnamed return values
                  renameReturnValues(funcDecl)
                  // Inject context propagation
                  stmts, _ := parseSnippet(`defer func(){...}()`)
                  funcDecl.Body.List = append(stmts, funcDecl.Body.List...)
                  return funcDecl, nil
              },
          },
      }
  }

  func (r *RuntimeHookProvider) GetStructModifications() []hooks.StructModification {
      return []hooks.StructModification{
          {
              Package:    "runtime",
              StructName: "g",
              AddFields: []hooks.StructField{
                  {Name: "otel_trace_context", Type: "interface{}"},
                  {Name: "otel_baggage_container", Type: "interface{}"},
              },
          },
      }
  }

Benefits

Aspect YAML Go DSL
Type safety Runtime errors Compile-time checks
Expressiveness Limited to predefined fields Full language power
AST manipulation Only raw code injection Complete control via go/ast
Conditional logic Not supported Native Go conditionals
Code reuse Copy-paste Functions, interfaces, composition
IDE support Basic YAML highlighting Full Go tooling (autocomplete, refactoring)
Testing Difficult Standard Go tests

Backwards Compatibility

YAML support could be maintained as a convenience layer that generates Go DSL internally. Simple rules would continue to work in YAML, while advanced use cases would use Go directly.

Reference Implementation

A working implementation of this approach exists in https://github.com/pdelewski/go-build-interceptor, demonstrating:

  • Before/After hooks
  • Function rewriting with full AST access
  • Struct modification
  • File generation

Next Steps

  1. Define the hooks package API
  2. Implement hook provider parsing/loading
  3. Add YAML-to-Go DSL converter for migration
  4. Update existing instrumentations to use Go DSL

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions