kiwicom / structlog-sentry

Sentry integration for structlog
MIT License
102 stars 28 forks source link

'<' not supported between instances of 'str' and 'datetime.datetime' #159

Open dephiros opened 1 month ago

dephiros commented 1 month ago

Sentry SDK: 2.12.0 structlog-sentry: 2.1.0

Run into this error when trying to upgrade sentry-sdk and structlog-sentry '<' not supported between instances of 'str' and 'datetime.datetime'

I manage to track it down to https://github.com/getsentry/sentry-python/pull/3307 which is included in sentry-sdk 2.11 release.

Appreciate your help

antonpirker commented 1 month ago

Hey,

sentry-sdk maintainer here.

I tried to reproduce this with this example:

import logging
import os

import sentry_sdk
import structlog
from structlog_sentry import SentryProcessor

def main():
    sentry_sdk.init(
        dsn=os.environ.get("SENTRY_DSN"),
        traces_sample_rate=1.0,
        debug=True,
    )

    structlog.configure(
        processors=[
            structlog.processors.TimeStamper(fmt="iso"),
            structlog.stdlib.add_logger_name,  # optional, must be placed before SentryProcessor()
            structlog.stdlib.add_log_level,  # required before SentryProcessor()
            SentryProcessor(event_level=logging.ERROR),
        ],
        logger_factory=structlog.stdlib.LoggerFactory(),
        wrapper_class=structlog.stdlib.BoundLogger,
    )

    log = structlog.get_logger()

    try:
        1/0
    except ZeroDivisionError:
        log.error("zero division")

if __name__ == "__main__":
    main()

And I get a TypeError: Logger.error() missing 1 required positional argument: 'msg' so it seems I am too stupid to use structlog in the first place.

Can anyone show me what I am doing wrong?

dbowring commented 1 month ago

@antonpirker Try adding structlog.stdlib.ProcessorFormatter.wrap_for_formatter to the end of your processors.

antonpirker commented 4 weeks ago

@dbowring thanks for the help, that did the trick!

antonpirker commented 4 weeks ago

@dephiros

I have now this example app: https://github.com/antonpirker/testing-sentry/tree/main/test-structlog-sentry

But I do not get the '<' not supported between instances of 'str' and 'datetime.datetime'. Can you help me reproduce this?

dephiros commented 4 weeks ago

@antonpirker , super appreciated you help create the example.

It seems like the I am able to recreate the error by just reraise the exception:

https://github.com/dephiros/testing-sentry/blob/main/test-structlog-sentry/main.py#L34

 [sentry] DEBUG: [Profiling] Setting up continuous profiler in thread mode
{'event': 'zero division', 'timestamp': '2024-08-13T11:19:07.795262Z', 'logger': '__main__', 'level': 'error'}
 [sentry] ERROR: Internal error in sentry_sdk
Traceback (most recent call last):
  File "/Users/annguyen/works/testing-sentry/test-structlog-sentry/.venv/lib/python3.11/site-packages/sentry_sdk/integrations/excepthook.py", line 66, in 
sentry_sdk_excepthook
    sentry_sdk.capture_event(event, hint=hint)
  File "/Users/annguyen/works/testing-sentry/test-structlog-sentry/.venv/lib/python3.11/site-packages/sentry_sdk/api.py", line 162, in capture_event
    return get_current_scope().capture_event(event, hint, scope=scope, **scope_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/annguyen/works/testing-sentry/test-structlog-sentry/.venv/lib/python3.11/site-packages/sentry_sdk/scope.py", line 1135, in capture_event
    event_id = self.get_client().capture_event(event=event, hint=hint, scope=scope)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/annguyen/works/testing-sentry/test-structlog-sentry/.venv/lib/python3.11/site-packages/sentry_sdk/client.py", line 743, in capture_event
    event_opt = self._prepare_event(event, hint, scope)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/annguyen/works/testing-sentry/test-structlog-sentry/.venv/lib/python3.11/site-packages/sentry_sdk/client.py", line 452, in _prepare_event
    event_ = scope.apply_to_event(event, hint, self.options)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/annguyen/works/testing-sentry/test-structlog-sentry/.venv/lib/python3.11/site-packages/sentry_sdk/scope.py", line 148, in wrapper
    return fn(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/annguyen/works/testing-sentry/test-structlog-sentry/.venv/lib/python3.11/site-packages/sentry_sdk/scope.py", line 1442, in apply_to_event
    self._apply_breadcrumbs_to_event(event, hint, options)
  File "/Users/annguyen/works/testing-sentry/test-structlog-sentry/.venv/lib/python3.11/site-packages/sentry_sdk/scope.py", line 1299, in _apply_breadcrum
bs_to_event
    event["breadcrumbs"]["values"].sort(key=lambda crumb: crumb["timestamp"])
TypeError: '<' not supported between instances of 'datetime.datetime' and 'str'
Traceback (most recent call last):
  File "/Users/annguyen/works/testing-sentry/test-structlog-sentry/main.py", line 38, in <module>
    main()
  File "/Users/annguyen/works/testing-sentry/test-structlog-sentry/main.py", line 31, in main
    1/0
    ~^~
ZeroDivisionError: division by zero
 [sentry] DEBUG: atexit: got shutdown signal
 [sentry] DEBUG: atexit: shutting down client
antonpirker commented 4 weeks ago

Hey @dephiros ! Now I can reproduce! Thanks for your help!

The errors happens because one breadcrumb has a datetime and the other one a string. But I think I fix this in the SDK to always parse the timestamp if it is a string.

Barsoomx commented 3 weeks ago

log.error("zero division", timestamp=datetime.datetime.now()) seems like this also occurs if you do this and try to send an event? Event without TimeStamper you can manually pass different types into logger events (which turn into breadcrumbs)

import logging
import os

import sentry_sdk
import structlog
from structlog_sentry import SentryProcessor
import datetime

def main():
    sentry_sdk.init(
        dsn="http://abc@localhost/1",
        traces_sample_rate=1.0,
        debug=True,
        environment='debug'
    )

    structlog.configure(
        processors=[
            #structlog.processors.TimeStamper(fmt="iso", key='date'),
            structlog.stdlib.add_logger_name,
            structlog.stdlib.add_log_level,
            SentryProcessor(event_level=logging.ERROR),
            structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
        ],
        logger_factory=structlog.stdlib.LoggerFactory(),
        wrapper_class=structlog.stdlib.BoundLogger,
    )

    log = structlog.get_logger()

    try:
        1/0
    except ZeroDivisionError:
        log.error("zero division", timestamp=datetime.datetime.now())
        log.error("zero division", timestamp=datetime.datetime.now().isoformat())

if __name__ == "__main__":
    main()