Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for motor (async pymongo) #1934

Open
jeff-goddard opened this issue Nov 10, 2023 · 3 comments
Open

Support for motor (async pymongo) #1934

jeff-goddard opened this issue Nov 10, 2023 · 3 comments
Labels

Comments

@jeff-goddard
Copy link

Is your feature request related to a problem? Please describe.
The Python Elastic APM agent does not automatically instrument async calls to mongodb using motor

Describe the solution you'd like
Support for automatic instrumentation of motor similar to the way pymongo is instrumented

Describe alternatives you've considered
For now we have lived without instrumentation, although it looks like it should be possible to adapt the pymongo wrappers

Additional context
This is the motor project: https://github.com/mongodb/motor
It should be similar to pymongo

@github-actions github-actions bot added agent-python community Issues opened by the community triage Issues awaiting triage labels Nov 10, 2023
@basepi basepi added feature instrumentation and removed triage Issues awaiting triage labels Nov 13, 2023
@basepi
Copy link
Contributor

basepi commented Nov 13, 2023

Thanks for the request!

@glexey
Copy link

glexey commented Apr 3, 2024

I found out that adopting opentelemetry's approach to tracing mongo driver events works well with motor. Here's a simple instrumenter based on this approach:

from elasticapm.traces import execution_context
from pymongo import monitoring

def instrument_app():
    mongo_tracer = MongoCommandTracer()
    monitoring.register(mongo_tracer)

class MongoCommandTracer(monitoring.CommandListener):

    def started(self, event: monitoring.CommandStartedEvent):
        command = event.command.get(event.command_name, "")
        name = event.command_name
        if command:
            name += f".{command}"

        transaction = execution_context.get_transaction()
        if transaction and transaction.is_sampled:
            transaction.begin_span(name=name, span_type="db", leaf=True, span_subtype="mongodb", span_action="query")

    def succeeded(self, event: monitoring.CommandSucceededEvent):
        transaction = execution_context.get_transaction()
        if transaction and transaction.is_sampled:
            transaction.end_span(skip_frames=0, duration=None, outcome="success")

    def failed(self, event: monitoring.CommandFailedEvent):
        transaction = execution_context.get_transaction()
        if transaction and transaction.is_sampled:
            transaction.end_span(skip_frames=0, duration=None, outcome="failure")

Original code adds extra metadata such as server host:port, which wasn't needed in my case.

@jeff-goddard
Copy link
Author

Thanks @glexey

I added a few more parameters for my own use case, and this works well.

Not sure if something like this should replace the official instrumentation?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants