Skip to content

Conversation

mdgilene
Copy link

@mdgilene mdgilene commented Oct 10, 2025

Description

Adds instrumentation for the python-arango library

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Originally implemented this instrumentation for a project at work. Unit tests verify ArangoDB client executions are intercepted and relevant request/response information is attached to the span.

Does This PR Require a Core Repo Change?

  • Yes. - Link to PR:
  • No.

Checklist:

See contributing.md for styleguide, changelog guidelines, and more.

  • Followed the style guidelines of this project
  • Changelogs have been updated
  • Unit tests have been added
  • Documentation has been updated

@mdgilene mdgilene requested a review from a team as a code owner October 10, 2025 17:51
Copy link

linux-foundation-easycla bot commented Oct 10, 2025

CLA Signed

The committers listed above are authorized under a signed CLA.

@mdgilene mdgilene force-pushed the feat/arangodb-instrumentor branch from 9aa83e6 to 3b74aca Compare October 10, 2025 17:55
@mdgilene mdgilene changed the title [WIP] Add ArangoDB Instrumentation Add ArangoDB Instrumentation Oct 10, 2025
@mdgilene
Copy link
Author

First time contributing here so looking for feedback.

Comment on lines 116 to 121
attributes = {
db_attributes.DB_SYSTEM_NAME: "arangodb",
db_attributes.DB_NAMESPACE: instance.db_name,
db_attributes.DB_OPERATION_NAME: request.endpoint,
db_attributes.DB_QUERY_TEXT: textwrap.dedent(query.strip("\n")),
}

Choose a reason for hiding this comment

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

As per https://opentelemetry.io/docs/specs/semconv/database/database-spans/ server.address/server.port should also be included.

Copy link
Author

Choose a reason for hiding this comment

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

Added server address and port info

else:
query = str(request.data)

attributes = {

Choose a reason for hiding this comment

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

Is there any case the table/collection could be extracted from the request and set?

Copy link
Author

Choose a reason for hiding this comment

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

Potentially? It would likely only be a guess at best. ArangoDB queries use a "SQL-ish" language called AQL. AQL queries can operate on one or more collections at a time. I'm not sure how this is handled in other database implementations, but open to suggestions.

Choose a reason for hiding this comment

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

So it would only be set if the query is known to only be operating on 1 table/collection. Looking at the http api there is api's for collections & documents would those be traced? If so you should be able to get collection from url.

Copy link
Author

Choose a reason for hiding this comment

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

Yes those should be traced. However it looks like those APIs take the collection name as part of the URL path. So would likely need to try to parse it out

_api/collection/<collection-name> or _api/document/<collection-name>

for key, value in bind_vars.items():
attributes[f"db.query.parameter.{key}"] = json.dumps(value)

attributes["db.query.options"] = json.dumps(options)
Copy link

@thompson-tomo thompson-tomo Oct 14, 2025

Choose a reason for hiding this comment

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

This feels too generic for me, I would recommend identifying if there is any options which would be important and defining them as arrangodb.*.

Copy link
Author

Choose a reason for hiding this comment

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

I went through and explicitly defined which query options to pull into the attributes. There are more than what I have defined but these seemed to me to be the "most useful"

Comment on lines 142 to 144
stats = extra.get("stats")
for key, value in stats.items():
attributes["db.execution.stats." + key] = value

Choose a reason for hiding this comment

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

Would avoid this as it is too generic and I'd remove it. I would suggest populating the key stats into metrics.

Copy link
Author

Choose a reason for hiding this comment

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

I just removed the stats part of this. I haven't really worked with metrics a whole lot so I will leave that as a future enhancement if needed and just focus on the traces.

Comment on lines 146 to 147
warnings = extra.get("warnings")
attributes["db.execution.warnings"] = json.dumps(warnings)

Choose a reason for hiding this comment

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

This feels more like data that would be on an event. I would leave off as no where else do we log warnings on the span.

Copy link
Author

Choose a reason for hiding this comment

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

Moved these warnings to span events

Copy link

@thompson-tomo thompson-tomo left a comment

Choose a reason for hiding this comment

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

Looks better but I feel we need to be careful about how many attributes we are adding to ensure that they all bring value as each attribute has a cost associated with adding them.

Comment on lines +179 to +180
if "count" in response.body:
attributes["arangodb.response.count"] = response.body.get("count")

Choose a reason for hiding this comment

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

What does count represent? Number of items?

Comment on lines +181 to +184
if "hasMore" in response.body:
attributes["arangodb.response.hasMore"] = response.body.get(
"hasMore"
)

Choose a reason for hiding this comment

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

I assume that has more is related to pagination? If so I don't see much benefit of it.

Comment on lines +149 to +152
if "failOnWarning" in options:
attributes[arangodb_attributes.FAIL_ON_WARNING] = options.get(
"failOnWarning"
)

Choose a reason for hiding this comment

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

This shouldn't be needed as the trace should convey this ie trace errored with xyz reason and trace has warning.

Comment on lines +157 to +160
if "maxRuntime" in options:
attributes[arangodb_attributes.MAX_RUNTIME] = options.get(
"maxRuntime"
)

Choose a reason for hiding this comment

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

Is this max execution time of query? If so would leave out as the error reason should convey when the limit has been hit.

Comment on lines +153 to +156
if "fullCount" in options:
attributes[arangodb_attributes.FULL_COUNT] = options.get(
"fullCount"
)

Choose a reason for hiding this comment

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

Leave this out as relates to stats

Comment on lines +143 to +146
if "allowRetry" in options:
attributes[arangodb_attributes.ALLOW_RETRY] = options.get(
"allowRetry"
)

Choose a reason for hiding this comment

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

I would leave this out as the usage is limited to batch reads & network failures.

Comment on lines +147 to +148
if "cache" in options:
attributes[arangodb_attributes.CACHE] = options.get("cache")

Choose a reason for hiding this comment

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

Rather than using true/false as the value, I would use skip for false & use for true.


ALLOW_RETRY = "arangodb.options.allowRetry"

CACHE = "arangodb.options.cache"

Choose a reason for hiding this comment

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

Suggest naming this arangodb.query.cache.


MAX_RUNTIME = "arangodb.options.maxRuntime"

STREAM = "arangodb.options.stream"

Choose a reason for hiding this comment

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

Suggest naming this arangodb.query.stream.

Comment on lines +163 to +166
if "usePlanCache" in options:
attributes[arangodb_attributes.USE_PLAN_CACHE] = options.get(
"usePlanCache"
)

Choose a reason for hiding this comment

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

I can't see this option in the js sdk, suggest removing it.

@xrmx xrmx moved this to Ready for review in @xrmx's Python PR digest Oct 15, 2025
@xrmx xrmx moved this from Ready for review to Reviewed PRs that need fixes in @xrmx's Python PR digest Oct 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Reviewed PRs that need fixes

Development

Successfully merging this pull request may close these issues.

2 participants