# Code Generation
weaver registry generate
|Quick Links | [weaver.yaml](weaver-config.md) | [Semconv Schema](../schemas/semconv-syntax.md) | [JQ Filters](../crates/weaver_forge/README.md#jq-filters-reference) | [Jinja Filters](../crates/weaver_forge/README.md#jinja-filters-reference) |
|-|-|-|-|-|
A core element of schema-first telemetry are the artifacts it enables:
- **Up-to-Date Documentation**: Created right from the schema, the source of truth. When a metric changes, the docs do so, too.
- **Type-safe Constructors**: `New*` functions for **_your_** telemetry that **always** create schema-conforming results.
- etc.
>[!Note]
>Weaver enables this using code-generation, powered by [minijinja](https://github.com/mitsuhiko/minijinja) (Jinja2-compatible templating) and [jaq](https://github.com/01mf02/jaq) (jq-compatible data processing).
>Full documentation is available at [Weaver Forge](../crates/weaver_forge/README.md)
## Learning Jinja Templates
If you're new to Jinja or need a refresher, these resources will help you get started:
- **[Official Jinja Template Documentation](https://jinja.palletsprojects.com/en/stable/templates)** - Comprehensive reference for Jinja template syntax
- **[Jinja2 Tutorial - Loops and Conditionals](https://ttl255.com/jinja2-tutorial-part-2-loops-and-conditionals/)** - Practical tutorial with examples
- **[MiniJinja Documentation](https://docs.rs/minijinja/latest/minijinja/)** - Rust implementation used by Weaver (mostly compatible with Jinja2)
Weaver extends Jinja with custom filters and functions specifically designed for semantic convention generation. See the [Jinja Filters Reference](../crates/weaver_forge/README.md#jinja-filters-reference) for details.
## High-level overview
>[!Tip]
>For a full tutorial, consider the [Step-by-Step Guide](../crates/weaver_forge/README.md#step-by-step-guide)
Let's say we want to generate Markdown documentation for the metrics of our application or convention.
With Weaver Forge, the following directory layout is used by default:
| Path | Description |
|-|-|
| | |
| `registry/http/` | Some [registry](registry.md) that defines telemetry. In this case for HTTP |
| `registry/http/metrics.yaml` | Schema file for some HTTP metrics |
| `registry/grpc/` | Another registry that defines telemetry for a different use-case. |
| `registry/grpc/metrics.yaml` | Schema file for some gRPC metrics |
| | |
| `templates/registry/md` | |
| `templates/registry/md/weaver.yaml` | Weaver (Forge) [configuration](weaver-config.md): Specifies templates to use, their input values, output path and various other transformations. |
| `templates/registry/md/metrics.md.j2` | Jinja2-style template that operates on the resolved schema and evaluates into the desired `metrics.md` file |
The most important files are `weaver.yaml` and the `*.j2` templates.
```yaml
# weaver.yaml
templates:
- template: "metrics.md.j2"
filter: semconv_grouped_metrics
application_mode: each
file_name: "metrics/{{ctx.root_namespace | snake_case}}.md"
```
The markdown template could look like this:
````jinja2
{#
Copyright The OpenTelemetry Authors
SPDX-License-Identifier: Apache-2.0
This file is:
- a Jinja template,
- used to generate semantic conventions,
- using weaver.
For doc on the template syntax:
https://jinja.palletsprojects.com/en/3.0.x/
For doc on the semantic conventions:
https://github.com/open-telemetry/semantic-conventions
For doc on weaver:
https://github.com/open-telemetry/weaver
#}
## {{ctx.root_namespace}}
{% for m in ctx.metrics %}
### {{m.metric_name}}
```promql
# TYPE {{m.metric_name}} {{m.instrument | upper}}
{{m.metric_name + "{"}}{{m.attributes | map(attribute='name') | join(", ") + "}"}}
```
{{m.brief}}
{% if m.attributes | length > 0%}
|Attribute|Type|Description|
|-|-|-|
{% for attr in m.attributes -%}
| {{attr.name}} | {{ attr.type }}| {{attr.brief}} |
{% endfor %}
{% endif %}
{% endfor %}
````
When calling `weaver registry generate md`, the following happens:
```mermaid
flowchart LR;
subgraph "Registry"
in1["http/metrics.yaml"]
in2["grpc/metrics.yaml"]
end
file1["metrics/http.md"]
file2["metrics/grpc.md"]
subgraph "Weaver"
res["Resolved Schema"]
subgraph "Weaver Forge"
jq["JQ Filter
filter:"]
jinja["Jinja2 template
template:"]
end
end
in1 --> res
in2 --> res
res --> jq --> jinja
jinja --> file1
jinja --> file2
```
Weaver resolves the entire registry (http and grpc in this case) into a single document. This is passed to the [JQ filter](../crates/weaver_forge/README.md#jq-filters) `semconv_grouped_metrics`. This groups individual metrics by their root namespace (`http` or `grpc`)
This output in turn is passed to the `metrics.md.j2` template, evaluated by the [minijinja](https://github.com/mitsuhiko/minijinja) templating engine. Because `application_mode` is set to `each`, the template is invoked for each group, so this yields a `http.md` and a separate `grpc.md`.
## Understanding Filters
The `filter` field in `weaver.yaml` is a powerful feature that preprocesses your schema data before it reaches your templates. Think of filters as data transformations that shape how your templates receive information.
### Default Behavior (No Filter)
When you **don't specify a filter**, the entire resolved registry is passed directly to your template in the `ctx` variable:
```yaml
templates:
- template: "all_data.j2"
application_mode: single
# No filter specified - ctx contains the entire registry
```
Your template receives everything:
```jinja
{# ctx.groups contains all semantic convention groups #}
{% for group in ctx.groups %}
{{ group.id }}
{% endfor %}
```
### With a Filter
When you **do specify a filter**, it transforms the data before passing it to the template. Filters use JQ expressions to select and transform data:
```yaml
templates:
- template: "metrics.md.j2"
filter: '.groups | map(select(.type == "metric"))' # Select only metric groups
application_mode: each
```
Now `ctx` contains the output of the filter - each metric group object:
```jinja
{# ctx is a single metric group #}
## {{ ctx.id }}
{{ ctx.brief }}
{% for attr in ctx.attributes %}
- {{ attr.name }}
{% endfor %}
```
### Common Helper Filters
For convenience, Weaver provides pre-built helper filters for common operations:
- **`semconv_grouped_attributes`** - Groups attributes by root namespace (e.g., `http`, `db`)
- **`semconv_grouped_metrics`** - Groups metrics by root namespace
- **`semconv_grouped_events`** - Groups events by root namespace
### Filter Options
Many filters accept options to customize their behavior:
```yaml
templates:
- template: "stable_attrs.j2"
filter: >
semconv_grouped_attributes({
"stable_only": true,
"exclude_deprecated": true,
"exclude_root_namespace": ["url", "network"]
})
application_mode: each
```
**Options explained:**
- `stable_only: true` - Only include stable attributes
- `exclude_deprecated: true` - Skip deprecated attributes
- `exclude_root_namespace` - Exclude specific namespaces
### The `ctx` Variable
The `ctx` variable is the primary way your template accesses data:
- **Without a filter**: `ctx` = the entire resolved registry
- **With `application_mode: single`**: `ctx` = the filter's complete output
- **With `application_mode: each`**: `ctx` = one element from the filter's output array
For more details on available filters, see the [JQ Filters Reference](../crates/weaver_forge/README.md#jq-filters-reference).
## Tips and Tricks
#### Use `debug()`
Jinja2 can be overwhelming and hard to discover. Try putting `debug()` somewhere in your code:
```
/*
{{ debug() }}
*/
```
This gives you a JSON-like dump of every exact variable and corresponding value, identifier, function, filter, test, etc.
## Built-in Helper Functions and Filters
Weaver provides numerous built-in filters and functions to simplify template development. Here are the most commonly used ones:
### Case Conversion Filters
Transform strings between different naming conventions:
- `kebab_case` - Converts to kebab-case: `{{ "Hello World" | kebab_case }}` → `hello-world`
- `snake_case` - Converts to snake_case: `{{ "Hello World" | snake_case }}` → `hello_world`
- `pascal_case` - Converts to PascalCase: `{{ "hello_world" | pascal_case }}` → `HelloWorld`
- `camel_case` - Converts to camelCase: `{{ "hello_world" | camel_case }}` → `helloWorld`
- `screaming_snake_case` - Converts to SCREAMING_SNAKE_CASE
- `screaming_kebab_case` - Converts to SCREAMING-KEBAB-CASE
- `upper_case`, `lower_case`, `title_case` - Standard case conversions
- `capitalize_first` - Capitalizes only the first character
**Example:**
```jinja
file_name: "{{ctx.root_namespace | snake_case}}.md"
```
### Other Useful Filters
- `comment` - Format text as comments (language-specific, configured in weaver.yaml)
- `attribute_sort` - Sort attributes by requirement level, then name
- `required` / `not_required` - Filter attributes by requirement level
- `flatten` - Convert nested lists into a single list
- `map_text` - Map values using text_maps in weaver.yaml
For a complete list of all filters, functions, and tests, see:
- **[Jinja Filters Reference](../crates/weaver_forge/README.md#jinja-filters-reference)** - Complete filter documentation
- **[Jinja Functions Reference](../crates/weaver_forge/README.md#jinja-functions-reference)** - Available functions
- **[Source Code](../../crates/weaver_forge/src/extensions)** - Implementation details