Skip to content

Commit 8bc993e

Browse files
authored
Feature/add laravel integration guide (#306)
* Add Laravel user guide * make improvements to symfony guide
1 parent cd8d714 commit 8bc993e

File tree

2 files changed

+253
-10
lines changed

2 files changed

+253
-10
lines changed

docs/laravel-integration.md

+240
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
# Integrating Opentelemetry PHP into Laravel Applications
2+
3+
## Introduction
4+
Distributed tracing helps developers and management gain insights into how well applications perform in terms of traces, metrics, and logs. This guide shows how developers can integrate OpenTelemetry PHP into their Laravel applications for the above benefits. Our example application visualizes exceptions from a Laravel application using both Jaeger and Zipkin.
5+
6+
To follow this guide you will need:
7+
8+
* PHP Installed; this example uses PHP 7.4.
9+
* [Composer](https://getcomposer.org/download/ ) for dependency management.
10+
* [Docker](https://docs.docker.com/get-docker/) for bundling our visualization tools. We have setup instructions for docker on this project's [readme](https://github.com/open-telemetry/opentelemetry-php#development).
11+
12+
This example uses Laravel version 8.0 .
13+
14+
## Step 1 - Creating a Laravel Application
15+
16+
The Laravel framework supports creating applications using composer. To do that, run `composer create-project <project-name>` . We are naming our project `otel-php-laravel-basic-example`, so the command is as follows:
17+
18+
`composer create-project laravel/laravel otel-php-laravel-basic-example`
19+
20+
To confirm that our application works, we can move to the application directory using `cd otel-php-laravel-basic-example` , then serve the application with `php artisan serve` .
21+
22+
![image](https://user-images.githubusercontent.com/22311928/112561555-c6ee7980-8dd5-11eb-844c-141d22334937.png)
23+
24+
Let's navigate to `http://127.0.0.1:8000` on our browser to see the default Laravel welcome page.
25+
26+
![image](https://user-images.githubusercontent.com/22311928/112561803-35cbd280-8dd6-11eb-8f6d-a4b60ee325b3.png)
27+
28+
## Step 2 - Require OpenTelemetry PHP Package
29+
30+
Laravel comes with most packages needed for development out of the box, so for this example, we will only require the open-telemetry PHP package. Let's run `composer require open-telemetry/opentelemetry` to pull that in.
31+
32+
** Notes **
33+
As of the time of writing this, Laravel ships with Guzzle version `^7.0.1`, but our open-telemetry PHP package uses Guzzle version `^6.2.0`, so pulling in open-telemetry PHP could lead to errors around unresolved packages. To fix the errors run `composer require guzzlehttp/guzzle:^6.2.0` to downgrade Guzzle first. Then run `composer require open-telemetry/opentelemetry` to pull in the open-telemetry package.
34+
35+
## Step 3 - Bundle Zipkin and Jaeger into the Application
36+
37+
To visualize traces exported from our application, we need to integrate open source tracing tools [Zipkin](https://zipkin.io/) and [Jaeger](https://www.jaegertracing.io/) into our setup using docker.
38+
39+
First, we create a `docker-compose.yaml` file in the root of our project, with content as follows:
40+
41+
```yaml
42+
version: '3.7'
43+
services:
44+
zipkin:
45+
image: openzipkin/zipkin-slim
46+
ports:
47+
- 9411:9411
48+
jaeger:
49+
image: jaegertracing/all-in-one
50+
environment:
51+
COLLECTOR_ZIPKIN_HTTP_PORT: 9412
52+
ports:
53+
- 9412:9412
54+
- 16686:16686
55+
```
56+
57+
Next, we pull in Zipkin and Jaeger by running `docker-compose up -d`. This might take some time, depending on your internet connection speed.
58+
59+
![image](https://user-images.githubusercontent.com/22311928/112565173-e8069880-8ddc-11eb-9220-0d50b7e92139.png)
60+
61+
We can confirm that Zipkin is up by navigating to `http://localhost:9411/` on our browser. For Jaeger, navigating to `http://localhost:16686/` on our browser should display the Jaeger home page.
62+
63+
![image](https://user-images.githubusercontent.com/22311928/110503699-bfa04e00-80fc-11eb-9186-c9b295d100f4.png)
64+
65+
![image](https://user-images.githubusercontent.com/22311928/110504108-1f96f480-80fd-11eb-9a2b-a7b4faf8b11c.png)
66+
67+
## Step 5 - Instrument Laravel Application
68+
69+
For this step, we will utilize our OpenTelemetry PHP Library to export traces to both Zipkin and Jaeger.
70+
71+
The default entry point for Laravel applications is the `index.php` file located in the `public` folder. If we navigate to `public\index.php` we can see that the index file autoloads classes from packages within our vendor folder, making them easily useable within our application.
72+
73+
```php
74+
require __DIR__.'/../vendor/autoload.php';
75+
```
76+
77+
The other parts of the `index.php` file enable request and response resolution using the application kernel.
78+
79+
```php
80+
$app = require_once __DIR__.'/../bootstrap/app.php';
81+
82+
$kernel = $app->make(Kernel::class);
83+
84+
$response = tap($kernel->handle(
85+
$request = Request::capture()
86+
))->send();
87+
88+
$kernel->terminate($request, $response);
89+
```
90+
It is worthy of note that resources(namespaces, classes, variables) created within the `index.php` file are available within the entire application.
91+
92+
To use open-telemetry specific classes within our application we have to import them at the top of our index file, using the `use` keyword. This is what our imports look like:
93+
94+
```php
95+
use OpenTelemetry\Contrib\Jaeger\Exporter as JaegerExporter;
96+
use OpenTelemetry\Contrib\Zipkin\Exporter as ZipkinExporter;
97+
use OpenTelemetry\Sdk\Trace\Clock;
98+
use OpenTelemetry\Sdk\Trace\Sampler\AlwaysOnSampler;
99+
use OpenTelemetry\Sdk\Trace\SamplingResult;
100+
use OpenTelemetry\Sdk\Trace\SpanProcessor\BatchSpanProcessor;
101+
use OpenTelemetry\Sdk\Trace\TracerProvider;
102+
use OpenTelemetry\Trace as API;
103+
```
104+
105+
Next, we create a sample recording trace using the [AlwaysOnSampler](https://github.com/open-telemetry/opentelemetry-php/blob/main/sdk/Trace/Sampler/AlwaysOnSampler.php) class, just before the Kernel instance is created like below:
106+
107+
```php
108+
$sampler = new AlwaysOnSampler();
109+
$samplingResult = $sampler->shouldSample(
110+
null,
111+
md5((string) microtime(true)),
112+
substr(md5((string) microtime(true)), 16),
113+
'io.opentelemetry.example',
114+
API\SpanKind::KIND_INTERNAL
115+
);
116+
```
117+
118+
Since we are looking to export traces to both Zipkin and Jaeger we have to make use of their exporters;
119+
120+
```php
121+
$jaegerExporter = new JaegerExporter(
122+
'Hello World Web Server Jaeger',
123+
'http://localhost:9412/api/v2/spans'
124+
);
125+
126+
$zipkinExporter = new ZipkinExporter(
127+
'Hello World Web Server Zipkin',
128+
'http://localhost:9411/api/v2/spans'
129+
);
130+
```
131+
132+
Next, we create a trace then add processors for each trace(One for Jaeger and another for Zipkin). Then we proceed to start and activate a span for each trace. We create a trace only if the RECORD AND SAMPLED sampling result condition passes as follows;
133+
134+
```php
135+
if (SamplingResult::RECORD_AND_SAMPLED === $samplingResult->getDecision()) {
136+
137+
$jaegerTracer = (new TracerProvider())
138+
->addSpanProcessor(new BatchSpanProcessor($jaegerExporter, Clock::get()))
139+
->getTracer('io.opentelemetry.contrib.php');
140+
141+
$zipkinTracer = (new TracerProvider())
142+
->addSpanProcessor(new BatchSpanProcessor($zipkinExporter, Clock::get()))
143+
->getTracer('io.opentelemetry.contrib.php');
144+
145+
$request = Request::createFromGlobals();
146+
$jaegerSpan = $jaegerTracer->startAndActivateSpan($request->getUri());
147+
$zipkinSpan = $zipkinTracer->startAndActivateSpan($request->getUri());
148+
149+
}
150+
```
151+
152+
Finally, we end the active spans if sampling is complete, by adding the following block at the end of the `index.php` file;
153+
154+
```php
155+
if (SamplingResult::RECORD_AND_SAMPLED === $samplingResult->getDecision()) {
156+
$zipkinTracer->endActiveSpan();
157+
$jaegerTracer->endActiveSpan();
158+
}
159+
```
160+
161+
Let's confirm that we can see exported traces on both Zipkin and Jaeger. To do that, we need to reload `http://127.0.0.1:8000` on our browser;
162+
163+
We also need reload both Zipkin and Jaeger on our browser, using the URLs `http://localhost:9411/` and `http://localhost:16686/`. Do ensure that both your Laravel server and docker instance are running for this step.
164+
165+
For Jaeger under service, you should see a `Hello World Web Server Jaeger` service. Go ahead and click find traces to see exported traces.
166+
167+
![image](https://user-images.githubusercontent.com/22311928/112386141-cc25c880-8cf0-11eb-84ae-56d5dc3bf4a0.png)
168+
169+
170+
Once we click on `Find Traces`, you should be able to see traces like below:
171+
172+
173+
![image](https://user-images.githubusercontent.com/22311928/112387947-bc0ee880-8cf2-11eb-88db-eb93a4170404.png)
174+
175+
176+
177+
We can click on a trace to get more information about the trace.
178+
179+
![image](https://user-images.githubusercontent.com/22311928/112388154-04c6a180-8cf3-11eb-8d22-abc9a9bc73b1.png)
180+
181+
182+
For Zipkin, we can visualize our trace by clicking on `Run Query`
183+
184+
![image](https://user-images.githubusercontent.com/22311928/111911625-9ec5ea00-8a66-11eb-90f8-2863a299a6de.png)
185+
186+
Since resources in Laravel's `public\index.php` file are available to the entire application, we can use any of the already instantiated tracers to further instrument controllers or any other parts of our application.
187+
188+
Let's create a `Hello` controller to check this out. Run the command `php artisan make:controller HelloController`
189+
190+
![image](https://user-images.githubusercontent.com/22311928/114498827-b5ea9880-9c1c-11eb-8603-dc1bc0400f77.png)
191+
192+
Next we need to add a route for accessing the controller. To do this we need to utilize the `HelloController` class within our web routes file located in the `routes\web.php` as follows:
193+
194+
```php
195+
use App\Http\Controllers\HelloController;
196+
```
197+
Next we need to add a route and method for the controller.
198+
199+
```php
200+
Route::get('/hello', [HelloController::class, 'index']);
201+
```
202+
The above snippet routes every GET request from the `/hello` route on the browser to an index method within the `HelloController` class. For now, this method does not exist, so we have to add it to our controller as follows
203+
204+
```php
205+
public function index(){
206+
return "hello";
207+
}
208+
```
209+
Let's confirm that everything works well by visiting the `/hello` route on our browser.
210+
211+
![image](https://user-images.githubusercontent.com/22311928/114500443-ea138880-9c1f-11eb-84c8-820bd8da8350.png)
212+
213+
Now that we have the `index` method working, we can simulate adding an exception event to our Zipkin trace as follows:
214+
215+
```php
216+
global $zipkinTracer;
217+
if ($zipkinTracer) {
218+
/** @var Span $span */
219+
$span = $zipkinTracer->getActiveSpan();
220+
221+
$span->setAttribute('foo', 'bar');
222+
$span->updateName('New name');
223+
224+
$zipkinTracer->startAndActivateSpan('Child span');
225+
try {
226+
throw new \Exception('Exception Example');
227+
} catch (\Exception $exception) {
228+
$span->setSpanStatus($exception->getCode(), $exception->getMessage());
229+
}
230+
$zipkinTracer->endActiveSpan();
231+
}
232+
```
233+
In the above snippet we change the span name and attributes for our Zipkin trace, we also add an exception event to the span.
234+
235+
We need to reload our `http://127.0.0.1:8000/hello` route, then navigate to Zipkin like before, to see that our span name gets updated to `new name` and our `Exception Example` is visible.
236+
237+
![image](https://user-images.githubusercontent.com/22311928/111915995-3cc2b000-8a79-11eb-82e9-78048da09b92.png)
238+
239+
## Summary
240+
With the above example we have been able to instrument a Laravel application using the OpenTelemetry PHP library. You can fork the example project [here](https://github.com/prondubuisi/otel-php-laravel-basic-example).

user-integration-guide/symfony-integration.md renamed to docs/symfony-integration.md

+13-10
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ To follow this guide you will need:
1111
* [Symfony CLI](https://symfony.com/download) for managing your Symfony application.
1212
* [Docker](https://docs.docker.com/get-docker/) for bundling our visualization tools. We have setup instructions for docker on this project's [readme](https://github.com/open-telemetry/opentelemetry-php#development).
1313

14+
This example uses Symfony version 5.2 .
15+
1416
## Step 1 - Creating a Symfony Application
1517

1618
Create a Symfony application by running the command `symfony new my_project_name`. We are calling this example `otel-php-symfony-basic-example`, so the command is as follows;
@@ -22,7 +24,7 @@ Create a Symfony application by running the command `symfony new my_project_name
2224
To define our routes within our controller methods, let's require the Doctrine annotation library by running the command `composer require doctrine/annotations`.
2325

2426
We can test that routes defined within Controllers work by creating a `HelloController.php` file within the `src\Controller` folder as follows:
25-
```
27+
```php
2628
<?php
2729

2830
namespace App\Controller;
@@ -67,7 +69,7 @@ To visualize traces from our application, we have to bundle open source tracing
6769

6870
Let's add a `docker-compose.yaml` file in the root of our project with the content as follows:
6971

70-
```
72+
```yaml
7173
version: '3.7'
7274
services:
7375
zipkin:
@@ -97,13 +99,13 @@ We can confirm that Zipkin is up by navigating to `http://localhost:9411/` on ou
9799

98100
Now it is time to utilize our OpenTelemetry PHP Library to export traces to both Zipkin and Jaeger.
99101

100-
## Step 5
102+
## Step 5 - Instrument Laravel Application
101103

102104
The entry point for all Symfony applications is the `index.php` file located in the `public` folder. Let's navigate to `public\index.php` to see what is happening. It is worthy of note that resources(namespaces, classes, variables) created within the `index.php` file are available within the entire application, by default the index file imports all auto loaded classes within the vendor folder. It also imports contents of the `.env` file. The other parts of the `index.php` file enable debugging as well as support request and response resolution using the application kernel.
103105

104106
To use open-telemetry specific classes we have to import them at the top of our index file, using the `use` keyword. This is what our imports look like:
105107

106-
```
108+
```php
107109
use App\Kernel;
108110
use OpenTelemetry\Contrib\Jaeger\Exporter as JaegerExporter;
109111
use OpenTelemetry\Contrib\Zipkin\Exporter as ZipkinExporter;
@@ -120,7 +122,7 @@ use Symfony\Component\HttpFoundation\Request;
120122

121123
Next, we create a sample recording trace using the [AlwaysOnSampler](https://github.com/open-telemetry/opentelemetry-php/blob/main/sdk/Trace/Sampler/AlwaysOnSampler.php) class, just before the Kernel instance is created like below:
122124

123-
```
125+
```php
124126
$sampler = new AlwaysOnSampler();
125127
$samplingResult = $sampler->shouldSample(
126128
null,
@@ -133,7 +135,7 @@ $samplingResult = $sampler->shouldSample(
133135

134136
Since we are looking to export traces to both Zipkin and Jaeger we have to make use of their individual exporters;
135137

136-
```
138+
```php
137139
$jaegerExporter = new JaegerExporter(
138140
'Hello World Web Server Jaeger',
139141
'http://localhost:9412/api/v2/spans'
@@ -147,7 +149,7 @@ $zipkinExporter = new ZipkinExporter(
147149

148150
Next we create a trace, and add processors for each trace(One for Jaeger and another for Zipkin). Then we proceed to start and activate a span for each trace. We create a trace only if the RECORD AND SAMPLED sampling result condition passes as follows;
149151

150-
```
152+
```php
151153
if (SamplingResult::RECORD_AND_SAMPLED === $samplingResult->getDecision()) {
152154
153155
$jaegerTracer = (new TracerProvider())
@@ -167,7 +169,7 @@ if (SamplingResult::RECORD_AND_SAMPLED === $samplingResult->getDecision()) {
167169

168170
Finally we end the active spans if sampling is complete, by adding the following block at the end of the `index.php` file;
169171

170-
```
172+
```php
171173
if (SamplingResult::RECORD_AND_SAMPLED === $samplingResult->getDecision()) {
172174
$zipkinTracer->endActiveSpan();
173175
$jaegerTracer->endActiveSpan();
@@ -178,7 +180,7 @@ lets confirm that we can see exported traces on both Zipkin and Jaeger. To do th
178180

179181
![image](https://user-images.githubusercontent.com/22311928/110263970-7810a980-7fb8-11eb-8683-b5f2d8a82c4a.png)
180182

181-
We also need to navigate to Zipkin and Jaeger on our browser, using the URLS `http://localhost:9411/` and `http://localhost:16686/`. Do ensure that both your symfony server and docker instance are running for this step.
183+
We also need reload both Zipkin and Jaeger on our browser, using the URLs `http://localhost:9411/` and `http://localhost:16686/`. Do ensure that both your symfony server and docker instance are running for this step.
182184

183185
For Jaeger under service, you should see a `Hello World Web Server Jaeger` service, go ahead and click find traces to see exported traces.
184186

@@ -205,7 +207,7 @@ Since resources in Symfony's `public\index.php` file are available to the entire
205207

206208
Lets try using the `addEvent` method, to capture errors within our controller as follows:
207209

208-
```
210+
```php
209211
global $zipkinTracer;
210212
if ($zipkinTracer) {
211213
/** @var Span $span */
@@ -223,6 +225,7 @@ global $zipkinTracer;
223225
$zipkinTracer->endActiveSpan();
224226
}
225227
```
228+
In the above snippet we change the span name and attributes for our Zipkin trace, we also add an exception event to the span.
226229

227230
We need to reload our `http://127.0.0.1:8000/hello` route, then navigate to Zipkin like before to see that our span name gets updated to `new name` and our `Exception Example` is visible
228231

0 commit comments

Comments
 (0)