Package buildbot :: Package status :: Package web :: Module logs
[frames] | no frames]

Source Code for Module buildbot.status.web.logs

  1  # This file is part of Buildbot.  Buildbot is free software: you can 
  2  # redistribute it and/or modify it under the terms of the GNU General Public 
  3  # License as published by the Free Software Foundation, version 2. 
  4  # 
  5  # This program is distributed in the hope that it will be useful, but WITHOUT 
  6  # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
  7  # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more 
  8  # details. 
  9  # 
 10  # You should have received a copy of the GNU General Public License along with 
 11  # this program; if not, write to the Free Software Foundation, Inc., 51 
 12  # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 
 13  # 
 14  # Copyright Buildbot Team Members 
 15   
 16   
 17  from zope.interface import implements 
 18  from twisted.python import components 
 19  from twisted.spread import pb 
 20  from twisted.web import server 
 21  from twisted.web.resource import Resource 
 22  from twisted.web.error import NoResource 
 23   
 24  from buildbot import interfaces 
 25  from buildbot.status import logfile 
 26  from buildbot.status.web.base import IHTMLLog, HtmlResource, path_to_root 
 27   
28 -class ChunkConsumer:
29 implements(interfaces.IStatusLogConsumer) 30
31 - def __init__(self, original, textlog):
32 self.original = original 33 self.textlog = textlog
34 - def registerProducer(self, producer, streaming):
35 self.producer = producer 36 self.original.registerProducer(producer, streaming)
37 - def unregisterProducer(self):
38 self.original.unregisterProducer()
39 - def writeChunk(self, chunk):
40 formatted = self.textlog.content([chunk]) 41 try: 42 if isinstance(formatted, unicode): 43 formatted = formatted.encode('utf-8') 44 self.original.write(formatted) 45 except pb.DeadReferenceError: 46 self.producing.stopProducing()
47 - def finish(self):
48 self.textlog.finished()
49 50 51 # /builders/$builder/builds/$buildnum/steps/$stepname/logs/$logname
52 -class TextLog(Resource):
53 # a new instance of this Resource is created for each client who views 54 # it, so we can afford to track the request in the Resource. 55 implements(IHTMLLog) 56 57 asText = False 58 subscribed = False 59
60 - def __init__(self, original):
61 Resource.__init__(self) 62 self.original = original
63
64 - def getChild(self, path, req):
65 if path == "text": 66 self.asText = True 67 return self 68 return HtmlResource.getChild(self, path, req)
69
70 - def content(self, entries):
71 html_entries = [] 72 text_data = '' 73 for type, entry in entries: 74 if type >= len(logfile.ChunkTypes) or type < 0: 75 # non-std channel, don't display 76 continue 77 78 is_header = type == logfile.HEADER 79 80 if not self.asText: 81 # jinja only works with unicode, or pure ascii, so assume utf-8 in logs 82 if not isinstance(entry, unicode): 83 entry = unicode(entry, 'utf-8', 'replace') 84 html_entries.append(dict(type = logfile.ChunkTypes[type], 85 text = entry, 86 is_header = is_header)) 87 elif not is_header: 88 text_data += entry 89 90 if self.asText: 91 return text_data 92 else: 93 return self.template.module.chunks(html_entries)
94
95 - def render_HEAD(self, req):
96 self._setContentType(req) 97 98 # vague approximation, ignores markup 99 req.setHeader("content-length", self.original.length) 100 return ''
101
102 - def render_GET(self, req):
103 self._setContentType(req) 104 self.req = req 105 106 if not self.asText: 107 self.template = req.site.buildbot_service.templates.get_template("logs.html") 108 109 data = self.template.module.page_header( 110 pageTitle = "Log File contents", 111 texturl = req.childLink("text"), 112 path_to_root = path_to_root(req)) 113 data = data.encode('utf-8') 114 req.write(data) 115 116 self.original.subscribeConsumer(ChunkConsumer(req, self)) 117 return server.NOT_DONE_YET
118
119 - def _setContentType(self, req):
120 if self.asText: 121 req.setHeader("content-type", "text/plain; charset=utf-8") 122 else: 123 req.setHeader("content-type", "text/html; charset=utf-8")
124
125 - def finished(self):
126 if not self.req: 127 return 128 try: 129 if not self.asText: 130 data = self.template.module.page_footer() 131 data = data.encode('utf-8') 132 self.req.write(data) 133 self.req.finish() 134 except pb.DeadReferenceError: 135 pass 136 # break the cycle, the Request's .notifications list includes the 137 # Deferred (from req.notifyFinish) that's pointing at us. 138 self.req = None 139 140 # release template 141 self.template = None
142 143 components.registerAdapter(TextLog, interfaces.IStatusLog, IHTMLLog) 144 145
146 -class HTMLLog(Resource):
147 implements(IHTMLLog) 148
149 - def __init__(self, original):
150 Resource.__init__(self) 151 self.original = original
152
153 - def render(self, request):
154 request.setHeader("content-type", "text/html") 155 return self.original.html
156 157 components.registerAdapter(HTMLLog, logfile.HTMLLogFile, IHTMLLog) 158 159
160 -class LogsResource(HtmlResource):
161 addSlash = True 162
163 - def __init__(self, step_status):
166
167 - def getChild(self, path, req):
168 for log in self.step_status.getLogs(): 169 if path == log.getName(): 170 if log.hasContents(): 171 return IHTMLLog(interfaces.IStatusLog(log)) 172 return NoResource("Empty Log '%s'" % path) 173 return HtmlResource.getChild(self, path, req)
174