Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
### v2.75.0 (2026-07-01)
* * *

### New Features
* Added an optional telemetry adapter hook for tracing Chargebee API calls via OpenTelemetry (or any APM). Configure it via `ChargeBee.configure(site: ..., api_key: ..., telemetry_adapter: adapter)`. When unconfigured, the SDK skips all telemetry work — no behavior change for existing integrations.
* Each API call emits one CLIENT span (`chargebee.{resource}.{operation}`) with OpenTelemetry HTTP semantic-convention attributes plus `chargebee.*` attributes. Adapters may inject W3C trace context (`traceparent`) into outbound request headers for distributed tracing.
* Exposed the `TelemetryAdapter`, `RequestTelemetryContext`, `RequestTelemetryResult`, `RequestTelemetryError` types, the `TelemetrySupport` helpers, and the `TelemetryAttributeKeys` constants under the `ChargeBee::Telemetry` module. OpenTelemetry is not bundled with the SDK; bring your own OTel (or APM) gems in the host application.


### v2.74.0 (2026-06-12)
* * *
### Bug Fixes:
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
chargebee (2.74.0)
chargebee (2.75.0)
cgi (>= 0.1.0, < 1.0.0)

GEM
Expand Down
53 changes: 53 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,59 @@ ChargeBee::default_env.retry_config = {
# ... your Chargebee API operations ...
```

### Telemetry (OpenTelemetry)

**Optional add-on.** Existing integrations do not need any changes — if you never set a telemetry adapter, API calls behave exactly as before.

Pass a `telemetry_adapter` when you want Chargebee API calls traced in your observability stack (Datadog, Splunk, Honeycomb, Jaeger, etc.). OpenTelemetry is **not** bundled with the `chargebee` gem — install and configure OTel (or your APM SDK) in your application, implement `ChargeBee::Telemetry::TelemetryAdapter`, and wire it on the environment.

The SDK builds standardized span attributes (`start_attributes`, `end_attributes`) following stable [OpenTelemetry HTTP semantic conventions](https://opentelemetry.io/docs/specs/semconv/http/http-spans/) (`url.full`, `http.request.method`, `http.response.status_code`, `server.address`, `error.type`) plus Chargebee-specific `chargebee.*` attributes (see constants in `ChargeBee::Telemetry::TelemetryAttributeKeys`).

Span names follow `chargebee.{resource}.{operation}` (for example, `chargebee.customer.create`). One span is created per SDK API call; retries reuse the same span. Adapter failures are logged and never affect the underlying API request.

Configure at startup — pass `telemetry_adapter` in the same `ChargeBee.configure` call. `configure` replaces the default environment, so calling it again without `telemetry_adapter` drops a previously configured adapter:

```ruby
require 'chargebee'

class MyTelemetryAdapter
include ChargeBee::Telemetry::TelemetryAdapter

def on_request_start(context, request_headers)
# Start a span using context.start_attributes and inject trace headers into request_headers
nil
end

def on_request_end(handle, result)
# End the span using result.end_attributes
end
end

ChargeBee.configure(
api_key: 'your_api_key',
site: 'your_site',
telemetry_adapter: MyTelemetryAdapter.new,
)
```

If you pass a custom `ChargeBee::Environment` into resource methods as `env`, set `telemetry_adapter` on that instance — `ChargeBee.configure` only updates the default environment:

```ruby
env = ChargeBee::Environment.new(
api_key: 'your_api_key',
site: 'your_site',
telemetry_adapter: MyTelemetryAdapter.new,
)

ChargeBee::Customer.list({}, env)
```

To pass custom `chargebee-*` headers (promoted to `http.request.header.chargebee-*` span attributes), include them in the `headers` argument on resource methods:

```ruby
ChargeBee::Customer.list({}, nil, { 'chargebee-business-entity-id' => 'entity-id' })
```

## License

See the LICENSE file.
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.74.0
2.75.0
4 changes: 2 additions & 2 deletions chargebee.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ Gem::Specification.new do |s|
s.rubygems_version = '1.3.5'
s.required_ruby_version = '>= 1.9.3'
s.name = 'chargebee'
s.version = '2.74.0'
s.date = '2026-06-18'
s.version = '2.75.0'
s.date = '2026-06-30'
s.summary = "Ruby client for Chargebee API."
s.description = "Subscription Billing - Simple. Secure. Affordable. More details at www.chargebee.com."
s.metadata = {
Expand Down
10 changes: 9 additions & 1 deletion lib/chargebee.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@

require File.dirname(__FILE__) + '/chargebee/errors'

require File.dirname(__FILE__) + '/chargebee/telemetry/telemetry_attribute_keys'
require File.dirname(__FILE__) + '/chargebee/telemetry/request_telemetry_context'
require File.dirname(__FILE__) + '/chargebee/telemetry/request_telemetry_error'
require File.dirname(__FILE__) + '/chargebee/telemetry/request_telemetry_result'
require File.dirname(__FILE__) + '/chargebee/telemetry/telemetry_adapter'
require File.dirname(__FILE__) + '/chargebee/telemetry/telemetry_support'
require File.dirname(__FILE__) + '/chargebee/telemetry_executor'

require File.dirname(__FILE__) + '/chargebee/models/model'
require File.dirname(__FILE__) + '/chargebee/models/addon'
require File.dirname(__FILE__) + '/chargebee/models/address'
Expand Down Expand Up @@ -121,7 +129,7 @@

module ChargeBee

VERSION = '2.74.0'
VERSION = '2.75.0'

@@default_env = nil
@@verify_ca_certs = true
Expand Down
5 changes: 3 additions & 2 deletions lib/chargebee/environment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ class Environment
attr_accessor :api_key, :site, :time_machine_sleeptime, :export_sleeptime, :connect_timeout, :read_timeout
attr_reader :api_endpoint

# Add retry_config and enable_debug_logs
attr_accessor :retry_config, :enable_debug_logs
# Add retry_config, enable_debug_logs, and telemetry_adapter
attr_accessor :retry_config, :enable_debug_logs, :telemetry_adapter

def initialize(options)
options[:time_machine_sleeptime] ||= TIME_MACHINE_TIMEOUT
Expand All @@ -23,6 +23,7 @@ def initialize(options)
end
@retry_config = options[:retry_config]
@enable_debug_logs = options[:enable_debug_logs] || false
@telemetry_adapter = options[:telemetry_adapter]
if($CHARGEBEE_DOMAIN == nil)
@api_endpoint = "https://#{@site}.chargebee.com/api/#{API_VERSION}"
else
Expand Down
14 changes: 7 additions & 7 deletions lib/chargebee/models/addon.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def self.create(params, env=nil, headers={})
options = {
:isIdempotent => true
}
Request.send('post', uri_path("addons"), params, env, headers,nil, false, jsonKeys, options)
Request.send('post', uri_path("addons"), params, env, headers,nil, false, jsonKeys, options, telemetry_resource: "addon", telemetry_operation: "create")
end

def self.update(id, params, env=nil, headers={})
Expand All @@ -38,21 +38,21 @@ def self.update(id, params, env=nil, headers={})
options = {
:isIdempotent => true
}
Request.send('post', uri_path("addons",id.to_s), params, env, headers,nil, false, jsonKeys, options)
Request.send('post', uri_path("addons",id.to_s), params, env, headers,nil, false, jsonKeys, options, telemetry_resource: "addon", telemetry_operation: "update")
end

def self.list(params={}, env=nil, headers={})
jsonKeys = {
}
options = {}
Request.send_list_request('get', uri_path("addons"), params, env, headers,nil, false, jsonKeys, options)
Request.send_list_request('get', uri_path("addons"), params, env, headers,nil, false, jsonKeys, options, telemetry_resource: "addon", telemetry_operation: "list")
end

def self.retrieve(id, env=nil, headers={})
jsonKeys = {
}
options = {}
Request.send('get', uri_path("addons",id.to_s), {}, env, headers,nil, false, jsonKeys, options)
Request.send('get', uri_path("addons",id.to_s), {}, env, headers,nil, false, jsonKeys, options, telemetry_resource: "addon", telemetry_operation: "retrieve")
end

def self.delete(id, env=nil, headers={})
Expand All @@ -61,7 +61,7 @@ def self.delete(id, env=nil, headers={})
options = {
:isIdempotent => true
}
Request.send('post', uri_path("addons",id.to_s,"delete"), {}, env, headers,nil, false, jsonKeys, options)
Request.send('post', uri_path("addons",id.to_s,"delete"), {}, env, headers,nil, false, jsonKeys, options, telemetry_resource: "addon", telemetry_operation: "delete")
end

def self.copy(params, env=nil, headers={})
Expand All @@ -70,7 +70,7 @@ def self.copy(params, env=nil, headers={})
options = {
:isIdempotent => true
}
Request.send('post', uri_path("addons","copy"), params, env, headers,nil, false, jsonKeys, options)
Request.send('post', uri_path("addons","copy"), params, env, headers,nil, false, jsonKeys, options, telemetry_resource: "addon", telemetry_operation: "copy")
end

def self.unarchive(id, env=nil, headers={})
Expand All @@ -79,7 +79,7 @@ def self.unarchive(id, env=nil, headers={})
options = {
:isIdempotent => true
}
Request.send('post', uri_path("addons",id.to_s,"unarchive"), {}, env, headers,nil, false, jsonKeys, options)
Request.send('post', uri_path("addons",id.to_s,"unarchive"), {}, env, headers,nil, false, jsonKeys, options, telemetry_resource: "addon", telemetry_operation: "unarchive")
end

end # ~Addon
Expand Down
4 changes: 2 additions & 2 deletions lib/chargebee/models/address.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def self.retrieve(params, env=nil, headers={})
jsonKeys = {
}
options = {}
Request.send('get', uri_path("addresses"), params, env, headers,nil, false, jsonKeys, options)
Request.send('get', uri_path("addresses"), params, env, headers,nil, false, jsonKeys, options, telemetry_resource: "address", telemetry_operation: "retrieve")
end

def self.update(params, env=nil, headers={})
Expand All @@ -20,7 +20,7 @@ def self.update(params, env=nil, headers={})
options = {
:isIdempotent => true
}
Request.send('post', uri_path("addresses"), params, env, headers,nil, false, jsonKeys, options)
Request.send('post', uri_path("addresses"), params, env, headers,nil, false, jsonKeys, options, telemetry_resource: "address", telemetry_operation: "update")
end

end # ~Address
Expand Down
12 changes: 6 additions & 6 deletions lib/chargebee/models/alert.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,21 @@ def self.create(params, env=nil, headers={})
options = {
:isIdempotent => true
}
Request.send('post', uri_path("alerts"), params, env, headers,nil, false, jsonKeys, options)
Request.send('post', uri_path("alerts"), params, env, headers,nil, false, jsonKeys, options, telemetry_resource: "alert", telemetry_operation: "create")
end

def self.retrieve(id, env=nil, headers={})
jsonKeys = {
}
options = {}
Request.send('get', uri_path("alerts",id.to_s), {}, env, headers,nil, false, jsonKeys, options)
Request.send('get', uri_path("alerts",id.to_s), {}, env, headers,nil, false, jsonKeys, options, telemetry_resource: "alert", telemetry_operation: "retrieve")
end

def self.list(params={}, env=nil, headers={})
jsonKeys = {
}
options = {}
Request.send_list_request('get', uri_path("alerts"), params, env, headers,nil, false, jsonKeys, options)
Request.send_list_request('get', uri_path("alerts"), params, env, headers,nil, false, jsonKeys, options, telemetry_resource: "alert", telemetry_operation: "list")
end

def self.update(id, params={}, env=nil, headers={})
Expand All @@ -36,7 +36,7 @@ def self.update(id, params={}, env=nil, headers={})
options = {
:isIdempotent => true
}
Request.send('post', uri_path("alerts",id.to_s), params, env, headers,nil, false, jsonKeys, options)
Request.send('post', uri_path("alerts",id.to_s), params, env, headers,nil, false, jsonKeys, options, telemetry_resource: "alert", telemetry_operation: "update")
end

def self.delete(id, env=nil, headers={})
Expand All @@ -45,14 +45,14 @@ def self.delete(id, env=nil, headers={})
options = {
:isIdempotent => true
}
Request.send('post', uri_path("alerts",id.to_s,"delete"), {}, env, headers,nil, false, jsonKeys, options)
Request.send('post', uri_path("alerts",id.to_s,"delete"), {}, env, headers,nil, false, jsonKeys, options, telemetry_resource: "alert", telemetry_operation: "delete")
end

def self.application_alerts_for_subscription(id, params={}, env=nil, headers={})
jsonKeys = {
}
options = {}
Request.send('get', uri_path("subscriptions",id.to_s,"applicable_alerts"), params, env, headers,nil, false, jsonKeys, options)
Request.send('get', uri_path("subscriptions",id.to_s,"applicable_alerts"), params, env, headers,nil, false, jsonKeys, options, telemetry_resource: "alert", telemetry_operation: "applicationAlertsForSubscription")
end

end # ~Alert
Expand Down
4 changes: 2 additions & 2 deletions lib/chargebee/models/alert_status.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ def self.alert_statuses_for_subscription(id, params={}, env=nil, headers={})
jsonKeys = {
}
options = {}
Request.send('get', uri_path("subscriptions",id.to_s,"alert_statuses"), params, env, headers,nil, false, jsonKeys, options)
Request.send('get', uri_path("subscriptions",id.to_s,"alert_statuses"), params, env, headers,nil, false, jsonKeys, options, telemetry_resource: "alertStatus", telemetry_operation: "alertStatusesForSubscription")
end

def self.alert_statuses_for_alert(id, params={}, env=nil, headers={})
jsonKeys = {
}
options = {}
Request.send('get', uri_path("alerts",id.to_s,"alert_statuses"), params, env, headers,nil, false, jsonKeys, options)
Request.send('get', uri_path("alerts",id.to_s,"alert_statuses"), params, env, headers,nil, false, jsonKeys, options, telemetry_resource: "alertStatus", telemetry_operation: "alertStatusesForAlert")
end

end # ~AlertStatus
Expand Down
10 changes: 5 additions & 5 deletions lib/chargebee/models/attached_item.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def self.create(id, params, env=nil, headers={})
options = {
:isIdempotent => true
}
Request.send('post', uri_path("items",id.to_s,"attached_items"), params, env, headers,nil, false, jsonKeys, options)
Request.send('post', uri_path("items",id.to_s,"attached_items"), params, env, headers,nil, false, jsonKeys, options, telemetry_resource: "attachedItem", telemetry_operation: "create")
end

def self.update(id, params, env=nil, headers={})
Expand All @@ -23,14 +23,14 @@ def self.update(id, params, env=nil, headers={})
options = {
:isIdempotent => true
}
Request.send('post', uri_path("attached_items",id.to_s), params, env, headers,nil, false, jsonKeys, options)
Request.send('post', uri_path("attached_items",id.to_s), params, env, headers,nil, false, jsonKeys, options, telemetry_resource: "attachedItem", telemetry_operation: "update")
end

def self.retrieve(id, params, env=nil, headers={})
jsonKeys = {
}
options = {}
Request.send('get', uri_path("attached_items",id.to_s), params, env, headers,nil, false, jsonKeys, options)
Request.send('get', uri_path("attached_items",id.to_s), params, env, headers,nil, false, jsonKeys, options, telemetry_resource: "attachedItem", telemetry_operation: "retrieve")
end

def self.delete(id, params, env=nil, headers={})
Expand All @@ -39,14 +39,14 @@ def self.delete(id, params, env=nil, headers={})
options = {
:isIdempotent => true
}
Request.send('post', uri_path("attached_items",id.to_s,"delete"), params, env, headers,nil, false, jsonKeys, options)
Request.send('post', uri_path("attached_items",id.to_s,"delete"), params, env, headers,nil, false, jsonKeys, options, telemetry_resource: "attachedItem", telemetry_operation: "delete")
end

def self.list(id, params={}, env=nil, headers={})
jsonKeys = {
}
options = {}
Request.send_list_request('get', uri_path("items",id.to_s,"attached_items"), params, env, headers,nil, false, jsonKeys, options)
Request.send_list_request('get', uri_path("items",id.to_s,"attached_items"), params, env, headers,nil, false, jsonKeys, options, telemetry_resource: "attachedItem", telemetry_operation: "list")
end

end # ~AttachedItem
Expand Down
4 changes: 2 additions & 2 deletions lib/chargebee/models/business_entity.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ def self.create_transfers(params, env=nil, headers={})
options = {
:isIdempotent => true
}
Request.send('post', uri_path("business_entities","transfers"), params, env, headers,nil, false, jsonKeys, options)
Request.send('post', uri_path("business_entities","transfers"), params, env, headers,nil, false, jsonKeys, options, telemetry_resource: "businessEntity", telemetry_operation: "createTransfers")
end

def self.get_transfers(params={}, env=nil, headers={})
jsonKeys = {
}
options = {}
Request.send('get', uri_path("business_entities","transfers"), params, env, headers,nil, false, jsonKeys, options)
Request.send('get', uri_path("business_entities","transfers"), params, env, headers,nil, false, jsonKeys, options, telemetry_resource: "businessEntity", telemetry_operation: "getTransfers")
end

end # ~BusinessEntity
Expand Down
10 changes: 5 additions & 5 deletions lib/chargebee/models/card.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def self.retrieve(id, env=nil, headers={})
jsonKeys = {
}
options = {}
Request.send('get', uri_path("cards",id.to_s), {}, env, headers,nil, false, jsonKeys, options)
Request.send('get', uri_path("cards",id.to_s), {}, env, headers,nil, false, jsonKeys, options, telemetry_resource: "card", telemetry_operation: "retrieve")
end

def self.update_card_for_customer(id, params, env=nil, headers={})
Expand All @@ -23,7 +23,7 @@ def self.update_card_for_customer(id, params, env=nil, headers={})
options = {
:isIdempotent => true
}
Request.send('post', uri_path("customers",id.to_s,"credit_card"), params, env, headers,nil, false, jsonKeys, options)
Request.send('post', uri_path("customers",id.to_s,"credit_card"), params, env, headers,nil, false, jsonKeys, options, telemetry_resource: "card", telemetry_operation: "updateCardForCustomer")
end

def self.switch_gateway_for_customer(id, params, env=nil, headers={})
Expand All @@ -32,7 +32,7 @@ def self.switch_gateway_for_customer(id, params, env=nil, headers={})
options = {
:isIdempotent => true
}
Request.send('post', uri_path("customers",id.to_s,"switch_gateway"), params, env, headers,nil, false, jsonKeys, options)
Request.send('post', uri_path("customers",id.to_s,"switch_gateway"), params, env, headers,nil, false, jsonKeys, options, telemetry_resource: "card", telemetry_operation: "switchGatewayForCustomer")
end

def self.copy_card_for_customer(id, params, env=nil, headers={})
Expand All @@ -41,7 +41,7 @@ def self.copy_card_for_customer(id, params, env=nil, headers={})
options = {
:isIdempotent => true
}
Request.send('post', uri_path("customers",id.to_s,"copy_card"), params, env, headers,nil, false, jsonKeys, options)
Request.send('post', uri_path("customers",id.to_s,"copy_card"), params, env, headers,nil, false, jsonKeys, options, telemetry_resource: "card", telemetry_operation: "copyCardForCustomer")
end

def self.delete_card_for_customer(id, env=nil, headers={})
Expand All @@ -50,7 +50,7 @@ def self.delete_card_for_customer(id, env=nil, headers={})
options = {
:isIdempotent => true
}
Request.send('post', uri_path("customers",id.to_s,"delete_card"), {}, env, headers,nil, false, jsonKeys, options)
Request.send('post', uri_path("customers",id.to_s,"delete_card"), {}, env, headers,nil, false, jsonKeys, options, telemetry_resource: "card", telemetry_operation: "deleteCardForCustomer")
end

end # ~Card
Expand Down
Loading
Loading