observable 0.1.0 → 0.1.4
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 +4 -4
- data/.claude/settings.local.json +9 -0
- data/.ruby-version +1 -0
- data/.standard.yml +5 -1
- data/CHANGELOG.md +52 -0
- data/CLAUDE.md +135 -0
- data/Gemfile +0 -6
- data/Gemfile.lock +178 -0
- data/README.md +156 -25
- data/Rakefile +2 -4
- data/example.rb +55 -0
- data/lib/observable/configuration.rb +17 -0
- data/lib/observable/instrumenter.rb +382 -0
- data/lib/observable/persistence/span.rb +119 -0
- data/lib/observable/persistence/span_repo.rb +142 -0
- data/lib/observable/persistence/trace.rb +22 -0
- data/lib/observable/persistence/trace_repo.rb +43 -0
- data/lib/observable/structured_error.rb +109 -0
- data/lib/observable/tracing_test_helper.rb +51 -0
- data/lib/observable/version.rb +1 -1
- data/lib/observable.rb +23 -1
- data/observable.gemspec +49 -0
- data/pretty.rb +12 -0
- metadata +197 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b257d27b8a651f747109ea43b983befa02c2e10d34d13a49cf025a5f46eb7592
|
|
4
|
+
data.tar.gz: '068c6c31c24e4473682c9d5072842d995cd43a0fae9dee2ca2749a7313bc69a5'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3d52b07648bd401f2494aa735e988a067c01bca1a443c1ee4ec609cebf582fc3eb3c59939c0b85a9880f0ad53a72b29495a249cea6bf92ee3dc8a114c674e340
|
|
7
|
+
data.tar.gz: eeb0349b60211293afe11f445789a3598e8ad2af6d577e53cf08958ec7237aa6c36799e047df11c0cb931ec6a960a085e14c2c96f79c5f504b6e0cf824ae03e2
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.4.1
|
data/.standard.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,57 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
## [0.1.4] - 2025-09-08
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- Update version bumping logic in bump script
|
|
11
|
+
- v0.1.3-beta
|
|
12
|
+
- Update gem build process and release script
|
|
13
|
+
- v0.1.2-beta
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## [0.1.2-beta] - 2025-09-08
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- v0.1.2-beta
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## [0.1.2-alpha] - 2025-09-08
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
- Add support for bumping alpha and beta pre-releases
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
- Update version bumping logic for bump gem
|
|
29
|
+
- Delegate all version bumping to bump gem
|
|
30
|
+
- Extract configuration into class
|
|
31
|
+
- Update gemspec and test setup for OTLP exporter
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
## [0.1.2-alpha] - 2025-09-08
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
- Add support for bumping alpha and beta pre-releases
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
- Update version to 0.1.2
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
- Delegate all version bumping to bump gem
|
|
44
|
+
- v0.1.2-alpha
|
|
45
|
+
- Extract configuration into class
|
|
46
|
+
- Update gemspec and test setup for OTLP exporter
|
|
47
|
+
- Add 'ignore' section to configuration file
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
## [0.1.2] - 2025-09-07
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
- Add Ruby 3.2.x versions to GitHub Actions matrix for better CI coverage
|
|
54
|
+
|
|
3
55
|
## [0.1.0] - 2024-04-22
|
|
4
56
|
|
|
5
57
|
- Initial release
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
This is the Observable gem - a Ruby library that provides OpenTelemetry instrumentation for method calls with configurable serialization, PII filtering, and argument tracking. It automatically captures method invocation details, arguments, return values, and exceptions as OpenTelemetry spans.
|
|
8
|
+
|
|
9
|
+
## Core Architecture
|
|
10
|
+
|
|
11
|
+
### Main Components
|
|
12
|
+
|
|
13
|
+
- **Observable::Instrumenter** (`lib/observable/instrumenter.rb`) - The core instrumentation engine that wraps method calls with OpenTelemetry spans
|
|
14
|
+
- **Observable::Configuration** (`lib/observable/instrumenter.rb`) - Uses Dry::Configurable for flexible configuration management
|
|
15
|
+
- **ArgumentExtractor** (`lib/observable/instrumenter.rb`) - Extracts method arguments from Ruby bindings using introspection
|
|
16
|
+
- **CallerInformation** (`lib/observable/instrumenter.rb`) - Value object containing method metadata (name, namespace, filepath, line number, arguments)
|
|
17
|
+
|
|
18
|
+
### Key Features
|
|
19
|
+
|
|
20
|
+
- **Automatic method instrumentation** - Wrap any method call with `instrumenter.instrument(binding) { ... }`
|
|
21
|
+
- **Argument tracking** - Captures method parameters with PII filtering capabilities
|
|
22
|
+
- **Return value serialization** - Configurable tracking of method return values
|
|
23
|
+
- **Exception handling** - Automatically captures and records exceptions in spans
|
|
24
|
+
- **Class/instance method detection** - Distinguishes between static (`.`) and instance (`#`) methods
|
|
25
|
+
- **Configurable serialization** - Supports custom formatters and max depth limits
|
|
26
|
+
|
|
27
|
+
## Development Commands
|
|
28
|
+
|
|
29
|
+
### Running Tests
|
|
30
|
+
```bash
|
|
31
|
+
bundle exec rake test
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Running Individual Tests
|
|
35
|
+
```bash
|
|
36
|
+
ruby -Itest test/unit/instrumenter_test.rb
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Linting
|
|
40
|
+
```bash
|
|
41
|
+
bundle exec standardrb
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Dependencies
|
|
45
|
+
```bash
|
|
46
|
+
bundle install
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Interactive Console
|
|
50
|
+
```bash
|
|
51
|
+
bin/console
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Build Gem
|
|
55
|
+
```bash
|
|
56
|
+
bundle exec rake build
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Version Management
|
|
60
|
+
```bash
|
|
61
|
+
# Bump patch version (0.1.1 -> 0.1.2)
|
|
62
|
+
bin/bump
|
|
63
|
+
|
|
64
|
+
# Bump minor version (0.1.1 -> 0.2.0)
|
|
65
|
+
bin/bump --minor
|
|
66
|
+
|
|
67
|
+
# Bump major version (0.1.1 -> 1.0.0)
|
|
68
|
+
bin/bump --major
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Release Process
|
|
72
|
+
|
|
73
|
+
The project uses automated releases via GitHub Actions:
|
|
74
|
+
|
|
75
|
+
1. **Manual Release**: Use `bin/bump` to increment version, update CHANGELOG.md, then commit and push to `main`
|
|
76
|
+
2. **Automated Release**: When a version change is detected in a push to `main`, GitHub Actions will:
|
|
77
|
+
- Run tests across multiple Ruby versions
|
|
78
|
+
- Run linting checks
|
|
79
|
+
- Build the gem
|
|
80
|
+
- Create a GitHub release with changelog
|
|
81
|
+
- Publish to RubyGems (requires `RUBYGEMS_API_KEY` secret)
|
|
82
|
+
|
|
83
|
+
**Prerequisites for automated release:**
|
|
84
|
+
- Set `RUBYGEMS_API_KEY` secret in GitHub repository settings
|
|
85
|
+
- Ensure CHANGELOG.md is updated with meaningful changes
|
|
86
|
+
|
|
87
|
+
## Configuration System
|
|
88
|
+
|
|
89
|
+
The gem uses Dry::Configurable with these key settings:
|
|
90
|
+
|
|
91
|
+
- `transport` - Transport mechanism (default: `:otel`)
|
|
92
|
+
- `app_namespace` - Application namespace for spans
|
|
93
|
+
- `attribute_namespace` - Attribute namespace prefix
|
|
94
|
+
- `tracer_names` - OpenTelemetry tracer configuration
|
|
95
|
+
- `formatters` - Object serialization methods (default: `:to_h`)
|
|
96
|
+
- `pii_filters` - Regex patterns to filter sensitive data
|
|
97
|
+
- `track_return_values` - Enable/disable return value capture (default: true)
|
|
98
|
+
|
|
99
|
+
## Usage Pattern
|
|
100
|
+
|
|
101
|
+
The primary usage pattern involves:
|
|
102
|
+
|
|
103
|
+
1. Create an instrumenter instance: `Observable::Instrumenter.new`
|
|
104
|
+
2. Wrap method calls: `instrumenter.instrument(binding) { method_logic }`
|
|
105
|
+
3. The instrumenter automatically:
|
|
106
|
+
- Extracts method name, class, and arguments from `binding`
|
|
107
|
+
- Creates OpenTelemetry spans with standardized naming (`Class#method` or `Class.method`)
|
|
108
|
+
- Serializes arguments and return values with PII filtering
|
|
109
|
+
- Handles exceptions and sets appropriate span status
|
|
110
|
+
|
|
111
|
+
## Testing Structure
|
|
112
|
+
|
|
113
|
+
- **Unit tests** in `test/unit/` - Test individual components
|
|
114
|
+
- **Support helpers** in `test/support/` - Test utilities and mocks
|
|
115
|
+
- Uses Minitest framework
|
|
116
|
+
- Custom tracing test helpers for OpenTelemetry span verification
|
|
117
|
+
|
|
118
|
+
## Dependencies
|
|
119
|
+
|
|
120
|
+
**Runtime:**
|
|
121
|
+
- `opentelemetry-sdk` ~> 1.5 - OpenTelemetry instrumentation
|
|
122
|
+
- `dry-configurable` >= 0.13.0, < 2.0 - Configuration management
|
|
123
|
+
- `dry-struct` ~> 1.4 - Value objects
|
|
124
|
+
|
|
125
|
+
**Development:**
|
|
126
|
+
- `minitest` ~> 5.14 - Testing framework
|
|
127
|
+
- `standard` ~> 1.40 - Ruby linting and formatting
|
|
128
|
+
- `rake` ~> 13.0 - Build automation
|
|
129
|
+
|
|
130
|
+
## Code Style
|
|
131
|
+
|
|
132
|
+
- Uses Standard Ruby for linting and formatting
|
|
133
|
+
- Ruby version: 3.4+ (configured in `.ruby-version` and `.standard.yml`)
|
|
134
|
+
- Frozen string literals enforced
|
|
135
|
+
- Follows Ruby community conventions for method naming and structure
|
data/Gemfile
CHANGED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
observable (0.1.4)
|
|
5
|
+
dry-configurable (>= 0.13.0, < 2.0)
|
|
6
|
+
dry-struct (~> 1.4)
|
|
7
|
+
opentelemetry-sdk (~> 1.5)
|
|
8
|
+
|
|
9
|
+
GEM
|
|
10
|
+
remote: https://rubygems.org/
|
|
11
|
+
specs:
|
|
12
|
+
ast (2.4.3)
|
|
13
|
+
attr_extras (7.1.0)
|
|
14
|
+
bigdecimal (3.2.3)
|
|
15
|
+
bump (0.10.0)
|
|
16
|
+
concurrent-ruby (1.3.5)
|
|
17
|
+
date (3.4.1)
|
|
18
|
+
debug (1.11.0)
|
|
19
|
+
irb (~> 1.10)
|
|
20
|
+
reline (>= 0.3.8)
|
|
21
|
+
diff-lcs (1.6.2)
|
|
22
|
+
docile (1.4.1)
|
|
23
|
+
dry-configurable (1.3.0)
|
|
24
|
+
dry-core (~> 1.1)
|
|
25
|
+
zeitwerk (~> 2.6)
|
|
26
|
+
dry-core (1.1.0)
|
|
27
|
+
concurrent-ruby (~> 1.0)
|
|
28
|
+
logger
|
|
29
|
+
zeitwerk (~> 2.6)
|
|
30
|
+
dry-inflector (1.2.0)
|
|
31
|
+
dry-logic (1.6.0)
|
|
32
|
+
bigdecimal
|
|
33
|
+
concurrent-ruby (~> 1.0)
|
|
34
|
+
dry-core (~> 1.1)
|
|
35
|
+
zeitwerk (~> 2.6)
|
|
36
|
+
dry-struct (1.8.0)
|
|
37
|
+
dry-core (~> 1.1)
|
|
38
|
+
dry-types (~> 1.8, >= 1.8.2)
|
|
39
|
+
ice_nine (~> 0.11)
|
|
40
|
+
zeitwerk (~> 2.6)
|
|
41
|
+
dry-types (1.8.3)
|
|
42
|
+
bigdecimal (~> 3.0)
|
|
43
|
+
concurrent-ruby (~> 1.0)
|
|
44
|
+
dry-core (~> 1.0)
|
|
45
|
+
dry-inflector (~> 1.0)
|
|
46
|
+
dry-logic (~> 1.4)
|
|
47
|
+
zeitwerk (~> 2.6)
|
|
48
|
+
erb (5.0.2)
|
|
49
|
+
google-protobuf (4.32.0)
|
|
50
|
+
bigdecimal
|
|
51
|
+
rake (>= 13)
|
|
52
|
+
google-protobuf (4.32.0-arm64-darwin)
|
|
53
|
+
bigdecimal
|
|
54
|
+
rake (>= 13)
|
|
55
|
+
googleapis-common-protos-types (1.21.0)
|
|
56
|
+
google-protobuf (~> 4.26)
|
|
57
|
+
ice_nine (0.11.2)
|
|
58
|
+
io-console (0.8.1)
|
|
59
|
+
irb (1.15.2)
|
|
60
|
+
pp (>= 0.6.0)
|
|
61
|
+
rdoc (>= 4.0.0)
|
|
62
|
+
reline (>= 0.4.2)
|
|
63
|
+
json (2.13.2)
|
|
64
|
+
language_server-protocol (3.17.0.5)
|
|
65
|
+
lint_roller (1.1.0)
|
|
66
|
+
logger (1.7.0)
|
|
67
|
+
m (1.6.2)
|
|
68
|
+
method_source (>= 0.6.7)
|
|
69
|
+
rake (>= 0.9.2.2)
|
|
70
|
+
method_source (1.1.0)
|
|
71
|
+
minitest (5.25.5)
|
|
72
|
+
opentelemetry-api (1.6.0)
|
|
73
|
+
opentelemetry-common (0.22.0)
|
|
74
|
+
opentelemetry-api (~> 1.0)
|
|
75
|
+
opentelemetry-exporter-otlp (0.30.0)
|
|
76
|
+
google-protobuf (>= 3.18)
|
|
77
|
+
googleapis-common-protos-types (~> 1.3)
|
|
78
|
+
opentelemetry-api (~> 1.1)
|
|
79
|
+
opentelemetry-common (~> 0.20)
|
|
80
|
+
opentelemetry-sdk (~> 1.2)
|
|
81
|
+
opentelemetry-semantic_conventions
|
|
82
|
+
opentelemetry-registry (0.4.0)
|
|
83
|
+
opentelemetry-api (~> 1.1)
|
|
84
|
+
opentelemetry-sdk (1.8.1)
|
|
85
|
+
opentelemetry-api (~> 1.1)
|
|
86
|
+
opentelemetry-common (~> 0.20)
|
|
87
|
+
opentelemetry-registry (~> 0.2)
|
|
88
|
+
opentelemetry-semantic_conventions
|
|
89
|
+
opentelemetry-semantic_conventions (1.11.0)
|
|
90
|
+
opentelemetry-api (~> 1.0)
|
|
91
|
+
optimist (3.2.1)
|
|
92
|
+
parallel (1.27.0)
|
|
93
|
+
parser (3.3.9.0)
|
|
94
|
+
ast (~> 2.4.1)
|
|
95
|
+
racc
|
|
96
|
+
patience_diff (1.2.0)
|
|
97
|
+
optimist (~> 3.0)
|
|
98
|
+
pp (0.6.2)
|
|
99
|
+
prettyprint
|
|
100
|
+
prettyprint (0.2.0)
|
|
101
|
+
prism (1.4.0)
|
|
102
|
+
psych (5.2.6)
|
|
103
|
+
date
|
|
104
|
+
stringio
|
|
105
|
+
racc (1.8.1)
|
|
106
|
+
rainbow (3.1.1)
|
|
107
|
+
rake (13.3.0)
|
|
108
|
+
rdoc (6.14.2)
|
|
109
|
+
erb
|
|
110
|
+
psych (>= 4.0.0)
|
|
111
|
+
regexp_parser (2.11.2)
|
|
112
|
+
reline (0.6.2)
|
|
113
|
+
io-console (~> 0.5)
|
|
114
|
+
rubocop (1.75.8)
|
|
115
|
+
json (~> 2.3)
|
|
116
|
+
language_server-protocol (~> 3.17.0.2)
|
|
117
|
+
lint_roller (~> 1.1.0)
|
|
118
|
+
parallel (~> 1.10)
|
|
119
|
+
parser (>= 3.3.0.2)
|
|
120
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
121
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
|
122
|
+
rubocop-ast (>= 1.44.0, < 2.0)
|
|
123
|
+
ruby-progressbar (~> 1.7)
|
|
124
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
|
125
|
+
rubocop-ast (1.46.0)
|
|
126
|
+
parser (>= 3.3.7.2)
|
|
127
|
+
prism (~> 1.4)
|
|
128
|
+
rubocop-performance (1.25.0)
|
|
129
|
+
lint_roller (~> 1.1)
|
|
130
|
+
rubocop (>= 1.75.0, < 2.0)
|
|
131
|
+
rubocop-ast (>= 1.38.0, < 2.0)
|
|
132
|
+
ruby-progressbar (1.13.0)
|
|
133
|
+
simplecov (0.22.0)
|
|
134
|
+
docile (~> 1.1)
|
|
135
|
+
simplecov-html (~> 0.11)
|
|
136
|
+
simplecov_json_formatter (~> 0.1)
|
|
137
|
+
simplecov-html (0.13.2)
|
|
138
|
+
simplecov_json_formatter (0.1.4)
|
|
139
|
+
standard (1.50.0)
|
|
140
|
+
language_server-protocol (~> 3.17.0.2)
|
|
141
|
+
lint_roller (~> 1.0)
|
|
142
|
+
rubocop (~> 1.75.5)
|
|
143
|
+
standard-custom (~> 1.0.0)
|
|
144
|
+
standard-performance (~> 1.8)
|
|
145
|
+
standard-custom (1.0.2)
|
|
146
|
+
lint_roller (~> 1.0)
|
|
147
|
+
rubocop (~> 1.50)
|
|
148
|
+
standard-performance (1.8.0)
|
|
149
|
+
lint_roller (~> 1.1)
|
|
150
|
+
rubocop-performance (~> 1.25.0)
|
|
151
|
+
stringio (3.1.7)
|
|
152
|
+
super_diff (0.16.0)
|
|
153
|
+
attr_extras (>= 6.2.4)
|
|
154
|
+
diff-lcs
|
|
155
|
+
patience_diff
|
|
156
|
+
unicode-display_width (3.1.5)
|
|
157
|
+
unicode-emoji (~> 4.0, >= 4.0.4)
|
|
158
|
+
unicode-emoji (4.0.4)
|
|
159
|
+
zeitwerk (2.7.3)
|
|
160
|
+
|
|
161
|
+
PLATFORMS
|
|
162
|
+
arm64-darwin-23
|
|
163
|
+
ruby
|
|
164
|
+
|
|
165
|
+
DEPENDENCIES
|
|
166
|
+
bump (~> 0.10)
|
|
167
|
+
debug (~> 1.8)
|
|
168
|
+
m (~> 1.6)
|
|
169
|
+
minitest (~> 5.14)
|
|
170
|
+
observable!
|
|
171
|
+
opentelemetry-exporter-otlp (~> 0.30)
|
|
172
|
+
rake (~> 13.0)
|
|
173
|
+
simplecov (~> 0.22)
|
|
174
|
+
standard (~> 1.40)
|
|
175
|
+
super_diff (~> 0.9)
|
|
176
|
+
|
|
177
|
+
BUNDLED WITH
|
|
178
|
+
2.6.3
|
data/README.md
CHANGED
|
@@ -1,37 +1,168 @@
|
|
|
1
1
|
# Observable
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Automatic OpenTelemetry instrumentation for Ruby methods with configurable serialization, PII filtering, and argument tracking.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Getting Started
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
```bash
|
|
8
|
+
bundle add observable
|
|
9
|
+
```
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
Or
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
```ruby
|
|
14
|
+
# Gemfile
|
|
15
|
+
gem 'observable'
|
|
16
|
+
```
|
|
12
17
|
|
|
13
|
-
|
|
18
|
+
Basic usage:
|
|
14
19
|
|
|
15
|
-
|
|
20
|
+
```ruby
|
|
21
|
+
require 'observable'
|
|
16
22
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
class UserService
|
|
24
|
+
def initialize
|
|
25
|
+
@instrumenter = Observable::Instrumenter.new
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def create_user(name, email)
|
|
29
|
+
@instrumenter.instrument(binding) do
|
|
30
|
+
User.create(name: name, email: email)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
OpenTelemetry spans are automatically created with method names, arguments, return values, and exceptions.
|
|
37
|
+
|
|
38
|
+
## Configuration
|
|
39
|
+
|
|
40
|
+
Configure globally or per-instrumenter:
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
# Global configuration
|
|
44
|
+
Observable::Configuration.configure do |config|
|
|
45
|
+
config.tracer_name = "my_app"
|
|
46
|
+
config.transport = :otel
|
|
47
|
+
config.app_namespace = "my_app"
|
|
48
|
+
config.attribute_namespace = "my_app"
|
|
49
|
+
config.track_return_values = true
|
|
50
|
+
config.serialization_depth = {default: 2, "MyClass" => 3}
|
|
51
|
+
config.formatters = {default: :to_h, "MyClass" => :to_formatted_h}
|
|
52
|
+
config.pii_filters = [/password/i, /secret/i]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Per-instrumenter configuration
|
|
56
|
+
config = Observable::Configuration.new
|
|
57
|
+
config.track_return_values = false
|
|
58
|
+
instrumenter = Observable::Instrumenter.new(config: config)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Configuration Options
|
|
62
|
+
|
|
63
|
+
- `tracer_name`: `"observable"` - Name for the OpenTelemetry tracer
|
|
64
|
+
- `transport`: `:otel` - Uses OpenTelemetry SDK
|
|
65
|
+
- `app_namespace`: `"app"` - Namespace for application-specific attributes
|
|
66
|
+
- `attribute_namespace`: `"app"` - Namespace for span attributes
|
|
67
|
+
- `track_return_values`: `true` - Captures method return values in spans
|
|
68
|
+
- `serialization_depth`: `{default: 2}` - Per-class serialization depth limits (Hash or Integer for backward compatibility)
|
|
69
|
+
- `formatters`: `{default: :to_h}` - Object serialization methods by class name
|
|
70
|
+
- `pii_filters`: `[]` - Regex patterns to filter sensitive data from spans
|
|
71
|
+
|
|
72
|
+
## OpenTelemetry Integration
|
|
73
|
+
|
|
74
|
+
This library seamlessly integrates with OpenTelemetry, the industry-standard observability framework. Spans are automatically created with standardized naming (`Class#method` or `Class.method`) and include rich metadata about method invocations, making your Ruby applications immediately observable without manual instrumentation.
|
|
75
|
+
|
|
76
|
+
## Custom Formatters
|
|
77
|
+
|
|
78
|
+
Control how domain objects are serialized in spans by configuring custom formatters.
|
|
79
|
+
|
|
80
|
+
```ruby
|
|
81
|
+
Observable::Configuration.configure do |config|
|
|
82
|
+
config.formatters = {
|
|
83
|
+
default: :to_h,
|
|
84
|
+
'YourCustomClass' => :to_formatted_h
|
|
85
|
+
}
|
|
86
|
+
config.serialization_depth = {
|
|
87
|
+
default: 2,
|
|
88
|
+
'YourCustomClass' => 3
|
|
89
|
+
}
|
|
90
|
+
end
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Example
|
|
94
|
+
|
|
95
|
+
A domain object `Customer` has an `Invoice`.
|
|
96
|
+
|
|
97
|
+
### Objective
|
|
98
|
+
|
|
99
|
+
Only send the invoice ID to the trace to save data.
|
|
100
|
+
|
|
101
|
+
### Background
|
|
102
|
+
|
|
103
|
+
Imagine domain objects are `Dry::Struct` value objects:
|
|
104
|
+
|
|
105
|
+
```ruby
|
|
106
|
+
class Customer < Dry::Struct
|
|
107
|
+
attribute :id, Dry.Types::String
|
|
108
|
+
attribute :name, Dry.Types::String
|
|
109
|
+
attribute :Invoice, Invoice
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
class Invoice < Dry::Struct
|
|
113
|
+
attribute :id, Dry.Types::String
|
|
114
|
+
attribute :status, Dry.Types::String
|
|
115
|
+
attribute :line_items, Dry.Types::Array
|
|
116
|
+
end
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Solution
|
|
120
|
+
|
|
121
|
+
1. Define custom formatting method - `#to_formatted_h`
|
|
122
|
+
|
|
123
|
+
```diff
|
|
124
|
+
class Customer < Dry::Struct
|
|
125
|
+
attribute :id, Dry.Types::String
|
|
126
|
+
attribute :name, Dry.Types::String
|
|
127
|
+
attribute :Invoice, Invoice
|
|
128
|
+
|
|
129
|
+
+ def to_formatted_h
|
|
130
|
+
+ {
|
|
131
|
+
+ id: id,
|
|
132
|
+
+ name: name,
|
|
133
|
+
+ invoice: {
|
|
134
|
+
+ id: invoice.id
|
|
135
|
+
+ }
|
|
136
|
+
+ }
|
|
137
|
+
+ end
|
|
138
|
+
end
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
2. Configure observable:
|
|
142
|
+
|
|
143
|
+
```ruby
|
|
144
|
+
Observable::Configuration.configure do |config|
|
|
145
|
+
config.formatters = {
|
|
146
|
+
default: :to_h,
|
|
147
|
+
'Customer' => :to_formatted_h
|
|
148
|
+
}
|
|
149
|
+
config.serialization_depth = {
|
|
150
|
+
default: 2,
|
|
151
|
+
'Customer' => 3
|
|
152
|
+
}
|
|
153
|
+
end
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
The instrumenter tries class-specific formatters first, then falls back to the default formatter, then `to_s`.
|
|
157
|
+
|
|
158
|
+
## Benefits
|
|
159
|
+
|
|
160
|
+
Why use this library? Why not write Otel attributes manually?
|
|
161
|
+
|
|
162
|
+
* **Zero-touch instrumentation** - Wrap any method call without modifying existing code or manually creating spans
|
|
163
|
+
* **Production-ready safety** - Built-in PII filtering, serialization depth limits, and exception handling prevent common observability pitfalls
|
|
164
|
+
* **Standardized telemetry** - Consistent span naming, attribute structure, and OpenTelemetry compliance across your entire application
|
|
30
165
|
|
|
31
166
|
## License
|
|
32
167
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
## Code of Conduct
|
|
36
|
-
|
|
37
|
-
Everyone interacting in the Observable project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/johngallagher/observable/blob/main/CODE_OF_CONDUCT.md).
|
|
168
|
+
MIT License. See LICENSE file for details.
|
data/Rakefile
CHANGED
|
@@ -6,9 +6,7 @@ require "rake/testtask"
|
|
|
6
6
|
Rake::TestTask.new(:test) do |t|
|
|
7
7
|
t.libs << "test"
|
|
8
8
|
t.libs << "lib"
|
|
9
|
-
t.test_files = FileList["test
|
|
9
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
task default: %i[test standard]
|
|
12
|
+
task default: %i[test]
|
data/example.rb
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require_relative "lib/observable"
|
|
4
|
+
|
|
5
|
+
# Configure the instrumenter
|
|
6
|
+
Observable::Configuration.configure do |config|
|
|
7
|
+
config.app_namespace = "example_app"
|
|
8
|
+
config.track_return_values = true
|
|
9
|
+
config.pii_filters = [/password/, /token/, /secret/]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class ExampleService
|
|
13
|
+
def initialize
|
|
14
|
+
@instrumenter = Observable::Instrumenter.new
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def process_order(order_id, customer_email, amount)
|
|
18
|
+
@instrumenter.instrument(binding) do
|
|
19
|
+
# Simulate some processing
|
|
20
|
+
puts "Processing order #{order_id} for #{customer_email}, amount: #{amount}"
|
|
21
|
+
|
|
22
|
+
# Return a result hash
|
|
23
|
+
{
|
|
24
|
+
order_id: order_id,
|
|
25
|
+
status: "processed",
|
|
26
|
+
processed_at: Time.now.to_i,
|
|
27
|
+
amount: amount
|
|
28
|
+
}
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.static_method(data)
|
|
33
|
+
instrumenter = Observable::Instrumenter.new
|
|
34
|
+
instrumenter.instrument(binding) do
|
|
35
|
+
puts "Processing data: #{data.inspect}"
|
|
36
|
+
data.transform_values(&:upcase) if data.is_a?(Hash)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Example usage
|
|
42
|
+
puts "=== Observable Example ==="
|
|
43
|
+
puts
|
|
44
|
+
|
|
45
|
+
service = ExampleService.new
|
|
46
|
+
result = service.process_order("ORD-123", "customer@example.com", 99.99)
|
|
47
|
+
puts "Result: #{result}"
|
|
48
|
+
|
|
49
|
+
puts
|
|
50
|
+
|
|
51
|
+
static_result = ExampleService.static_method({name: "john", city: "nyc"})
|
|
52
|
+
puts "Static result: #{static_result}"
|
|
53
|
+
|
|
54
|
+
puts "\n=== Check OpenTelemetry spans were created ==="
|
|
55
|
+
# In a real application, these spans would be exported to your observability platform
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require "dry/configurable"
|
|
2
|
+
|
|
3
|
+
module Observable
|
|
4
|
+
class Configuration
|
|
5
|
+
extend Dry::Configurable
|
|
6
|
+
|
|
7
|
+
setting :tracer_name, default: "observable"
|
|
8
|
+
setting :transport, default: :otel
|
|
9
|
+
setting :app_namespace, default: "app"
|
|
10
|
+
setting :attribute_namespace, default: "app"
|
|
11
|
+
setting :formatters, default: {default: :to_h}
|
|
12
|
+
setting :pii_filters, default: []
|
|
13
|
+
setting :serialization_depth, default: {default: 2}
|
|
14
|
+
setting :track_return_values, default: true
|
|
15
|
+
setting :custom_error_converters, default: {}
|
|
16
|
+
end
|
|
17
|
+
end
|