@@ -90,6 +90,8 @@ the string is returned unchanged."
9090(defun content-type (file)
9191 (cond ((equalp (pathname-type file) " css" )
9292 " text/css" )
93+ ((equalp (pathname-type file) " rss" )
94+ " application/rss+xml" )
9395 (t
9496 " text/html" )))
9597
@@ -119,6 +121,12 @@ the string is returned unchanged."
119121(defgeneric system-name (object))
120122(defgeneric system-file-name (object))
121123(defgeneric failure-report-url (object))
124+ (defgeneric full-failure-report-url (object)
125+ (:method (object)
126+ (format nil " http://~A /~A~A "
127+ *failtail-bucket*
128+ (report-prefix)
129+ (failure-report-url object))))
122130(defgeneric failure-report-html-file (base object))
123131
124132(defgeneric stylesheet-path (object))
@@ -186,7 +194,7 @@ the string is returned unchanged."
186194(defclass failing-source ()
187195 ((failure-data
188196 :initarg :failure-data
189- :reader failure-data)
197+ :accessor failure-data)
190198 (source
191199 :initarg :source
192200 :reader source)
@@ -223,7 +231,7 @@ the string is returned unchanged."
223231(defclass failure-report ()
224232 ((failure-data
225233 :initarg :failure-data
226- :reader failure-data)
234+ :accessor failure-data)
227235 (stylesheet-path
228236 :initform " failure-report.css"
229237 :reader stylesheet-path)))
@@ -239,23 +247,70 @@ the string is returned unchanged."
239247(defmethod name ((object failure-report))
240248 " Failure report" )
241249
250+ (defun parse-failure-file-name (failure-file)
251+ " Parse FAILURE-FILE's namestring into a FAILING-SYSTEM object; if no
252+ source is found that matches the filename, return nil."
253+ ; ; Syntax is:
254+ ; ; fail_<project>_<system-file-name>_<failing-system-escaped-name>.txt
255+ (ppcre :register-groups-bind (source-name system-file-name failing-system)
256+ (" ^fail_(.*?)_(.*?)_(.*?)\\ .txt"
257+ (file-namestring failure-file))
258+ ; ; It's possible that the logfile belongs to a source that is no
259+ ; ; longer part of Quicklisp (due to renaming, removal, or whatever
260+ ; ; reason), so don't try to make a failing-source in that case.
261+ (let ((source (find-source source-name)))
262+ (when source
263+ (make-instance ' failing-system
264+ :source (find-source source-name)
265+ :failure-log-file failure-file
266+ :system-file-name system-file-name
267+ :system-name (decode-string-from-filesystem
268+ failing-system))))))
269+
270+ (defun failing-source-log-files ()
271+ (let* ((base (translate-logical-pathname " quicklisp-controller:dist;build-cache;" ))
272+ (fail-wild (merge-pathnames " **/fail_*_*_*.txt" base)))
273+ (directory fail-wild)))
274+
275+ (defun failing-systems ()
276+ (remove nil
277+ (mapcar #' parse-failure-file-name
278+ (failing-source-log-files))))
279+
280+ (defun failure-log-failure-report ()
281+ " Scan the failure log files of all projects to produce a failure report."
282+ (let ((systems (make-hash-table :test ' equal)))
283+ (flet ((fsource (source)
284+ (or (gethash (name source) systems)
285+ (setf (gethash (name source) systems)
286+ (make-instance ' failing-source
287+ :failure-data nil
288+ :source source)))))
289+ (let ((table (make-hash-table :test ' eq))
290+ (systems (failing-systems))
291+ (report (make-instance ' failure-report
292+ :failure-data ' ())))
293+ (dolist (system systems)
294+ (let ((key (fsource (source system))))
295+ (push system (gethash key table))))
296+ (maphash (lambda (failing-source failing-systems)
297+ (setf (failure-data failing-source) failing-systems)
298+ (push failing-source (failure-data report)))
299+ table)
300+ report))))
301+
242302(defmethod failure-data ((object (eql t )))
243- (let ((sources
244- (mapcan (lambda (source)
245- (let ((failing-source
246- (find-failing-source source)))
247- (when failing-source
248- (list failing-source))))
249- (all-of-type t ))))
250- (make-instance ' failure-report
251- :failure-data (sort sources
252- #' string<
253- :key #' name))))
303+ (failure-log-failure-report))
304+
305+ (defparameter *log-lines-that-are-boring*
306+ (mapcar ' ppcre:create-scanner
307+ ' (" ^WARNING:" )))
254308
255309(defparameter *log-lines-to-highlight*
256310 (mapcar ' ppcre:create-scanner
257311 ' (" ^; caught (WARNING|ERROR):"
258312 " READ error during"
313+ " ^Backtrace for"
259314 " ^Unhandled" )))
260315
261316(defparameter *failure-log-reconstitution-patterns*
@@ -267,21 +322,12 @@ the string is returned unchanged."
267322
268323(defun highlighted-log-line-p (line)
269324 (loop for scanner in *log-lines-to-highlight*
270- thereis (ppcre :scan scanner line)))
325+ thereis (ppcre :scan scanner line)))
326+
327+ (defun boring-log-line-p (line)
328+ (loop for scanner in *log-lines-that-are-boring*
329+ thereis (ppcre :scan scanner line)))
271330
272- (defun highlight-log-lines (input-stream output-stream)
273- (loop for line = (read-line input-stream nil )
274- while line
275- do
276- (setf line (cl-who :escape-string line))
277- (setf line (failure-log-reconstitute-line line))
278- (if (highlighted-log-line-p line)
279- (progn
280- (write-string " <strong>" output-stream)
281- (write-string line output-stream)
282- (write-string " </strong>" output-stream))
283- (write-string line output-stream))
284- (terpri output-stream)))
285331
286332(defmethod write-html-failure-report-header (object stream )
287333 (format stream " <html><head><title>~A </title>~
@@ -317,14 +363,16 @@ the string is returned unchanged."
317363 (loop for line = (read-line log-stream nil )
318364 while line
319365 do
320- (setf line (cl-who :escape-string line))
321- (if (highlighted-log-line-p line)
322- (progn
323- (write-string " <strong>" stream )
324- (write-string line stream )
325- (write-string " </strong>" stream ))
326- (write-string line stream ))
327- (terpri stream )))
366+ (setf line (cl-who :escape-string line))
367+ (cond ((highlighted-log-line-p line)
368+ (write-string " <strong>" stream )
369+ (write-string line stream )
370+ (write-string " </strong>" stream )
371+ (terpri stream ))
372+ ((boring-log-line-p line)
373+ (format stream " <span class='boring'>~A~% </span>" line))
374+ (t
375+ (write-line line stream )))))
328376 (format stream " </pre>" )
329377 (format stream " </div>~% " ))
330378
0 commit comments