You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2020/02/12 11:29:01 UTC

[syncope] 01/02: [SYNCOPE-1517] Making JdbcAuditAppender configurable (but still enabled by default)

This is an automated email from the ASF dual-hosted git repository.

ilgrosso pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git

commit 7cd9b5d683b14fec0dbd8095b194e71053f19ee0
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Wed Feb 12 10:44:21 2020 +0100

    [SYNCOPE-1517] Making JdbcAuditAppender configurable (but still enabled by default)
---
 .../client/console/audit/AuditHistoryDetails.java  |  16 +-
 .../console/audit/AuditHistoryDirectoryPanel.java  |  35 ++--
 .../console/rest/AuditHistoryRestClient.java       |  72 -------
 .../client/console/rest/LoggerRestClient.java      |  45 +++++
 .../apache/syncope/common/lib/log/AuditEntry.java  | 158 +++++++++++++++
 .../apache/syncope/common/lib/to/AuditEntryTO.java | 141 -------------
 .../common/rest/api/service/AuditService.java      |  54 -----
 .../common/rest/api/service/LoggerService.java     |  15 ++
 .../syncope/core/logic/AbstractAuditAppender.java  |  72 -------
 .../org/apache/syncope/core/logic/AuditLogic.java  |  73 -------
 .../org/apache/syncope/core/logic/LoggerLogic.java |  50 +++--
 .../syncope/core/logic/audit/AuditAppender.java    |  20 +-
 .../core/logic/audit/DefaultAuditAppender.java     |  31 ++-
 .../logic/audit/DefaultRewriteAuditAppender.java   |  38 ++--
 .../core/logic/audit/JdbcAuditAppender.java        | 102 ++++++++++
 .../logic/audit/PassThroughAuditRewritePolicy.java |   1 -
 .../init/ClassPathScanImplementationLookup.java    |   7 +-
 .../syncope/core/logic/init/LoggerLoader.java      | 131 ++++--------
 .../logic/src/main/resources/logic.properties      |   1 +
 .../core/rest/cxf/service/AuditServiceImpl.java    |  52 -----
 .../core/rest/cxf/service/LoggerServiceImpl.java   |  40 +++-
 .../syncope/core/persistence/api/dao/AuditDAO.java |  45 -----
 .../core/persistence/api/dao/LoggerDAO.java        |  20 ++
 .../core/persistence/api/entity/AuditEntry.java    |  40 ----
 ...AuditDAO.java => AbstractJPAJSONLoggerDAO.java} |   2 +-
 ...PAJSONAuditDAO.java => MyJPAJSONLoggerDAO.java} |  10 +-
 ...PAJSONAuditDAO.java => PGJPAJSONLoggerDAO.java} |  14 +-
 .../main/resources/myjson/persistence.properties   |   2 +-
 .../main/resources/pgjsonb/persistence.properties  |   2 +-
 .../core/persistence/jpa/PersistenceContext.java   |   8 +-
 .../jpa/content/XMLContentExporter.java            |  14 +-
 .../core/persistence/jpa/dao/JPAAuditDAO.java      | 163 ---------------
 .../core/persistence/jpa/dao/JPALoggerDAO.java     | 141 ++++++++++++-
 .../persistence/jpa/entity/AbstractEntity.java     |   3 +
 .../persistence/jpa/entity/JPAConnInstance.java    |   1 -
 .../src/main/resources/persistence.properties      |   2 +-
 .../core/provisioning/api/AuditEntryImpl.java      | 219 ---------------------
 ...{AuditDataBinder.java => LoggerDataBinder.java} |  11 +-
 .../provisioning/api/serialization/POJOHelper.java |  12 --
 .../core/provisioning/api/AuditEntryImplTest.java  |  93 ---------
 .../api/PropagationByResourceTest.java             |  13 +-
 .../api/cache/VirAttrCacheValueTest.java           |   6 +-
 .../serialization/AttributeDeserializerTest.java   |  14 +-
 .../api/serialization/AttributeSerializerTest.java |  16 +-
 .../api/serialization/POJOHelperTest.java          |   7 -
 .../provisioning/java/DefaultAuditManager.java     |  85 +++++---
 .../java/data/AuditDataBinderImpl.java             |  66 -------
 .../java/data/LoggerDataBinderImpl.java}           |  43 ++--
 .../java/job/report/AuditReportlet.java            |  18 +-
 .../src/main/resources/persistence.properties.all  |   2 +-
 .../main/resources/persistence.properties.myjson   |   2 +-
 .../main/resources/persistence.properties.pgjsonb  |   2 +-
 .../src/main/resources/persistence.properties      |   2 +-
 .../core/reference/SyslogRewriteAuditAppender.java |  47 ++---
 .../fit/core/reference/TestFileAuditAppender.java  |  30 ++-
 .../reference/TestFileRewriteAuditAppender.java    |  23 +--
 .../fit/core/reference/TestRewritePolicy.java      |   7 +-
 .../resources/elasticsearch/persistence.properties |   2 +-
 .../src/main/resources/logic.properties            |   1 +
 .../org/apache/syncope/fit/AbstractITCase.java     |   4 -
 .../org/apache/syncope/fit/core/AuditITCase.java   |  36 ++--
 .../org/apache/syncope/fit/core/LoggerITCase.java  |   1 -
 .../systemadministration/dbms.adoc                 |   4 +-
 63 files changed, 871 insertions(+), 1516 deletions(-)

diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/audit/AuditHistoryDetails.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/audit/AuditHistoryDetails.java
index 8d491df..4b58d90 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/audit/AuditHistoryDetails.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/audit/AuditHistoryDetails.java
@@ -20,12 +20,13 @@ package org.apache.syncope.client.console.audit;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.Serializable;
 import java.util.Date;
 import org.apache.syncope.client.console.SyncopeConsoleSession;
 import org.apache.syncope.client.console.panels.MultilevelPanel;
 import org.apache.syncope.client.console.wicket.markup.html.form.JsonDiffPanel;
+import org.apache.syncope.common.lib.log.AuditEntry;
 import org.apache.syncope.common.lib.to.AbstractAnnotatedBean;
-import org.apache.syncope.common.lib.to.AuditEntryTO;
 import org.apache.syncope.common.lib.to.EntityTO;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.wicket.PageReference;
@@ -38,7 +39,7 @@ import org.apache.wicket.model.Model;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public abstract class AuditHistoryDetails<T extends EntityTO> extends MultilevelPanel.SecondLevel {
+public abstract class AuditHistoryDetails<T extends Serializable> extends MultilevelPanel.SecondLevel {
 
     private static final long serialVersionUID = -7400543686272100483L;
 
@@ -46,19 +47,16 @@ public abstract class AuditHistoryDetails<T extends EntityTO> extends Multilevel
 
     private static final ObjectMapper MAPPER = new ObjectMapper();
 
-    private static final String KEY_CURRENT = "current";
-
     public AuditHistoryDetails(
             final MultilevelPanel mlp,
-            final AuditEntryTO selected,
-            final T currentEntity,
+            final AuditEntry selected,
+            final EntityTO currentEntity,
             final String auditRestoreEntitlement,
             final PageReference pageRef) {
 
         super();
 
-        AuditEntryTO current = new AuditEntryTO();
-        current.setKey(KEY_CURRENT);
+        AuditEntry current = new AuditEntry();
         if (currentEntity instanceof AbstractAnnotatedBean) {
             current.setWho(((AbstractAnnotatedBean) currentEntity).getCreator());
             current.setDate(((AbstractAnnotatedBean) currentEntity).getCreationDate());
@@ -112,7 +110,7 @@ public abstract class AuditHistoryDetails<T extends EntityTO> extends Multilevel
 
     protected abstract void restore(String json, AjaxRequestTarget target);
 
-    private Model<String> toJSON(final AuditEntryTO auditEntry, final Class<T> reference) {
+    private Model<String> toJSON(final AuditEntry auditEntry, final Class<T> reference) {
         try {
             String content = auditEntry.getBefore() == null
                     ? MAPPER.readTree(auditEntry.getOutput()).get("entity").toPrettyString()
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/audit/AuditHistoryDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/audit/AuditHistoryDirectoryPanel.java
index 790f0b5..7b43498 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/audit/AuditHistoryDirectoryPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/audit/AuditHistoryDirectoryPanel.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.client.console.audit;
 
+import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
@@ -26,14 +27,14 @@ import org.apache.syncope.client.console.commons.IdRepoConstants;
 import org.apache.syncope.client.console.panels.AjaxDataTablePanel;
 import org.apache.syncope.client.console.panels.DirectoryPanel;
 import org.apache.syncope.client.console.panels.MultilevelPanel;
-import org.apache.syncope.client.console.rest.AuditHistoryRestClient;
+import org.apache.syncope.client.console.rest.LoggerRestClient;
 import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn;
 import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
 import org.apache.syncope.client.ui.commons.DirectoryDataProvider;
 import org.apache.syncope.client.ui.commons.panels.ModalPanel;
-import org.apache.syncope.common.lib.to.AuditEntryTO;
+import org.apache.syncope.common.lib.log.AuditEntry;
 import org.apache.syncope.common.lib.to.EntityTO;
 import org.apache.syncope.common.lib.types.AuditElements;
 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
@@ -47,8 +48,8 @@ import org.apache.wicket.model.CompoundPropertyModel;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.StringResourceModel;
 
-public abstract class AuditHistoryDirectoryPanel<T extends EntityTO> extends DirectoryPanel<
-        AuditEntryTO, AuditEntryTO, AuditHistoryDirectoryPanel<T>.AuditHistoryProvider, AuditHistoryRestClient>
+public abstract class AuditHistoryDirectoryPanel<T extends Serializable> extends DirectoryPanel<
+        AuditEntry, AuditEntry, AuditHistoryDirectoryPanel<T>.AuditHistoryProvider, LoggerRestClient>
         implements ModalPanel {
 
     private static final long serialVersionUID = -8248734710505211261L;
@@ -65,7 +66,7 @@ public abstract class AuditHistoryDirectoryPanel<T extends EntityTO> extends Dir
 
     private final String category;
 
-    private final T entity;
+    private final EntityTO entity;
 
     private final String auditRestoreEntitlement;
 
@@ -74,7 +75,7 @@ public abstract class AuditHistoryDirectoryPanel<T extends EntityTO> extends Dir
             final MultilevelPanel mlp,
             final AuditElements.EventCategoryType type,
             final String category,
-            final T entity,
+            final EntityTO entity,
             final String auditRestoreEntitlement,
             final PageReference pageRef) {
 
@@ -89,7 +90,7 @@ public abstract class AuditHistoryDirectoryPanel<T extends EntityTO> extends Dir
         this.auditRestoreEntitlement = auditRestoreEntitlement;
         this.pageRef = pageRef;
 
-        this.restClient = new AuditHistoryRestClient();
+        this.restClient = new LoggerRestClient();
         initResultTable();
     }
 
@@ -104,30 +105,30 @@ public abstract class AuditHistoryDirectoryPanel<T extends EntityTO> extends Dir
     }
 
     @Override
-    protected List<IColumn<AuditEntryTO, String>> getColumns() {
-        List<IColumn<AuditEntryTO, String>> columns = new ArrayList<>();
+    protected List<IColumn<AuditEntry, String>> getColumns() {
+        List<IColumn<AuditEntry, String>> columns = new ArrayList<>();
         columns.add(new PropertyColumn<>(new StringResourceModel("who", this), "who"));
         columns.add(new DatePropertyColumn<>(new StringResourceModel("date", this), null, "date"));
         return columns;
     }
 
     @Override
-    protected void resultTableCustomChanges(final AjaxDataTablePanel.Builder<AuditEntryTO, String> resultTableBuilder) {
+    protected void resultTableCustomChanges(final AjaxDataTablePanel.Builder<AuditEntry, String> resultTableBuilder) {
         resultTableBuilder.setMultiLevelPanel(baseModal, mlp);
     }
 
     protected abstract void restore(String json, AjaxRequestTarget target);
 
     @Override
-    protected ActionsPanel<AuditEntryTO> getActions(final IModel<AuditEntryTO> model) {
-        final ActionsPanel<AuditEntryTO> panel = super.getActions(model);
+    protected ActionsPanel<AuditEntry> getActions(final IModel<AuditEntry> model) {
+        final ActionsPanel<AuditEntry> panel = super.getActions(model);
 
-        panel.add(new ActionLink<AuditEntryTO>() {
+        panel.add(new ActionLink<AuditEntry>() {
 
             private static final long serialVersionUID = -6745431735457245600L;
 
             @Override
-            public void onClick(final AjaxRequestTarget target, final AuditEntryTO modelObject) {
+            public void onClick(final AjaxRequestTarget target, final AuditEntry modelObject) {
                 AuditHistoryDirectoryPanel.this.getTogglePanel().close(target);
 
                 mlp.next(
@@ -159,7 +160,7 @@ public abstract class AuditHistoryDirectoryPanel<T extends EntityTO> extends Dir
         return List.of();
     }
 
-    protected class AuditHistoryProvider extends DirectoryDataProvider<AuditEntryTO> {
+    protected class AuditHistoryProvider extends DirectoryDataProvider<AuditEntry> {
 
         private static final long serialVersionUID = 415113175628260864L;
 
@@ -174,7 +175,7 @@ public abstract class AuditHistoryDirectoryPanel<T extends EntityTO> extends Dir
         }
 
         @Override
-        public Iterator<AuditEntryTO> iterator(final long first, final long count) {
+        public Iterator<AuditEntry> iterator(final long first, final long count) {
             int page = ((int) first / paginatorRows);
             return restClient.search(
                     entity.getKey(),
@@ -189,7 +190,7 @@ public abstract class AuditHistoryDirectoryPanel<T extends EntityTO> extends Dir
         }
 
         @Override
-        public IModel<AuditEntryTO> model(final AuditEntryTO auditEntryBean) {
+        public IModel<AuditEntry> model(final AuditEntry auditEntryBean) {
             return new CompoundPropertyModel<>(auditEntryBean);
         }
     }
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/AuditHistoryRestClient.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/AuditHistoryRestClient.java
deleted file mode 100644
index 8666aa1..0000000
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/AuditHistoryRestClient.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.console.rest;
-
-import java.util.List;
-import org.apache.syncope.common.lib.to.AuditEntryTO;
-import org.apache.syncope.common.lib.types.AuditElements;
-import org.apache.syncope.common.rest.api.beans.AuditQuery;
-import org.apache.syncope.common.rest.api.service.AuditService;
-import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
-
-public class AuditHistoryRestClient extends BaseRestClient {
-
-    private static final long serialVersionUID = -381814125643246243L;
-
-    public List<AuditEntryTO> search(
-            final String key,
-            final int page,
-            final int size,
-            final AuditElements.EventCategoryType type,
-            final String category,
-            final List<String> events,
-            final AuditElements.Result result,
-            final SortParam<String> sort) {
-
-        AuditQuery query = new AuditQuery.Builder(key).
-                size(size).
-                page(page).
-                type(type).
-                category(category).
-                events(events).
-                result(result).
-                orderBy(toOrderBy(sort)).
-                build();
-
-        return getService(AuditService.class).search(query).getResult();
-    }
-
-    public int count(
-            final String key,
-            final AuditElements.EventCategoryType type,
-            final String category,
-            final List<String> events,
-            final AuditElements.Result result) {
-
-        AuditQuery query = new AuditQuery.Builder(key).
-                page(1).
-                size(1).
-                type(type).
-                category(category).
-                events(events).
-                result(result).
-                build();
-        return getService(AuditService.class).search(query).getTotalCount();
-    }
-}
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/LoggerRestClient.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/LoggerRestClient.java
index c283c02..462b608 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/LoggerRestClient.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/LoggerRestClient.java
@@ -26,15 +26,19 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
+import org.apache.syncope.common.lib.log.AuditEntry;
 import org.apache.syncope.common.lib.log.EventCategory;
 import org.apache.syncope.common.lib.log.LogAppender;
 import org.apache.syncope.common.lib.log.LogStatement;
 import org.apache.syncope.common.lib.log.LoggerTO;
+import org.apache.syncope.common.lib.types.AuditElements;
 import org.apache.syncope.common.lib.types.AuditLoggerName;
 import org.apache.syncope.common.lib.types.LoggerLevel;
 import org.apache.syncope.common.lib.types.LoggerType;
 import org.apache.syncope.common.rest.api.LoggerWrapper;
+import org.apache.syncope.common.rest.api.beans.AuditQuery;
 import org.apache.syncope.common.rest.api.service.LoggerService;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 
 public class LoggerRestClient extends BaseRestClient {
 
@@ -104,4 +108,45 @@ public class LoggerRestClient extends BaseRestClient {
             return List.of();
         }
     }
+
+    public List<AuditEntry> search(
+            final String key,
+            final int page,
+            final int size,
+            final AuditElements.EventCategoryType type,
+            final String category,
+            final List<String> events,
+            final AuditElements.Result result,
+            final SortParam<String> sort) {
+
+        AuditQuery query = new AuditQuery.Builder(key).
+                size(size).
+                page(page).
+                type(type).
+                category(category).
+                events(events).
+                result(result).
+                orderBy(toOrderBy(sort)).
+                build();
+
+        return getService(LoggerService.class).search(query).getResult();
+    }
+
+    public int count(
+            final String key,
+            final AuditElements.EventCategoryType type,
+            final String category,
+            final List<String> events,
+            final AuditElements.Result result) {
+
+        AuditQuery query = new AuditQuery.Builder(key).
+                page(1).
+                size(1).
+                type(type).
+                category(category).
+                events(events).
+                result(result).
+                build();
+        return getService(LoggerService.class).search(query).getTotalCount();
+    }
 }
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/log/AuditEntry.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/log/AuditEntry.java
new file mode 100644
index 0000000..47f5acf
--- /dev/null
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/log/AuditEntry.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.common.lib.log;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.syncope.common.lib.BaseBean;
+import org.apache.syncope.common.lib.types.AuditLoggerName;
+
+@XmlRootElement(name = "auditEntry")
+@XmlType
+public class AuditEntry extends BaseBean {
+
+    private static final long serialVersionUID = 1215115961911228005L;
+
+    private String who;
+
+    private Date date;
+
+    private AuditLoggerName logger;
+
+    private String before;
+
+    private final List<String> inputs = new ArrayList<>();
+
+    private String output;
+
+    private String throwable;
+
+    @Schema(accessMode = Schema.AccessMode.READ_ONLY)
+    public String getWho() {
+        return who;
+    }
+
+    public void setWho(final String who) {
+        this.who = who;
+    }
+
+    @Schema(accessMode = Schema.AccessMode.READ_ONLY)
+    public Date getDate() {
+        return date == null
+                ? null
+                : new Date(date.getTime());
+    }
+
+    public void setDate(final Date date) {
+        this.date = date == null
+                ? null
+                : new Date(date.getTime());
+    }
+
+    @Schema(accessMode = Schema.AccessMode.READ_ONLY)
+    public AuditLoggerName getLogger() {
+        return logger;
+    }
+
+    public void setLogger(final AuditLoggerName logger) {
+        this.logger = logger;
+    }
+
+    @Schema(accessMode = Schema.AccessMode.READ_ONLY)
+    public String getBefore() {
+        return before;
+    }
+
+    @XmlElementWrapper(name = "inputs")
+    @XmlElement(name = "input")
+    @JsonProperty("inputs")
+    @Schema(accessMode = Schema.AccessMode.READ_ONLY)
+    public List<String> getInputs() {
+        return inputs;
+    }
+
+    public void setBefore(final String before) {
+        this.before = before;
+    }
+
+    @Schema(accessMode = Schema.AccessMode.READ_ONLY)
+    public String getOutput() {
+        return output;
+    }
+
+    public void setOutput(final String output) {
+        this.output = output;
+    }
+
+    @Schema(accessMode = Schema.AccessMode.READ_ONLY)
+    public String getThrowable() {
+        return throwable;
+    }
+
+    public void setThrowable(final String throwable) {
+        this.throwable = throwable;
+    }
+
+    @Override
+    public int hashCode() {
+        return new HashCodeBuilder().
+                appendSuper(super.hashCode()).
+                append(who).
+                append(date).
+                append(logger).
+                append(before).
+                append(inputs).
+                append(output).
+                append(throwable).
+                build();
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final AuditEntry other = (AuditEntry) obj;
+        return new EqualsBuilder().
+                appendSuper(super.equals(obj)).
+                append(who, other.who).
+                append(date, other.date).
+                append(logger, other.logger).
+                append(before, other.before).
+                append(inputs, other.inputs).
+                append(output, other.output).
+                append(throwable, other.throwable).
+                build();
+    }
+}
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/AuditEntryTO.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/AuditEntryTO.java
deleted file mode 100644
index 9d183e6..0000000
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/AuditEntryTO.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.common.lib.to;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlType;
-import org.apache.syncope.common.lib.BaseBean;
-
-@XmlRootElement(name = "audit")
-@XmlType
-public class AuditEntryTO extends BaseBean implements EntityTO {
-
-    private static final long serialVersionUID = 1215115961911228005L;
-
-    private String who;
-
-    private String subCategory;
-
-    private String event;
-
-    private String result;
-
-    private String before;
-
-    private final List<String> inputs = new ArrayList<>();
-
-    private String output;
-
-    private Date date;
-
-    private String throwable;
-
-    private String key;
-
-    private String loggerName;
-
-    public Date getDate() {
-        return date;
-    }
-
-    public void setDate(final Date date) {
-        this.date = date;
-    }
-
-    public String getThrowable() {
-        return throwable;
-    }
-
-    public void setThrowable(final String throwable) {
-        this.throwable = throwable;
-    }
-
-    public String getOutput() {
-        return output;
-    }
-
-    public void setOutput(final String output) {
-        this.output = output;
-    }
-
-    public String getBefore() {
-        return before;
-    }
-
-    public void setBefore(final String before) {
-        this.before = before;
-    }
-
-    public List<String> getInputs() {
-        return inputs;
-    }
-
-    public String getSubCategory() {
-        return subCategory;
-    }
-
-    public void setSubCategory(final String subCategory) {
-        this.subCategory = subCategory;
-    }
-
-    public String getEvent() {
-        return event;
-    }
-
-    public void setEvent(final String event) {
-        this.event = event;
-    }
-
-    public String getResult() {
-        return result;
-    }
-
-    public void setResult(final String result) {
-        this.result = result;
-    }
-
-    public String getWho() {
-        return who;
-    }
-
-    public void setWho(final String who) {
-        this.who = who;
-    }
-
-    public String getLoggerName() {
-        return loggerName;
-    }
-
-    public void setLoggerName(final String loggerName) {
-        this.loggerName = loggerName;
-    }
-
-    @Override
-    public String getKey() {
-        return this.key;
-    }
-
-    @Override
-    public void setKey(final String key) {
-        this.key = key;
-    }
-}
diff --git a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AuditService.java b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AuditService.java
deleted file mode 100644
index 5035494..0000000
--- a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AuditService.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.common.rest.api.service;
-
-import io.swagger.v3.oas.annotations.security.SecurityRequirement;
-import io.swagger.v3.oas.annotations.security.SecurityRequirements;
-import io.swagger.v3.oas.annotations.tags.Tag;
-import org.apache.syncope.common.lib.to.AuditEntryTO;
-import org.apache.syncope.common.lib.to.PagedResult;
-import org.apache.syncope.common.rest.api.RESTHeaders;
-import org.apache.syncope.common.rest.api.beans.AuditQuery;
-
-import javax.ws.rs.BeanParam;
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-
-/**
- * REST operations for audit events.
- */
-@Tag(name = "Audits")
-@SecurityRequirements({
-    @SecurityRequirement(name = "BasicAuthentication"),
-    @SecurityRequirement(name = "Bearer") })
-@Path("audits")
-public interface AuditService {
-
-    /**
-     * Returns a paged list of audit objects matching the given query.
-     *
-     * @param auditQuery query conditions
-     * @return paged list of objects matching the given query
-     */
-    @GET
-    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    PagedResult<AuditEntryTO> search(@BeanParam AuditQuery auditQuery);
-}
diff --git a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/LoggerService.java b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/LoggerService.java
index 293aeba..1b494a3 100644
--- a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/LoggerService.java
+++ b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/LoggerService.java
@@ -28,6 +28,7 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirements;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import java.util.List;
 import javax.validation.constraints.NotNull;
+import javax.ws.rs.BeanParam;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
@@ -36,12 +37,15 @@ import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
+import org.apache.syncope.common.lib.log.AuditEntry;
 import org.apache.syncope.common.lib.log.EventCategory;
 import org.apache.syncope.common.lib.log.LogAppender;
 import org.apache.syncope.common.lib.log.LogStatement;
 import org.apache.syncope.common.lib.log.LoggerTO;
+import org.apache.syncope.common.lib.to.PagedResult;
 import org.apache.syncope.common.lib.types.LoggerType;
 import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.beans.AuditQuery;
 
 /**
  * REST operations for logging and auditing.
@@ -85,6 +89,17 @@ public interface LoggerService extends JAXRSService {
     List<EventCategory> events();
 
     /**
+     * Returns a paged list of audit entries matching the given query.
+     *
+     * @param auditQuery query conditions
+     * @return paged list of audit entries matching the given query
+     */
+    @GET
+    @Path("AUDIT/entries")
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    PagedResult<AuditEntry> search(@BeanParam AuditQuery auditQuery);
+
+    /**
      * Returns logger with matching type and name.
      *
      * @param type LoggerType to be selected.
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AbstractAuditAppender.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AbstractAuditAppender.java
deleted file mode 100644
index 0065543..0000000
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AbstractAuditAppender.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic;
-
-import org.apache.syncope.core.logic.audit.AuditAppender;
-import org.apache.logging.log4j.core.Appender;
-import org.apache.logging.log4j.core.appender.rewrite.RewriteAppender;
-import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy;
-
-public abstract class AbstractAuditAppender implements AuditAppender {
-
-    protected String domainName;
-
-    protected Appender targetAppender;
-
-    protected RewriteAppender rewriteAppender;
-
-    @Override
-    public abstract void init();
-
-    public abstract void initTargetAppender();
-
-    public abstract void initRewriteAppender();
-
-    @Override
-    public abstract RewritePolicy getRewritePolicy();
-
-    @Override
-    public String getDomainName() {
-        return domainName;
-    }
-
-    @Override
-    public void setDomainName(final String domainName) {
-        this.domainName = domainName;
-    }
-
-    @Override
-    public abstract String getTargetAppenderName();
-
-    @Override
-    public boolean isRewriteEnabled() {
-        return rewriteAppender != null;
-    }
-
-    @Override
-    public RewriteAppender getRewriteAppender() {
-        return rewriteAppender;
-    }
-
-    @Override
-    public Appender getTargetAppender() {
-        return targetAppender;
-    }
-
-}
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AuditLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AuditLogic.java
deleted file mode 100644
index f57eda9..0000000
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AuditLogic.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic;
-
-import java.lang.reflect.Method;
-import java.util.List;
-import java.util.stream.Collectors;
-import org.apache.commons.lang3.tuple.Pair;
-import org.apache.syncope.common.lib.to.AuditEntryTO;
-import org.apache.syncope.common.lib.types.IdRepoEntitlement;
-import org.apache.syncope.common.lib.types.AuditElements;
-import org.apache.syncope.core.persistence.api.dao.AuditDAO;
-import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
-import org.apache.syncope.core.persistence.api.entity.AuditEntry;
-import org.apache.syncope.core.provisioning.api.data.AuditDataBinder;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.stereotype.Component;
-import org.springframework.transaction.annotation.Transactional;
-
-@Component
-public class AuditLogic extends AbstractTransactionalLogic<AuditEntryTO> {
-
-    @Autowired
-    private AuditDataBinder binder;
-
-    @Autowired
-    private AuditDAO auditDAO;
-
-    @PreAuthorize("hasRole('" + IdRepoEntitlement.AUDIT_SEARCH + "')")
-    @Transactional(readOnly = true)
-    public Pair<Integer, List<AuditEntryTO>> search(
-            final String entityKey,
-            final int page,
-            final int size,
-            final AuditElements.EventCategoryType type,
-            final String category,
-            final String subcategory,
-            final List<String> events,
-            final AuditElements.Result result,
-            final List<OrderByClause> orderByClauses) {
-
-        Integer count = auditDAO.count(entityKey);
-        List<AuditEntry> matching = auditDAO.findByEntityKey(
-                entityKey, page, size, type, category, subcategory, events, result, orderByClauses);
-        List<AuditEntryTO> searchResults = matching.stream().
-                map(auditEntry -> binder.getAuditTO(entityKey, auditEntry)).
-                collect(Collectors.toList());
-        return Pair.of(count, searchResults);
-    }
-
-    @Override
-    protected AuditEntryTO resolveReference(final Method method, final Object... args)
-            throws UnresolvedReferenceException {
-        throw new UnresolvedReferenceException();
-    }
-}
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/LoggerLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/LoggerLogic.java
index 22c5d37..3b1fe49 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/LoggerLogic.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/LoggerLogic.java
@@ -26,10 +26,12 @@ import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 import java.util.stream.Collectors;
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.core.LoggerContext;
 import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.syncope.common.lib.log.AuditEntry;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.log.EventCategory;
@@ -54,6 +56,7 @@ import org.apache.syncope.core.persistence.api.DomainHolder;
 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
 import org.apache.syncope.core.persistence.api.dao.LoggerDAO;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.persistence.api.entity.Logger;
 import org.apache.syncope.core.provisioning.java.pushpull.PushJobDelegate;
@@ -72,6 +75,7 @@ import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.SystemPropertyUtils;
+import org.apache.syncope.core.provisioning.api.data.LoggerDataBinder;
 
 @Component
 public class LoggerLogic extends AbstractTransactionalLogic<EntityTO> {
@@ -91,6 +95,9 @@ public class LoggerLogic extends AbstractTransactionalLogic<EntityTO> {
     @Autowired
     private EntityFactory entityFactory;
 
+    @Autowired
+    private LoggerDataBinder binder;
+
     @PreAuthorize("hasRole('" + IdRepoEntitlement.LOG_LIST + "') and authentication.details.domain == "
             + "T(org.apache.syncope.common.lib.SyncopeConstants).MASTER_DOMAIN")
     @Transactional(readOnly = true)
@@ -115,12 +122,7 @@ public class LoggerLogic extends AbstractTransactionalLogic<EntityTO> {
     }
 
     private List<LoggerTO> list(final LoggerType type) {
-        return loggerDAO.findAll(type).stream().map(logger -> {
-            LoggerTO loggerTO = new LoggerTO();
-            loggerTO.setKey(logger.getKey());
-            loggerTO.setLevel(logger.getLevel());
-            return loggerTO;
-        }).collect(Collectors.toList());
+        return loggerDAO.findAll(type).stream().map(binder::getLoggerTO).collect(Collectors.toList());
     }
 
     @PreAuthorize("hasRole('" + IdRepoEntitlement.LOG_LIST + "') and authentication.details.domain == "
@@ -173,12 +175,7 @@ public class LoggerLogic extends AbstractTransactionalLogic<EntityTO> {
     public LoggerTO readAudit(final String name) {
         return listAudits().stream().
                 filter(logger -> logger.toLoggerName().equals(name)).findFirst().
-                map(logger -> {
-                    LoggerTO loggerTO = new LoggerTO();
-                    loggerTO.setKey(logger.toLoggerName());
-                    loggerTO.setLevel(LoggerLevel.DEBUG);
-                    return loggerTO;
-                }).orElseThrow(() -> new NotFoundException("Audit " + name));
+                map(binder::getLoggerTO).orElseThrow(() -> new NotFoundException("Audit " + name));
     }
 
     private LoggerTO setLevel(final String name, final Level level, final LoggerType expectedType) {
@@ -231,11 +228,7 @@ public class LoggerLogic extends AbstractTransactionalLogic<EntityTO> {
         logConf.setLevel(level);
         ctx.updateLoggers();
 
-        LoggerTO result = new LoggerTO();
-        result.setKey(syncopeLogger.getKey());
-        result.setLevel(syncopeLogger.getLevel());
-
-        return result;
+        return binder.getLoggerTO(syncopeLogger);
     }
 
     @PreAuthorize("hasRole('" + IdRepoEntitlement.LOG_SET_LEVEL + "') and authentication.details.domain == "
@@ -264,9 +257,7 @@ public class LoggerLogic extends AbstractTransactionalLogic<EntityTO> {
             throwInvalidLogger(expectedType);
         }
 
-        LoggerTO loggerToDelete = new LoggerTO();
-        loggerToDelete.setKey(syncopeLogger.getKey());
-        loggerToDelete.setLevel(syncopeLogger.getLevel());
+        LoggerTO loggerToDelete = binder.getLoggerTO(syncopeLogger);
 
         // remove SyncopeLogger from local storage, so that LoggerLoader won't load this next time
         loggerDAO.delete(syncopeLogger);
@@ -405,6 +396,25 @@ public class LoggerLogic extends AbstractTransactionalLogic<EntityTO> {
         return new ArrayList<>(events);
     }
 
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.AUDIT_SEARCH + "')")
+    @Transactional(readOnly = true)
+    public Pair<Integer, List<AuditEntry>> search(
+            final String entityKey,
+            final int page,
+            final int size,
+            final AuditElements.EventCategoryType type,
+            final String category,
+            final String subcategory,
+            final List<String> events,
+            final AuditElements.Result result,
+            final List<OrderByClause> orderByClauses) {
+
+        int count = loggerDAO.countAuditEntries(entityKey);
+        List<AuditEntry> matching = loggerDAO.findAuditEntries(
+                entityKey, page, size, type, category, subcategory, events, result, orderByClauses);
+        return Pair.of(count, matching);
+    }
+
     @Override
     protected EntityTO resolveReference(final Method method, final Object... args)
             throws UnresolvedReferenceException {
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/audit/AuditAppender.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/audit/AuditAppender.java
index f6a0716..d81daa6 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/audit/AuditAppender.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/audit/AuditAppender.java
@@ -18,10 +18,10 @@
  */
 package org.apache.syncope.core.logic.audit;
 
+import java.util.Optional;
 import java.util.Set;
 import org.apache.logging.log4j.core.Appender;
 import org.apache.logging.log4j.core.appender.rewrite.RewriteAppender;
-import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy;
 import org.apache.syncope.common.lib.types.AuditLoggerName;
 
 /**
@@ -32,21 +32,17 @@ import org.apache.syncope.common.lib.types.AuditLoggerName;
  */
 public interface AuditAppender {
 
-    void init();
+    void init(String domain);
 
-    Set<AuditLoggerName> getEvents();
+    default Set<AuditLoggerName> getEvents() {
+        return Set.of();
+    }
 
     Appender getTargetAppender();
 
-    RewritePolicy getRewritePolicy();
-
     String getTargetAppenderName();
 
-    void setDomainName(String name);
-
-    String getDomainName();
-
-    boolean isRewriteEnabled();
-
-    RewriteAppender getRewriteAppender();
+    default Optional<RewriteAppender> getRewriteAppender() {
+        return Optional.empty();
+    }
 }
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/audit/DefaultAuditAppender.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/audit/DefaultAuditAppender.java
index 45b28a3..a78f82f 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/audit/DefaultAuditAppender.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/audit/DefaultAuditAppender.java
@@ -18,36 +18,31 @@
  */
 package org.apache.syncope.core.logic.audit;
 
-import java.util.Set;
-import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy;
-import org.apache.syncope.common.lib.types.AuditLoggerName;
-import org.apache.syncope.core.logic.AbstractAuditAppender;
+import org.apache.logging.log4j.core.Appender;
 
 /**
  * Default (abstract) implementation of custom audit appender.
+ *
  * It is bound to an empty collection of events, i.e. it does not create any logger.
- * This class shall be extended by non-rewriting appenders; for rewriting, extend
+ * This class shall be extended by non-rewrite appenders; for rewrite, extend
  * {@link DefaultRewriteAuditAppender} instead.
  */
-public abstract class DefaultAuditAppender extends AbstractAuditAppender {
+public abstract class DefaultAuditAppender implements AuditAppender {
 
-    @Override
-    public void init() {
-        initTargetAppender();
-    }
+    protected String domain;
 
-    @Override
-    public Set<AuditLoggerName> getEvents() {
-        return Set.of();
-    }
+    protected Appender targetAppender;
 
     @Override
-    public void initRewriteAppender() {
+    public void init(final String domain) {
+        this.domain = domain;
+        initTargetAppender();
     }
 
+    protected abstract void initTargetAppender();
+
     @Override
-    public RewritePolicy getRewritePolicy() {
-        return null;
+    public Appender getTargetAppender() {
+        return targetAppender;
     }
-
 }
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/audit/DefaultRewriteAuditAppender.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/audit/DefaultRewriteAuditAppender.java
index aa4220e..ed62206 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/audit/DefaultRewriteAuditAppender.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/audit/DefaultRewriteAuditAppender.java
@@ -18,46 +18,44 @@
  */
 package org.apache.syncope.core.logic.audit;
 
-import java.util.Set;
+import java.util.Optional;
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.core.LoggerContext;
 import org.apache.logging.log4j.core.appender.rewrite.RewriteAppender;
 import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy;
 import org.apache.logging.log4j.core.config.AppenderRef;
-import org.apache.syncope.common.lib.types.AuditLoggerName;
-import org.apache.syncope.core.logic.AbstractAuditAppender;
 
 /**
- * Default (abstract) implementation of custom rewriting audit appender; it provides rewrite appender definition and
- * a default "pass-through" policy.
+ * Default (abstract) implementation of rewrite audit appender, allowing the logging event to be manipulated
+ * before it is processed.
+ * It provides rewrite appender definition and a default "pass-through" policy.
  * It is bound to an empty collection of events, i.e. it does not create any logger.
- * This class shall be extended by rewriting appenders; for non-rewriting, extend {@link DefaultAuditAppender} instead.
+ * This class shall be extended by rewrite appenders; for non-rewrite, extend {@link DefaultAuditAppender} instead.
+ *
+ * @see RewriteAppender
  */
-public abstract class DefaultRewriteAuditAppender extends AbstractAuditAppender {
+public abstract class DefaultRewriteAuditAppender extends DefaultAuditAppender {
 
-    @Override
-    public void init() {
-        initTargetAppender();
-        initRewriteAppender();
-    }
+    protected RewriteAppender rewriteAppender;
 
     @Override
-    public void initRewriteAppender() {
-        rewriteAppender = RewriteAppender.createAppender(getTargetAppenderName() + "_rewrite",
+    public void init(final String domain) {
+        super.init(domain);
+
+        rewriteAppender = RewriteAppender.createAppender(
+                getTargetAppenderName() + "_rewrite",
                 "true",
                 new AppenderRef[] { AppenderRef.createAppenderRef(getTargetAppenderName(), Level.DEBUG, null) },
                 ((LoggerContext) LogManager.getContext(false)).getConfiguration(), getRewritePolicy(), null);
     }
 
-    @Override
-    public Set<AuditLoggerName> getEvents() {
-        return Set.of();
+    protected RewritePolicy getRewritePolicy() {
+        return PassThroughAuditRewritePolicy.createPolicy();
     }
 
     @Override
-    public RewritePolicy getRewritePolicy() {
-        return PassThroughAuditRewritePolicy.createPolicy();
+    public Optional<RewriteAppender> getRewriteAppender() {
+        return Optional.of(rewriteAppender);
     }
-
 }
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/audit/JdbcAuditAppender.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/audit/JdbcAuditAppender.java
new file mode 100644
index 0000000..e96132f
--- /dev/null
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/audit/JdbcAuditAppender.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.logic.audit;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import javax.sql.DataSource;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.db.ColumnMapping;
+import org.apache.logging.log4j.core.appender.db.jdbc.AbstractConnectionSource;
+import org.apache.logging.log4j.core.appender.db.jdbc.JdbcAppender;
+import org.apache.syncope.core.persistence.api.DomainHolder;
+import org.apache.syncope.core.persistence.api.dao.LoggerDAO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.datasource.DataSourceUtils;
+
+public class JdbcAuditAppender extends DefaultAuditAppender {
+
+    @Autowired
+    protected DomainHolder domainHolder;
+
+    @Override
+    protected void initTargetAppender() {
+        LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
+
+        ColumnMapping[] columnMappings = {
+            ColumnMapping.newBuilder().
+            setConfiguration(ctx.getConfiguration()).setName("EVENT_DATE").setType(Timestamp.class).build(),
+            ColumnMapping.newBuilder().
+            setConfiguration(ctx.getConfiguration()).setName("LOGGER_LEVEL").setPattern("%level").build(),
+            ColumnMapping.newBuilder().
+            setConfiguration(ctx.getConfiguration()).setName("LOGGER").setPattern("%logger").build(),
+            ColumnMapping.newBuilder().
+            setConfiguration(ctx.getConfiguration()).
+            setName(LoggerDAO.AUDIT_MESSAGE_COLUMN).setPattern("%message").build(),
+            ColumnMapping.newBuilder().
+            setConfiguration(ctx.getConfiguration()).setName("THROWABLE").setPattern("%ex{full}").build()
+        };
+
+        Appender appender = ctx.getConfiguration().getAppender("audit_for_" + domain);
+        if (appender == null) {
+            appender = JdbcAppender.newBuilder().
+                    setName("audit_for_" + domain).
+                    setIgnoreExceptions(false).
+                    setConnectionSource(new DataSourceConnectionSource(domain, domainHolder.getDomains().get(domain))).
+                    setBufferSize(0).
+                    setTableName(LoggerDAO.AUDIT_TABLE).
+                    setColumnMappings(columnMappings).
+                    build();
+            appender.start();
+            ctx.getConfiguration().addAppender(appender);
+        }
+        targetAppender = appender;
+    }
+
+    @Override
+    public String getTargetAppenderName() {
+        // not used
+        return null;
+    }
+
+    protected static class DataSourceConnectionSource extends AbstractConnectionSource {
+
+        private final String description;
+
+        private final DataSource dataSource;
+
+        DataSourceConnectionSource(final String domain, final DataSource dataSource) {
+            this.description = "dataSource{ domain=" + domain + ", value=" + dataSource + " }";
+            this.dataSource = dataSource;
+        }
+
+        @Override
+        public Connection getConnection() throws SQLException {
+            return DataSourceUtils.getConnection(dataSource);
+        }
+
+        @Override
+        public String toString() {
+            return this.description;
+        }
+    }
+}
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/audit/PassThroughAuditRewritePolicy.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/audit/PassThroughAuditRewritePolicy.java
index b3ee968..609c486 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/audit/PassThroughAuditRewritePolicy.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/audit/PassThroughAuditRewritePolicy.java
@@ -39,5 +39,4 @@ public class PassThroughAuditRewritePolicy implements RewritePolicy {
     public static PassThroughAuditRewritePolicy createPolicy() {
         return new PassThroughAuditRewritePolicy();
     }
-
 }
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
index a9ecdb0..446a7db 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
@@ -34,6 +34,7 @@ import org.apache.syncope.common.lib.types.IdMImplementationType;
 import org.apache.syncope.common.lib.types.IdRepoImplementationType;
 import org.apache.syncope.common.lib.types.ImplementationTypesHolder;
 import org.apache.syncope.core.logic.audit.AuditAppender;
+import org.apache.syncope.core.logic.audit.JdbcAuditAppender;
 import org.apache.syncope.core.persistence.api.ImplementationLookup;
 import org.apache.syncope.core.persistence.api.attrvalue.validation.Validator;
 import org.apache.syncope.core.persistence.api.dao.AccountRule;
@@ -131,7 +132,7 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
         scanner.findCandidateComponents(getBasePackage()).forEach(bd -> {
             try {
                 Class<?> clazz = ClassUtils.resolveClassName(
-                    Objects.requireNonNull(bd.getBeanClassName()), ClassUtils.getDefaultClassLoader());
+                        Objects.requireNonNull(bd.getBeanClassName()), ClassUtils.getDefaultClassLoader());
                 boolean isAbstractClazz = Modifier.isAbstract(clazz.getModifiers());
 
                 if (JWTSSOProvider.class.isAssignableFrom(clazz) && !isAbstractClazz) {
@@ -231,7 +232,9 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
                     classNames.get(IdRepoImplementationType.RECIPIENTS_PROVIDER).add(bd.getBeanClassName());
                 }
 
-                if (AuditAppender.class.isAssignableFrom(clazz) && !isAbstractClazz) {
+                if (AuditAppender.class.isAssignableFrom(clazz)
+                        && !JdbcAuditAppender.class.equals(clazz) && !isAbstractClazz) {
+
                     classNames.get(IdRepoImplementationType.AUDIT_APPENDER).add(clazz.getName());
                     auditAppenderClasses.add(clazz);
                 }
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/LoggerLoader.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/LoggerLoader.java
index 282d726..45ea48c 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/LoggerLoader.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/LoggerLoader.java
@@ -18,35 +18,30 @@
  */
 package org.apache.syncope.core.logic.init;
 
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.sql.Timestamp;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.stream.Collectors;
 import javax.sql.DataSource;
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.core.Appender;
 import org.apache.logging.log4j.core.LoggerContext;
-import org.apache.logging.log4j.core.appender.db.ColumnMapping;
-import org.apache.logging.log4j.core.appender.db.jdbc.AbstractConnectionSource;
-import org.apache.logging.log4j.core.appender.db.jdbc.JdbcAppender;
 import org.apache.logging.log4j.core.appender.rewrite.RewriteAppender;
 import org.apache.logging.log4j.core.config.LoggerConfig;
 import org.apache.syncope.common.lib.types.AuditLoggerName;
 import org.apache.syncope.core.logic.audit.AuditAppender;
 import org.apache.syncope.core.logic.MemoryAppender;
+import org.apache.syncope.core.logic.audit.JdbcAuditAppender;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.apache.syncope.core.persistence.api.ImplementationLookup;
 import org.apache.syncope.core.persistence.api.SyncopeCoreLoader;
-import org.apache.syncope.core.persistence.api.dao.AuditDAO;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
 import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.beans.factory.support.AbstractBeanDefinition;
-import org.springframework.jdbc.datasource.DataSourceUtils;
 import org.springframework.stereotype.Component;
 
 @Component
@@ -58,6 +53,9 @@ public class LoggerLoader implements SyncopeCoreLoader {
     @Autowired
     private ImplementationLookup implementationLookup;
 
+    @Value("${enable.jdbcAuditAppender:true}")
+    private boolean enableJdbcAuditAppender;
+
     private final Map<String, MemoryAppender> memoryAppenders = new HashMap<>();
 
     @Override
@@ -65,70 +63,46 @@ public class LoggerLoader implements SyncopeCoreLoader {
         return 300;
     }
 
-    private static ColumnMapping[] buildColumnMappings(final LoggerContext ctx) {
-        return new ColumnMapping[] {
-            ColumnMapping.newBuilder().
-            setConfiguration(ctx.getConfiguration()).setName("EVENT_DATE").setType(Timestamp.class).build(),
-            ColumnMapping.newBuilder().
-            setConfiguration(ctx.getConfiguration()).setName("LOGGER_LEVEL").setPattern("%level").build(),
-            ColumnMapping.newBuilder().
-            setConfiguration(ctx.getConfiguration()).setName("LOGGER").setPattern("%logger").build(),
-            ColumnMapping.newBuilder().
-            setConfiguration(ctx.getConfiguration()).setName(AuditDAO.MESSAGE_COLUMN).setPattern("%message").build(),
-            ColumnMapping.newBuilder().
-            setConfiguration(ctx.getConfiguration()).setName("THROWABLE").setPattern("%ex{full}").build()
-        };
-    }
-
     @Override
     public void load(final String domain, final DataSource datasource) {
         LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
 
         ctx.getConfiguration().getAppenders().entrySet().stream().
-                filter(entry -> (entry.getValue() instanceof MemoryAppender)).
+                filter(entry -> entry.getValue() instanceof MemoryAppender).
                 forEach(entry -> memoryAppenders.put(entry.getKey(), (MemoryAppender) entry.getValue()));
 
-        // Audit table and DataSource for the given domain
-        Appender appender = ctx.getConfiguration().getAppender("audit_for_" + domain);
-        if (appender == null) {
-            appender = JdbcAppender.newBuilder().
-                    setName("audit_for_" + domain).
-                    setIgnoreExceptions(false).
-                    setConnectionSource(new DataSourceConnectionSource(domain, datasource)).
-                    setBufferSize(0).
-                    setTableName(AuditDAO.TABLE).
-                    setColumnMappings(buildColumnMappings(ctx)).
-                    build();
-            appender.start();
-            ctx.getConfiguration().addAppender(appender);
+        if (enableJdbcAuditAppender) {
+            JdbcAuditAppender jdbcAuditAppender = (JdbcAuditAppender) ApplicationContextProvider.getBeanFactory().
+                    createBean(JdbcAuditAppender.class, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, true);
+            jdbcAuditAppender.init(domain);
 
             LoggerConfig logConf = new LoggerConfig(AuditLoggerName.getAuditLoggerName(domain), null, false);
-            logConf.addAppender(appender, Level.DEBUG, null);
+            logConf.addAppender(jdbcAuditAppender.getTargetAppender(), Level.DEBUG, null);
             logConf.setLevel(Level.DEBUG);
             ctx.getConfiguration().addLogger(logConf.getName(), logConf);
-
-            // SYNCOPE-1144 For each custom audit appender class add related appenders to log4j logger
-            auditAppenders(domain).forEach(auditAppender -> auditAppender.getEvents().stream().
-                    map(event -> AuditLoggerName.getAuditEventLoggerName(domain, event.toLoggerName())).
-                    forEachOrdered(domainAuditLoggerName -> {
-                        LoggerConfig eventLogConf = ctx.getConfiguration().getLoggerConfig(domainAuditLoggerName);
-                        boolean isRootLogConf = LogManager.ROOT_LOGGER_NAME.equals(eventLogConf.getName());
-                        if (isRootLogConf) {
-                            eventLogConf = new LoggerConfig(domainAuditLoggerName, null, false);
-                        }
-                        addAppenderToContext(ctx, auditAppender, eventLogConf);
-                        eventLogConf.setLevel(Level.DEBUG);
-                        if (isRootLogConf) {
-                            ctx.getConfiguration().addLogger(domainAuditLoggerName, eventLogConf);
-                        }
-                    }));
-
-            AuthContextUtils.callAsAdmin(domain, () -> {
-                loggerAccessor.synchronizeLog4J(ctx);
-                return null;
-            });
         }
 
+        // SYNCOPE-1144 For each custom audit appender class add related appenders to log4j logger
+        auditAppenders(domain).forEach(auditAppender -> auditAppender.getEvents().stream().
+                map(event -> AuditLoggerName.getAuditEventLoggerName(domain, event.toLoggerName())).
+                forEach(domainAuditLoggerName -> {
+                    LoggerConfig eventLogConf = ctx.getConfiguration().getLoggerConfig(domainAuditLoggerName);
+                    boolean isRootLogConf = LogManager.ROOT_LOGGER_NAME.equals(eventLogConf.getName());
+                    if (isRootLogConf) {
+                        eventLogConf = new LoggerConfig(domainAuditLoggerName, null, false);
+                    }
+                    addAppenderToContext(ctx, auditAppender, eventLogConf);
+                    eventLogConf.setLevel(Level.DEBUG);
+                    if (isRootLogConf) {
+                        ctx.getConfiguration().addLogger(domainAuditLoggerName, eventLogConf);
+                    }
+                }));
+
+        AuthContextUtils.callAsAdmin(domain, () -> {
+            loggerAccessor.synchronizeLog4J(ctx);
+            return null;
+        });
+
         ctx.updateLoggers();
     }
 
@@ -145,8 +119,7 @@ public class LoggerLoader implements SyncopeCoreLoader {
             } else {
                 auditAppender = (AuditAppender) ApplicationContextProvider.getBeanFactory().
                         createBean(clazz, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, true);
-                auditAppender.setDomainName(domain);
-                auditAppender.init();
+                auditAppender.init(domain);
             }
             return auditAppender;
         }).collect(Collectors.toList());
@@ -163,39 +136,13 @@ public class LoggerLoader implements SyncopeCoreLoader {
         }
         targetAppender.start();
         ctx.getConfiguration().addAppender(targetAppender);
-        if (auditAppender.isRewriteEnabled()) {
-            RewriteAppender rewriteAppender = ctx.getConfiguration().getAppender(auditAppender.
-                    getTargetAppenderName() + "_rewrite");
-            if (rewriteAppender == null) {
-                rewriteAppender = auditAppender.getRewriteAppender();
-            }
-            rewriteAppender.start();
-            ctx.getConfiguration().addAppender(rewriteAppender);
-            eventLogConf.addAppender(rewriteAppender, Level.DEBUG, null);
+
+        Optional<RewriteAppender> rewriteAppender = auditAppender.getRewriteAppender();
+        if (rewriteAppender.isPresent()) {
+            rewriteAppender.get().start();
+            eventLogConf.addAppender(rewriteAppender.get(), Level.DEBUG, null);
         } else {
             eventLogConf.addAppender(targetAppender, Level.DEBUG, null);
         }
     }
-
-    private static class DataSourceConnectionSource extends AbstractConnectionSource {
-
-        private final String description;
-
-        private final DataSource dataSource;
-
-        DataSourceConnectionSource(final String domain, final DataSource dataSource) {
-            this.description = "dataSource{ domain=" + domain + ", value=" + dataSource + " }";
-            this.dataSource = dataSource;
-        }
-
-        @Override
-        public Connection getConnection() throws SQLException {
-            return DataSourceUtils.getConnection(dataSource);
-        }
-
-        @Override
-        public String toString() {
-            return this.description;
-        }
-    }
 }
diff --git a/core/idrepo/logic/src/main/resources/logic.properties b/core/idrepo/logic/src/main/resources/logic.properties
index 8256e4b..3da2e2c 100644
--- a/core/idrepo/logic/src/main/resources/logic.properties
+++ b/core/idrepo/logic/src/main/resources/logic.properties
@@ -18,3 +18,4 @@ version=${syncope.version}
 buildNumber=${buildNumber}
 logicInvocationHandler=org.apache.syncope.core.logic.LogicInvocationHandler
 classPathScanImplementationLookup=org.apache.syncope.core.logic.init.ClassPathScanImplementationLookup
+enable.jdbcAuditAppender=true
diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AuditServiceImpl.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AuditServiceImpl.java
deleted file mode 100644
index b646f91..0000000
--- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AuditServiceImpl.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.rest.cxf.service;
-
-import org.apache.commons.lang3.tuple.Pair;
-import org.apache.syncope.common.lib.to.AuditEntryTO;
-import org.apache.syncope.common.lib.to.PagedResult;
-import org.apache.syncope.common.rest.api.beans.AuditQuery;
-import org.apache.syncope.common.rest.api.service.AuditService;
-import org.apache.syncope.core.logic.AuditLogic;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-import java.util.List;
-
-@Service
-public class AuditServiceImpl extends AbstractServiceImpl implements AuditService {
-
-    @Autowired
-    private AuditLogic logic;
-
-    @Override
-    public PagedResult<AuditEntryTO> search(final AuditQuery auditQuery) {
-        Pair<Integer, List<AuditEntryTO>> result = logic.search(
-                auditQuery.getEntityKey(),
-                auditQuery.getPage(),
-                auditQuery.getSize(),
-                auditQuery.getType(),
-                auditQuery.getCategory(),
-                auditQuery.getSubcategory(),
-                auditQuery.getEvents(),
-                auditQuery.getResult(),
-                getOrderByClauses(auditQuery.getOrderBy()));
-        return buildPagedResult(result.getRight(), auditQuery.getPage(), auditQuery.getSize(), result.getLeft());
-    }
-}
diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/LoggerServiceImpl.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/LoggerServiceImpl.java
index a143403..b8ab5b4 100644
--- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/LoggerServiceImpl.java
+++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/LoggerServiceImpl.java
@@ -21,13 +21,17 @@ package org.apache.syncope.core.rest.cxf.service;
 import java.text.ParseException;
 import java.util.List;
 import javax.ws.rs.BadRequestException;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.log.AuditEntry;
 import org.apache.syncope.common.lib.log.EventCategory;
 import org.apache.syncope.common.lib.log.LogAppender;
 import org.apache.syncope.common.lib.log.LogStatement;
 import org.apache.syncope.common.lib.log.LoggerTO;
+import org.apache.syncope.common.lib.to.PagedResult;
 import org.apache.syncope.common.lib.types.AuditLoggerName;
 import org.apache.syncope.common.lib.types.LoggerType;
 import org.apache.syncope.common.rest.api.LoggerWrapper;
+import org.apache.syncope.common.rest.api.beans.AuditQuery;
 import org.apache.syncope.common.rest.api.service.LoggerService;
 import org.apache.syncope.core.logic.LoggerLogic;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -58,11 +62,11 @@ public class LoggerServiceImpl extends AbstractServiceImpl implements LoggerServ
 
             case AUDIT:
                 try {
-                    logic.disableAudit(AuditLoggerName.fromLoggerName(name));
-                } catch (IllegalArgumentException | ParseException e) {
-                    throw new BadRequestException(e);
-                }
-                break;
+                logic.disableAudit(AuditLoggerName.fromLoggerName(name));
+            } catch (IllegalArgumentException | ParseException e) {
+                throw new BadRequestException(e);
+            }
+            break;
 
             default:
                 throw new BadRequestException();
@@ -107,11 +111,11 @@ public class LoggerServiceImpl extends AbstractServiceImpl implements LoggerServ
 
             case AUDIT:
                 try {
-                    logic.enableAudit(AuditLoggerName.fromLoggerName(logger.getKey()));
-                } catch (Exception e) {
-                    throw new BadRequestException(e);
-                }
-                break;
+                logic.enableAudit(AuditLoggerName.fromLoggerName(logger.getKey()));
+            } catch (Exception e) {
+                throw new BadRequestException(e);
+            }
+            break;
 
             default:
                 throw new BadRequestException();
@@ -122,4 +126,20 @@ public class LoggerServiceImpl extends AbstractServiceImpl implements LoggerServ
     public List<EventCategory> events() {
         return logic.listAuditEvents();
     }
+
+    @Override
+    public PagedResult<AuditEntry> search(final AuditQuery auditQuery) {
+        Pair<Integer, List<AuditEntry>> result = logic.search(
+                auditQuery.getEntityKey(),
+                auditQuery.getPage(),
+                auditQuery.getSize(),
+                auditQuery.getType(),
+                auditQuery.getCategory(),
+                auditQuery.getSubcategory(),
+                auditQuery.getEvents(),
+                auditQuery.getResult(),
+                getOrderByClauses(auditQuery.getOrderBy()));
+
+        return buildPagedResult(result.getRight(), auditQuery.getPage(), auditQuery.getSize(), result.getLeft());
+    }
 }
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AuditDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AuditDAO.java
deleted file mode 100644
index 5a78787..0000000
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AuditDAO.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.persistence.api.dao;
-
-import java.util.List;
-
-import org.apache.syncope.common.lib.types.AuditElements;
-import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
-import org.apache.syncope.core.persistence.api.entity.AuditEntry;
-
-public interface AuditDAO {
-
-    String TABLE = "SYNCOPEAUDIT";
-
-    String MESSAGE_COLUMN = "MESSAGE";
-
-    List<AuditEntry> findByEntityKey(
-            String entityKey,
-            int page,
-            int size,
-            AuditElements.EventCategoryType type,
-            String category,
-            String subcategory,
-            List<String> events,
-            AuditElements.Result result,
-            List<OrderByClause> orderByClauses);
-
-    int count(String key);
-}
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/LoggerDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/LoggerDAO.java
index 55a5db7..e8cfe5a 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/LoggerDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/LoggerDAO.java
@@ -19,11 +19,18 @@
 package org.apache.syncope.core.persistence.api.dao;
 
 import java.util.List;
+import org.apache.syncope.common.lib.log.AuditEntry;
+import org.apache.syncope.common.lib.types.AuditElements;
 import org.apache.syncope.common.lib.types.LoggerType;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.syncope.core.persistence.api.entity.Logger;
 
 public interface LoggerDAO extends DAO<Logger> {
 
+    String AUDIT_TABLE = "SYNCOPEAUDIT";
+
+    String AUDIT_MESSAGE_COLUMN = "MESSAGE";
+
     Logger find(String key);
 
     List<Logger> findAll(LoggerType type);
@@ -33,4 +40,17 @@ public interface LoggerDAO extends DAO<Logger> {
     void delete(String key);
 
     void delete(Logger logger);
+
+    List<AuditEntry> findAuditEntries(
+            String entityKey,
+            int page,
+            int size,
+            AuditElements.EventCategoryType type,
+            String category,
+            String subcategory,
+            List<String> events,
+            AuditElements.Result result,
+            List<OrderByClause> orderByClauses);
+
+    int countAuditEntries(String entityKey);
 }
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AuditEntry.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AuditEntry.java
deleted file mode 100644
index 2b10e43..0000000
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AuditEntry.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.persistence.api.entity;
-
-import java.io.Serializable;
-import java.util.Date;
-import org.apache.syncope.common.lib.types.AuditLoggerName;
-
-public interface AuditEntry extends Serializable {
-
-    String getWho();
-
-    AuditLoggerName getLogger();
-
-    Object getBefore();
-
-    Object getOutput();
-
-    Object[] getInput();
-
-    String getThrowable();
-
-    Date getDate();
-}
diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAuditDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONLoggerDAO.java
similarity index 98%
rename from core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAuditDAO.java
rename to core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONLoggerDAO.java
index a921db1..3a7c1f3 100644
--- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAuditDAO.java
+++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONLoggerDAO.java
@@ -27,7 +27,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.types.AuditElements;
 import org.springframework.util.CollectionUtils;
 
-public abstract class AbstractJPAJSONAuditDAO extends JPAAuditDAO {
+public abstract class AbstractJPAJSONLoggerDAO extends JPALoggerDAO {
 
     private static final ObjectMapper MAPPER = new ObjectMapper();
 
diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAuditDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONLoggerDAO.java
similarity index 79%
rename from core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAuditDAO.java
rename to core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONLoggerDAO.java
index 020069b..bbffaed 100644
--- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAuditDAO.java
+++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONLoggerDAO.java
@@ -23,20 +23,20 @@ import java.util.List;
 import java.util.stream.Collectors;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 
-public class MyJPAJSONAuditDAO extends AbstractJPAJSONAuditDAO {
+public class MyJPAJSONLoggerDAO extends AbstractJPAJSONLoggerDAO {
 
     private static class MyMessageCriteriaBuilder extends JSONMessageCriteriaBuilder {
 
         @Override
         protected String doBuild(final List<ObjectNode> containers) {
-            query.append('(').append(MESSAGE_COLUMN).append(" -> '$.before' LIKE '%").append(entityKey).
-                    append("%' OR ").append(MESSAGE_COLUMN).append(" -> '$.input' LIKE '%").append(entityKey).
-                    append("%' OR ").append(MESSAGE_COLUMN).append(" -> '$.output' LIKE '%").append(entityKey).
+            query.append('(').append(AUDIT_MESSAGE_COLUMN).append(" -> '$.before' LIKE '%").append(entityKey).
+                    append("%' OR ").append(AUDIT_MESSAGE_COLUMN).append(" -> '$.input' LIKE '%").append(entityKey).
+                    append("%' OR ").append(AUDIT_MESSAGE_COLUMN).append(" -> '$.output' LIKE '%").append(entityKey).
                     append("%')");
 
             if (!containers.isEmpty()) {
                 query.append(" AND (").
-                        append(containers.stream().map(container -> "JSON_CONTAINS(" + MESSAGE_COLUMN + ", '"
+                        append(containers.stream().map(container -> "JSON_CONTAINS(" + AUDIT_MESSAGE_COLUMN + ", '"
                         + POJOHelper.serialize(container).replace("'", "''")
                         + "')").collect(Collectors.joining(" OR "))).
                         append(')');
diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAuditDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONLoggerDAO.java
similarity index 78%
rename from core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAuditDAO.java
rename to core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONLoggerDAO.java
index 54ea828..c9ee797 100644
--- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAuditDAO.java
+++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONLoggerDAO.java
@@ -21,23 +21,23 @@ package org.apache.syncope.core.persistence.jpa.dao;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import java.util.List;
 import java.util.stream.Collectors;
-import org.apache.syncope.core.persistence.jpa.dao.AbstractJPAJSONAuditDAO.JSONMessageCriteriaBuilder;
+import org.apache.syncope.core.persistence.jpa.dao.AbstractJPAJSONLoggerDAO.JSONMessageCriteriaBuilder;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 
-public class PGJPAJSONAuditDAO extends AbstractJPAJSONAuditDAO {
+public class PGJPAJSONLoggerDAO extends AbstractJPAJSONLoggerDAO {
 
     private static class PGMessageCriteriaBuilder extends JSONMessageCriteriaBuilder {
 
         @Override
         protected String doBuild(final List<ObjectNode> containers) {
-            query.append('(').append(MESSAGE_COLUMN).append(" ->> 'before' LIKE '%").append(entityKey).
-                    append("%' OR ").append(MESSAGE_COLUMN).append(" ->> 'input' LIKE '%").append(entityKey).
-                    append("%' OR ").append(MESSAGE_COLUMN).append(" ->> 'output' LIKE '%").append(entityKey).
+            query.append('(').append(AUDIT_MESSAGE_COLUMN).append(" ->> 'before' LIKE '%").append(entityKey).
+                    append("%' OR ").append(AUDIT_MESSAGE_COLUMN).append(" ->> 'input' LIKE '%").append(entityKey).
+                    append("%' OR ").append(AUDIT_MESSAGE_COLUMN).append(" ->> 'output' LIKE '%").append(entityKey).
                     append("%')");
 
             if (!containers.isEmpty()) {
                 query.append(" AND (").
-                        append(containers.stream().map(container -> MESSAGE_COLUMN + " @> '"
+                        append(containers.stream().map(container -> AUDIT_MESSAGE_COLUMN + " @> '"
                         + POJOHelper.serialize(container).replace("'", "''")
                         + "'::jsonb").collect(Collectors.joining(" OR "))).
                         append(')');
@@ -49,7 +49,7 @@ public class PGJPAJSONAuditDAO extends AbstractJPAJSONAuditDAO {
 
     @Override
     protected String select() {
-        return MESSAGE_COLUMN + "::text";
+        return AUDIT_MESSAGE_COLUMN + "::text";
     }
 
     @Override
diff --git a/core/persistence-jpa-json/src/main/resources/myjson/persistence.properties b/core/persistence-jpa-json/src/main/resources/myjson/persistence.properties
index 1a7d556..a2a38a9 100644
--- a/core/persistence-jpa-json/src/main/resources/myjson/persistence.properties
+++ b/core/persistence-jpa-json/src/main/resources/myjson/persistence.properties
@@ -24,5 +24,5 @@ any.search.visitor=org.apache.syncope.core.persistence.api.search.SearchCondVisi
 user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONUserDAO
 group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONGroupDAO
 anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONAnyObjectDAO
-audit.dao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONAuditDAO
+logger.dao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONLoggerDAO
 openjpa.RemoteCommitProvider=sjvm
diff --git a/core/persistence-jpa-json/src/main/resources/pgjsonb/persistence.properties b/core/persistence-jpa-json/src/main/resources/pgjsonb/persistence.properties
index d4a22e9..5bb728a 100644
--- a/core/persistence-jpa-json/src/main/resources/pgjsonb/persistence.properties
+++ b/core/persistence-jpa-json/src/main/resources/pgjsonb/persistence.properties
@@ -24,5 +24,5 @@ any.search.visitor=org.apache.syncope.core.persistence.api.search.SearchCondVisi
 user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONUserDAO
 group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONGroupDAO
 anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONAnyObjectDAO
-audit.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONAuditDAO
+logger.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONLoggerDAO
 openjpa.RemoteCommitProvider=sjvm
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java
index 91bd97a..9d48a90 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java
@@ -25,8 +25,8 @@ import javax.persistence.ValidationMode;
 import javax.validation.Validator;
 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
-import org.apache.syncope.core.persistence.api.dao.AuditDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+import org.apache.syncope.core.persistence.api.dao.LoggerDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainAttrDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainAttrValueDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
@@ -182,13 +182,13 @@ public class PersistenceContext implements EnvironmentAware {
         return (AnyObjectDAO) Class.forName(env.getProperty("anyObject.dao")).getConstructor().newInstance();
     }
 
-    @ConditionalOnMissingBean(name = "auditDAO")
+    @ConditionalOnMissingBean(name = "loggerDAO")
     @Bean
-    public AuditDAO auditDAO()
+    public LoggerDAO loggerDAO()
             throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException,
             IllegalArgumentException, InvocationTargetException {
 
-        return (AuditDAO) Class.forName(env.getProperty("audit.dao")).getConstructor().newInstance();
+        return (LoggerDAO) Class.forName(env.getProperty("logger.dao")).getConstructor().newInstance();
     }
 
     @Bean
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java
index 8015088..116e88d 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/XMLContentExporter.java
@@ -109,11 +109,11 @@ public class XMLContentExporter implements ContentExporter {
     private static final Logger LOG = LoggerFactory.getLogger(XMLContentExporter.class);
 
     private static final Set<String> TABLE_PREFIXES_TO_BE_EXCLUDED = SetUtils.hashSet(
-        "QRTZ_", "LOGGING", JPAReportExec.TABLE, JPATaskExec.TABLE,
-        JPAUser.TABLE, JPAUPlainAttr.TABLE, JPAUPlainAttrValue.TABLE, JPAUPlainAttrUniqueValue.TABLE,
-        JPAURelationship.TABLE, JPAUMembership.TABLE,
-        JPAAnyObject.TABLE, JPAAPlainAttr.TABLE, JPAAPlainAttrValue.TABLE, JPAAPlainAttrUniqueValue.TABLE,
-        JPAARelationship.TABLE, JPAAMembership.TABLE, JPAAccessToken.TABLE
+            "QRTZ_", "LOGGING", JPAReportExec.TABLE, JPATaskExec.TABLE,
+            JPAUser.TABLE, JPAUPlainAttr.TABLE, JPAUPlainAttrValue.TABLE, JPAUPlainAttrUniqueValue.TABLE,
+            JPAURelationship.TABLE, JPAUMembership.TABLE,
+            JPAAnyObject.TABLE, JPAAPlainAttr.TABLE, JPAAPlainAttrValue.TABLE, JPAAPlainAttrUniqueValue.TABLE,
+            JPAARelationship.TABLE, JPAAMembership.TABLE, JPAAccessToken.TABLE
     );
 
     private static final Map<String, String> TABLES_TO_BE_FILTERED =
@@ -134,7 +134,7 @@ public class XMLContentExporter implements ContentExporter {
     }
 
     private static List<String> sortByForeignKeys(final String dbSchema, final Connection conn,
-                                                  final Set<String> tableNames)
+            final Set<String> tableNames)
             throws SQLException {
 
         Set<MultiParentNode<String>> roots = new HashSet<>();
@@ -548,7 +548,7 @@ public class XMLContentExporter implements ContentExporter {
 
             EntityManagerFactory emf = EntityManagerFactoryUtils.findEntityManagerFactory(
                     ApplicationContextProvider.getBeanFactory(), domain);
-            Set<EntityType<?>> entityTypes = emf == null ? Collections.emptySet() : emf.getMetamodel().getEntities();
+            Set<EntityType<?>> entityTypes = emf == null ? Set.of() : emf.getMetamodel().getEntities();
             BidiMap<String, EntityType<?>> entities = entities(entityTypes);
 
             // then sort tables based on foreign keys and dump
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAuditDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAuditDAO.java
deleted file mode 100644
index be47a81..0000000
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAuditDAO.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.persistence.jpa.dao;
-
-import java.sql.Clob;
-import java.sql.SQLException;
-import java.util.List;
-import java.util.Objects;
-import java.util.stream.Collectors;
-import javax.persistence.Query;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.common.lib.types.AuditElements;
-import org.apache.syncope.core.persistence.api.dao.AuditDAO;
-import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
-import org.apache.syncope.core.persistence.api.entity.AuditEntry;
-import org.apache.syncope.core.persistence.jpa.entity.AbstractEntity;
-import org.apache.syncope.core.provisioning.api.AuditEntryImpl;
-import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
-import org.springframework.transaction.annotation.Transactional;
-
-public class JPAAuditDAO extends AbstractDAO<AbstractEntity> implements AuditDAO {
-
-    protected static class MessageCriteriaBuilder {
-
-        protected final StringBuilder query = new StringBuilder();
-
-        protected MessageCriteriaBuilder entityKey(final String entityKey) {
-            query.append(' ').append(MESSAGE_COLUMN).append(" LIKE '%\"key\":\"").append(entityKey).append("\"%'");
-            return this;
-        }
-
-        public MessageCriteriaBuilder type(final AuditElements.EventCategoryType type) {
-            if (type != null) {
-                query.append(" AND " + MESSAGE_COLUMN + " LIKE '%\"type\":\"").append(type.name()).append("\"%'");
-            }
-            return this;
-        }
-
-        public MessageCriteriaBuilder category(final String category) {
-            if (StringUtils.isNotBlank(category)) {
-                query.append(" AND " + MESSAGE_COLUMN + " LIKE '%\"category\":\"").append(category).append("\"%'");
-            }
-            return this;
-        }
-
-        public MessageCriteriaBuilder subcategory(final String subcategory) {
-            if (StringUtils.isNotBlank(subcategory)) {
-                query.append(" AND " + MESSAGE_COLUMN + " LIKE '%\"subcategory\":\"").
-                        append(subcategory).append("\"%'");
-            }
-            return this;
-        }
-
-        public MessageCriteriaBuilder events(final List<String> events) {
-            if (!events.isEmpty()) {
-                query.append(" AND ( ").
-                        append(events.stream().
-                                map(event -> MESSAGE_COLUMN + " LIKE '%\"event\":\"" + event + "\"%'").
-                                collect(Collectors.joining(" OR "))).
-                        append(" )");
-            }
-            return this;
-        }
-
-        public MessageCriteriaBuilder result(final AuditElements.Result result) {
-            if (result != null) {
-                query.append(" AND ").
-                        append(MESSAGE_COLUMN).append(" LIKE '%\"result\":\"").append(result.name()).append("\"%' ");
-            }
-            return this;
-        }
-
-        public String build() {
-            return query.toString();
-        }
-    }
-
-    protected MessageCriteriaBuilder messageCriteriaBuilder(final String entityKey) {
-        return new MessageCriteriaBuilder().entityKey(entityKey);
-    }
-
-    protected String select() {
-        return MESSAGE_COLUMN;
-    }
-
-    @Transactional(readOnly = true)
-    @Override
-    public List<AuditEntry> findByEntityKey(
-            final String entityKey,
-            final int page,
-            final int itemsPerPage,
-            final AuditElements.EventCategoryType type,
-            final String category,
-            final String subcategory,
-            final List<String> events,
-            final AuditElements.Result result,
-            final List<OrderByClause> orderByClauses) {
-
-        String queryString = "SELECT " + select()
-                + " FROM " + TABLE
-                + " WHERE " + messageCriteriaBuilder(entityKey).
-                        type(type).
-                        category(category).
-                        subcategory(subcategory).
-                        result(result).
-                        events(events).
-                        build();
-        if (!orderByClauses.isEmpty()) {
-            queryString += " ORDER BY " + orderByClauses.stream().
-                    map(orderBy -> orderBy.getField() + ' ' + orderBy.getDirection().name()).
-                    collect(Collectors.joining(","));
-        }
-
-        Query query = entityManager().createNativeQuery(queryString);
-        query.setFirstResult(itemsPerPage * (page <= 0 ? 0 : page - 1));
-        if (itemsPerPage >= 0) {
-            query.setMaxResults(itemsPerPage);
-        }
-
-        @SuppressWarnings("unchecked")
-        List<Object> entries = query.getResultList();
-        return entries.stream().map(row -> {
-            String value;
-            if (row instanceof Clob) {
-                Clob clob = (Clob) row;
-                try {
-                    value = clob.getSubString(1, (int) clob.length());
-                } catch (SQLException e) {
-                    LOG.error("Unexpected error reading Audit Entry for entity key {}", entityKey, e);
-                    return null;
-                }
-            } else {
-                value = row.toString();
-            }
-            return POJOHelper.deserialize(value, AuditEntryImpl.class);
-        }).filter(Objects::nonNull).collect(Collectors.toList());
-    }
-
-    @Override
-    public int count(final String key) {
-        String queryString = "SELECT COUNT(0) FROM " + TABLE
-                + " WHERE " + messageCriteriaBuilder(key).build();
-        Query countQuery = entityManager().createNativeQuery(queryString);
-
-        return ((Number) countQuery.getSingleResult()).intValue();
-    }
-}
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPALoggerDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPALoggerDAO.java
index 6e4f414..6e85952 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPALoggerDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPALoggerDAO.java
@@ -18,18 +18,85 @@
  */
 package org.apache.syncope.core.persistence.jpa.dao;
 
+import java.sql.Clob;
+import java.sql.SQLException;
 import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import javax.persistence.Query;
 import javax.persistence.TypedQuery;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.log.AuditEntry;
+import org.apache.syncope.common.lib.types.AuditElements;
 import org.apache.syncope.common.lib.types.LoggerLevel;
 import org.apache.syncope.common.lib.types.LoggerType;
 import org.apache.syncope.core.persistence.api.dao.LoggerDAO;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.syncope.core.persistence.api.entity.Logger;
 import org.apache.syncope.core.persistence.jpa.entity.JPALogger;
-import org.springframework.stereotype.Repository;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+import org.springframework.transaction.annotation.Transactional;
 
-@Repository
 public class JPALoggerDAO extends AbstractDAO<Logger> implements LoggerDAO {
 
+    protected static class MessageCriteriaBuilder {
+
+        protected final StringBuilder query = new StringBuilder();
+
+        protected MessageCriteriaBuilder entityKey(final String entityKey) {
+            query.append(' ').append(AUDIT_MESSAGE_COLUMN).
+                    append(" LIKE '%key%").append(entityKey).append("%'");
+            return this;
+        }
+
+        public MessageCriteriaBuilder type(final AuditElements.EventCategoryType type) {
+            if (type != null) {
+                query.append(" AND ").append(AUDIT_MESSAGE_COLUMN).
+                        append(" LIKE '%\"type\":\"").append(type.name()).append("\"%'");
+            }
+            return this;
+        }
+
+        public MessageCriteriaBuilder category(final String category) {
+            if (StringUtils.isNotBlank(category)) {
+                query.append(" AND ").append(AUDIT_MESSAGE_COLUMN).
+                        append(" LIKE '%\"category\":\"").append(category).append("\"%'");
+            }
+            return this;
+        }
+
+        public MessageCriteriaBuilder subcategory(final String subcategory) {
+            if (StringUtils.isNotBlank(subcategory)) {
+                query.append(" AND ").append(AUDIT_MESSAGE_COLUMN).
+                        append(" LIKE '%\"subcategory\":\"").append(subcategory).append("\"%'");
+            }
+            return this;
+        }
+
+        public MessageCriteriaBuilder events(final List<String> events) {
+            if (!events.isEmpty()) {
+                query.append(" AND ( ").
+                        append(events.stream().
+                                map(event -> AUDIT_MESSAGE_COLUMN + " LIKE '%\"event\":\"" + event + "\"%'").
+                                collect(Collectors.joining(" OR "))).
+                        append(" )");
+            }
+            return this;
+        }
+
+        public MessageCriteriaBuilder result(final AuditElements.Result result) {
+            if (result != null) {
+                query.append(" AND ").append(AUDIT_MESSAGE_COLUMN).
+                        append(" LIKE '%\"result\":\"").append(result.name()).append("\"%' ");
+            }
+            return this;
+        }
+
+        public String build() {
+            return query.toString();
+        }
+    }
+
     @Override
     public Logger find(final String key) {
         return entityManager().find(JPALogger.class, key);
@@ -66,4 +133,74 @@ public class JPALoggerDAO extends AbstractDAO<Logger> implements LoggerDAO {
 
         delete(logger);
     }
+
+    protected MessageCriteriaBuilder messageCriteriaBuilder(final String entityKey) {
+        return new MessageCriteriaBuilder().entityKey(entityKey);
+    }
+
+    @Override
+    public int countAuditEntries(final String entityKey) {
+        String queryString = "SELECT COUNT(0) FROM " + AUDIT_TABLE
+                + " WHERE " + messageCriteriaBuilder(entityKey).build();
+        Query countQuery = entityManager().createNativeQuery(queryString);
+
+        return ((Number) countQuery.getSingleResult()).intValue();
+    }
+
+    protected String select() {
+        return AUDIT_MESSAGE_COLUMN;
+    }
+
+    @Transactional(readOnly = true)
+    @Override
+    public List<AuditEntry> findAuditEntries(
+            final String entityKey,
+            final int page,
+            final int itemsPerPage,
+            final AuditElements.EventCategoryType type,
+            final String category,
+            final String subcategory,
+            final List<String> events,
+            final AuditElements.Result result,
+            final List<OrderByClause> orderByClauses) {
+
+        String queryString = "SELECT " + select()
+                + " FROM " + AUDIT_TABLE
+                + " WHERE " + messageCriteriaBuilder(entityKey).
+                        type(type).
+                        category(category).
+                        subcategory(subcategory).
+                        result(result).
+                        events(events).
+                        build();
+        if (!orderByClauses.isEmpty()) {
+            queryString += " ORDER BY " + orderByClauses.stream().
+                    map(orderBy -> orderBy.getField() + ' ' + orderBy.getDirection().name()).
+                    collect(Collectors.joining(","));
+        }
+
+        Query query = entityManager().createNativeQuery(queryString);
+        query.setFirstResult(itemsPerPage * (page <= 0 ? 0 : page - 1));
+        if (itemsPerPage >= 0) {
+            query.setMaxResults(itemsPerPage);
+        }
+
+        @SuppressWarnings("unchecked")
+        List<Object> entries = query.getResultList();
+        return entries.stream().map(row -> {
+            String value;
+            if (row instanceof Clob) {
+                Clob clob = (Clob) row;
+                try {
+                    value = clob.getSubString(1, (int) clob.length());
+                } catch (SQLException e) {
+                    LOG.error("Unexpected error reading Audit Entry for entity key {}", entityKey, e);
+                    return null;
+                }
+            } else {
+                value = row.toString();
+            }
+            return POJOHelper.deserialize(value, AuditEntry.class);
+        }).filter(Objects::nonNull).collect(Collectors.toList());
+    }
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractEntity.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractEntity.java
index 1dd570d..a65b3fb 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractEntity.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractEntity.java
@@ -18,12 +18,15 @@
  */
 package org.apache.syncope.core.persistence.jpa.entity;
 
+import com.fasterxml.jackson.annotation.JsonIdentityInfo;
+import com.fasterxml.jackson.annotation.ObjectIdGenerators;
 import java.util.Objects;
 import org.apache.syncope.core.persistence.api.entity.Entity;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "key")
 public abstract class AbstractEntity implements Entity {
 
     private static final long serialVersionUID = -9017214159540857901L;
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnInstance.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnInstance.java
index 5affad4..d44e081 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnInstance.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnInstance.java
@@ -246,5 +246,4 @@ public class JPAConnInstance extends AbstractGeneratedKeyEntity implements ConnI
         checkType(poolConf, JPAConnPoolConf.class);
         this.poolConf = (JPAConnPoolConf) poolConf;
     }
-
 }
diff --git a/core/persistence-jpa/src/main/resources/persistence.properties b/core/persistence-jpa/src/main/resources/persistence.properties
index ac6d8a8..b547e65 100644
--- a/core/persistence-jpa/src/main/resources/persistence.properties
+++ b/core/persistence-jpa/src/main/resources/persistence.properties
@@ -24,5 +24,5 @@ any.search.visitor=org.apache.syncope.core.persistence.api.search.SearchCondVisi
 user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO
 group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAGroupDAO
 anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAnyObjectDAO
-audit.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAuditDAO
+logger.dao=org.apache.syncope.core.persistence.jpa.dao.JPALoggerDAO
 openjpa.RemoteCommitProvider=sjvm
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AuditEntryImpl.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AuditEntryImpl.java
deleted file mode 100644
index 3d6b949..0000000
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AuditEntryImpl.java
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License; Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing;
- * software distributed under the License is distributed on an
- * "AS IS" BASIS; WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND; either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.provisioning.api;
-
-import java.util.Date;
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import org.apache.commons.lang3.ArrayUtils;
-import org.apache.commons.lang3.SerializationUtils;
-import org.apache.syncope.common.lib.request.UserCR;
-import org.apache.syncope.common.lib.request.UserUR;
-import org.apache.syncope.common.lib.to.UserTO;
-import org.apache.syncope.common.lib.types.AuditLoggerName;
-import org.apache.syncope.core.persistence.api.entity.AuditEntry;
-
-@SuppressWarnings({ "squid:S1450", "squid:S1948" })
-public class AuditEntryImpl implements AuditEntry {
-
-    private static final long serialVersionUID = -2299082316063743582L;
-
-    private static final String MASKED_VALUE = "<MASKED>";
-
-    private final String who;
-
-    private final AuditLoggerName logger;
-
-    private final Object before;
-
-    private final Object output;
-
-    private final Object[] input;
-
-    private String throwable;
-
-    private Date date;
-
-    @JsonCreator
-    public AuditEntryImpl(
-            @JsonProperty("who") final String who,
-            @JsonProperty("logger") final AuditLoggerName logger,
-            @JsonProperty("before") final Object before,
-            @JsonProperty("output") final Object output,
-            @JsonProperty("input") final Object[] input) {
-
-        super();
-
-        this.who = who;
-        this.logger = logger;
-        this.before = maskSensitive(before);
-        this.output = maskSensitive(output);
-        this.input = ArrayUtils.clone(input);
-        if (this.input != null) {
-            for (int i = 0; i < this.input.length; i++) {
-                this.input[i] = maskSensitive(this.input[i]);
-            }
-        }
-    }
-
-    private static Object maskSensitive(final Object object) {
-        Object masked;
-
-        if (object instanceof UserTO) {
-            masked = SerializationUtils.clone((UserTO) object);
-            if (((UserTO) masked).getPassword() != null) {
-                ((UserTO) masked).setPassword(MASKED_VALUE);
-            }
-            if (((UserTO) masked).getSecurityAnswer() != null) {
-                ((UserTO) masked).setSecurityAnswer(MASKED_VALUE);
-            }
-        } else if (object instanceof UserCR) {
-            masked = SerializationUtils.clone((UserCR) object);
-            if (((UserCR) masked).getPassword() != null) {
-                ((UserCR) masked).setPassword(MASKED_VALUE);
-            }
-            if (((UserCR) masked).getSecurityAnswer() != null) {
-                ((UserCR) masked).setSecurityAnswer(MASKED_VALUE);
-            }
-        } else if (object instanceof UserUR && ((UserUR) object).getPassword() != null) {
-            masked = SerializationUtils.clone((UserUR) object);
-            ((UserUR) masked).getPassword().setValue(MASKED_VALUE);
-        } else {
-            masked = object;
-        }
-
-        return masked;
-    }
-
-    @Override
-    public String getWho() {
-        return who;
-    }
-
-    @Override
-    public AuditLoggerName getLogger() {
-        return logger;
-    }
-
-    @Override
-    public Object getBefore() {
-        return before;
-    }
-
-    @Override
-    public Object getOutput() {
-        return output;
-    }
-
-    @Override
-    public Object[] getInput() {
-        return input;
-    }
-
-    @Override
-    public String getThrowable() {
-        return throwable;
-    }
-
-    public void setThrowable(final String throwable) {
-        this.throwable = throwable;
-    }
-
-    @Override
-    public Date getDate() {
-        return date;
-    }
-
-    public void setDate(final Date date) {
-        this.date = date;
-    }
-
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    public static final class Builder {
-
-        private String who;
-
-        private AuditLoggerName logger;
-
-        private Object before;
-
-        private Object output;
-
-        private Object[] input;
-
-        private String throwable;
-
-        private Date date;
-
-        private String key;
-
-        private Builder() {
-        }
-
-        public Builder date(final Date date) {
-            this.date = date;
-            return this;
-        }
-
-        public Builder throwable(final String throwable) {
-            this.throwable = throwable;
-            return this;
-        }
-
-        public Builder key(final String key) {
-            this.key = key;
-            return this;
-        }
-
-        public Builder who(final String who) {
-            this.who = who;
-            return this;
-        }
-
-        public Builder logger(final AuditLoggerName logger) {
-            this.logger = logger;
-            return this;
-        }
-
-        public Builder before(final Object before) {
-            this.before = before;
-            return this;
-        }
-
-        public Builder output(final Object output) {
-            this.output = output;
-            return this;
-        }
-
-        public Builder input(final Object[] input) {
-            this.input = input;
-            return this;
-        }
-
-        public AuditEntryImpl build() {
-            AuditEntryImpl entry = new AuditEntryImpl(who, logger, before, output, input);
-            entry.setDate(date);
-            entry.setThrowable(throwable);
-            return entry;
-        }
-    }
-}
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AuditDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/LoggerDataBinder.java
similarity index 73%
rename from core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AuditDataBinder.java
rename to core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/LoggerDataBinder.java
index 082f71f..1b491ce 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AuditDataBinder.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/LoggerDataBinder.java
@@ -18,10 +18,13 @@
  */
 package org.apache.syncope.core.provisioning.api.data;
 
-import org.apache.syncope.common.lib.to.AuditEntryTO;
-import org.apache.syncope.core.persistence.api.entity.AuditEntry;
+import org.apache.syncope.common.lib.log.LoggerTO;
+import org.apache.syncope.common.lib.types.AuditLoggerName;
+import org.apache.syncope.core.persistence.api.entity.Logger;
 
-public interface AuditDataBinder {
+public interface LoggerDataBinder {
 
-    AuditEntryTO getAuditTO(String key, AuditEntry auditEntry);
+    LoggerTO getLoggerTO(Logger logger);
+
+    LoggerTO getLoggerTO(AuditLoggerName auditLoggerName);
 }
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/POJOHelper.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/POJOHelper.java
index c7b8faa..af99906 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/POJOHelper.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/serialization/POJOHelper.java
@@ -64,18 +64,6 @@ public final class POJOHelper {
         return result;
     }
 
-    public static String serializeWithDefaultPrettyPrinter(final Object object) {
-        String result = null;
-
-        try {
-            result = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(object);
-        } catch (Exception e) {
-            LOG.error("During serialization", e);
-        }
-
-        return result;
-    }
-
     public static <T extends Object> T deserialize(final String serialized, final Class<T> reference) {
         T result = null;
 
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/AuditEntryImplTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/AuditEntryImplTest.java
deleted file mode 100644
index 42aff67..0000000
--- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/AuditEntryImplTest.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.provisioning.api;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import java.util.Date;
-import java.util.UUID;
-import org.apache.commons.lang3.SerializationUtils;
-import org.apache.commons.lang3.builder.EqualsBuilder;
-import org.apache.syncope.common.lib.request.UserUR;
-import org.apache.syncope.common.lib.to.UserTO;
-import org.apache.syncope.common.lib.types.AuditLoggerName;
-import org.junit.jupiter.api.Test;
-import org.mockito.Mock;
-import org.springframework.test.util.ReflectionTestUtils;
-
-public class AuditEntryImplTest extends AbstractTest {
-
-    @Mock
-    private AuditLoggerName logger;
-
-    private final String who = "testUser";
-
-    private final Object before = "before";
-
-    private final String output = "output";
-
-    private final String[] input = { "test1", "test2" };
-
-    private final String throwable = "throwable";
-
-    private final String key = UUID.randomUUID().toString();
-
-    private final Date date = new Date();
-
-    @Test
-    public void AuditEntryImpl() {
-        AuditEntryImpl auditEntryImpl = new AuditEntryImpl(who, logger, before, output, input);
-        AuditEntryImpl auditEntryImpl2 = AuditEntryImpl.builder().
-                who(who).
-                before(before).
-                logger(logger).
-                output(output).
-                input(null).
-                date(date).
-                key(key).
-                throwable(throwable).
-                build();
-
-        assertEquals(auditEntryImpl2.getWho(), auditEntryImpl.getWho());
-        assertEquals(auditEntryImpl2.getLogger(), auditEntryImpl.getLogger());
-        assertNotEquals(auditEntryImpl2.getInput(), auditEntryImpl.getInput().length);
-        assertEquals(auditEntryImpl2.getDate(), auditEntryImpl2.getDate());
-        assertEquals(auditEntryImpl2.getThrowable(), auditEntryImpl2.getThrowable());
-    }
-
-    @Test
-    public void AuditEntryImplWithUserTO(@Mock UserTO userTO) {
-        AuditEntryImpl auditEntryImpl = new AuditEntryImpl(who, logger, before, userTO, input);
-        assertTrue(EqualsBuilder.reflectionEquals(SerializationUtils.clone(userTO), auditEntryImpl.getOutput()));
-
-        ReflectionTestUtils.setField(userTO, "password", "testP4ssw0rd!");
-        ReflectionTestUtils.setField(userTO, "securityAnswer", "42");
-        AuditEntryImpl auditEntryImpl2 = new AuditEntryImpl(who, logger, before, userTO, input);
-        assertFalse(EqualsBuilder.reflectionEquals(SerializationUtils.clone(userTO), auditEntryImpl2.getOutput()));
-    }
-
-    @Test
-    public void AuditEntryImplWithUserPatch(@Mock UserUR userUR) {
-        AuditEntryImpl auditEntryImpl = new AuditEntryImpl(who, logger, userUR, output, input);
-        assertTrue(EqualsBuilder.reflectionEquals(SerializationUtils.clone(userUR), auditEntryImpl.getBefore()));
-    }
-}
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/PropagationByResourceTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/PropagationByResourceTest.java
index 5671b32..65e6228 100644
--- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/PropagationByResourceTest.java
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/PropagationByResourceTest.java
@@ -26,7 +26,6 @@ import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -156,15 +155,15 @@ public class PropagationByResourceTest extends AbstractTest {
 
         ReflectionTestUtils.setField(propagationByResource, "toBeDeleted", matchingList);
         assertEquals(matchingList, propagationByResource.get(ResourceOperation.DELETE));
-        assertEquals(Collections.<String>emptySet(), propagationByResource.get(ResourceOperation.CREATE));
-        assertEquals(Collections.<String>emptySet(), propagationByResource.get(ResourceOperation.UPDATE));
-        assertEquals(Collections.<String>emptySet(), propagationByResource.get(ResourceOperation.NONE));
+        assertEquals(Set.of(), propagationByResource.get(ResourceOperation.CREATE));
+        assertEquals(Set.of(), propagationByResource.get(ResourceOperation.UPDATE));
+        assertEquals(Set.of(), propagationByResource.get(ResourceOperation.NONE));
 
     }
 
     @Test
     public void asMap() {
-        assertEquals(Collections.emptyMap(), propagationByResource.asMap());
+        assertEquals(Map.of(), propagationByResource.asMap());
     }
 
     @Test
@@ -173,8 +172,8 @@ public class PropagationByResourceTest extends AbstractTest {
         keys.add("testKey1");
         keys.add("testKey2");
 
-        propagationByResource.set(ResourceOperation.CREATE, Collections.<String>emptySet());
-        assertEquals(Collections.emptySet(), ReflectionTestUtils.getField(propagationByResource, "toBeCreated"));
+        propagationByResource.set(ResourceOperation.CREATE, Set.of());
+        assertEquals(Set.of(), ReflectionTestUtils.getField(propagationByResource, "toBeCreated"));
         propagationByResource.set(ResourceOperation.CREATE, keys);
         assertEquals(keys, ReflectionTestUtils.getField(propagationByResource, "toBeCreated"));
         propagationByResource.set(ResourceOperation.UPDATE, keys);
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/cache/VirAttrCacheValueTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/cache/VirAttrCacheValueTest.java
index c4ee199..561d53b 100644
--- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/cache/VirAttrCacheValueTest.java
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/cache/VirAttrCacheValueTest.java
@@ -23,7 +23,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
-import java.util.Collections;
+import java.util.List;
 import org.apache.syncope.core.provisioning.api.AbstractTest;
 import org.junit.jupiter.api.Test;
 
@@ -32,8 +32,8 @@ public class VirAttrCacheValueTest extends AbstractTest {
     @Test
     public void test() {
         Object nullObj = null;
-        VirAttrCacheValue cacheValue = new VirAttrCacheValue(Collections.singletonList("testValue"));
-        VirAttrCacheValue cacheValue2 = new VirAttrCacheValue(Collections.emptyList());
+        VirAttrCacheValue cacheValue = new VirAttrCacheValue(List.of("testValue"));
+        VirAttrCacheValue cacheValue2 = new VirAttrCacheValue(List.of());
 
         cacheValue.forceExpiring();
         cacheValue2.forceExpiring();
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeserializerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeserializerTest.java
index 4d8c578..a9748ce 100644
--- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeserializerTest.java
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeDeserializerTest.java
@@ -26,7 +26,9 @@ import com.fasterxml.jackson.databind.DeserializationContext;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.Collections;
+import java.util.List;
 import org.apache.syncope.core.provisioning.api.AbstractTest;
 import org.identityconnectors.framework.common.objects.Attribute;
 import org.junit.jupiter.api.BeforeEach;
@@ -62,7 +64,7 @@ public class AttributeDeserializerTest extends AbstractTest {
         when(jp.readValueAsTree()).thenReturn(tree);
         when(tree.get("name")).thenReturn(node2);
         when(tree.get("value")).thenReturn(node);
-        when(node.iterator()).thenReturn(Collections.singletonList(node).iterator());
+        when(node.iterator()).thenReturn(List.of(node).iterator());
     }
 
     @Test
@@ -81,7 +83,7 @@ public class AttributeDeserializerTest extends AbstractTest {
         when(node.asBoolean()).thenReturn(Boolean.TRUE);
         attr = deserializer.deserialize(jp, ct);
         assertEquals(name, attr.getName());
-        assertEquals(Collections.singletonList(Boolean.TRUE.toString()).get(0), attr.getValue().get(0));
+        assertEquals(List.of(Boolean.TRUE.toString()).get(0), attr.getValue().get(0));
     }
 
     @Test
@@ -93,7 +95,7 @@ public class AttributeDeserializerTest extends AbstractTest {
         when(node.asDouble()).thenReturn(number);
         attr = deserializer.deserialize(jp, ct);
         assertEquals(name, attr.getName());
-        assertEquals(Collections.singletonList(number).get(0), attr.getValue().get(0));
+        assertEquals(List.of(number).get(0), attr.getValue().get(0));
     }
 
     @Test
@@ -105,7 +107,7 @@ public class AttributeDeserializerTest extends AbstractTest {
         when(node.asLong()).thenReturn(number);
         attr = deserializer.deserialize(jp, ct);
         assertEquals(name, attr.getName());
-        assertEquals(Collections.singletonList(number.toString()).get(0), attr.getValue().get(0));
+        assertEquals(List.of(number.toString()).get(0), attr.getValue().get(0));
     }
 
     @Test
@@ -116,7 +118,7 @@ public class AttributeDeserializerTest extends AbstractTest {
         when(node.asInt()).thenReturn(number);
         attr = deserializer.deserialize(jp, ct);
         assertEquals(attr.getName(), name);
-        assertEquals(Collections.singletonList(number.toString()).get(0), attr.getValue().get(0));
+        assertEquals(List.of(number.toString()).get(0), attr.getValue().get(0));
     }
 
     @Test
@@ -126,6 +128,6 @@ public class AttributeDeserializerTest extends AbstractTest {
         when(node.asText()).thenReturn(text);
         attr = deserializer.deserialize(jp, ct);
         assertEquals(attr.getName(), name);
-        assertEquals(Collections.singletonList(text).get(0), attr.getValue().get(0));
+        assertEquals(List.of(text).get(0), attr.getValue().get(0));
     }
 }
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeSerializerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeSerializerTest.java
index 36f25a4..0a62afd 100644
--- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeSerializerTest.java
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/AttributeSerializerTest.java
@@ -31,7 +31,7 @@ import static org.mockito.Mockito.when;
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.databind.SerializerProvider;
 import java.io.IOException;
-import java.util.Collections;
+import java.util.List;
 import org.apache.syncope.core.provisioning.api.AbstractTest;
 import org.identityconnectors.common.security.GuardedString;
 import org.identityconnectors.framework.common.objects.Attribute;
@@ -50,31 +50,31 @@ public class AttributeSerializerTest extends AbstractTest {
         verify(jgen).writeFieldName("value");
         verify(jgen).writeNull();
 
-        when(source.getValue()).thenAnswer(ic -> Collections.singletonList(new GuardedString()));
+        when(source.getValue()).thenAnswer(ic -> List.of(new GuardedString()));
         serializer.serialize(source, jgen, sp);
         verify(jgen).writeObject(any(GuardedString.class));
 
-        when(source.getValue()).thenAnswer(ic -> Collections.singletonList(9000));
+        when(source.getValue()).thenAnswer(ic -> List.of(9000));
         serializer.serialize(source, jgen, sp);
         verify(jgen).writeNumber(anyInt());
 
-        when(source.getValue()).thenAnswer(ic -> Collections.singletonList(9000L));
+        when(source.getValue()).thenAnswer(ic -> List.of(9000L));
         serializer.serialize(source, jgen, sp);
         verify(jgen).writeNumber(anyLong());
 
-        when(source.getValue()).thenAnswer(ic -> Collections.singletonList(9000.1));
+        when(source.getValue()).thenAnswer(ic -> List.of(9000.1));
         serializer.serialize(source, jgen, sp);
         verify(jgen).writeNumber(anyDouble());
 
-        when(source.getValue()).thenAnswer(ic -> Collections.singletonList(Boolean.TRUE));
+        when(source.getValue()).thenAnswer(ic -> List.of(Boolean.TRUE));
         serializer.serialize(source, jgen, sp);
         verify(jgen).writeBoolean(anyBoolean());
 
-        when(source.getValue()).thenAnswer(ic -> Collections.singletonList(new byte[] { 9, 0, 0, 0 }));
+        when(source.getValue()).thenAnswer(ic -> List.of(new byte[] { 9, 0, 0, 0 }));
         serializer.serialize(source, jgen, sp);
         verify(jgen).writeString(anyString());
 
-        when(source.getValue()).thenAnswer(ic -> Collections.singletonList("test"));
+        when(source.getValue()).thenAnswer(ic -> List.of("test"));
         serializer.serialize(source, jgen, sp);
         verify(jgen).writeString(eq("test"));
     }
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/POJOHelperTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/POJOHelperTest.java
index d94a87e..e3d83dd 100644
--- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/POJOHelperTest.java
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/serialization/POJOHelperTest.java
@@ -36,13 +36,6 @@ public class POJOHelperTest extends AbstractTest {
     }
 
     @Test
-    public void serializeWithDefaultPrettyPrinter() {
-        Object object = 9001;
-
-        assertEquals(String.valueOf(object), POJOHelper.serializeWithDefaultPrettyPrinter(object));
-    }
-
-    @Test
     public void deserializeWithClassReference() {
         String serialized = "false";
 
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAuditManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAuditManager.java
index 374c717..1610019 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAuditManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAuditManager.java
@@ -18,8 +18,14 @@
  */
 package org.apache.syncope.core.provisioning.java;
 
-import org.apache.syncope.core.persistence.api.entity.AuditEntry;
-import org.apache.syncope.core.provisioning.api.AuditEntryImpl;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.stream.Collectors;
+import org.apache.commons.lang3.SerializationUtils;
+import org.apache.syncope.common.lib.log.AuditEntry;
+import org.apache.syncope.common.lib.request.UserCR;
+import org.apache.syncope.common.lib.request.UserUR;
+import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.core.provisioning.api.AuditManager;
 import org.apache.syncope.common.lib.types.AuditElements;
 import org.apache.syncope.common.lib.types.AuditElements.Result;
@@ -28,6 +34,7 @@ import org.apache.syncope.common.lib.types.LoggerLevel;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 import org.apache.syncope.core.persistence.api.dao.LoggerDAO;
 import org.apache.syncope.core.provisioning.api.event.AfterHandlingEvent;
+import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -35,11 +42,40 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 
-import java.util.Date;
-
 @Transactional(readOnly = true)
 public class DefaultAuditManager implements AuditManager {
 
+    private static final String MASKED_VALUE = "<MASKED>";
+
+    private static Object maskSensitive(final Object object) {
+        Object masked;
+
+        if (object instanceof UserTO) {
+            masked = SerializationUtils.clone((UserTO) object);
+            if (((UserTO) masked).getPassword() != null) {
+                ((UserTO) masked).setPassword(MASKED_VALUE);
+            }
+            if (((UserTO) masked).getSecurityAnswer() != null) {
+                ((UserTO) masked).setSecurityAnswer(MASKED_VALUE);
+            }
+        } else if (object instanceof UserCR) {
+            masked = SerializationUtils.clone((UserCR) object);
+            if (((UserCR) masked).getPassword() != null) {
+                ((UserCR) masked).setPassword(MASKED_VALUE);
+            }
+            if (((UserCR) masked).getSecurityAnswer() != null) {
+                ((UserCR) masked).setSecurityAnswer(MASKED_VALUE);
+            }
+        } else if (object instanceof UserUR && ((UserUR) object).getPassword() != null) {
+            masked = SerializationUtils.clone((UserUR) object);
+            ((UserUR) masked).getPassword().setValue(MASKED_VALUE);
+        } else {
+            masked = object;
+        }
+
+        return masked;
+    }
+
     @Autowired
     private LoggerDAO loggerDAO;
 
@@ -51,11 +87,11 @@ public class DefaultAuditManager implements AuditManager {
             final String subcategory,
             final String event) {
 
-        AuditEntry auditEntry = AuditEntryImpl.builder().
-                who(who).
-                logger(new AuditLoggerName(type, category, subcategory, event, Result.SUCCESS)).
-                date(new Date()).
-                build();
+        AuditEntry auditEntry = new AuditEntry();
+        auditEntry.setWho(who);
+        auditEntry.setLogger(new AuditLoggerName(type, category, subcategory, event, Result.SUCCESS));
+        auditEntry.setDate(new Date());
+
         org.apache.syncope.core.persistence.api.entity.Logger syncopeLogger =
                 loggerDAO.find(auditEntry.getLogger().toLoggerName());
         boolean auditRequested = syncopeLogger != null && syncopeLogger.getLevel() == LoggerLevel.DEBUG;
@@ -64,11 +100,8 @@ public class DefaultAuditManager implements AuditManager {
             return true;
         }
 
-        auditEntry = AuditEntryImpl.builder()
-                .who(who)
-                .logger(new AuditLoggerName(type, category, subcategory, event, Result.FAILURE))
-                .date(new Date())
-                .build();
+        auditEntry.setLogger(new AuditLoggerName(type, category, subcategory, event, Result.FAILURE));
+
         syncopeLogger = loggerDAO.find(auditEntry.getLogger().toLoggerName());
         auditRequested = syncopeLogger != null && syncopeLogger.getLevel() == LoggerLevel.DEBUG;
 
@@ -108,14 +141,22 @@ public class DefaultAuditManager implements AuditManager {
             throwable = (Throwable) output;
         }
 
-        AuditEntry auditEntry = AuditEntryImpl.builder().
-                who(who).
-                logger(new AuditLoggerName(type, category, subcategory, event, condition)).
-                before(before).
-                output(throwable == null ? output : throwable.getMessage()).
-                input(input).
-                date(new Date()).
-                build();
+        AuditEntry auditEntry = new AuditEntry();
+        auditEntry.setWho(who);
+        auditEntry.setLogger(new AuditLoggerName(type, category, subcategory, event, condition));
+        auditEntry.setDate(new Date());
+        auditEntry.setBefore(POJOHelper.serialize((maskSensitive(before))));
+        if (throwable == null) {
+            auditEntry.setOutput(POJOHelper.serialize((maskSensitive(output))));
+        } else {
+            auditEntry.setOutput(throwable.getMessage());
+            auditEntry.setThrowable(ExceptionUtils2.getFullStackTrace(throwable));
+        }
+        if (input != null) {
+            auditEntry.getInputs().addAll(Arrays.stream(input).
+                    map(DefaultAuditManager::maskSensitive).map(POJOHelper::serialize).
+                    collect(Collectors.toList()));
+        }
 
         org.apache.syncope.core.persistence.api.entity.Logger syncopeLogger =
                 loggerDAO.find(auditEntry.getLogger().toLoggerName());
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuditDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuditDataBinderImpl.java
deleted file mode 100644
index 4937503..0000000
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuditDataBinderImpl.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.provisioning.java.data;
-
-import java.util.Arrays;
-import java.util.Objects;
-import java.util.stream.Collectors;
-import org.apache.syncope.common.lib.to.AuditEntryTO;
-import org.apache.syncope.core.persistence.api.entity.AuditEntry;
-import org.apache.syncope.core.provisioning.api.data.AuditDataBinder;
-import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
-import org.springframework.stereotype.Component;
-
-@Component
-public class AuditDataBinderImpl implements AuditDataBinder {
-
-    @Override
-    public AuditEntryTO getAuditTO(final String key, final AuditEntry auditEntry) {
-        AuditEntryTO auditEntryTO = new AuditEntryTO();
-        auditEntryTO.setKey(key);
-        auditEntryTO.setWho(auditEntry.getWho());
-        auditEntryTO.setDate(auditEntry.getDate());
-        auditEntryTO.setThrowable(auditEntry.getThrowable());
-        auditEntryTO.setLoggerName(auditEntry.getLogger().toLoggerName());
-
-        auditEntryTO.setSubCategory(auditEntry.getLogger().getSubcategory());
-        auditEntryTO.setEvent(auditEntry.getLogger().getEvent());
-
-        if (auditEntry.getLogger().getResult() != null) {
-            auditEntryTO.setResult(auditEntry.getLogger().getResult().name());
-        }
-
-        if (auditEntry.getBefore() != null) {
-            auditEntryTO.setBefore(POJOHelper.serializeWithDefaultPrettyPrinter(auditEntry.getBefore()));
-        }
-
-        if (auditEntry.getInput() != null) {
-            auditEntryTO.getInputs().addAll(Arrays.stream(auditEntry.getInput()).
-                    filter(Objects::nonNull).
-                    map(POJOHelper::serializeWithDefaultPrettyPrinter).
-                    collect(Collectors.toList()));
-        }
-
-        if (auditEntry.getOutput() != null) {
-            auditEntryTO.setOutput(POJOHelper.serializeWithDefaultPrettyPrinter(auditEntry.getOutput()));
-        }
-
-        return auditEntryTO;
-    }
-}
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/audit/DefaultAuditAppender.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/LoggerDataBinderImpl.java
similarity index 51%
copy from core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/audit/DefaultAuditAppender.java
copy to core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/LoggerDataBinderImpl.java
index 45b28a3..906c92d 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/audit/DefaultAuditAppender.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/LoggerDataBinderImpl.java
@@ -16,38 +16,31 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.logic.audit;
+package org.apache.syncope.core.provisioning.java.data;
 
-import java.util.Set;
-import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy;
+import org.apache.syncope.common.lib.log.LoggerTO;
 import org.apache.syncope.common.lib.types.AuditLoggerName;
-import org.apache.syncope.core.logic.AbstractAuditAppender;
+import org.apache.syncope.common.lib.types.LoggerLevel;
+import org.apache.syncope.core.persistence.api.entity.Logger;
+import org.springframework.stereotype.Component;
+import org.apache.syncope.core.provisioning.api.data.LoggerDataBinder;
 
-/**
- * Default (abstract) implementation of custom audit appender.
- * It is bound to an empty collection of events, i.e. it does not create any logger.
- * This class shall be extended by non-rewriting appenders; for rewriting, extend
- * {@link DefaultRewriteAuditAppender} instead.
- */
-public abstract class DefaultAuditAppender extends AbstractAuditAppender {
-
-    @Override
-    public void init() {
-        initTargetAppender();
-    }
+@Component
+public class LoggerDataBinderImpl implements LoggerDataBinder {
 
     @Override
-    public Set<AuditLoggerName> getEvents() {
-        return Set.of();
+    public LoggerTO getLoggerTO(final Logger logger) {
+        LoggerTO loggerTO = new LoggerTO();
+        loggerTO.setKey(logger.getKey());
+        loggerTO.setLevel(logger.getLevel());
+        return loggerTO;
     }
 
     @Override
-    public void initRewriteAppender() {
+    public LoggerTO getLoggerTO(final AuditLoggerName auditLoggerName) {
+        LoggerTO loggerTO = new LoggerTO();
+        loggerTO.setKey(auditLoggerName.toLoggerName());
+        loggerTO.setLevel(LoggerLevel.DEBUG);
+        return loggerTO;
     }
-
-    @Override
-    public RewritePolicy getRewritePolicy() {
-        return null;
-    }
-
 }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AuditReportlet.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AuditReportlet.java
index a0632d4..7a67647 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AuditReportlet.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AuditReportlet.java
@@ -23,12 +23,11 @@ import java.util.Map;
 import java.util.concurrent.atomic.AtomicReference;
 import javax.sql.DataSource;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.log.AuditEntry;
 import org.apache.syncope.common.lib.report.AuditReportletConf;
 import org.apache.syncope.common.lib.report.ReportletConf;
 import org.apache.syncope.core.persistence.api.DomainHolder;
-import org.apache.syncope.core.persistence.api.dao.AuditDAO;
-import org.apache.syncope.core.persistence.api.entity.AuditEntry;
-import org.apache.syncope.core.provisioning.api.AuditEntryImpl;
+import org.apache.syncope.core.persistence.api.dao.LoggerDAO;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
@@ -49,17 +48,17 @@ public class AuditReportlet extends AbstractReportlet {
     private DataSource datasource;
 
     private void doExtractConf(final ContentHandler handler, final AtomicReference<String> status) throws SAXException {
-        status.set("Fetching " + conf.getSize() + " rows from the " + AuditDAO.TABLE + " table");
+        status.set("Fetching " + conf.getSize() + " rows from the " + LoggerDAO.AUDIT_TABLE + " table");
 
         JdbcTemplate jdbcTemplate = new JdbcTemplate(datasource);
         jdbcTemplate.setMaxRows(conf.getSize());
         List<Map<String, Object>> rows = jdbcTemplate.
-                queryForList("SELECT * FROM " + AuditDAO.TABLE + " ORDER BY EVENT_DATE DESC");
+                queryForList("SELECT * FROM " + LoggerDAO.AUDIT_TABLE + " ORDER BY EVENT_DATE DESC");
 
         handler.startElement("", "", "events", null);
         AttributesImpl atts = new AttributesImpl();
         for (Map<String, Object> row : rows) {
-            AuditEntry auditEntry = POJOHelper.deserialize(row.get("MESSAGE").toString(), AuditEntryImpl.class);
+            AuditEntry auditEntry = POJOHelper.deserialize(row.get("MESSAGE").toString(), AuditEntry.class);
 
             atts.clear();
             if (StringUtils.isNotBlank(auditEntry.getWho())) {
@@ -94,12 +93,11 @@ public class AuditReportlet extends AbstractReportlet {
                 handler.endElement("", "", "before");
             }
 
-            if (auditEntry.getInput() != null) {
+            if (!auditEntry.getInputs().isEmpty()) {
                 handler.startElement("", "", "inputs", null);
-                for (Object inputObj : auditEntry.getInput()) {
-                    char[] input = POJOHelper.serialize(inputObj).toCharArray();
+                for (String input : auditEntry.getInputs()) {
                     handler.startElement("", "", "input", null);
-                    handler.characters(input, 0, input.length);
+                    handler.characters(input.toCharArray(), 0, input.length());
                     handler.endElement("", "", "input");
                 }
                 handler.endElement("", "", "inputs");
diff --git a/docker/core/src/main/resources/persistence.properties.all b/docker/core/src/main/resources/persistence.properties.all
index 583c689..4808aa0 100644
--- a/docker/core/src/main/resources/persistence.properties.all
+++ b/docker/core/src/main/resources/persistence.properties.all
@@ -24,5 +24,5 @@ any.search.visitor=org.apache.syncope.core.persistence.api.search.SearchCondVisi
 user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO
 group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAGroupDAO
 anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAnyObjectDAO
-audit.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAuditDAO
+logger.dao=org.apache.syncope.core.persistence.jpa.dao.JPALoggerDAO
 openjpa.RemoteCommitProvider=${OPENJPA_REMOTE_COMMIT}
diff --git a/docker/core/src/main/resources/persistence.properties.myjson b/docker/core/src/main/resources/persistence.properties.myjson
index dc034b6..20b19a8 100644
--- a/docker/core/src/main/resources/persistence.properties.myjson
+++ b/docker/core/src/main/resources/persistence.properties.myjson
@@ -25,5 +25,5 @@ user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONUserDAO
 group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONGroupDAO
 anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONAnyObjectDAO
 conf.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONConfDAO
-audit.dao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONAuditDAO
+logger.dao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONLoggerDAO
 openjpa.RemoteCommitProvider=sjvm
diff --git a/docker/core/src/main/resources/persistence.properties.pgjsonb b/docker/core/src/main/resources/persistence.properties.pgjsonb
index fe9250c..238a10b 100644
--- a/docker/core/src/main/resources/persistence.properties.pgjsonb
+++ b/docker/core/src/main/resources/persistence.properties.pgjsonb
@@ -24,5 +24,5 @@ any.search.visitor=org.apache.syncope.core.persistence.api.search.SearchCondVisi
 user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONUserDAO
 group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONGroupDAO
 anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONAnyObjectDAO
-audit.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONAuditDAO
+logger.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONLoggerDAO
 openjpa.RemoteCommitProvider=${OPENJPA_REMOTE_COMMIT}
diff --git a/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties b/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties
index 82b0b51..6a968d8 100644
--- a/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties
+++ b/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties
@@ -24,5 +24,5 @@ any.search.visitor=org.apache.syncope.core.persistence.api.search.SearchCondVisi
 user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO
 group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAGroupDAO
 anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAnyObjectDAO
-audit.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAuditDAO
+logger.dao=org.apache.syncope.core.persistence.jpa.dao.JPALoggerDAO
 openjpa.RemoteCommitProvider=sjvm
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/SyslogRewriteAuditAppender.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/SyslogRewriteAuditAppender.java
index 0f9fa20..6398777 100644
--- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/SyslogRewriteAuditAppender.java
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/SyslogRewriteAuditAppender.java
@@ -35,46 +35,41 @@ public class SyslogRewriteAuditAppender extends DefaultRewriteAuditAppender {
     @Override
     public Set<AuditLoggerName> getEvents() {
         Set<AuditLoggerName> events = new HashSet<>();
-        events.add(
-                new AuditLoggerName(
-                        AuditElements.EventCategoryType.LOGIC,
-                        ResourceLogic.class.getSimpleName(),
-                        null,
-                        "update",
-                        AuditElements.Result.SUCCESS));
-        events.add(
-                new AuditLoggerName(
-                        AuditElements.EventCategoryType.LOGIC,
-                        ConnectorLogic.class.getSimpleName(),
-                        null,
-                        "update",
-                        AuditElements.Result.SUCCESS));
-        events.add(
-                new AuditLoggerName(
-                        AuditElements.EventCategoryType.LOGIC,
-                        ResourceLogic.class.getSimpleName(),
-                        null,
-                        "delete",
-                        AuditElements.Result.SUCCESS));
+        events.add(new AuditLoggerName(
+                AuditElements.EventCategoryType.LOGIC,
+                ResourceLogic.class.getSimpleName(),
+                null,
+                "update",
+                AuditElements.Result.SUCCESS));
+        events.add(new AuditLoggerName(
+                AuditElements.EventCategoryType.LOGIC,
+                ConnectorLogic.class.getSimpleName(),
+                null,
+                "update",
+                AuditElements.Result.SUCCESS));
+        events.add(new AuditLoggerName(
+                AuditElements.EventCategoryType.LOGIC,
+                ResourceLogic.class.getSimpleName(),
+                null,
+                "delete",
+                AuditElements.Result.SUCCESS));
         return events;
     }
 
     @Override
-    public void initTargetAppender() {
+    protected void initTargetAppender() {
         targetAppender = SyslogAppender.newSyslogAppenderBuilder().
                 setName(getTargetAppenderName()).
                 withHost("localhost").
                 withPort(514).
                 withProtocol(Protocol.UDP).
-                setLayout(PatternLayout.newBuilder().
-                        withPattern("%d{ISO8601} %-5level %logger - %msg%n").
-                        build()).
+                setLayout(PatternLayout.newBuilder().withPattern("%d{ISO8601} %-5level %logger - %msg%n").build()).
                 setFacility(Facility.LOCAL1).
                 build();
     }
 
     @Override
     public String getTargetAppenderName() {
-        return "audit_for_" + domainName + "_syslog";
+        return "audit_for_" + domain + "_syslog";
     }
 }
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestFileAuditAppender.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestFileAuditAppender.java
index cc9cd3e..ca508f7 100644
--- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestFileAuditAppender.java
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestFileAuditAppender.java
@@ -38,25 +38,23 @@ public class TestFileAuditAppender extends DefaultAuditAppender {
     @Override
     public Set<AuditLoggerName> getEvents() {
         Set<AuditLoggerName> events = new HashSet<>();
-        events.add(
-                new AuditLoggerName(
-                        AuditElements.EventCategoryType.LOGIC,
-                        ResourceLogic.class.getSimpleName(),
-                        null,
-                        "create",
-                        AuditElements.Result.SUCCESS));
-        events.add(
-                new AuditLoggerName(
-                        AuditElements.EventCategoryType.LOGIC,
-                        ConnectorLogic.class.getSimpleName(),
-                        null,
-                        "update",
-                        AuditElements.Result.SUCCESS));
+        events.add(new AuditLoggerName(
+                AuditElements.EventCategoryType.LOGIC,
+                ResourceLogic.class.getSimpleName(),
+                null,
+                "create",
+                AuditElements.Result.SUCCESS));
+        events.add(new AuditLoggerName(
+                AuditElements.EventCategoryType.LOGIC,
+                ConnectorLogic.class.getSimpleName(),
+                null,
+                "update",
+                AuditElements.Result.SUCCESS));
         return events;
     }
 
     @Override
-    public void initTargetAppender() {
+    protected void initTargetAppender() {
         LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
         // get log file path from existing file appender
         RollingRandomAccessFileAppender mainFile =
@@ -79,6 +77,6 @@ public class TestFileAuditAppender extends DefaultAuditAppender {
 
     @Override
     public String getTargetAppenderName() {
-        return "audit_for_" + domainName + "_norewrite_file";
+        return "audit_for_" + domain + "_norewrite_file";
     }
 }
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestFileRewriteAuditAppender.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestFileRewriteAuditAppender.java
index 8ce0d99..9741a29 100644
--- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestFileRewriteAuditAppender.java
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestFileRewriteAuditAppender.java
@@ -18,7 +18,7 @@
  */
 package org.apache.syncope.fit.core.reference;
 
-import java.util.HashSet;
+import java.util.Collections;
 import java.util.Set;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.logging.log4j.LogManager;
@@ -36,19 +36,16 @@ public class TestFileRewriteAuditAppender extends DefaultRewriteAuditAppender {
 
     @Override
     public Set<AuditLoggerName> getEvents() {
-        Set<AuditLoggerName> events = new HashSet<>();
-        events.add(
-                new AuditLoggerName(
-                        AuditElements.EventCategoryType.LOGIC,
-                        ResourceLogic.class.getSimpleName(),
-                        null,
-                        "update",
-                        AuditElements.Result.SUCCESS));
-        return events;
+        return Collections.singleton(new AuditLoggerName(
+                AuditElements.EventCategoryType.LOGIC,
+                ResourceLogic.class.getSimpleName(),
+                null,
+                "update",
+                AuditElements.Result.SUCCESS));
     }
 
     @Override
-    public void initTargetAppender() {
+    protected void initTargetAppender() {
         LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
 
         // get log file path from existing file appender
@@ -68,11 +65,11 @@ public class TestFileRewriteAuditAppender extends DefaultRewriteAuditAppender {
 
     @Override
     public String getTargetAppenderName() {
-        return "audit_for_" + domainName + "_file";
+        return "audit_for_" + domain + "_file";
     }
 
     @Override
-    public RewritePolicy getRewritePolicy() {
+    protected RewritePolicy getRewritePolicy() {
         return TestRewritePolicy.createPolicy();
     }
 }
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestRewritePolicy.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestRewritePolicy.java
index 641f203..2cb724b 100644
--- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestRewritePolicy.java
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestRewritePolicy.java
@@ -25,14 +25,10 @@ import org.apache.logging.log4j.core.config.plugins.Plugin;
 import org.apache.logging.log4j.core.config.plugins.PluginFactory;
 import org.apache.logging.log4j.core.impl.Log4jLogEvent;
 import org.apache.logging.log4j.message.SimpleMessage;
-import org.apache.logging.log4j.status.StatusLogger;
 
-@Plugin(name = "TestRewritePolicy", category = Core.CATEGORY_NAME, elementType = "rewritePolicy",
-        printObject = true)
+@Plugin(name = "TestRewritePolicy", category = Core.CATEGORY_NAME, elementType = "rewritePolicy", printObject = true)
 public class TestRewritePolicy implements RewritePolicy {
 
-    protected static final StatusLogger LOGGER = StatusLogger.getLogger();
-
     @Override
     public LogEvent rewrite(final LogEvent event) {
         return new Log4jLogEvent.Builder(event).setMessage(new SimpleMessage("This is a static test message")).build();
@@ -42,5 +38,4 @@ public class TestRewritePolicy implements RewritePolicy {
     public static TestRewritePolicy createPolicy() {
         return new TestRewritePolicy();
     }
-
 }
diff --git a/fit/core-reference/src/main/resources/elasticsearch/persistence.properties b/fit/core-reference/src/main/resources/elasticsearch/persistence.properties
index 82b0b51..6a968d8 100644
--- a/fit/core-reference/src/main/resources/elasticsearch/persistence.properties
+++ b/fit/core-reference/src/main/resources/elasticsearch/persistence.properties
@@ -24,5 +24,5 @@ any.search.visitor=org.apache.syncope.core.persistence.api.search.SearchCondVisi
 user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO
 group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAGroupDAO
 anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAnyObjectDAO
-audit.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAuditDAO
+logger.dao=org.apache.syncope.core.persistence.jpa.dao.JPALoggerDAO
 openjpa.RemoteCommitProvider=sjvm
diff --git a/fit/core-reference/src/main/resources/logic.properties b/fit/core-reference/src/main/resources/logic.properties
index 54d7cdb..abab00d 100644
--- a/fit/core-reference/src/main/resources/logic.properties
+++ b/fit/core-reference/src/main/resources/logic.properties
@@ -18,3 +18,4 @@ version=${syncope.version}
 buildNumber=${buildNumber}
 logicInvocationHandler=org.apache.syncope.core.logic.LogicInvocationHandler
 classPathScanImplementationLookup=org.apache.syncope.fit.core.reference.ITImplementationLookup
+enable.jdbcAuditAppender=true
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
index d55090e..111690f 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
@@ -85,7 +85,6 @@ import org.apache.syncope.common.rest.api.service.AnyObjectService;
 import org.apache.syncope.common.rest.api.service.AnyTypeClassService;
 import org.apache.syncope.common.rest.api.service.AnyTypeService;
 import org.apache.syncope.common.rest.api.service.ApplicationService;
-import org.apache.syncope.common.rest.api.service.AuditService;
 import org.apache.syncope.common.rest.api.service.CamelRouteService;
 import org.apache.syncope.common.rest.api.service.ConnectorService;
 import org.apache.syncope.common.rest.api.service.DynRealmService;
@@ -287,8 +286,6 @@ public abstract class AbstractITCase {
 
     protected static SCIMConfService scimConfService;
 
-    protected static AuditService auditService;
-
     @BeforeAll
     public static void securitySetup() {
         try (InputStream propStream = Encryptor.class.getResourceAsStream("/security.properties")) {
@@ -358,7 +355,6 @@ public abstract class AbstractITCase {
         oidcClientService = adminClient.getService(OIDCClientService.class);
         oidcProviderService = adminClient.getService(OIDCProviderService.class);
         scimConfService = adminClient.getService(SCIMConfService.class);
-        auditService = adminClient.getService(AuditService.class);
     }
 
     @Autowired
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuditITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuditITCase.java
index 866eb0b..7aa36ab 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuditITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuditITCase.java
@@ -31,8 +31,8 @@ import java.util.Set;
 import org.apache.commons.lang3.SerializationUtils;
 import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.log.AuditEntry;
 import org.apache.syncope.common.lib.to.AnyObjectTO;
-import org.apache.syncope.common.lib.to.AuditEntryTO;
 import org.apache.syncope.common.lib.to.ConnInstanceTO;
 import org.apache.syncope.common.lib.to.GroupTO;
 import org.apache.syncope.common.lib.to.PagedResult;
@@ -47,8 +47,8 @@ import org.junit.jupiter.api.Test;
 
 public class AuditITCase extends AbstractITCase {
 
-    private static AuditEntryTO query(final AuditQuery query, final int maxWaitSeconds, final boolean failIfEmpty) {
-        List<AuditEntryTO> results = query(query, maxWaitSeconds);
+    private static AuditEntry query(final AuditQuery query, final int maxWaitSeconds, final boolean failIfEmpty) {
+        List<AuditEntry> results = query(query, maxWaitSeconds);
         if (results.isEmpty()) {
             if (failIfEmpty) {
                 fail("Timeout when executing query for key " + query.getEntityKey());
@@ -58,15 +58,15 @@ public class AuditITCase extends AbstractITCase {
         return results.get(0);
     }
 
-    private static List<AuditEntryTO> query(final AuditQuery query, final int maxWaitSeconds) {
+    private static List<AuditEntry> query(final AuditQuery query, final int maxWaitSeconds) {
         int i = 0;
-        List<AuditEntryTO> results = List.of();
+        List<AuditEntry> results = List.of();
         do {
             try {
                 Thread.sleep(1000);
             } catch (InterruptedException e) {
             }
-            results = auditService.search(query).getResult();
+            results = loggerService.search(query).getResult();
             i++;
         } while (results.isEmpty() && i < maxWaitSeconds);
         return results;
@@ -78,7 +78,7 @@ public class AuditITCase extends AbstractITCase {
         assertNotNull(userTO.getKey());
 
         AuditQuery query = new AuditQuery.Builder(userTO.getKey()).build();
-        List<AuditEntryTO> entries = query(query, MAX_WAIT_SECONDS);
+        List<AuditEntry> entries = query(query, MAX_WAIT_SECONDS);
         assertEquals(1, entries.size());
 
         PagedResult<UserTO> usersTOs = userService.search(
@@ -100,8 +100,8 @@ public class AuditITCase extends AbstractITCase {
 
         AuditQuery query = new AuditQuery.Builder(userTO.getKey()).orderBy("event_date desc").
                 page(1).size(1).build();
-        AuditEntryTO entry = query(query, MAX_WAIT_SECONDS, true);
-        assertEquals(userTO.getKey(), entry.getKey());
+        AuditEntry entry = query(query, MAX_WAIT_SECONDS, true);
+        assertNotNull(entry);
         userService.delete(userTO.getKey());
     }
 
@@ -119,8 +119,8 @@ public class AuditITCase extends AbstractITCase {
                 event("create").
                 result(AuditElements.Result.SUCCESS).
                 build();
-        AuditEntryTO entry = query(query, MAX_WAIT_SECONDS, true);
-        assertEquals(userTO.getKey(), entry.getKey());
+        AuditEntry entry = query(query, MAX_WAIT_SECONDS, true);
+        assertNotNull(entry);
         userService.delete(userTO.getKey());
     }
 
@@ -131,8 +131,8 @@ public class AuditITCase extends AbstractITCase {
 
         AuditQuery query = new AuditQuery.Builder(groupTO.getKey()).orderBy("event_date desc").
                 page(1).size(1).build();
-        AuditEntryTO entry = query(query, MAX_WAIT_SECONDS, true);
-        assertEquals(groupTO.getKey(), entry.getKey());
+        AuditEntry entry = query(query, MAX_WAIT_SECONDS, true);
+        assertNotNull(entry);
         groupService.delete(groupTO.getKey());
     }
 
@@ -142,7 +142,7 @@ public class AuditITCase extends AbstractITCase {
         assertNotNull(groupTO.getKey());
 
         AuditQuery query = new AuditQuery.Builder(groupTO.getKey()).build();
-        List<AuditEntryTO> entries = query(query, MAX_WAIT_SECONDS);
+        List<AuditEntry> entries = query(query, MAX_WAIT_SECONDS);
         assertEquals(1, entries.size());
 
         PagedResult<GroupTO> groups = groupService.search(
@@ -163,8 +163,8 @@ public class AuditITCase extends AbstractITCase {
         assertNotNull(anyObjectTO.getKey());
         AuditQuery query = new AuditQuery.Builder(anyObjectTO.getKey()).orderBy("event_date desc").
                 page(1).size(1).build();
-        AuditEntryTO entry = query(query, MAX_WAIT_SECONDS, true);
-        assertEquals(anyObjectTO.getKey(), entry.getKey());
+        AuditEntry entry = query(query, MAX_WAIT_SECONDS, true);
+        assertNotNull(entry);
         anyObjectService.delete(anyObjectTO.getKey());
     }
 
@@ -174,7 +174,7 @@ public class AuditITCase extends AbstractITCase {
         assertNotNull(anyObjectTO);
 
         AuditQuery query = new AuditQuery.Builder(anyObjectTO.getKey()).build();
-        List<AuditEntryTO> entries = query(query, MAX_WAIT_SECONDS);
+        List<AuditEntry> entries = query(query, MAX_WAIT_SECONDS);
         assertEquals(1, entries.size());
 
         PagedResult<AnyObjectTO> anyObjects = anyObjectService.search(
@@ -199,7 +199,7 @@ public class AuditITCase extends AbstractITCase {
                 event("update").
                 result(AuditElements.Result.SUCCESS).
                 build();
-        List<AuditEntryTO> entries = query(query, 0);
+        List<AuditEntry> entries = query(query, 0);
         int pre = entries.size();
 
         ConnInstanceTO ldapConn = connectorService.read(connectorKey, null);
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LoggerITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LoggerITCase.java
index 4d18370..7857c71 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LoggerITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LoggerITCase.java
@@ -105,7 +105,6 @@ public class LoggerITCase extends AbstractITCase {
     @Test
     public void listAudits() throws ParseException {
         List<LoggerTO> audits = loggerService.list(LoggerType.AUDIT);
-
         assertNotNull(audits);
         assertFalse(audits.isEmpty());
         for (LoggerTO audit : audits) {
diff --git a/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/dbms.adoc b/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/dbms.adoc
index c1128ff..6602420 100644
--- a/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/dbms.adoc
+++ b/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/dbms.adoc
@@ -91,7 +91,7 @@ any.search.visitor=org.apache.syncope.core.persistence.api.search.SearchCondVisi
 user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONUserDAO
 group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONGroupDAO
 anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONAnyObjectDAO
-audit.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONAuditDAO
+logger.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONLoggerDAO
 ....
 
 In `provisioning.properties`, replace as follows:
@@ -224,7 +224,7 @@ any.search.visitor=org.apache.syncope.core.persistence.api.search.SearchCondVisi
 user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONUserDAO
 group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONGroupDAO
 anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONAnyObjectDAO
-audit.dao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONAuditDAO
+logger.dao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONLoggerDAO
 ....
 
 In `provisioning.properties`, replace as follows: