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

Opentelemetry integration does not set the Transaction/Span status #2107

Closed
daniil-konovalenko opened this issue May 13, 2023 · 1 comment · Fixed by #2115
Closed

Opentelemetry integration does not set the Transaction/Span status #2107

daniil-konovalenko opened this issue May 13, 2023 · 1 comment · Fixed by #2115

Comments

@daniil-konovalenko
Copy link
Contributor

daniil-konovalenko commented May 13, 2023

How do you use Sentry?

Self-hosted/on-premise

Version

1.22.2

Steps to Reproduce

I use the Opentelemetry integration to send my Opentelemetry spans to Sentry. My code is manually instrumented with the opentelemetry sdk.

I noticed that if an otel span has an error status, the status is not propagated to Sentry's transaction/span trace context.

Below is a script that demonstrates the issue. It needs the following requirements to work:

opentelemetry-api==1.17.0
opentelemetry-exporter-jaeger==1.17.0
opentelemetry-sdk==1.17.0
sentry-sdk==1.22.2

The script reports two transactions to sentry: one with opentelemetry sdk, and another one with sentry sdk.
My expectations are that the transactions' trace context would be similar, however the opentelemetry-created transaction does not have a status set.

import json

import sentry_sdk
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.propagate import set_global_textmap
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor
from opentelemetry.trace import Status, StatusCode
from sentry_sdk.integrations.opentelemetry import SentryPropagator, SentrySpanProcessor


tracer = trace.get_tracer("main")


class ConsoleTransport(sentry_sdk.Transport):
    """
    A sentry transport that stores events in memory and sends them to stdout
    instead of sending them to Sentry
    """

    def __init__(self):
        super().__init__()
        self.transactions = []

    def capture_envelope(self, envelope):
        for item in envelope.items:
            if item.data_category != "transaction":
                continue

            transaction = item.get_transaction_event()

            print("captured transaction:")
            print(json.dumps(transaction, indent=4))

            self.transactions.append(item.get_transaction_event())

    def find_transaction(self, name):
        for transaction in self.transactions:
            if transaction.get("transaction") == name:
                return transaction


def configure_sentry(transport):
    sentry_sdk.init(
        # fake dsn to enable tracing
        dsn="https://[email protected]/1",
        traces_sample_rate=1.0,
        transport=transport,
        instrumenter="otel",
    )


def configure_opentelemetry():
    provider = TracerProvider()

    # Sentry integration
    provider.add_span_processor(SentrySpanProcessor())
    set_global_textmap(SentryPropagator())

    # Console exporter to verify that the otel span has an error status
    exporter = ConsoleSpanExporter(
        formatter=lambda span: f"opentelemetry span:\n {span.to_json()}\n"
    )
    processor = SimpleSpanProcessor(exporter)
    provider.add_span_processor(processor)

    trace.set_tracer_provider(provider)


def create_otel_transaction():
    """
    Capture an example transaction with opentelemetry sdk
    :return:
    """
    with tracer.start_as_current_span(name="otel_transaction") as span:
        try:
            raise ValueError("from otel transaction")
        except ValueError as exc:
            span.record_exception(exc)
            span.set_status(Status(StatusCode.ERROR))
            # At this point I expect that sentry's transaction
            # would also have an error status


def create_sentry_transaction():
    """
    Capture an example transaction with sentry sdk
    """
    with sentry_sdk.start_transaction(
        name="sentry_transaction", instrumenter="otel"
    ) as transaction:
        try:
            raise ValueError("from sentry transaction")
        except ValueError:
            transaction.set_status("unknown_error")


def main():
    sentry_transport = ConsoleTransport()

    configure_sentry(sentry_transport)
    configure_opentelemetry()

    create_otel_transaction()
    create_sentry_transaction()

    otel_transaction = sentry_transport.find_transaction("otel_transaction")
    sentry_transaction = sentry_transport.find_transaction("sentry_transaction")
    sentry_transaction_status = sentry_transaction["contexts"]["trace"].get("status")

    otel_transaction_status = otel_transaction["contexts"]["trace"].get("status")

    print(f"Sentry-created transaction status: {sentry_transaction_status}")
    print(f"Otel-created transaction status: {otel_transaction_status}")


if __name__ == "__main__":
    main()

Expected Result

Sentry's Opentelemetry integration propagates the span status to the Sentry transaction trace context, and Sentry classifies the transaction as an error in the user interface.

Actual Result

The transaction created from an opentelemetry span does not have a status set.

Example script output:

captured sentry transaction:
{
    "type": "transaction",
    "transaction": "otel_transaction",
    "transaction_info": {
        "source": "custom"
    },
    "contexts": {
        "otel": {
            "resource": {
                "telemetry.sdk.language": "python",
                "telemetry.sdk.name": "opentelemetry",
                "telemetry.sdk.version": "1.17.0",
                "service.name": "unknown_service"
            }
        },
        "trace": {
            "trace_id": "3c27001c81f1e5f2be8fabebb94ef671",
            "span_id": "d251cd410abfd884",
            "parent_span_id": null,
            "op": "otel_transaction",
            "description": null
        },
        "runtime": {
            "name": "CPython",
            "version": "3.11.1",
            "build": "3.11.1 (main, Jan  2 2023, 14:37:00) [Clang 14.0.0 (clang-1400.0.29.202)]"
        }
    },
    "tags": {},
    "timestamp": "2023-05-13T09:39:13.480122Z",
    "start_timestamp": "2023-05-13T09:39:13.471644Z",
    "spans": [],
    "measurements": {},
    "event_id": "7031c540e1384cd2ba48699bcf95a075",
    "extra": {
        "sys.argv": [
            "/Users/danielkono/personal/python/sentry-transaction-mrrp/main2.py"
        ]
    },
    "environment": "production",
    "server_name": "danielkono-o",
    "sdk": {
        "name": "sentry.python",
        "version": "1.22.2",
        "packages": [
            {
                "name": "pypi:sentry-sdk",
                "version": "1.22.2"
            }
        ],
        "integrations": [
            "argv",
            "atexit",
            "dedupe",
            "excepthook",
            "logging",
            "modules",
            "redis",
            "stdlib",
            "threading"
        ]
    },
    "platform": "python"
}
opentelemetry span:
 {
    "name": "otel_transaction",
    "context": {
        "trace_id": "0x3c27001c81f1e5f2be8fabebb94ef671",
        "span_id": "0xd251cd410abfd884",
        "trace_state": "[]"
    },
    "kind": "SpanKind.INTERNAL",
    "parent_id": null,
    "start_time": "2023-05-13T07:39:13.471644Z",
    "end_time": "2023-05-13T07:39:13.480122Z",
    "status": {
        "status_code": "ERROR"
    },
    "attributes": {},
    "events": [
        {
            "name": "exception",
            "timestamp": "2023-05-13T07:39:13.480099Z",
            "attributes": {
                "exception.type": "ValueError",
                "exception.message": "from otel transaction",
                "exception.stacktrace": "Traceback (most recent call last):\n  File \"/Users/danielkono/personal/python/sentry-transaction-mrrp/main2.py\", line 85, in create_otel_transaction\n    raise ValueError(\"from otel transaction\")\nValueError: from otel transaction\n",
                "exception.escaped": "False"
            }
        }
    ],
    "links": [],
    "resource": {
        "attributes": {
            "telemetry.sdk.language": "python",
            "telemetry.sdk.name": "opentelemetry",
            "telemetry.sdk.version": "1.17.0",
            "service.name": "unknown_service"
        },
        "schema_url": ""
    }
}
captured sentry transaction:
{
    "type": "transaction",
    "transaction": "sentry_transaction",
    "transaction_info": {
        "source": "custom"
    },
    "contexts": {
        "trace": {
            "trace_id": "dd7179ed52b84d58bb59bacec4d07e36",
            "span_id": "b2d81c64dc73e927",
            "parent_span_id": null,
            "op": null,
            "description": null,
            "status": "internal_error"
        },
        "runtime": {
            "name": "CPython",
            "version": "3.11.1",
            "build": "3.11.1 (main, Jan  2 2023, 14:37:00) [Clang 14.0.0 (clang-1400.0.29.202)]"
        }
    },
    "tags": {},
    "timestamp": "2023-05-13T07:39:13.483194Z",
    "start_timestamp": "2023-05-13T07:39:13.483116Z",
    "spans": [],
    "measurements": {},
    "event_id": "892dda351c974b0fa73f8baaa17cf8a1",
    "extra": {
        "sys.argv": [
            "/Users/danielkono/personal/python/sentry-transaction-mrrp/main2.py"
        ]
    },
    "environment": "production",
    "server_name": "danielkono-o",
    "sdk": {
        "name": "sentry.python",
        "version": "1.22.2",
        "packages": [
            {
                "name": "pypi:sentry-sdk",
                "version": "1.22.2"
            }
        ],
        "integrations": [
            "argv",
            "atexit",
            "dedupe",
            "excepthook",
            "logging",
            "modules",
            "redis",
            "stdlib",
            "threading"
        ]
    },
    "platform": "python"
}
Sentry transaction status: internal_error
Otel transaction status: None
@antonpirker
Copy link
Member

Hey @daniil-konovalenko !

Thanks for the report and the code to reproduce this. This will be a big help in fixing this!

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

Successfully merging a pull request may close this issue.

4 participants