rapitapir 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +57 -0
- data/CHANGELOG.md +94 -0
- data/CLEANUP_SUMMARY.md +155 -0
- data/CONTRIBUTING.md +280 -0
- data/LICENSE +21 -0
- data/README.md +485 -0
- data/debug_hash.rb +20 -0
- data/docs/EXTENSION_COMPARISON.md +388 -0
- data/docs/SINATRA_EXTENSION.md +467 -0
- data/docs/archive/PHASE_1_2_COMPLETE.md +77 -0
- data/docs/archive/PHASE_1_3_COMPLETE.md +152 -0
- data/docs/archive/PHASE_2_1_OBSERVABILITY_COMPLETED.md +203 -0
- data/docs/archive/PHASE_2_SUMMARY.md +209 -0
- data/docs/archive/REFACTORING_SUMMARY.md +184 -0
- data/docs/archive/phase_1_3_plan.md +136 -0
- data/docs/archive/sinatra_extension_summary.md +188 -0
- data/docs/archive/sinatra_working_solution.md +113 -0
- data/docs/archive/typescript-client-generator-summary.md +259 -0
- data/docs/auto-derivation.md +146 -0
- data/docs/blueprint.md +1091 -0
- data/docs/endpoint-definition.md +211 -0
- data/docs/github_pages_fix.md +52 -0
- data/docs/github_pages_setup.md +49 -0
- data/docs/implementation-status.md +357 -0
- data/docs/observability.md +647 -0
- data/docs/phase3-plan.md +108 -0
- data/docs/sinatra_rapitapir.md +87 -0
- data/docs/type_shortcuts.md +146 -0
- data/examples/README_ENTERPRISE.md +202 -0
- data/examples/authentication_example.rb +192 -0
- data/examples/auto_derivation_ruby_friendly.rb +163 -0
- data/examples/cli/user_api_endpoints.rb +56 -0
- data/examples/client/typescript_client_example.rb +102 -0
- data/examples/client/user-api-client.ts +193 -0
- data/examples/demo_api.rb +41 -0
- data/examples/docs/documentation_example.rb +112 -0
- data/examples/docs/user-api-docs.html +789 -0
- data/examples/docs/user-api-docs.md +403 -0
- data/examples/enhanced_auto_derivation_test.rb +83 -0
- data/examples/enterprise_extension_demo.rb +417 -0
- data/examples/enterprise_rapitapir_api.rb +662 -0
- data/examples/getting_started_extension.rb +218 -0
- data/examples/hello_world.rb +74 -0
- data/examples/oauth2/.env.example +19 -0
- data/examples/oauth2/README.md +205 -0
- data/examples/oauth2/generic_oauth2_api.rb +226 -0
- data/examples/oauth2/get_token.rb +72 -0
- data/examples/oauth2/songs_api_with_auth0.rb +320 -0
- data/examples/oauth2/test_api.sh +16 -0
- data/examples/oauth2/test_songs_api.sh +110 -0
- data/examples/observability/.env.example +35 -0
- data/examples/observability/README.md +230 -0
- data/examples/observability/README_HONEYCOMB.md +332 -0
- data/examples/observability/advanced_setup.rb +384 -0
- data/examples/observability/basic_setup.rb +192 -0
- data/examples/observability/complete_test.rb +121 -0
- data/examples/observability/honeycomb_example.rb +523 -0
- data/examples/observability/honeycomb_rapitapir_clean.rb +488 -0
- data/examples/observability/honeycomb_rapitapir_example.rb +523 -0
- data/examples/observability/honeycomb_working_example.rb +489 -0
- data/examples/observability/quick_test.rb +78 -0
- data/examples/observability/simple_test.rb +14 -0
- data/examples/observability/test_honeycomb_demo.rb +354 -0
- data/examples/observability/test_live_honeycomb.rb +111 -0
- data/examples/observability/test_validation.rb +78 -0
- data/examples/observability/test_working_validation.rb +66 -0
- data/examples/openapi/user_api_schema.rb +132 -0
- data/examples/production_ready_example.rb +105 -0
- data/examples/rails/users_controller.rb +146 -0
- data/examples/readme/basic_sinatra_example.rb +128 -0
- data/examples/server/user_api.rb +179 -0
- data/examples/simple_auto_derivation_demo.rb +44 -0
- data/examples/simple_demo_api.rb +18 -0
- data/examples/sinatra/user_app.rb +127 -0
- data/examples/t_shortcut_demo.rb +59 -0
- data/examples/user_api.rb +190 -0
- data/examples/working_getting_started.rb +184 -0
- data/examples/working_simple_example.rb +195 -0
- data/lib/rapitapir/auth/configuration.rb +129 -0
- data/lib/rapitapir/auth/context.rb +122 -0
- data/lib/rapitapir/auth/errors.rb +104 -0
- data/lib/rapitapir/auth/middleware.rb +324 -0
- data/lib/rapitapir/auth/oauth2.rb +350 -0
- data/lib/rapitapir/auth/schemes.rb +420 -0
- data/lib/rapitapir/auth.rb +113 -0
- data/lib/rapitapir/cli/command.rb +535 -0
- data/lib/rapitapir/cli/server.rb +243 -0
- data/lib/rapitapir/cli/validator.rb +373 -0
- data/lib/rapitapir/client/generator_base.rb +272 -0
- data/lib/rapitapir/client/typescript_generator.rb +350 -0
- data/lib/rapitapir/core/endpoint.rb +158 -0
- data/lib/rapitapir/core/enhanced_endpoint.rb +235 -0
- data/lib/rapitapir/core/input.rb +182 -0
- data/lib/rapitapir/core/output.rb +164 -0
- data/lib/rapitapir/core/request.rb +19 -0
- data/lib/rapitapir/core/response.rb +17 -0
- data/lib/rapitapir/docs/html_generator.rb +780 -0
- data/lib/rapitapir/docs/markdown_generator.rb +464 -0
- data/lib/rapitapir/dsl/endpoint_dsl.rb +116 -0
- data/lib/rapitapir/dsl/enhanced_endpoint_dsl.rb +62 -0
- data/lib/rapitapir/dsl/enhanced_input.rb +73 -0
- data/lib/rapitapir/dsl/enhanced_output.rb +63 -0
- data/lib/rapitapir/dsl/enhanced_structures.rb +393 -0
- data/lib/rapitapir/dsl/fluent_dsl.rb +72 -0
- data/lib/rapitapir/dsl/fluent_endpoint_builder.rb +316 -0
- data/lib/rapitapir/dsl/http_verbs.rb +77 -0
- data/lib/rapitapir/dsl/input_methods.rb +47 -0
- data/lib/rapitapir/dsl/observability_methods.rb +81 -0
- data/lib/rapitapir/dsl/output_methods.rb +43 -0
- data/lib/rapitapir/dsl/type_resolution.rb +43 -0
- data/lib/rapitapir/observability/configuration.rb +108 -0
- data/lib/rapitapir/observability/health_check.rb +236 -0
- data/lib/rapitapir/observability/logging.rb +270 -0
- data/lib/rapitapir/observability/metrics.rb +203 -0
- data/lib/rapitapir/observability/middleware.rb +243 -0
- data/lib/rapitapir/observability/tracing.rb +143 -0
- data/lib/rapitapir/observability.rb +28 -0
- data/lib/rapitapir/openapi/schema_generator.rb +403 -0
- data/lib/rapitapir/schema.rb +136 -0
- data/lib/rapitapir/server/enhanced_rack_adapter.rb +379 -0
- data/lib/rapitapir/server/middleware.rb +120 -0
- data/lib/rapitapir/server/path_matcher.rb +45 -0
- data/lib/rapitapir/server/rack_adapter.rb +215 -0
- data/lib/rapitapir/server/rails_adapter.rb +17 -0
- data/lib/rapitapir/server/rails_adapter_class.rb +53 -0
- data/lib/rapitapir/server/rails_controller.rb +72 -0
- data/lib/rapitapir/server/rails_input_processor.rb +73 -0
- data/lib/rapitapir/server/rails_response_handler.rb +29 -0
- data/lib/rapitapir/server/sinatra_adapter.rb +200 -0
- data/lib/rapitapir/server/sinatra_integration.rb +93 -0
- data/lib/rapitapir/sinatra/configuration.rb +91 -0
- data/lib/rapitapir/sinatra/extension.rb +214 -0
- data/lib/rapitapir/sinatra/oauth2_helpers.rb +236 -0
- data/lib/rapitapir/sinatra/resource_builder.rb +152 -0
- data/lib/rapitapir/sinatra/swagger_ui_generator.rb +166 -0
- data/lib/rapitapir/sinatra_rapitapir.rb +40 -0
- data/lib/rapitapir/types/array.rb +163 -0
- data/lib/rapitapir/types/auto_derivation.rb +265 -0
- data/lib/rapitapir/types/base.rb +146 -0
- data/lib/rapitapir/types/boolean.rb +46 -0
- data/lib/rapitapir/types/date.rb +92 -0
- data/lib/rapitapir/types/datetime.rb +98 -0
- data/lib/rapitapir/types/email.rb +32 -0
- data/lib/rapitapir/types/float.rb +134 -0
- data/lib/rapitapir/types/hash.rb +161 -0
- data/lib/rapitapir/types/integer.rb +143 -0
- data/lib/rapitapir/types/object.rb +156 -0
- data/lib/rapitapir/types/optional.rb +65 -0
- data/lib/rapitapir/types/string.rb +185 -0
- data/lib/rapitapir/types/uuid.rb +32 -0
- data/lib/rapitapir/types.rb +155 -0
- data/lib/rapitapir/version.rb +5 -0
- data/lib/rapitapir.rb +173 -0
- data/rapitapir.gemspec +66 -0
- metadata +387 -0
@@ -0,0 +1,110 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
# Test script for Songs API with Auth0
|
4
|
+
echo "π΅ Testing Songs API with Auth0 OAuth2..."
|
5
|
+
|
6
|
+
# Colors for output
|
7
|
+
RED='\033[0;31m'
|
8
|
+
GREEN='\033[0;32m'
|
9
|
+
YELLOW='\033[1;33m'
|
10
|
+
NC='\033[0m' # No Color
|
11
|
+
|
12
|
+
# Get a fresh token
|
13
|
+
echo "π Getting Auth0 token..."
|
14
|
+
TOKEN=$(ruby get_token.rb 2>/dev/null | grep "π Full token:" -A1 | tail -1)
|
15
|
+
|
16
|
+
if [ -z "$TOKEN" ]; then
|
17
|
+
echo -e "${RED}β Failed to get token${NC}"
|
18
|
+
exit 1
|
19
|
+
fi
|
20
|
+
|
21
|
+
echo -e "${GREEN}β
Token obtained${NC}"
|
22
|
+
|
23
|
+
# Test 1: Public endpoint (should work without auth)
|
24
|
+
echo
|
25
|
+
echo "π§ͺ Test 1: GET /songs (public endpoint)"
|
26
|
+
RESPONSE=$(curl -s -w "%{http_code}" http://localhost:4567/songs)
|
27
|
+
HTTP_CODE=${RESPONSE: -3}
|
28
|
+
BODY=${RESPONSE%???}
|
29
|
+
|
30
|
+
if [ "$HTTP_CODE" = "200" ]; then
|
31
|
+
echo -e "${GREEN}β
Public endpoint accessible${NC}"
|
32
|
+
echo "π Response: $BODY"
|
33
|
+
else
|
34
|
+
echo -e "${RED}β Public endpoint failed (HTTP $HTTP_CODE)${NC}"
|
35
|
+
echo "π Response: $BODY"
|
36
|
+
fi
|
37
|
+
|
38
|
+
# Test 2: Protected endpoint without auth (should fail)
|
39
|
+
echo
|
40
|
+
echo "π§ͺ Test 2: POST /songs without authentication"
|
41
|
+
RESPONSE=$(curl -s -w "%{http_code}" -X POST http://localhost:4567/songs \
|
42
|
+
-H "Content-Type: application/json" \
|
43
|
+
-d '{"name":"Test Song","url":"https://example.com/test"}')
|
44
|
+
HTTP_CODE=${RESPONSE: -3}
|
45
|
+
BODY=${RESPONSE%???}
|
46
|
+
|
47
|
+
if [ "$HTTP_CODE" = "401" ]; then
|
48
|
+
echo -e "${GREEN}β
Authentication required (as expected)${NC}"
|
49
|
+
echo "π Response: $BODY"
|
50
|
+
else
|
51
|
+
echo -e "${RED}β Should require authentication (HTTP $HTTP_CODE)${NC}"
|
52
|
+
echo "π Response: $BODY"
|
53
|
+
fi
|
54
|
+
|
55
|
+
# Test 3: Protected endpoint with valid token (should work)
|
56
|
+
echo
|
57
|
+
echo "π§ͺ Test 3: POST /songs with valid Auth0 token"
|
58
|
+
RESPONSE=$(curl -s -w "%{http_code}" -X POST http://localhost:4567/songs \
|
59
|
+
-H "Content-Type: application/json" \
|
60
|
+
-H "Authorization: Bearer $TOKEN" \
|
61
|
+
-d '{"name":"Test Song from API","url":"https://example.com/test-api"}')
|
62
|
+
HTTP_CODE=${RESPONSE: -3}
|
63
|
+
BODY=${RESPONSE%???}
|
64
|
+
|
65
|
+
if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "201" ]; then
|
66
|
+
echo -e "${GREEN}β
Song created successfully${NC}"
|
67
|
+
echo "π Response: $BODY"
|
68
|
+
else
|
69
|
+
echo -e "${RED}β Song creation failed (HTTP $HTTP_CODE)${NC}"
|
70
|
+
echo "π Response: $BODY"
|
71
|
+
fi
|
72
|
+
|
73
|
+
# Test 4: Verify the song was added
|
74
|
+
echo
|
75
|
+
echo "π§ͺ Test 4: GET /songs (verify new song added)"
|
76
|
+
RESPONSE=$(curl -s -w "%{http_code}" http://localhost:4567/songs)
|
77
|
+
HTTP_CODE=${RESPONSE: -3}
|
78
|
+
BODY=${RESPONSE%???}
|
79
|
+
|
80
|
+
if [ "$HTTP_CODE" = "200" ]; then
|
81
|
+
echo -e "${GREEN}β
Songs list retrieved${NC}"
|
82
|
+
echo "π Response: $BODY"
|
83
|
+
|
84
|
+
# Check if our test song appears in the list
|
85
|
+
if [[ $BODY == *"Test Song from API"* ]]; then
|
86
|
+
echo -e "${GREEN}π Test song found in the list!${NC}"
|
87
|
+
else
|
88
|
+
echo -e "${YELLOW}β οΈ Test song not found in the list${NC}"
|
89
|
+
fi
|
90
|
+
else
|
91
|
+
echo -e "${RED}β Failed to retrieve songs list (HTTP $HTTP_CODE)${NC}"
|
92
|
+
fi
|
93
|
+
|
94
|
+
# Test 5: Test /me endpoint
|
95
|
+
echo
|
96
|
+
echo "π§ͺ Test 5: GET /me (user info with token)"
|
97
|
+
RESPONSE=$(curl -s -w "%{http_code}" -H "Authorization: Bearer $TOKEN" http://localhost:4567/me)
|
98
|
+
HTTP_CODE=${RESPONSE: -3}
|
99
|
+
BODY=${RESPONSE%???}
|
100
|
+
|
101
|
+
if [ "$HTTP_CODE" = "200" ]; then
|
102
|
+
echo -e "${GREEN}β
User info retrieved${NC}"
|
103
|
+
echo "π Response: $BODY"
|
104
|
+
else
|
105
|
+
echo -e "${RED}β User info failed (HTTP $HTTP_CODE)${NC}"
|
106
|
+
echo "π Response: $BODY"
|
107
|
+
fi
|
108
|
+
|
109
|
+
echo
|
110
|
+
echo "π΅ Songs API testing complete!"
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# Honeycomb.io Configuration for RapiTapir Demo
|
2
|
+
# Copy this file to .env and update with your actual Honeycomb API key
|
3
|
+
|
4
|
+
# Required: Your Honeycomb API key (get from https://ui.honeycomb.io/account)
|
5
|
+
# Replace 'your-honeycomb-api-key-here' with your actual API key
|
6
|
+
OTEL_EXPORTER_OTLP_HEADERS=x-honeycomb-team=your-honeycomb-api-key-here
|
7
|
+
|
8
|
+
# Honeycomb endpoint (US instance - change to EU if needed)
|
9
|
+
OTEL_EXPORTER_OTLP_ENDPOINT=https://api.honeycomb.io
|
10
|
+
|
11
|
+
# For EU instance, uncomment the line below instead:
|
12
|
+
# OTEL_EXPORTER_OTLP_ENDPOINT=https://api.eu1.honeycomb.io
|
13
|
+
|
14
|
+
# Service configuration
|
15
|
+
OTEL_SERVICE_NAME=rapitapir-demo
|
16
|
+
OTEL_SERVICE_VERSION=1.0.0
|
17
|
+
|
18
|
+
# Resource attributes (optional but recommended)
|
19
|
+
OTEL_RESOURCE_ATTRIBUTES=service.name=rapitapir-demo,service.version=1.0.0,deployment.environment=development
|
20
|
+
|
21
|
+
# Sampling configuration (optional - keep 100% for demo, adjust for production)
|
22
|
+
# OTEL_TRACES_SAMPLER=traceidratio
|
23
|
+
# OTEL_TRACES_SAMPLER_ARG=0.1
|
24
|
+
# OTEL_RESOURCE_ATTRIBUTES=SampleRate=10
|
25
|
+
|
26
|
+
# Logging level for OpenTelemetry (optional)
|
27
|
+
OTEL_LOG_LEVEL=info
|
28
|
+
|
29
|
+
# Enable/disable specific signals (optional)
|
30
|
+
OTEL_TRACES_EXPORTER=otlp
|
31
|
+
OTEL_METRICS_EXPORTER=otlp
|
32
|
+
OTEL_LOGS_EXPORTER=otlp
|
33
|
+
|
34
|
+
# Additional debugging (optional - set to true for troubleshooting)
|
35
|
+
OTEL_RUBY_TRACES_EXPORTER_DEBUG=false
|
@@ -0,0 +1,230 @@
|
|
1
|
+
# RapiTapir Honeycomb.io Observability Example
|
2
|
+
|
3
|
+
This example demonstrates how to integrate Honeycomb.io observability with a Ruby API using OpenTelemetry and RapiTapir patterns.
|
4
|
+
|
5
|
+
## Prerequisites
|
6
|
+
|
7
|
+
1. **Honeycomb.io Account**: Sign up at [honeycomb.io](https://honeycomb.io)
|
8
|
+
2. **API Key**: Get your API key from Honeycomb settings
|
9
|
+
3. **Ruby 3.1+**: Ensure you have Ruby installed
|
10
|
+
4. **Bundler**: For managing dependencies
|
11
|
+
|
12
|
+
## Setup
|
13
|
+
|
14
|
+
### 1. Install Dependencies
|
15
|
+
|
16
|
+
From the project root directory:
|
17
|
+
|
18
|
+
```bash
|
19
|
+
cd /path/to/ruby-tapir
|
20
|
+
bundle install
|
21
|
+
```
|
22
|
+
|
23
|
+
### 2. Configure Environment
|
24
|
+
|
25
|
+
Create a `.env` file in the `examples/observability/` directory:
|
26
|
+
|
27
|
+
```bash
|
28
|
+
# Honeycomb.io Configuration
|
29
|
+
HONEYCOMB_API_KEY=your_api_key_here
|
30
|
+
OTEL_EXPORTER_OTLP_ENDPOINT=https://api.honeycomb.io
|
31
|
+
OTEL_EXPORTER_OTLP_HEADERS=x-honeycomb-team=your_api_key_here
|
32
|
+
OTEL_SERVICE_NAME=rapitapir-demo
|
33
|
+
OTEL_RESOURCE_ATTRIBUTES=service.name=rapitapir-demo,service.version=1.0.0
|
34
|
+
```
|
35
|
+
|
36
|
+
Replace `your_api_key_here` with your actual Honeycomb API key.
|
37
|
+
|
38
|
+
### 3. Run the Server
|
39
|
+
|
40
|
+
From the project root directory:
|
41
|
+
|
42
|
+
```bash
|
43
|
+
bundle exec ruby examples/observability/honeycomb_working_example.rb
|
44
|
+
```
|
45
|
+
|
46
|
+
The server will start on `http://localhost:4567` and display available endpoints.
|
47
|
+
|
48
|
+
## API Endpoints
|
49
|
+
|
50
|
+
### Health Check
|
51
|
+
```bash
|
52
|
+
curl http://localhost:4567/health
|
53
|
+
```
|
54
|
+
|
55
|
+
### User Management
|
56
|
+
```bash
|
57
|
+
# List users (with pagination and filtering)
|
58
|
+
curl "http://localhost:4567/users?page=1&limit=10&department=engineering"
|
59
|
+
|
60
|
+
# Create user
|
61
|
+
curl -X POST http://localhost:4567/users \
|
62
|
+
-H 'Content-Type: application/json' \
|
63
|
+
-d '{"name":"Alice Johnson","email":"alice@example.com","age":28,"department":"engineering"}'
|
64
|
+
|
65
|
+
# Get user by ID
|
66
|
+
curl http://localhost:4567/users/USER_ID
|
67
|
+
|
68
|
+
# Update user
|
69
|
+
curl -X PUT http://localhost:4567/users/USER_ID \
|
70
|
+
-H 'Content-Type: application/json' \
|
71
|
+
-d '{"name":"Alice Johnson-Smith","department":"product"}'
|
72
|
+
|
73
|
+
# Delete user
|
74
|
+
curl -X DELETE http://localhost:4567/users/USER_ID
|
75
|
+
```
|
76
|
+
|
77
|
+
### Analytics
|
78
|
+
```bash
|
79
|
+
curl http://localhost:4567/analytics/department-stats
|
80
|
+
```
|
81
|
+
|
82
|
+
## Testing the Integration
|
83
|
+
|
84
|
+
Run the comprehensive test suite:
|
85
|
+
|
86
|
+
```bash
|
87
|
+
ruby examples/observability/complete_test.rb
|
88
|
+
```
|
89
|
+
|
90
|
+
This will:
|
91
|
+
1. Start the server
|
92
|
+
2. Test all endpoints
|
93
|
+
3. Generate sample data
|
94
|
+
4. Create traces in Honeycomb
|
95
|
+
5. Clean up the server
|
96
|
+
|
97
|
+
## Observability Features
|
98
|
+
|
99
|
+
### Automatic Instrumentation
|
100
|
+
- **HTTP Requests**: All incoming requests are automatically traced
|
101
|
+
- **Database Queries**: Simulated database operations with realistic timings
|
102
|
+
- **External API Calls**: Net::HTTP requests are automatically instrumented
|
103
|
+
- **Error Tracking**: Exceptions and errors are captured in spans
|
104
|
+
|
105
|
+
### Custom Business Context
|
106
|
+
Each request includes business-relevant attributes:
|
107
|
+
- User information (ID, department, etc.)
|
108
|
+
- Request metadata (pagination, filters)
|
109
|
+
- Performance metrics (database response times)
|
110
|
+
- Error context and stack traces
|
111
|
+
|
112
|
+
### Span Hierarchy
|
113
|
+
```
|
114
|
+
HTTP Request Span (e.g., "POST /users")
|
115
|
+
βββ Database Operation Span (e.g., "users.create")
|
116
|
+
βββ Validation Span (e.g., "validate_user_data")
|
117
|
+
βββ Business Logic Span (e.g., "process_user_creation")
|
118
|
+
```
|
119
|
+
|
120
|
+
### Custom Metrics
|
121
|
+
- Request duration and status codes
|
122
|
+
- Database operation timing
|
123
|
+
- User activity by department
|
124
|
+
- Error rates and types
|
125
|
+
|
126
|
+
## Viewing Traces in Honeycomb
|
127
|
+
|
128
|
+
1. Log into your Honeycomb.io dashboard
|
129
|
+
2. Navigate to the `rapitapir-demo` dataset
|
130
|
+
3. You'll see traces for:
|
131
|
+
- HTTP requests with full request/response context
|
132
|
+
- Database operations with query timing
|
133
|
+
- Business operations with custom attributes
|
134
|
+
- Error traces with stack traces and context
|
135
|
+
|
136
|
+
### Useful Queries
|
137
|
+
|
138
|
+
**Find slow requests:**
|
139
|
+
```
|
140
|
+
duration_ms > 1000
|
141
|
+
```
|
142
|
+
|
143
|
+
**Group by endpoint:**
|
144
|
+
```
|
145
|
+
GROUP BY http.route
|
146
|
+
```
|
147
|
+
|
148
|
+
**Filter by department:**
|
149
|
+
```
|
150
|
+
user.department = "engineering"
|
151
|
+
```
|
152
|
+
|
153
|
+
**Find errors:**
|
154
|
+
```
|
155
|
+
status_code >= 400 OR error = true
|
156
|
+
```
|
157
|
+
|
158
|
+
## Architecture
|
159
|
+
|
160
|
+
### OpenTelemetry Integration
|
161
|
+
- **SDK**: Full OpenTelemetry SDK with OTLP exporter
|
162
|
+
- **Auto-instrumentation**: Rack, Sinatra, and Net::HTTP
|
163
|
+
- **Custom spans**: Business logic and database operations
|
164
|
+
- **Baggage**: Cross-service context propagation
|
165
|
+
|
166
|
+
### RapiTapir Extension
|
167
|
+
The `RapiTapirObservability` module provides:
|
168
|
+
- Automatic span creation for route handlers
|
169
|
+
- Business context extraction from requests
|
170
|
+
- Error handling and exception tracking
|
171
|
+
- Performance monitoring helpers
|
172
|
+
|
173
|
+
### Trace Context
|
174
|
+
Each trace includes:
|
175
|
+
- **Service metadata**: Name, version, environment
|
176
|
+
- **Request context**: Method, path, query parameters, headers
|
177
|
+
- **User context**: ID, department, business attributes
|
178
|
+
- **Performance data**: Timing, resource usage, dependencies
|
179
|
+
- **Error information**: Exception details, stack traces
|
180
|
+
|
181
|
+
## Best Practices
|
182
|
+
|
183
|
+
1. **Meaningful Span Names**: Use business-relevant names like "create_user" instead of generic ones
|
184
|
+
2. **Rich Attributes**: Include business context that helps with debugging
|
185
|
+
3. **Error Handling**: Always capture errors with full context
|
186
|
+
4. **Performance Monitoring**: Track both technical and business metrics
|
187
|
+
5. **Security**: Never log sensitive data like passwords or tokens
|
188
|
+
|
189
|
+
## Production Considerations
|
190
|
+
|
191
|
+
- **Sampling**: Configure sampling rates for high-traffic applications
|
192
|
+
- **Resource Limits**: Set memory and CPU limits for the OpenTelemetry SDK
|
193
|
+
- **Security**: Use environment variables for API keys, never commit them
|
194
|
+
- **Monitoring**: Monitor the observability system itself for health
|
195
|
+
- **Privacy**: Ensure compliance with data protection regulations
|
196
|
+
|
197
|
+
## Troubleshooting
|
198
|
+
|
199
|
+
### Server Won't Start
|
200
|
+
```bash
|
201
|
+
# Check if gems are installed
|
202
|
+
bundle install
|
203
|
+
|
204
|
+
# Run from project root with bundle exec
|
205
|
+
bundle exec ruby examples/observability/honeycomb_working_example.rb
|
206
|
+
```
|
207
|
+
|
208
|
+
### No Traces in Honeycomb
|
209
|
+
1. Verify API key is correct
|
210
|
+
2. Check environment variables are loaded
|
211
|
+
3. Confirm network connectivity to api.honeycomb.io
|
212
|
+
4. Look for OpenTelemetry initialization messages in logs
|
213
|
+
|
214
|
+
### Port Already in Use
|
215
|
+
```bash
|
216
|
+
# Kill any existing processes on port 4567
|
217
|
+
lsof -ti:4567 | xargs kill -9
|
218
|
+
```
|
219
|
+
|
220
|
+
## Contributing
|
221
|
+
|
222
|
+
Feel free to extend this example with:
|
223
|
+
- Additional instrumentation for your specific use cases
|
224
|
+
- Custom metrics and dashboards
|
225
|
+
- Integration with other observability tools
|
226
|
+
- Performance optimizations
|
227
|
+
|
228
|
+
---
|
229
|
+
|
230
|
+
This example demonstrates production-ready observability integration that you can adapt for your own RapiTapir applications. The patterns shown here scale from development to production environments.
|
@@ -0,0 +1,332 @@
|
|
1
|
+
# π― RapiTapir Honeycomb.io Observability Example
|
2
|
+
|
3
|
+
This example demonstrates how to integrate **RapiTapir** with **Honeycomb.io** using **OpenTelemetry** for comprehensive observability, including distributed tracing, custom spans, baggage propagation, and business metrics.
|
4
|
+
|
5
|
+
## π Features Demonstrated
|
6
|
+
|
7
|
+
- **OpenTelemetry Integration**: Full SDK setup with Honeycomb.io OTLP exporter
|
8
|
+
- **Automatic Instrumentation**: Sinatra, Rack, HTTP, and JSON instrumentation
|
9
|
+
- **Custom Spans**: Business logic tracing with detailed attributes
|
10
|
+
- **Baggage Propagation**: Context sharing across spans
|
11
|
+
- **Error Handling**: Comprehensive error tracing and status reporting
|
12
|
+
- **Health Checks**: Traced health check endpoints
|
13
|
+
- **Business Analytics**: Custom spans for complex operations
|
14
|
+
- **Performance Monitoring**: Request timing and resource usage
|
15
|
+
|
16
|
+
## π Prerequisites
|
17
|
+
|
18
|
+
1. **Ruby 3.2+** installed
|
19
|
+
2. **Honeycomb.io account** (free account available at [honeycomb.io](https://ui.honeycomb.io/signup))
|
20
|
+
3. **Honeycomb API Key** with "Can create datasets" permission
|
21
|
+
|
22
|
+
## π§ Setup Instructions
|
23
|
+
|
24
|
+
### 1. Install Dependencies
|
25
|
+
|
26
|
+
First, install the required OpenTelemetry gems. The main Gemfile already includes them:
|
27
|
+
|
28
|
+
```bash
|
29
|
+
bundle install
|
30
|
+
```
|
31
|
+
|
32
|
+
### 2. Get Your Honeycomb API Key
|
33
|
+
|
34
|
+
1. Sign up for a free Honeycomb account at [honeycomb.io](https://ui.honeycomb.io/signup)
|
35
|
+
2. Go to **Environment Settings** β **API Keys**
|
36
|
+
3. Create a new API key with **"Can create datasets"** checked
|
37
|
+
4. Copy your API key (you won't be able to see it again!)
|
38
|
+
|
39
|
+
### 3. Configure Environment Variables
|
40
|
+
|
41
|
+
Copy the example environment file and update it with your API key:
|
42
|
+
|
43
|
+
```bash
|
44
|
+
cd examples/observability
|
45
|
+
cp .env.example .env
|
46
|
+
```
|
47
|
+
|
48
|
+
Edit `.env` and replace `your-honeycomb-api-key-here` with your actual API key:
|
49
|
+
|
50
|
+
```bash
|
51
|
+
# Required: Your Honeycomb API key
|
52
|
+
OTEL_EXPORTER_OTLP_HEADERS=x-honeycomb-team=YOUR_ACTUAL_API_KEY_HERE
|
53
|
+
|
54
|
+
# Honeycomb endpoint (US instance)
|
55
|
+
OTEL_EXPORTER_OTLP_ENDPOINT=https://api.honeycomb.io
|
56
|
+
|
57
|
+
# Service configuration
|
58
|
+
OTEL_SERVICE_NAME=rapitapir-demo
|
59
|
+
OTEL_SERVICE_VERSION=1.0.0
|
60
|
+
```
|
61
|
+
|
62
|
+
For EU instance users, use:
|
63
|
+
```bash
|
64
|
+
OTEL_EXPORTER_OTLP_ENDPOINT=https://api.eu1.honeycomb.io
|
65
|
+
```
|
66
|
+
|
67
|
+
### 4. Load Environment Variables
|
68
|
+
|
69
|
+
```bash
|
70
|
+
# Install dotenv if not already installed
|
71
|
+
gem install dotenv
|
72
|
+
|
73
|
+
# Load the environment
|
74
|
+
source .env
|
75
|
+
# Or use dotenv if you prefer
|
76
|
+
dotenv -f .env
|
77
|
+
```
|
78
|
+
|
79
|
+
## πββοΈ Running the Demo
|
80
|
+
|
81
|
+
### Start the Server
|
82
|
+
|
83
|
+
```bash
|
84
|
+
cd examples/observability
|
85
|
+
ruby honeycomb_example.rb
|
86
|
+
```
|
87
|
+
|
88
|
+
The server will start on `http://localhost:4567` and display:
|
89
|
+
|
90
|
+
```
|
91
|
+
π― Starting RapiTapir Demo API with Honeycomb.io Observability
|
92
|
+
π Traces will be sent to: https://api.honeycomb.io
|
93
|
+
π§ Service Name: rapitapir-demo
|
94
|
+
|
95
|
+
π Available endpoints:
|
96
|
+
GET /health - Health check
|
97
|
+
GET /users - List users
|
98
|
+
POST /users - Create user
|
99
|
+
GET /users/:id - Get user by ID
|
100
|
+
PUT /users/:id - Update user
|
101
|
+
DELETE /users/:id - Delete user
|
102
|
+
GET /analytics/department-stats - Department analytics
|
103
|
+
```
|
104
|
+
|
105
|
+
### Test the API
|
106
|
+
|
107
|
+
Generate some traces by making requests:
|
108
|
+
|
109
|
+
```bash
|
110
|
+
# Health check
|
111
|
+
curl http://localhost:4567/health
|
112
|
+
|
113
|
+
# Create users
|
114
|
+
curl -X POST http://localhost:4567/users \
|
115
|
+
-H 'Content-Type: application/json' \
|
116
|
+
-d '{
|
117
|
+
"name": "Alice Engineer",
|
118
|
+
"email": "alice@example.com",
|
119
|
+
"age": 28,
|
120
|
+
"department": "engineering"
|
121
|
+
}'
|
122
|
+
|
123
|
+
curl -X POST http://localhost:4567/users \
|
124
|
+
-H 'Content-Type: application/json' \
|
125
|
+
-d '{
|
126
|
+
"name": "Bob Sales",
|
127
|
+
"email": "bob@example.com",
|
128
|
+
"age": 32,
|
129
|
+
"department": "sales"
|
130
|
+
}'
|
131
|
+
|
132
|
+
# List users with filtering and pagination
|
133
|
+
curl "http://localhost:4567/users"
|
134
|
+
curl "http://localhost:4567/users?department=engineering&page=1&limit=5"
|
135
|
+
|
136
|
+
# Get analytics (complex operation with multiple spans)
|
137
|
+
curl http://localhost:4567/analytics/department-stats
|
138
|
+
|
139
|
+
# Test error handling
|
140
|
+
curl -X POST http://localhost:4567/users \
|
141
|
+
-H 'Content-Type: application/json' \
|
142
|
+
-d '{"invalid": "data"}'
|
143
|
+
```
|
144
|
+
|
145
|
+
## π Viewing Data in Honeycomb
|
146
|
+
|
147
|
+
1. Go to [Honeycomb.io](https://ui.honeycomb.io/) and log in
|
148
|
+
2. You should see a new dataset called **"rapitapir-demo"** (or whatever you set as `OTEL_SERVICE_NAME`)
|
149
|
+
3. Click on the dataset to start exploring your traces
|
150
|
+
|
151
|
+
### Key Honeycomb Features to Explore
|
152
|
+
|
153
|
+
#### 1. **Traces View**
|
154
|
+
- See complete request flows from HTTP request to business logic
|
155
|
+
- Identify performance bottlenecks and slow operations
|
156
|
+
- Visualize parent-child span relationships
|
157
|
+
|
158
|
+
#### 2. **Useful Queries to Try**
|
159
|
+
|
160
|
+
```sql
|
161
|
+
-- Find slow requests
|
162
|
+
WHERE duration_ms > 100
|
163
|
+
|
164
|
+
-- Find errors by type
|
165
|
+
WHERE error.type EXISTS
|
166
|
+
|
167
|
+
-- Analyze by department
|
168
|
+
GROUP BY user.department
|
169
|
+
|
170
|
+
-- Find database operations
|
171
|
+
WHERE db.operation EXISTS
|
172
|
+
|
173
|
+
-- Look at business operations
|
174
|
+
WHERE business.operation = "create_user"
|
175
|
+
|
176
|
+
-- Health check performance
|
177
|
+
WHERE business.operation = "health_check"
|
178
|
+
```
|
179
|
+
|
180
|
+
#### 3. **Custom Attributes Added**
|
181
|
+
|
182
|
+
The example adds rich context to spans:
|
183
|
+
|
184
|
+
**HTTP Attributes:**
|
185
|
+
- `http.method`, `http.url`, `http.status_code`
|
186
|
+
- `http.user_agent`, `http.response_size`
|
187
|
+
|
188
|
+
**Business Attributes:**
|
189
|
+
- `business.operation` (create_user, list_users, etc.)
|
190
|
+
- `business.entity` (user, analytics)
|
191
|
+
- `user.id`, `user.department`
|
192
|
+
|
193
|
+
**Database Simulation:**
|
194
|
+
- `db.operation` (SELECT, INSERT, UPDATE, DELETE)
|
195
|
+
- `db.table` (users)
|
196
|
+
- Query timing and performance
|
197
|
+
|
198
|
+
**Custom Metrics:**
|
199
|
+
- `duration_ms` - Request/operation timing
|
200
|
+
- `result.count` - Number of items returned
|
201
|
+
- `pagination.*` - Pagination metadata
|
202
|
+
|
203
|
+
#### 4. **Baggage Propagation**
|
204
|
+
|
205
|
+
The example demonstrates OpenTelemetry baggage:
|
206
|
+
- `request.id` - Unique identifier for each request
|
207
|
+
- `service.name` - Service context
|
208
|
+
- `user.created` - Business event flags
|
209
|
+
|
210
|
+
## π§ Advanced Configuration
|
211
|
+
|
212
|
+
### Sampling for Production
|
213
|
+
|
214
|
+
For high-traffic production environments, enable sampling:
|
215
|
+
|
216
|
+
```bash
|
217
|
+
# Keep 10% of traces (1/10)
|
218
|
+
OTEL_TRACES_SAMPLER=traceidratio
|
219
|
+
OTEL_TRACES_SAMPLER_ARG=0.1
|
220
|
+
OTEL_RESOURCE_ATTRIBUTES=SampleRate=10
|
221
|
+
```
|
222
|
+
|
223
|
+
### Custom Span Processors
|
224
|
+
|
225
|
+
The example includes custom span processors:
|
226
|
+
|
227
|
+
```ruby
|
228
|
+
# Add baggage to all spans
|
229
|
+
config.add_span_processor(OpenTelemetry::Processor::Baggage::BaggageSpanProcessor.new)
|
230
|
+
|
231
|
+
# Batch export to Honeycomb
|
232
|
+
config.add_span_processor(
|
233
|
+
OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(
|
234
|
+
OpenTelemetry::Exporter::OTLP::Exporter.new
|
235
|
+
)
|
236
|
+
)
|
237
|
+
```
|
238
|
+
|
239
|
+
### Error Handling
|
240
|
+
|
241
|
+
Comprehensive error tracking:
|
242
|
+
|
243
|
+
```ruby
|
244
|
+
rescue StandardError => e
|
245
|
+
span.set_attribute('error.type', e.class.name)
|
246
|
+
span.set_attribute('error.message', e.message)
|
247
|
+
span.status = OpenTelemetry::Trace::Status.error(e.message)
|
248
|
+
end
|
249
|
+
```
|
250
|
+
|
251
|
+
## ποΈ Architecture
|
252
|
+
|
253
|
+
The example demonstrates a layered observability approach:
|
254
|
+
|
255
|
+
```
|
256
|
+
βββββββββββββββββββββββββββββββββββββββ
|
257
|
+
β HTTP Request (Automatic Instrumentation) β
|
258
|
+
βββββββββββββββββββββββββββββββββββββββ€
|
259
|
+
β Sinatra Route Handler β
|
260
|
+
βββββββββββββββββββββββββββββββββββββββ€
|
261
|
+
β Business Logic Spans β
|
262
|
+
βββββββββββββββββββββββββββββββββββββββ€
|
263
|
+
β Database/External Service Simulationβ
|
264
|
+
βββββββββββββββββββββββββββββββββββββββ€
|
265
|
+
β Honeycomb.io via OTLP β
|
266
|
+
βββββββββββββββββββββββββββββββββββββββ
|
267
|
+
```
|
268
|
+
|
269
|
+
### Span Hierarchy Example
|
270
|
+
|
271
|
+
```
|
272
|
+
POST /users
|
273
|
+
ββ users.create (business logic)
|
274
|
+
β ββ validation.user_input
|
275
|
+
β ββ database.insert.user
|
276
|
+
ββ HTTP span (automatic)
|
277
|
+
```
|
278
|
+
|
279
|
+
## π Troubleshooting
|
280
|
+
|
281
|
+
### No Data in Honeycomb?
|
282
|
+
|
283
|
+
1. **Check API Key**: Ensure your API key is correct and has "Can create datasets" permission
|
284
|
+
2. **Check Endpoint**: US vs EU instance
|
285
|
+
3. **Check Environment**: Make sure environment variables are loaded
|
286
|
+
4. **Enable Debug Mode**:
|
287
|
+
```bash
|
288
|
+
OTEL_RUBY_TRACES_EXPORTER_DEBUG=true
|
289
|
+
OTEL_LOG_LEVEL=debug
|
290
|
+
```
|
291
|
+
|
292
|
+
### Debugging OpenTelemetry
|
293
|
+
|
294
|
+
Add debug logging to see what's happening:
|
295
|
+
|
296
|
+
```ruby
|
297
|
+
# Add this to the top of honeycomb_example.rb
|
298
|
+
ENV['OTEL_LOG_LEVEL'] = 'debug'
|
299
|
+
```
|
300
|
+
|
301
|
+
### Common Issues
|
302
|
+
|
303
|
+
1. **Missing Dataset**: Ensure API key has "Can create datasets" permission
|
304
|
+
2. **EU vs US Instance**: Check you're using the correct endpoint
|
305
|
+
3. **Firewall**: Ensure outbound HTTPS to Honeycomb is allowed
|
306
|
+
4. **Gem Conflicts**: Run `bundle install` to ensure all OpenTelemetry gems are compatible
|
307
|
+
|
308
|
+
## π Further Reading
|
309
|
+
|
310
|
+
- [Honeycomb OpenTelemetry Ruby Guide](https://docs.honeycomb.io/send-data/ruby/opentelemetry-sdk/)
|
311
|
+
- [OpenTelemetry Ruby Documentation](https://opentelemetry.io/docs/instrumentation/ruby/)
|
312
|
+
- [Honeycomb Query Language](https://docs.honeycomb.io/query-data/)
|
313
|
+
- [OpenTelemetry Semantic Conventions](https://opentelemetry.io/docs/specs/semconv/)
|
314
|
+
|
315
|
+
## π― Production Considerations
|
316
|
+
|
317
|
+
1. **Sampling**: Enable sampling for high-traffic applications
|
318
|
+
2. **Resource Attributes**: Add deployment environment, version tags
|
319
|
+
3. **Security**: Store API keys securely (not in code)
|
320
|
+
4. **Monitoring**: Set up alerts in Honeycomb for error rates and latency
|
321
|
+
5. **Performance**: Monitor the overhead of instrumentation
|
322
|
+
|
323
|
+
## π€ Integration with RapiTapir
|
324
|
+
|
325
|
+
This example shows how to extend RapiTapir with comprehensive observability:
|
326
|
+
|
327
|
+
- **Custom Sinatra Extension**: `RapiTapir::Extensions::HoneycombObservability`
|
328
|
+
- **Automatic Context Propagation**: Request IDs and business context
|
329
|
+
- **Health Check Integration**: Traced health checks with timing
|
330
|
+
- **Error Handling**: Structured error reporting with context
|
331
|
+
|
332
|
+
The pattern can be extended to other frameworks (Rails, Roda, etc.) and provides a foundation for production-ready observability in RapiTapir applications.
|