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
How to implement structured logging through indentation? #424
Comments
I've come up with this, but it feels pretty hacky: @contextmanager
def indent_logs(loggr, indent_size):
old_log = loggr._log
original_log = getattr(old_log, '_original_log', old_log)
old_indent_size = getattr(old_log, '_indent_size', 0)
new_indent_size = old_indent_size + indent_size
try:
def new_log(level_id, static_level_no, from_decorator, options, message, args, kwargs):
message = f'{ " " * new_indent_size }{message}'
original_log(level_id, static_level_no, from_decorator, options, message, args, kwargs)
new_log._indent_size = new_indent_size
new_log._original_log = original_log
loggr._log = new_log
yield
finally:
loggr._log = old_log Is there a better way? Or if not, is this at least safe from everything other than the API changes? |
Interestingly, this still looks like an use case that could be elegantly implemented once #318 is in place. In the meantime, what do you think of using a custom def formatter(record):
indentation = " " * record["extra"].get("indent_size", 0)
return "{time} | {level} | " + identation + "{message}\n{exception}"
logger.add(sys.stderr, format=formatter)
logger.info('this is the "root" level')
with logger.contextualize(indent_size=4):
logger.info("I'm at the first level") You could also replace |
I'm not at a computer to test, but perhaps I'm missing something... I'm looking for a more generic solution, i.e. one that will work regardless of how the logger is configured or how many sinks it has. Hence my hacky solution above. |
You're right. I assumed you could modify the handlers of your application. Currently there is no way to modify the formatted message generically. One possibility is to use @contextmanager
def indent_logs(indent_size):
yield logger.patch(lambda r: r.update(message=" " * indent_size + r["message"]))
logger.info('this is the "root" level')
with indent_logs(4) as logger:
logger.info("I'm at the first level") Note however that indenting logs can't always work perfectly. The first part of the logged message is not guaranteed to always be the same size (it changes depending on the calling function and the line number), and so it can cause vertical misalignment of the messages. |
Yes, you're right that indentation won't work if the pre-message part of the log lines are variable length. I should have mentioned that my log format has fixed length. Your solution with I've also realised that my function does not work with |
Oops, sorry for the incorrect workaround suggestion. I still think that from contextlib import contextmanager
from loguru import logger
from contextvars import ContextVar
logger_indentation = ContextVar('logger_indentation', default=0)
@contextmanager
def indent_logs(indent_size):
val = logger_indentation.get()
logger_indentation.set(val + indent_size)
yield
logger_indentation.set(val)
def patcher(record):
indentation = logger_indentation.get()
record.update(message=" " * indentation + record["message"])
logger = logger.patch(patcher)
logger.info('this is the "root" level')
with indent_logs(4):
logger.info("I'm at the first level")
with indent_logs(2):
logger.info("In second level")
logger.info("Back to level 1")
logger.info("Back root")
The downside contrary to your solution is that it requires to create a new |
Hi @Delgan, is this being actively pursued? It'd be great if Related SO thread: https://stackoverflow.com/questions/5498907/python-indentation-context-level-to-log-prefix-length |
Hi @ma-sadeghi. I don't think adding such new method to the The discussed solutions could be integrated in the documentation though. Do the previously suggested snippets generate the output you want or do you think they can be improved? |
@Delgan Loving the logger you've built :) I think my question could be related to this one, so just posting it here too.. I had an "issue" where when using
And I think it looks terrible like this.. So, with the python logging lib (and the help of people waaaay more smarter than myself :) ) I've ended up the below class:
The above class helps keep the indentation of the logged messages to be more like this:
And then for extremely long strings, like this:
I have this function:
Which displays the text like this:
And to get all of this to work, I need to create my own Define Logger:
Custom Logger:
So here I have a way of getting multi-line indentation aligned and have long strings wrapped, but there's 2 issues with this approach:
Losing the text wrapping is okay, but for readability, the |
Hi @edagnin. :) Have you thought about using a dynamic formatter with Loguru? By doing a quick test, I get the following result: import sys
from loguru import logger
def formatter(record):
base_format = "{time} {level} {name} {message} " + " " * 10
base = base_format.format_map(record)
lines = str(record["extra"].get("data", "")).splitlines()
indent = "\n" + " " * len(base)
reformatted = base + indent.join(lines)
record["extra"]["reformatted"] = reformatted
return "{extra[reformatted]}\n{exception}"
logger.remove()
logger.add(sys.stderr, format=formatter)
data = """---------------- Request ----------------
Headers : {"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Connection": "keep-alive",
"Content-Length": "20",
"User-Agent": "python-requests/2.27.1",
"cookie": "foo=bar; bar=baz",
"x-pretty-print": "2"}
URL : http://mockbin.com/request/test/1/mock?foo=bar&foo=baz
Method : POST
Body : {"foo": "bar"}"""
logger.info("Default message")
logger.bind(data=data).info("Message with data")
|
@Delgan I tested and works as you have shown, my only question is whether there's a way to achieve the same output without the need to use I thought perhaps when this is done: Also, why do we need to this: |
@edagnin You're welcome. ;)
I used
The exists the
I used |
@Delgan I just really want to say thank you for your replies, it truly is appreciated! I am in the process of replacing our logging with
Okay cool, this makes sense yes, so I would actually also prefer using the
Yeah, I looked at this, but I couldn't figure out how to actually get a formatter applied. But you've already given a better implementation using
Okay, so this is similar to this: Final question, what does this return statement do:
I would have thought this: Also, I am using this:
But it doesn't seem that the format is carried through to the HTML report.. Do you have any suggestions perhaps? |
Thanks for your gratitude, @edagnin. :)
This is kind of similar. The difference is that Loguru uses only one
The function must return the string format that will be used by Loguru to serialize the messages. The The
What is the HTML report you're referring? Aren't you seeing any logs reported at all, or aren't they formatted as expected? This might be related to the standard |
Awesome, thank you for the feedback! I've pretty much implemented your logger throughout our testing framework and it's working great! On the HTML report, I am using pytest-html to generate the report. Essentially this report just receives logs from the logger and puts it into a nice HTML report. I haven't configured anything other than telling it where to place the report. With So in your previous answer, you provided this formatter:
And I am using it for our logs.. So The log file itself (and console) I can see this format applied like this:
However, in the HTML report, it's like the format wasn't applied. So we see this:
Obviously this isn't of your concern, so I will see if I can figure it out, but if you do have some kind of insight or solution, please do share :) Maybe |
@edagnin Well, let me know if you think this is due to Loguru. I tested it birefly and it looked to work fine: |
My apologies, the issue was on my side.. I am still fine-tuning a few things and I had created this
With test like this:
And I discovered when I have the Not really sure why this would duplicate the log and "change" the formatter style, but at least I found the cause of my issue. I think up to this point, you've been more than helpful and thank you for that. With EDIT: I see it's doing this:
Which is what the setting is set for here:
And THEN, it's doing this:
Which is duplicating the output. |
@edagnin Maybe you don't need the |
@Delgan Thank you for your help! I have done a bit of a cleanup and everything is working as expected. I didn't end using your last suggestions, since after I did the cleanup it was working AND I am not using the So, with that said! Thank you for your help, genuinely appreciated! I hope this issue with all of its replies helps the next person too! :) |
I'd like to implement a context manager that will add some indentation to the log messages sent from within its scope.
Something like this:
Which would output:
But I'm unsure of what is the safest way to modify the logger so that all handlers and configurations are affected properly.
Any advice?
The text was updated successfully, but these errors were encountered: