Skip to content

Conversation

jerrytfleung
Copy link

@jerrytfleung jerrytfleung commented Jul 14, 2025

Description:

Currently, there isn't any sqlcommenter for the database instrumentation library that allows customers to correlate their span with the database query. This PR adds the implementation for sqlcommenter according to semantic-conventions PR

  • Updated PDO instrumentation to add sqlcommenter feature

Testing:

Unit Testing:

Wrote unit tests for sql comment for correctness and they're all passing.

Manual Testing:

  1. Run sqlcommenter query against a PostgreSQL database
[
    {
        "name": "PDO::query",
        "context": {
            "trace_id": "5ecd8a80fabe727e8500067f49d48d53",
            "span_id": "9f68c175bb598ac8",
            "trace_state": "",
            "trace_flags": 1
        },
        "resource": {
            "service.name": "auto-pgsql",
            "host.name": "otel-VMware20-1",
            "host.arch": "aarch64",
            "host.id": "a6046f12d335446a880c0d1f7366f46a",
            "os.type": "linux",
            "os.description": "6.11.0-29-generic",
            "os.name": "Linux",
            "os.version": "#29~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Jun 26 13:59:03 UTC 2",
            "process.runtime.name": "cli-server",
            "process.runtime.version": "8.3.6",
            "process.pid": 5453,
            "process.executable.path": "\/usr\/bin\/php8.3",
            "process.owner": "otel",
            "sw.data.module": "apm",
            "sw.apm.version": "1.0.0+no-version-set",
            "telemetry.sdk.name": "opentelemetry",
            "telemetry.sdk.language": "php",
            "telemetry.sdk.version": "1.6.0",
            "telemetry.distro.name": "opentelemetry-php-instrumentation",
            "telemetry.distro.version": "1.1.3",
            "service.instance.id": "761e5800-ee00-45a0-96ad-0d1b4a61ca2f"
        },
        "parent_span_id": "2087ce003b4d0b9b",
        "kind": "KIND_CLIENT",
        "start": 1752530856479431714,
        "end": 1752530856649181220,
        "attributes": {
            "code.function.name": "PDO::query",
            "db.query.text": "SELECT code, name, region from country LIMIT 1\/*traceparent='00-5ecd8a80fabe727e8500067f49d48d53-9f68c175bb598ac8-01'*\/;",
            "server.address": "44.203.164.2",
            "db.namespace": "world-db",
            "db.system.name": "postgresql"
        },
        "status": {
            "code": "Unset",
            "description": ""
        },
        "events": [],
        "links": [],
        "schema_url": "https:\/\/opentelemetry.io\/schemas\/1.32.0"
    }
]

From PostgreSQL database server log,
We are able to find the modified SQL statement

2025-07-14 22:07:36.539 UTC,"world","world-db",450907,"24.87.147.252:58074",68757fa7.6e15b,1,"PARSE",2025-07-14 22:07:35 UTC,6/74550,0,LOG,00000,"duration: 0.543 ms  parse pdo_stmt_00000001: SELECT code, name, region from country LIMIT 1/*traceparent='00-5ecd8a80fabe727e8500067f49d48d53-9f68c175bb598ac8-01'*/;",,,,,,,,,"","client backend",,625232117496043950
2025-07-14 22:07:36.625 UTC,"world","world-db",450907,"24.87.147.252:58074",68757fa7.6e15b,2,"BIND",2025-07-14 22:07:35 UTC,6/74551,0,LOG,00000,"duration: 3.720 ms  bind pdo_stmt_00000001: SELECT code, name, region from country LIMIT 1/*traceparent='00-5ecd8a80fabe727e8500067f49d48d53-9f68c175bb598ac8-01'*/;",,,,,,,,,"","client backend",,625232117496043950
2025-07-14 22:07:36.625 UTC,"world","world-db",450907,"24.87.147.252:58074",68757fa7.6e15b,3,"SELECT",2025-07-14 22:07:35 UTC,6/74551,0,LOG,00000,"execute pdo_stmt_00000001: SELECT code, name, region from country LIMIT 1/*traceparent='00-5ecd8a80fabe727e8500067f49d48d53-9f68c175bb598ac8-01'*/;",,,,,,,,,"","client backend",,625232117496043950

Copy link

welcome bot commented Jul 14, 2025

Thanks for opening your first pull request! If you haven't yet signed our Contributor License Agreement (CLA), then please do so that we can accept your contribution. A link should appear shortly in this PR if you have not already signed one.

Copy link

codecov bot commented Jul 14, 2025

Codecov Report

❌ Patch coverage is 63.82979% with 34 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.29%. Comparing base (afc575f) to head (df60ca8).

Files with missing lines Patch % Lines
src/Instrumentation/PDO/src/PDOInstrumentation.php 23.80% 32 Missing ⚠️
src/Instrumentation/PDO/src/PDOTracker.php 88.23% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff              @@
##               main     #406      +/-   ##
============================================
- Coverage     81.55%   81.29%   -0.27%     
- Complexity     1659     1692      +33     
============================================
  Files           131      135       +4     
  Lines          6848     6923      +75     
============================================
+ Hits           5585     5628      +43     
- Misses         1263     1295      +32     
Flag Coverage Δ
Aws 93.41% <ø> (ø)
Context/Swoole 0.00% <ø> (ø)
Exporter/Instana 49.42% <ø> (ø)
Instrumentation/AwsSdk 81.13% <ø> (ø)
Instrumentation/CakePHP 20.40% <ø> (ø)
Instrumentation/CodeIgniter 73.98% <ø> (ø)
Instrumentation/Curl 87.66% <ø> (ø)
Instrumentation/Doctrine 92.92% <ø> (ø)
Instrumentation/ExtAmqp 88.48% <ø> (ø)
Instrumentation/ExtRdKafka 86.11% <ø> (ø)
Instrumentation/Guzzle 75.58% <ø> (ø)
Instrumentation/HttpAsyncClient 78.04% <ø> (ø)
Instrumentation/IO 70.68% <ø> (ø)
Instrumentation/Laravel 70.97% <ø> (ø)
Instrumentation/MySqli 95.81% <ø> (ø)
Instrumentation/OpenAIPHP 87.21% <ø> (ø)
Instrumentation/PDO 87.04% <63.82%> (-7.17%) ⬇️
Instrumentation/PostgreSql 93.54% <ø> (ø)
Instrumentation/Psr14 76.47% <ø> (ø)
Instrumentation/Psr15 89.15% <ø> (ø)
Instrumentation/Psr16 97.50% <ø> (ø)
Instrumentation/Psr18 77.46% <ø> (ø)
Instrumentation/Psr3 67.01% <ø> (ø)
Instrumentation/Psr6 97.61% <ø> (ø)
Instrumentation/ReactPHP 99.45% <ø> (ø)
Instrumentation/Slim 86.11% <ø> (ø)
Instrumentation/Symfony 84.88% <ø> (ø)
Instrumentation/Yii 77.86% <ø> (ø)
Logs/Monolog 100.00% <ø> (ø)
Propagation/CloudTrace 89.77% <ø> (ø)
Propagation/Instana 98.11% <ø> (ø)
Propagation/ServerTiming 100.00% <ø> (ø)
Propagation/TraceResponse 100.00% <ø> (ø)
ResourceDetectors/Azure 91.66% <ø> (ø)
ResourceDetectors/Container 93.02% <ø> (ø)
ResourceDetectors/DigitalOcean 100.00% <ø> (ø)
Sampler/RuleBased 33.51% <ø> (ø)
Sampler/Xray 78.23% <ø> (ø)
Shims/OpenTracing 92.45% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
.../Instrumentation/PDO/src/ContextInfoPropagator.php 100.00% <100.00%> (ø)
src/Instrumentation/PDO/src/ContextPropagation.php 100.00% <100.00%> (ø)
...c/Instrumentation/PDO/src/SqlCommentPropagator.php 100.00% <100.00%> (ø)
src/Instrumentation/PDO/src/Utils.php 100.00% <100.00%> (ø)
src/Instrumentation/PDO/src/PDOTracker.php 94.04% <88.23%> (ø)
src/Instrumentation/PDO/src/PDOInstrumentation.php 83.14% <23.80%> (-11.13%) ⬇️

Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update afc575f...df60ca8. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jerrytfleung jerrytfleung changed the title Service name propagator Add SQL commenter as context propagation for databases Jul 14, 2025
@@ -121,6 +125,18 @@ public static function register(): void
$span->setAttributes($attributes);

Context::storage()->attach($span->storeInContext($parent));
if (self::isSqlCommenterEnabled() && $sqlStatement !== 'undefined') {
$sqlStatement = self::appendSqlComments($sqlStatement, true);
$span->setAttributes([
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's redundant to include the sql comments in the statement, that info is already available in the trace. Does the spec suggest to do this? (comment applies to several similar bits of code)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spec doesn't suggest this, it's a bit from our own experience--capturing the final statement in the attribute helps easily diagnose integration issues by looking at the trace (e.g. database monitoring expects certain info in the comment but didn't see it) rather than digging through server logs/system views.

But there has been the concern that including this in db.query.text makes the value high cardinality which may not be handled well by all tracing backends. What we did for Python contrib was to make it a config option whether to collect query text before or after injection. Defer to PHP maintainers though, if you simply want to always capture pre-injection.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, there is no spec / requirement to update db.query.text.
As the instrumentation library changed the sql statement and the database is actually going to execute the modified statement, so updating TraceAttributes::DB_QUERY_TEXT to reflect the truth and it helps diagnose too.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracking the actual (mutated) statement that the DB executed does sound reasonable, but I do share the concern that it makes the value high cardinality. Can we make it opt-in?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. Done!


use OpenTelemetry\API\Trace\Propagation\TraceContextPropagator;

class Opentelemetry
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you look at what contrib-auto-slim does with the still-in-draft response propagation? I don't think this is a good class name, what does it do? Should there be an interface that it implements?

Copy link
Author

@jerrytfleung jerrytfleung Jul 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure which still in draft PR. Can you put the url here?

Agreed it is not a very good name, my initial thought is to keep the google sqlcommenter code. This class is to retrieve the trace context / service name required to comment sql statement. Do you have any idea about the class name?

I think using interface is a good idea as Instrumentation\MySqli will also call the same in order to retrieve trace context / service name.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think what this class needs to be is an instance of a TextMapPropagator, or perhaps its own interface like DatabaseQueryPropagationSetter (the terminology should be spec-driven), where "carrier" is a string (database query text), and this is where the query is injected with sql comments.

In my mind, it could work something like this (based on https://github.com/open-telemetry/opentelemetry-php-contrib/blob/main/src/Instrumentation/Slim/src/SlimInstrumentation.php#L100):

if ($dbPropagationEnabled && $doServiceNamePropagation) {
  if (class_exists(`\OpenTelemetry\...\ServiceNamePropagator`)) {
     $prop = new \OpenTelemetry\...\ServiceNamePropagator();
     $prop->inject($dbQueryString, DatabaseQueryPropagationSetter::instance(), $scope->context());
   }
}

//similar for trace context propagation

I also wonder if we should use Globals::propagator() for the trace context propagation. Users can configure and use different propagators or even provide their own (for example, for AWS x-ray), and may want those values to be propagated as well as/instead of ours? (Has this come up in spec discussions?)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, it seems we don't need the Opentelemetry class but putting the injection part to the instrumentation code.
I updated the code in this commit. Please correct me if I get it wrong.

SQL commenter in theory is to put information about the code in the query statement and it is not limited to trace context / service name. However, the majority implementation is putting a trace context ( or the proposed service name) so that user can correlate the trace with the database queries. Also, there is a limit about the length of the query statement so I am not so sure about opening up to different propagators and that is not in the spec discussions.

Copy link

@cheempz cheempz Jul 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re:

I also wonder if we should use Globals::propagator() for the trace context propagation. Users can configure and use different propagators or even provide their own (for example, for AWS x-ray), and may want those values to be propagated as well as/instead of ours? (Has this come up in spec discussions?)

Yes this was discussed in the spec, there seems consensus that the default would be the global propagator.

And users should be able to configure their own propagator(s), which if multiple, can be passed via a composite propagator. So my take is our implementation should accept a composite propagator instead of the current logic of running through "if with trace context", "if service propagator exists".

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Globals::propagator() can add flexibility to configure other propagators, but it would also add high
cardinality risk and make the comment longer.
I don't think it is a good idea to include Globals as we just want to correlate the trace with the database query.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High cardinality will result just from the traceontext propagation, but it is a valid concern about length of comment. We may need yet another 😬 config to limit that.

{
$setter ??= ArrayAccessGetterSetter::getInstance();

$detector = new Detectors\Service();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will need some thought. Resources including service.name can come from a variety of sources (for example, a yaml file in declarative config). I think the best source is the resource that's associated with the tracer, but that's not currently accessible. Perhaps service name needs to be added to SpanContext, so that it can be accessed in the same way that other components are for context propagation? That's a spec change, we won't just add it to our API and SDK.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, definitely the best source is from Tracer. Agreed it requires a spec change and involves a change in the API and SDK. I will monitor the discussion of the semconv to see what is the recommendation to get the service name.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this an active question or issue in spec and/or semconv? If so, can you link to it? I think it should block this being merged.

Copy link
Author

@jerrytfleung jerrytfleung Jul 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is the semconv PR. I saw you put comments there. Indeed there is still no breakthrough on how to get the service name.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The most recent comments on that PR suggest that it should be provided by the user and it may be the service name or some completely different user-defined value. We can wait for that to be agreed upon, but to me that means it's not coming from a detector, but actual user input. For an auto-instrumentation, that could be an env var or from declarative configuration.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the recommendation for MS SQL Server is to use CONTEXT_INFO instead of sqlcomment as propagation method. As mentioned in #406 (comment) i really think we should limit this first cut to just the DBMS we're testing against, e.g. MySQL and maybe Postgres.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Limited to postgresql and mysql only

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@brettmc The spec mentioned the The instrumentation implementation SHOULD append the comment to the end of the query. Should I remove the option to allow prepending the query?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It goes on to say that Semantic conventions for individual database systems MAY specify different format, which may include different position, encoding, or schema, depending on the specific database system's requirements or preferences.
Do we know of any databases where prepending could cause an issue, or is it just user preference (in which case, removing the option seems ok to me) ?

Copy link

@cheempz cheempz Aug 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd vote to keep this option in, based on the semconv discussion where there are valid reasons for the user to choose prepend depending on database.

@cheempz
Copy link

cheempz commented Jul 15, 2025

@jerrytfleung i know it's not addressed yet in the semconv, but we probably do want configuration to explicitly opt into particular DB systems. For example only enable for MySQL and not for MS SQL Server, etc.

@bobstrecansky bobstrecansky added the blocked blocked on some sort of external dependency label Jul 16, 2025
@jerrytfleung
Copy link
Author

@jerrytfleung i know it's not addressed yet in the semconv, but we probably do want configuration to explicitly opt into particular DB systems. For example only enable for MySQL and not for MS SQL Server, etc.

I am not sure if I get it correctly. Does it mean we need a different implementation for MS SQL Server? From this and that, it is using SET CONTEXT_INFO to pass information to MS SQL Server.

@cheempz
Copy link

cheempz commented Jul 16, 2025

@jerrytfleung i know it's not addressed yet in the semconv, but we probably do want configuration to explicitly opt into particular DB systems. For example only enable for MySQL and not for MS SQL Server, etc.

I am not sure if I get it correctly. Does it mean we need a different implementation for MS SQL Server? From this and that, it is using SET CONTEXT_INFO to pass information to MS SQL Server.

That's right, there's now the MS SQL Server-specific semconv that states trace context propagation should be achieved via the CONTEXT_INFO mechanism. So we should not blindly enable sqlcomment injection of trace context into every database system supported at the PDO level; we should allow explicit opt-in to each db system and i'd even say hardcoded to limit to systems where we've done some testing against.

@jerrytfleung jerrytfleung marked this pull request as ready for review July 18, 2025 19:34
@jerrytfleung jerrytfleung requested a review from a team as a code owner July 18, 2025 19:34
private static function addSqlComments(string $query): string
{
$comments = [];
$prop = TraceContextPropagator::getInstance();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

other propagators can be configured, and the correct way to get them would be via Globals

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Done.

@@ -329,4 +374,63 @@ private static function isDistributeStatementToLinkedSpansEnabled(): bool

return filter_var(get_cfg_var('otel.instrumentation.pdo.distribute_statement_to_linked_spans'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ?? false;
}

private static function isSqlCommenterEnabled(): bool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a lot of this code could be moved into its own class, and then be unit tested. It seems like it shouldn't be called "sql commenter" - that was the name of the product before it was donated to otel, but it looks like the objective is to define how to do something similar/better. I guess we're waiting on approved spec changes to see what the names of things should be?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I can see in the pending approval PR, the name should be something like "Context Propagation", and SQL commenter is a way to realize the "Context Propagation".

Is ContextPropagation a good class name for you?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Moved to its own class, named ContextPropagation. Please see if it looks good. Thanks!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you were to add the sql-server propagation mentioned elsewhere in this PR, would it be in that class, or in a different, similar class? I feel like it should be in a different one, and so maybe SqlPropagator is an interface, and SqlCommentPropagator is this class, with potentially a SqlServerPropagator coming later which uses set_context_info?

Copy link
Author

@jerrytfleung jerrytfleung Jul 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not going to implement the sql-server propagation in this PR so as to make this scope manageable but I am happy to add the placeholder class SqlServerPropagator first.

Done! I added the class SqlPropagatorInterface, SqlCommenterPropagator & SqlServerPropagator.

@jerrytfleung jerrytfleung requested a review from brettmc July 23, 2025 18:24
@jerrytfleung
Copy link
Author

@bobstrecansky open-telemetry/semantic-conventions#2495 has been merged to the spec. Can you please unblock this PR?
@brettmc I addressed the comments. Please take a look. Thanks!

Copy link

@cheempz cheempz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jerry I added a couple questions/comments. Also, now that the semconv is merged, maybe the README can refer to that instead.

case 'postgresql':
case 'mysql':
$comments = [];
Globals::propagator()->inject($comments);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jerrytfleung the semconv states:

The instrumentation SHOULD allow users to pass a propagator to overwrite the global propagator. If no propagator is provided by the user, instrumentation SHOULD use the global propagator.

It doesn't seem we currently support this. And side curiosity why not have the SqlCommentPropagator use global propagator directly instead of passing around $comments?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is because SqlCommentPropagator is not registered in Registry. The Globals::propagator() needs environment variable OTEL_PROPAGATORS https://github.com/open-telemetry/opentelemetry-php/blob/main/src/SDK/Propagation/PropagatorFactory.php#L21 to initialize the propagator list.


namespace OpenTelemetry\Contrib\Instrumentation\PDO;

class SqlServerPropagator implements SqlPropagatorInterface
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jerrytfleung naming nit ;)... IMHO ContextInfoPropagator makes more sense given the other one is called SqlCommentPropagator.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. Done

@jerrytfleung jerrytfleung force-pushed the service_name_propagator branch from 6e65ec4 to ee41b2a Compare August 29, 2025 19:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blocked blocked on some sort of external dependency
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants