OpenTelemetry CI GitHub release GitHub commits since latest release (by SemVer)

OpenTelemetry-SDK

Click through to the Full Generated Documentation for somewhat more complete documentation.

This library implements an OpenTelemetry SDK for Crystal, using the API interfaces defined in the API shard.

As a general rule, naming conventions have been based on the standard glossary of OpenTelementry terms, as found at https://opentelemetry.io/docs/concepts/glossary/

The general architecture of the implementation is guided by this document:

https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md

The TL;DR is that a TracerProvider is used to create a Tracer. A Span is created inside of the context of a Tracer, and one Span may nest inside of another.

Full Generated Documentation

https://wyhaines.github.io/opentelemetry-sdk.cr/

A lot of documentation needs to be added. PRs would be welcome!

Discord

A Discord community for help and discussion with Crystal OpenTelemetry can be found at:

https://discord.gg/WKe9WWJ3HE

Installation

  1. Add the dependency to your shard.yml:

    dependencies:
      opentelemetry-sdk:
        github: wyhaines/opentelemetry-sdk.cr
  2. Run shards install

Usage

require "opentelemetry-sdk"

Global Tracer Provider

The most common pattern for usage is to have a single global TracerProvider that is configured early in the program's execution. One may create an explicit configuration block, which will configure a globally held TracerProvider:

OpenTelemetry.configure do |config|
  config.service_name = "my_app_or_library"
  config.service_version = "1.1.1"
  config.exporter = OpenTelemetry::Exporter.new(variant: :stdout)
end

One may also provision a TracerProvider object directly:

tracer_provider = OpenTelemetry.tracer_provider("my_app_or_library", "1.1.1")
tracer_provider = OpenTelemetry.tracer_provider do |tracer|
  tracer.service_name = "my_app_or_library"
  tracer.service_version = "1.1.1"
end

This allows multiple providers with different configuration to be created:

provider_a = OpenTelemetry::TracerProvider.new("my_app_or_library", "1.1.1")
provider_a.exporter = OpenTelemetry::Exporter.new(variant: :stdout)
provider_b = OpenTelementry::TracerProvider.new do |config|
  config.service_name = "my_app_or_library"
  config.service_version = "1.1.1"
  config.exporter = OpenTelemetry::Exporter.new(variant: :stdout)
end

All TracerProvider configuration done in this way will respect OpenTelemetry SDK conventions for environment variable based configuration. Configuration delivered via environment variables supercedes configuration delivered in code. For example:

environment variables

OTEL_SERVICE_NAME="FIB ON COMMAND"
OTEL_SERVICE_VERSION="1.1.1"
OTEL_TRACES_EXPORTER="stdout"
OTEL_TRACES_SAMPLER=traceidratio
OTEL_TRACES_SAMPLER_ARG="0.10"

configuration code

OpenTelemetry.configure do |config|
  config.service_name = "Fibonacci Server"
  config.service_version = Fibonacci::VERSION
  config.exporter = OpenTelemetry::Exporter.new(variant: :http) do |exporter|
    exporter = exporter.as(OpenTelemetry::Exporter::Http)
    exporter.endpoint = "https://otlp.nr-data.net:4318/v1/traces"
    headers = HTTP::Headers.new
    headers["api-key"] = ENV["NEW_RELIC_LICENSE_KEY"]?.to_s
    exporter.headers = headers
  end
end

In the above code, the code specifies a default set of configuration, which includes setting up an exporter to send traces to New Relic. The environment variable based configuration will override that configuration, however, and will instead setup a Stdout exporter with a sampler that only sends 10% of the traces to the exporter.

If one knows that one will be depending on environment variable based configuration, the initial configuration of the OpenTelemetry library can be simplified down to:

OpenTelemetry.configure

The SDK will support the full range of environment variable based configuration (https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md), but currently only a minimal subset is supported:

Getting a Tracer From a Provider Object

Most typically, when using a default TracerProvider, the SDK will be leveraged to produce a Tracer like so:

OpenTelemetry.tracer.in_span("I am a span") do |span|
  span.set_attribute("key1", "value1")
  span.set_attribute("key2", "value2")
  span.set_attribute("key3", "value3")

  do_some_work
end

If one wishes to override the configuration held by a TracerProvider when creating a Tracer, new configuration can be provided in the #tracer method call:

tracer = provider_a.tracer("microservice foo", "1.2.3") # Override the configuration
tracer = provider_a.tracer do |tracer|
  tracer.service_name = "microservice foo"
  tracer.service_version = "1.2.3"
end

This new configuration only applies to the specific Tracer instance created. It does not alter the TracerProvider configuration.

Creating Spans Using a Tracer


tracer.in_span("request") do |span|
  span.set_attribute("verb", "GET")
  span.set_attribute("url", "http://example.com/foo")
  span.add_event("dispatching to handler")
  tracer.in_span("handler") do |child_span|
    child_span.add_event("handling request")
    tracer.in_span("db") do |child_span|
      child_span.add_event("querying database")
    end
  end
end

Development

If you want to help with development, fork this repo. Do your work in a branch inside your fork, and when it is ready (and has specs), submit a PR. See Contributing below.

If you have a question or find an issue, you can start a discussion or create an issue.

Contributing

  1. Fork it (https://github.com/wyhaines/opentelemetry-sdk.cr/fork)
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

Contributors

GitHub code size in bytes GitHub issues