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

Source Code for Module buildbot.status.web.logs

  1   
  2  from zope.interface import implements 
  3  from twisted.python import components 
  4  from twisted.spread import pb 
  5  from twisted.web import html, server 
  6  from twisted.web.resource import Resource 
  7  from twisted.web.error import NoResource 
  8   
  9  from buildbot import interfaces 
 10  from buildbot.status import builder 
 11  from buildbot.status.web.base import IHTMLLog, HtmlResource 
 12   
 13   
 14  textlog_stylesheet = """ 
 15  <style type="text/css"> 
 16   div.data { 
 17    font-family: "Courier New", courier, monotype; 
 18   } 
 19   span.stdout { 
 20    font-family: "Courier New", courier, monotype; 
 21   } 
 22   span.stderr { 
 23    font-family: "Courier New", courier, monotype; 
 24    color: red; 
 25   } 
 26   span.header { 
 27    font-family: "Courier New", courier, monotype; 
 28    color: blue; 
 29   } 
 30  </style> 
 31  """ 
 32   
33 -class ChunkConsumer:
34 implements(interfaces.IStatusLogConsumer) 35
36 - def __init__(self, original, textlog):
37 self.original = original 38 self.textlog = textlog
39 - def registerProducer(self, producer, streaming):
40 self.producer = producer 41 self.original.registerProducer(producer, streaming)
42 - def unregisterProducer(self):
43 self.original.unregisterProducer()
44 - def writeChunk(self, chunk):
45 formatted = self.textlog.content([chunk]) 46 try: 47 if isinstance(formatted, unicode): 48 formatted = formatted.encode('utf-8') 49 self.original.write(formatted) 50 except pb.DeadReferenceError: 51 self.producing.stopProducing()
52 - def finish(self):
53 self.textlog.finished()
54 55 56 # /builders/$builder/builds/$buildnum/steps/$stepname/logs/$logname
57 -class TextLog(Resource):
58 # a new instance of this Resource is created for each client who views 59 # it, so we can afford to track the request in the Resource. 60 implements(IHTMLLog) 61 62 asText = False 63 subscribed = False 64
65 - def __init__(self, original):
66 Resource.__init__(self) 67 self.original = original
68
69 - def getChild(self, path, req):
70 if path == "text": 71 self.asText = True 72 return self 73 return HtmlResource.getChild(self, path, req)
74
75 - def htmlHeader(self, request):
76 title = "Log File contents" 77 data = "<html>\n<head><title>" + title + "</title>\n" 78 data += textlog_stylesheet 79 data += "</head>\n" 80 data += "<body vlink=\"#800080\">\n" 81 texturl = request.childLink("text") 82 data += '<a href="%s">(view as text)</a><br />\n' % texturl 83 data += "<pre>\n" 84 return data
85
86 - def content(self, entries):
87 spanfmt = '<span class="%s">%s</span>' 88 data = "" 89 for type, entry in entries: 90 if type >= len(builder.ChunkTypes) or type < 0: 91 # non-std channel, don't display 92 continue 93 if self.asText: 94 if type != builder.HEADER: 95 data += entry 96 else: 97 data += spanfmt % (builder.ChunkTypes[type], 98 html.escape(entry)) 99 return data
100
101 - def htmlFooter(self):
102 data = "</pre>\n" 103 data += "</body></html>\n" 104 return data
105
106 - def render_HEAD(self, request):
107 if self.asText: 108 request.setHeader("content-type", "text/plain") 109 else: 110 request.setHeader("content-type", "text/html") 111 112 # vague approximation, ignores markup 113 request.setHeader("content-length", self.original.length) 114 return ''
115
116 - def render_GET(self, req):
117 self.req = req 118 119 if self.asText: 120 req.setHeader("content-type", "text/plain") 121 else: 122 req.setHeader("content-type", "text/html") 123 124 if not self.asText: 125 req.write(self.htmlHeader(req)) 126 127 self.original.subscribeConsumer(ChunkConsumer(req, self)) 128 return server.NOT_DONE_YET
129
130 - def finished(self):
131 if not self.req: 132 return 133 try: 134 if not self.asText: 135 self.req.write(self.htmlFooter()) 136 self.req.finish() 137 except pb.DeadReferenceError: 138 pass 139 # break the cycle, the Request's .notifications list includes the 140 # Deferred (from req.notifyFinish) that's pointing at us. 141 self.req = 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, builder.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