-
Notifications
You must be signed in to change notification settings - Fork 631
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
Record logger name as the instrumentation scope name #3810
Record logger name as the instrumentation scope name #3810
Conversation
Overall LGTM, Please address the failed checks. |
Cache one Logger object per Python logger name in LoggingHandler
f3dc1d5
to
133bc7c
Compare
Thanks @srikanthccv I've addressed the failed lint check. I'm working on the missing CLA authorization. I'm contributing as a Snowflake employee, so the CLA authorization needs more than just my approval... |
70f7724
to
0576859
Compare
There is still one failing lint check: |
@@ -448,9 449,6 @@ def __init__( | |||
) -> None: | |||
super().__init__(level=level) | |||
self._logger_provider = logger_provider or get_logger_provider() | |||
self._logger = get_logger( | |||
__name__, logger_provider=self._logger_provider | |||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like a very significant change. But I am also confused about why the logging handler was storing a logger for it's own source namespace and not even the namespace of the logger it's added to. Can anyone explain the purpose of this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be more clear. This seems like a good change. I'm just surprised by how strange the existing code's functionality is. So, I wanted to make sure there was not a good reason for it to be that way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems a single logger instance stored in _logger
was chosen based on perceived need for performance, based on the discussion in the original issue #2485
Based on the discussion yesterday, I'm planning to use pytest-benchmark to test if there is a performance regression with creating one logger instance per logger name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you planning on including these tests as part of this pr?
|
||
def flush(self) -> None: | ||
""" | ||
Flushes the logging output. Skip flushing if logger is NoOp. | ||
""" | ||
if not isinstance(self._logger, NoOpLogger): | ||
self._logger_provider.force_flush() | ||
self._logger_provider.force_flush() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why remove the if statement here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no longer a single self._logger
instance to check against. Perhaps we need to keep track of all logger instances (one for each logger name encountered) and do a force_flush() if even a single one is not a NoOpLogger.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it. It doesn't seem like there would be an issue with flushing a no op either so I think this works fine. Resolved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there an issue here with typing, because the LoggerProvider
is of the API variety, as it comes from the API's get_logger_provider()
function, and it doesn't specify a force_flush()
method.
@@ -618,6 617,7 @@ def __init__( | |||
def resource(self): | |||
return self._resource | |||
|
|||
@lru_cache(maxsize=None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the point of this only to speed up the multiple calls to get logger? Or is there a functionality benefit. Given that the logs will be coming from multiple loggers, I am not sure this cache is necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is only to speed up the multiple calls to get_logger. We can be more explicit in keeping track of one logger instance per logger name encountered. @lru_cache
may not be the right approach here if we want to be able to retrieve all logger instances after they've been placed in the cache.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"@lru_cache may not be the right approach here if we want to be able to retrieve all logger instances after they've been placed in the cache"
Not sure I understood this. Could you explain further?
Please add cache tests and the pytest benchmarking you mentioned. Otherwise, looks great to me!
This is a great contribution. Please add some benchmark tests to see the implications of multiple calls to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the change. I've left a couple of questions.
self._logger.emit(self._translate(record)) | ||
logger = get_logger(record.name, logger_provider=self._logger_provider) | ||
if not isinstance(logger, NoOpLogger): | ||
logger.emit(self._translate(record)) | ||
|
||
def flush(self) -> None: | ||
""" | ||
Flushes the logging output. Skip flushing if logger is NoOp. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we update this comment?
|
||
def flush(self) -> None: | ||
""" | ||
Flushes the logging output. Skip flushing if logger is NoOp. | ||
""" | ||
if not isinstance(self._logger, NoOpLogger): | ||
self._logger_provider.force_flush() | ||
self._logger_provider.force_flush() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there an issue here with typing, because the LoggerProvider
is of the API variety, as it comes from the API's get_logger_provider()
function, and it doesn't specify a force_flush()
method.
Gentle ping on this issue. Are you still working on this? We will be reassigning this issue if we don't hear back from you. |
@@ -622,6 621,7 @@ def __init__( | |||
def resource(self): | |||
return self._resource | |||
|
|||
@lru_cache(maxsize=None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we are not defining maxsize this is the same as just @cache
right?
Gentle ping on this. Are you still planning to work on this? |
@sfc-gh-jopel has opened a PR with these changes: He will be following up on getting this fix merged. Thanks @sfc-gh-jopel! |
Description
Fixes issue #2485 Record logger name as the instrumentation scope name
Approach: Cache one Logger object per Python logger name in LoggingHandler. The
@lru_cache
annotation onget_logger
requires Python 3.2 or later.The Open Telemetry Spec specifies that the Logger Name SHOULD be recorded as the Instrumentation Scope name. Reference: open-telemetry/opentelemetry-specification#2359
This has already been implemented in Open Telemetry Java, but not in Open Telemetry Python.
Type of change
Please delete options that are not relevant.
How Has This Been Tested?
I've modified many of the tests in test_export.py to include an assert that tests that the logger name and instrumentation scope name are the same.
Will be happy to modify others to do the same validation.
Does This PR Require a Contrib Repo Change?
No, this brings Open Telemetry Python in line with the existing Open Telemetry spec.
Answer the following question based on these examples of changes that would require a Contrib Repo Change:
The OTel specification has changed which prompted this PR to update the method interfaces of
opentelemetry-api/
oropentelemetry-sdk/
The method interfaces of
test/util
have changedScripts in
scripts/
that were copied over to the Contrib repo have changedConfiguration files that were copied over to the Contrib repo have changed (when consistency between repositories is applicable) such as in
pyproject.toml
isort.cfg
.flake8
When a new
.github/CODEOWNER
is addedMajor changes to project information, such as in:
README.md
CONTRIBUTING.md
Yes. - Link to PR:
No.
Checklist: