Skip to content

Conversation

@birschick-bq
Copy link
Contributor

@birschick-bq birschick-bq commented Apr 22, 2025

Initial implementation of integrating OpenTelemetry tracing into the go drivers.

Instantiates an OTel exporter in the driverbase.database based on the value of the environment variable OTEL_TRACES_EXPORTER. Supports the values none, otlp, console and adbcfile

  • none (default) indicates no tracing should be exported.
  • otlp indicates tracing should be exported to an OTLP receiver. By default, the endpoint is http://localhost:4318 using HTTP/protobufs protocol. The OTLP exporter can be configured using the environment variables described here.
  • console indicates tracing should be exported to stdout.
  • adbcfile indicates tracing should be exported to a tracing file folder using a rotating file mechanism. The default folder is ~/.adbc/traces/ and the files are named according the driver used and the (UTC) time they were first created. Files are generated such that no file is greater than ~ 1 MB and there are never more than 100 archived trace files.

If the OTLP exporter option is chosen, a OTLP receiver is required. One option is the OpenTelemetry Collector.

This PR includes some instrumentation in the Snowflake driver to indicate how to add trace information.

Resolves: #2210

@birschick-bq birschick-bq changed the title feat(go/adbc): prototype OpenTelemetry rotating trace file exporter feat(go/adbc): prototype OpenTelemetry trace file exporter in go driver Apr 28, 2025
@birschick-bq birschick-bq marked this pull request as ready for review April 30, 2025 22:19
@birschick-bq birschick-bq requested a review from zeroshade as a code owner April 30, 2025 22:19
@github-actions github-actions bot added this to the ADBC Libraries 18 milestone Apr 30, 2025
@birschick-bq
Copy link
Contributor Author

@davidhcoe / @CurtHagenlocher
Prototype/POC for OTel tracing in the go drivers.

Copy link
Member

@zeroshade zeroshade left a comment

Choose a reason for hiding this comment

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

Overall looks good, but there's lots of areas that can be simplified or otherwise more concise. Plus a few spots where you lose the span context

@birschick-bq birschick-bq requested a review from zeroshade May 6, 2025 17:34
Copy link
Member

@zeroshade zeroshade left a comment

Choose a reason for hiding this comment

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

Just a few more comments, but this is looking good to me!

@lidavidm any comments?

Copy link
Member

Choose a reason for hiding this comment

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

Missing docstrings?

Copy link
Member

Choose a reason for hiding this comment

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

Why is ADBC handling this? If it's for testing, isn't the standard to dump to OTLP and let the OTLP collector dump to stdout/file?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This handles the scenario

  1. when the end-user is not authorized or doesn't want to install/deploy/configure an OTLP Collector
  2. the caller cannot directly capture stdout - for example when a background process is started

Copy link
Contributor

Choose a reason for hiding this comment

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

@lidavidm - Think about the scenario like what exists for ODBC drivers. If a user has an issue with a product that consumes the driver, they will need to capture logs to send to the support team of said product. The file writer enables the capturing of the text-based files so the user can send those logs, in addition to whatever product logs may exist.

If we call this through the C# interop layer (like we do with Snowflake and Flight SQL today), then the only way to get the captured output is via the log files because there is no stdout capability.

Copy link
Member

Choose a reason for hiding this comment

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

This doesn't integrate with the existing logging mechanism, though...

Copy link
Member

Choose a reason for hiding this comment

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

In any case, I'd ask

(1) let's not dump stuff directly into $HOME; can we use something more proper/follow desktop standards?
(2) adbcfile is a bit of an odd name to me, but I guess we're stuck trying not to accidentally alias some (future) standard OTel sink somehow
(3) Ideally we could use the existing log config as the sink? Or unify them? We currently have an API to set an slog sink; it would be best if there was at least some plan to migrate/settle on a single log provider, since OTel has its own logging API. (Thankfully, the current logging API is also in ext.go so I'm more comfortable updating it.)

Copy link
Member

Choose a reason for hiding this comment

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

Also, is there any upstream/community decision or discussion on file exporters? It seems odd to me that this isn't provided at all.

I see this: https://opentelemetry.io/docs/specs/otel/protocol/file-exporter/

Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't integrate with the existing logging mechanism, though...

Previously, I was not able to successfully get log outputs when I call through the C# interop layer. I only tried with Snowflake. I set the ADBC_DRIVER_SNOWFLAKE_LOG_LEVEL environment variable but couldn't find if/where the log output was written.

Copy link
Contributor Author

@birschick-bq birschick-bq May 9, 2025

Choose a reason for hiding this comment

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

@lidavidm / @davidhcoe

1. Migration from SLOG

  1. Logging levels don't really apply to traces. Traces have attributes, statuses or other properties which could be filtered before export. But that would be complicated to configure. That is a good candidate for using OTel Collector.
  2. Currently, the slog output (in go) is set to output to stderr. Which, as @davidhcoe notes, is difficult to redirect to a file in his scenario where new processes are being spawned in the background.
  3. Maybe we could transition such that if ADBC_DRIVER_{driver-name}_LOG_LEVEL is set to one of [debug, info, warn, warning, error], that would enable OTel traces to be exported to stdout (or stderr). That is, be the equivalent of OTEL_TRACES_EXPORTER=console

2. Location of trace files in file system

  1. I would just suggest that the location be portably available to the executing user.
  2. It ensures the user has the permission to write the files.
  3. It reduces a possible need for configuration option if the default is reasonable.
  4. It is more likely that it doesn't inadvertently expose the files to other users in the system.
  5. Your suggestion of os.UserConfigDir() is a good choice.

3. Choice of adbcfile for OTEL_TRACES_EXPORTER

  1. This seems to be the the most obvious choice given the existing values for this configuration. It avoids the possible name clash.
  2. From my exposure to the OTel SDK console exporters in different languages (go and C#) - they have different schema format. That is the main dilemma of why an existing file exporter has not be defined and published, yet. From my opinion, I don't think the schema matters for this use case - debug. If it was going to be consumed by some automation (i.e., OTel Collector), then schema would be important.
  3. They (OTel) seem to be stuck on the schema format. But I think also they have the requirement that the contents could be used for an automated pipeline. So schema is important in this scenario.

Copy link
Member

Choose a reason for hiding this comment

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

OTel conceives of logs and traces as part of the same thing, which is why I ask (and OTel has its own logging standard) - so ideally we would have some plan to unify logging, if not right now


defer func() {
if !st.SetErrorOnSpan(span, err) {
span.SetAttributes(attribute.Int64("db.response.returned_rows", numRows))
Copy link
Member

Choose a reason for hiding this comment

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

Is -1 ending up here potentially problematic for downstream consumers?

Copy link
Member

Choose a reason for hiding this comment

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

To be clear: does OTel accept -1 for this attribute? Since ADBC specifies -1 as the return value for when the number of rows is unknown.

Copy link
Member

Choose a reason for hiding this comment

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

Also do we need a utility for this given this pattern seems to recur?

Copy link
Contributor Author

@birschick-bq birschick-bq May 13, 2025

Choose a reason for hiding this comment

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

@lidavidm

Negative numbers are not a problem.


defer func() {
if !st.SetErrorOnSpan(span, err) {
span.SetAttributes(attribute.Int64("db.response.returned_rows", numRows))
Copy link
Member

Choose a reason for hiding this comment

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

To be clear: does OTel accept -1 for this attribute? Since ADBC specifies -1 as the return value for when the number of rows is unknown.


defer func() {
if !st.SetErrorOnSpan(span, err) {
span.SetAttributes(attribute.Int64("db.response.returned_rows", numRows))
Copy link
Member

Choose a reason for hiding this comment

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

Also do we need a utility for this given this pattern seems to recur?

@birschick-bq
Copy link
Contributor Author

@lidavidm

Also do we need a utility for this given this pattern seems to recur?

Yes. I agree.

In general, I'd say that I'm not really satisfied with the transparency of this instrumentation example. I was going to look at some other possible solutions like evaluating the original source as a lambda expressions.

This was only intended to give an example of instrumentation. We're going to need to provide a more complete instrumentation for each of the drivers. I'd suggest we look at improving the ease and reusability when we tackle instrumentation.

@lidavidm
Copy link
Member

@zeroshade any more comments?

I still think we need to unify log/trace config at some point, but we don't have to do that here

@zeroshade zeroshade self-requested a review May 13, 2025 16:05
Copy link
Member

@zeroshade zeroshade left a comment

Choose a reason for hiding this comment

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

LGTM now. I agree that we can address unifying logs and otel as a follow up

Copy link
Member

@lidavidm lidavidm left a comment

Choose a reason for hiding this comment

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

Thanks!

@lidavidm lidavidm merged commit d61c1b4 into apache:main May 14, 2025
41 checks passed
@lidavidm
Copy link
Member

I filed #2823 and #2824.

@birschick-bq birschick-bq deleted the dev/birschick-bq/go-otel-tracing branch June 11, 2025 17:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add end-user logging and tracing for drivers

4 participants