-
Notifications
You must be signed in to change notification settings - Fork 704
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
Feature: multiple independent bound Loggers wich have separate set of handlers #72
Comments
I just tried my quick poc with the code snippet above, and it worked flawlessly - each task only logged to its own file. I have not yet checked what parts this change could break (Obviously bunch of tests fail with it) |
Hi @Fak3. This is an interesting use case. However, your workaround to bound handlers to Fortunately, there is a recipe for this kind of situation. Using async def runtasks():
async def worker(taskid):
logger.bind(taskid=taskid).info(f'ping {taskid}')
tasks = []
seen = set()
for taskid in (1,2,3,1):
if taskid not in seen:
def filter(record, taskid=taskid):
return record["extra"]["taskid"] == taskid
logger.add(f'{taskid}.log', filter=filter)
seen.add(taskid)
tasks.append(asyncio.create_task(worker(taskid)))
await asyncio.gather(*tasks)
asyncio.run(runtasks()) The only downside is that each log has to be checked by the filter function of each added handler. This may needlessly slow down your application if you have really a lot of task identifiers. In such case, I would suggest to use one unique handler with a custom sink selecting file appropriately based on the async def runtasks():
async def worker(taskid):
logger.bind(taskid=taskid).info(f'ping {taskid}')
def sink(message):
taskid = message.record["extra"]["taskid"]
with open(f'{taskid}.log', 'a') as file:
file.write(message)
logger.add(sink)
tasks = []
for taskid in (1,2,3,1):
tasks.append(asyncio.create_task(worker(taskid)))
await asyncio.gather(*tasks)
asyncio.run(runtasks()) |
Thanks. I think the last example would fit my needs, albeit sad that it takes much more lines. The downisde is that file will be constantly opened\closed on each log message. It will require more code lines to prevent that... Actually I wonder if something like "ParametrizedFileSink" could become part of loguru standard package. Currently loguru already supports parametrizing with {time} for rotation, and it could theoretically become even smarter about filenames. |
The problem is that the file handler actually does not know what should be parametrized. Currently, it just has to call Actually, I think the solution I first proposed and your I will try to think about an improvement of the API so it can handle such use case. |
Next Loguru release will add a way to use independent loggers, finally. 🎉 I refactored the So, if you want to use independent loggers with independent handlers, you just have to call import asyncio
import copy
from loguru import logger
async def runtasks():
async def worker(taskid, log):
log.info(f'ping {taskid}')
loggers = {}
logger.remove()
for taskid in (1, 2, 3):
task_logger = copy.deepcopy(logger)
task_logger.add(f'{taskid}.log')
loggers[taskid] = task_logger
tasks = []
for taskid in (1,2,3,1):
log = loggers[taskid]
tasks.append(asyncio.create_task(worker(taskid, log)))
await asyncio.gather(*tasks)
asyncio.run(runtasks()) This is intentionally not part of the Loguru API (it requires to import the Note that most of the handlers are not picklable by default, so it's better to I also added this to the documentation: I will close this issue once next Loguru release is published. 😉 |
@Delgan any news about the release? I'm in the same situation and would start using deepcopy rather than bind. Thanks. |
@lsabi I just published the The |
Hi. In my app I found a need to create a Logger with its own set of handlers, disjoint with the root logger.
In my usecase, I am trying to spawn many tasks (async functions), so that each task logs into its own file. Minimal code snippet:
In this code snippet, i spawn 4 tasks, with identifiers 1, 2, 3, and 1 again. I want each task to write into its log file, named
{taskid}.log
. Ufortunately it is hard to accomplish right with any of the logging libraries i tried - loguru, structlog, stdlib.The above snippet fails to achieve what i want, and the contents of
2.log
contains the log messages from the wrong tasks:It would work if the call to
log = logger.bind(taskid=taskid)
would return a bound logger instance, not connected to the root logger, so that subsequent call tolog.add()
only affected this bound logger instance. That would be a nice feature to have.The text was updated successfully, but these errors were encountered: