Verified Commit 3f304a94 authored by Hercules Merscher's avatar Hercules Merscher 🌴
Browse files

fix: default service name should be a string for compatibility with OTel

Having DEFAULT_SERVICE_NAME as a symbol was returning this warning, and
because of that instrumentation was not being properly working when a
service name wasn't manually provided.

```shell
GITLAB_TRACING=otlp://console bin/rails s
Unable to instantiate tracer: attribute values must be (array of) strings, integers, floats, or booleans
=> Booting Puma
=> Rails 8.0.0 application starting in development
=> Run `bin/rails server --help` for more startup options
Puma starting in single mode...
* Puma version: 7.2.0 ("On The Corner")
* Ruby version: ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +PRISM [arm64-darwin25]
*  Min threads: 3
*  Max threads: 3
*  Environment: development
*          PID: 45405
* Listening on http://127.0.0.1:3000
* Listening on http://[::1]:3000
* Listening on http://127.4.21.72:3000
Use Ctrl-C to stop
Started GET "/endpoint1" for ::1 at 2026-02-16 12:11:31 +0100
  ActiveRecord::SchemaMigration Load (0.1ms)  SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC /*application='TracingSampleApp'*/
Processing by TraceTestController#endpoint1 as */*
Started GET "/endpoint2" for ::1 at 2026-02-16 12:11:31 +0100
Processing by TraceTestController#endpoint2 as */*
Started GET "/endpoint3" for ::1 at 2026-02-16 12:11:31 +0100
Processing by TraceTestController#endpoint3 as */*
Completed 200 OK in 517ms (Views: 0.5ms | ActiveRecord: 0.0ms (0 queries, 0 cached) | GC: 0.0ms)

Completed 200 OK in 604ms (Views: 0.1ms | ActiveRecord: 0.0ms (0 queries, 0 cached) | GC: 0.7ms)

Completed 200 OK in 636ms (Views: 0.0ms | ActiveRecord: 0.0ms (0 queries, 0 cached) | GC: 1.0ms)
```

This commit fixes it:

```shell
GITLAB_TRACING=otlp://console bin/rails s
I, [2026-02-16T12:28:16.976538 #49006]  INFO -- : Instrumentation: OpenTelemetry::Instrumentation::ActiveSupport was successfully installed with the following options {}
I, [2026-02-16T12:28:16.978252 #49006]  INFO -- : Instrumentation: OpenTelemetry::Instrumentation::Rack was successfully installed with the following options {allowed_request_headers: [], allowed_response_headers: [], application: nil, record_frontend_span: false, untraced_endpoints: [], url_quantization: nil, untraced_requests: nil, response_propagators: [], use_rack_events: true, allowed_rack_request_headers: {}, allowed_rack_response_headers: {}}
I, [2026-02-16T12:28:16.978876 #49006]  INFO -- : Instrumentation: OpenTelemetry::Instrumentation::ActionPack was successfully installed with the following options {span_naming: :semconv}
I, [2026-02-16T12:28:16.980147 #49006]  INFO -- : Instrumentation: OpenTelemetry::Instrumentation::ActiveRecord was successfully installed with the following options {}
I, [2026-02-16T12:28:16.980345 #49006]  INFO -- : Instrumentation: OpenTelemetry::Instrumentation::ActionView was successfully installed with the following options {disallowed_notification_payload_keys: [], notification_payload_transform: nil, legacy_span_names: false}
I, [2026-02-16T12:28:16.980545 #49006]  INFO -- : Instrumentation: OpenTelemetry::Instrumentation::ConcurrentRuby was successfully installed with the following options {}
I, [2026-02-16T12:28:16.980874 #49006]  INFO -- : Instrumentation: OpenTelemetry::Instrumentation::Net::HTTP was successfully installed with the following options {untraced_hosts: []}
I, [2026-02-16T12:28:16.980904 #49006]  INFO -- : Instrumentation: OpenTelemetry::Instrumentation::Rails was successfully installed with the following options {}
=> Booting Puma
=> Rails 8.0.0 application starting in development
=> Run `bin/rails server --help` for more startup options
Puma starting in single mode...
* Puma version: 7.2.0 ("On The Corner")
* Ruby version: ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +PRISM [arm64-darwin25]
*  Min threads: 3
*  Max threads: 3
*  Environment: development
*          PID: 49006
* Listening on http://127.0.0.1:3000
* Listening on http://[::1]:3000
* Listening on http://127.4.21.72:3000
Use Ctrl-C to stop
```
parent 4cb16b61
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -30,7 +30,9 @@ module Labkit
      autoload :OpentracingTracer, "labkit/tracing/adapters/opentracing_tracer"
    end

    DEFAULT_SERVICE_NAME = :'labkit-service'
    # Must be a String, not Symbol, as OpenTelemetry requires resource attribute values
    # to be strings, integers, floats, or booleans
    DEFAULT_SERVICE_NAME = "labkit-service"

    # Module-level attribute for storing the configured service name
    # Set by Factory.create_tracer when a tracer is created
+10 −1
Original line number Diff line number Diff line
@@ -9,9 +9,18 @@ module Labkit
      def self.detect_service_name(connection_string)
        return Labkit::Tracing::DEFAULT_SERVICE_NAME unless connection_string

        begin
          parsed = URI.parse(connection_string)
          options = TracingUtils.parse_query_string(parsed.query)
        options[:service_name] || Labkit::Tracing::DEFAULT_SERVICE_NAME

          return options[:service_name] if options[:service_name]

          warn "Labkit::Tracing: service_name not found in connection string, using default: '#{Labkit::Tracing::DEFAULT_SERVICE_NAME}'"
          Labkit::Tracing::DEFAULT_SERVICE_NAME
        rescue URI::InvalidURIError => e
          warn "Labkit::Tracing: Invalid connection string (#{e.message}), using default service name: '#{Labkit::Tracing::DEFAULT_SERVICE_NAME}'"
          Labkit::Tracing::DEFAULT_SERVICE_NAME
        end
      end

      def self.initialize!
+21 −3
Original line number Diff line number Diff line
@@ -6,11 +6,13 @@ require "spec_helper"
describe Labkit::Tracing::AutoInitialize do
  describe ".detect_service_name" do
    it "returns default service name when connection string is nil" do
      expect(described_class.detect_service_name(nil)).to eq(:'labkit-service')
      expect(described_class.detect_service_name(nil)).to eq(Labkit::Tracing::DEFAULT_SERVICE_NAME)
    end

    it "returns default service name when service_name query param is missing" do
      expect(described_class.detect_service_name("otlp://localhost:4318")).to eq(:'labkit-service')
      expected_warning = /service_name not found in connection string, using default: 'labkit-service'/
      expect { described_class.detect_service_name("otlp://localhost:4318") }.to output(expected_warning).to_stderr
      expect(described_class.detect_service_name("otlp://localhost:4318")).to eq(Labkit::Tracing::DEFAULT_SERVICE_NAME)
    end

    it "extracts service_name from query parameters" do
@@ -33,6 +35,22 @@ describe Labkit::Tracing::AutoInitialize do
    it "handles service_name with special characters" do
      expect(described_class.detect_service_name("otlp://localhost:4318?service_name=api-v2")).to eq("api-v2")
    end

    it "returns default service name and warns when connection string is malformed" do
      malformed_uri = "not a valid uri with spaces"
      expected_warning = /Invalid connection string.*using default service name: 'labkit-service'/

      expect { described_class.detect_service_name(malformed_uri) }.to output(expected_warning).to_stderr
      expect(described_class.detect_service_name(malformed_uri)).to eq(Labkit::Tracing::DEFAULT_SERVICE_NAME)
    end

    it "handles invalid URI scheme gracefully" do
      invalid_uri = "ht!tp://invalid"
      expected_warning = /Invalid connection string.*using default service name: 'labkit-service'/

      expect { described_class.detect_service_name(invalid_uri) }.to output(expected_warning).to_stderr
      expect(described_class.detect_service_name(invalid_uri)).to eq(Labkit::Tracing::DEFAULT_SERVICE_NAME)
    end
  end

  describe ".initialize!" do
@@ -95,7 +113,7 @@ describe Labkit::Tracing::AutoInitialize do
        described_class.initialize!

        expect(Labkit::Tracing::Factory).to have_received(:create_tracer)
          .with(:'labkit-service', "otlp://localhost:4318")
          .with(Labkit::Tracing::DEFAULT_SERVICE_NAME, "otlp://localhost:4318")
      end

      it "passes a configuration block to Factory.create_tracer" do
+8 −0
Original line number Diff line number Diff line
@@ -240,6 +240,14 @@ describe Labkit::Tracing::OpenTelemetryFactory do
        provider = OpenTelemetry.tracer_provider
        expect(provider).to be_a(OpenTelemetry::SDK::Trace::TracerProvider)
      end

      it "accepts default service name when provided as string" do
        tracer = described_class.create_tracer(Labkit::Tracing::DEFAULT_SERVICE_NAME, "otlp://console")
        expect(tracer).to be_a(OpenTelemetry::Trace::Tracer)

        provider = OpenTelemetry.tracer_provider
        expect(provider).to be_a(OpenTelemetry::SDK::Trace::TracerProvider)
      end
    end
  end

+1 −1
Original line number Diff line number Diff line
@@ -199,7 +199,7 @@ describe Labkit::Tracing::TracingUtils do
      it "falls back to default service name when no service name configured" do
        clear_tracer_memoization

        expect(fake_tracer_provider).to receive(:tracer).with(:'labkit-service').and_return(fake_tracer)
        expect(fake_tracer_provider).to receive(:tracer).with(Labkit::Tracing::DEFAULT_SERVICE_NAME).and_return(fake_tracer)

        described_class.tracer
      end