Skip to main content
Gather relevant insights about your application and modules with custom metrics and telemetry.

Overview

The telemetry package provides observability tooling for Cosmos SDK applications using OpenTelemetry. It offers a unified initialization point for traces, metrics, and logs via the OpenTelemetry declarative configuration API. This package:
  • Initializes OpenTelemetry SDK using YAML configuration files
  • Provides backward compatibility with Cosmos SDK’s legacy go-metrics wrapper API
  • Includes built-in instrumentation for host, runtime, and disk I/O metrics

Quick Start

1. Start a Local Telemetry Backend

docker run -p 3000:3000 -p 4317:4317 -p 4318:4318 --rm -ti grafana/otel-lgtm

2. Create Configuration File

Create an otel.yaml file:
file_format: "1.0-rc.3"
resource:
  attributes:
    - name: service.name
      value: my-cosmos-app

tracer_provider:
  processors:
    - batch:
        exporter:
          otlp_grpc:
            endpoint: http://localhost:4317

meter_provider:
  readers:
    - pull:
        exporter:
          prometheus/development:
            host: 0.0.0.0
            port: 9464

logger_provider:
  processors:
    - batch:
        exporter:
          otlp_grpc:
            endpoint: http://localhost:4317

extensions:
  instruments:
    host: {}
    runtime: {}
    diskio: {}
  propagators:
    - tracecontext

3. Initialize Telemetry

Option A: Environment Variable (Recommended) Set OTEL_EXPERIMENTAL_CONFIG_FILE to your config path. This initializes the SDK before any meters/tracers are created, avoiding atomic load overhead.
export OTEL_EXPERIMENTAL_CONFIG_FILE=/path/to/otel.yaml
Option B: Node Config Directory An empty otel.yaml will now be generated in ~/.<node_home>/config/. Place the desired configuration in otel.yaml. Option C: Programmatic Initialization The SDK will first attempt to initialize via env var, then using the config in the node’s home directory. You may optionally initialize telemetry yourself using the telemetry.InitializeOpenTelemetry function:
err := telemetry.InitializeOpenTelemetry("/path/to/otel.yaml")
if err != nil {
    log.Fatal(err)
}
defer telemetry.Shutdown(context.Background())

Configuration

OpenTelemetry Configuration

The package uses the OpenTelemetry declarative configuration spec. Key sections:
SectionPurpose
resourceService identity and attributes
tracer_providerTrace export configuration
meter_providerMetrics export configuration
logger_providerLog export configuration
For examples containing available options, see the OpenTelemetry configuration examples.

Extensions

The extensions section of the otel.yaml configuration file provides additional features not yet supported by the standard otelconf:
extensions:
  # Optional file-based exporters
  trace_file: "/path/to/traces.json"
  metrics_file: "/path/to/metrics.json"
  metrics_file_interval: "10s"
  logs_file: "/path/to/logs.json"

  # Custom instrumentation additions
  instruments:
    host: {}
    runtime: {}
    diskio:
      disable_virtual_device_filter: true # removes the automatic filtering of virtual disks. Operating systems such as Linux typically add virtual disks, which can add duplication to disk io data. These disks usually take the form of loopback, RAID, partitions, etc.

  # Trace context propagation
  propagators:
    - tracecontext
    - baggage
    - b3
    - jaeger

Custom Instruments

Host Instrumentation (host)

Reports host-level metrics using go.opentelemetry.io/contrib/instrumentation/host:
  • CPU usage
  • Memory usage
  • Network I/O
extensions:
  instruments:
    host: {}

Runtime Instrumentation (runtime)

Reports Go runtime metrics using go.opentelemetry.io/contrib/instrumentation/runtime:
  • Goroutine count
  • GC statistics
  • Memory allocations
extensions:
  instruments:
    runtime: {}

Disk I/O Instrumentation (diskio)

Reports disk I/O metrics using gopsutil:
MetricDescription
system.disk.ioBytes read/written
system.disk.operationsRead/write operation counts
system.disk.io_timeTime spent on I/O operations
system.disk.operation_timeTime per read/write operation
system.disk.mergedMerged read/write operations
extensions:
  instruments:
    diskio: {}
    # Or with options:
    diskio:
      disable_virtual_device_filter: true  # Include loopback, RAID, partitions on Linux
By default, virtual devices (loopback, RAID, partitions) are filtered out on Linux to avoid double-counting I/O.

Propagators

Configure trace context propagation for distributed tracing:
PropagatorDescription
tracecontextW3C Trace Context (default)
baggageW3C Baggage
b3Zipkin B3 single header
b3multiZipkin B3 multi-header
jaegerJaeger propagation

Developer Usage

Using Meters and Tracers

After initialization, use standard OpenTelemetry APIs:
import (
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/metric"
)

var (
    tracer = otel.Tracer("my-package")
    meter  = otel.Meter("my-package")

    myCounter metric.Int64Counter
)

func init() {
   var err error
   myCounter, err = meter.Int64Counter("my.counter")
   if err != nil {
	   panic(err)
   }
}

func MyFunction(ctx context.Context) error {
    ctx, span := tracer.Start(ctx, "MyFunction")
    defer span.End()

    myCounter.Add(ctx, 1)

    // ... your code
    return nil
}

Shutdown

Always call Shutdown() when the application exits:
func (a *App) Close() {
    telemetry.Shutdown(ctx)
}

Legacy API (Deprecated)

The package provides backward-compatible wrappers for github.com/hashicorp/go-metrics. These are deprecated and users should migrate to OpenTelemetry APIs directly.

OpenTelemetry Bridge

Cosmos SDK v0.54.0+ provides a bridge to send existing go-metrics to the meter provider defined in your OpenTelemetry config. To bridge your metrics, set the metrics-sink in app.toml to “otel”.

###############################################################################
###                         Telemetry Configuration                         ###
###############################################################################
[telemetry]

# other fields...

metrics-sink = "otel"

Legacy Configuration

cfg := telemetry.Config{
    ServiceName:            "my-service",
    Enabled:                true,
    EnableHostname:         true,
    EnableHostnameLabel:    true,
    EnableServiceLabel:     true,
    PrometheusRetentionTime: 60,  // seconds
    GlobalLabels:           [][]string{{"chain_id", "cosmoshub-1"}},
    MetricsSink:            "otel",  // "mem", "statsd", "dogstatsd", "otel"
    StatsdAddr:             "localhost:8125",
}

m, err := telemetry.New(cfg)

Legacy Metrics Functions

All are deprecated; prefer OpenTelemetry:
// Counters
telemetry.IncrCounter(1.0, "tx", "count")
telemetry.IncrCounterWithLabels([]string{"tx", "count"}, 1.0, labels)

// Gauges
telemetry.SetGauge(42.0, "mempool", "size")
telemetry.SetGaugeWithLabels([]string{"mempool", "size"}, 42.0, labels)

// Timing
start := telemetry.Now()
// ... operation
telemetry.MeasureSince(start, "tx", "process_time")

// Module-specific helpers
telemetry.ModuleMeasureSince("bank", start, "send", "time")
telemetry.ModuleSetGauge("bank", 100.0, "balance", "total")

Metrics Sink Types

SinkDescription
memIn-memory sink with SIGUSR1 dump support (default)
statsdStatsD protocol
dogstatsdDatadog DogStatsD
otelOpenTelemetry (bridges to configured MeterProvider)

Best Practices

  1. Use environment variable initialization for production to avoid atomic load overhead
  2. Always call Shutdown() to ensure metrics/traces are flushed
  3. Thread context.Context properly for correct span correlation

Viewing Telemetry Data

With Grafana LGTM running:
  1. Open http://localhost:3000
  2. Use the Drilldown views to explore:
    • Traces: Distributed trace visualization
    • Metrics: Query and dashboard metrics
    • Logs: Structured log search

Cosmos SDK Metrics

The following metrics are emitted from the Cosmos SDK.
MetricDescriptionUnitType
tx_countTotal number of txs processed via DeliverTxtxcounter
tx_successfulTotal number of successful txs processed via DeliverTxtxcounter
tx_failedTotal number of failed txs processed via DeliverTxtxcounter
tx_gas_usedThe total amount of gas used by a txgasgauge
tx_gas_wantedThe total amount of gas requested by a txgasgauge
tx_msg_sendThe total amount of tokens sent in a MsgSend (per denom)tokengauge
tx_msg_withdraw_rewardThe total amount of tokens withdrawn in a MsgWithdrawDelegatorReward (per denom)tokengauge
tx_msg_withdraw_commissionThe total amount of tokens withdrawn in a MsgWithdrawValidatorCommission (per denom)tokengauge
tx_msg_delegateThe total amount of tokens delegated in a MsgDelegatetokengauge
tx_msg_begin_unbondingThe total amount of tokens undelegated in a MsgUndelegatetokengauge
tx_msg_begin_begin_redelegateThe total amount of tokens redelegated in a MsgBeginRedelegatetokengauge
tx_msg_ibc_transferThe total amount of tokens transferred via IBC in a MsgTransfer (source or sink chain)tokengauge
ibc_transfer_packet_receiveThe total amount of tokens received in a FungibleTokenPacketData (source or sink chain)tokengauge
new_accountTotal number of new accounts createdaccountcounter
gov_proposalTotal number of governance proposalsproposalcounter
gov_voteTotal number of governance votes for a proposalvotecounter
gov_depositTotal number of governance deposits for a proposaldepositcounter
staking_delegateTotal number of delegationsdelegationcounter
staking_undelegateTotal number of undelegationsundelegationcounter
staking_redelegateTotal number of redelegationsredelegationcounter
ibc_transfer_sendTotal number of IBC transfers sent from a chain (source or sink)transfercounter
ibc_transfer_receiveTotal number of IBC transfers received to a chain (source or sink)transfercounter
ibc_client_createTotal number of clients createdcreatecounter
ibc_client_updateTotal number of client updatesupdatecounter
ibc_client_upgradeTotal number of client upgradesupgradecounter
ibc_client_misbehaviourTotal number of client misbehaviorsmisbehaviourcounter
ibc_connection_open-initTotal number of connection OpenInit handshakeshandshakecounter
ibc_connection_open-tryTotal number of connection OpenTry handshakeshandshakecounter
ibc_connection_open-ackTotal number of connection OpenAck handshakeshandshakecounter
ibc_connection_open-confirmTotal number of connection OpenConfirm handshakeshandshakecounter
ibc_channel_open-initTotal number of channel OpenInit handshakeshandshakecounter
ibc_channel_open-tryTotal number of channel OpenTry handshakeshandshakecounter
ibc_channel_open-ackTotal number of channel OpenAck handshakeshandshakecounter
ibc_channel_open-confirmTotal number of channel OpenConfirm handshakeshandshakecounter
ibc_channel_close-initTotal number of channel CloseInit handshakeshandshakecounter
ibc_channel_close-confirmTotal number of channel CloseConfirm handshakeshandshakecounter
tx_msg_ibc_recv_packetTotal number of IBC packets receivedpacketcounter
tx_msg_ibc_acknowledge_packetTotal number of IBC packets acknowledgedacknowledgementcounter
ibc_timeout_packetTotal number of IBC timeout packetstimeoutcounter
store_iavl_getDuration of an IAVL Store#Get callmssummary
store_iavl_setDuration of an IAVL Store#Set callmssummary
store_iavl_hasDuration of an IAVL Store#Has callmssummary
store_iavl_deleteDuration of an IAVL Store#Delete callmssummary
store_iavl_commitDuration of an IAVL Store#Commit callmssummary
store_iavl_queryDuration of an IAVL Store#Query callmssummary