I have a fastapi application and it is initialized with opentelemetry as follows:
CMD opentelemetry-instrument --logs_exporter otlp_proto_grpc --traces_exporter otlp_proto_grpc --metrics_exporter otlp_proto_grpc gunicorn wombo.fastapi:app --workers 2 --worker-class uvicorn.workers.UvicornH11Worker --bind 0.0.0.0:8000
I get a lot of the following warnings that clog the logs:
Invalid type dict for attribute 'graphql.param.paginator' value. Expected one of ['bool', 'str', 'bytes', 'int', 'float'] or a sequence of those types
Invalid type DisplayType for attribute 'graphql.param.displayType' value. Expected one of ['bool', 'str', 'bytes', 'int', 'float'] or a sequence of those types
I have added codes for them in the Additional Context section
I believe it should be able to set the span or skip it if necessary instead of clouding the logs with tons of these messages, as all of these are strawberry objects and they should be stringable.
The issue seems to be here:
strawberry/strawberry/extensions/tracing/opentelemetry.py
Lines 104 to 116 in 8a6a96d
Is there a workaround or a fix to do this?
I have the following python code that does get executed in its lifecyle during GraphQL resolve for one of the fields:
DisplayAsset
from typing import Optional, Union
import strawberry
from strawberry.scalars import ID
from datetime import datetime
from enum import unique, Enum
import logging
class LogErrorHandler(logging.Handler):
def emit(self, record):
span = get_current_span()
got_exception = False
if span is not None:
if record.exc_info is not None:
exc_type, exc_value, tb = record.exc_info
if exc_value is not None:
span.record_exception(exc_value)
got_exception = True
if record.levelno >= logging.ERROR or got_exception:
span.set_status(Status(StatusCode.ERROR, record.getMessage()))
LOG_ERROR_HANDLER = LogErrorHandler()
def get_logger_for_file(name):
"""
This is required because we cannot use logging.basicConfig as it messes with opentelemetry. But somehow
default log level is warn, so we manually have to set this everywhere to be INFO
"""
file_logger = logging.getLogger(name)
file_logger.addHandler(LOG_ERROR_HANDLER)
file_logger.setLevel(logging.INFO)
return file_logger
logger = get_logger_for_file(__name__)
@strawberry.enum
@unique
class DisplayType(Enum):
lottie = "lottie"
jpg = "jpg"
png = "png"
@strawberry.type
class DisplayAsset:
id: ID
url: str
type: DisplayType
updated_at: datetime
def populate_display_asset(
display_id: Optional[str],
display_url: str,
display_type: Union[DisplayType, str], # Ideally should be just str but don't want to change callers
display_updated_at: datetime) -> Optional[DisplayAsset]:
# A valid display asset does not exist
if display_id is None:
return None
if isinstance(display_type, DisplayType): # Use as is
return DisplayAsset(id=display_id, url=display_url, type=display_type, updated_at=display_updated_at)
elif isinstance(display_type, str):
try:
display_type_enum = DisplayType(display_type)
return DisplayAsset(id=display_id, url=display_url, type=display_type_enum, updated_at=display_updated_at)
except Exception as e:
logger.error(f"Can't create display asset object for ID:{display_id}, type:{display_type}, {e}",
exc_info=True)
return None
else: # This is something completely wrong
logger.error(f"type: {display_type} is incorrect for display_id:{display_id}")
return None
from typing import Optional
import strawberry
from strawberry import ID
@strawberry.input
class Paginator:
first: int = strawberry.field(description="Number of objects to get")
after: Optional[ID] = strawberry.field(description="like the start, can be a cursor too", default=None)
Pay now to fund the work behind this issue.
Get updates on progress being made.
Maintainer is rewarded once the issue is completed.
You're funding impactful open source efforts
You want to contribute to this effort
You want to get funding like this too