loguru icon indicating copy to clipboard operation
loguru copied to clipboard

Set a local format for a bind logger

Open gygabyte017 opened this issue 4 years ago • 5 comments

Hi, in some methods of a class I need to always log a set of values together with the message. But this should happen only in those methods. I think the bind method is the answer, but I can't figure out how to format the message locally.

def f(i):
  context_logger = logger.bind(i=i)
  context_logger.info("Starting")
  ...
  context_logger.info("Ending")

Where to put the format for context_logger only without altering any other parameter?

gygabyte017 avatar Apr 07 '21 13:04 gygabyte017

Hi.

The format is configured in the handler. You probably need to use a custom function to differentiate between bound and default logger.

def formatter(record):
    if "i" in record["extra"]:
         return "[i={extra[i]}] {level} {message}\n{exception}"
    else:
        return "{level} {message}\n{exception}"

logger.add(sys.stderr, format=formatter)

Is that what you're looking for?

Delgan avatar Apr 07 '21 21:04 Delgan

Thank you for your answer, not quite, perhaps I am misinterpreting what the bind is for.

I try to explain what I have in mind: the "main" logger is already setup and configured with general options valid in the whole project; now, everywhere I use e.g. logger.info("Hi") and it works as expected (it is the "main" logger); but sometimes, I have a function somewhere which logs a lot, and I want to track some parameters which are repeated in every log call, so it would be boring to repeat them all the time. For example:

from loguru import logger

def a_function_without_this_need(param):
  logger.info("Start")
  logger.info("End")

a_function_without_this_need("Hello")
# Logs:
# Start
# End

def a_function_with_this_need(param):
  logger.info("Start with param={}", param)
  logger.info("Something with param={}", param)
  logger.info("Something else with param={}", param)
  logger.info("End with param={}", param)

a_function_with_this_need("Hello")
# Logs: 
# Start with param=Hello
# Something with param=Hello
# Something else with param=Hello
# End with param=Hello

So in the second function I have to repeat the logging of param every time and it is very verbose.

I was looking for something which would work like this:

from loguru import logger

def a_function_without_this_need(param):
  logger.info("Start")
  logger.info("End")

a_function_without_this_need("Hello")
# Logs:
# Start
# End
# >>>>> This is untouched and keeps working as before

def a_function_with_this_need(param):
  context_logger = logger.bind(param=param).format("{msg} with param={extra[param]}")  # Fake code
  context_logger.info("Start")
  context_logger.info("Something")
  context_logger.info("Something else")
  context_logger.info("End")

a_function_with_this_need("Hello")
# Logs: 
# Start with param=Hello
# Something with param=Hello
# Something else with param=Hello
# End with param=Hello
# >>>>> It works as before without touching the main logger and without specifing the param every time

Is there a way to achieve this in a simple way?

gygabyte017 avatar Apr 07 '21 21:04 gygabyte017

Thanks for the clarification. Currently, there is no easy way to achieve this. A possible workaround is to create a small utility function, although I agree this is not very satisfying.

def log(level, message):
    logger.log(level, message + f" with param={param}")

log("INFO", "Start")
log("INFO", "Something")
log("INFO", "Something else")
log("INFO", "End")

This is an interesting use case, though. It's definitely something that should be possible in the future as I plan to make the message formatting customizable (see #318).

Delgan avatar Apr 07 '21 22:04 Delgan

I just came to realization that you can use patch() also.

context_logger = logger.patch(lambda r: r.update(message=r["message"] + f" with param={param}"))
context_logger.info("Start")
context_logger.info("Something")
context_logger.info("Something else")
context_logger.info("End")

Be aware not to use it with opt(colors=True) because colorization won't work due to implementation details.

Delgan avatar Apr 10 '21 10:04 Delgan

Thanks it work great!

gygabyte017 avatar Apr 12 '21 10:04 gygabyte017