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/28 20:36:51 UTC

[3/6] git commit: configuration webservice for MARMOTTA-390 (configurable logging) functional

configuration webservice for MARMOTTA-390 (configurable logging) functional


Project: http://git-wip-us.apache.org/repos/asf/marmotta/repo
Commit: http://git-wip-us.apache.org/repos/asf/marmotta/commit/e5dd9cf6
Tree: http://git-wip-us.apache.org/repos/asf/marmotta/tree/e5dd9cf6
Diff: http://git-wip-us.apache.org/repos/asf/marmotta/diff/e5dd9cf6

Branch: refs/heads/develop
Commit: e5dd9cf63a7f3fe4526aa7a09677ab1125f65ecc
Parents: 7b9cb5a
Author: Sebastian Schaffert <ss...@apache.org>
Authored: Thu Nov 28 16:24:25 2013 +0100
Committer: Sebastian Schaffert <ss...@apache.org>
Committed: Thu Nov 28 16:24:25 2013 +0100

----------------------------------------------------------------------
 .../core/api/config/ConfigurationService.java   |   2 +
 .../config/ConfigurationServiceImpl.java        |  98 +++++-
 .../services/logging/LoggingServiceImpl.java    |   9 +
 .../webservices/logging/LoggingWebService.java  | 332 +++++++++++++++++++
 .../src/main/resources/kiwi-module.properties   |   3 +-
 5 files changed, 426 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/marmotta/blob/e5dd9cf6/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/api/config/ConfigurationService.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/api/config/ConfigurationService.java b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/api/config/ConfigurationService.java
index 4f8c5f1..19909c4 100644
--- a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/api/config/ConfigurationService.java
+++ b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/api/config/ConfigurationService.java
@@ -69,6 +69,8 @@ public interface ConfigurationService {
 
     static final String DIR_IMPORT = "import";
 
+    static final String LOGGING_PATH = "logging";
+
     /**
      * Get the base URI of the system.
      * The base URI is used by the LMF to create local resource URIs. In this way, all Apache Marmotta resources

http://git-wip-us.apache.org/repos/asf/marmotta/blob/e5dd9cf6/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/services/config/ConfigurationServiceImpl.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/services/config/ConfigurationServiceImpl.java b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/services/config/ConfigurationServiceImpl.java
index 676f763..752fe74 100644
--- a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/services/config/ConfigurationServiceImpl.java
+++ b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/services/config/ConfigurationServiceImpl.java
@@ -26,6 +26,7 @@ import org.apache.commons.configuration.ConfigurationException;
 import org.apache.commons.configuration.MapConfiguration;
 import org.apache.commons.configuration.PropertiesConfiguration;
 import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.marmotta.platform.core.api.config.ConfigurationService;
 import org.apache.marmotta.platform.core.events.ConfigurationChangedEvent;
 import org.apache.marmotta.platform.core.events.ConfigurationServiceInitEvent;
@@ -48,15 +49,9 @@ import java.io.IOException;
 import java.lang.reflect.Array;
 import java.net.URL;
 import java.net.UnknownHostException;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
+import java.util.*;
 import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -128,10 +123,28 @@ public class ConfigurationServiceImpl implements ConfigurationService {
      */
     private ReadWriteLock lock;
 
+    /**
+     * Backlog for delayed event collection; only fires a configuration changed event if there has not been a further
+     * update in a specified amount of time (default 250ms);
+     */
+    private Set<String> eventBacklog;
+
+    private long EVENT_DELAY = 250L;
+
+
+    /*
+     * Timer and task for delayed execution of configuration changed events
+     */
+    private Timer          eventTimer;
+    private ReentrantLock  eventLock;
+
 
     public ConfigurationServiceImpl() {
         runtimeFlags = new HashMap<String, Boolean>();
         lock = new ReentrantReadWriteLock();
+
+        eventTimer = new Timer("Configuration Event Timer", true);
+        eventLock  = new ReentrantLock();
     }
 
     /**
@@ -578,7 +591,7 @@ public class ConfigurationServiceImpl implements ConfigurationService {
             }
 
             if (!initialising) {
-                configurationEvent.fire(new ConfigurationChangedEvent(key));
+                raiseDelayedConfigurationEvent(Collections.singleton(key));
             }
         }
     }
@@ -713,7 +726,7 @@ public class ConfigurationServiceImpl implements ConfigurationService {
             }
 
             if (!initialising) {
-                configurationEvent.fire(new ConfigurationChangedEvent(key));
+                raiseDelayedConfigurationEvent(Collections.singleton(key));
             }
         }
     }
@@ -764,7 +777,7 @@ public class ConfigurationServiceImpl implements ConfigurationService {
             }
 
             if (!initialising) {
-                configurationEvent.fire(new ConfigurationChangedEvent(key));
+                raiseDelayedConfigurationEvent(Collections.singleton(key));
             }
         }
     }
@@ -831,7 +844,7 @@ public class ConfigurationServiceImpl implements ConfigurationService {
             }
 
             if (!initialising) {
-                configurationEvent.fire(new ConfigurationChangedEvent(key));
+                raiseDelayedConfigurationEvent(Collections.singleton(key));
             }
         }
     }
@@ -886,7 +899,7 @@ public class ConfigurationServiceImpl implements ConfigurationService {
 
 
             if (!initialising) {
-                configurationEvent.fire(new ConfigurationChangedEvent(key));
+                raiseDelayedConfigurationEvent(Collections.singleton(key));
             }
         }
     }
@@ -982,7 +995,7 @@ public class ConfigurationServiceImpl implements ConfigurationService {
 
             if (!initialising) {
                 log.debug("firing configuration changed event for key {}", key);
-                configurationEvent.fire(new ConfigurationChangedEvent(key));
+                raiseDelayedConfigurationEvent(Collections.singleton(key));
             }
         }
     }
@@ -1030,7 +1043,7 @@ public class ConfigurationServiceImpl implements ConfigurationService {
 
             if (!initialising) {
                 log.debug("firing configuration changed event for key {}", key);
-                configurationEvent.fire(new ConfigurationChangedEvent(key));
+                raiseDelayedConfigurationEvent(Collections.singleton(key));
             }
         }
     }
@@ -1087,7 +1100,7 @@ public class ConfigurationServiceImpl implements ConfigurationService {
 
 
         if (!initialising) {
-            configurationEvent.fire(new ConfigurationChangedEvent(values.keySet()));
+            raiseDelayedConfigurationEvent((values.keySet()));
         }
     }
 
@@ -1107,7 +1120,7 @@ public class ConfigurationServiceImpl implements ConfigurationService {
 
 
             if (!initialising) {
-                configurationEvent.fire(new ConfigurationChangedEvent(key));
+                raiseDelayedConfigurationEvent(Collections.singleton(key));
             }
         }
     }
@@ -1470,4 +1483,55 @@ public class ConfigurationServiceImpl implements ConfigurationService {
         }
         return value;
     }
+
+
+    /**
+     * Start a delayed execution of raising an event
+     * @param keys
+     */
+    private void raiseDelayedConfigurationEvent(Set<String> keys) {
+        eventLock.lock();
+        try {
+            if(eventBacklog == null) {
+                eventBacklog = new HashSet<>();
+            }
+            eventBacklog.addAll(keys);
+
+            if(eventTimer != null) {
+                eventTimer.cancel();
+            }
+            eventTimer = new Timer("Configuration Event Timer", true);
+            eventTimer.schedule(new EventTimerTask(), EVENT_DELAY);
+        } finally {
+            eventLock.unlock();
+        }
+
+        if(log.isDebugEnabled()) {
+            log.debug("updated configuration keys [{}]", StringUtils.join(keys,", "));
+        }
+
+    }
+
+    /**
+     * Delayed event firing task
+     */
+    private class EventTimerTask extends TimerTask {
+
+        @Override
+        public void run() {
+            eventLock.lock();
+            try {
+                Set<String> keys = eventBacklog;
+                eventBacklog = null;
+
+                if(log.isDebugEnabled()) {
+                    log.debug("firing delayed ({}ms) configuration changed event with keys [{}]",EVENT_DELAY, StringUtils.join(keys,", "));
+                }
+
+                configurationEvent.fire(new ConfigurationChangedEvent(keys));
+            } finally {
+                eventLock.unlock();
+            }
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/marmotta/blob/e5dd9cf6/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/services/logging/LoggingServiceImpl.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/services/logging/LoggingServiceImpl.java b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/services/logging/LoggingServiceImpl.java
index d77897b..0265660 100644
--- a/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/services/logging/LoggingServiceImpl.java
+++ b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/services/logging/LoggingServiceImpl.java
@@ -38,6 +38,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.marmotta.platform.core.api.config.ConfigurationService;
 import org.apache.marmotta.platform.core.api.logging.LoggingModule;
 import org.apache.marmotta.platform.core.api.logging.LoggingService;
+import org.apache.marmotta.platform.core.events.ConfigurationChangedEvent;
 import org.apache.marmotta.platform.core.events.LoggingStartEvent;
 import org.apache.marmotta.platform.core.exception.MarmottaConfigurationException;
 import org.apache.marmotta.platform.core.model.logging.ConsoleOutput;
@@ -150,6 +151,14 @@ public class LoggingServiceImpl implements LoggingService {
         configureLoggers();
     }
 
+    public void configurationEventHandler(@Observes ConfigurationChangedEvent event) {
+        if(event.containsChangedKeyWithPrefix("logging.")) {
+            log.warn("LOGGING: Reloading logging configuration");
+
+            configureLoggers();
+        }
+    }
+
 
     /**
      * Configure all loggers according to their configuration and set some reasonable fallback for the root level

http://git-wip-us.apache.org/repos/asf/marmotta/blob/e5dd9cf6/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
new file mode 100644
index 0000000..c44ab09
--- /dev/null
+++ b/platform/marmotta-core/src/main/java/org/apache/marmotta/platform/core/webservices/logging/LoggingWebService.java
@@ -0,0 +1,332 @@
+package org.apache.marmotta.platform.core.webservices.logging;
+
+import ch.qos.logback.classic.Level;
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.marmotta.platform.core.api.config.ConfigurationService;
+import org.apache.marmotta.platform.core.api.logging.LoggingModule;
+import org.apache.marmotta.platform.core.api.logging.LoggingService;
+import org.apache.marmotta.platform.core.model.logging.ConsoleOutput;
+import org.apache.marmotta.platform.core.model.logging.LogFileOutput;
+import org.apache.marmotta.platform.core.model.logging.LoggingOutput;
+import org.apache.marmotta.platform.core.model.logging.SyslogOutput;
+import org.codehaus.jackson.JsonParseException;
+import org.codehaus.jackson.map.JsonMappingException;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.type.TypeReference;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Listing and modifying logging configuration for appenders and logging modules. JSON format for each appender is:
+ * <pre>
+ *     {
+ *         "type":    ("console" | "logfile" | "syslog" ),
+ *         "id":      unique identifier
+ *         "name":    human-readable name
+ *         "pattern": pattern for logging output
+ *         "level":   ("ERROR" | "WARN" | "INFO" | "DEBUG" | "TRACE" | "OFF" )
+ *     }
+ * </pre>
+ *
+ * Depending on the type of appender, the following additional fields need to be provided:
+ * <ul>
+ *     <li><strong>logfile</strong>
+ *     <pre>
+ *         "file":    name of the logfile
+ *         "keep":    how many days to keep old logfiles
+ *     </pre>
+ *     </li>
+ *     <li><strong>syslog</strong>
+ *     <pre>
+ *         "host":      name of server where the syslog daemon is running
+ *         "facility":  facility to use for logging
+ *     </pre>
+ *     </li>
+ * </ul>
+ *
+ * JSON format for logging modules is:
+ * <pre>
+ *     {
+ *         "id":        unique identifier
+ *         "name":      human-readable name
+ *         "level":     ("ERROR" | "WARN" | "INFO" | "DEBUG" | "TRACE" | "OFF" )
+ *         "appenders": list of appender ids to send logging to
+ *         "packages":  list of package ids managed by module
+ *     }
+ * </pre>
+ *
+ * @author Sebastian Schaffert (sschaffert@apache.org)
+ */
+@ApplicationScoped
+@Path("/" + ConfigurationService.LOGGING_PATH)
+public class LoggingWebService {
+
+    @Inject
+    private ConfigurationService configurationService;
+
+    @Inject
+    private LoggingService loggingService;
+
+    /**
+     * Get a JSON list of all log appenders currently configured in the system using the JSON format described in the
+     * header of the class
+     *
+     * @return JSON list
+     */
+    @GET
+    @Path("/appenders")
+    @Produces("application/json")
+    public Response listAppenders() {
+        return Response.ok(Lists.transform(loggingService.listOutputConfigurations(), new Function<LoggingOutput, Object>() {
+            @Override
+            public Object apply(LoggingOutput input) {
+                return appenderToJSON(input);
+            }
+        })).build();
+    }
+
+    /**
+     * Get the configuration of the log appender with the given ID using the JSON format described in the header of
+     * the class
+     *
+     * @HTTP 200 appender configuration returned successfully
+     * @HTTP 404 appender not found
+     *
+     * @param id unique identifier of appender
+     * @return JSON formatted representation of configuration
+     */
+    @GET
+    @Path("/appenders/{id}")
+    @Produces("application/json")
+    public Response getAppender(@PathParam("id") String id) {
+        LoggingOutput appender = loggingService.getOutputConfiguration(id);
+        if(appender != null) {
+            return Response.ok(appenderToJSON(appender)).build();
+        } else {
+            return Response.status(Response.Status.NOT_FOUND).build();
+        }
+    }
+
+
+    /**
+     * Create or update the appender with the given id, using the JSON description sent in the body of the request
+     *
+     * @HTTP 200 appender updated successfully
+     * @HTTP 400 appender configuration invalid (e.g. not proper JSON)
+     * @HTTP 404 appender not found
+     *
+     * @param id unique identifier of appender
+     * @return HTTP status 200 in case of success
+     */
+    @POST
+    @Path("/appenders/{id}")
+    @Consumes("application/json")
+    public Response updateAppender(@PathParam("id") String id, @Context HttpServletRequest request) {
+        ObjectMapper mapper = new ObjectMapper();
+        try {
+            //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"));
+            }
+
+            return Response.ok().build();
+        } catch (IllegalArgumentException ex) {
+            // thrown by Preconditions.checkArgument
+            return Response.status(Response.Status.BAD_REQUEST).entity(ex.getMessage()).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();
+        }
+    }
+
+
+    /**
+     * List all modules currently available in the system as a JSON list using the JSON format described in the
+     * header of this class
+     *
+     * @HTTP 200 in case the modules are listed properly
+     *
+     * @return JSON list of module descriptions
+     */
+    @GET
+    @Path("/modules")
+    @Produces("application/json")
+    public Response listModules() {
+        return Response.ok(Lists.transform(loggingService.listModules(), new Function<LoggingModule, Object>() {
+            @Override
+            public Object apply(LoggingModule input) {
+                return moduleToJSON(input);
+            }
+        })).build();
+    }
+
+    /**
+     * Get the configuration of the logging module with the given id, using the JSON format described in the
+     * header of this class.
+     *
+     * @HTTP 200 module found
+     * @HTTP 404 module not found
+     *
+     * @param id unique logging module identifier
+     * @return HTTP status 200 in case of success
+     */
+    @GET
+    @Path("/modules/{id}")
+    @Produces("application/json")
+    public Response getModule(@PathParam("id") String id) {
+        for(LoggingModule module : loggingService.listModules()) {
+            if(StringUtils.equals(module.getId(), id)) {
+                return Response.ok(moduleToJSON(module)).build();
+            }
+        }
+
+        return Response.status(Response.Status.NOT_FOUND).build();
+    }
+
+
+
+    /**
+     * Update the module with the given id, using the JSON description sent in the body of the request. Only the fields
+     * "level" and "appenders" can be updated for modules.
+     *
+     * @HTTP 200 module updated successfully
+     * @HTTP 400 module configuration invalid (e.g. not proper JSON)
+     * @HTTP 404 module not found
+     *
+     * @param id unique logging module identifier
+     * @return HTTP status 200 in case of success
+     */
+    @POST
+    @Path("/modules/{id}")
+    @Consumes("application/json")
+    public Response updateModule(@PathParam("id") String id, @Context HttpServletRequest request) {
+        ObjectMapper mapper = new ObjectMapper();
+        try {
+            //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();
+                }
+            }
+
+            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) {
+            return Response.status(Response.Status.BAD_REQUEST).entity("could not read stream: "+e.getMessage()).build();
+        }
+    }
+
+
+    private static Map<String,Object> appenderToJSON(LoggingOutput out) {
+        Map<String,Object> result = new HashMap<>();
+
+        if(out instanceof SyslogOutput) {
+            result.put("type", "syslog");
+            result.put("host", ((SyslogOutput) out).getHostName());
+            result.put("facility", ((SyslogOutput) out).getFacility());
+        } else if(out instanceof ConsoleOutput) {
+            result.put("type", "console");
+        } else if(out instanceof LogFileOutput) {
+            result.put("type", "logfile");
+            result.put("file", ((LogFileOutput) out).getFileName());
+            result.put("keep", ((LogFileOutput) out).getKeepDays());
+        }
+
+        result.put("level", out.getMaxLevel().toString());
+        result.put("pattern", out.getPattern());
+        result.put("name", out.getName());
+        result.put("id", out.getId());
+
+        return result;
+    }
+
+
+    private static Map<String,Object> moduleToJSON(LoggingModule module) {
+        Map<String,Object> result = new HashMap<>();
+
+        result.put("id", module.getId());
+        result.put("name", module.getName());
+        result.put("level", module.getCurrentLevel().toString());
+        result.put("appenders", module.getLoggingOutputIds());
+        result.put("packages", module.getPackages());
+
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/marmotta/blob/e5dd9cf6/platform/marmotta-core/src/main/resources/kiwi-module.properties
----------------------------------------------------------------------
diff --git a/platform/marmotta-core/src/main/resources/kiwi-module.properties b/platform/marmotta-core/src/main/resources/kiwi-module.properties
index aeecd4d..a71bb0b 100644
--- a/platform/marmotta-core/src/main/resources/kiwi-module.properties
+++ b/platform/marmotta-core/src/main/resources/kiwi-module.properties
@@ -70,5 +70,6 @@ webservices=org.apache.marmotta.platform.core.webservices.config.ConfigurationWe
   org.apache.marmotta.platform.core.webservices.resource.InspectionWebService,\
   org.apache.marmotta.platform.core.webservices.triplestore.LdpWebService,\
   org.apache.marmotta.platform.core.webservices.triplestore.ContextWebService,\
-  org.apache.marmotta.platform.core.webservices.prefix.PrefixWebService
+  org.apache.marmotta.platform.core.webservices.prefix.PrefixWebService,\
+  org.apache.marmotta.platform.core.webservices.logging.LoggingWebService
   
\ No newline at end of file