diff --git a/README.md b/README.md
index 951e4fe..ac2b116 100644
--- a/README.md
+++ b/README.md
@@ -35,12 +35,18 @@ The extension can be disabled via [runtime configuration](https://opentelemetry.
OTEL_PHP_DISABLED_INSTRUMENTATIONS=reactphp
```
-Custom HTTP methods can replace the known methods via environment variables, e.g.:
+Custom HTTP methods can replace the known methods via an environment variable, e.g.:
```shell
OTEL_INSTRUMENTATION_HTTP_KNOWN_METHODS="GET,HEAD,POST,PUT,DELETE,CONNECT,OPTIONS,TRACE,PATCH,MyCustomMethod"
```
+Additional HTTP query string parameters can be redacted via an environment variable, e.g.,
+
+```shell
+OTEL_PHP_INSTRUMENTATION_URL_SANITIZE_FIELD_NAMES="password,passwd,pwd,secret"
+```
+
Request and/or response headers can be added as span attributes via environment variables, e.g.:
```shell
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 50fd90f..c0b1f0e 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -32,6 +32,7 @@
+
diff --git a/src/ReactPHPInstrumentation.php b/src/ReactPHPInstrumentation.php
index 36f5987..9230108 100644
--- a/src/ReactPHPInstrumentation.php
+++ b/src/ReactPHPInstrumentation.php
@@ -56,6 +56,15 @@ class ReactPHPInstrumentation
* @see https://opentelemetry.io/docs/specs/semconv/http/http-spans/#http-client-span
*/
private const ENV_HTTP_RESPONSE_HEADERS = 'OTEL_PHP_INSTRUMENTATION_HTTP_RESPONSE_HEADERS';
+ /**
+ * The environment variable which adds to the URL query parameter keys to redact the values for.
+ * This supports a comma-separated list of case-sensitive query parameter keys.
+ *
+ * Note that this is not currently defined in OTel SemConv, and therefore subject to change.
+ *
+ * @see https://github.com/open-telemetry/semantic-conventions/issues/877
+ */
+ private const ENV_URL_SANITIZE_FIELD_NAMES = 'OTEL_PHP_INSTRUMENTATION_URL_SANITIZE_FIELD_NAMES';
/**
* The `{method}` component of the span name when the original method is not known to the instrumentation.
*
@@ -313,6 +322,12 @@ private static function sanitizeUrl(UriInterface $uri): string
$uri = $uri->withUserInfo(self::URL_REDACTION);
}
+ $sanitizeFields = self::URL_QUERY_REDACT_KEYS;
+ $customFields = $_ENV[self::ENV_URL_SANITIZE_FIELD_NAMES] ?? '';
+ if ($customFields !== '') {
+ $sanitizeFields = array_merge($sanitizeFields, explode(',', $customFields));
+ }
+
$queryString = $uri->getQuery();
// http_build_query(parse_str()) is not idempotent, so using Guzzle’s Query class for now
if ($queryString !== '') {
@@ -321,7 +336,7 @@ private static function sanitizeUrl(UriInterface $uri): string
$queryParameters,
array_intersect_key(
array_fill_keys(
- self::URL_QUERY_REDACT_KEYS,
+ $sanitizeFields,
self::URL_REDACTION
),
$queryParameters
diff --git a/tests/Integration/ReactPHPInstrumentationTest.php b/tests/Integration/ReactPHPInstrumentationTest.php
index 586bb86..ac4da19 100644
--- a/tests/Integration/ReactPHPInstrumentationTest.php
+++ b/tests/Integration/ReactPHPInstrumentationTest.php
@@ -111,7 +111,7 @@ public function test_fulfilled_promise(): void
$this->assertSame(['text/plain; charset=utf-8'], $span->getAttributes()->get(sprintf('%s.%s', TraceAttributes::HTTP_RESPONSE_HEADER, 'content-type')));
}
- public function test_fulfilled_promise_with_redactions(): void
+ public function test_fulfilled_promise_with_required_redactions(): void
{
$this->browser->request('GET', 'http://username@example.com/success')->then();
@@ -124,6 +124,14 @@ public function test_fulfilled_promise_with_redactions(): void
$this->assertSame('http://REDACTED:REDACTED@example.com/success?Signature=REDACTED', $span->getAttributes()->get(TraceAttributes::URL_FULL));
}
+ public function test_fulfilled_promise_with_custom_redactions(): void
+ {
+ $this->browser->request('GET', 'http://example.com/success?password=private')->then();
+
+ $span = $this->storage->offsetGet(0);
+ $this->assertSame('http://example.com/success?password=REDACTED', $span->getAttributes()->get(TraceAttributes::URL_FULL));
+ }
+
public function test_fulfilled_promise_with_overridden_methods(): void
{
$this->browser->request('CUSTOM', 'http://example.com:8888/success')->then();