-
Notifications
You must be signed in to change notification settings - Fork 27
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 logging to API #2051
base: master
Are you sure you want to change the base?
Add logging to API #2051
Conversation
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.
Pausing here because I think the main comment about "why aren't we doing this the usual log4x way" is the most relevant and impacts the rest of the review.
import os | ||
|
||
|
||
class Logger: |
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.
discussion, blocking With the qualifier I am looking at this as a fairly standard Log4X-style logger, but there may be aspects /conventions in python that make this different...
Generally I would expect you to just use logging.getLogger in your classes like this
And configure your logs globally at the root logger level rather than doing it on child loggers, generally with something like logging.config.fileConfig or command line arguments or whatever).
Where you might have different settings for the main app thread vs. the worker thread and you could tune specific loggers
suggestion I think this is worth a discussion. You may have gone this way for specific reasons that I'm not aware of.
|
||
# Format message with context if provided | ||
if context: | ||
context_str = " ".join(f"{k}={v}" for k, v in context.items()) |
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 makes more sense to me in terms of standardizing your message string. you don't necessarily need to wrap logger to do this though, you could just write a 'format_context' function or whatever:
logger.info(format_context(message, context))
self.cloud_client: cloud_logging.Client = None | ||
|
||
# Prevent duplicate handlers | ||
if not self.logger.handlers: |
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.
I believe if we configure as discussed above you don't have to worry about this.
self.logger.addHandler(console_handler) | ||
|
||
# Google Cloud Logging handler; don't log to GCP if in debug | ||
if log_to_cloud and os.environ.get("FLASK_DEBUG") != "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.
Similarly you could configure this at the application level rather than in code at each logger declaration. Maybe there are reasons to only cloud log some things, but I'd generally expect it to be on/off for the whole app.
monitor_memory (bool): Whether to monitor memory usage (defaults to True) | ||
memory_threshold (int): Memory usage threshold to trigger warnings (default: 75%) | ||
memory_check_interval (int): How often to check memory in seconds (default: 5) |
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.
nitt, documentation Arguments in documentation not in the constructor. suggestion remove them
self.logger = logging.getLogger(self.name) | ||
self.logger.setLevel(logging.INFO) | ||
|
||
# Create log directory if it doesn't exist | ||
try: | ||
self.dir.mkdir(parents=True, exist_ok=True) | ||
except Exception as e: |
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.
question, blocking - who/what is this for? Google app engine looks at either stdout/stderr or the cloud logging API.
For that reason I'm surprised we write this by default even in the cloud.
suggestion I would not do this by default I would only do it in dev mode. In the context of the suggestion above I would pass a different config in dev than in prod.
Fixes #1947.
This PR adds logging to both the API and attached workers, as well as testing to the base
Logger
class (but not yetWorkerLogger
). I'm opening this in draft because there are still a few drawbacks (below) that I'm stuck on, and I'd love any assistance the requested reviewers might have. This is very much a work in progress, and as I've delved into some of the complexities of logging, I realize that there are a lot of considerations this code may not be handling, even at a design level. Any and all feedback is welcomed.Drawbacks:
before_request
feature, but that's not a solution.Logger
class, the service will actually create a new log file, perhaps because it is run within the worker thread, not the main API one. However, if logging with theWorkerLogger
class, the service has no access to the proper worker ID (at least, not that I found), so it will create a file with the wrong name and log to a separate file from the worker itself.