Javascript plugin to beautify exceptions on the backend event log page (#2035)

This commit is contained in:
Jérémy Gaulin 2016-05-23 22:03:31 +02:00 committed by Samuel Georges
parent c72670e450
commit 38ec5d109a
7 changed files with 436 additions and 0 deletions

View File

@ -0,0 +1,83 @@
[data-plugin="exception-beautifier"] {
width: auto !important;
}
.markup_exception_container {
overflow-x: scroll;
}
.markup_exception_name {
display: block;
font-weight: bold;
font-size: 1.2em;
}
.markup_exception_message {
display: block;
padding: 15px 0;
font-size: 1.1em;
}
.markup_exception_stacktrace_title {
display: block;
margin-top: 15px;
font-weight: bold;
}
.markup_exception_stacktrace_line {
display: block;
white-space: nowrap;
padding: 8px 0;
}
.markup_exception_stacktrace_line:nth-child(even) {
background-color: #ffffff;
}
.markup_exception_stacktrace_line:nth-child(odd) {
background-color: #f2f2f2;
}
.markup_exception_stacktrace_line_number {
display: inline-block;
width: 40px;
margin-right: 15px;
text-align: right;
color: black;
}
.markup_exception_stacktrace_line_internal {
color: #2C3E50;
}
.markup_exception_stacktrace_line_function {
display: block;
margin-left: 55px;
}
.markup_exception_file {
color: #0c7eff;
}
.markup_exception_line_number {
color: #af0016;
}
.markup_exception_class {
font-weight: bold;
font-family: monospace;
}
.markup_exception_function {
font-weight: bold;
color: #4d9a98;
font-family: monospace;
}
.markup_exception_function.--system {
color: #3d7a78;
}
.markup_exception_sign {
color: #4d9a98;
}

View File

@ -0,0 +1,208 @@
/*
* Previews class
*/
+function ($) {
"use strict";
var ExceptionBeautifier = function (el, options) {
var self = this
self.$el = $(el)
self.options = options || {}
// Init
self.init()
}
ExceptionBeautifier.DEFAULTS = {
editor: 'phpstorm'
}
ExceptionBeautifier.REGEX = {
phpLine: /^(#[0-9]+)\s+(.+\.php)(?:\(([0-9]+)\))?\s*:(.*)/,
internalLine: /^(#[0-9]+)\s+(\[internal function\]\s*:)(.*)/,
defaultLine: /^(#[0-9]+)\s*(.*)/,
className: /([a-z0-9]+\\[a-z0-9\\]+)/gi,
filePath: /((?:[A-Z]:[/\\]|\/)?[\w/\\]+\.php)(\(([0-9]+)\)|:([0-9]+))?/gi,
functionCall: /->([^(]+)\((.*)\)/i
}
ExceptionBeautifier.extensions = []
ExceptionBeautifier.prototype.init = function () {
var self = this
ExceptionBeautifier.extensions.forEach(function (extension) {
extension.onInit(self);
})
self.$el.html(self.parseSource())
}
ExceptionBeautifier.prototype.parseSource = function () {
var self = this,
source = self.$el.text(),
markup = {lines: []},
start = 0,
end,
matches
// We only parse stack trace message. Just transform newline to <br> and format spaces for standard messages.
if (source.indexOf('Stack trace:') < 0) {
return '<div class="form-control">' + self.formatMessage(source) + '</div>';
}
end = source.indexOf(':', start) + 1
markup.name = source.substring(start, end)
start = end
end = source.indexOf('Stack trace:', start)
markup.message = source.substring(start, end)
start = source.indexOf('#', end)
while ((end = source.indexOf('#', start + 1)) > 0) {
markup.lines.push(self.parseLine(source.substring(start, end)))
start = end
}
markup.lines.push(self.parseLine(source.substring(start)))
source = '<div class="form-control">' +
'<span class="markup_exception_name">' + markup.name + '</span>' +
'<span class="markup_exception_message">' + self.formatMessage(markup.message) + '</span>' +
'</div>' +
'<span class="markup_exception_stacktrace_title">Stack trace:</span>'
markup.lines.forEach(function (line) {
source += '<span class="markup_exception_stacktrace_line">' + self.formatLine(line) + '</span>'
})
ExceptionBeautifier.extensions.forEach(function (extension) {
extension.onParse(self);
})
return '<div class="markup_exception_container">' + source + '</div>'
}
ExceptionBeautifier.prototype.parseLine = function (str) {
var self = this,
line = {},
matches
if (matches = str.match(ExceptionBeautifier.REGEX.phpLine)) {
line.type = 'phpLine'
line.number = $.trim(matches[1])
line.file = $.trim(matches[2])
line.lineNumber = $.trim(matches[3])
line.function = $.trim(matches[4])
}
else if (matches = str.match(ExceptionBeautifier.REGEX.internalLine)) {
line.type = 'internal'
line.number = $.trim(matches[1])
line.internal = $.trim(matches[2])
line.function = $.trim(matches[3])
} else if (matches = str.match(ExceptionBeautifier.REGEX.defaultLine)){
line.type = 'default'
line.number = $.trim(matches[1])
line.function = $.trim(matches[2])
}
return line
}
ExceptionBeautifier.prototype.formatLine = function (line) {
var self = this
if (line.function) {
line.function = self.formatClasses(line.function)
}
switch (line.type) {
case 'phpLine':
return '<span class="markup_exception_stacktrace_line_number">' + line.number + '</span>' +
self.formatFilePath(line.file, line.lineNumber) +
'<span class="markup_exception_line_number">(' + line.lineNumber + ')</span>: ' +
'<span class="markup_exception_stacktrace_line_function">' + line.function + '</span>'
case 'internal':
return '<span class="markup_exception_stacktrace_line_number">' + line.number + '</span>' +
'<span class="markup_exception_stacktrace_line_internal">' + line.internal + '</span>' +
'<span class="markup_exception_stacktrace_line_function">' + line.function + '</span>'
case 'default':
return '<span class="markup_exception_stacktrace_line_number">' + line.number + '</span>' +
'<span class="markup_exception_stacktrace_line_function">' + line.function + '</span>'
}
return ''
}
ExceptionBeautifier.prototype.formatClasses = function (str) {
var self = this
str = str.replace(ExceptionBeautifier.REGEX.className, '<span class="markup_exception_class">$1</span>')
str = str.replace(ExceptionBeautifier.REGEX.filePath, self.formatFilePath('$1', '$3$4') +
'<span class="markup_exception_line_number">$2</span>')
if (str.match(/^\s*(call_user_func|spl_autoload_call)/)) {
str = str.replace(/^\s*(?:call_user_func|spl_autoload_call)([^(]*)\((.*)\)/,
'<span class="markup_exception_function --system">call_user_func$1</span>' +
'<span class="markup_exception_sign">(</span>' +
'$2' +
'<span class="markup_exception_sign">)</span>')
} else {
str = str.replace(ExceptionBeautifier.REGEX.functionCall,
'<span class="markup_exception_sign">-></span>' +
'<span class="markup_exception_function">$1</span>' +
'<span class="markup_exception_sign">(</span>' +
'$2' +
'<span class="markup_exception_sign">)</span>')
}
return str
}
ExceptionBeautifier.prototype.formatFilePath = function (path, line) {
var self = this
return '<span class="markup_exception_file"">' + path + '</span>'
}
ExceptionBeautifier.prototype.formatMessage = function (str) {
var self = this
return self.formatClasses(str)
.replace(/^\s+/, '')
.replace(/\r|\n|\r\r/g, '<br>')
.replace(/\r| {4}/g, '&nbsp;&nbsp;&nbsp;&nbsp;')
}
// EXCEPTION BEAUTIFIER PLUGIN DEFINITION
// ============================
$.fn.exceptionBeautifier = function (option) {
var args = arguments,
result
this.each(function () {
var $this = $(this)
var data = $this.data('oc.exceptionBeautifier')
var options = $.extend({}, ExceptionBeautifier.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('oc.exceptionBeautifier', (data = new ExceptionBeautifier(this, options)))
if (typeof option == 'string') result = data[option].call($this)
if (typeof result != 'undefined') return false
})
return result ? result : this
}
$.fn.exceptionBeautifier.Constructor = ExceptionBeautifier
$(document).render(function () {
$('[data-plugin="exception-beautifier"]').exceptionBeautifier()
})
}(window.jQuery)

View File

@ -0,0 +1,109 @@
/*
* Previews class
*/
+function ($) {
"use strict";
var ExceptionBeautifier = $.fn.exceptionBeautifier.Constructor;
ExceptionBeautifier.EDITORS = {
subl: {scheme: 'subl://open?url=file://%file&line=%line', name: 'Sublime (subl://)'},
txmt: {scheme: 'txmt://open/?url=file://%file&line=%line', name: 'TextMate (txmt://)'},
mvim: {scheme: 'mvim://open/?url=file://%file&line=%line', name: 'MacVim (mvim://)'},
phpstorm: {scheme: 'phpstorm://open?file=%file&line=%line', name: 'PhpStorm (phpstorm://)'},
editor: {scheme: 'editor://open/?file=%file&line=%line', name: 'Custom (editor://)'}
}
ExceptionBeautifier.REGEX.editor = /idelink:\/\/([^#]+)&([0-9]+)?/
ExceptionBeautifier.extensions.push({
onInit: function (exceptionBeautfier) {
exceptionBeautfier.initEditorPopup();
},
onParse: function (exceptionBeautfier) {
exceptionBeautfier.$el.on('click', 'a', function () {
exceptionBeautfier.openWithEditor($(this).data('href'));
})
}
})
ExceptionBeautifier.prototype.initEditorPopup = function () {
var self = this,
title = $.oc.lang.get('eventlog.editor.title'),
description = $.oc.lang.get('eventlog.editor.description'),
openWith = $.oc.lang.get('eventlog.editor.openWith'),
saveRememberClose = $.oc.lang.get('eventlog.editor.openAndRemember'),
openClose = $.oc.lang.get('eventlog.editor.open')
self.$el.parent().append(' \
<div class="control-popup modal fade" id="exception-link-editor" tabindex="-1" role="dialog"> \
<div class="modal-dialog"><div class="modal-content"><div class="modal-header"> \
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> \
<h4 class="modal-title">' + title + '</h4> \
</div> \
<div class="modal-body"><p>' + description + '</p> \
<div class="form-group"> \
<label class="control-label">' + openWith + ':</label> \
<select class="form-control" name="select-exception-link-editor"></select> \
</div> \
</div> \
<div class="modal-footer"> \
<button type="button" class="btn btn-default" data-trigger="editor-open-save" \
data-dismiss="modal">' + saveRememberClose + '</button> \
<button type="button" class="btn btn-primary" data-trigger="editor-open-close" \
data-dismiss="modal">' + openClose + '</button> \
</div></div></div></div>')
}
ExceptionBeautifier.prototype.openWithEditor = function (link) {
var self = this,
matches,
open = function (value) {
window.open(link.replace(
ExceptionBeautifier.REGEX.editor,
ExceptionBeautifier.EDITORS[value].scheme
.replace(/%file/, matches[1])
.replace(/%line/, matches[2])
), '_self')
}
if (matches = link.match(ExceptionBeautifier.REGEX.editor)) {
if (window.sessionStorage && window.sessionStorage['oc-exception-editor']) {
open(window.sessionStorage['oc-exception-editor']);
} else {
$('#exception-link-editor')
.popup({content: $('#exception-link-editor').html()})
.on('shown.oc.popup', function (event, source, popup) {
var $select = $('select', popup).empty()
if (!window.sessionStorage) {
$('[data-trigger="editor-open-save"]', popup).hide();
}
$('[data-trigger="editor-open-close"]', popup).on('click', function () {
open($select.val());
})
$('[data-trigger="editor-open-save"]', popup).on('click', function () {
window.sessionStorage['oc-exception-editor'] = $select.val();
open($select.val());
})
for (var key in ExceptionBeautifier.EDITORS) {
$select.append('<option value="' + key + '">' + ExceptionBeautifier.EDITORS[key].name + '</option>')
}
})
.on('hide.oc.popup', function (event, source, popup) {
$('[data-trigger]', popup).off('click')
})
}
}
}
ExceptionBeautifier.prototype.formatFilePath = function (path, line) {
return '<a class="markup_exception_file" href="javascript:" data-href="idelink://' + path + '&' + line + '">' + path + '</a>'
}
}(window.jQuery)

View File

@ -1,5 +1,6 @@
<?php namespace System\Controllers;
use App;
use Str;
use Lang;
use File;
@ -69,4 +70,17 @@ class EventLogs extends Controller
return $this->listRefresh();
}
public function preview($id)
{
$this->addCss('/modules/system/assets/css/eventlogs/preview.css', 'core');
$this->addJs('/modules/system/assets/js/eventlogs/preview.js', 'core');
if (in_array(App::environment(), ['dev', 'local'])) {
$this->addJs('/modules/system/assets/js/eventlogs/preview.links.js', 'core');
}
return $this->asExtension('FormController')->preview($id);
}
}

View File

@ -72,6 +72,16 @@ return [
'before_placeholder' => 'Before'
]
],
'eventlog' => [
'editor' => [
'title' => 'Select the source code editor to use',
'description' => 'Your environnement must be configured to listen to one of these URL schemes.',
'openWith' => 'Open with',
'openAndRemember' => 'Open & Remember for this session',
'open' => 'Open'
]
]
];

View File

@ -75,6 +75,16 @@ return [
'before_placeholder' => 'Avant le',
]
],
'eventlog' => [
'editor' => [
'title' => 'Sélectionnez léditeur de code source à utiliser',
'description' => 'Votre environnement doit être configuré pour ouvrir lun des schéma dURL ci-dessous.',
'openWith' => 'Ouvrir avec',
'openAndRemember' => 'Ouvrir et enregistrer pour la session',
'open' => 'Ouvrir'
]
]
];

View File

@ -6,3 +6,5 @@ fields:
message:
type: textarea
containerAttributes:
data-plugin: exception-beautifier