Package buildbot :: Package status :: Package web :: Module logs
[hide private]
[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 self.original.write(formatted) 48 except pb.DeadReferenceError: 49 self.producing.stopProducing()
50 - def finish(self):
51 self.textlog.finished()
52 53 54 # /builders/$builder/builds/$buildnum/steps/$stepname/logs/$logname
55 -class TextLog(Resource):
56 # a new instance of this Resource is created for each client who views 57 # it, so we can afford to track the request in the Resource. 58 implements(IHTMLLog) 59 60 asText = False 61 subscribed = False 62
63 - def __init__(self, original):
64 Resource.__init__(self) 65 self.original = original
66
67 - def getChild(self, path, req):
68 if path == "text": 69 self.asText = True 70 return self 71 return HtmlResource.getChild(self, path, req)
72
73 - def htmlHeader(self, request):
74 title = "Log File contents" 75 data = "<html>\n<head><title>" + title + "</title>\n" 76 data += textlog_stylesheet 77 data += "</head>\n" 78 data += "<body vlink=\"#800080\">\n" 79 texturl = request.childLink("text") 80 data += '<a href="%s">(view as text)</a><br />\n' % texturl 81 data += "<pre>\n" 82 return data
83
84 - def content(self, entries):
85 spanfmt = '<span class="%s">%s</span>' 86 data = "" 87 for type, entry in entries: 88 if type >= len(builder.ChunkTypes) or type < 0: 89 # non-std channel, don't display 90 continue 91 if self.asText: 92 if type != builder.HEADER: 93 data += entry 94 else: 95 data += spanfmt % (builder.ChunkTypes[type], 96 html.escape(entry)) 97 return data
98
99 - def htmlFooter(self):
100 data = "</pre>\n" 101 data += "</body></html>\n" 102 return data
103
104 - def render_HEAD(self, request):
105 if self.asText: 106 request.setHeader("content-type", "text/plain") 107 else: 108 request.setHeader("content-type", "text/html") 109 110 # vague approximation, ignores markup 111 request.setHeader("content-length", self.original.length) 112 return ''
113
114 - def render_GET(self, req):
115 self.req = req 116 117 if self.asText: 118 req.setHeader("content-type", "text/plain") 119 else: 120 req.setHeader("content-type", "text/html") 121 122 if not self.asText: 123 req.write(self.htmlHeader(req)) 124 125 self.original.subscribeConsumer(ChunkConsumer(req, self)) 126 return server.NOT_DONE_YET
127
128 - def finished(self):
129 if not self.req: 130 return 131 try: 132 if not self.asText: 133 self.req.write(self.htmlFooter()) 134 self.req.finish() 135 except pb.DeadReferenceError: 136 pass 137 # break the cycle, the Request's .notifications list includes the 138 # Deferred (from req.notifyFinish) that's pointing at us. 139 self.req = None
140 141 components.registerAdapter(TextLog, interfaces.IStatusLog, IHTMLLog) 142 143
144 -class HTMLLog(Resource):
145 implements(IHTMLLog) 146
147 - def __init__(self, original):
148 Resource.__init__(self) 149 self.original = original
150
151 - def render(self, request):
152 request.setHeader("content-type", "text/html") 153 return self.original.html
154 155 components.registerAdapter(HTMLLog, builder.HTMLLogFile, IHTMLLog) 156 157
158 -class LogsResource(HtmlResource):
159 addSlash = True 160
161 - def __init__(self, step_status):
164
165 - def getChild(self, path, req):
166 for log in self.step_status.getLogs(): 167 if path == log.getName(): 168 if log.hasContents(): 169 return IHTMLLog(interfaces.IStatusLog(log)) 170 return NoResource("Empty Log '%s'" % path) 171 return HtmlResource.getChild(self, path, req)
172