PageRenderTime 48ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/src/error.lisp

http://github.com/mtravers/wuwei
Lisp | 159 lines | 94 code | 21 blank | 44 comment | 1 complexity | de26b8d76dda3a5d809f6ad8caad2e36 MD5 | raw file
  1. (in-package :wu)
  2. ;;; +=========================================================================+
  3. ;;; | Copyright (c) 2009, 2010 Mike Travers and CollabRx, Inc |
  4. ;;; | |
  5. ;;; | Released under the MIT Open Source License |
  6. ;;; | http://www.opensource.org/licenses/mit-license.php |
  7. ;;; | |
  8. ;;; | Permission is hereby granted, free of charge, to any person obtaining |
  9. ;;; | a copy of this software and associated documentation files (the |
  10. ;;; | "Software"), to deal in the Software without restriction, including |
  11. ;;; | without limitation the rights to use, copy, modify, merge, publish, |
  12. ;;; | distribute, sublicense, and/or sell copies of the Software, and to |
  13. ;;; | permit persons to whom the Software is furnished to do so, subject to |
  14. ;;; | the following conditions: |
  15. ;;; | |
  16. ;;; | The above copyright notice and this permission notice shall be included |
  17. ;;; | in all copies or substantial portions of the Software. |
  18. ;;; | |
  19. ;;; | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
  20. ;;; | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
  21. ;;; | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
  22. ;;; | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
  23. ;;; | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
  24. ;;; | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
  25. ;;; | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
  26. ;;; +=========================================================================+
  27. ;;; Author: Mike Travers and David Sobeck
  28. (export '(error-box render-error clear-error
  29. with-html-error-handling
  30. with-json-error-handling
  31. with-html-safe-error-handling
  32. with-ajax-error-handler
  33. ))
  34. (defun system-info ()
  35. "Replace with commands to get your system version info, eg by running 'hg log -l 1' in a shell")
  36. (defun report-bug-button (&optional (info ""))
  37. (html
  38. ((:a :href (format nil "~a?description=~A" *bug-report-url* (uriencode-string (format nil "In ~A:~%~%~a" (system-info) info)))
  39. :target "error") "Report a bug")))
  40. ;;; Insert an error box for use by the error handler (++ should have a clear button)
  41. (defun error-box ()
  42. (html ((:div :id "error_box" :style "display:none;")))) ;invisible until replaced
  43. ;;; This isn't called anywhere (and should :update and set invisible rather than :replace)
  44. (defun clear-error ()
  45. (render-update
  46. (:replace "error_box" (html ((:div :id "error_box"))))))
  47. ;;; This isn't called anywhere (and should :update rather than :replace)
  48. (defun render-error (msg &key stack-trace user-error?)
  49. (render-update
  50. (:replace "error_box"
  51. (html
  52. ((:div :class (if user-error? "uerror" "error") :id "error_box") ;!!! have to keep this id or later errors won't work
  53. (:princ-safe msg)
  54. (unless user-error?
  55. (html
  56. (report-bug-button stack-trace)
  57. ((:a :onclick "toggle_visibility('error_box_stack_trace');") " Show stack ")
  58. ((:div :id "error_box_stack_trace" :style "display:none;") ;:class "error"
  59. (:pre
  60. (:princ-safe stack-trace))
  61. ))))))))
  62. ;;; Set to T to use the error box rather than alert method.
  63. (def-session-variable *ajax-error-box?* nil)
  64. ;;; ++ needs better name: this composes, logs, and sends it back to client
  65. (defun compose-error-message (path &key error stack-trace extra-js)
  66. (let ((message (format nil "Lisp error while servicing ~a: ~A~:[~;~a~]" path error *developer-mode* stack-trace)))
  67. (log-message message)
  68. ;;; This doesn't work; the header is already generated and sent.
  69. ;(setf (request-reply-code *ajax-request*) 400)
  70. (if *multipart-request*
  71. (html
  72. (:princ (json:encode-json-to-string `((failure . true)
  73. ;;(success . false)
  74. (records ((data . ,(clean-upload-js-string message))))))))
  75. (let ((estring (princ-to-string error)))
  76. (if *ajax-error-box?*
  77. (render-update
  78. (:update "error_box" (:princ-safe estring))
  79. (:show "error_box"))
  80. ;; alertbox method
  81. (render-update
  82. (:alert (clean-js-string estring))))
  83. (when extra-js
  84. (render-update
  85. (:js extra-js)))
  86. ))))
  87. ;; --> conditionalize to use html or javascript, depending on context.
  88. ;; Scrub the string more vigorously!
  89. (defun html-report-error (&key error stack-trace)
  90. ;; Log this?
  91. (log-message (format nil "~%Unhandled exception caught by with-html-error-handling: ~a~%~a~%" error stack-trace))
  92. (html
  93. ((:div :class "error")
  94. (:b
  95. (:princ-safe (string+ "Error: " (princ-to-string error))
  96. ))
  97. (if (and stack-trace *developer-mode*)
  98. (html
  99. (:pre
  100. (:princ-safe stack-trace))
  101. )
  102. )
  103. )
  104. ))
  105. (defun create-block-for-error (&key error stack-trace)
  106. (html-report-error :error error :stack-trace stack-trace)
  107. (write-string (html-string
  108. (html-report-error :error error))))
  109. ;;; Another method: do all generation to a string; if an error occurs catch it and make a error block instead
  110. (defmacro with-html-safe-error-handling (&body body)
  111. `(without-unwinding-restart (create-block-for-error)
  112. (write-string (html-string ,@body) *html-stream*)))
  113. (defmacro with-ajax-error-handler ((name &key extra-js) &body body)
  114. `(without-unwinding-restart (compose-error-message ,name :extra-js ,extra-js)
  115. ,@body
  116. ))
  117. (defun json-report-error (&key error stack-trace)
  118. (log-message (format nil "~%Unhandled exception caught by with-html-error-handling: ~a~%~a~%" error stack-trace))
  119. (html
  120. (:princ (json:encode-json-to-string `((failure . true)
  121. (success . false)
  122. (message . ,(format nil "~A" error)))))))
  123. (defmacro with-json-error-handling (&body body)
  124. `(without-unwinding-restart (json-report-error)
  125. ,@body))
  126. ;;; Note: has to be inside of with-http-response-and-body or equivalent
  127. ;;; unfortunately this means that errors can't cause a 404 or 500 or whatever HTTP response like they should +++ rethinking needed
  128. ;;; If you want to close off html elements in case of an error, I think you need to add unwind-protects to html-body-key-form
  129. ;;; in /misc/downloads/cl-portable-aserve-1.2.42/aserve/htmlgen/htmlgen.cl
  130. ;;; get-frames-list for a backtrace (but probably need a different kind of handler in that case)
  131. (defmacro with-html-error-handling (&body body)
  132. `(without-unwinding-restart (html-report-error)
  133. ,@body))
  134. (defvar *logging* t)
  135. (defvar *logging-stream* *standard-output*)
  136. (defun log-message (message)
  137. (if *logging*
  138. (format *logging-stream* "~a ~a~%" (net.aserve::universal-time-to-date (get-universal-time)) message)))