-
Notifications
You must be signed in to change notification settings - Fork 983
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
Add a KServe module level logger #2884
Conversation
41e74b6
to
5c63f0c
Compare
python/kserve/kserve/logging.py
Outdated
"loggers": { | ||
KSERVE_LOGGER_NAME: { | ||
"handlers": ["default"], | ||
"level": KSERVE_LOGLEVEL, |
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.
So here we can then use environment variable to set the log level right? Let’s add the documentation for this here https://kserve.github.io/website/master/modelserving/v1beta1/custom/custom_model/#environment-variables
5c63f0c
to
b0f27b6
Compare
@@ -0,0 +1,51 @@ | |||
# Copyright 2022 The KServe Authors. |
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.
Hi @cmaddalozzo, I hope you don't mind me jumping into this PR. We rely extensively on KServe in our applications and would like to share some knowledge about how to configure loggers for libraries to ensure that the library's loggers do not interfere with application code.
First, please note that a library should ideally not be configuring its own logging, and definitely not be adding its own handlers. The "Note" warnings in the documentation explain the reasoning: https://docs.python.org/3/howto/logging.html#configuring-logging-for-a-library This was the cause of the original problem where KServe's logging configuration was taking over the logging configuration of our application code, which caused our logs to disappear. (Some of our logs are monitored for critical system health, so we rely on them extensively.)
Instead, the way it's typically done is shown in this StackOverflow post.
Concretely, the library code defines module-level loggers like this:
# my_library_class.py
import logging
# Note that no logging configuration is done here.
# This is critically important. This logger will inherit its configuration from the application code's configuration.
logger = logging.getLogger(__name__)
class MyLibraryClass:
def work(self) -> None:
logger.info("Logging message")
and the application code defines the logging configuration:
# main.py
import logging
def main() -> None:
start_application()
if __name__ == "__main__":
# This is where the application code will define its own logging configuration
# This includes the logging configuration for any libraries that it uses
logging.basicConfig(....)
main()
The way this PR is currently written, we have this line:
from .logging import logger
that is causing all of this logging configuration to be executed when ModelServer
is imported into application code. This is what causes problems for downstream application developers, because they do not expect a library that they import to change their existing logging configuration. The same concept applies elsewhere; for example, there is more logging configuration done here that also affects downstream application developers.
Of course, sometimes it is necessary for a library to configure a logger for debugging or testing purposes. That is fine, but when that is necessary, the logging configuration should be kept entirely separate from the code that is exposed to downstream application developers.
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.
Hi @hjweide,
You input is most welcome. We want to make sure we get this right so it stops causing issues for our users.
It seems a lot of popular web server libraries do configure their own logging (e.g. Uvicorn, Flask) despite the best practice outlined in the docs you shared. They do all seem to provide an easy way to override this functionality though. Your point about configuring the logger on import is well taken and I agree we should change this behavior.
Based on what you outlined I think it makes sense to remove the logging config from module import level and put it in a function (e.g. .logging.configure
). We can then add a configure_logging
flag to the ModelServer
class that allows users to disable logging config. Does that work for your use case?
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.
Thanks @cmaddalozzo for the reply! I think there might be 2 ways to move forward following different conventions:
- Do not setup logging inside the library
- Where we can have
setup_logging
function that takes alog_config
as adict
which has the default value we have here.
- Follow the
uvicorn
orflask
server paradigm to 1)setup logging in lib and 2)allow user to passlog_config
to the server instance to configure for logging. Indeed they configure logging in the library & they have provided easy way to configure custom logging.
- We can also change kserve's
ModelServer
andUvicornServer
to be like the originalUvicornServer
where a defaultlog_config
is provided but it is easily overwriteable.
Currently the kserve's ModelServer
's UvicornServer
has no way to overwrite the log_config
with a dict
. Also the current default log_config
file is wrong without this line.
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.
Thanks @cmaddalozzo for making the change! If I understand correctly that now we default to configure the logging by KSERVE_LOG_CONFIG
and provide options for developer to either disable log_config or passing log_config of their own. I saw that is consistent with what the UvicornServer
does. And it makes sense to me.
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.
hey @Yucao42, that's correct. We are basically relying on Uvicorn to configure all logging (both its own and KServe's) while giving the developer the option to disable this behavior by passing log_config=None
or provide their own config by passing log_config='path_to_log_config.[yaml|json]'
or log_config=log_config_dict
.
I don't love relying on Uvicorn to configure logging but it's convenient since they allow providing the config as either a path to the log config or a dictionary containing the config. One downside is that all logs output by the KServe logger will have the default configuration until the Uvicorn server is instantiated and the logging is configured.
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.
@Yucao42 hi! All the conversation makes sense, I am wondering if you could give me your opinion about the recent work that was added to allow Kserve to have a good access log format. Due to limitations in Uvicorn and ASGI specs (see encode/uvicorn#947 (comment)) I added a specific option to use asgi-logger and be able to add fields like User Agent etc.. to the access log format (otherwise it seems not possible with the default Uvicorn's config). The library creates its own handler and is able to allow a custom format like Gunicorn's one. The complete change is #2782. Do you think it is ok to keep it? Or do you have an alternative suggestion about how to customize the access logs? Thanks!
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.
@elukey Hello! I skimmed over the PR. I think there are two things that are fixed in this PR: 1) the default config value disables all other loggings, 2) we could preserve the UvicornServer
's API of passing config of our own (either Dict
or str
as a json file). How do we do 2) might be a little discussion: currently we add another variable of log_config
as Dict
. I advocate for using log_config
as both types like UvicornServer
here, but current code(adding logging_config
) in this PR makes sense too. I appreciate both your efforts! @cmaddalozzo @elukey !
b0f27b6
to
e02ca16
Compare
python/kserve/kserve/model_server.py
Outdated
@@ -93,6 +91,8 @@ def __init__(self, http_port: int = args.http_port, | |||
enable_grpc: bool = args.enable_grpc, | |||
enable_docs_url: bool = args.enable_docs_url, | |||
enable_latency_logging: bool = args.enable_latency_logging, | |||
configure_logging: bool = args.configure_logging, | |||
log_config: Optional[Dict] = 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.
Thanks @cmaddalozzo for making the change! This makes sense to me! One nit: shall we rename this log_config
arg name to log_config_dict
to clearly indicate the diff between this and log_config_file
?
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.
Or we can align with the UvicornServer
's __init__
where we just allow passing log_config
which could either be dict
or str
(and defaulted to KSERVE_LOG_CONFIG
) so that we can use only 1 argument. Also the code here can be simplified to
if configure_logging:
self.log_config = log_config
But I understand that might change the function signature, so the decision is yours:) IMHO, having one arg is 1)simper, 2)aligns better with UvicornServer
.
cae8e48
to
4ac6bcc
Compare
Signed-off-by: Curtis Maddalozzo <cmaddalozzo@bloomberg.net>
4ac6bcc
to
e343057
Compare
@@ -80,7 +77,8 @@ class ModelServer: | |||
enable_grpc (bool): Whether to turn on grpc server. Default: ``True`` | |||
enable_docs_url (bool): Whether to turn on ``/docs`` Swagger UI. Default: ``False``. | |||
enable_latency_logging (bool): Whether to log latency metric. Default: ``True``. | |||
log_config_file (dict): File path containing UvicornServer's log config. Default: ``None``. | |||
configure_logging (bool): Whether to configure KServe and Uvicorn logging. Default: ``True``. |
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.
@elukey Can you help review this change as you originally added the log_config_file
? We are trying to align with Uvicorn 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.
@yuzisun the change looks good to me, I'll test it in my settings to see if it all works but on paper I am +1
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.
Test went fine, asgi-logger worked as expected.
Signed-off-by: Dan Sun <dsun20@bloomberg.net>
Thanks everyone @cmaddalozzo @Yucao42 @hjweide and @elukey for the nice collaborations on the PR! /lgtm |
Signed-off-by: Dan Sun <dsun20@bloomberg.net>
/lgtm |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: cmaddalozzo, yuzisun The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
* Add a KServe module level logger Signed-off-by: Curtis Maddalozzo <cmaddalozzo@bloomberg.net> * Fix merge issue Signed-off-by: Dan Sun <dsun20@bloomberg.net> --------- Signed-off-by: Curtis Maddalozzo <cmaddalozzo@bloomberg.net> Signed-off-by: Dan Sun <dsun20@bloomberg.net> Co-authored-by: Dan Sun <dsun20@bloomberg.net>
* Add a KServe module level logger Signed-off-by: Curtis Maddalozzo <cmaddalozzo@bloomberg.net> * Fix merge issue Signed-off-by: Dan Sun <dsun20@bloomberg.net> --------- Signed-off-by: Curtis Maddalozzo <cmaddalozzo@bloomberg.net> Signed-off-by: Dan Sun <dsun20@bloomberg.net> Co-authored-by: Dan Sun <dsun20@bloomberg.net>
* Add a KServe module level logger (#2884) * Add a KServe module level logger Signed-off-by: Curtis Maddalozzo <cmaddalozzo@bloomberg.net> * Fix merge issue Signed-off-by: Dan Sun <dsun20@bloomberg.net> --------- Signed-off-by: Curtis Maddalozzo <cmaddalozzo@bloomberg.net> Signed-off-by: Dan Sun <dsun20@bloomberg.net> Co-authored-by: Dan Sun <dsun20@bloomberg.net> * Log exception stack Signed-off-by: Dan Sun <dsun20@bloomberg.net> --------- Signed-off-by: Curtis Maddalozzo <cmaddalozzo@bloomberg.net> Signed-off-by: Dan Sun <dsun20@bloomberg.net> Co-authored-by: Curtis Maddalozzo <cmaddalozzo@users.noreply.github.com>
* Add a KServe module level logger Signed-off-by: Curtis Maddalozzo <cmaddalozzo@bloomberg.net> * Fix merge issue Signed-off-by: Dan Sun <dsun20@bloomberg.net> --------- Signed-off-by: Curtis Maddalozzo <cmaddalozzo@bloomberg.net> Signed-off-by: Dan Sun <dsun20@bloomberg.net> Co-authored-by: Dan Sun <dsun20@bloomberg.net> Signed-off-by: iamlovingit <freecode666@gmail.com>
Tell uvicorn not to disable existing loggers
What this PR does / why we need it:
Which issue(s) this PR fixes :
Fixes #2851
Type of changes