open-telemetry / open-telemetry/opentelemetry-python-contrib

Tornado: trace and span ID logging not working when raising HTTPErrors

Open
#1,063 5 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Python
Stars
1.1k
Forks
1.1k
Avg merge
4d 15h
Merged PRs (30d)
16

Description

Describe your environment

  • Platform Linux
  • Python 3.9.1
  • Tornado 6.1
  • OTel API + SDK + exporter 1.11.0
  • OTel Tornado + logging instrumentation 0.30b0

Steps to reproduce

When raising a Tornado HTTPError, there are no trace or span IDs in the logs. Traces end up in the collector just fine.

I can reproduce the issue with the following example by using these environment variables:

  • OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4317
  • OTEL_PYTHON_LOG_CORRELATION=true
  • OTEL_TRACES_EXPORTER=otlp
import tornado.ioloop
import tornado.web

from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.logging import LoggingInstrumentor
from opentelemetry.instrumentation.tornado import TornadoInstrumentor
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.set_status(200)

class ErrorHandler(tornado.web.RequestHandler):
    def get(self):
        raise tornado.web.HTTPError(401, "Foo")

def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
        (r"/error", ErrorHandler),
    ])

if __name__ == "__main__":

    provider = TracerProvider()
    exporter = OTLPSpanExporter()
    processor = BatchSpanProcessor(exporter)
    provider.add_span_processor(processor)
    trace.set_tracer_provider(provider)

    LoggingInstrumentor().instrument()
    TornadoInstrumentor().instrument()

    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

What is the expected behavior?

Logs should include the trace and span IDs for all requests.

What is the actual behavior?

Logs have the IDs for successful requests:

2022-04-20 09:03:37,487 INFO [tornado.access] [web.py:2239] [trace_id=2761d8e6686b29ae94006170d4c2cc1d span_id=e06216539c3d3f8b resource.service.name=unknown_service] - 200 GET / (127.0.0.1) 5.02ms

For anything that raises HTTPError, we get no IDs:

2022-04-20 09:03:45,119 WARNING [tornado.general] [web.py:1787] [trace_id=0 span_id=0 resource.service.name=unknown_service] - 401 GET /error (127.0.0.1): Foo
2022-04-20 09:03:45,120 WARNING [tornado.access] [web.py:2239] [trace_id=0 span_id=0 resource.service.name=unknown_service] - 401 GET /error (127.0.0.1) 5.53ms

Additional context

I worked around the problem to get one of the log lines show the IDs by overriding the Tornado _handle_request_exception private method in my base handler class to set the current span as an attribute, and then later on in the log method checking if there is a span attribute and logging the line within the span's context. I haven't yet figured out a way to show the IDs on both of the log lines produced, though.

Looks like all exceptions suffer from the same issues, not just HTTPError. By doing a division by zero in the error handler, I get the exception logged and added to the trace, but ID's at not present in the logs.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the example with TornadoInstrumentor and LoggingInstrumentor, then inspect the request exception path around Tornado's _handle_request_exception and the logging calls that produce the general and access lines. Done means requests that raise HTTPError or other exceptions retain their trace and span IDs in both log lines, while successful requests continue to work.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
observability
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.