Overview
The Zitadel Ruby SDK provides built-in debugging capabilities to help you troubleshoot integration issues, inspect HTTP requests/responses, and understand SDK behavior. Debug mode logs detailed information about API calls, including request parameters, headers, and response bodies.
Enabling Debug Mode
During Initialization
Enable debugging when creating the client:
require 'zitadel-client'
client = Zitadel :: Client :: Zitadel . with_access_token (
"https://example.us1.zitadel.cloud" ,
"your-access-token"
) do | config |
config. debugging = true
end
With Any Authentication Method
# Private Key JWT
client = Zitadel :: Client :: Zitadel . with_private_key (
"https://example.us1.zitadel.cloud" ,
"path/to/key.json"
) do | config |
config. debugging = true
end
# Client Credentials
client = Zitadel :: Client :: Zitadel . with_client_credentials (
"https://example.us1.zitadel.cloud" ,
"client-id" ,
"client-secret"
) do | config |
config. debugging = true
end
Debug mode is controlled by the debugging attribute in lib/zitadel/client/configuration.rb:51
Debug Output
When debugging is enabled, the SDK logs:
HTTP request body (before sending)
HTTP response body (after receiving)
HTTP request/response details (via Typhoeus verbose mode)
Example Output
client = Zitadel :: Client :: Zitadel . with_access_token (
"https://example.us1.zitadel.cloud" ,
"token"
) do | config |
config. debugging = true
end
response = client. users . add_human_user (
Zitadel :: Client :: UserServiceAddHumanUserRequest . new (
username: "john.doe" ,
profile: Zitadel :: Client :: UserServiceSetHumanProfile . new (
given_name: "John" ,
family_name: "Doe"
),
email: Zitadel :: Client :: UserServiceSetHumanEmail . new (
email: "[email protected] "
)
)
)
Console output:
HTTP request body param ~BEGIN~
{
"username": "john.doe",
"profile": {
"givenName": "John",
"familyName": "Doe"
},
"email": {
"email": "[email protected] "
}
}
~END~
HTTP response body ~BEGIN~
{
"userId": "123456789",
"details": {
"sequence": "1",
"creationDate": "2024-01-15T10:30:00Z"
}
}
~END~
Request and response logging is implemented in lib/zitadel/client/api_client.rb:71 and api_client.rb:120
Custom Logger
Using a Custom Logger Instance
Provide your own logger for more control over output format and destination:
require 'logger'
require 'zitadel-client'
# Create custom logger
custom_logger = Logger . new ( 'log/zitadel_debug.log' )
custom_logger. level = Logger :: DEBUG
custom_logger. formatter = proc do | severity , datetime , progname , msg |
"[ #{ datetime. strftime ( '%Y-%m-%d %H:%M:%S' ) } ] #{ severity } : #{ msg } \n "
end
client = Zitadel :: Client :: Zitadel . with_private_key (
"https://example.us1.zitadel.cloud" ,
"key.json"
) do | config |
config. debugging = true
config. logger = custom_logger
end
Rails Logger Integration
# config/initializers/zitadel.rb
require 'zitadel-client'
ZITADEL_CLIENT = Zitadel :: Client :: Zitadel . with_private_key (
Rails . application . credentials . dig ( :zitadel , :host ),
Rails . root . join ( 'config' , 'zitadel-key.json' ). to_s
) do | config |
config. debugging = Rails . env . development?
config. logger = Rails . logger
end
Rails log output:
[2024-01-15 10:30:45] DEBUG -- : HTTP request body param ~BEGIN~
{"limit":10}
~END~
[2024-01-15 10:30:46] DEBUG -- : HTTP response body ~BEGIN~
{"result":[...]}
~END~
Environment-Based Logging
require 'zitadel-client'
client = Zitadel :: Client :: Zitadel . with_access_token (
ENV [ 'ZITADEL_HOST' ],
ENV [ 'ZITADEL_TOKEN' ]
) do | config |
# Enable debugging based on environment variable
config. debugging = ENV [ 'ZITADEL_DEBUG' ] == 'true'
# Use different log levels per environment
if ENV [ 'RAILS_ENV' ] == 'production'
config. logger = Logger . new ( 'log/zitadel.log' )
config. logger . level = Logger :: WARN
else
config. logger = Logger . new ( STDOUT )
config. logger . level = Logger :: DEBUG
end
end
Troubleshooting Common Issues
Authentication Failures
Enable debug logging to inspect authentication headers and token exchange:
client = Zitadel :: Client :: Zitadel . with_client_credentials (
"https://example.us1.zitadel.cloud" ,
ENV [ 'CLIENT_ID' ],
ENV [ 'CLIENT_SECRET' ]
) do | config |
config. debugging = true
end
begin
response = client. users . list_users (
Zitadel :: Client :: UserServiceListUsersRequest . new ( limit: 5 )
)
rescue Zitadel :: Client :: ApiError => e
puts "Error Code: #{ e. code } "
puts "Response: #{ e. response_body } "
# Check debug logs for authentication headers
end
What to look for:
Missing or malformed Authorization header
Expired access tokens
Incorrect token type (Bearer vs. other)
Debug authentication issues step-by-step
Enable debugging
config. debugging = true
config. logger = Logger . new ( STDOUT )
Make a simple API call
begin
client. users . list_users (
Zitadel :: Client :: UserServiceListUsersRequest . new ( limit: 1 )
)
rescue => e
puts "Error: #{ e. message } "
end
Check debug output for:
HTTP status code (should see in response)
Authorization header format
Token presence and format
Common issues:
401 Unauthorized : Token is invalid or expired
403 Forbidden : Token is valid but lacks permissions
Empty Authorization header : Authenticator not configured correctly
Network Timeout Issues
Increase timeout and enable debugging to understand slow requests:
client = Zitadel :: Client :: Zitadel . with_private_key (
"https://example.us1.zitadel.cloud" ,
"key.json"
) do | config |
config. timeout = 60 # Increase to 60 seconds
config. debugging = true
end
begin
start_time = Time . now
response = client. users . list_users (
Zitadel :: Client :: UserServiceListUsersRequest . new ( limit: 1000 )
)
puts "Request completed in #{ Time . now - start_time } seconds"
rescue RuntimeError => e
if e. message == 'Connection timed out'
puts "Request exceeded 60 seconds"
# Consider pagination or increasing timeout further
end
end
Timeout errors are raised as RuntimeError with message "Connection timed out" from lib/zitadel/client/api_client.rb:75
SSL Certificate Issues
Debug SSL verification problems:
client = Zitadel :: Client :: Zitadel . with_access_token (
"https://example.us1.zitadel.cloud" ,
"token"
) do | config |
config. debugging = true
config. verify_ssl = true
config. verify_ssl_host = true
end
begin
client. users . list_users (
Zitadel :: Client :: UserServiceListUsersRequest . new ( limit: 1 )
)
rescue RuntimeError => e
if e. message . include? ( 'SSL' )
puts "SSL Error: #{ e. message } "
# Check certificate validity or provide custom CA cert
end
end
Solutions:
Provide custom CA certificate:
config. ssl_ca_cert = "/path/to/ca-bundle.crt"
For development only (not recommended for production):
config. verify_ssl = false if Rails . env . development?
Request/Response Inspection
Inspect malformed requests or unexpected responses:
require 'json'
client = Zitadel :: Client :: Zitadel . with_private_key (
"https://example.us1.zitadel.cloud" ,
"key.json"
) do | config |
config. debugging = true
end
request = Zitadel :: Client :: UserServiceAddHumanUserRequest . new (
username: "test.user" ,
profile: Zitadel :: Client :: UserServiceSetHumanProfile . new (
given_name: "Test" ,
family_name: "User"
)
)
# Inspect request object before sending
puts "Request object: #{ request. to_hash . to_json } "
begin
response = client. users . add_human_user (request)
puts "Response object: #{ response. to_hash . to_json } "
rescue Zitadel :: Client :: ApiError => e
puts "API returned error:"
puts " Status: #{ e. code } "
puts " Body: #{ e. response_body } "
# Parse error details
begin
error_details = JSON . parse (e. response_body )
puts " Message: #{ error_details[ 'message' ] } "
puts " Details: #{ error_details[ 'details' ] } "
rescue JSON :: ParserError
puts " Raw error: #{ e. response_body } "
end
end
Security Considerations
Debug logs may contain sensitive information including:
Access tokens and authentication credentials
Personal user data (emails, names, etc.)
Internal system details
Never enable debug mode in production unless absolutely necessary, and always secure your logs.
Best Practices
Use environment variables to control debugging:
config. debugging = ENV [ 'ZITADEL_DEBUG' ] == 'true'
Separate log files for sensitive data:
if config. debugging
config. logger = Logger . new ( 'log/zitadel_debug.log' )
# Set appropriate file permissions
File . chmod ( 0600 , 'log/zitadel_debug.log' )
end
Redact sensitive fields in production logs:
class RedactingLogger < Logger
def debug ( message )
redacted = message. gsub ( /"Authorization" \s *=> \s *"[^"]+"/ , '"Authorization" => "[REDACTED]"' )
super (redacted)
end
end
config. logger = RedactingLogger . new ( 'log/zitadel.log' )
Rotate and purge debug logs regularly:
config. logger = Logger . new (
'log/zitadel.log' ,
10 , # Keep 10 log files
10_485_760 # 10 MB per file
)
Debugging Strategies
Isolate the Problem
# Create minimal reproducible example
require 'zitadel-client'
client = Zitadel :: Client :: Zitadel . with_access_token (
"https://example.us1.zitadel.cloud" ,
ENV [ 'ZITADEL_TOKEN' ]
) do | config |
config. debugging = true
config. timeout = 30
end
# Test simplest operation first
begin
puts "Testing basic connectivity..."
response = client. users . list_users (
Zitadel :: Client :: UserServiceListUsersRequest . new ( limit: 1 )
)
puts "✓ Connection successful"
puts "✓ Authentication successful"
puts "✓ API responding normally"
rescue => e
puts "✗ Error: #{ e. class } - #{ e. message } "
end
Compare Working vs. Non-Working Requests
# Working request (baseline)
working_request = Zitadel :: Client :: UserServiceGetUserByIdRequest . new (
user_id: "known-valid-id"
)
# Non-working request
failing_request = Zitadel :: Client :: UserServiceGetUserByIdRequest . new (
user_id: "problematic-id"
)
# Compare debug output
[working_request, failing_request]. each do | request |
begin
puts " \n === Testing user_id: #{ request. user_id } ==="
response = client. users . get_user_by_id (request)
puts "Success: #{ response. user_id } "
rescue => e
puts "Failed: #{ e. message } "
end
end
Capture Full Request Details
require 'zitadel-client'
require 'json'
class DebugWrapper
def initialize ( client )
@client = client
end
def method_missing ( method , * args , & block )
service = @client . send (method)
# Wrap service methods
service. define_singleton_method ( :method_missing ) do | service_method , * service_args |
puts " \n === API Call Debug ==="
puts "Service: #{ method } "
puts "Method: #{ service_method } "
puts "Arguments: #{ service_args. map ( & :to_hash ). to_json } "
puts "Timestamp: #{ Time . now . iso8601 } "
begin
result = super (service_method, * service_args)
puts "Result: Success"
result
rescue => e
puts "Result: Error - #{ e. class } "
puts "Error Details: #{ e. message } "
raise
end
end
service
end
end
# Usage
base_client = Zitadel :: Client :: Zitadel . with_access_token (
"https://example.us1.zitadel.cloud" ,
ENV [ 'ZITADEL_TOKEN' ]
) { | c | c. debugging = true }
client = DebugWrapper . new (base_client)
client. users . list_users (
Zitadel :: Client :: UserServiceListUsersRequest . new ( limit: 5 )
)
Getting Help
If you’re still experiencing issues after enabling debug mode:
Collect debug logs showing the full request/response
Check the Zitadel Community Discord for support
Report issues on GitHub with:
Ruby version
SDK version
Debug logs (redact sensitive data)
Steps to reproduce
See the README section on debugging: /workspace/source/README.md:194-206
Next Steps