# Getting Started

> Get up and running with OpenTelemetry for PHP.

---

OpenTelemetry for PHP can be used to generate and export [traces][], [metrics][]
and [logs][].

This page will show you how to get started with OpenTelemetry in PHP. We will
create a simple "roll the dice" application, then apply both zero-code and code
based instrumentation to generate [traces][] and export them to the console. We
will then emit some [logs][] which will also be sent to the console.

## Prerequisites

OpenTelemetry requires PHP 8.0+ for zero-code instrumentation, however manual
instrumentation will work with PHP 7.4

Ensure that you have the following installed:

- [PHP 8.0+](https://www.php.net/)
- [PECL](https://pecl.php.net/)
- [composer](https://getcomposer.org/)

Before you get started make sure that you have both available in your shell:

```sh
php -v
composer -v
```

## Example Application

The following example uses a basic
[Slim Framework](https://www.slimframework.com/) application. If you are not
using Slim, that's OK — you can use OpenTelemetry PHP with other web frameworks
as well, such as WordPress, Symfony and Laravel. For a complete list of
libraries for supported frameworks, see the
[registry](/ecosystem/registry/?component=instrumentation&language=php).

### Dependencies

In an empty directory initialize a minimal `composer.json` file:

```sh
composer init \
  --no-interaction \
  --require slim/slim:"^4" \
  --require slim/psr7:"^1"
composer update
```

### Create and launch an HTTP Server

In that same directory, create a file called `index.php` with the following
content:

```php
<?php
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;

require __DIR__ . '/vendor/autoload.php';

$app = AppFactory::create();

$app->get('/rolldice', function (Request $request, Response $response) {
    $result = random_int(1,6);
    $response->getBody()->write(strval($result));
    return $response;
});

$app->run();
```

Run the application using the PHP built-in web server:

```shell
php -S localhost:8080
```

Open <http://localhost:8080/rolldice> in your web browser to ensure it is
working.

## Add zero-code instrumentation

Next, you’ll use the OpenTelemetry PHP extension to
[automatically instrument](/docs/zero-code/php/) the application.

1. Since the extension is built from source, you need to install some build
   tools

      <ul class="nav nav-tabs" id="tabs-0" role="tablist">
  <li class="nav-item">
      <button class="nav-link active"
          id="tabs-00-00-tab" data-bs-toggle="tab" data-bs-target="#tabs-00-00" role="tab"
          data-td-tp-persist="linux (apt)" aria-controls="tabs-00-00" aria-selected="true">
        Linux (apt)
      </button>
    </li><li class="nav-item">
      <button class="nav-link"
          id="tabs-00-01-tab" data-bs-toggle="tab" data-bs-target="#tabs-00-01" role="tab"
          data-td-tp-persist="macos (homebrew)" aria-controls="tabs-00-01" aria-selected="false">
        macOS (homebrew)
      </button>
    </li>
</ul>

<div class="tab-content" id="tabs-0-content">
    <div class="tab-body tab-pane fade show active"
        id="tabs-00-00" role="tabpanel" aria-labelled-by="tabs-00-00-tab" tabindex="0">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">sudo apt-get install gcc make autoconf
</span></span></code></pre></div>
    </div>
    <div class="tab-body tab-pane fade"
        id="tabs-00-01" role="tabpanel" aria-labelled-by="tabs-00-01-tab" tabindex="0">
        <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">brew install gcc make autoconf
</span></span></code></pre></div>
    </div>
</div>


2. Build the extension with `PECL`:

   ```sh
   pecl install opentelemetry
   ```

   > [!NOTE]
   >
   > Alternative methods of installing the extension are detailed in
   > [zero-code instrumentation](/docs/zero-code/php/#install-the-opentelemetry-extension).

3. Add the extension to your `php.ini` file:

   ```ini
   [opentelemetry]
   extension=opentelemetry.so
   ```

4. Verify that the extension is installed and enabled:

   ```sh
   php --ri opentelemetry
   ```

5. Add additional dependencies to your application, which are required for the
   automatic instrumentation of your code:

   ```sh
   composer config allow-plugins.php-http/discovery false
   composer require \
     open-telemetry/sdk \
     open-telemetry/opentelemetry-auto-slim
   ```

With the OpenTelemetry PHP extension set up and an instrumentation library
installed, you can now run your application and generate some traces:

```sh
env OTEL_PHP_AUTOLOAD_ENABLED=true \
    OTEL_TRACES_EXPORTER=console \
    OTEL_METRICS_EXPORTER=none \
    OTEL_LOGS_EXPORTER=none \
    php -S localhost:8080
```

Open <http://localhost:8080/rolldice> in your web browser and reload the page a
few times. After a while you should see the spans printed to your console:

<details>
<summary>View example output</summary>

```json
[
  {
    "name": "GET /rolldice",
    "context": {
      "trace_id": "16d7c6da7c021c574205736527816eb7",
      "span_id": "268e52331de62e33",
      "trace_state": ""
    },
    "resource": {
      "service.name": "__root__",
      "service.version": "1.0.0+no-version-set",
      "telemetry.sdk.name": "opentelemetry",
      "telemetry.sdk.language": "php",
      "telemetry.sdk.version": "1.0.0beta10",
      "telemetry.auto.version": "1.0.0beta5",
      "process.runtime.name": "cli-server",
      "process.runtime.version": "8.2.6",
      "process.pid": 24435,
      "process.executable.path": "/bin/php",
      "process.owner": "php",
      "os.type": "darwin",
      "os.description": "22.4.0",
      "os.name": "Darwin",
      "os.version": "Darwin Kernel Version 22.4.0: Mon Mar  6 20:59:28 PST 2023; root:xnu-8796.101.5~3/RELEASE_ARM64_T6000",
      "host.name": "OPENTELEMETRY-PHP",
      "host.arch": "arm64"
    },
    "parent_span_id": "",
    "kind": "KIND_SERVER",
    "start": 1684749478068582482,
    "end": 1684749478072715774,
    "attributes": {
      "code.function": "handle",
      "code.namespace": "Slim\\App",
      "code.filepath": "/vendor/slim/slim/Slim/App.php",
      "code.lineno": 197,
      "http.url": "http://localhost:8080/rolldice",
      "http.method": "GET",
      "http.request_content_length": "",
      "http.scheme": "http",
      "http.status_code": 200,
      "http.flavor": "1.1",
      "http.response_content_length": ""
    },
    "status": {
      "code": "Unset",
      "description": ""
    },
    "events": [],
    "links": []
  }
]
```

</details>

## Add manual instrumentation

### Traces

Manual tracing requires a `TracerProvider`. There are a number of ways to set
one up. In this example we will use the autoloaded TracerProvider, which is
globally available.

Replace `index.php` with the following code:

```php
<?php

use OpenTelemetry\API\Globals;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;

require __DIR__ . '/vendor/autoload.php';

$tracer = Globals::tracerProvider()->getTracer('demo');

$app = AppFactory::create();

$app->get('/rolldice', function (Request $request, Response $response) use ($tracer) {
    $span = $tracer
        ->spanBuilder('manual-span')
        ->startSpan();
    $result = random_int(1,6);
    $response->getBody()->write(strval($result));
    $span
        ->addEvent('rolled dice', ['result' => $result])
        ->end();
    return $response;
});

$app->run();
```

Start the built-in web server again, and browse to
<http://localhost:8080/rolldice>. You should see similar output, but with the
addition of a new span named `manual-span`.

Note that the manual span's `parent_span_id` contains the same value as the
"{closure}" span's `context.span_id`. Manual and automatic instrumentation work
well together, since under the hood they use the same APIs.

### Logging

Now let's add some logging. We will use the popular `monolog` logging library to
do this, via a handler which will emit the logs in the OpenTelemetry format.

First, let's install some more dependencies:

```shell
composer require \
  monolog/monolog \
  open-telemetry/opentelemetry-logger-monolog
```

Replace the `index.php` file with the following code:

```php
<?php

use Monolog\Logger;
use OpenTelemetry\API\Globals;
use OpenTelemetry\Contrib\Logs\Monolog\Handler;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Log\LogLevel;
use Slim\Factory\AppFactory;

require __DIR__ . '/vendor/autoload.php';

$loggerProvider = Globals::loggerProvider();
$handler = new Handler(
    $loggerProvider,
    LogLevel::INFO
);
$monolog = new Logger('otel-php-monolog', [$handler]);

$app = AppFactory::create();

$app->get('/rolldice', function (Request $request, Response $response) use ($monolog) {
    $result = random_int(1,6);
    $response->getBody()->write(strval($result));
    $monolog->info('dice rolled', ['result' => $result]);
    return $response;
});

$app->run();
```

Start the built-in web server with the following command (note the change to
`OTEL_LOGS_EXPORTER`):

```shell
env OTEL_PHP_AUTOLOAD_ENABLED=true \
    OTEL_TRACES_EXPORTER=console \
    OTEL_METRICS_EXPORTER=none \
    OTEL_LOGS_EXPORTER=console \
    php -S localhost:8080
```

This time when browsing to <http://localhost:8080/rolldice> you should see the
automatic instrumentation traces as before, and also a log record which was
generated from the monolog handler.

Note that `trace_id` and `span_id` were added to the log output, and that the
values correspond to the active span at the time the log message was generated.

<details>
<summary>View example output</summary>

```json
[
    {
        "name": "{closure}",
        "context": {
            "trace_id": "8b046fc5d43864058b6a5a18e0dfce3f",
            "span_id": "9cf24c78c6868bfe",
            "trace_state": ""
        },
        "resource": {
            "service.name": "__root__",
            "service.version": "1.0.0+no-version-set",
            "telemetry.sdk.name": "opentelemetry",
            "telemetry.sdk.language": "php",
            "telemetry.sdk.version": "1.0.0beta11",
            "telemetry.auto.version": "1.0.0beta6",
            "process.runtime.name": "cli-server",
            "process.runtime.version": "8.0.27",
            "process.pid": 2672,
            "process.executable.path": "\/usr\/local\/bin\/php",
            "process.owner": "root",
            "os.type": "linux",
            "os.description": "5.15.0-75-generic",
            "os.name": "Linux",
            "os.version": "#82-Ubuntu SMP Tue Jun 6 23:10:23 UTC 2023",
            "host.name": "f2c0afe83ea9",
            "host.arch": "x86_64"
        },
        "parent_span_id": "df2199a615085705",
        "kind": "KIND_INTERNAL",
        "start": 1687323704059486500,
        "end": 1687323704060820769,
        "attributes": {
            "code.function": "__invoke",
            "code.namespace": "Slim\\Handlers\\Strategies\\RequestResponse",
            "code.filepath": "\/usr\/src\/myapp\/vendor\/slim\/slim\/Slim\/Handlers\/Strategies\/RequestResponse.php",
            "code.lineno": 28
        },
        "status": {
            "code": "Unset",
            "description": ""
        },
        "events": [],
        "links": []
    }
]
[
    {
        "name": "GET \/rolldice",
        "context": {
            "trace_id": "8b046fc5d43864058b6a5a18e0dfce3f",
            "span_id": "df2199a615085705",
            "trace_state": ""
        },
        "resource": {
            "service.name": "__root__",
            "service.version": "1.0.0+no-version-set",
            "telemetry.sdk.name": "opentelemetry",
            "telemetry.sdk.language": "php",
            "telemetry.sdk.version": "1.0.0beta11",
            "telemetry.auto.version": "1.0.0beta6",
            "process.runtime.name": "cli-server",
            "process.runtime.version": "8.0.27",
            "process.pid": 2672,
            "process.executable.path": "\/usr\/local\/bin\/php",
            "process.owner": "root",
            "os.type": "linux",
            "os.description": "5.15.0-75-generic",
            "os.name": "Linux",
            "os.version": "#82-Ubuntu SMP Tue Jun 6 23:10:23 UTC 2023",
            "host.name": "f2c0afe83ea9",
            "host.arch": "x86_64"
        },
        "parent_span_id": "",
        "kind": "KIND_SERVER",
        "start": 1687323704058191192,
        "end": 1687323704060981779,
        "attributes": {
            "code.function": "handle",
            "code.namespace": "Slim\\App",
            "code.filepath": "\/usr\/src\/myapp\/vendor\/slim\/slim\/Slim\/App.php",
            "code.lineno": 197,
            "http.url": "http:\/\/localhost:8080\/rolldice",
            "http.method": "GET",
            "http.request_content_length": "",
            "http.scheme": "http",
            "http.status_code": 200,
            "http.flavor": "1.1",
            "http.response_content_length": ""
        },
        "status": {
            "code": "Unset",
            "description": ""
        },
        "events": [],
        "links": []
    }
]
{
    "resource": {
        "attributes": {
            "service.name": "__root__",
            "service.version": "1.0.0+no-version-set",
            "telemetry.sdk.name": "opentelemetry",
            "telemetry.sdk.language": "php",
            "telemetry.sdk.version": "1.0.0beta11",
            "telemetry.auto.version": "1.0.0beta6",
            "process.runtime.name": "cli-server",
            "process.runtime.version": "8.0.27",
            "process.pid": 2672,
            "process.executable.path": "\/usr\/local\/bin\/php",
            "process.owner": "root",
            "os.type": "linux",
            "os.description": "5.15.0-75-generic",
            "os.name": "Linux",
            "os.version": "#82-Ubuntu SMP Tue Jun 6 23:10:23 UTC 2023",
            "host.name": "f2c0afe83ea9",
            "host.arch": "x86_64"
        },
        "dropped_attributes_count": 0
    },
    "scope": {
        "name": "monolog",
        "version": null,
        "attributes": [],
        "dropped_attributes_count": 0,
        "schema_url": null,
        "logs": [
            {
                "timestamp": 1687323704059648000,
                "observed_timestamp": 1687323704060784128,
                "severity_number": 9,
                "severity_text": "INFO",
                "body": "dice rolled",
                "trace_id": "8b046fc5d43864058b6a5a18e0dfce3f",
                "span_id": "9cf24c78c6868bfe",
                "trace_flags": 1,
                "attributes": {
                    "channel": "otel-php-monolog",
                    "context": {
                        "result": 4
                    }
                },
                "dropped_attributes_count": 0
            }
        ]
    }
}
```

</details>

## What's next?

For more:

- Run this example with another [exporter][] for telemetry data.
- Try [zero-code instrumentation](/docs/zero-code/php/) on one of your own apps.
- Learn more about [manual instrumentation][] and try out some
  [examples](/docs/languages/php/examples/).
- Take a look at the [OpenTelemetry Demo](/docs/demo/), which includes the PHP
  based [Quote Service](/docs/demo/services/quote/).

[traces]: /docs/concepts/signals/traces/
[metrics]: /docs/concepts/signals/metrics/
[logs]: /docs/concepts/signals/logs/
[exporter]: ../exporters/
[manual instrumentation]: ../instrumentation/
