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
=== modified file 'bin/nova-ajax-console-proxy'
--- bin/nova-ajax-console-proxy 2011-01-10 21:44:45 +0000
+++ bin/nova-ajax-console-proxy 2011-02-22 00:22:48 +0000
@@ -25,7 +25,6 @@
2525
26import exceptions26import exceptions
27import gettext27import gettext
28import logging
29import os28import os
30import sys29import sys
31import time30import time
@@ -130,6 +129,7 @@
130if __name__ == '__main__':129if __name__ == '__main__':
131 utils.default_flagfile()130 utils.default_flagfile()
132 FLAGS(sys.argv)131 FLAGS(sys.argv)
132 logging.setup()
133 server = wsgi.Server()133 server = wsgi.Server()
134 acp = AjaxConsoleProxy()134 acp = AjaxConsoleProxy()
135 acp.register_listeners()135 acp.register_listeners()
136136
=== modified file 'bin/nova-api'
--- bin/nova-api 2011-01-21 21:10:26 +0000
+++ bin/nova-api 2011-02-22 00:22:48 +0000
@@ -39,9 +39,7 @@
39from nova import version39from nova import version
40from nova import wsgi40from nova import wsgi
4141
42logging.basicConfig()
43LOG = logging.getLogger('nova.api')42LOG = logging.getLogger('nova.api')
44LOG.setLevel(logging.DEBUG)
4543
46FLAGS = flags.FLAGS44FLAGS = flags.FLAGS
4745
@@ -71,7 +69,8 @@
71 return69 return
7270
73 # NOTE(todd): redo logging config, verbose could be set in paste config71 # NOTE(todd): redo logging config, verbose could be set in paste config
74 logging.basicConfig()72 logging.reset()
73
75 server = wsgi.Server()74 server = wsgi.Server()
76 for app in apps:75 for app in apps:
77 server.start(*app)76 server.start(*app)
@@ -80,6 +79,7 @@
8079
81if __name__ == '__main__':80if __name__ == '__main__':
82 FLAGS(sys.argv)81 FLAGS(sys.argv)
82 logging.setup()
83 LOG.audit(_("Starting nova-api node (version %s)"),83 LOG.audit(_("Starting nova-api node (version %s)"),
84 version.version_string_with_vcs())84 version.version_string_with_vcs())
85 conf = wsgi.paste_config_file('nova-api.conf')85 conf = wsgi.paste_config_file('nova-api.conf')
8686
=== modified file 'bin/nova-combined'
--- bin/nova-combined 2011-02-15 00:11:29 +0000
+++ bin/nova-combined 2011-02-22 00:22:48 +0000
@@ -49,7 +49,7 @@
49if __name__ == '__main__':49if __name__ == '__main__':
50 utils.default_flagfile()50 utils.default_flagfile()
51 FLAGS(sys.argv)51 FLAGS(sys.argv)
52 logging.basicConfig()52 logging.setup()
5353
54 compute = service.Service.create(binary='nova-compute')54 compute = service.Service.create(binary='nova-compute')
55 network = service.Service.create(binary='nova-network')55 network = service.Service.create(binary='nova-network')
@@ -73,7 +73,6 @@
73 apps.append((app, getattr(FLAGS, "%s_port" % api),73 apps.append((app, getattr(FLAGS, "%s_port" % api),
74 getattr(FLAGS, "%s_host" % api)))74 getattr(FLAGS, "%s_host" % api)))
75 if len(apps) > 0:75 if len(apps) > 0:
76 logging.basicConfig()
77 server = wsgi.Server()76 server = wsgi.Server()
78 for app in apps:77 for app in apps:
79 server.start(*app)78 server.start(*app)
8079
=== modified file 'bin/nova-compute'
--- bin/nova-compute 2010-12-15 19:02:33 +0000
+++ bin/nova-compute 2011-02-22 00:22:48 +0000
@@ -36,10 +36,14 @@
3636
37gettext.install('nova', unicode=1)37gettext.install('nova', unicode=1)
3838
39from nova import flags
40from nova import log as logging
39from nova import service41from nova import service
40from nova import utils42from nova import utils
4143
42if __name__ == '__main__':44if __name__ == '__main__':
43 utils.default_flagfile()45 utils.default_flagfile()
46 flags.FLAGS(sys.argv)
47 logging.setup()
44 service.serve()48 service.serve()
45 service.wait()49 service.wait()
4650
=== modified file 'bin/nova-console'
--- bin/nova-console 2011-01-05 20:16:14 +0000
+++ bin/nova-console 2011-02-22 00:22:48 +0000
@@ -35,10 +35,14 @@
3535
36gettext.install('nova', unicode=1)36gettext.install('nova', unicode=1)
3737
38from nova import flags
39from nova import log as logging
38from nova import service40from nova import service
39from nova import utils41from nova import utils
4042
41if __name__ == '__main__':43if __name__ == '__main__':
42 utils.default_flagfile()44 utils.default_flagfile()
45 flags.FLAGS(sys.argv)
46 logging.setup()
43 service.serve()47 service.serve()
44 service.wait()48 service.wait()
4549
=== modified file 'bin/nova-dhcpbridge'
--- bin/nova-dhcpbridge 2011-01-24 15:19:51 +0000
+++ bin/nova-dhcpbridge 2011-02-22 00:22:48 +0000
@@ -102,7 +102,7 @@
102 flagfile = os.environ.get('FLAGFILE', FLAGS.dhcpbridge_flagfile)102 flagfile = os.environ.get('FLAGFILE', FLAGS.dhcpbridge_flagfile)
103 utils.default_flagfile(flagfile)103 utils.default_flagfile(flagfile)
104 argv = FLAGS(sys.argv)104 argv = FLAGS(sys.argv)
105 logging.basicConfig()105 logging.setup()
106 interface = os.environ.get('DNSMASQ_INTERFACE', 'br0')106 interface = os.environ.get('DNSMASQ_INTERFACE', 'br0')
107 if int(os.environ.get('TESTING', '0')):107 if int(os.environ.get('TESTING', '0')):
108 FLAGS.fake_rabbit = True108 FLAGS.fake_rabbit = True
109109
=== modified file 'bin/nova-direct-api'
--- bin/nova-direct-api 2011-01-18 13:48:50 +0000
+++ bin/nova-direct-api 2011-02-22 00:22:48 +0000
@@ -35,6 +35,7 @@
35gettext.install('nova', unicode=1)35gettext.install('nova', unicode=1)
3636
37from nova import flags37from nova import flags
38from nova import log as logging
38from nova import utils39from nova import utils
39from nova import wsgi40from nova import wsgi
40from nova.api import direct41from nova.api import direct
@@ -48,6 +49,7 @@
48if __name__ == '__main__':49if __name__ == '__main__':
49 utils.default_flagfile()50 utils.default_flagfile()
50 FLAGS(sys.argv)51 FLAGS(sys.argv)
52 logging.setup()
5153
52 direct.register_service('compute', compute_api.API())54 direct.register_service('compute', compute_api.API())
53 direct.register_service('reflect', direct.Reflection())55 direct.register_service('reflect', direct.Reflection())
5456
=== modified file 'bin/nova-import-canonical-imagestore'
--- bin/nova-import-canonical-imagestore 2010-12-14 23:22:03 +0000
+++ bin/nova-import-canonical-imagestore 2011-02-22 00:22:48 +0000
@@ -41,6 +41,7 @@
41gettext.install('nova', unicode=1)41gettext.install('nova', unicode=1)
4242
43from nova import flags43from nova import flags
44from nova import log as logging
44from nova import utils45from nova import utils
45from nova.objectstore import image46from nova.objectstore import image
4647
@@ -92,6 +93,7 @@
92 """Main entry point."""93 """Main entry point."""
93 utils.default_flagfile()94 utils.default_flagfile()
94 argv = FLAGS(sys.argv)95 argv = FLAGS(sys.argv)
96 logging.setup()
95 images = get_images()97 images = get_images()
9698
97 if len(argv) == 2:99 if len(argv) == 2:
98100
=== modified file 'bin/nova-instancemonitor'
--- bin/nova-instancemonitor 2011-01-04 05:26:41 +0000
+++ bin/nova-instancemonitor 2011-02-22 00:22:48 +0000
@@ -41,9 +41,6 @@
41from nova import twistd41from nova import twistd
42from nova.compute import monitor42from nova.compute import monitor
4343
44# TODO(todd): shouldn't this be done with flags? And what about verbose?
45logging.getLogger('boto').setLevel(logging.WARN)
46
47LOG = logging.getLogger('nova.instancemonitor')44LOG = logging.getLogger('nova.instancemonitor')
4845
4946
5047
=== modified file 'bin/nova-manage'
--- bin/nova-manage 2011-02-17 21:39:03 +0000
+++ bin/nova-manage 2011-02-22 00:22:48 +0000
@@ -86,8 +86,6 @@
86from nova.cloudpipe import pipelib86from nova.cloudpipe import pipelib
87from nova.db import migration87from nova.db import migration
8888
89
90logging.basicConfig()
91FLAGS = flags.FLAGS89FLAGS = flags.FLAGS
92flags.DECLARE('fixed_range', 'nova.network.manager')90flags.DECLARE('fixed_range', 'nova.network.manager')
93flags.DECLARE('num_networks', 'nova.network.manager')91flags.DECLARE('num_networks', 'nova.network.manager')
@@ -710,6 +708,7 @@
710 """Parse options and call the appropriate class/method."""708 """Parse options and call the appropriate class/method."""
711 utils.default_flagfile()709 utils.default_flagfile()
712 argv = FLAGS(sys.argv)710 argv = FLAGS(sys.argv)
711 logging.setup()
713712
714 script_name = argv.pop(0)713 script_name = argv.pop(0)
715 if len(argv) < 1:714 if len(argv) < 1:
716715
=== modified file 'bin/nova-network'
--- bin/nova-network 2010-12-15 19:02:33 +0000
+++ bin/nova-network 2011-02-22 00:22:48 +0000
@@ -36,10 +36,14 @@
3636
37gettext.install('nova', unicode=1)37gettext.install('nova', unicode=1)
3838
39from nova import flags
40from nova import log as logging
39from nova import service41from nova import service
40from nova import utils42from nova import utils
4143
42if __name__ == '__main__':44if __name__ == '__main__':
43 utils.default_flagfile()45 utils.default_flagfile()
46 flags.FLAGS(sys.argv)
47 logging.setup()
44 service.serve()48 service.serve()
45 service.wait()49 service.wait()
4650
=== modified file 'bin/nova-scheduler'
--- bin/nova-scheduler 2010-12-15 19:02:33 +0000
+++ bin/nova-scheduler 2011-02-22 00:22:48 +0000
@@ -36,10 +36,14 @@
3636
37gettext.install('nova', unicode=1)37gettext.install('nova', unicode=1)
3838
39from nova import flags
40from nova import log as logging
39from nova import service41from nova import service
40from nova import utils42from nova import utils
4143
42if __name__ == '__main__':44if __name__ == '__main__':
43 utils.default_flagfile()45 utils.default_flagfile()
46 flags.FLAGS(sys.argv)
47 logging.setup()
44 service.serve()48 service.serve()
45 service.wait()49 service.wait()
4650
=== modified file 'bin/nova-volume'
--- bin/nova-volume 2010-12-15 19:02:33 +0000
+++ bin/nova-volume 2011-02-22 00:22:48 +0000
@@ -36,10 +36,14 @@
3636
37gettext.install('nova', unicode=1)37gettext.install('nova', unicode=1)
3838
39from nova import flags
40from nova import log as logging
39from nova import service41from nova import service
40from nova import utils42from nova import utils
4143
42if __name__ == '__main__':44if __name__ == '__main__':
43 utils.default_flagfile()45 utils.default_flagfile()
46 flags.FLAGS(sys.argv)
47 logging.setup()
44 service.serve()48 service.serve()
45 service.wait()49 service.wait()
4650
=== modified file 'nova/api/openstack/auth.py'
--- nova/api/openstack/auth.py 2011-02-18 22:35:12 +0000
+++ nova/api/openstack/auth.py 2011-02-22 00:22:48 +0000
@@ -19,7 +19,6 @@
19import hashlib19import hashlib
20import json20import json
21import time21import time
22import logging
2322
24import webob.exc23import webob.exc
25import webob.dec24import webob.dec
2625
=== modified file 'nova/api/openstack/backup_schedules.py'
--- nova/api/openstack/backup_schedules.py 2011-01-03 23:46:55 +0000
+++ nova/api/openstack/backup_schedules.py 2011-02-22 00:22:48 +0000
@@ -15,7 +15,6 @@
15# License for the specific language governing permissions and limitations15# License for the specific language governing permissions and limitations
16# under the License.16# under the License.
1717
18import logging
19import time18import time
2019
21from webob import exc20from webob import exc
2221
=== modified file 'nova/api/openstack/images.py'
--- nova/api/openstack/images.py 2011-01-13 19:48:01 +0000
+++ nova/api/openstack/images.py 2011-02-22 00:22:48 +0000
@@ -15,8 +15,6 @@
15# License for the specific language governing permissions and limitations15# License for the specific language governing permissions and limitations
16# under the License.16# under the License.
1717
18import logging
19
20from webob import exc18from webob import exc
2119
22from nova import compute20from nova import compute
2321
=== modified file 'nova/api/openstack/servers.py'
--- nova/api/openstack/servers.py 2011-02-18 22:36:01 +0000
+++ nova/api/openstack/servers.py 2011-02-22 00:22:48 +0000
@@ -33,7 +33,6 @@
3333
3434
35LOG = logging.getLogger('server')35LOG = logging.getLogger('server')
36LOG.setLevel(logging.DEBUG)
3736
3837
39FLAGS = flags.FLAGS38FLAGS = flags.FLAGS
4039
=== modified file 'nova/api/openstack/shared_ip_groups.py'
--- nova/api/openstack/shared_ip_groups.py 2011-01-11 07:38:40 +0000
+++ nova/api/openstack/shared_ip_groups.py 2011-02-22 00:22:48 +0000
@@ -15,8 +15,6 @@
15# License for the specific language governing permissions and limitations15# License for the specific language governing permissions and limitations
16# under the License.16# under the License.
1717
18import logging
19
20from webob import exc18from webob import exc
2119
22from nova import wsgi20from nova import wsgi
2321
=== modified file 'nova/api/openstack/zones.py'
--- nova/api/openstack/zones.py 2011-02-15 03:02:26 +0000
+++ nova/api/openstack/zones.py 2011-02-22 00:22:48 +0000
@@ -14,7 +14,6 @@
14# under the License.14# under the License.
1515
16import common16import common
17import logging
1817
19from nova import flags18from nova import flags
20from nova import wsgi19from nova import wsgi
2120
=== modified file 'nova/console/manager.py'
--- nova/console/manager.py 2011-01-20 17:52:02 +0000
+++ nova/console/manager.py 2011-02-22 00:22:48 +0000
@@ -20,11 +20,11 @@
20"""20"""
2121
22import functools22import functools
23import logging
24import socket23import socket
2524
26from nova import exception25from nova import exception
27from nova import flags26from nova import flags
27from nova import log as logging
28from nova import manager28from nova import manager
29from nova import rpc29from nova import rpc
30from nova import utils30from nova import utils
3131
=== modified file 'nova/console/xvp.py'
--- nova/console/xvp.py 2011-01-20 17:52:02 +0000
+++ nova/console/xvp.py 2011-02-22 00:22:48 +0000
@@ -20,7 +20,6 @@
20"""20"""
2121
22import fcntl22import fcntl
23import logging
24import os23import os
25import signal24import signal
26import subprocess25import subprocess
@@ -31,6 +30,7 @@
31from nova import db30from nova import db
32from nova import exception31from nova import exception
33from nova import flags32from nova import flags
33from nova import log as logging
34from nova import utils34from nova import utils
3535
36flags.DEFINE_string('console_xvp_conf_template',36flags.DEFINE_string('console_xvp_conf_template',
3737
=== modified file 'nova/log.py'
--- nova/log.py 2011-02-18 09:24:55 +0000
+++ nova/log.py 2011-02-22 00:22:48 +0000
@@ -65,6 +65,7 @@
65flags.DEFINE_list('default_log_levels',65flags.DEFINE_list('default_log_levels',
66 ['amqplib=WARN',66 ['amqplib=WARN',
67 'sqlalchemy=WARN',67 'sqlalchemy=WARN',
68 'boto=WARN',
68 'eventlet.wsgi.server=WARN'],69 'eventlet.wsgi.server=WARN'],
69 'list of logger=LEVEL pairs')70 'list of logger=LEVEL pairs')
7071
@@ -117,7 +118,7 @@
117 return os.path.basename(inspect.stack()[-1][1])118 return os.path.basename(inspect.stack()[-1][1])
118119
119120
120def get_log_file_path(binary=None):121def _get_log_file_path(binary=None):
121 if FLAGS.logfile:122 if FLAGS.logfile:
122 return FLAGS.logfile123 return FLAGS.logfile
123 if FLAGS.logdir:124 if FLAGS.logdir:
@@ -125,25 +126,6 @@
125 return '%s.log' % (os.path.join(FLAGS.logdir, binary),)126 return '%s.log' % (os.path.join(FLAGS.logdir, binary),)
126127
127128
128def basicConfig():
129 logging.basicConfig()
130 for handler in logging.root.handlers:
131 handler.setFormatter(_formatter)
132 if FLAGS.verbose:
133 logging.root.setLevel(logging.DEBUG)
134 else:
135 logging.root.setLevel(logging.INFO)
136 if FLAGS.use_syslog:
137 syslog = SysLogHandler(address='/dev/log')
138 syslog.setFormatter(_formatter)
139 logging.root.addHandler(syslog)
140 logpath = get_log_file_path()
141 if logpath:
142 logfile = WatchedFileHandler(logpath)
143 logfile.setFormatter(_formatter)
144 logging.root.addHandler(logfile)
145
146
147class NovaLogger(logging.Logger):129class NovaLogger(logging.Logger):
148 """130 """
149 NovaLogger manages request context and formatting.131 NovaLogger manages request context and formatting.
@@ -151,23 +133,19 @@
151 This becomes the class that is instanciated by logging.getLogger.133 This becomes the class that is instanciated by logging.getLogger.
152 """134 """
153 def __init__(self, name, level=NOTSET):135 def __init__(self, name, level=NOTSET):
154 level_name = self._get_level_from_flags(name, FLAGS)
155 level = globals()[level_name]
156 logging.Logger.__init__(self, name, level)136 logging.Logger.__init__(self, name, level)
137 self.setup_from_flags()
157138
158 def _get_level_from_flags(self, name, FLAGS):139 def setup_from_flags(self):
159 # if exactly "nova", or a child logger, honor the verbose flag140 """Setup logger from flags"""
160 if (name == "nova" or name.startswith("nova.")) and FLAGS.verbose:141 level = NOTSET
161 return 'DEBUG'
162 for pair in FLAGS.default_log_levels:142 for pair in FLAGS.default_log_levels:
163 logger, _sep, level = pair.partition('=')143 logger, _sep, level_name = pair.partition('=')
164 # NOTE(todd): if we set a.b, we want a.b.c to have the same level144 # NOTE(todd): if we set a.b, we want a.b.c to have the same level
165 # (but not a.bc, so we check the dot)145 # (but not a.bc, so we check the dot)
166 if name == logger:146 if self.name == logger or self.name.startswith("%s." % logger):
167 return level147 level = globals()[level_name]
168 if name.startswith(logger) and name[len(logger)] == '.':148 self.setLevel(level)
169 return level
170 return 'INFO'
171149
172 def _log(self, level, msg, args, exc_info=None, extra=None, context=None):150 def _log(self, level, msg, args, exc_info=None, extra=None, context=None):
173 """Extract context from any log call"""151 """Extract context from any log call"""
@@ -176,12 +154,12 @@
176 if context:154 if context:
177 extra.update(_dictify_context(context))155 extra.update(_dictify_context(context))
178 extra.update({"nova_version": version.version_string_with_vcs()})156 extra.update({"nova_version": version.version_string_with_vcs()})
179 logging.Logger._log(self, level, msg, args, exc_info, extra)157 return logging.Logger._log(self, level, msg, args, exc_info, extra)
180158
181 def addHandler(self, handler):159 def addHandler(self, handler):
182 """Each handler gets our custom formatter"""160 """Each handler gets our custom formatter"""
183 handler.setFormatter(_formatter)161 handler.setFormatter(_formatter)
184 logging.Logger.addHandler(self, handler)162 return logging.Logger.addHandler(self, handler)
185163
186 def audit(self, msg, *args, **kwargs):164 def audit(self, msg, *args, **kwargs):
187 """Shortcut for our AUDIT level"""165 """Shortcut for our AUDIT level"""
@@ -208,23 +186,6 @@
208 self.error(message, **kwargs)186 self.error(message, **kwargs)
209187
210188
211def handle_exception(type, value, tb):
212 logging.root.critical(str(value), exc_info=(type, value, tb))
213
214
215sys.excepthook = handle_exception
216logging.setLoggerClass(NovaLogger)
217
218
219class NovaRootLogger(NovaLogger):
220 pass
221
222if not isinstance(logging.root, NovaRootLogger):
223 logging.root = NovaRootLogger("nova.root", WARNING)
224 NovaLogger.root = logging.root
225 NovaLogger.manager.root = logging.root
226
227
228class NovaFormatter(logging.Formatter):189class NovaFormatter(logging.Formatter):
229 """190 """
230 A nova.context.RequestContext aware formatter configured through flags.191 A nova.context.RequestContext aware formatter configured through flags.
@@ -271,8 +232,72 @@
271_formatter = NovaFormatter()232_formatter = NovaFormatter()
272233
273234
235class NovaRootLogger(NovaLogger):
236 def __init__(self, name, level=NOTSET):
237 self.logpath = None
238 self.filelog = None
239 self.syslog = SysLogHandler(address='/dev/log')
240 self.streamlog = StreamHandler()
241 NovaLogger.__init__(self, name, level)
242
243 def setup_from_flags(self):
244 """Setup logger from flags"""
245 global _filelog
246 if FLAGS.use_syslog:
247 self.addHandler(self.syslog)
248 else:
249 self.removeHandler(self.syslog)
250 logpath = _get_log_file_path()
251 if logpath:
252 self.removeHandler(self.streamlog)
253 if logpath != self.logpath:
254 self.removeHandler(self.filelog)
255 self.filelog = WatchedFileHandler(logpath)
256 self.addHandler(self.filelog)
257 self.logpath = logpath
258 else:
259 self.removeHandler(self.filelog)
260 self.addHandler(self.streamlog)
261 if FLAGS.verbose:
262 self.setLevel(DEBUG)
263 else:
264 self.setLevel(INFO)
265
266
267def handle_exception(type, value, tb):
268 logging.root.critical(str(value), exc_info=(type, value, tb))
269
270
271def reset():
272 """Resets logging handlers. Should be called if FLAGS changes."""
273 for logger in NovaLogger.manager.loggerDict.itervalues():
274 if isinstance(logger, NovaLogger):
275 logger.setup_from_flags()
276
277
278def setup():
279 """Setup nova logging."""
280 if not isinstance(logging.root, NovaRootLogger):
281 logging._acquireLock()
282 for handler in logging.root.handlers:
283 logging.root.removeHandler(handler)
284 logging.root = NovaRootLogger("nova")
285 NovaLogger.root = logging.root
286 NovaLogger.manager.root = logging.root
287 for logger in NovaLogger.manager.loggerDict.itervalues():
288 logger.root = logging.root
289 if isinstance(logger, logging.Logger):
290 NovaLogger.manager._fixupParents(logger)
291 NovaLogger.manager.loggerDict["nova"] = logging.root
292 logging._releaseLock()
293 sys.excepthook = handle_exception
294 reset()
295
296
297root = logging.root
298logging.setLoggerClass(NovaLogger)
299
300
274def audit(msg, *args, **kwargs):301def audit(msg, *args, **kwargs):
275 """Shortcut for logging to root log with sevrity 'AUDIT'."""302 """Shortcut for logging to root log with sevrity 'AUDIT'."""
276 if len(logging.root.handlers) == 0:
277 basicConfig()
278 logging.root.log(AUDIT, msg, *args, **kwargs)303 logging.root.log(AUDIT, msg, *args, **kwargs)
279304
=== modified file 'nova/service.py'
--- nova/service.py 2011-01-27 19:52:10 +0000
+++ nova/service.py 2011-02-22 00:22:48 +0000
@@ -214,9 +214,6 @@
214214
215215
216def serve(*services):216def serve(*services):
217 FLAGS(sys.argv)
218 logging.basicConfig()
219
220 if not services:217 if not services:
221 services = [Service.create()]218 services = [Service.create()]
222219
223220
=== modified file 'nova/test.py'
--- nova/test.py 2011-01-26 21:58:24 +0000
+++ nova/test.py 2011-02-22 00:22:48 +0000
@@ -32,9 +32,9 @@
32from nova import db32from nova import db
33from nova import fakerabbit33from nova import fakerabbit
34from nova import flags34from nova import flags
35from nova import log as logging
35from nova import rpc36from nova import rpc
36from nova.network import manager as network_manager37from nova.network import manager as network_manager
37from nova.tests import fake_flags
3838
3939
40FLAGS = flags.FLAGS40FLAGS = flags.FLAGS
4141
=== modified file 'nova/tests/fake_flags.py'
--- nova/tests/fake_flags.py 2011-01-18 23:39:24 +0000
+++ nova/tests/fake_flags.py 2011-02-22 00:22:48 +0000
@@ -41,3 +41,4 @@
41FLAGS.verbose = True41FLAGS.verbose = True
42FLAGS.sql_connection = 'sqlite:///nova.sqlite'42FLAGS.sql_connection = 'sqlite:///nova.sqlite'
43FLAGS.use_ipv6 = True43FLAGS.use_ipv6 = True
44FLAGS.logfile = 'tests.log'
4445
=== modified file 'nova/tests/test_auth.py'
--- nova/tests/test_auth.py 2011-01-04 05:23:35 +0000
+++ nova/tests/test_auth.py 2011-02-22 00:22:48 +0000
@@ -327,15 +327,6 @@
327class AuthManagerLdapTestCase(AuthManagerTestCase, test.TestCase):327class AuthManagerLdapTestCase(AuthManagerTestCase, test.TestCase):
328 auth_driver = 'nova.auth.ldapdriver.FakeLdapDriver'328 auth_driver = 'nova.auth.ldapdriver.FakeLdapDriver'
329329
330 def __init__(self, *args, **kwargs):
331 AuthManagerTestCase.__init__(self)
332 test.TestCase.__init__(self, *args, **kwargs)
333 import nova.auth.fakeldap as fakeldap
334 if FLAGS.flush_db:
335 LOG.info("Flushing datastore")
336 r = fakeldap.Store.instance()
337 r.flushdb()
338
339330
340class AuthManagerDbTestCase(AuthManagerTestCase, test.TestCase):331class AuthManagerDbTestCase(AuthManagerTestCase, test.TestCase):
341 auth_driver = 'nova.auth.dbdriver.DbDriver'332 auth_driver = 'nova.auth.dbdriver.DbDriver'
342333
=== modified file 'nova/tests/test_console.py'
--- nova/tests/test_console.py 2011-01-15 01:44:47 +0000
+++ nova/tests/test_console.py 2011-02-22 00:22:48 +0000
@@ -21,7 +21,6 @@
21"""21"""
2222
23import datetime23import datetime
24import logging
2524
26from nova import context25from nova import context
27from nova import db26from nova import db
@@ -38,7 +37,6 @@
38class ConsoleTestCase(test.TestCase):37class ConsoleTestCase(test.TestCase):
39 """Test case for console proxy"""38 """Test case for console proxy"""
40 def setUp(self):39 def setUp(self):
41 logging.getLogger().setLevel(logging.DEBUG)
42 super(ConsoleTestCase, self).setUp()40 super(ConsoleTestCase, self).setUp()
43 self.flags(console_driver='nova.console.fake.FakeConsoleProxy',41 self.flags(console_driver='nova.console.fake.FakeConsoleProxy',
44 stub_compute=True)42 stub_compute=True)
4543
=== modified file 'nova/tests/test_direct.py'
--- nova/tests/test_direct.py 2011-01-08 00:05:06 +0000
+++ nova/tests/test_direct.py 2011-02-22 00:22:48 +0000
@@ -19,7 +19,6 @@
19"""Tests for Direct API."""19"""Tests for Direct API."""
2020
21import json21import json
22import logging
2322
24import webob23import webob
2524
2625
=== modified file 'nova/tests/test_localization.py'
--- nova/tests/test_localization.py 2011-01-27 22:54:59 +0000
+++ nova/tests/test_localization.py 2011-02-22 00:22:48 +0000
@@ -15,7 +15,6 @@
15# under the License.15# under the License.
1616
17import glob17import glob
18import logging
19import os18import os
20import re19import re
21import sys20import sys
2221
=== modified file 'nova/tests/test_log.py'
--- nova/tests/test_log.py 2011-02-16 09:02:17 +0000
+++ nova/tests/test_log.py 2011-02-22 00:22:48 +0000
@@ -1,9 +1,12 @@
1import cStringIO1import cStringIO
22
3from nova import context3from nova import context
4from nova import flags
4from nova import log5from nova import log
5from nova import test6from nova import test
67
8FLAGS = flags.FLAGS
9
710
8def _fake_context():11def _fake_context():
9 return context.RequestContext(1, 1)12 return context.RequestContext(1, 1)
@@ -14,15 +17,11 @@
14 super(RootLoggerTestCase, self).setUp()17 super(RootLoggerTestCase, self).setUp()
15 self.log = log.logging.root18 self.log = log.logging.root
1619
17 def tearDown(self):
18 super(RootLoggerTestCase, self).tearDown()
19 log.NovaLogger.manager.loggerDict = {}
20
21 def test_is_nova_instance(self):20 def test_is_nova_instance(self):
22 self.assert_(isinstance(self.log, log.NovaLogger))21 self.assert_(isinstance(self.log, log.NovaLogger))
2322
24 def test_name_is_nova_root(self):23 def test_name_is_nova(self):
25 self.assertEqual("nova.root", self.log.name)24 self.assertEqual("nova", self.log.name)
2625
27 def test_handlers_have_nova_formatter(self):26 def test_handlers_have_nova_formatter(self):
28 formatters = []27 formatters = []
@@ -45,25 +44,36 @@
45 log.audit("foo", context=_fake_context())44 log.audit("foo", context=_fake_context())
46 self.assert_(True) # didn't raise exception45 self.assert_(True) # didn't raise exception
4746
47 def test_will_be_verbose_if_verbose_flag_set(self):
48 self.flags(verbose=True)
49 log.reset()
50 self.assertEqual(log.DEBUG, self.log.level)
51
52 def test_will_not_be_verbose_if_verbose_flag_not_set(self):
53 self.flags(verbose=False)
54 log.reset()
55 self.assertEqual(log.INFO, self.log.level)
56
4857
49class LogHandlerTestCase(test.TestCase):58class LogHandlerTestCase(test.TestCase):
50 def test_log_path_logdir(self):59 def test_log_path_logdir(self):
51 self.flags(logdir='/some/path')60 self.flags(logdir='/some/path', logfile=None)
52 self.assertEquals(log.get_log_file_path(binary='foo-bar'),61 self.assertEquals(log._get_log_file_path(binary='foo-bar'),
53 '/some/path/foo-bar.log')62 '/some/path/foo-bar.log')
5463
55 def test_log_path_logfile(self):64 def test_log_path_logfile(self):
56 self.flags(logfile='/some/path/foo-bar.log')65 self.flags(logfile='/some/path/foo-bar.log')
57 self.assertEquals(log.get_log_file_path(binary='foo-bar'),66 self.assertEquals(log._get_log_file_path(binary='foo-bar'),
58 '/some/path/foo-bar.log')67 '/some/path/foo-bar.log')
5968
60 def test_log_path_none(self):69 def test_log_path_none(self):
61 self.assertTrue(log.get_log_file_path(binary='foo-bar') is None)70 self.flags(logdir=None, logfile=None)
71 self.assertTrue(log._get_log_file_path(binary='foo-bar') is None)
6272
63 def test_log_path_logfile_overrides_logdir(self):73 def test_log_path_logfile_overrides_logdir(self):
64 self.flags(logdir='/some/other/path',74 self.flags(logdir='/some/other/path',
65 logfile='/some/path/foo-bar.log')75 logfile='/some/path/foo-bar.log')
66 self.assertEquals(log.get_log_file_path(binary='foo-bar'),76 self.assertEquals(log._get_log_file_path(binary='foo-bar'),
67 '/some/path/foo-bar.log')77 '/some/path/foo-bar.log')
6878
6979
@@ -76,13 +86,15 @@
76 logging_debug_format_suffix="--DBG")86 logging_debug_format_suffix="--DBG")
77 self.log = log.logging.root87 self.log = log.logging.root
78 self.stream = cStringIO.StringIO()88 self.stream = cStringIO.StringIO()
79 handler = log.StreamHandler(self.stream)89 self.handler = log.StreamHandler(self.stream)
80 self.log.addHandler(handler)90 self.log.addHandler(self.handler)
91 self.level = self.log.level
81 self.log.setLevel(log.DEBUG)92 self.log.setLevel(log.DEBUG)
8293
83 def tearDown(self):94 def tearDown(self):
95 self.log.setLevel(self.level)
96 self.log.removeHandler(self.handler)
84 super(NovaFormatterTestCase, self).tearDown()97 super(NovaFormatterTestCase, self).tearDown()
85 log.NovaLogger.manager.loggerDict = {}
8698
87 def test_uncontextualized_log(self):99 def test_uncontextualized_log(self):
88 self.log.info("foo")100 self.log.info("foo")
@@ -102,30 +114,15 @@
102class NovaLoggerTestCase(test.TestCase):114class NovaLoggerTestCase(test.TestCase):
103 def setUp(self):115 def setUp(self):
104 super(NovaLoggerTestCase, self).setUp()116 super(NovaLoggerTestCase, self).setUp()
105 self.flags(default_log_levels=["nova-test=AUDIT"], verbose=False)117 levels = FLAGS.default_log_levels
118 levels.append("nova-test=AUDIT")
119 self.flags(default_log_levels=levels,
120 verbose=True)
106 self.log = log.getLogger('nova-test')121 self.log = log.getLogger('nova-test')
107122
108 def tearDown(self):
109 super(NovaLoggerTestCase, self).tearDown()
110 log.NovaLogger.manager.loggerDict = {}
111
112 def test_has_level_from_flags(self):123 def test_has_level_from_flags(self):
113 self.assertEqual(log.AUDIT, self.log.level)124 self.assertEqual(log.AUDIT, self.log.level)
114125
115 def test_child_log_has_level_of_parent_flag(self):126 def test_child_log_has_level_of_parent_flag(self):
116 l = log.getLogger('nova-test.foo')127 l = log.getLogger('nova-test.foo')
117 self.assertEqual(log.AUDIT, l.level)128 self.assertEqual(log.AUDIT, l.level)
118
119
120class VerboseLoggerTestCase(test.TestCase):
121 def setUp(self):
122 super(VerboseLoggerTestCase, self).setUp()
123 self.flags(default_log_levels=["nova.test=AUDIT"], verbose=True)
124 self.log = log.getLogger('nova.test')
125
126 def tearDown(self):
127 super(VerboseLoggerTestCase, self).tearDown()
128 log.NovaLogger.manager.loggerDict = {}
129
130 def test_will_be_verbose_if_named_nova_and_verbose_flag_set(self):
131 self.assertEqual(log.DEBUG, self.log.level)
132129
=== modified file 'nova/twistd.py'
--- nova/twistd.py 2011-02-14 22:19:15 +0000
+++ nova/twistd.py 2011-02-22 00:22:48 +0000
@@ -148,6 +148,7 @@
148 options.insert(0, '')148 options.insert(0, '')
149149
150 args = FLAGS(options)150 args = FLAGS(options)
151 logging.setup()
151 argv = args[1:]152 argv = args[1:]
152 # ignore subcommands153 # ignore subcommands
153154
@@ -258,7 +259,6 @@
258 print 'usage: %s [options] [start|stop|restart]' % argv[0]259 print 'usage: %s [options] [start|stop|restart]' % argv[0]
259 sys.exit(1)260 sys.exit(1)
260261
261 logging.basicConfig()
262 logging.debug(_("Full set of FLAGS:"))262 logging.debug(_("Full set of FLAGS:"))
263 for flag in FLAGS:263 for flag in FLAGS:
264 logging.debug("%s : %s" % (flag, FLAGS.get(flag, None)))264 logging.debug("%s : %s" % (flag, FLAGS.get(flag, None)))
265265
=== modified file 'nova/utils.py'
--- nova/utils.py 2011-02-17 22:09:26 +0000
+++ nova/utils.py 2011-02-22 00:22:48 +0000
@@ -55,7 +55,7 @@
55 __import__(mod_str)55 __import__(mod_str)
56 return getattr(sys.modules[mod_str], class_str)56 return getattr(sys.modules[mod_str], class_str)
57 except (ImportError, ValueError, AttributeError), exc:57 except (ImportError, ValueError, AttributeError), exc:
58 logging.debug(_('Inner Exception: %s'), exc)58 LOG.debug(_('Inner Exception: %s'), exc)
59 raise exception.NotFound(_('Class %s cannot be found') % class_str)59 raise exception.NotFound(_('Class %s cannot be found') % class_str)
6060
6161
6262
=== modified file 'nova/wsgi.py'
--- nova/wsgi.py 2011-01-21 21:10:26 +0000
+++ nova/wsgi.py 2011-02-22 00:22:48 +0000
@@ -59,7 +59,6 @@
59 """Server class to manage multiple WSGI sockets and applications."""59 """Server class to manage multiple WSGI sockets and applications."""
6060
61 def __init__(self, threads=1000):61 def __init__(self, threads=1000):
62 logging.basicConfig()
63 self.pool = eventlet.GreenPool(threads)62 self.pool = eventlet.GreenPool(threads)
6463
65 def start(self, application, port, host='0.0.0.0', backlog=128):64 def start(self, application, port, host='0.0.0.0', backlog=128):
6665
=== modified file 'run_tests.py'
--- run_tests.py 2011-01-31 05:38:58 +0000
+++ run_tests.py 2011-02-22 00:22:48 +0000
@@ -27,6 +27,7 @@
27from nose import core27from nose import core
2828
29from nova import log as logging29from nova import log as logging
30from nova.tests import fake_flags
3031
3132
32class NovaTestResult(result.TextTestResult):33class NovaTestResult(result.TextTestResult):
@@ -60,7 +61,7 @@
6061
6162
62if __name__ == '__main__':63if __name__ == '__main__':
63 logging.basicConfig()64 logging.setup()
64 c = config.Config(stream=sys.stdout,65 c = config.Config(stream=sys.stdout,
65 env=os.environ,66 env=os.environ,
66 verbosity=3,67 verbosity=3,
6768
=== modified file 'run_tests.sh'
--- run_tests.sh 2011-01-31 05:55:32 +0000
+++ run_tests.sh 2011-02-22 00:22:48 +0000
@@ -40,7 +40,7 @@
40function run_tests {40function run_tests {
41 # Just run the test suites in current environment41 # Just run the test suites in current environment
42 ${wrapper} rm -f nova.sqlite42 ${wrapper} rm -f nova.sqlite
43 ${wrapper} $NOSETESTS 2> run_tests.err.log43 ${wrapper} $NOSETESTS
44}44}
4545
46NOSETESTS="python run_tests.py $noseargs"46NOSETESTS="python run_tests.py $noseargs"