Fixed FELIX-3315 Log plugin does not show the bundle that has logged the event
https://issues.apache.org/jira/browse/FELIX-3315
Fixed FELIX-3316 Log plugin should provide more detailed exception column
https://issues.apache.org/jira/browse/FELIX-3316
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1235728 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/LogServlet.java b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/LogServlet.java
index db2c660..d0fb2b5 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/LogServlet.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/internal/compendium/LogServlet.java
@@ -17,7 +17,9 @@
package org.apache.felix.webconsole.internal.compendium;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.Enumeration;
@@ -30,6 +32,8 @@
import org.apache.felix.webconsole.internal.OsgiManagerPlugin;
import org.json.JSONException;
import org.json.JSONWriter;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.service.log.LogEntry;
import org.osgi.service.log.LogReaderService;
@@ -41,9 +45,9 @@
*/
public class LogServlet extends SimpleWebConsolePlugin implements OsgiManagerPlugin
{
- private static final String LABEL = "logs";
- private static final String TITLE = "%logs.pluginTitle";
- private static final String CSS[] = { "/res/ui/logs.css" };
+ private static final String LABEL = "logs"; //$NON-NLS-1$
+ private static final String TITLE = "%logs.pluginTitle"; //$NON-NLS-1$
+ private static final String CSS[] = { "/res/ui/logs.css" }; //$NON-NLS-1$
private final static int MAX_LOGS = 200; //maximum number of log entries
@@ -56,7 +60,7 @@
super(LABEL, TITLE, CSS);
// load templates
- TEMPLATE = readTemplateFile( "/templates/logs.html" );
+ TEMPLATE = readTemplateFile( "/templates/logs.html" ); //$NON-NLS-1$
}
@@ -65,16 +69,21 @@
*/
protected void doPost( HttpServletRequest req, HttpServletResponse resp ) throws IOException
{
- final int minLevel = WebConsoleUtil.getParameterInt( req, "minLevel", LogService.LOG_DEBUG );
+ final int minLevel = WebConsoleUtil.getParameterInt( req, "minLevel", LogService.LOG_DEBUG ); //$NON-NLS-1$
- resp.setContentType( "application/json" );
- resp.setCharacterEncoding( "utf-8" );
-
- renderJSON( resp.getWriter(), minLevel );
+ resp.setContentType( "application/json" ); //$NON-NLS-1$
+ resp.setCharacterEncoding( "utf-8" ); //$NON-NLS-1$
+
+ renderJSON( resp.getWriter(), minLevel, trasesEnabled(req) );
+ }
+
+ private static boolean trasesEnabled( final HttpServletRequest req )
+ {
+ String traces = req.getParameter("traces"); //$NON-NLS-1$
+ return null == traces ? false : Boolean.parseBoolean(traces);
}
-
- private final void renderJSON( final PrintWriter pw, int minLogLevel ) throws IOException
+ private final void renderJSON( final PrintWriter pw, int minLogLevel, boolean traces ) throws IOException
{
// create status line
final LogReaderService logReaderService = ( LogReaderService ) this.getService( LogReaderService.class
@@ -85,10 +94,10 @@
{
jw.object();
- jw.key( "status" );
+ jw.key( "status" ); //$NON-NLS-1$
jw.value( logReaderService == null ? Boolean.FALSE : Boolean.TRUE );
- jw.key( "data" );
+ jw.key( "data" ); //$NON-NLS-1$
jw.array();
if ( logReaderService != null )
@@ -100,7 +109,7 @@
LogEntry nextLog = ( LogEntry ) logEntries.nextElement();
if ( nextLog.getLevel() <= minLogLevel )
{
- logJson( jw, nextLog, index++ );
+ logJson( jw, nextLog, index++, traces );
}
}
}
@@ -124,15 +133,15 @@
protected void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException,
IOException
{
- final int minLevel = WebConsoleUtil.getParameterInt( request, "minLevel", LogService.LOG_DEBUG );
+ final int minLevel = WebConsoleUtil.getParameterInt( request, "minLevel", LogService.LOG_DEBUG ); //$NON-NLS-1$
final String info = request.getPathInfo();
- if ( info.endsWith( ".json" ) )
+ if ( info.endsWith( ".json" ) ) //$NON-NLS-1$
{
- response.setContentType( "application/json" );
- response.setCharacterEncoding( "UTF-8" );
+ response.setContentType( "application/json" ); //$NON-NLS-1$
+ response.setCharacterEncoding( "UTF-8" ); //$NON-NLS-1$
PrintWriter pw = response.getWriter();
- this.renderJSON( pw, minLevel );
+ this.renderJSON( pw, minLevel, trasesEnabled(request) );
return;
}
super.doGet( request, response );
@@ -148,23 +157,40 @@
}
- private static final void logJson( JSONWriter jw, LogEntry info, int index ) throws JSONException
+ private static final void logJson( JSONWriter jw, LogEntry info, int index, boolean traces ) throws JSONException
{
jw.object();
- jw.key( "id" );
+ jw.key( "id" ); //$NON-NLS-1$
jw.value( String.valueOf( index ) );
- jw.key( "received" );
+ jw.key( "received" ); //$NON-NLS-1$
jw.value( info.getTime() );
- jw.key( "level" );
+ jw.key( "level" ); //$NON-NLS-1$
jw.value( logLevel( info.getLevel() ) );
- jw.key( "raw_level" );
+ jw.key( "raw_level" ); //$NON-NLS-1$
jw.value( info.getLevel() );
- jw.key( "message" );
+ jw.key( "message" ); //$NON-NLS-1$
jw.value( info.getMessage() );
- jw.key( "service" );
+ jw.key( "service" ); //$NON-NLS-1$
jw.value( serviceDescription( info.getServiceReference() ) );
- jw.key( "exception" );
- jw.value( exceptionMessage( info.getException() ) );
+ jw.key( "exception" ); //$NON-NLS-1$
+ jw.value( exceptionMessage( info.getException(), traces ) );
+ Bundle bundle = info.getBundle();
+ if (null != bundle)
+ {
+ jw.key("bundleId"); //$NON-NLS-1$
+ jw.value(bundle.getBundleId());
+ String name = (String) bundle.getHeaders().get(Constants.BUNDLE_NAME);
+ if (null == name)
+ {
+ name = bundle.getSymbolicName();
+ }
+ if (null == name)
+ {
+ name = bundle.getLocation();
+ }
+ jw.key("bundleName"); //$NON-NLS-1$
+ jw.value(name);
+ }
jw.endObject();
}
@@ -173,7 +199,7 @@
{
if ( serviceReference == null )
{
- return "";
+ return ""; //$NON-NLS-1$
}
return serviceReference.toString();
}
@@ -184,25 +210,40 @@
switch ( level )
{
case LogService.LOG_INFO:
- return "INFO";
+ return "INFO"; //$NON-NLS-1$
case LogService.LOG_WARNING:
- return "WARNING";
+ return "WARNING"; //$NON-NLS-1$
case LogService.LOG_ERROR:
- return "ERROR";
+ return "ERROR"; //$NON-NLS-1$
case LogService.LOG_DEBUG:
default:
- return "DEBUG";
+ return "DEBUG"; //$NON-NLS-1$
}
}
+ private static final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ private static final PrintStream printStream = new PrintStream(baos);
- private static final String exceptionMessage( Throwable e )
+ private static final String exceptionMessage( Throwable e, boolean traces )
{
if ( e == null )
{
- return "";
+ return ""; //$NON-NLS-1$
}
- return e.getClass().getName() + ": " + e.getMessage();
+ if (traces) {
+ String ret = null;
+ synchronized (printStream)
+ {
+ try {
+ e.printStackTrace(printStream);
+ ret = baos.toString();
+ } finally {
+ baos.reset();
+ }
+ }
+ return ret;
+ }
+ return e.getClass().getName() + ": " + e.getMessage(); //$NON-NLS-1$
}
}
diff --git a/webconsole/src/main/native2ascii/OSGI-INF/l10n/bundle_bg.properties b/webconsole/src/main/native2ascii/OSGI-INF/l10n/bundle_bg.properties
index 240412a..bbf41d7 100644
--- a/webconsole/src/main/native2ascii/OSGI-INF/l10n/bundle_bg.properties
+++ b/webconsole/src/main/native2ascii/OSGI-INF/l10n/bundle_bg.properties
@@ -93,6 +93,9 @@
log.level.warn=ПРЕДУПР
log.level.info=ИНФО
log.level.debug=ДЕТАЙЛИ
+log.traces=Изглед изключения:
+log.traces.full=Пълен
+log.traces.min=Само съобщение
# Bundles plugin
bundles.pluginTitle=Бъндъли
diff --git a/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties b/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties
index 8928d08..4d4f592 100644
--- a/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties
+++ b/webconsole/src/main/resources/OSGI-INF/l10n/bundle.properties
@@ -93,6 +93,10 @@
log.level.warn=WARN
log.level.info=INFO
log.level.debug=DEBUG
+log.traces=Exception details:
+log.traces.full=Full Trace
+log.traces.min=Message Only
+
# Bundles plugin
bundles.pluginTitle=Bundles
diff --git a/webconsole/src/main/resources/res/ui/logs.css b/webconsole/src/main/resources/res/ui/logs.css
index 16f46df..21f6d95 100644
--- a/webconsole/src/main/resources/res/ui/logs.css
+++ b/webconsole/src/main/resources/res/ui/logs.css
@@ -22,3 +22,8 @@
th.col_Level { width: 6em }
th.col_Received,
th.col_Exception { min-width: 10em }
+td.ex {
+ font-size: smaller;
+ font-family: monospace;
+ white-space: pre
+}
diff --git a/webconsole/src/main/resources/res/ui/logs.js b/webconsole/src/main/resources/res/ui/logs.js
index acfdcec..00f2221 100644
--- a/webconsole/src/main/resources/res/ui/logs.js
+++ b/webconsole/src/main/resources/res/ui/logs.js
@@ -47,6 +47,7 @@
var message = dataEntry.message;
var level = dataEntry.level;
var exception = dataEntry.exception;
+ var exceptionClass = $(".enableTraces").val() == 'true' ? 'ex' : null;
var service = dataEntry.service;
switch (dataEntry.raw_level) { // i18n
case 1: level = i18n.error; break;
@@ -54,11 +55,16 @@
case 3: level = i18n.info; break;
case 4: level = i18n.debug; break;
}
+ var bundle = text('');
+ if (dataEntry.bundleId) {
+ bundle = createElement( 'a', null, {href : appRoot + '/bundles/' + dataEntry.bundleId}, [ text (dataEntry.bundleName) ] )
+ }
parent.appendChild( td( null, null, [ text( printDate(dataEntry.received) ) ] ) );
parent.appendChild( td( null, { lvl:dataEntry.raw_level }, [ text( level ) ] ) );
parent.appendChild( td( null, null, [ text( wordWrap(message) ) ] ) );
parent.appendChild( td( null, null, [ text( wordWrap(service) ) ] ) );
- parent.appendChild( td( null, null, [ text( exception ) ] ) );
+ parent.appendChild( td( null, null, [ bundle ] ) );
+ parent.appendChild( td( exceptionClass, null, [ text( exception ) ] ) );
}
/* displays a date in the user's local timezone */
@@ -68,28 +74,34 @@
}
function loadData() {
- $.get(pluginRoot + "/data.json", { "minLevel":$(".minLevel").val()}, renderData, "json");
+ $.post(pluginRoot, {
+ "minLevel" : $(".minLevel").val(),
+ "traces" : $(".enableTraces").val()
+ }, renderData, "json");
return false; // for button
}
$(document).ready(function() {
// install user interaction handlers
$(".reloadButton").click(loadData);
+ $(".enableTraces").change(function() {
+ var val = $(this).val();
+ $(".enableTraces").val(val); // same values for both select boxes
+ loadData();
+ })
$(".minLevel").change(function() {
var value = $(this).val();
$(".minLevel").val(value); // same values for both select boxes
- $.post(pluginRoot, {"minLevel":value}, function(data) {
- renderData(data);
- }, "json");
+ loadData();
});
- // init tablesorte
+ // init tablesorte
$('#plugin_table').tablesorter({
textExtraction: function(node) {
var _ = $(node);
return _.attr('lvl') ? _.attr('lvl') : _.text();
}
});
-
+
logsElem = $("#logs");
logs2Elem = $("#logs2");
tableElem = $("#plugin_table");
diff --git a/webconsole/src/main/resources/templates/logs.html b/webconsole/src/main/resources/templates/logs.html
index 2086dcb..48a852f 100644
--- a/webconsole/src/main/resources/templates/logs.html
+++ b/webconsole/src/main/resources/templates/logs.html
@@ -20,6 +20,11 @@
<!-- buttons top -->
<form method="post" enctype="multipart/form-data" action="">
<div class="ui-widget-header ui-corner-top buttonGroup">
+ <label>${log.traces}</label>
+ <select class="enableTraces">
+ <option value="false">${log.traces.min}</option>
+ <option value="true">${log.traces.full}</option>
+ </select>
<label>${log.severity.label}</label>
<select class="minLevel">
<option value="1">${log.level.error}</option>
@@ -39,17 +44,23 @@
<th class="col_Level">${log.level}</th>
<th class="col_Message">${log.message}</th>
<th class="col_Service">${log.service}</th>
+ <th class="col_Bundle">${bundle}</th>
<th class="col_Exception">${log.exception}</th>
</tr>
</thead>
<tbody>
- <tr><td colspan="5"> </td></tr>
+ <tr><td colspan="6"> </td></tr>
</tbody>
</table>
<!-- buttons bottom -->
<form method="post" enctype="multipart/form-data" action="">
<div class="ui-widget-header ui-corner-bottom buttonGroup">
+ <label>${log.traces}</label>
+ <select class="enableTraces">
+ <option value="false">${log.traces.min}</option>
+ <option value="true">${log.traces.full}</option>
+ </select>
<label>${log.severity.label}</label>
<select class="minLevel">
<option value="1">${log.level.error}</option>