README.md

# TeleDec

Zero-dependency Elixir library for declarative telemetry instrumentation using compile-time decorators.

TeleDec provides a clean, efficient way to add telemetry events to your functions with minimal boilerplate. Simply add `@telemetry` attributes to your functions, and the library handles all the instrumentation at compile-time with negligible runtime overhead.

## Features

- **Zero Dependencies** - Only requires the standard `:telemetry` library
- **Compile-Time Code Generation** - All instrumentation happens at compile-time with no runtime reflection
- **Auto-Inferred Event Names** - Automatically generate event names from module and function names
- **Multiple Modes** - Choose between full span tracking or fast one-shot events
- **Performance Optimized** - Minimal overhead (~700ns for span mode, ~300ns for one-shot mode)
- **Flexible Configuration** - Global and per-function configuration options
- **Selective Metadata** - Capture only the arguments and variables you need

## Installation

Add `tele_dec` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:tele_dec, "~> 0.1.0"}
  ]
end
```

## Quick Start

### Basic Usage with Auto-Inferred Names

```elixir
# config/config.exs
config :tele_dec, app: :my_app

# lib/my_app/user_service.ex
defmodule MyApp.UserService do
  use TeleDec

  @telemetry_service :user_service

  @telemetry true
  def create_user(attrs) do
    # Your implementation
    # Emits: [:my_app, :user_service, :create_user, :start/:stop/:exception]
  end

  @telemetry include: [:user_count]
  def list_users() do
    users = fetch_users()
    user_count = length(users)  # This will be included in telemetry metadata
    users
  end
end
```

### Explicit Event Names

```elixir
defmodule MyApp.Service do
  use TeleDec

  @telemetry {[:my_app, :service, :process], []}
  def process(data) do
    # Implementation
    # Emits: [:my_app, :service, :process, :start/:stop/:exception]
  end
end
```

## Configuration

### Global Configuration

Set in `config/config.exs`:

```elixir
config :tele_dec,
  app: :my_app,           # Application name prefix for auto-inferred events
  enabled: true            # Global enable/disable flag (default: true)
```

### Module Configuration

```elixir
defmodule MyApp.Service do
  use TeleDec

  # Set service name for auto-inferred event names
  @telemetry_service :service_name

  # Functions...
end
```

### Function Options

```elixir
# Auto-infer event name
@telemetry true

# Auto-infer with options
@telemetry include: [:computed_value]

# Explicit event name
@telemetry {[:custom, :event], [include: [:var1]]}

# Available options:
# - mode: :span (default) or :one_shot
# - include: list of variables to include in stop event metadata
# - args: list of specific arguments to capture (default: all)
# - metadata: set to false to skip all metadata capture
# - enabled: set to false to disable at compile-time
```

## Performance Modes

### Span Mode (default)

Full telemetry span with start, stop, and exception events:

```elixir
@telemetry true
def process(data) do
  # ~700ns overhead
end
```

### One-Shot Mode

Single event on completion, approximately 2x faster:

```elixir
@telemetry mode: :one_shot
def fast_operation(x, y) do
  # ~300ns overhead
  # No start event, no exception tracking
end
```

### Metadata-Free Mode

Maximum performance, no metadata capture:

```elixir
@telemetry metadata: false
def high_frequency_operation() do
  # Minimal overhead
end
```

## Performance Characteristics

TeleDec is designed for minimal overhead:

- **Baseline** function call: ~27ns
- **Disabled** telemetry (`enabled: false`): ~32ns (5ns overhead)
- **One-shot mode**: ~300ns
- **Span mode** (full instrumentation): ~700ns

All metadata capture happens at compile-time via direct map construction, avoiding runtime reflection and `Kernel.binding()` calls.

## Event Structure

### Span Mode Events

- `event_name ++ [:start]` - Emitted when function starts
  - Measurements: `%{monotonic_time: integer}`
  - Metadata: Function arguments

- `event_name ++ [:stop]` - Emitted when function completes successfully
  - Measurements: `%{duration: integer}`
  - Metadata: Arguments + result + included variables

- `event_name ++ [:exception]` - Emitted when function raises
  - Measurements: `%{duration: integer}`
  - Metadata: Arguments + error info (kind, reason, stacktrace)

### One-Shot Mode Events

- `event_name` - Single event emitted on completion
  - Measurements: `%{duration: integer}`
  - Metadata: Arguments + result + included variables
  - No exception tracking

## Attaching Handlers

Use standard `:telemetry` functions to attach handlers:

```elixir
:telemetry.attach_many(
  "my-handler",
  [
    [:my_app, :user_service, :create_user, :start],
    [:my_app, :user_service, :create_user, :stop],
    [:my_app, :user_service, :create_user, :exception]
  ],
  fn event, measurements, metadata, config ->
    # Handle event
  end,
  %{some: :config}
)
```

## Documentation

Full documentation is available on [HexDocs](https://hexdocs.pm/tele_dec).

## License

MIT License - see [LICENSE](LICENSE) for details.

## Contributing

Issues and pull requests are welcome on [GitHub](https://github.com/viragecloud/tele_dec).