Merge lp:~pfalcon/linaro-ci-dashboard/logging into lp:linaro-ci-dashboard

Proposed by Paul Sokolovsky
Status: Merged
Approved by: Stevan Radaković
Approved revision: 25
Merged at revision: 21
Proposed branch: lp:~pfalcon/linaro-ci-dashboard/logging
Merge into: lp:linaro-ci-dashboard
Diff against target: 107 lines (+50/-0)
4 files modified
dashboard/frontend/integration_loop/models/integration_loop.py (+3/-0)
dashboard/jenkinsserver/models/jenkins_server.py (+3/-0)
dashboard/lib/logger.py (+30/-0)
dashboard/settings.py (+14/-0)
To merge this branch: bzr merge lp:~pfalcon/linaro-ci-dashboard/logging
Reviewer Review Type Date Requested Status
Stevan Radaković Approve
Review via email: mp+120095@code.launchpad.net

Description of the change

Add (proper) logging support for linaro-ci-dashboard. Example:

2012-08-17 04:45:12,361 INFO [jenkinsserver.JenkinsServer] Creating/updating Jenkins job for loop: IntegrationLoop(id=1, branch=lp:linaro-android-build-tools, ...)

Note that helpers.py is intended as catch-all for all other helper things we may need, that's why it's named though and put at the top level.

To post a comment you must log in.
Revision history for this message
Stevan Radaković (stevanr) wrote :

Hi Paul, thanks for implementing the logging support!!

One small change is needed though...
Since we'er following the OO concept to the core (we even have class-based views in apps), let's try to respect it now as well.
My idea is to create a 'lib' directory under dashboard and add the i.e. Logger class over there with static 'getClassLogger' method.. Then do the same thing you're doing right now, meaning everything else looks good.

review: Needs Fixing
Revision history for this message
Paul Sokolovsky (pfalcon) wrote :

Well, artificially wrapping standalone functions in once-off classes in not really OO, nor Pythonic way of doing things - unlike Java, with Python, there's no need (nor there's a pattern of) wrapping everything in classes. I'm going to make as you suggest in this case, but this whole matter needs more discussion.

Revision history for this message
Stevan Radaković (stevanr) wrote :

On 08/17/2012 02:13 PM, Paul Sokolovsky wrote:
> Well, artificially wrapping standalone functions in once-off classes in not really OO, nor Pythonic way of doing things - unlike Java, with Python, there's no need (nor there's a pattern of) wrapping everything in classes. I'm going to make as you suggest in this case, but this whole matter needs more discussion.
>

Yes I was thinking of a Logger class which would actually contain
self.log as an attribute and then all the log-able classes could inherit
this class and use the self.log outright... But as I played with this I
realized that most of our relevant classes already inherit other django
classes like Model etc. and I had problems with multiple inheritance in
my quick examples (i.e. calling __init__ for both base classes), so I
just suggested the next best thing...

25. By Paul Sokolovsky

Move helpers.getClassLogger() to lib.logger.Logger.getClassLogger().

Revision history for this message
Paul Sokolovsky (pfalcon) wrote :

> Yes I was thinking of a Logger class which would actually contain
self.log as an attribute and then all the log-able classes could inherit
this class and use the self.log outright...

Well, this would make much more sense, and I already regretted that I didn't strongly suggest to just use module-level logger in my RFC. Because for constructor-less classes, we'd now need to add constructor, and due to Python's verbose super() syntax, that really sucks.

But yeah, you can easily mix-in a behavior (i.e. standalone methods), but not so easy a state, which would require a constructor to initialize.

Anyway, the refactor is ready.

Revision history for this message
Stevan Radaković (stevanr) wrote :

Looks good.
Approve +1.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'dashboard/frontend/integration_loop/models/integration_loop.py'
2--- dashboard/frontend/integration_loop/models/integration_loop.py 2012-08-16 15:12:10 +0000
3+++ dashboard/frontend/integration_loop/models/integration_loop.py 2012-08-17 13:44:39 +0000
4@@ -45,3 +45,6 @@
5 # loop config on the remote server.
6 self.server.create_or_update_integration_loop(self)
7 super(IntegrationLoop, self).schedule_build()
8+
9+ def __repr__(self):
10+ return "IntegrationLoop(id=%d, branch=%s, ...)" % (self.id, self.branch)
11
12=== modified file 'dashboard/jenkinsserver/models/jenkins_server.py'
13--- dashboard/jenkinsserver/models/jenkins_server.py 2012-07-26 13:25:06 +0000
14+++ dashboard/jenkinsserver/models/jenkins_server.py 2012-08-17 13:44:39 +0000
15@@ -22,6 +22,7 @@
16 import jenkins
17 import json
18 import urllib2
19+from lib.logger import Logger
20
21 class JenkinsServer(models.Model):
22 class Meta:
23@@ -33,6 +34,7 @@
24
25 def __init__(self, *args, **kwargs):
26 super(JenkinsServer, self).__init__(*args, **kwargs)
27+ self.log = Logger.getClassLogger(self)
28 self.jenkins = jenkins.Jenkins(self.url,
29 self.username.encode('utf-8'),
30 self.password.encode('utf-8'))
31@@ -41,6 +43,7 @@
32 """ Create integration loop if it doesn't exist on Jenkins.
33 Otherwise, update the existing one with new XML."""
34
35+ self.log.info("Creating/updating Jenkins job for loop: %r", loop)
36 jxml = self.create_integration_job_xml(loop)
37 if not self.jenkins.job_exists(loop.name):
38 self.create_job(loop.name, jxml)
39
40=== added directory 'dashboard/lib'
41=== added file 'dashboard/lib/__init__.py'
42=== added file 'dashboard/lib/logger.py'
43--- dashboard/lib/logger.py 1970-01-01 00:00:00 +0000
44+++ dashboard/lib/logger.py 2012-08-17 13:44:39 +0000
45@@ -0,0 +1,30 @@
46+# Copyright (C) 2012 Linaro
47+#
48+# This file is part of linaro-ci-dashboard.
49+#
50+# linaro-ci-dashboard is free software: you can redistribute it and/or modify
51+# it under the terms of the GNU Affero General Public License as published by
52+# the Free Software Foundation, either version 3 of the License, or
53+# (at your option) any later version.
54+#
55+# linaro-ci-dashboard is distributed in the hope that it will be useful,
56+# but WITHOUT ANY WARRANTY; without even the implied warranty of
57+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
58+# GNU Affero General Public License for more details.
59+#
60+# You should have received a copy of the GNU Affero General Public License
61+# along with linaro-ci-dashboard. If not, see <http://www.gnu.org/licenses/>.
62+# Django settings for dashboard project.
63+
64+import logging
65+
66+
67+class Logger:
68+ "Logging-related helper class."
69+
70+ @staticmethod
71+ def getClassLogger(obj):
72+ "Given a class instance, return logger to use for that class."
73+ toplevel_package = obj.__class__.__module__.split(".", 1)[0]
74+ logger_name = toplevel_package + '.' + obj.__class__.__name__
75+ return logging.getLogger(logger_name)
76
77=== modified file 'dashboard/settings.py'
78--- dashboard/settings.py 2012-08-17 07:32:04 +0000
79+++ dashboard/settings.py 2012-08-17 13:44:39 +0000
80@@ -170,7 +170,17 @@
81 LOGGING = {
82 'version': 1,
83 'disable_existing_loggers': False,
84+ 'formatters': {
85+ 'default': {
86+ 'format': '%(asctime)s %(levelname)s [%(name)s] %(message)s'
87+ },
88+ },
89 'handlers': {
90+ 'console': {
91+ 'class': 'logging.StreamHandler',
92+ 'stream': 'ext://sys.stdout',
93+ 'formatter': 'default',
94+ },
95 'mail_admins': {
96 'level': 'ERROR',
97 'class': 'django.utils.log.AdminEmailHandler'
98@@ -182,6 +192,10 @@
99 'level': 'ERROR',
100 'propagate': True,
101 },
102+ },
103+ 'root': {
104+ 'level': 'INFO',
105+ 'handlers': ['console'],
106 }
107 }
108

Subscribers

People subscribed via source and target branches