Merge lp:~jtv/maas/oops into lp:maas/trunk

Proposed by Jeroen T. Vermeulen on 2012-01-19
Status: Merged
Approved by: Julian Edwards on 2012-01-19
Approved revision: 29
Merged at revision: 28
Proposed branch: lp:~jtv/maas/oops
Merge into: lp:maas/trunk
Diff against target: 157 lines (+82/-3)
7 files modified
buildout.cfg (+3/-0)
src/maas/development.py (+3/-0)
src/maas/production.py (+3/-0)
src/maas/settings.py (+3/-0)
src/maasserver/management/commands/deletedb.py (+15/-1)
src/maasserver/management/commands/query.py (+16/-2)
src/maasserver/management/commands/runserver.py (+39/-0)
To merge this branch: bzr merge lp:~jtv/maas/oops
Reviewer Review Type Date Requested Status
Julian Edwards (community) Approve on 2012-01-19
Gavin Panella (community) 2012-01-19 Approve on 2012-01-19
Review via email: mp+89267@code.launchpad.net

Commit Message

Basic oops integration.

Description of the Change

Basic oops integration.

This enables basic oops logging for MaaS: if django raises an exception while handling a request, log it as an oops. By default, production oopses go into /var/log/maas/<date> (our packaging will have to create /var/log/maas with appropriate ownership) and development oopses go into ./logs (to maintain isolation between branches).

Don't expect to see familiar Launchpad-style oops pages just yet. Two things are still missing:

 * In debug mode, django catches exceptions and displays its own error page, with essentially the same information. Which is probably fine, but the error doesn't bubble up out of django and so no oops is stored.

 * There's no display support for oopses. You may want to install and use python-oops-tools yourself to work with the oops files.

Jeroen

To post a comment you must log in.
Gavin Panella (allenap) wrote :

Looks great :)

[1]

=== modified file 'src/maas/settings.py'
--- src/maas/settings.py 2012-01-19 13:56:36 +0000
+++ src/maas/settings.py 2012-01-19 15:28:18 +0000
@@ -20,6 +20,9 @@

 MANAGERS = ADMINS

+# Location where python-oops should store errors.
+OOPS_REPOSITORY = '/var/log/maas'
+

Would it be worth setting this in production.py and using something
like "/does/not/exist" in settings.py?

[2]

=== added file 'src/maasserver/management/commands/runserver.py'

What happens if we get another app in this Django project? Does Django
correctly execute the runserver command for each application in in the
project, or will this break?

[3]

Can you use the template from templates/module.py and run
utilities/format-new-and-modified-imports?

[4]

+ wsgi_handler = BaseRunserverCommand.get_handler(self, *args, **kwargs)

Use super()?

review: Approve
Julian Edwards (julian-edwards) wrote :

+ oops_config = Config()

I think this would scan better if you did:

import oops
oops_config = oops.Config()

because I was left wondering what Config() was a config for....

Otherwise Gavin's comments capture everything I would have said, and more :)

review: Approve
lp:~jtv/maas/oops updated on 2012-01-19
30. By Jeroen T. Vermeulen on 2012-01-19

Review changes.

Jeroen T. Vermeulen (jtv) wrote :

> === modified file 'src/maas/settings.py'
> --- src/maas/settings.py 2012-01-19 13:56:36 +0000
> +++ src/maas/settings.py 2012-01-19 15:28:18 +0000
> @@ -20,6 +20,9 @@
>
> MANAGERS = ADMINS
>
> +# Location where python-oops should store errors.
> +OOPS_REPOSITORY = '/var/log/maas'
> +
>
> Would it be worth setting this in production.py and using something
> like "/does/not/exist" in settings.py?

Not very nice; at any rate the directory gets created. I did add it to production.py, and made the default local. I considered None, but that would just produce an obscure error on top of an app failure.

> === added file 'src/maasserver/management/commands/runserver.py'
>
> What happens if we get another app in this Django project? Does Django
> correctly execute the runserver command for each application in in the
> project, or will this break?

I think that, assuming we wanted the same oops-tools setup, we'd want to move common code out to a shared location somewhere and give that app its own runserver.py.

Not something I'm worried about right now though.

> Can you use the template from templates/module.py and run
> utilities/format-new-and-modified-imports?

Done. Also for the other command modules.

> [4]
>
> + wsgi_handler = BaseRunserverCommand.get_handler(self, *args,
> **kwargs)
>
> Use super()?

I worried about super() being too clever for this use-case and surprising me in some way, but okay, I'll give it a whirl.

Jeroen

lp:~jtv/maas/oops updated on 2012-01-19
31. By Jeroen T. Vermeulen on 2012-01-19

Review change: super().

32. By Jeroen T. Vermeulen on 2012-01-19

Review change.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'buildout.cfg'
2--- buildout.cfg 2012-01-18 16:48:01 +0000
3+++ buildout.cfg 2012-01-19 16:53:59 +0000
4@@ -25,6 +25,9 @@
5 django-debug-toolbar
6 django-piston
7 fixtures
8+ oops
9+ oops-datedir-repo
10+ oops-wsgi
11 psycopg2
12 rabbitfixture
13 South
14
15=== modified file 'src/maas/development.py'
16--- src/maas/development.py 2012-01-19 13:56:36 +0000
17+++ src/maas/development.py 2012-01-19 16:53:59 +0000
18@@ -19,6 +19,9 @@
19 # cluster is running in the branch.
20 TEST_RUNNER = 'maas.testing.runner.TestRunner'
21
22+# Location where python-oops should store errors.
23+OOPS_REPOSITORY = 'logs'
24+
25 DEBUG = True
26 TEMPLATE_DEBUG = DEBUG
27 YUI_DEBUG = DEBUG
28
29=== modified file 'src/maas/production.py'
30--- src/maas/production.py 2012-01-19 13:56:36 +0000
31+++ src/maas/production.py 2012-01-19 16:53:59 +0000
32@@ -12,3 +12,6 @@
33 __all__ = []
34
35 from maas.settings import *
36+
37+# Location where python-oops should store errors.
38+OOPS_REPOSITORY = '/var/log/maas'
39
40=== modified file 'src/maas/settings.py'
41--- src/maas/settings.py 2012-01-19 13:56:36 +0000
42+++ src/maas/settings.py 2012-01-19 16:53:59 +0000
43@@ -20,6 +20,9 @@
44
45 MANAGERS = ADMINS
46
47+# Location where python-oops should store errors.
48+OOPS_REPOSITORY = 'logs'
49+
50 DEBUG = False
51 TEMPLATE_DEBUG = DEBUG
52 YUI_DEBUG = DEBUG
53
54=== modified file 'src/maasserver/management/commands/deletedb.py'
55--- src/maasserver/management/commands/deletedb.py 2012-01-19 12:27:11 +0000
56+++ src/maasserver/management/commands/deletedb.py 2012-01-19 16:53:59 +0000
57@@ -1,6 +1,20 @@
58+# Copyright 2012 Canonical Ltd. This software is licensed under the
59+# GNU Affero General Public License version 3 (see the file LICENSE).
60+
61+from __future__ import print_function
62+
63 from subprocess import check_call
64
65-from django.core.management.base import BaseCommand, CommandError
66+from django.core.management.base import (
67+ BaseCommand,
68+ CommandError,
69+ )
70+
71+
72+"""Django command: stop and delete the local database cluster."""
73+
74+__metaclass__ = type
75+__all__ = ['Command']
76
77
78 class Command(BaseCommand):
79
80=== modified file 'src/maasserver/management/commands/query.py'
81--- src/maasserver/management/commands/query.py 2012-01-19 12:27:11 +0000
82+++ src/maasserver/management/commands/query.py 2012-01-19 16:53:59 +0000
83@@ -1,6 +1,20 @@
84+# Copyright 2012 Canonical Ltd. This software is licensed under the
85+# GNU Affero General Public License version 3 (see the file LICENSE).
86+
87+from __future__ import print_function
88+
89 from subprocess import check_call
90
91-from django.core.management.base import BaseCommand, CommandError
92+from django.core.management.base import (
93+ BaseCommand,
94+ CommandError,
95+ )
96+
97+
98+"""Django command: access the development database directly in SQL."""
99+
100+__metaclass__ = type
101+__all__ = ['Command']
102
103
104 class Command(BaseCommand):
105@@ -8,7 +22,7 @@
106
107 Executes an SQL statement given on the command line, or opens an SQL
108 shell if no statement was given.
109- """
110+ """
111
112 args = "[SQL statement]"
113 help = "Access the database directly in SQL."
114
115=== added file 'src/maasserver/management/commands/runserver.py'
116--- src/maasserver/management/commands/runserver.py 1970-01-01 00:00:00 +0000
117+++ src/maasserver/management/commands/runserver.py 2012-01-19 16:53:59 +0000
118@@ -0,0 +1,39 @@
119+# Copyright 2012 Canonical Ltd. This software is licensed under the
120+# GNU Affero General Public License version 3 (see the file LICENSE).
121+
122+from __future__ import print_function
123+
124+from django.conf import settings
125+from django.core.management.commands.runserver import BaseRunserverCommand
126+import oops
127+from oops_datedir_repo import DateDirRepo
128+from oops_wsgi import (
129+ install_hooks,
130+ make_app,
131+ )
132+
133+
134+"""Django command: run the server. Overrides the default implementation."""
135+
136+__metaclass__ = type
137+__all__ = ['Command']
138+
139+
140+class Command(BaseRunserverCommand):
141+ """Customized "runserver" command that wraps the WSGI handler."""
142+
143+ def get_handler(self, *args, **kwargs):
144+ """Overridable from `BaseRunserverCommand`: Obtain a WSGI handler."""
145+ wsgi_handler = super(Command, self).get_handler(self, *args, **kwargs)
146+
147+ # Wrap the WSGI handler in an oops handler. This catches (most)
148+ # exceptions bubbling up out of the app, and stores them as
149+ # oopses in the directory specified by the OOPS_REPOSITORY
150+ # configuration setting.
151+ # Django's debug mode causes it to handle exceptions itself, so
152+ # don't expect oopses when DEBUG is set to True.
153+ oops_config = oops.Config()
154+ oops_repository = DateDirRepo(settings.OOPS_REPOSITORY, 'maasserver')
155+ oops_config.publishers.append(oops_repository.publish)
156+ install_hooks(oops_config)
157+ return make_app(wsgi_handler, oops_config)