|
5 | 5 |
|
6 | 6 | This package provides a simple way to add OpenTelemetry to your Laravel application. |
7 | 7 |
|
| 8 | +## ⚠️ Breaking Changes in Recent Versions |
| 9 | + |
| 10 | +**SpanBuilder API Changes**: The `SpanBuilder::start()` method behavior has been updated for better safety and predictability: |
| 11 | + |
| 12 | +- **Before**: `start()` automatically activated the span's scope, which could cause issues in async scenarios |
| 13 | +- **Now**: `start()` only creates the span without activating its scope (safer default behavior) |
| 14 | +- **Migration**: If you need the old behavior, use `startAndActivate()` instead of `start()` |
| 15 | + |
| 16 | +```php |
| 17 | +// Old code (if you need scope activation) |
| 18 | +$span = Measure::span('my-operation')->start(); // This now returns SpanInterface |
| 19 | + |
| 20 | +// New code (for scope activation) |
| 21 | +$startedSpan = Measure::span('my-operation')->startAndActivate(); // Returns StartedSpan |
| 22 | +``` |
| 23 | + |
| 24 | +For most use cases, the new `start()` behavior is safer and recommended. See the [Advanced Span Creation](#advanced-span-creation-with-spanbuilder) section for detailed usage patterns. |
| 25 | + |
8 | 26 | ## Features |
9 | 27 |
|
10 | 28 | - ✅ **Zero Configuration**: Works out of the box with sensible defaults. |
@@ -142,38 +160,96 @@ The `trace` method will: |
142 | 160 | - Automatically record and re-throw any exceptions that occur within the callback. |
143 | 161 | - End the span when the callback completes. |
144 | 162 |
|
| 163 | +### Advanced Span Creation with SpanBuilder |
| 164 | + |
| 165 | +For more control over span lifecycle, you can use the `SpanBuilder` directly through `Measure::span()`. The SpanBuilder provides several methods for different use cases: |
| 166 | + |
| 167 | +#### Basic Span Creation (Recommended for most cases) |
| 168 | + |
| 169 | +```php |
| 170 | +// Create a span without activating its scope (safer for async operations) |
| 171 | +$span = Measure::span('my-operation') |
| 172 | + ->setAttribute('operation.type', 'data-processing') |
| 173 | + ->setSpanKind(SpanKind::KIND_INTERNAL) |
| 174 | + ->start(); // Returns SpanInterface |
| 175 | + |
| 176 | +// Your business logic here |
| 177 | +$result = $this->processData(); |
| 178 | + |
| 179 | +// Remember to end the span manually |
| 180 | +$span->end(); |
| 181 | +``` |
| 182 | + |
| 183 | +#### Span with Activated Scope |
| 184 | + |
| 185 | +```php |
| 186 | +// Create a span and activate its scope (for nested operations) |
| 187 | +$startedSpan = Measure::span('parent-operation') |
| 188 | + ->setAttribute('operation.type', 'user-workflow') |
| 189 | + ->setSpanKind(SpanKind::KIND_INTERNAL) |
| 190 | + ->startAndActivate(); // Returns StartedSpan |
| 191 | + |
| 192 | +// Any spans created within this block will be children of this span |
| 193 | +$childSpan = Measure::span('child-operation')->start(); |
| 194 | +$childSpan->end(); |
| 195 | + |
| 196 | +// The StartedSpan automatically manages scope cleanup |
| 197 | +$startedSpan->end(); // Ends span and detaches scope |
| 198 | +``` |
| 199 | + |
| 200 | +#### Span with Context (For Manual Propagation) |
| 201 | + |
| 202 | +```php |
| 203 | +// Create a span and get both span and context for manual management |
| 204 | +[$span, $context] = Measure::span('async-operation') |
| 205 | + ->setAttribute('operation.async', true) |
| 206 | + ->startWithContext(); // Returns [SpanInterface, ContextInterface] |
| 207 | + |
| 208 | +// Use context for propagation (e.g., in HTTP headers) |
| 209 | +$headers = Measure::propagationHeaders($context); |
| 210 | + |
| 211 | +// Your async operation here |
| 212 | +$span->end(); |
| 213 | +``` |
| 214 | + |
145 | 215 | ### Using Semantic Spans |
146 | 216 |
|
147 | 217 | To promote standardization, the package provides semantic helper methods that create spans with attributes conforming to OpenTelemetry's [Semantic Conventions](https://opentelemetry.io/docs/specs/semconv/). |
148 | 218 |
|
149 | 219 | #### Database Spans |
150 | 220 | ```php |
151 | | -use OpenTelemetry\SemConv\TraceAttributes; |
152 | | - |
153 | 221 | // Manually trace a block of database operations |
154 | | -$user = Measure::database('repository:find-user', function ($span) use ($userId) { |
155 | | - $span->setAttribute(TraceAttributes::DB_STATEMENT, "SELECT * FROM users WHERE id = ?"); |
| 222 | +$user = Measure::database('SELECT', 'users'); // Quick shortcut for database operations |
| 223 | +// Or use the general trace method for more complex operations |
| 224 | +$user = Measure::trace('repository:find-user', function ($span) use ($userId) { |
| 225 | + $span->setAttribute('db.statement', "SELECT * FROM users WHERE id = ?"); |
| 226 | + $span->setAttribute('db.table', 'users'); |
156 | 227 | return User::find($userId); |
157 | 228 | }); |
158 | 229 | ``` |
159 | 230 | *Note: If `QueryWatcher` is enabled, individual queries are already traced. This is useful for tracing a larger transaction or a specific business operation involving multiple queries.* |
160 | 231 |
|
161 | | -#### Cache Spans |
| 232 | +#### HTTP Client Spans |
162 | 233 | ```php |
163 | | -$user = Measure::cache('fetch-user-from-cache', function ($span) use ($userId) { |
164 | | - $span->setAttributes([ |
165 | | - 'cache.key' => "user:{$userId}", |
166 | | - 'cache.ttl' => 3600, |
167 | | - ]); |
168 | | - return Cache::remember("user:{$userId}", 3600, fn() => User::find($userId)); |
| 234 | +// Quick shortcut for HTTP client requests |
| 235 | +$response = Measure::httpClient('POST', 'https://api.example.com/users'); |
| 236 | +// Or use the general trace method for more control |
| 237 | +$response = Measure::trace('api-call', function ($span) { |
| 238 | + $span->setAttribute('http.method', 'POST'); |
| 239 | + $span->setAttribute('http.url', 'https://api.example.com/users'); |
| 240 | + return Http::post('https://api.example.com/users', $data); |
169 | 241 | }); |
170 | 242 | ``` |
171 | 243 |
|
172 | | -#### Queue Spans |
| 244 | +#### Custom Spans |
173 | 245 | ```php |
174 | | -// Manually trace putting a job on the queue |
175 | | -Measure::queue('dispatch-welcome-email', function() use ($user) { |
176 | | - WelcomeEmailJob::dispatch($user); |
| 246 | +// For any custom operation, use the general trace method |
| 247 | +$result = Measure::trace('process-payment', function ($span) use ($payment) { |
| 248 | + $span->setAttribute('payment.amount', $payment->amount); |
| 249 | + $span->setAttribute('payment.currency', $payment->currency); |
| 250 | + |
| 251 | + // Your business logic here |
| 252 | + return $this->processPayment($payment); |
177 | 253 | }); |
178 | 254 | ``` |
179 | 255 |
|
@@ -221,7 +297,7 @@ Or apply it globally in `app/Http/Kernel.php`: |
221 | 297 | protected $middlewareGroups = [ |
222 | 298 | 'web' => [ |
223 | 299 | // ... |
224 | | - \Overtrue\LaravelOpenTelemetry\Http\Middleware\TraceIdMiddleware::class, |
| 300 | + \Overtrue\LaravelOpenTelemetry\Http\Middleware\AddTraceId::class, |
225 | 301 | ], |
226 | 302 | // ... |
227 | 303 | ]; |
|
0 commit comments