Skip to content

motel: Custom Semantic Conventions

2026-02-21T13:58:49Z by Showboat 0.6.0

OpenTelemetry's semantic conventions define standard attribute names and types for common domains like HTTP, database, and messaging. But organisations often need attributes specific to their own services — a payment platform might track transaction types, a game studio might record match IDs. The --semconv flag lets you bring your own convention definitions without forking motel or recompiling.

Defining custom conventions

A semantic convention directory mirrors the Weaver registry layout: YAML files organised into subdirectories by domain. Each file defines one or more attribute groups.

mkdir -p /tmp/motel-semconv/payments && cat > /tmp/motel-semconv/payments/registry.yaml << 'EOF'
groups:
  - id: registry.payments
    type: attribute_group
    brief: Payment processing attributes.
    attributes:
      - id: payments.transaction_type
        type:
          members:
            - id: purchase
              value: purchase
              brief: Standard purchase.
              stability: stable
            - id: refund
              value: refund
              brief: Refund transaction.
              stability: stable
            - id: chargeback
              value: chargeback
              brief: Disputed charge.
              stability: stable
        brief: Type of payment transaction.
        examples: ["purchase", "refund"]
      - id: payments.amount_cents
        type: int
        brief: Transaction amount in cents.
        examples: [1999, 4500, 150]
      - id: payments.currency
        type: string
        brief: ISO 4217 currency code.
        examples: ["USD", "EUR", "GBP"]
      - id: payments.merchant_id
        type: string
        brief: Unique merchant identifier.
        examples: ["merch_abc123", "merch_def456"]
EOF
cat /tmp/motel-semconv/payments/registry.yaml
groups:
  - id: registry.payments
    type: attribute_group
    brief: Payment processing attributes.
    attributes:
      - id: payments.transaction_type
        type:
          members:
            - id: purchase
              value: purchase
              brief: Standard purchase.
              stability: stable
            - id: refund
              value: refund
              brief: Refund transaction.
              stability: stable
            - id: chargeback
              value: chargeback
              brief: Disputed charge.
              stability: stable
        brief: Type of payment transaction.
        examples: ["purchase", "refund"]
      - id: payments.amount_cents
        type: int
        brief: Transaction amount in cents.
        examples: [1999, 4500, 150]
      - id: payments.currency
        type: string
        brief: ISO 4217 currency code.
        examples: ["USD", "EUR", "GBP"]
      - id: payments.merchant_id
        type: string
        brief: Unique merchant identifier.
        examples: ["merch_abc123", "merch_def456"]

This defines a payments domain with four attributes: an enum for transaction type, an integer for the amount, and strings for currency and merchant ID. The directory name (payments/) becomes the domain name used in topology files.

Using custom conventions in a topology

Reference the custom domain in an operation's domain field. motel resolves the domain against the convention registry and generates attributes matching the defined types.

cat > /tmp/motel-payments.yaml << 'EOF'
version: 1
services:
  checkout:
    operations:
      process:
        duration: 50ms +/- 15ms
        error_rate: 0.5%
        domain: payments
        calls:
          - ledger.record
  ledger:
    operations:
      record:
        duration: 10ms +/- 3ms
        domain: payments
traffic:
  rate: 20/s
EOF
cat /tmp/motel-payments.yaml
version: 1
services:
  checkout:
    operations:
      process:
        duration: 50ms +/- 15ms
        error_rate: 0.5%
        domain: payments
        calls:
          - ledger.record
  ledger:
    operations:
      record:
        duration: 10ms +/- 3ms
        domain: payments
traffic:
  rate: 20/s

Validating with custom conventions

Pass --semconv to point motel at the convention directory. Without it, the payments domain would be silently unresolved — with it, motel knows what attributes to generate.

motel validate \
  --semconv /tmp/motel-semconv \
  /tmp/motel-payments.yaml
Configuration valid: 2 services, 1 root operation

To generate signals:
  motel run --stdout /tmp/motel-payments.yaml

See https://github.com/andrewh/motel/tree/main/docs/examples for more examples.

Generating traces with custom attributes

Run with --semconv and inspect the output. Each span carries attributes drawn from the custom convention definitions.

motel run \
  --stdout \
  --duration 200ms \
  --semconv /tmp/motel-semconv \
  /tmp/motel-payments.yaml 2>/dev/null |
  jq -rs '
    [.[].Attributes[] | select(.Key | startswith("payments."))]
    | group_by(.Key) | map(.[0].Key) | sort | .[]'
payments.amount_cents
payments.currency
payments.merchant_id
payments.transaction_type

All four custom attributes appear on the generated spans: the enum, integer, and string types all produce values drawn from their defined examples.

Extending the embedded registry

Custom conventions are additive — the full upstream OTel registry is still available. You can mix standard and custom domains in the same topology.

cat > /tmp/motel-mixed.yaml << 'EOF'
version: 1
services:
  gateway:
    operations:
      checkout:
        duration: 30ms +/- 10ms
        domain: http
        calls:
          - payments.charge
  payments:
    operations:
      charge:
        duration: 20ms +/- 5ms
        domain: payments
traffic:
  rate: 10/s
EOF
motel run \
  --stdout \
  --duration 200ms \
  --semconv /tmp/motel-semconv \
  /tmp/motel-mixed.yaml 2>/dev/null |
  jq -rs '
    [.[].Attributes[] | select(.Key | startswith("http.") or startswith("payments."))]
    | group_by(.Key) | map(.[0].Key) | sort | group_by(split(".")[0])
    | map("\(.[0] | split(".")[0]) domain: \(length) attributes")
    | .[]'
http domain: 10 attributes
payments domain: 4 attributes

The gateway spans carry standard HTTP attributes from the embedded registry, while the payments spans carry the custom attributes we defined. Both registries are available simultaneously.

Error handling

The --semconv flag validates that the path exists and is a directory.

motel validate \
  --semconv /nonexistent \
  /tmp/motel-payments.yaml 2>&1 |
  head -1
Error: --semconv directory "/nonexistent" does not exist