Merge lp:~vishvananda/nova/log-fix into lp:~hudson-openstack/nova/trunk

Proposed by Vish Ishaya
Status: Merged
Approved by: Soren Hansen
Approved revision: 724
Merged at revision: 711
Proposed branch: lp:~vishvananda/nova/log-fix
Merge into: lp:~hudson-openstack/nova/trunk
Diff against target: 871 lines (+148/-130)
35 files modified
bin/nova-ajax-console-proxy (+1/-1)
bin/nova-api (+3/-3)
bin/nova-combined (+1/-2)
bin/nova-compute (+4/-0)
bin/nova-console (+4/-0)
bin/nova-dhcpbridge (+1/-1)
bin/nova-direct-api (+2/-0)
bin/nova-import-canonical-imagestore (+2/-0)
bin/nova-instancemonitor (+0/-3)
bin/nova-manage (+1/-2)
bin/nova-network (+4/-0)
bin/nova-scheduler (+4/-0)
bin/nova-volume (+4/-0)
nova/api/openstack/auth.py (+0/-1)
nova/api/openstack/backup_schedules.py (+0/-1)
nova/api/openstack/images.py (+0/-2)
nova/api/openstack/servers.py (+0/-1)
nova/api/openstack/shared_ip_groups.py (+0/-2)
nova/api/openstack/zones.py (+0/-1)
nova/console/manager.py (+1/-1)
nova/console/xvp.py (+1/-1)
nova/log.py (+78/-53)
nova/service.py (+0/-3)
nova/test.py (+1/-1)
nova/tests/fake_flags.py (+1/-0)
nova/tests/test_auth.py (+0/-9)
nova/tests/test_console.py (+0/-2)
nova/tests/test_direct.py (+0/-1)
nova/tests/test_localization.py (+0/-1)
nova/tests/test_log.py (+30/-33)
nova/twistd.py (+1/-1)
nova/utils.py (+1/-1)
nova/wsgi.py (+0/-1)
run_tests.py (+2/-1)
run_tests.sh (+1/-1)
To merge this branch: bzr merge lp:~vishvananda/nova/log-fix
Reviewer Review Type Date Requested Status
Thierry Carrez (community) Approve
Devin Carlen (community) Approve
termie (community) Approve
Review via email: mp+50554@code.launchpad.net

Description of the change

Fixes various issues regarding verbose logging and logging errors on import.

* Adds a call to logging.reset() after flags are loaded via FLAGS()
* Uses logfile flag to log properly during run_tests
* Only adds handlers to root logger
* Removes the stream handler if logfile is set
* Syslog handler is in addition to logfile or stream handler
* Removed need for logging.basicConfig()

To post a comment you must log in.
Revision history for this message
termie (termie) wrote :

I will dive into this again in the morning, but what strikes me right away is that it feels unusual for logging to require adding an additional "initialized" attribute to Flags, my instinct is that this is a problem solvable by good practice rather than by force. If you'd like to explain further why it that needs to be done, that'd be swell and will make it easier to review again in the morning.

review: Needs Information
Revision history for this message
Vish Ishaya (vishvananda) wrote :

The main issue is that how logging is configured is heavily dependent on flags. There are a number of ways to get into our code:
a) nova-* bin files
b) tests through nose
c) external code importing files

We can do all configuration without the initialized flag if we do something like
import sys
from nova import flags
flags.FLAGS(sys.argv)
from nova import log as logging

at the top of every file that may be run from the command line. I find this ugly and fragile. If someone tries to use our code and fails to do these commands, logging will break. Also the flags are modified in various places, including tests, and nova-api after loading the paste.deploy conf.

An alternative to the initialized attribute is to cache the values of the relevant logging flags, and just check them for changes each time we log. I'm a little concerned about the performance of this option, especially for the default_log_levels flag which is a list and potentially expensive to check for changes every time we want to write a log message.

Perhaps the best option would be to put a logging_setup() in the end of flags.call(), so it will reset the logging conf after loading flags.

Revision history for this message
Vish Ishaya (vishvananda) wrote :

Ok I switched it to the Final option above. To reset the logging config because the flags have changed, there is a logging.reset() method.

I'm calling the method automatically at the end of flag parsing, but if this seems too messy, then we can manually add a logging.reset() after each call to FLAGS(sys.argv)

Revision history for this message
Thierry Carrez (ttx) wrote :

This is currently not fixing bug 721285.

Calling logging.reset() does not actually reset the logging level for instances of NovaLogger based on --verbose flag, since NovaLogger.setup_from_flags() does only look into --default_log_levels. NovaLogger should probably setLevel based on the contents of --verbose like NovaRootLogger does.

review: Needs Fixing
Revision history for this message
Todd Willey (xtoddx) wrote :

You can wipe out all references to basicConfig in the log module. All the logging.basicConfig call does is set up handlers for the root logger if they don't exist yet. Since NovaRootLogger will always have handlers, it becomes a no-op without having to re-define it. So you can remove
 * The definition of basicConfig
 * Setting the logging.basicConfig function
 * The check in the top-level audit() function that calls basicConfig

By returning on the first call to 'error' in the NovaLogger.exception, you stop environment dumps.

You should set the "nova" value of loggerDict to be the root logger as well, otherwise you'll get a new instance when you call logging.getLogger("nova"). It will require fixing the parent links in other loggers, and possibly using the same locking mechanics (see logging). That will also let the loop in reset find the root logger.

Instead of checking the class of loggers in the 'reset' function, you should probably check if they have a 'setup_from_flags' function, since it allows better duck-typing. It would also keep you from making the explicit call to the root logger.

I'd also vote for keeping the call to reset() away from the flags library, and put in in the binaries immediately after they call FLAGS(). Also, nova-api is changing soon so that it doesn't mangle flags (ttx has a branch).

Revision history for this message
Todd Willey (xtoddx) wrote :

To address TTX's point, I think both the root logger and NovaLogger instances should look at both --verbose and --default_log_levels, with default_log_levels having higher priority.

Revision history for this message
Vish Ishaya (vishvananda) wrote :

> This is currently not fixing bug 721285.
>
> Calling logging.reset() does not actually reset the logging level for
> instances of NovaLogger based on --verbose flag, since
> NovaLogger.setup_from_flags() does only look into --default_log_levels.
> NovaLogger should probably setLevel based on the contents of --verbose like
> NovaRootLogger does.

If a logger is explicitly set to a value, verbose should have no affect on it. If a logger has the value NOTSET, the root logger will pick it up and log it according to its own verbosity, so we don't need to set the individual loggers separately. That said, if the flags change to remove default_log_levels, then the reset will not pick it up, so I'll set it back to notset in that case.

Revision history for this message
Vish Ishaya (vishvananda) wrote :

> You can wipe out all references to basicConfig in the log module. All the
> logging.basicConfig call does is set up handlers for the root logger if they
> don't exist yet. Since NovaRootLogger will always have handlers, it becomes a
> no-op without having to re-define it. So you can remove
> * The definition of basicConfig
> * Setting the logging.basicConfig function
> * The check in the top-level audit() function that calls basicConfig

done.

>
> By returning on the first call to 'error' in the NovaLogger.exception, you
> stop environment dumps.

good catch

>
> You should set the "nova" value of loggerDict to be the root logger as well,
> otherwise you'll get a new instance when you call logging.getLogger("nova").
> It will require fixing the parent links in other loggers, and possibly using
> the same locking mechanics (see logging). That will also let the loop in
> reset find the root logger.

Good call.

>
> Instead of checking the class of loggers in the 'reset' function, you should
> probably check if they have a 'setup_from_flags' function, since it allows
> better duck-typing. It would also keep you from making the explicit call to
> the root logger.

The explicit call was fixed by above. Not sure that i see the value of duck-typing here.

>
> I'd also vote for keeping the call to reset() away from the flags library, and
> put in in the binaries immediately after they call FLAGS(). Also, nova-api is
> changing soon so that it doesn't mangle flags (ttx has a branch).

I'm ok with this although i find it annoying that we have to put this code in multiple places. Perhaps a utils.py function called parse_flags that does both?

Revision history for this message
Todd Willey (xtoddx) wrote :

I'd be good with with a method in utils that does both flag parsing & log reset.

Revision history for this message
Vish Ishaya (vishvananda) wrote :

As per Todd and termie's suggestions, I put in an explicit logging.setup after each call to FLAGS(). I also put the flag parsing directly into the bin files as this is a bit more explicit. As it is now, logging doesn't do anything on import except for override the type of the default logger. Everything else happens in .setup()

Revision history for this message
termie (termie) wrote :

594: extra newline was added
860: necessary newline was removed

It'd be nice to have the modifications to fake_flags.py "tests.log" and "tests.sqlite" be taken out of this patch.

review: Needs Fixing
Revision history for this message
termie (termie) wrote :

(as well as the removal of the db in run_tests.py)

lp:~vishvananda/nova/log-fix updated
723. By Vish Ishaya

remove changes to test db

Revision history for this message
termie (termie) wrote :

those numbers are now 585 and 848...

Revision history for this message
Vish Ishaya (vishvananda) wrote :

> those numbers are now 585 and 848...

fixed

lp:~vishvananda/nova/log-fix updated
724. By Vish Ishaya

fixed newline and moved import fake_flags into run_tests where it makes more sense

Revision history for this message
termie (termie) wrote :

lgtm

review: Approve
Revision history for this message
Devin Carlen (devcamcar) wrote :

lgtm

review: Approve
Revision history for this message
Thierry Carrez (ttx) wrote :

Tested & fixes bug 721285 alright now !

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bin/nova-ajax-console-proxy'
2--- bin/nova-ajax-console-proxy 2011-01-10 21:44:45 +0000
3+++ bin/nova-ajax-console-proxy 2011-02-22 00:22:48 +0000
4@@ -25,7 +25,6 @@
5
6 import exceptions
7 import gettext
8-import logging
9 import os
10 import sys
11 import time
12@@ -130,6 +129,7 @@
13 if __name__ == '__main__':
14 utils.default_flagfile()
15 FLAGS(sys.argv)
16+ logging.setup()
17 server = wsgi.Server()
18 acp = AjaxConsoleProxy()
19 acp.register_listeners()
20
21=== modified file 'bin/nova-api'
22--- bin/nova-api 2011-01-21 21:10:26 +0000
23+++ bin/nova-api 2011-02-22 00:22:48 +0000
24@@ -39,9 +39,7 @@
25 from nova import version
26 from nova import wsgi
27
28-logging.basicConfig()
29 LOG = logging.getLogger('nova.api')
30-LOG.setLevel(logging.DEBUG)
31
32 FLAGS = flags.FLAGS
33
34@@ -71,7 +69,8 @@
35 return
36
37 # NOTE(todd): redo logging config, verbose could be set in paste config
38- logging.basicConfig()
39+ logging.reset()
40+
41 server = wsgi.Server()
42 for app in apps:
43 server.start(*app)
44@@ -80,6 +79,7 @@
45
46 if __name__ == '__main__':
47 FLAGS(sys.argv)
48+ logging.setup()
49 LOG.audit(_("Starting nova-api node (version %s)"),
50 version.version_string_with_vcs())
51 conf = wsgi.paste_config_file('nova-api.conf')
52
53=== modified file 'bin/nova-combined'
54--- bin/nova-combined 2011-02-15 00:11:29 +0000
55+++ bin/nova-combined 2011-02-22 00:22:48 +0000
56@@ -49,7 +49,7 @@
57 if __name__ == '__main__':
58 utils.default_flagfile()
59 FLAGS(sys.argv)
60- logging.basicConfig()
61+ logging.setup()
62
63 compute = service.Service.create(binary='nova-compute')
64 network = service.Service.create(binary='nova-network')
65@@ -73,7 +73,6 @@
66 apps.append((app, getattr(FLAGS, "%s_port" % api),
67 getattr(FLAGS, "%s_host" % api)))
68 if len(apps) > 0:
69- logging.basicConfig()
70 server = wsgi.Server()
71 for app in apps:
72 server.start(*app)
73
74=== modified file 'bin/nova-compute'
75--- bin/nova-compute 2010-12-15 19:02:33 +0000
76+++ bin/nova-compute 2011-02-22 00:22:48 +0000
77@@ -36,10 +36,14 @@
78
79 gettext.install('nova', unicode=1)
80
81+from nova import flags
82+from nova import log as logging
83 from nova import service
84 from nova import utils
85
86 if __name__ == '__main__':
87 utils.default_flagfile()
88+ flags.FLAGS(sys.argv)
89+ logging.setup()
90 service.serve()
91 service.wait()
92
93=== modified file 'bin/nova-console'
94--- bin/nova-console 2011-01-05 20:16:14 +0000
95+++ bin/nova-console 2011-02-22 00:22:48 +0000
96@@ -35,10 +35,14 @@
97
98 gettext.install('nova', unicode=1)
99
100+from nova import flags
101+from nova import log as logging
102 from nova import service
103 from nova import utils
104
105 if __name__ == '__main__':
106 utils.default_flagfile()
107+ flags.FLAGS(sys.argv)
108+ logging.setup()
109 service.serve()
110 service.wait()
111
112=== modified file 'bin/nova-dhcpbridge'
113--- bin/nova-dhcpbridge 2011-01-24 15:19:51 +0000
114+++ bin/nova-dhcpbridge 2011-02-22 00:22:48 +0000
115@@ -102,7 +102,7 @@
116 flagfile = os.environ.get('FLAGFILE', FLAGS.dhcpbridge_flagfile)
117 utils.default_flagfile(flagfile)
118 argv = FLAGS(sys.argv)
119- logging.basicConfig()
120+ logging.setup()
121 interface = os.environ.get('DNSMASQ_INTERFACE', 'br0')
122 if int(os.environ.get('TESTING', '0')):
123 FLAGS.fake_rabbit = True
124
125=== modified file 'bin/nova-direct-api'
126--- bin/nova-direct-api 2011-01-18 13:48:50 +0000
127+++ bin/nova-direct-api 2011-02-22 00:22:48 +0000
128@@ -35,6 +35,7 @@
129 gettext.install('nova', unicode=1)
130
131 from nova import flags
132+from nova import log as logging
133 from nova import utils
134 from nova import wsgi
135 from nova.api import direct
136@@ -48,6 +49,7 @@
137 if __name__ == '__main__':
138 utils.default_flagfile()
139 FLAGS(sys.argv)
140+ logging.setup()
141
142 direct.register_service('compute', compute_api.API())
143 direct.register_service('reflect', direct.Reflection())
144
145=== modified file 'bin/nova-import-canonical-imagestore'
146--- bin/nova-import-canonical-imagestore 2010-12-14 23:22:03 +0000
147+++ bin/nova-import-canonical-imagestore 2011-02-22 00:22:48 +0000
148@@ -41,6 +41,7 @@
149 gettext.install('nova', unicode=1)
150
151 from nova import flags
152+from nova import log as logging
153 from nova import utils
154 from nova.objectstore import image
155
156@@ -92,6 +93,7 @@
157 """Main entry point."""
158 utils.default_flagfile()
159 argv = FLAGS(sys.argv)
160+ logging.setup()
161 images = get_images()
162
163 if len(argv) == 2:
164
165=== modified file 'bin/nova-instancemonitor'
166--- bin/nova-instancemonitor 2011-01-04 05:26:41 +0000
167+++ bin/nova-instancemonitor 2011-02-22 00:22:48 +0000
168@@ -41,9 +41,6 @@
169 from nova import twistd
170 from nova.compute import monitor
171
172-# TODO(todd): shouldn't this be done with flags? And what about verbose?
173-logging.getLogger('boto').setLevel(logging.WARN)
174-
175 LOG = logging.getLogger('nova.instancemonitor')
176
177
178
179=== modified file 'bin/nova-manage'
180--- bin/nova-manage 2011-02-17 21:39:03 +0000
181+++ bin/nova-manage 2011-02-22 00:22:48 +0000
182@@ -86,8 +86,6 @@
183 from nova.cloudpipe import pipelib
184 from nova.db import migration
185
186-
187-logging.basicConfig()
188 FLAGS = flags.FLAGS
189 flags.DECLARE('fixed_range', 'nova.network.manager')
190 flags.DECLARE('num_networks', 'nova.network.manager')
191@@ -710,6 +708,7 @@
192 """Parse options and call the appropriate class/method."""
193 utils.default_flagfile()
194 argv = FLAGS(sys.argv)
195+ logging.setup()
196
197 script_name = argv.pop(0)
198 if len(argv) < 1:
199
200=== modified file 'bin/nova-network'
201--- bin/nova-network 2010-12-15 19:02:33 +0000
202+++ bin/nova-network 2011-02-22 00:22:48 +0000
203@@ -36,10 +36,14 @@
204
205 gettext.install('nova', unicode=1)
206
207+from nova import flags
208+from nova import log as logging
209 from nova import service
210 from nova import utils
211
212 if __name__ == '__main__':
213 utils.default_flagfile()
214+ flags.FLAGS(sys.argv)
215+ logging.setup()
216 service.serve()
217 service.wait()
218
219=== modified file 'bin/nova-scheduler'
220--- bin/nova-scheduler 2010-12-15 19:02:33 +0000
221+++ bin/nova-scheduler 2011-02-22 00:22:48 +0000
222@@ -36,10 +36,14 @@
223
224 gettext.install('nova', unicode=1)
225
226+from nova import flags
227+from nova import log as logging
228 from nova import service
229 from nova import utils
230
231 if __name__ == '__main__':
232 utils.default_flagfile()
233+ flags.FLAGS(sys.argv)
234+ logging.setup()
235 service.serve()
236 service.wait()
237
238=== modified file 'bin/nova-volume'
239--- bin/nova-volume 2010-12-15 19:02:33 +0000
240+++ bin/nova-volume 2011-02-22 00:22:48 +0000
241@@ -36,10 +36,14 @@
242
243 gettext.install('nova', unicode=1)
244
245+from nova import flags
246+from nova import log as logging
247 from nova import service
248 from nova import utils
249
250 if __name__ == '__main__':
251 utils.default_flagfile()
252+ flags.FLAGS(sys.argv)
253+ logging.setup()
254 service.serve()
255 service.wait()
256
257=== modified file 'nova/api/openstack/auth.py'
258--- nova/api/openstack/auth.py 2011-02-18 22:35:12 +0000
259+++ nova/api/openstack/auth.py 2011-02-22 00:22:48 +0000
260@@ -19,7 +19,6 @@
261 import hashlib
262 import json
263 import time
264-import logging
265
266 import webob.exc
267 import webob.dec
268
269=== modified file 'nova/api/openstack/backup_schedules.py'
270--- nova/api/openstack/backup_schedules.py 2011-01-03 23:46:55 +0000
271+++ nova/api/openstack/backup_schedules.py 2011-02-22 00:22:48 +0000
272@@ -15,7 +15,6 @@
273 # License for the specific language governing permissions and limitations
274 # under the License.
275
276-import logging
277 import time
278
279 from webob import exc
280
281=== modified file 'nova/api/openstack/images.py'
282--- nova/api/openstack/images.py 2011-01-13 19:48:01 +0000
283+++ nova/api/openstack/images.py 2011-02-22 00:22:48 +0000
284@@ -15,8 +15,6 @@
285 # License for the specific language governing permissions and limitations
286 # under the License.
287
288-import logging
289-
290 from webob import exc
291
292 from nova import compute
293
294=== modified file 'nova/api/openstack/servers.py'
295--- nova/api/openstack/servers.py 2011-02-18 22:36:01 +0000
296+++ nova/api/openstack/servers.py 2011-02-22 00:22:48 +0000
297@@ -33,7 +33,6 @@
298
299
300 LOG = logging.getLogger('server')
301-LOG.setLevel(logging.DEBUG)
302
303
304 FLAGS = flags.FLAGS
305
306=== modified file 'nova/api/openstack/shared_ip_groups.py'
307--- nova/api/openstack/shared_ip_groups.py 2011-01-11 07:38:40 +0000
308+++ nova/api/openstack/shared_ip_groups.py 2011-02-22 00:22:48 +0000
309@@ -15,8 +15,6 @@
310 # License for the specific language governing permissions and limitations
311 # under the License.
312
313-import logging
314-
315 from webob import exc
316
317 from nova import wsgi
318
319=== modified file 'nova/api/openstack/zones.py'
320--- nova/api/openstack/zones.py 2011-02-15 03:02:26 +0000
321+++ nova/api/openstack/zones.py 2011-02-22 00:22:48 +0000
322@@ -14,7 +14,6 @@
323 # under the License.
324
325 import common
326-import logging
327
328 from nova import flags
329 from nova import wsgi
330
331=== modified file 'nova/console/manager.py'
332--- nova/console/manager.py 2011-01-20 17:52:02 +0000
333+++ nova/console/manager.py 2011-02-22 00:22:48 +0000
334@@ -20,11 +20,11 @@
335 """
336
337 import functools
338-import logging
339 import socket
340
341 from nova import exception
342 from nova import flags
343+from nova import log as logging
344 from nova import manager
345 from nova import rpc
346 from nova import utils
347
348=== modified file 'nova/console/xvp.py'
349--- nova/console/xvp.py 2011-01-20 17:52:02 +0000
350+++ nova/console/xvp.py 2011-02-22 00:22:48 +0000
351@@ -20,7 +20,6 @@
352 """
353
354 import fcntl
355-import logging
356 import os
357 import signal
358 import subprocess
359@@ -31,6 +30,7 @@
360 from nova import db
361 from nova import exception
362 from nova import flags
363+from nova import log as logging
364 from nova import utils
365
366 flags.DEFINE_string('console_xvp_conf_template',
367
368=== modified file 'nova/log.py'
369--- nova/log.py 2011-02-18 09:24:55 +0000
370+++ nova/log.py 2011-02-22 00:22:48 +0000
371@@ -65,6 +65,7 @@
372 flags.DEFINE_list('default_log_levels',
373 ['amqplib=WARN',
374 'sqlalchemy=WARN',
375+ 'boto=WARN',
376 'eventlet.wsgi.server=WARN'],
377 'list of logger=LEVEL pairs')
378
379@@ -117,7 +118,7 @@
380 return os.path.basename(inspect.stack()[-1][1])
381
382
383-def get_log_file_path(binary=None):
384+def _get_log_file_path(binary=None):
385 if FLAGS.logfile:
386 return FLAGS.logfile
387 if FLAGS.logdir:
388@@ -125,25 +126,6 @@
389 return '%s.log' % (os.path.join(FLAGS.logdir, binary),)
390
391
392-def basicConfig():
393- logging.basicConfig()
394- for handler in logging.root.handlers:
395- handler.setFormatter(_formatter)
396- if FLAGS.verbose:
397- logging.root.setLevel(logging.DEBUG)
398- else:
399- logging.root.setLevel(logging.INFO)
400- if FLAGS.use_syslog:
401- syslog = SysLogHandler(address='/dev/log')
402- syslog.setFormatter(_formatter)
403- logging.root.addHandler(syslog)
404- logpath = get_log_file_path()
405- if logpath:
406- logfile = WatchedFileHandler(logpath)
407- logfile.setFormatter(_formatter)
408- logging.root.addHandler(logfile)
409-
410-
411 class NovaLogger(logging.Logger):
412 """
413 NovaLogger manages request context and formatting.
414@@ -151,23 +133,19 @@
415 This becomes the class that is instanciated by logging.getLogger.
416 """
417 def __init__(self, name, level=NOTSET):
418- level_name = self._get_level_from_flags(name, FLAGS)
419- level = globals()[level_name]
420 logging.Logger.__init__(self, name, level)
421+ self.setup_from_flags()
422
423- def _get_level_from_flags(self, name, FLAGS):
424- # if exactly "nova", or a child logger, honor the verbose flag
425- if (name == "nova" or name.startswith("nova.")) and FLAGS.verbose:
426- return 'DEBUG'
427+ def setup_from_flags(self):
428+ """Setup logger from flags"""
429+ level = NOTSET
430 for pair in FLAGS.default_log_levels:
431- logger, _sep, level = pair.partition('=')
432+ logger, _sep, level_name = pair.partition('=')
433 # NOTE(todd): if we set a.b, we want a.b.c to have the same level
434 # (but not a.bc, so we check the dot)
435- if name == logger:
436- return level
437- if name.startswith(logger) and name[len(logger)] == '.':
438- return level
439- return 'INFO'
440+ if self.name == logger or self.name.startswith("%s." % logger):
441+ level = globals()[level_name]
442+ self.setLevel(level)
443
444 def _log(self, level, msg, args, exc_info=None, extra=None, context=None):
445 """Extract context from any log call"""
446@@ -176,12 +154,12 @@
447 if context:
448 extra.update(_dictify_context(context))
449 extra.update({"nova_version": version.version_string_with_vcs()})
450- logging.Logger._log(self, level, msg, args, exc_info, extra)
451+ return logging.Logger._log(self, level, msg, args, exc_info, extra)
452
453 def addHandler(self, handler):
454 """Each handler gets our custom formatter"""
455 handler.setFormatter(_formatter)
456- logging.Logger.addHandler(self, handler)
457+ return logging.Logger.addHandler(self, handler)
458
459 def audit(self, msg, *args, **kwargs):
460 """Shortcut for our AUDIT level"""
461@@ -208,23 +186,6 @@
462 self.error(message, **kwargs)
463
464
465-def handle_exception(type, value, tb):
466- logging.root.critical(str(value), exc_info=(type, value, tb))
467-
468-
469-sys.excepthook = handle_exception
470-logging.setLoggerClass(NovaLogger)
471-
472-
473-class NovaRootLogger(NovaLogger):
474- pass
475-
476-if not isinstance(logging.root, NovaRootLogger):
477- logging.root = NovaRootLogger("nova.root", WARNING)
478- NovaLogger.root = logging.root
479- NovaLogger.manager.root = logging.root
480-
481-
482 class NovaFormatter(logging.Formatter):
483 """
484 A nova.context.RequestContext aware formatter configured through flags.
485@@ -271,8 +232,72 @@
486 _formatter = NovaFormatter()
487
488
489+class NovaRootLogger(NovaLogger):
490+ def __init__(self, name, level=NOTSET):
491+ self.logpath = None
492+ self.filelog = None
493+ self.syslog = SysLogHandler(address='/dev/log')
494+ self.streamlog = StreamHandler()
495+ NovaLogger.__init__(self, name, level)
496+
497+ def setup_from_flags(self):
498+ """Setup logger from flags"""
499+ global _filelog
500+ if FLAGS.use_syslog:
501+ self.addHandler(self.syslog)
502+ else:
503+ self.removeHandler(self.syslog)
504+ logpath = _get_log_file_path()
505+ if logpath:
506+ self.removeHandler(self.streamlog)
507+ if logpath != self.logpath:
508+ self.removeHandler(self.filelog)
509+ self.filelog = WatchedFileHandler(logpath)
510+ self.addHandler(self.filelog)
511+ self.logpath = logpath
512+ else:
513+ self.removeHandler(self.filelog)
514+ self.addHandler(self.streamlog)
515+ if FLAGS.verbose:
516+ self.setLevel(DEBUG)
517+ else:
518+ self.setLevel(INFO)
519+
520+
521+def handle_exception(type, value, tb):
522+ logging.root.critical(str(value), exc_info=(type, value, tb))
523+
524+
525+def reset():
526+ """Resets logging handlers. Should be called if FLAGS changes."""
527+ for logger in NovaLogger.manager.loggerDict.itervalues():
528+ if isinstance(logger, NovaLogger):
529+ logger.setup_from_flags()
530+
531+
532+def setup():
533+ """Setup nova logging."""
534+ if not isinstance(logging.root, NovaRootLogger):
535+ logging._acquireLock()
536+ for handler in logging.root.handlers:
537+ logging.root.removeHandler(handler)
538+ logging.root = NovaRootLogger("nova")
539+ NovaLogger.root = logging.root
540+ NovaLogger.manager.root = logging.root
541+ for logger in NovaLogger.manager.loggerDict.itervalues():
542+ logger.root = logging.root
543+ if isinstance(logger, logging.Logger):
544+ NovaLogger.manager._fixupParents(logger)
545+ NovaLogger.manager.loggerDict["nova"] = logging.root
546+ logging._releaseLock()
547+ sys.excepthook = handle_exception
548+ reset()
549+
550+
551+root = logging.root
552+logging.setLoggerClass(NovaLogger)
553+
554+
555 def audit(msg, *args, **kwargs):
556 """Shortcut for logging to root log with sevrity 'AUDIT'."""
557- if len(logging.root.handlers) == 0:
558- basicConfig()
559 logging.root.log(AUDIT, msg, *args, **kwargs)
560
561=== modified file 'nova/service.py'
562--- nova/service.py 2011-01-27 19:52:10 +0000
563+++ nova/service.py 2011-02-22 00:22:48 +0000
564@@ -214,9 +214,6 @@
565
566
567 def serve(*services):
568- FLAGS(sys.argv)
569- logging.basicConfig()
570-
571 if not services:
572 services = [Service.create()]
573
574
575=== modified file 'nova/test.py'
576--- nova/test.py 2011-01-26 21:58:24 +0000
577+++ nova/test.py 2011-02-22 00:22:48 +0000
578@@ -32,9 +32,9 @@
579 from nova import db
580 from nova import fakerabbit
581 from nova import flags
582+from nova import log as logging
583 from nova import rpc
584 from nova.network import manager as network_manager
585-from nova.tests import fake_flags
586
587
588 FLAGS = flags.FLAGS
589
590=== modified file 'nova/tests/fake_flags.py'
591--- nova/tests/fake_flags.py 2011-01-18 23:39:24 +0000
592+++ nova/tests/fake_flags.py 2011-02-22 00:22:48 +0000
593@@ -41,3 +41,4 @@
594 FLAGS.verbose = True
595 FLAGS.sql_connection = 'sqlite:///nova.sqlite'
596 FLAGS.use_ipv6 = True
597+FLAGS.logfile = 'tests.log'
598
599=== modified file 'nova/tests/test_auth.py'
600--- nova/tests/test_auth.py 2011-01-04 05:23:35 +0000
601+++ nova/tests/test_auth.py 2011-02-22 00:22:48 +0000
602@@ -327,15 +327,6 @@
603 class AuthManagerLdapTestCase(AuthManagerTestCase, test.TestCase):
604 auth_driver = 'nova.auth.ldapdriver.FakeLdapDriver'
605
606- def __init__(self, *args, **kwargs):
607- AuthManagerTestCase.__init__(self)
608- test.TestCase.__init__(self, *args, **kwargs)
609- import nova.auth.fakeldap as fakeldap
610- if FLAGS.flush_db:
611- LOG.info("Flushing datastore")
612- r = fakeldap.Store.instance()
613- r.flushdb()
614-
615
616 class AuthManagerDbTestCase(AuthManagerTestCase, test.TestCase):
617 auth_driver = 'nova.auth.dbdriver.DbDriver'
618
619=== modified file 'nova/tests/test_console.py'
620--- nova/tests/test_console.py 2011-01-15 01:44:47 +0000
621+++ nova/tests/test_console.py 2011-02-22 00:22:48 +0000
622@@ -21,7 +21,6 @@
623 """
624
625 import datetime
626-import logging
627
628 from nova import context
629 from nova import db
630@@ -38,7 +37,6 @@
631 class ConsoleTestCase(test.TestCase):
632 """Test case for console proxy"""
633 def setUp(self):
634- logging.getLogger().setLevel(logging.DEBUG)
635 super(ConsoleTestCase, self).setUp()
636 self.flags(console_driver='nova.console.fake.FakeConsoleProxy',
637 stub_compute=True)
638
639=== modified file 'nova/tests/test_direct.py'
640--- nova/tests/test_direct.py 2011-01-08 00:05:06 +0000
641+++ nova/tests/test_direct.py 2011-02-22 00:22:48 +0000
642@@ -19,7 +19,6 @@
643 """Tests for Direct API."""
644
645 import json
646-import logging
647
648 import webob
649
650
651=== modified file 'nova/tests/test_localization.py'
652--- nova/tests/test_localization.py 2011-01-27 22:54:59 +0000
653+++ nova/tests/test_localization.py 2011-02-22 00:22:48 +0000
654@@ -15,7 +15,6 @@
655 # under the License.
656
657 import glob
658-import logging
659 import os
660 import re
661 import sys
662
663=== modified file 'nova/tests/test_log.py'
664--- nova/tests/test_log.py 2011-02-16 09:02:17 +0000
665+++ nova/tests/test_log.py 2011-02-22 00:22:48 +0000
666@@ -1,9 +1,12 @@
667 import cStringIO
668
669 from nova import context
670+from nova import flags
671 from nova import log
672 from nova import test
673
674+FLAGS = flags.FLAGS
675+
676
677 def _fake_context():
678 return context.RequestContext(1, 1)
679@@ -14,15 +17,11 @@
680 super(RootLoggerTestCase, self).setUp()
681 self.log = log.logging.root
682
683- def tearDown(self):
684- super(RootLoggerTestCase, self).tearDown()
685- log.NovaLogger.manager.loggerDict = {}
686-
687 def test_is_nova_instance(self):
688 self.assert_(isinstance(self.log, log.NovaLogger))
689
690- def test_name_is_nova_root(self):
691- self.assertEqual("nova.root", self.log.name)
692+ def test_name_is_nova(self):
693+ self.assertEqual("nova", self.log.name)
694
695 def test_handlers_have_nova_formatter(self):
696 formatters = []
697@@ -45,25 +44,36 @@
698 log.audit("foo", context=_fake_context())
699 self.assert_(True) # didn't raise exception
700
701+ def test_will_be_verbose_if_verbose_flag_set(self):
702+ self.flags(verbose=True)
703+ log.reset()
704+ self.assertEqual(log.DEBUG, self.log.level)
705+
706+ def test_will_not_be_verbose_if_verbose_flag_not_set(self):
707+ self.flags(verbose=False)
708+ log.reset()
709+ self.assertEqual(log.INFO, self.log.level)
710+
711
712 class LogHandlerTestCase(test.TestCase):
713 def test_log_path_logdir(self):
714- self.flags(logdir='/some/path')
715- self.assertEquals(log.get_log_file_path(binary='foo-bar'),
716+ self.flags(logdir='/some/path', logfile=None)
717+ self.assertEquals(log._get_log_file_path(binary='foo-bar'),
718 '/some/path/foo-bar.log')
719
720 def test_log_path_logfile(self):
721 self.flags(logfile='/some/path/foo-bar.log')
722- self.assertEquals(log.get_log_file_path(binary='foo-bar'),
723+ self.assertEquals(log._get_log_file_path(binary='foo-bar'),
724 '/some/path/foo-bar.log')
725
726 def test_log_path_none(self):
727- self.assertTrue(log.get_log_file_path(binary='foo-bar') is None)
728+ self.flags(logdir=None, logfile=None)
729+ self.assertTrue(log._get_log_file_path(binary='foo-bar') is None)
730
731 def test_log_path_logfile_overrides_logdir(self):
732 self.flags(logdir='/some/other/path',
733 logfile='/some/path/foo-bar.log')
734- self.assertEquals(log.get_log_file_path(binary='foo-bar'),
735+ self.assertEquals(log._get_log_file_path(binary='foo-bar'),
736 '/some/path/foo-bar.log')
737
738
739@@ -76,13 +86,15 @@
740 logging_debug_format_suffix="--DBG")
741 self.log = log.logging.root
742 self.stream = cStringIO.StringIO()
743- handler = log.StreamHandler(self.stream)
744- self.log.addHandler(handler)
745+ self.handler = log.StreamHandler(self.stream)
746+ self.log.addHandler(self.handler)
747+ self.level = self.log.level
748 self.log.setLevel(log.DEBUG)
749
750 def tearDown(self):
751+ self.log.setLevel(self.level)
752+ self.log.removeHandler(self.handler)
753 super(NovaFormatterTestCase, self).tearDown()
754- log.NovaLogger.manager.loggerDict = {}
755
756 def test_uncontextualized_log(self):
757 self.log.info("foo")
758@@ -102,30 +114,15 @@
759 class NovaLoggerTestCase(test.TestCase):
760 def setUp(self):
761 super(NovaLoggerTestCase, self).setUp()
762- self.flags(default_log_levels=["nova-test=AUDIT"], verbose=False)
763+ levels = FLAGS.default_log_levels
764+ levels.append("nova-test=AUDIT")
765+ self.flags(default_log_levels=levels,
766+ verbose=True)
767 self.log = log.getLogger('nova-test')
768
769- def tearDown(self):
770- super(NovaLoggerTestCase, self).tearDown()
771- log.NovaLogger.manager.loggerDict = {}
772-
773 def test_has_level_from_flags(self):
774 self.assertEqual(log.AUDIT, self.log.level)
775
776 def test_child_log_has_level_of_parent_flag(self):
777 l = log.getLogger('nova-test.foo')
778 self.assertEqual(log.AUDIT, l.level)
779-
780-
781-class VerboseLoggerTestCase(test.TestCase):
782- def setUp(self):
783- super(VerboseLoggerTestCase, self).setUp()
784- self.flags(default_log_levels=["nova.test=AUDIT"], verbose=True)
785- self.log = log.getLogger('nova.test')
786-
787- def tearDown(self):
788- super(VerboseLoggerTestCase, self).tearDown()
789- log.NovaLogger.manager.loggerDict = {}
790-
791- def test_will_be_verbose_if_named_nova_and_verbose_flag_set(self):
792- self.assertEqual(log.DEBUG, self.log.level)
793
794=== modified file 'nova/twistd.py'
795--- nova/twistd.py 2011-02-14 22:19:15 +0000
796+++ nova/twistd.py 2011-02-22 00:22:48 +0000
797@@ -148,6 +148,7 @@
798 options.insert(0, '')
799
800 args = FLAGS(options)
801+ logging.setup()
802 argv = args[1:]
803 # ignore subcommands
804
805@@ -258,7 +259,6 @@
806 print 'usage: %s [options] [start|stop|restart]' % argv[0]
807 sys.exit(1)
808
809- logging.basicConfig()
810 logging.debug(_("Full set of FLAGS:"))
811 for flag in FLAGS:
812 logging.debug("%s : %s" % (flag, FLAGS.get(flag, None)))
813
814=== modified file 'nova/utils.py'
815--- nova/utils.py 2011-02-17 22:09:26 +0000
816+++ nova/utils.py 2011-02-22 00:22:48 +0000
817@@ -55,7 +55,7 @@
818 __import__(mod_str)
819 return getattr(sys.modules[mod_str], class_str)
820 except (ImportError, ValueError, AttributeError), exc:
821- logging.debug(_('Inner Exception: %s'), exc)
822+ LOG.debug(_('Inner Exception: %s'), exc)
823 raise exception.NotFound(_('Class %s cannot be found') % class_str)
824
825
826
827=== modified file 'nova/wsgi.py'
828--- nova/wsgi.py 2011-01-21 21:10:26 +0000
829+++ nova/wsgi.py 2011-02-22 00:22:48 +0000
830@@ -59,7 +59,6 @@
831 """Server class to manage multiple WSGI sockets and applications."""
832
833 def __init__(self, threads=1000):
834- logging.basicConfig()
835 self.pool = eventlet.GreenPool(threads)
836
837 def start(self, application, port, host='0.0.0.0', backlog=128):
838
839=== modified file 'run_tests.py'
840--- run_tests.py 2011-01-31 05:38:58 +0000
841+++ run_tests.py 2011-02-22 00:22:48 +0000
842@@ -27,6 +27,7 @@
843 from nose import core
844
845 from nova import log as logging
846+from nova.tests import fake_flags
847
848
849 class NovaTestResult(result.TextTestResult):
850@@ -60,7 +61,7 @@
851
852
853 if __name__ == '__main__':
854- logging.basicConfig()
855+ logging.setup()
856 c = config.Config(stream=sys.stdout,
857 env=os.environ,
858 verbosity=3,
859
860=== modified file 'run_tests.sh'
861--- run_tests.sh 2011-01-31 05:55:32 +0000
862+++ run_tests.sh 2011-02-22 00:22:48 +0000
863@@ -40,7 +40,7 @@
864 function run_tests {
865 # Just run the test suites in current environment
866 ${wrapper} rm -f nova.sqlite
867- ${wrapper} $NOSETESTS 2> run_tests.err.log
868+ ${wrapper} $NOSETESTS
869 }
870
871 NOSETESTS="python run_tests.py $noseargs"