Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion manifests/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -743,7 +743,6 @@ manifest:
tests/parametric/test_otel_env_vars.py::Test_Otel_Env_Vars::test_otel_traces_parentbased_on:
- declaration: missing_feature (The always_on sampler mapping is properly implemented in v1.2.0)
component_version: <=1.1.0
tests/parametric/test_otel_logs.py: missing_feature
tests/parametric/test_otel_logs.py::Test_FR07_Host_Name::test_hostname_from_dd_hostname: irrelevant (DD_HOSTNAME is only supported in Python)
tests/parametric/test_otel_metrics.py: missing_feature
tests/parametric/test_otel_span_methods.py::Test_Otel_Span_Methods: v0.94.0
Expand Down
4 changes: 3 additions & 1 deletion utils/build/docker/php/parametric/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
"amphp/http-server": "3.x-dev",
"amphp/http-server-router": "2.x-dev",
"amphp/log": "2.x-dev",
"open-telemetry/sdk": "^1.0.0",
"open-telemetry/api": "^1.0.0 <1.4.0",
"open-telemetry/sdk": "1.4.0",
"open-telemetry/exporter-otlp": "^1.0.0",
"symfony/http-client": "6.4.x-dev",
"nyholm/psr7": "^1.8@dev"
},
Expand Down
88 changes: 88 additions & 0 deletions utils/build/docker/php/parametric/server.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
ini_set("datadog.trace.generate_root_span", "0");
ini_set("datadog.trace.revolt_enabled", "0");

// Set OTLP endpoint before autoload so SdkAutoloader sees it
$_agentHost = getenv('DD_AGENT_HOST') ?: 'localhost';
if (!getenv('OTEL_EXPORTER_OTLP_ENDPOINT')) {
putenv('OTEL_EXPORTER_OTLP_ENDPOINT=http://' . $_agentHost . ':4318');
}

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

use Amp\ByteStream;
Expand All @@ -20,10 +26,18 @@
use DDTrace\Tag;
use Monolog\Logger;
use Monolog\Processor\PsrLogMessageProcessor;
use OpenTelemetry\API\Logs\LogRecord;
use OpenTelemetry\Contrib\Otlp\LogsExporterFactory;
use OpenTelemetry\SDK\Logs\LoggerProvider as SDKLoggerProvider;
use OpenTelemetry\SDK\Common\Configuration\Configuration;
use OpenTelemetry\SDK\Common\Configuration\Variables;
use OpenTelemetry\SDK\Common\Time\ClockFactory;
use OpenTelemetry\SDK\Logs\Processor\BatchLogRecordProcessor;
use OpenTelemetry\API\Trace\Propagation\TraceContextPropagator;
use OpenTelemetry\API\Trace\Span;
use OpenTelemetry\API\Trace\SpanKind;
use OpenTelemetry\API\Trace\StatusCode;
use OpenTelemetry\Context\Context;
use OpenTelemetry\Context\ScopeInterface;
use OpenTelemetry\SDK\Trace as SDK;
use OpenTelemetry\SDK\Trace\TracerProvider;
Expand Down Expand Up @@ -108,6 +122,22 @@ function remappedSpanKind($spanKind) {
$activeSpan = null;
/** @var array[] $spansDistributedTracingHeaders */
$spansDistributedTracingHeaders = [];
/** @var \OpenTelemetry\API\Logs\LoggerInterface[] $loggerDict */
$loggerDict = [];
// Build OTLP HTTP export pipeline for logs when enabled.
// LogsExporterFactory reads all OTEL_EXPORTER_OTLP_* env vars (endpoint, headers, protocol, timeout).
$_ddLogsOtelEnabled = strtolower(getenv('DD_LOGS_OTEL_ENABLED') ?: 'false') === 'true';
if ($_ddLogsOtelEnabled) {
// Explicitly read OTEL_EXPORTER_OTLP_LOGS_TIMEOUT so it is tracked for dd-trace telemetry
// (LogsExporterFactory only reads this when it's explicitly set, falling back to OTEL_EXPORTER_OTLP_TIMEOUT).
Configuration::getInt(Variables::OTEL_EXPORTER_OTLP_LOGS_TIMEOUT);
$_logExporter = (new LogsExporterFactory())->create();
$sdkLoggerProvider = SDKLoggerProvider::builder()
->addLogRecordProcessor(new BatchLogRecordProcessor($_logExporter, ClockFactory::getDefault()))
->build();
} else {
$sdkLoggerProvider = SDKLoggerProvider::builder()->build();
}

$router = new Router($server, $logger, $errorHandler);
$router->addRoute('POST', '/trace/span/start', new ClosureRequestHandler(function (Request $req) use (&$spans, &$activeSpan, &$spansDistributedTracingHeaders) {
Expand Down Expand Up @@ -498,6 +528,64 @@ function remappedSpanKind($spanKind) {

return jsonResponse([]);
}));
$router->addRoute('POST', '/otel/logger/create', new ClosureRequestHandler(function (Request $req) use (&$loggerDict, &$sdkLoggerProvider) {
$name = arg($req, 'name');

if (isset($loggerDict[$name])) {
return jsonResponse(['success' => false]);
}

$version = arg($req, 'version');
$schemaUrl = arg($req, 'schema_url');
$attributes = arg($req, 'attributes') ?? [];

$loggerDict[$name] = $sdkLoggerProvider->getLogger($name, $version, $schemaUrl, $attributes);

return jsonResponse(['success' => true]);
}));
$router->addRoute('POST', '/otel/logger/write', new ClosureRequestHandler(function (Request $req) use (&$loggerDict, &$otelSpans, &$spans) {
$loggerName = arg($req, 'logger_name');
$level = arg($req, 'level');
$message = arg($req, 'message');
$spanId = arg($req, 'span_id');

if (!isset($loggerDict[$loggerName])) {
return jsonResponse(['success' => false]);
}

$levelUpper = strtoupper((string)$level);
$severityMap = [
'TRACE' => ['number' => 1, 'text' => 'TRACE'],
'DEBUG' => ['number' => 5, 'text' => 'DEBUG'],
'INFO' => ['number' => 9, 'text' => 'INFO'],
'WARN' => ['number' => 13, 'text' => 'WARN'],
'ERROR' => ['number' => 17, 'text' => 'ERROR'],
'FATAL' => ['number' => 21, 'text' => 'FATAL'],
];
$severity = $severityMap[$levelUpper] ?? $severityMap['INFO'];

$logRecord = (new LogRecord($message))
->setSeverityNumber($severity['number'])
->setSeverityText($severity['text']);

if ($spanId !== null) {
if (isset($otelSpans[$spanId])) {
$context = $otelSpans[$spanId]->storeInContext(Context::getCurrent());
$logRecord->setContext($context);
} elseif (isset($spans[$spanId])) {
\DDTrace\switch_stack($spans[$spanId]);
$logRecord->setContext(Context::getCurrent());
}
}

$loggerDict[$loggerName]->emit($logRecord);

return jsonResponse(['success' => true]);
}));
$router->addRoute('POST', '/log/otel/flush', new ClosureRequestHandler(function (Request $req) use (&$sdkLoggerProvider) {
$sdkLoggerProvider->forceFlush();
return jsonResponse(['success' => true, 'message' => 'DDTrace']);
}));
$router->addRoute('GET', '/trace/config', new ClosureRequestHandler(function (Request $req) {

$tags_array = \dd_trace_env_config("DD_TAGS");
Expand Down
Loading