Closed 7a034f16-717b-4d44-97a6-6fc13eabbb7f closed 19 years ago
I would like a trailing '.' to be ignored in names passed to getLogger(), like a trainling '/' in a Unix path.
In module 'foo': logfoo = getLogger('.foo.') # logger '"" should be the parent of ".foo"
Elsewhere, controlled by the user of that module: import foo logdefault = getLogger('.') hdlr = StreamHandler() fmtr = Formatter("%(name)s:%(msg)s") hdlr.setFormatter(fmtr) logdefault.addHandler(hdlr)
Given this change, I would also like the name of the default logger to be displayed as '.', or even "", rather than 'root'. The current behavior is odd:
logfoo.info("Foo message")
displays
.foo:Foo message
buf
logdefault.info("Default message")
displays
root:Default message
I NEVER mentioned the word "root" anywhere! And I don't think it's very descriptive.
I would rather see ANY of these: :Default message .:Default message default:Default message logging:Default message
These changes would make the system more intuitive.
-cxdunn
Logged In: YES user_id=1267419
Novices always ask, "Why did it print 'root'? Where did that come from? After discussing this with some other "logging" module users, I think we've come up with a very good idea, which would maintain BACKWARDS COMPATIBILITY.
Essentially, treat the logging module as a shell and the logger name as a path. Specifically,
Examples:: from logging import * log = getLogger() #or getLogger(".") or getLogger("root") h1 = StreamHandler() f1 = Formatter("[%(name)s]%(message)s") h1.setFormatter(f1) log.addHandler(h1) h2 = StreamHandler() f2 = Formatter("[%(absname)s]%(message)s") h2.setFormatter(f2) log.addHandler(h2) h3 = StreamHandler() f3 = Formatter("[%(logger)s]%(message)s") h3.setFormatter(f3) log.addHandler(h3) log.error("First message")
# ...
child = getLogger("child") # or getLogger(".child")
child.error("Bad news")
This should print:
[root]First message [.]First message []First message [child]Bad news [.child]Bad news [child]Bad news
This would create tremendous flexibility, add some clarity to the meaning of the "root" logger, and still maintain complete backwards compatibility.
I am willing to make the changes myself, including UnitTests, if there is agreement that they would be adopted. (Note that String.before() and String.after() would make the coding a little easier/clearer, but that's a different feature request.)
-cxdunn
Logged In: YES user_id=1267419
I am attaching a first pass it it.
I've stored the "absolute" names everywhere, including both leading and trailing '.'
I call this "absolute" by analogy with os.path.abspath(). I believe that similarities within the Python library help the user remember key concepts.
What's missing:
But those are all very simple changes. The tough part is getting the path-searching correct. I have a big UnitTest suite which I can send to you if you'd like.
The most important thing is that the word "root" is completely gone, but perhaps %(name)s should translate '.' to 'root' for backwards compatibility.
The second-most important thing is that getLogger('.') returns the root logger.
Third is that getLogger("Package.Module") is equivalent to getLogger(".Package.Module.")
As for tags in the Formatter, after some testing I suggest these:
%(name)s => abspath.rstrip('.'), but "." becomes "root" %(absname)s => abspath, with leading AND trailing dot, like a directory, so there is no question about whether the root displays as "." or "". It is always just dot in absolute notation. %(logger)s => abspath.rstrip('.'), maybe the prettiest
I must tell you that, once I figured out how the logging module works, I really love it!
Other possible additions:
-cxdunn
Logged In: YES user_id=1267419
Oops. Where I wrote abspath.rstrip('.'), I meant abspath.strip('.') Drop both leading and trailing dots for the prettified path. -cdunn
Logged In: YES user_id=308438
Logger names are hierarchical with dots separating levels in the hierarchy. So to me it does not make sense to have logger names which end in a dot, and you have given no reason why trailing dots should be supported. However, the hierarchy is not completely anologous to file system hierarchies - there is by design no concept of a "default" or "current" logger. I do not propose to make a change to this.
However, I agree that the name of the root logger being "root" might be seen as unintuitive by some. Of your alternatives I think "logging" is best. I propose to add to the documentation the suggestion that users can define their own name for the root logger as in the following example:
logging.getLogger().name = "myapp"
People who use the root logger directly typically don't use other (named) loggers, because the whole point of using named loggers is to pinpoint areas of the application. Those users who use the root logger directly are typically not interested in finer granularity than the application or script itself.
Logged In: YES user_id=308438
Whoops!
I don't quite know what happened, but I think both of us were updating this RFE entry concurrently. I only saw your followup starting "Novices always ask..." before I posted my response.
Logged In: YES user_id=1267419
You're right! That works like a charm:
>>> import logging
>>> logging.getLogger().name = '.'
>>> logging.warning("I am root")
WARNING:.:I am root
>>> sub = logging.getLogger('.sub')
>>> sub.warning("I am a child")
WARNING:.sub:I am a child
Setting the root to "" also works:
>>> import logging
>>> logging.getLogger().name = ""
>>> logging.warning("I am root")
WARNING::I am root
>>> sub = logging.getLogger('sub')
>>> sub.warning("I am a child")
WARNING:sub:I am a child
I agree with your other points. The flexibility would add little value. I brought this issue up b/c I was confused by the docs. Clearly, But it is not so clear that "A" is a child of "root".
Since *everything* is a child of the root logger, that's worth reiterating in the docs. And if there is truly only 1 root logger, then it should be possible to find it by name:
>>> import logging
>>> logging.getLogger().name ="."
>>> logging.warning("I am root")
WARNING:.:I am root
>>> unknown = logging.getLogger(".")
>>> unknown.warning("Who am I?")
WARNING:.:Who am I?
>>> unknown == logging.getLogger()
False
In fact:
>>> import logging
>>> logging.getLogger() == logging.getLogger() #just a test
True
>>> logging.getLogger() == logging.getLogger("root") #should
be same!
False
This is not an easy module to understand, but it's amazingly powerful.
One last suggestion. You have logging.handlers. You could also have logging.filters. For example:
class Opaque(Filter):
"""A simple way to prevent any messages from getting
through."""
def __init__(self, name=None): pass
def filter(self, rec): return False
class Unique(Filter):
"""Messages are allowed through just once.
The 'message' includes substitutions, but is not
formatted by the
handler. If it were, then practically all messages would
be unique!
"""
def __init__(self, name=""):
Filter.__init__(self, name)
self.reset()
def reset(self):
"""Act as if nothing has happened."""
self.__logged = {}
def filter(self, rec):
return Filter.filter(self, rec) and
self.__is_first_time(rec)
def __is_first_time(self, rec):
"""Emit a message only once."""
msg = rec.msg %(rec.args)
if msg in self.__logged:
self.__logged[msg] += 1
return False
else:
self.__logged[msg] = 1
return True
Actually, this might be Cookbook material. I'll write it up.
Thanks for your time.
-cxdunn
Logged In: YES user_id=1267419
There's a bug wrt renaming the root logger:
>>> import logging.config
>>> logging.root.name = "snafu"
>>> logging.config.fileConfig("test.cfg")
Traceback (most recent call last):
File "python2.3/logging/config.py", line 132, in fileConfig
llist.remove("root")
ValueError: list.remove(x): x not in list
This is no different in 2.4. list.remove(root.name) is an easy fix.
Also, logging.getLogger() != logging.getLogger("root") or any other name. I now access the root logger strictly via logging.root
getRootLogger(), which is deprecated, should be preferred, since the root logger's name is not actually in the hash-table.
We need to make a sharp distinction between the root logger and the others. There is only one root; you cannot look it up by name; and the "dot" hierarchy does not apply to the root (for if it did, we would have to look specify children as .child, a convention that you've already rejected).
-cxdunn
P.S. I've posted some useful logging-related stuff at the ActivePython Cookbook. Feel free to add any of that to the module. Especially, the Unique filter could be added to logging.filters
Logged In: YES user_id=308438
There's no need to rename the root logger in a configuration file, so it can still be called "root" in the configuration file and have a different name displayed in messages. The config logic specifically looks for the section name "logger_root" and associates that section with the root logger. There is no reason to change this.
You should not use logging.root to get the root logger. Instead, use logging.getLogger().
If you want to make clearer a distinction between root logger and other loggers (they're not that different, in my view - see the docstring for the RootLogger class), please submit a documentation patch.
Logged In: YES user_id=1267419
You're right. The following works:
>>> import logging, logging.config
>>> logging.root.name = "snafu"
>>> logging.config.fileConfig('test.cfg')
>>> logging.warning("Hello")
[snafu] Hello
Where test.cfg is: [loggers] keys=root
[handlers] keys=hand01
[formatters] keys=form01
[logger_root] level=NOTSET handlers=hand01
[handler_hand01] class=StreamHandler level=NOTSET formatter=form01 args=(sys.stderr,)
[formatter_form01] format=[%(name)s] %(message)s
"If you want to make clearer a distinction between root logger and other loggers ... please submit a documentation patch."
OK....
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields: ```python assignee = 'https://github.com/vsajip' closed_at =
created_at =
labels = ['type-feature', 'library']
title = 'logging module root logger name'
updated_at =
user = 'https://bugs.python.org/cxdunn'
```
bugs.python.org fields:
```python
activity =
actor = 'cxdunn'
assignee = 'vinay.sajip'
closed = True
closed_date = None
closer = None
components = ['Library (Lib)']
creation =
creator = 'cxdunn'
dependencies = []
files = []
hgrepos = []
issue_num = 1190689
keywords = []
message_count = 10.0
messages = ['54477', '54478', '54479', '54480', '54481', '54482', '54483', '54484', '54485', '54486']
nosy_count = 2.0
nosy_names = ['vinay.sajip', 'cxdunn']
pr_nums = []
priority = 'low'
resolution = 'works for me'
stage = None
status = 'closed'
superseder = None
type = 'enhancement'
url = 'https://bugs.python.org/issue1190689'
versions = []
```