diff options
| author | Saumit <justsaumit@protonmail.com> | 2025-09-27 02:14:26 +0530 |
|---|---|---|
| committer | Saumit <justsaumit@protonmail.com> | 2025-09-27 02:14:26 +0530 |
| commit | 82e03978b89938219958032efb1448cc76baa181 (patch) | |
| tree | 626f3e54d52ecd49be0ed3bee30abacc0453d081 /src/quote | |
Initial snapshot - OpenTelemetry demo 2.1.3 -f
Diffstat (limited to 'src/quote')
| -rw-r--r-- | src/quote/.dockerignore | 4 | ||||
| -rw-r--r-- | src/quote/.gitignore | 7 | ||||
| -rw-r--r-- | src/quote/Dockerfile | 39 | ||||
| -rw-r--r-- | src/quote/README.md | 46 | ||||
| -rw-r--r-- | src/quote/app/dependencies.php | 30 | ||||
| -rw-r--r-- | src/quote/app/routes.php | 74 | ||||
| -rw-r--r-- | src/quote/app/settings.php | 30 | ||||
| -rw-r--r-- | src/quote/composer.json | 34 | ||||
| -rw-r--r-- | src/quote/public/index.php | 94 | ||||
| -rw-r--r-- | src/quote/src/Application/Settings/Settings.php | 27 | ||||
| -rw-r--r-- | src/quote/src/Application/Settings/SettingsInterface.php | 18 |
11 files changed, 403 insertions, 0 deletions
diff --git a/src/quote/.dockerignore b/src/quote/.dockerignore new file mode 100644 index 0000000..98a3aae --- /dev/null +++ b/src/quote/.dockerignore @@ -0,0 +1,4 @@ +.dockerignore +.idea +Dockerfile +vendor diff --git a/src/quote/.gitignore b/src/quote/.gitignore new file mode 100644 index 0000000..63066a6 --- /dev/null +++ b/src/quote/.gitignore @@ -0,0 +1,7 @@ +.idea/ +.vscode/ +/coverage/ +/vendor/ +/logs/* +!/logs/README.md +.phpunit.result.cache diff --git a/src/quote/Dockerfile b/src/quote/Dockerfile new file mode 100644 index 0000000..9811889 --- /dev/null +++ b/src/quote/Dockerfile @@ -0,0 +1,39 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + + +FROM ghcr.io/mlocati/php-extension-installer:2.7.34 AS installer + +FROM docker.io/library/composer:2.8.9 AS vendor + +WORKDIR /tmp/ + +COPY ./src/quote/composer.json composer.json + +RUN composer install \ + --ignore-platform-reqs \ + --no-interaction \ + --no-plugins \ + --no-scripts \ + --no-dev \ + --prefer-dist + +FROM docker.io/library/php:8.4-cli-alpine3.22 + +COPY --from=installer /usr/bin/install-php-extensions /usr/local/bin/ + +RUN install-php-extensions opcache pcntl protobuf opentelemetry + +WORKDIR /var/www + +USER www-data + +COPY --from=vendor /tmp/vendor/ vendor/ + +COPY ./src/quote/app/ app/ +COPY ./src/quote/public/ public/ +COPY ./src/quote/src/ src/ + +EXPOSE ${QUOTE_PORT} + +CMD ["php", "public/index.php"] diff --git a/src/quote/README.md b/src/quote/README.md new file mode 100644 index 0000000..e1deaff --- /dev/null +++ b/src/quote/README.md @@ -0,0 +1,46 @@ +# Quote Service + +The Quote Service calculates the shipping costs, +based on the number of items to be shipped. + +It is a PHP based service, using a combination of automatic and manual instrumentation. + +## Docker Build + +To build the quote service, run the following from root directory +of opentelemetry-demo + +```sh +docker compose build quote +``` + +## Run the service + +Execute the below command to run the service. + +```sh +docker compose up quote +``` + +In order to get traffic into the service you have to deploy +the whole opentelemetry-demo. + +Please follow the root README to do so. + +## Development + +To build and run the quote service locally: + +```sh +docker build src/quote --target base -t quote +cd src/quote +docker run --rm -it -v $(pwd):/var/www -e QUOTE_PORT=8999 -p "8999:8999" quote +``` + +Then, send some curl requests: + +```sh +curl --location 'http://localhost:8999/getquote' \ +--header 'Content-Type: application/json' \ +--data '{"numberOfItems":3}' +``` diff --git a/src/quote/app/dependencies.php b/src/quote/app/dependencies.php new file mode 100644 index 0000000..5d9ee1d --- /dev/null +++ b/src/quote/app/dependencies.php @@ -0,0 +1,30 @@ +<?php +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + + + +declare(strict_types=1); + +use App\Application\Settings\SettingsInterface; +use DI\ContainerBuilder; +use OpenTelemetry\API\Globals; +use OpenTelemetry\Contrib\Logs\Monolog\Handler; +use Monolog\Logger; +use Psr\Container\ContainerInterface; +use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; + +return function (ContainerBuilder $containerBuilder) { + $containerBuilder->addDefinitions([ + LoggerInterface::class => function (ContainerInterface $c) { + $settings = $c->get(SettingsInterface::class); + $loggerSettings = $settings->get('logger'); + $handler = new Handler( + Globals::loggerProvider(), + LogLevel::INFO, + ); + return new Logger($loggerSettings['name'], [$handler]); + }, + ]); +}; diff --git a/src/quote/app/routes.php b/src/quote/app/routes.php new file mode 100644 index 0000000..2c2231a --- /dev/null +++ b/src/quote/app/routes.php @@ -0,0 +1,74 @@ +<?php +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + + + +use OpenTelemetry\API\Globals; +use OpenTelemetry\API\Trace\Span; +use OpenTelemetry\API\Trace\SpanKind; +use Psr\Http\Message\ResponseInterface as Response; +use Psr\Http\Message\ServerRequestInterface as Request; +use Psr\Log\LoggerInterface; +use Slim\App; + +function calculateQuote($jsonObject): float +{ + $quote = 0.0; + $childSpan = Globals::tracerProvider()->getTracer('manual-instrumentation') + ->spanBuilder('calculate-quote') + ->setSpanKind(SpanKind::KIND_INTERNAL) + ->startSpan(); + $childSpan->addEvent('Calculating quote'); + + try { + if (!array_key_exists('numberOfItems', $jsonObject)) { + throw new \InvalidArgumentException('numberOfItems not provided'); + } + $numberOfItems = intval($jsonObject['numberOfItems']); + $costPerItem = rand(400, 1000)/10; + $quote = round($costPerItem * $numberOfItems, 2); + + $childSpan->setAttribute('app.quote.items.count', $numberOfItems); + $childSpan->setAttribute('app.quote.cost.total', $quote); + + $childSpan->addEvent('Quote calculated, returning its value'); + + //manual metrics + static $counter; + $counter ??= Globals::meterProvider() + ->getMeter('quotes') + ->createCounter('quotes', 'quotes', 'number of quotes calculated'); + $counter->add(1, ['number_of_items' => $numberOfItems]); + } catch (\Exception $exception) { + $childSpan->recordException($exception); + } finally { + $childSpan->end(); + return $quote; + } +} + +return function (App $app) { + $app->post('/getquote', function (Request $request, Response $response, LoggerInterface $logger) { + $span = Span::getCurrent(); + $span->addEvent('Received get quote request, processing it'); + + $jsonObject = $request->getParsedBody(); + + $data = calculateQuote($jsonObject); + + $payload = json_encode($data); + $response->getBody()->write($payload); + + $span->addEvent('Quote processed, response sent back', [ + 'app.quote.cost.total' => $data + ]); + //exported as an opentelemetry log (see dependencies.php) + $logger->info('Calculated quote', [ + 'total' => $data, + ]); + + return $response + ->withHeader('Content-Type', 'application/json'); + }); +}; diff --git a/src/quote/app/settings.php b/src/quote/app/settings.php new file mode 100644 index 0000000..dd4774e --- /dev/null +++ b/src/quote/app/settings.php @@ -0,0 +1,30 @@ +<?php +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + + + +declare(strict_types=1); + +use App\Application\Settings\Settings; +use App\Application\Settings\SettingsInterface; +use DI\ContainerBuilder; +use Psr\Log\LogLevel; + +return function (ContainerBuilder $containerBuilder) { + // Global Settings Object + $containerBuilder->addDefinitions([ + SettingsInterface::class => function () { + return new Settings([ + 'displayErrorDetails' => true, // Should be set to false in production + 'logError' => false, + 'logErrorDetails' => false, + 'logger' => [ + 'name' => 'slim-app', + 'path' => 'php://stdout', + 'level' => LogLevel::DEBUG, + ], + ]); + } + ]); +}; diff --git a/src/quote/composer.json b/src/quote/composer.json new file mode 100644 index 0000000..0a921a6 --- /dev/null +++ b/src/quote/composer.json @@ -0,0 +1,34 @@ +{ + "name": "openteletry-demo/quote", + "description": "Quote Service part of OpenTelemetry Demo", + "license": "Apache-2.0", + "require": { + "php": ">= 8.3", + "ext-json": "*", + "ext-pcntl": "*", + "monolog/monolog": "3.9.0", + "open-telemetry/api": "1.6.0", + "open-telemetry/sdk": "1.8.0", + "open-telemetry/exporter-otlp": "1.3.2", + "open-telemetry/opentelemetry-auto-slim": "1.2.0", + "open-telemetry/detector-container": "1.1.0", + "open-telemetry/opentelemetry-logger-monolog": "1.1.0", + "guzzlehttp/guzzle": "7.10.0", + "php-di/php-di": "7.1.1", + "php-di/slim-bridge": "3.4.1", + "php-http/guzzle7-adapter": "1.1.0", + "react/http": "v1.11.0", + "slim/psr7": "1.7.1", + "slim/slim": "4.15.0" + }, + "autoload": { + "psr-4": { + "App\\": "src/" + } + }, + "config": { + "allow-plugins": { + "php-http/discovery": false + } + } +} diff --git a/src/quote/public/index.php b/src/quote/public/index.php new file mode 100644 index 0000000..fd02e5b --- /dev/null +++ b/src/quote/public/index.php @@ -0,0 +1,94 @@ +<?php +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + + + +declare(strict_types=1); + +use DI\Bridge\Slim\Bridge; +use DI\ContainerBuilder; +use OpenTelemetry\API\Globals; +use OpenTelemetry\SDK\Common\Configuration\Configuration; +use OpenTelemetry\SDK\Common\Configuration\Variables; +use OpenTelemetry\SDK\Logs\LoggerProviderInterface; +use OpenTelemetry\SDK\Metrics\MeterProviderInterface; +use OpenTelemetry\SDK\Trace\TracerProviderInterface; +use Psr\Http\Message\ServerRequestInterface; +use React\EventLoop\Loop; +use React\Http\HttpServer; +use React\Socket\SocketServer; +use Slim\Factory\AppFactory; + +require __DIR__ . '/../vendor/autoload.php'; + +// Instantiate PHP-DI ContainerBuilder +$containerBuilder = new ContainerBuilder(); + +// Set up settings +$settings = require __DIR__ . '/../app/settings.php'; +$settings($containerBuilder); + +// Set up dependencies +$dependencies = require __DIR__ . '/../app/dependencies.php'; +$dependencies($containerBuilder); + +// Build PHP-DI Container instance +$container = $containerBuilder->build(); + +// Instantiate the app +AppFactory::setContainer($container); +$app = Bridge::create($container); + +// Register middleware +$app->addRoutingMiddleware(); + +// Register routes +$routes = require __DIR__ . '/../app/routes.php'; +$routes($app); + +// Add Body Parsing Middleware +$app->addBodyParsingMiddleware(); + +// Add Error Middleware +$errorMiddleware = $app->addErrorMiddleware(true, true, true); +Loop::get()->addSignal(SIGTERM, function() { + exit; +}); + +/* workaround for non-async batch processors */ +if (($tracerProvider = Globals::tracerProvider()) instanceof TracerProviderInterface) { + Loop::addPeriodicTimer(Configuration::getInt(Variables::OTEL_BSP_SCHEDULE_DELAY)/1000, function() use ($tracerProvider) { + $tracerProvider->forceFlush(); + }); +} +if (($loggerProvider = Globals::loggerProvider()) instanceof LoggerProviderInterface) { + Loop::addPeriodicTimer(Configuration::getInt(Variables::OTEL_BLRP_SCHEDULE_DELAY)/1000, function() use ($loggerProvider) { + $loggerProvider->forceFlush(); + }); +} +if (($meterProvider = Globals::meterProvider()) instanceof MeterProviderInterface) { + Loop::addPeriodicTimer(Configuration::getInt(Variables::OTEL_METRIC_EXPORT_INTERVAL)/1000, function() use ($meterProvider) { + $meterProvider->forceFlush(); + }); +} + +$server = new HttpServer(function (ServerRequestInterface $request) use ($app) { + $response = $app->handle($request); + echo sprintf('[%s] "%s %s HTTP/%s" %d %d %s', + date('Y-m-d H:i:sP'), + $request->getMethod(), + $request->getUri()->getPath(), + $request->getProtocolVersion(), + $response->getStatusCode(), + $response->getBody()->getSize(), + PHP_EOL, + ); + + return $response; +}); +$address = '0.0.0.0:' . getenv('QUOTE_PORT'); +$socket = new SocketServer($address); +$server->listen($socket); + +echo "Listening on: {$address}" . PHP_EOL; diff --git a/src/quote/src/Application/Settings/Settings.php b/src/quote/src/Application/Settings/Settings.php new file mode 100644 index 0000000..b13fe4c --- /dev/null +++ b/src/quote/src/Application/Settings/Settings.php @@ -0,0 +1,27 @@ +<?php +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + + + +declare(strict_types=1); + +namespace App\Application\Settings; + +class Settings implements SettingsInterface +{ + private array $settings; + + public function __construct(array $settings) + { + $this->settings = $settings; + } + + /** + * @return mixed + */ + public function get(string $key = '') + { + return (empty($key)) ? $this->settings : $this->settings[$key]; + } +} diff --git a/src/quote/src/Application/Settings/SettingsInterface.php b/src/quote/src/Application/Settings/SettingsInterface.php new file mode 100644 index 0000000..0a1a586 --- /dev/null +++ b/src/quote/src/Application/Settings/SettingsInterface.php @@ -0,0 +1,18 @@ +<?php +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + + + +declare(strict_types=1); + +namespace App\Application\Settings; + +interface SettingsInterface +{ + /** + * @param string $key + * @return mixed + */ + public function get(string $key = ''); +} |
