Search documentation of Metastatic

# Metastatic.Analysis.BusinessLogic.TelemetryInRecursiveFunction (Metastatic v0.13.2)

View Source

Detects telemetry/metrics emissions inside recursive functions.

This analyzer identifies recursive functions that emit telemetry events or metrics on each iteration, causing metric spam and performance degradation.

## Cross-Language Applicability

This is a universal observability anti-pattern across all languages:

- Python: `metrics.emit()` in recursive function
- JavaScript: `statsd.increment()` in recursive function
- Elixir: `:telemetry.execute()` in recursive function
- Go: `metrics.Inc()` in recursive function
- C#: `meter.RecordValue()` in recursive function
- Java: `meter.mark()` in recursive function
- Ruby: `StatsD.increment` in recursive function

## Problem

Emitting telemetry inside recursive functions causes:

- Metric spam: N emissions for N iterations
- Performance degradation: Telemetry overhead per recursion
- Misleading metrics: Inflated counts
- Backend overload: Too many metric events

For recursive traversal of 10,000 nodes:

- Bad: 10,000 telemetry events
- Good: 1 telemetry event with aggregate data

## Examples

### Bad (Python)

    def process_tree(node):
        metrics.increment('tree.node')  # Emitted N times!
        if node.children:
            for child in node.children:
                process_tree(child)

### Good (Python)

    def process_tree_wrapper(root):
        count = process_tree(root, 0)
        metrics.increment('tree.nodes', count)

    def process_tree(node, count):
        count += 1
        for child in node.children:
            count = process_tree(child, count)
        return count

### Bad (JavaScript)

    function fibonacci(n) {
        statsd.increment('fib.calls');  // Exponential spam!
        if (n <= 1) return n;
        return fibonacci(n-1) + fibonacci(n-2);
    }

### Good (JavaScript)

    function fibonacci(n) {
        const start = Date.now();
        const result = fib_internal(n);
        statsd.timing('fib.duration', Date.now() - start);
        return result;
    }

    function fib_internal(n) {
        if (n <= 1) return n;
        return fib_internal(n-1) + fib_internal(n-2);
    }

### Bad (Elixir)

    def traverse([head | tail]) do
      :telemetry.execute([:app, :item], %{})  # N times!
      process(head)
      traverse(tail)
    end
    def traverse([]), do: :ok

### Good (Elixir)

    def traverse(items) do
      :telemetry.span([:app, :traverse], %{count: length(items)}, fn ->
        {do_traverse(items), %{}}
      end)
    end

    defp do_traverse([head | tail]) do
      process(head)
      do_traverse(tail)
    end
    defp do_traverse([]), do: :ok

### Bad (Go)

    func process(node *Node) {
        metrics.Inc("nodes")  // Called N times
        if node.Left != nil {
            process(node.Left)
        }
        if node.Right != nil {
            process(node.Right)
        }
    }

### Good (Go)

    func processTree(root *Node) {
        count := processNode(root, 0)
        metrics.Add("nodes", float64(count))
    }

    func processNode(node *Node, count int) int {
        count++
        if node.Left != nil {
            count = processNode(node.Left, count)
        }
        if node.Right != nil {
            count = processNode(node.Right, count)
        }
        return count
    }

## Detection Strategy

1. Identify recursive functions (functions that call themselves)
2. Check if function body contains telemetry/metrics calls
3. Flag if both conditions are met

### Telemetry Function Heuristics

Function names suggesting telemetry/metrics:

- `*telemetry*`, `*metric*`, `*statsd*`
- `*emit*`, `*record*`, `*increment*`, `*gauge*`
- `*.execute*`, `*.span*`, `*.timing*`

## Solution

Wrap the recursive operation with telemetry at the top level:

- Use telemetry spans for duration
- Aggregate counts and emit once
- Move instrumentation out of recursion

## Limitations

- Requires structural recursion detection (function calling itself)
- Cannot detect mutual recursion easily
- May miss indirect telemetry calls