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, NoResource 
 22   
 23  from buildbot import interfaces 
 24  from buildbot.status import logfile 
 25  from buildbot.status.web.base import IHTMLLog, HtmlResource, path_to_root 
 26   
27 -class ChunkConsumer:
28 implements(interfaces.IStatusLogConsumer) 29
30 - def __init__(self, original, textlog):
31 self.original = original 32 self.textlog = textlog
33 - def registerProducer(self, producer, streaming):
34 self.producer = producer 35 self.original.registerProducer(producer, streaming)
36 - def unregisterProducer(self):
37 self.original.unregisterProducer()
38 - def writeChunk(self, chunk):
39 formatted = self.textlog.content([chunk]) 40 try: 41 if isinstance(formatted, unicode): 42 formatted = formatted.encode('utf-8') 43 self.original.write(formatted) 44 except pb.DeadReferenceError: 45 self.producing.stopProducing()
46 - def finish(self):
47 self.textlog.finished()
48 49 50 # /builders/$builder/builds/$buildnum/steps/$stepname/logs/$logname
51 -class TextLog(Resource):
52 # a new instance of this Resource is created for each client who views 53 # it, so we can afford to track the request in the Resource. 54 implements(IHTMLLog) 55 56 asText = False 57 subscribed = False 58
59 - def __init__(self, original):
60 Resource.__init__(self) 61 self.original = original
62
63 - def getChild(self, path, req):
64 if path == "text": 65 self.asText = True 66 return self 67 return Resource.getChild(self, path, req)
68
69 - def content(self, entries):
70 html_entries = [] 71 text_data = '' 72 for type, entry in entries: 73 if type >= len(logfile.ChunkTypes) or type < 0: 74 # non-std channel, don't display 75 continue 76 77 is_header = type == logfile.HEADER 78 79 if not self.asText: 80 # jinja only works with unicode, or pure ascii, so assume utf-8 in logs 81 if not isinstance(entry, unicode): 82 entry = unicode(entry, 'utf-8', 'replace') 83 html_entries.append(dict(type = logfile.ChunkTypes[type], 84 text = entry, 85 is_header = is_header)) 86 elif not is_header: 87 text_data += entry 88 89 if self.asText: 90 return text_data 91 else: 92 return self.template.module.chunks(html_entries)
93
94 - def render_HEAD(self, req):
95 self._setContentType(req) 96 97 # vague approximation, ignores markup 98 req.setHeader("content-length", self.original.length) 99 return ''
100
101 - def render_GET(self, req):
102 self._setContentType(req) 103 self.req = req 104 105 if (self.original.isFinished()): 106 req.setHeader("Cache-Control", "max-age=604800") 107 else: 108 req.setHeader("Cache-Control", "no-cache") 109 110 if not self.asText: 111 self.template = req.site.buildbot_service.templates.get_template("logs.html") 112 113 data = self.template.module.page_header( 114 pageTitle = "Log File contents", 115 texturl = req.childLink("text"), 116 path_to_root = path_to_root(req)) 117 data = data.encode('utf-8') 118 req.write(data) 119 120 self.original.subscribeConsumer(ChunkConsumer(req, self)) 121 return server.NOT_DONE_YET
122
123 - def _setContentType(self, req):
124 if self.asText: 125 req.setHeader("content-type", "text/plain; charset=utf-8") 126 else: 127 req.setHeader("content-type", "text/html; charset=utf-8")
128
129 - def finished(self):
130 if not self.req: 131 return 132 try: 133 if not self.asText: 134 data = self.template.module.page_footer() 135 data = data.encode('utf-8') 136 self.req.write(data) 137 self.req.finish() 138 except pb.DeadReferenceError: 139 pass 140 # break the cycle, the Request's .notifications list includes the 141 # Deferred (from req.notifyFinish) that's pointing at us. 142 self.req = None 143 144 # release template 145 self.template = None
146 147 components.registerAdapter(TextLog, interfaces.IStatusLog, IHTMLLog) 148 149
150 -class HTMLLog(Resource):
151 implements(IHTMLLog) 152
153 - def __init__(self, original):
154 Resource.__init__(self) 155 self.original = original
156
157 - def render(self, request):
158 request.setHeader("content-type", "text/html") 159 return self.original.html
160 161 components.registerAdapter(HTMLLog, logfile.HTMLLogFile, IHTMLLog) 162 163
164 -class LogsResource(HtmlResource):
165 addSlash = True 166
167 - def __init__(self, step_status):
170
171 - def getChild(self, path, req):
172 for log in self.step_status.getLogs(): 173 if path == log.getName(): 174 if log.hasContents(): 175 return IHTMLLog(interfaces.IStatusLog(log)) 176 return NoResource("Empty Log '%s'" % path) 177 return HtmlResource.getChild(self, path, req)
178