You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@marmotta.apache.org by ss...@apache.org on 2013/11/29 13:01:02 UTC
git commit: more logging configuration support (MARMOTTA-390)
Updated Branches:
refs/heads/develop 5a6e37abd -> ac6f56d50
more logging configuration support (MARMOTTA-390)
Project: http://git-wip-us.apache.org/repos/asf/marmotta/repo
Commit: http://git-wip-us.apache.org/repos/asf/marmotta/commit/ac6f56d5
Tree: http://git-wip-us.apache.org/repos/asf/marmotta/tree/ac6f56d5
Diff: http://git-wip-us.apache.org/repos/asf/marmotta/diff/ac6f56d5
Branch: refs/heads/develop
Commit: ac6f56d50ade2173ef53b0031051a6e96af7ba68
Parents: 5a6e37a
Author: Sebastian Schaffert <ss...@apache.org>
Authored: Fri Nov 29 13:00:53 2013 +0100
Committer: Sebastian Schaffert <ss...@apache.org>
Committed: Fri Nov 29 13:00:53 2013 +0100
----------------------------------------------------------------------
.../webservices/logging/LoggingWebService.java | 219 +++++++++++++------
.../src/main/resources/web/admin/js/logging.js | 66 +++++-
.../src/main/resources/web/admin/logging.html | 75 +++----
3 files changed, 247 insertions(+), 113 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/marmotta/blob/ac6f56d5/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/webservices/logging/LoggingWebService.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/webservices/logging/LoggingWebService.java b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/webservices/logging/LoggingWebService.java
index c44ab09..56ee852 100644
--- a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/webservices/logging/LoggingWebService.java
+++ b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/webservices/logging/LoggingWebService.java
@@ -29,6 +29,7 @@ import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -103,6 +104,38 @@ public class LoggingWebService {
}
/**
+ * Update all log appenders passed in the JSON list given in the body of the POST service request.
+ *
+ * @HTTP 200 appenders updated successfully
+ * @HTTP 400 appender configuration invalid (e.g. not proper JSON)
+ *
+ * @return HTTP status 200 in case of success
+ */
+ @POST
+ @Path("/appenders")
+ @Consumes("application/json")
+ public Response updateAppenders(@Context HttpServletRequest request) {
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ //log.info(getContentData(request.getReader()));
+ List<Map<String,Object>> values = mapper.readValue(request.getInputStream(), new TypeReference<ArrayList<HashMap<String,Object>>>(){});
+
+ for(Map<String,Object> module : values) {
+ if(module.get("id") != null) {
+ updateAppenderJSON((String) module.get("id"), module);
+ }
+ }
+ return Response.ok("modules updated successfully").build();
+
+ } catch (JsonMappingException | JsonParseException e) {
+ return Response.status(Response.Status.BAD_REQUEST).entity("invalid JSON format: "+e.getMessage()).build();
+ } catch (IOException e) {
+ return Response.status(Response.Status.BAD_REQUEST).entity("could not read stream: "+e.getMessage()).build();
+ }
+ }
+
+
+ /**
* Get the configuration of the log appender with the given ID using the JSON format described in the header of
* the class
*
@@ -144,57 +177,10 @@ public class LoggingWebService {
//log.info(getContentData(request.getReader()));
Map<String,Object> values = mapper.readValue(request.getInputStream(), new TypeReference<HashMap<String,Object>>(){});
- String type = (String) values.get("type");
- String name = (String) values.get("name");
- String level = (String) values.get("level");
- String pattern = (String) values.get("pattern");
-
- LoggingOutput appender = loggingService.getOutputConfiguration(id);
- if(appender == null) {
- // type information required
- Preconditions.checkArgument(type != null, "appender type was not given");
- Preconditions.checkArgument(name != null, "appender name was not given");
-
- if("logfile".equals(type)) {
- String file = (String) values.get("file");
-
- Preconditions.checkArgument(file != null, "logfile name was not given");
-
- appender = loggingService.createLogFileOutput(id,name,file);
- } else if("syslog".equals(type)) {
- String host = (String) values.get("host");
-
- Preconditions.checkArgument(host != null, "syslog host was not given");
-
- appender = loggingService.createSyslogOutput(id,name);
- } else {
- return Response.status(Response.Status.NOT_IMPLEMENTED).entity("new appenders of type "+type+" not supported").build();
- }
- }
-
- appender.setName(name);
-
- if(level != null) {
- appender.setMaxLevel(Level.toLevel(level));
- }
- if(pattern != null) {
- appender.setPattern(pattern);
- }
- if(values.get("file") != null && appender instanceof LogFileOutput) {
- ((LogFileOutput) appender).setFileName((String) values.get("file"));
- }
- if(values.get("keep") != null && appender instanceof LogFileOutput) {
- ((LogFileOutput) appender).setKeepDays(Integer.parseInt(values.get("keep").toString()));
- }
- if(values.get("host") != null && appender instanceof SyslogOutput) {
- ((SyslogOutput) appender).setHostName((String) values.get("host"));
- }
- if(values.get("facility") != null && appender instanceof SyslogOutput) {
- ((SyslogOutput) appender).setFacility((String) values.get("facility"));
- }
+ updateAppenderJSON(id,values);
return Response.ok().build();
- } catch (IllegalArgumentException ex) {
+ } catch (IllegalArgumentException | UnsupportedOperationException ex) {
// thrown by Preconditions.checkArgument
return Response.status(Response.Status.BAD_REQUEST).entity(ex.getMessage()).build();
} catch (JsonMappingException | JsonParseException e) {
@@ -226,6 +212,45 @@ public class LoggingWebService {
}
/**
+ * Update all modules passed as JSON list argument to the POST body of the service call. Only the fields
+ * "level" and "appenders" can be updated for modules.
+ *
+ * @HTTP 200 modules updated successfully
+ * @HTTP 400 module configuration invalid (e.g. not proper JSON)
+ * @HTTP 404 module not found
+ *
+ * @return 200 OK in case modules have been updated successfully
+ */
+ @POST
+ @Path("/modules")
+ @Consumes("application/json")
+ public Response updateModules(@Context HttpServletRequest request) {
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ //log.info(getContentData(request.getReader()));
+ List<Map<String,Object>> values = mapper.readValue(request.getInputStream(), new TypeReference<ArrayList<HashMap<String,Object>>>(){});
+
+ boolean updated = false;
+ for(Map<String,Object> module : values) {
+ if(module.get("id") != null) {
+ updated = updateModuleJSON((String) module.get("id"), module) || updated;
+ }
+ }
+ if(updated) {
+ return Response.ok("modules updated successfully").build();
+ } else {
+ return Response.status(Response.Status.NOT_FOUND).entity("one or more modules where not found").build();
+ }
+
+ } catch (JsonMappingException | JsonParseException e) {
+ return Response.status(Response.Status.BAD_REQUEST).entity("invalid JSON format: "+e.getMessage()).build();
+ } catch (IOException e) {
+ return Response.status(Response.Status.BAD_REQUEST).entity("could not read stream: "+e.getMessage()).build();
+ }
+ }
+
+
+ /**
* Get the configuration of the logging module with the given id, using the JSON format described in the
* header of this class.
*
@@ -270,22 +295,13 @@ public class LoggingWebService {
//log.info(getContentData(request.getReader()));
Map<String,Object> values = mapper.readValue(request.getInputStream(), new TypeReference<HashMap<String,Object>>(){});
- String level = (String) values.get("level");
- List appenders = (List) values.get("appenders");
- for(LoggingModule module : loggingService.listModules()) {
- if(StringUtils.equals(module.getId(), id)) {
- if(level != null) {
- module.setCurrentLevel(Level.toLevel(level));
- }
- if(appenders != null) {
- module.setLoggingOutputIds(appenders);
- }
-
- return Response.ok("module updated").build();
- }
+
+ if(updateModuleJSON(id, values)) {
+ return Response.ok("module updated").build();
+ } else {
+ return Response.status(Response.Status.NOT_FOUND).build();
}
- return Response.status(Response.Status.NOT_FOUND).build();
} catch (JsonMappingException | JsonParseException e) {
return Response.status(Response.Status.BAD_REQUEST).entity("invalid JSON format: "+e.getMessage()).build();
} catch (IOException e) {
@@ -294,6 +310,83 @@ public class LoggingWebService {
}
+ /**
+ * Update a module following the JSON specification given as argument.
+ * @param spec
+ * @return
+ */
+ private boolean updateModuleJSON(String id, Map<String,Object> spec) {
+ String level = (String) spec.get("level");
+ List appenders = (List) spec.get("appenders");
+ for(LoggingModule module : loggingService.listModules()) {
+ if(StringUtils.equals(module.getId(), id)) {
+ if(level != null) {
+ module.setCurrentLevel(Level.toLevel(level));
+ }
+ if(appenders != null) {
+ module.setLoggingOutputIds(appenders);
+ }
+
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ private void updateAppenderJSON(String id, Map<String,Object> values) {
+ String type = (String) values.get("type");
+ String name = (String) values.get("name");
+ String level = (String) values.get("level");
+ String pattern = (String) values.get("pattern");
+
+ LoggingOutput appender = loggingService.getOutputConfiguration(id);
+ if(appender == null) {
+ // type information required
+ Preconditions.checkArgument(type != null, "appender type was not given");
+ Preconditions.checkArgument(name != null, "appender name was not given");
+
+ if("logfile".equals(type)) {
+ String file = (String) values.get("file");
+
+ Preconditions.checkArgument(file != null, "logfile name was not given");
+
+ appender = loggingService.createLogFileOutput(id,name,file);
+ } else if("syslog".equals(type)) {
+ String host = (String) values.get("host");
+
+ Preconditions.checkArgument(host != null, "syslog host was not given");
+
+ appender = loggingService.createSyslogOutput(id,name);
+ } else {
+ throw new UnsupportedOperationException("new appenders of type "+type+" not supported");
+ }
+ }
+
+ appender.setName(name);
+
+ if(level != null) {
+ appender.setMaxLevel(Level.toLevel(level));
+ }
+ if(pattern != null) {
+ appender.setPattern(pattern);
+ }
+ if(values.get("file") != null && appender instanceof LogFileOutput) {
+ ((LogFileOutput) appender).setFileName((String) values.get("file"));
+ }
+ if(values.get("keep") != null && appender instanceof LogFileOutput) {
+ ((LogFileOutput) appender).setKeepDays(Integer.parseInt(values.get("keep").toString()));
+ }
+ if(values.get("host") != null && appender instanceof SyslogOutput) {
+ ((SyslogOutput) appender).setHostName((String) values.get("host"));
+ }
+ if(values.get("facility") != null && appender instanceof SyslogOutput) {
+ ((SyslogOutput) appender).setFacility((String) values.get("facility"));
+ }
+
+ }
+
+
private static Map<String,Object> appenderToJSON(LoggingOutput out) {
Map<String,Object> result = new HashMap<>();
http://git-wip-us.apache.org/repos/asf/marmotta/blob/ac6f56d5/platform/marmotta-core/src/main/resources/web/admin/js/logging.js
----------------------------------------------------------------------
diff --git a/platform/marmotta-core/src/main/resources/web/admin/js/logging.js b/platform/marmotta-core/src/main/resources/web/admin/js/logging.js
index eabceed..9620d5b 100644
--- a/platform/marmotta-core/src/main/resources/web/admin/js/logging.js
+++ b/platform/marmotta-core/src/main/resources/web/admin/js/logging.js
@@ -25,27 +25,33 @@
var loggingApp = angular.module('logging', []);
loggingApp.controller('LoggingController', function ($scope, $http) {
+ $scope.levels = ['OFF', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'TRACE'];
+
+
$http.get(url + 'logging/modules').success(function(data) {
$scope.modules = data;
+
+ $scope.$watch('modules', function(newValue, oldValue) {
+ if(oldValue != newValue) {
+ $scope.needsModulesSave = true;
+ }
+ }, true);
});
$http.get(url + 'logging/appenders').success(function(data) {
$scope.appenders = data;
- });
- $scope.updateAppender = function(appender) {
- $http.post(url + 'logging/appenders/' + appender.id, appender);
- };
-
- $scope.updateModule = function(module) {
- $http.post(url + 'logging/modules/' + module.id, module);
- };
+ $scope.$watch('appenders', function(newValue, oldValue) {
+ if(oldValue != newValue) {
+ $scope.needsAppendersSave = true;
+ }
+ }, true);
+ });
$scope.removeModuleAppender = function(module,appender_id) {
var i = module.appenders.indexOf(appender_id);
if(i >= 0) {
module.appenders.splice(i,1);
- $scope.updateModule(module);
}
};
@@ -53,7 +59,6 @@ loggingApp.controller('LoggingController', function ($scope, $http) {
var i = module.appenders.indexOf(appender_id);
if(i < 0) {
module.appenders.push(appender_id);
- $scope.updateModule(module);
}
};
@@ -66,4 +71,45 @@ loggingApp.controller('LoggingController', function ($scope, $http) {
}
return result;
};
+
+ /**
+ * Save all appenders in this scope back to the Marmotta Webservice
+ */
+ $scope.saveAppenders = function() {
+ // $http.post takes the old model, so we use jQuery
+ $.ajax({
+ type: "POST",
+ url: url + 'logging/appenders',
+ data: angular.toJson($scope.appenders),
+ contentType: 'application/json'
+ });
+
+ //$http.post(url + 'logging/appenders', $scope.appenders);
+ $scope.needsAppendersSave = false;
+ }
+
+
+ /**
+ * Save all modules in this scope back to the Marmotta Webservice
+ */
+ $scope.saveModules = function() {
+ // $http.post takes the old model, so we use jQuery
+ $.ajax({
+ type: "POST",
+ url: url + 'logging/modules',
+ data: angular.toJson($scope.modules),
+ contentType: 'application/json'
+ });
+
+ //$http.post(url + 'logging/modules', mods);
+ $scope.needsModulesSave = false;
+ }
+
+ /*
+ * Watch updates to the model and set a flag to enable save buttons in UI
+ */
+ $scope.needsModulesSave = false;
+ $scope.needsAppendersSave = false;
+
+
});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/marmotta/blob/ac6f56d5/platform/marmotta-core/src/main/resources/web/admin/logging.html
----------------------------------------------------------------------
diff --git a/platform/marmotta-core/src/main/resources/web/admin/logging.html b/platform/marmotta-core/src/main/resources/web/admin/logging.html
index dd4ab0d..577f173 100644
--- a/platform/marmotta-core/src/main/resources/web/admin/logging.html
+++ b/platform/marmotta-core/src/main/resources/web/admin/logging.html
@@ -90,6 +90,24 @@
<h1>Marmotta Logging Configuration</h1>
+ <p>
+ The following forms allow to configure logging in Apache Marmotta. Logging in Marmotta is based on
+ <a href="http://logback.qos.ch/">Logback</a> and uses the following concepts:
+ </p>
+
+ <ul>
+ <li>
+ <strong>appenders</strong> are log output destinations (either the console, a logfile, or a syslog facility);
+ each appender has at least a pattern describing the layout (following the
+ <a href="http://logback.qos.ch/manual/layouts.html#ClassicPatternLayout">Logback Pattern Layout</a>) and a maximum
+ level; messages with higher level are always ignored by the appender
+ </li>
+ <li>
+ <strong>modules</strong> are logical components in Marmotta that represent a certain functionality (e.g. "SPARQL");
+ each module has a loglevel and a list of appenders to which it will send its output.
+ </li>
+ </ul>
+
<div ng-app="logging" ng-controller="LoggingController">
<h2>Log Appenders</h2>
@@ -98,20 +116,14 @@
<h3>Console</h3>
<table class="appenders">
<tr>
- <th>Name</th><th>Level</th><th>Pattern</th><th></th><th></th>
+ <th>Name</th><th>Level</th><th>Pattern <a href="http://logback.qos.ch/manual/layouts.html#ClassicPatternLayout" target="new">?</a> </th><th></th><th></th>
</tr>
<tr ng-repeat="appender in appenders | filter:{type:'console'}" ng-class-even="'even'" ng-class-odd="'odd'">
<td class="name">{{appender.name}}</td>
<td class="level">
- <select ng-model="appender.level" ng-change="updateAppender(appender)">
- <option>ERROR</option>
- <option>WARN</option>
- <option>INFO</option>
- <option>DEBUG</option>
- <option>TRACE</option>
- </select>
+ <select ng-model="appender.level" ng-options="level for level in levels"></select>
</td>
- <td class="pattern">{{appender.pattern}}</td>
+ <td class="pattern"><input type="text" ng-model="appender.pattern" size="40"/> </td>
<td class="additional"></td>
<td class="additional"></td>
</tr>
@@ -120,45 +132,33 @@
<h3>Logfile</h3>
<table class="appenders">
<tr>
- <th>Name</th><th>Level</th><th>Pattern</th><th>Filename</th><th>Keep Days</th>
+ <th>Name</th><th>Level</th><th>Pattern <a href="http://logback.qos.ch/manual/layouts.html#ClassicPatternLayout" target="new">?</a> </th><th>Filename</th><th>Keep Days</th>
</tr>
<tr ng-repeat="appender in appenders | filter:{type:'logfile'}" ng-class-even="'even'" ng-class-odd="'odd'">
<td class="name">{{appender.name}}</td>
<td class="level">
- <select ng-model="appender.level" ng-change="updateAppender(appender)">
- <option>ERROR</option>
- <option>WARN</option>
- <option>INFO</option>
- <option>DEBUG</option>
- <option>TRACE</option>
- </select>
+ <select ng-model="appender.level" ng-options="level for level in levels"></select>
</td>
- <td class="pattern">{{appender.pattern}}</td>
- <td class="additional">{{appender.file}}</td>
- <td class="additional">{{appender.keep}}</td>
+ <td class="pattern"><input type="text" ng-model="appender.pattern" size="40"/></td>
+ <td class="additional"><input type="text" ng-model="appender.file" size="20"/></td>
+ <td class="additional"><input type="number" ng-model="appender.keep" size="3" min="0"/></td>
</tr>
</table>
<h3>Syslog</h3>
<table class="appenders">
<tr>
- <th>Name</th><th>Level</th><th>Pattern</th><th>Host</th><th>Facility</th>
+ <th>Name</th><th>Level</th><th>Pattern <a href="http://logback.qos.ch/manual/layouts.html#ClassicPatternLayout" target="new">?</a> </th><th>Host</th><th>Facility</th>
</tr>
<tr ng-repeat="appender in appenders | filter:{type:'syslog'}" ng-class-even="'even'" ng-class-odd="'odd'">
<td class="name">{{appender.name}}</td>
<td class="level">
- <select ng-model="appender.level" ng-change="updateAppender(appender)">
- <option>ERROR</option>
- <option>WARN</option>
- <option>INFO</option>
- <option>DEBUG</option>
- <option>TRACE</option>
- </select>
+ <select ng-model="appender.level" ng-options="level for level in levels"></select>
</td>
- <td class="pattern">{{appender.pattern}}</td>
- <td class="additional">{{appender.host}}</td>
+ <td class="pattern"><input type="text" ng-model="appender.pattern" size="40"/></td>
+ <td class="additional"><input type="text" ng-model="appender.host" size="20"/></td>
<td class="additional">
- <select ng-model="appender.facility" ng-change="updateAppender(appender)">
+ <select ng-model="appender.facility">
<option>USER</option>
<option>DAEMON</option>
<option>SYSLOG</option>
@@ -175,6 +175,7 @@
</tr>
</table>
+ <div class="save-button"><button ng-click="saveAppenders()" ng-disabled="!needsAppendersSave">Save</button> </div>
<h2>Log Modules</h2>
@@ -186,13 +187,7 @@
<tr ng-repeat="module in modules" ng-class-even="'even'" ng-class-odd="'odd'">
<td class="name">{{module.name}}</td>
<td class="level">
- <select ng-model="module.level" ng-change="updateModule(module)">
- <option>ERROR</option>
- <option>WARN</option>
- <option>INFO</option>
- <option>DEBUG</option>
- <option>TRACE</option>
- </select>
+ <select ng-model="module.level" ng-options="level for level in levels"></select>
</td>
<td class="appenders">
<span ng-repeat="appender in module.appenders">
@@ -203,12 +198,12 @@
<td class="actions">
<select ng-model="module.new_appender" style="width: 80%">
<option ng-repeat="appender in getUnselectedModuleAppenders(module)" value="{{appender.id}}">{{appender.name}}</option>
- </select>
- <button ng-click="addModuleAppender(module,module.new_appender)">+</button>
+ </select> <button ng-click="addModuleAppender(module,module.new_appender)">+</button>
</td>
</tr>
</table>
+ <div class="save-button"><button ng-click="saveModules()" ng-disabled="!needsModulesSave">Save</button> </div>
</div>