diff --git a/gms/src/main/java/it/inaf/ia2/gms/authn/JWTFilter.java b/gms/src/main/java/it/inaf/ia2/gms/authn/JWTFilter.java
index 92845ced2220b9fc362cf59c73655235f0cc9ced..ed7be0d35da5a9abed52c142cd33b71782d43fac 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/authn/JWTFilter.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/authn/JWTFilter.java
@@ -3,6 +3,7 @@ package it.inaf.ia2.gms.authn;
 import it.inaf.ia2.aa.UserManager;
 import it.inaf.ia2.aa.data.User;
 import it.inaf.ia2.gms.persistence.LoggingDAO;
+import it.inaf.ia2.gms.persistence.model.ActionType;
 import java.io.IOException;
 import java.security.Principal;
 import java.util.Map;
@@ -55,13 +56,13 @@ public class JWTFilter implements Filter {
         Map<String, Object> claims = userManager.parseIdTokenClaims(token);
 
         if (claims.get("sub") == null) {
-            loggingDAO.logAction("Attempt to access WS with invalid token", request);
+            loggingDAO.logAction(ActionType.UNAUTHORIZED_ACCESS_ATTEMPT, "Attempt to access API with invalid token", request);
             response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid access token: missing sub claim");
             return;
         }
 
         ServletRequestWithJWTPrincipal wrappedRequest = new ServletRequestWithJWTPrincipal(request, token, claims);
-        loggingDAO.logAction("WS access from " + wrappedRequest.getUserPrincipal().getName(), request);
+        loggingDAO.logAction(ActionType.UNAUTHORIZED_ACCESS_ATTEMPT, "API access from " + wrappedRequest.getUserPrincipal().getName(), request);
 
         fc.doFilter(wrappedRequest, res);
     }
diff --git a/gms/src/main/java/it/inaf/ia2/gms/manager/GroupsManager.java b/gms/src/main/java/it/inaf/ia2/gms/manager/GroupsManager.java
index fa2e47506d58ce8b2dcbb4134f136088ee5492eb..dcbadeabd68c98e8d740635505b66a5fee01888a 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/manager/GroupsManager.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/manager/GroupsManager.java
@@ -4,6 +4,7 @@ import it.inaf.ia2.gms.exception.BadRequestException;
 import it.inaf.ia2.gms.exception.UnauthorizedException;
 import it.inaf.ia2.gms.model.Permission;
 import it.inaf.ia2.gms.persistence.LoggingDAO;
+import it.inaf.ia2.gms.persistence.model.ActionType;
 import it.inaf.ia2.gms.persistence.model.GroupEntity;
 import it.inaf.ia2.gms.persistence.model.PermissionEntity;
 import it.inaf.ia2.gms.service.GroupsService;
@@ -84,14 +85,14 @@ public class GroupsManager extends UserAwareComponent {
 
     public void verifyUserCanReadGroup(GroupEntity group) {
         if (permissionsManager.getCurrentUserPermission(group) == null) {
-            loggingDAO.logAction("Unauthorized group management request, group_id=" + group.getId());
+            loggingDAO.logAction(ActionType.UNAUTHORIZED_ACCESS_ATTEMPT, "Unauthorized group management request, group_id=" + group.getId());
             throw new UnauthorizedException("Missing permission to see this group");
         }
     }
 
     private void verifyUserCanManageGroup(GroupEntity group) {
         if (permissionsManager.getCurrentUserPermission(group) != Permission.ADMIN) {
-            loggingDAO.logAction("Unauthorized group management request, group_id=" + group.getId());
+            loggingDAO.logAction(ActionType.UNAUTHORIZED_ACCESS_ATTEMPT, "Unauthorized group management request, group_id=" + group.getId());
             throw new UnauthorizedException("Missing admin permission");
         }
     }
diff --git a/gms/src/main/java/it/inaf/ia2/gms/manager/InvitedRegistrationManager.java b/gms/src/main/java/it/inaf/ia2/gms/manager/InvitedRegistrationManager.java
index e0051270b77c84a6fa86c4dd7bd8d6ccf4f4830a..5c14a9f961b15d3d7166f3361555292967d7e55a 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/manager/InvitedRegistrationManager.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/manager/InvitedRegistrationManager.java
@@ -14,6 +14,7 @@ import it.inaf.ia2.gms.persistence.model.InvitedRegistration;
 import it.inaf.ia2.gms.persistence.model.MembershipEntity;
 import it.inaf.ia2.gms.service.PermissionsService;
 import it.inaf.ia2.gms.authn.RapClient;
+import static it.inaf.ia2.gms.persistence.model.ActionType.*;
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
@@ -68,6 +69,7 @@ public class InvitedRegistrationManager extends UserAwareComponent {
         for (Map.Entry<GroupEntity, Permission> entry : groupsPermissions.entrySet()) {
             GroupEntity group = entry.getKey();
             if (permissionsManager.getCurrentUserPermission(group) != Permission.ADMIN) {
+                loggingDAO.logAction(UNAUTHORIZED_ACCESS_ATTEMPT, "Attempt to add invited registration for group " + group.getId());
                 throw new UnauthorizedException("You don't have the permission to perform invited registrations");
             }
             groupIdsPermissions.put(group.getId(), entry.getValue());
@@ -80,6 +82,8 @@ public class InvitedRegistrationManager extends UserAwareComponent {
                 .setGroupsPermissions(groupIdsPermissions);
 
         invitedRegistrationDAO.addInvitedRegistration(invitedRegistration);
+
+        loggingDAO.logAction(INVITED_REGISTRATION_ADDED, "Email=" + email);
     }
 
     public InvitedRegistration getInvitedRegistrationFromToken(String token) {
@@ -94,7 +98,7 @@ public class InvitedRegistrationManager extends UserAwareComponent {
 
             httpSession.setAttribute(INVITED_REGISTRATION, invitedRegistration);
 
-            loggingDAO.logAction("Started invited registration for email " + invitedRegistration.getEmail());
+            loggingDAO.logAction(INVITED_REGISTRATION_OPENED, "Started invited registration for email " + invitedRegistration.getEmail());
 
             return invitedRegistration;
         } catch (NoSuchAlgorithmException ex) {
@@ -144,9 +148,11 @@ public class InvitedRegistrationManager extends UserAwareComponent {
     }
 
     private void completeInvitedRegistration(InvitedRegistration invitedRegistration) {
+
+        String userId = getCurrentUserId();
+
         for (Map.Entry<String, Permission> entry : invitedRegistration.getGroupsPermissions().entrySet()) {
             String groupId = entry.getKey();
-            String userId = getCurrentUserId();
 
             GroupEntity groupEntity = groupsDAO.findGroupById(groupId).get();
 
@@ -156,11 +162,14 @@ public class InvitedRegistrationManager extends UserAwareComponent {
             membershipEntity.setCreatedBy(getCurrentUserId());
             membershipsDAO.addMember(membershipEntity);
 
-            permissionsService.addPermission(groupEntity, userId, entry.getValue(), getCurrentUserId());
+            permissionsService.addPermission(groupEntity, userId, entry.getValue(), userId);
         }
 
-        invitedRegistration.setUserId(getCurrentUserId());
+        invitedRegistration.setUserId(userId);
         invitedRegistrationDAO.setRegistrationDone(invitedRegistration);
+
+        loggingDAO.logAction(INVITED_REGISTRATION_COMPLETED, "user_id=" + userId + " groups=["
+                + String.join(",", invitedRegistration.getGroupsPermissions().keySet()) + "]");
     }
 
     public List<InvitedRegistrationItem> getInvitedRegistrationsForGroup(GroupEntity group) {
@@ -201,7 +210,7 @@ public class InvitedRegistrationManager extends UserAwareComponent {
 
         invitedRegistrationDAO.deleteInvitedRegistrationRequest(registrationId, groupId);
 
-        loggingDAO.logAction("Deleted invited registration request. "
+        loggingDAO.logAction(INVITED_REGISTRATION_DELETED, "Deleted invited registration request. "
                 + "[request_id=" + registrationId + ", group_id=" + groupId
                 + ", group_name=" + group.getName() + "]");
     }
diff --git a/gms/src/main/java/it/inaf/ia2/gms/manager/MembershipManager.java b/gms/src/main/java/it/inaf/ia2/gms/manager/MembershipManager.java
index ec522fc37e36d9ad0f3d129318e60c6833349caa..622a49675ca0b2ae8fb4261ab1dadbd3c25b6f86 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/manager/MembershipManager.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/manager/MembershipManager.java
@@ -10,6 +10,7 @@ import it.inaf.ia2.gms.persistence.model.MembershipEntity;
 import it.inaf.ia2.gms.persistence.model.PermissionEntity;
 import it.inaf.ia2.gms.service.PermissionUtils;
 import it.inaf.ia2.gms.authn.RapClient;
+import static it.inaf.ia2.gms.persistence.model.ActionType.*;
 import it.inaf.ia2.rap.data.RapUser;
 import java.util.HashSet;
 import java.util.List;
@@ -49,6 +50,7 @@ public class MembershipManager extends UserAwareComponent {
         Permission groupPermission = permissionsManager.getCurrentUserPermission(group);
 
         if (!Permission.includes(groupPermission, Permission.VIEW_MEMBERS)) {
+            loggingDAO.logAction(UNAUTHORIZED_ACCESS_ATTEMPT, "Attempted to view members of group " + group.getId());
             throw new UnauthorizedException("You don't have the permission to view members");
         }
 
@@ -86,7 +88,7 @@ public class MembershipManager extends UserAwareComponent {
         membership.setCreatedBy(getCurrentUserId());
 
         membership = membershipsDAO.addMember(membership);
-        loggingDAO.logAction("Added member, group_id=" + group.getId() + ", user_id=" + userId);
+        loggingDAO.logAction(MEMBER_ADDED, "Added member, group_id=" + group.getId() + ", user_id=" + userId);
 
         return membership;
     }
@@ -94,12 +96,13 @@ public class MembershipManager extends UserAwareComponent {
     public void removeMember(GroupEntity group, String userId) {
         verifyUserCanManageMembers(group);
         membershipsDAO.removeMembership(group.getId(), userId);
-        loggingDAO.logAction("Member removed, group_id=" + group.getId() + ", user_id=" + userId);
+        loggingDAO.logAction(MEMBER_REMOVED, "Member removed, group_id=" + group.getId() + ", user_id=" + userId);
     }
 
     private Permission verifyUserCanManageMembers(GroupEntity group) {
         Permission permission = permissionsManager.getCurrentUserPermission(group);
         if (!Permission.includes(permission, Permission.MANAGE_MEMBERS)) {
+            loggingDAO.logAction(UNAUTHORIZED_ACCESS_ATTEMPT, "Attempted to manage members of group " + group.getId());
             throw new UnauthorizedException("Missing manage members permissions");
         }
         return permission;
diff --git a/gms/src/main/java/it/inaf/ia2/gms/manager/PermissionsManager.java b/gms/src/main/java/it/inaf/ia2/gms/manager/PermissionsManager.java
index d4568f3160df3e3916292196f3f45bcf0f410e18..f684d6dd28134cac298402e5671fb8b721243997 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/manager/PermissionsManager.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/manager/PermissionsManager.java
@@ -9,6 +9,7 @@ import it.inaf.ia2.gms.persistence.model.PermissionEntity;
 import it.inaf.ia2.gms.service.PermissionUtils;
 import it.inaf.ia2.gms.service.PermissionsService;
 import it.inaf.ia2.gms.authn.RapClient;
+import it.inaf.ia2.gms.persistence.model.ActionType;
 import it.inaf.ia2.rap.data.RapUser;
 import java.util.ArrayList;
 import java.util.List;
@@ -144,7 +145,7 @@ public class PermissionsManager extends UserAwareComponent {
     }
 
     private Supplier<UnauthorizedException> unauthorizedExceptionSupplier(GroupEntity group) {
-        loggingDAO.logAction("Unauthorized attempt to manage permissions [group_id=" + group.getId() + "]");
+        loggingDAO.logAction(ActionType.UNAUTHORIZED_ACCESS_ATTEMPT, "Unauthorized attempt to manage permissions [group_id=" + group.getId() + "]");
         return () -> new UnauthorizedException("You don't have the privileges for managing the requested permission");
     }
 
diff --git a/gms/src/main/java/it/inaf/ia2/gms/persistence/LoggingDAO.java b/gms/src/main/java/it/inaf/ia2/gms/persistence/LoggingDAO.java
index dc26fd488dede187917961d2a3ebad10d73551b0..277f7e9da52d950a6d675452e838421ad6359a49 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/persistence/LoggingDAO.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/persistence/LoggingDAO.java
@@ -1,5 +1,6 @@
 package it.inaf.ia2.gms.persistence;
 
+import it.inaf.ia2.gms.persistence.model.ActionType;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.sql.PreparedStatement;
@@ -52,19 +53,20 @@ public class LoggingDAO {
         return sw.toString();
     }
 
-    public void logAction(String action) {
-        logAction(action, request);
+    public void logAction(ActionType type, String action) {
+        logAction(type, action, request);
     }
 
-    public void logAction(String action, HttpServletRequest request) {
+    public void logAction(ActionType type, String description, HttpServletRequest request) {
         try {
-            String sql = "INSERT INTO audit_log (\"user\", action, ip_address) VALUES (?, ?, ?)";
+            String sql = "INSERT INTO audit_log (\"user\", action_type, description, ip_address) VALUES (?, ?, ?, ?)";
 
             jdbcTemplate.update(conn -> {
                 PreparedStatement ps = conn.prepareStatement(sql);
                 int i = 0;
                 ps.setString(++i, getUser(request));
-                ps.setString(++i, action);
+                ps.setString(++i, type.toString());
+                ps.setString(++i, description);
                 ps.setString(++i, getIPAddress(request));
                 return ps;
             });
diff --git a/gms/src/main/java/it/inaf/ia2/gms/persistence/model/ActionType.java b/gms/src/main/java/it/inaf/ia2/gms/persistence/model/ActionType.java
new file mode 100644
index 0000000000000000000000000000000000000000..f867604ecfc355ae5d0ff118b432dfe23086713c
--- /dev/null
+++ b/gms/src/main/java/it/inaf/ia2/gms/persistence/model/ActionType.java
@@ -0,0 +1,19 @@
+package it.inaf.ia2.gms.persistence.model;
+
+public enum ActionType {
+
+    GROUP_CREATED,
+    GROUP_DELETED,
+    GROUP_UPDATED,
+    MEMBER_ADDED,
+    MEMBER_REMOVED,
+    PERMISSION_ADDED,
+    PERMISSION_UPDATED,
+    PERMISSION_REMOVED,
+    JOIN,
+    INVITED_REGISTRATION_ADDED,
+    INVITED_REGISTRATION_OPENED,
+    INVITED_REGISTRATION_DELETED,
+    INVITED_REGISTRATION_COMPLETED,
+    UNAUTHORIZED_ACCESS_ATTEMPT
+}
diff --git a/gms/src/main/java/it/inaf/ia2/gms/service/GroupsService.java b/gms/src/main/java/it/inaf/ia2/gms/service/GroupsService.java
index e39e2855883b870d7d2e8cd0ccffab024bf1b779..462eb36e896545886cef02bbe4f1d6a85c20bec8 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/service/GroupsService.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/service/GroupsService.java
@@ -10,6 +10,7 @@ import it.inaf.ia2.gms.persistence.InvitedRegistrationDAO;
 import it.inaf.ia2.gms.persistence.LoggingDAO;
 import it.inaf.ia2.gms.persistence.MembershipsDAO;
 import it.inaf.ia2.gms.persistence.PermissionsDAO;
+import static it.inaf.ia2.gms.persistence.model.ActionType.*;
 import it.inaf.ia2.gms.persistence.model.GroupEntity;
 import java.util.Date;
 import java.util.List;
@@ -74,7 +75,7 @@ public class GroupsService {
         group.setCreationTime(new Date());
 
         groupsDAO.createGroup(group);
-        loggingDAO.logAction("Added group: parent_path=" + parent.getPath() + ", group_name=" + groupName);
+        loggingDAO.logAction(GROUP_CREATED, "Added group: parent_path=" + parent.getPath() + ", group_name=" + groupName);
 
         return group;
     }
@@ -90,7 +91,7 @@ public class GroupsService {
         group.setLeaf(leaf);
 
         GroupEntity entity = groupsDAO.updateGroup(group);
-        loggingDAO.logAction("Group updated, group_id=" + group.getId()
+        loggingDAO.logAction(GROUP_UPDATED, "Group updated, group_id=" + group.getId()
                 + ", new name: " + newGroupName + ", leaf: " + leaf);
 
         return entity;
@@ -124,7 +125,7 @@ public class GroupsService {
             groupsDAO.deleteGroup(g);
         }
 
-        loggingDAO.logAction("Group deleted [group_id=" + group.getId() + ", group_name=" + group.getName() + "]");
+        loggingDAO.logAction(GROUP_DELETED, "Group deleted [group_id=" + group.getId() + ", group_name=" + group.getName() + "]");
 
         return parent;
     }
diff --git a/gms/src/main/java/it/inaf/ia2/gms/service/JoinService.java b/gms/src/main/java/it/inaf/ia2/gms/service/JoinService.java
index 402decf0deaebba3f449b07ba05e12591f550613..f268243b98727f9b791811932a77adc83d3b1323 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/service/JoinService.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/service/JoinService.java
@@ -2,11 +2,14 @@ package it.inaf.ia2.gms.service;
 
 import it.inaf.ia2.gms.model.Permission;
 import it.inaf.ia2.gms.persistence.JoinDAO;
+import it.inaf.ia2.gms.persistence.LoggingDAO;
 import it.inaf.ia2.gms.persistence.MembershipsDAO;
 import it.inaf.ia2.gms.persistence.PermissionsDAO;
+import it.inaf.ia2.gms.persistence.model.ActionType;
 import it.inaf.ia2.gms.persistence.model.GroupEntity;
 import it.inaf.ia2.gms.persistence.model.MembershipEntity;
 import it.inaf.ia2.gms.persistence.model.PermissionEntity;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -25,6 +28,9 @@ public class JoinService {
     @Autowired
     private JoinDAO joinDAO;
 
+    @Autowired
+    private LoggingDAO loggingDAO;
+
     public String join(String userId1, String userId2) {
 
         List<GroupEntity> user1Groups = membershipsDAO.getUserMemberships(userId1);
@@ -61,9 +67,22 @@ public class JoinService {
 
         joinDAO.join(membershipsToAdd, permissionsToAdd, deletingUserId);
 
+        loggingDAO.logAction(ActionType.JOIN, "removed_user=" + deletingUserId
+                + " added_memberships: " + String.join(", ", getAddedGroups(membershipsToAdd))
+                + " added_permissions: " + String.join(", ", getAddedPermissions(permissionsToAdd))
+        );
+
         return remainingUserId;
     }
 
+    private List<String> getAddedGroups(Set<MembershipEntity> membershipsToAdd) {
+        return membershipsToAdd.stream().map(m -> m.getGroupId()).collect(Collectors.toList());
+    }
+
+    private List<String> getAddedPermissions(Set<PermissionEntity> permissionsToAdd) {
+        return permissionsToAdd.stream().map(m -> "(" + m.getGroupId() + "," + m.getPermission() + ")").collect(Collectors.toList());
+    }
+
     private MembershipEntity getMembershipEntity(String groupId, String userId) {
         MembershipEntity entity = new MembershipEntity();
         entity.setGroupId(groupId);
diff --git a/gms/src/main/java/it/inaf/ia2/gms/service/PermissionsService.java b/gms/src/main/java/it/inaf/ia2/gms/service/PermissionsService.java
index 645757144cd70d4894319e60713ab63a6035a386..727d78481a58230cd0d63d0a5dd95d68980e81fb 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/service/PermissionsService.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/service/PermissionsService.java
@@ -4,6 +4,7 @@ import it.inaf.ia2.gms.model.Permission;
 import it.inaf.ia2.gms.persistence.LoggingDAO;
 import it.inaf.ia2.gms.persistence.model.PermissionEntity;
 import it.inaf.ia2.gms.persistence.PermissionsDAO;
+import static it.inaf.ia2.gms.persistence.model.ActionType.*;
 import it.inaf.ia2.gms.persistence.model.GroupEntity;
 import java.util.List;
 import java.util.Optional;
@@ -36,7 +37,7 @@ public class PermissionsService {
 
     public void removePermission(GroupEntity group, String userId) {
         permissionsDAO.deletePermission(group.getId(), userId);
-        loggingDAO.logAction("Removed permission for " + userId + "in group " + group.getId());
+        loggingDAO.logAction(PERMISSION_REMOVED, "Removed permission for " + userId + " in group " + group.getId());
     }
 
     public PermissionEntity addPermission(GroupEntity group, String userId, Permission permission, String setBy) {
@@ -62,7 +63,7 @@ public class PermissionsService {
         }
 
         permissionEntity = permissionsDAO.createOrUpdatePermission(permissionEntity);
-        loggingDAO.logAction("Added " + permission + " permission for " + userId + "in group " + group.getId());
+        loggingDAO.logAction(PERMISSION_ADDED, "Added " + permission + " permission for " + userId + " in group " + group.getId());
 
         return permissionEntity;
     }
@@ -76,7 +77,10 @@ public class PermissionsService {
         permissionEntity.setGroupPath(group.getPath());
         permissionEntity.setSetBy(setBy);
 
-        return permissionsDAO.createOrUpdatePermission(permissionEntity);
+        permissionEntity = permissionsDAO.createOrUpdatePermission(permissionEntity);
+        loggingDAO.logAction(PERMISSION_ADDED, "Added " + permission + " permission for " + userId + " in group " + group.getId());
+
+        return permissionEntity;
     }
 
     public PermissionEntity updatePermission(GroupEntity group, String userId, Permission permission, String setBy) {
@@ -87,6 +91,9 @@ public class PermissionsService {
         permissionEntity.setSetBy(setBy);
         permissionEntity.setPermission(permission);
 
-        return permissionsDAO.updatePermission(permissionEntity);
+        permissionEntity = permissionsDAO.updatePermission(permissionEntity);
+        loggingDAO.logAction(PERMISSION_UPDATED, "Updated permission for " + userId + " in group " + group.getId() + " to " + permission);
+
+        return permissionEntity;
     }
 }
diff --git a/gms/src/main/resources/sql/init.sql b/gms/src/main/resources/sql/init.sql
index f8fdad6cb4904a1fdbc2a16205e5b9f95511b0c8..0978e21a90bde4bd06cc34df2b6de7462079b384 100644
--- a/gms/src/main/resources/sql/init.sql
+++ b/gms/src/main/resources/sql/init.sql
@@ -48,7 +48,8 @@ CREATE TABLE audit_log (
   "date" timestamp DEFAULT NOW(),
   "user" varchar,
   "ip_address" varchar,
-  "action" TEXT
+  "action_type" varchar,
+  "description" TEXT
 );
 
 CREATE TABLE invited_registration_request (
diff --git a/gms/src/main/resources/sql/upgrade.sql b/gms/src/main/resources/sql/upgrade.sql
index 2b41d531ea70c57574e55c246947a15c95b38ab6..2030268d5062d31f0ec64daa350e780ba3171a80 100644
--- a/gms/src/main/resources/sql/upgrade.sql
+++ b/gms/src/main/resources/sql/upgrade.sql
@@ -5,3 +5,7 @@ ALTER TABLE gms_membership ADD COLUMN creation_time timestamp DEFAULT NOW();
 ALTER TABLE gms_membership ADD COLUMN created_by varchar;
 ALTER TABLE gms_permission ADD COLUMN update_time timestamp DEFAULT NOW();
 ALTER TABLE gms_permission ADD COLUMN set_by varchar;
+
+--- 2021-03-19
+ALTER TABLE audit_log ADD COLUMN action_type varchar;
+ALTER TABLE audit_log RENAME COLUMN action TO description;
diff --git a/gms/src/test/java/it/inaf/ia2/gms/persistence/LoggingDAOTest.java b/gms/src/test/java/it/inaf/ia2/gms/persistence/LoggingDAOTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..82c799c59de357c89d07869b596f66e199830808
--- /dev/null
+++ b/gms/src/test/java/it/inaf/ia2/gms/persistence/LoggingDAOTest.java
@@ -0,0 +1,41 @@
+package it.inaf.ia2.gms.persistence;
+
+import it.inaf.ia2.gms.DataSourceConfig;
+import it.inaf.ia2.gms.persistence.model.ActionType;
+import javax.servlet.http.HttpServletRequest;
+import javax.sql.DataSource;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@ContextConfiguration(classes = DataSourceConfig.class)
+public class LoggingDAOTest {
+
+    @MockBean
+    private HttpServletRequest request;
+
+    @Autowired
+    private DataSource dataSource;
+
+    private LoggingDAO dao;
+
+    @Before
+    public void setUp() {
+        dao = new LoggingDAO(dataSource);
+    }
+
+    @Test
+    public void testLogAction() {
+        dao.logAction(ActionType.GROUP_CREATED, "description");
+    }
+
+    @Test
+    public void testlogException() {
+        dao.logException(new RuntimeException("msg"));
+    }
+}
diff --git a/gms/src/test/java/it/inaf/ia2/gms/service/JoinServiceTest.java b/gms/src/test/java/it/inaf/ia2/gms/service/JoinServiceTest.java
index aff3fc2946c3d5fd398076b0ab33cbd1ea084ccf..9652ee3d0b988ba1c9e8df75cdbfaebc3dc214d0 100644
--- a/gms/src/test/java/it/inaf/ia2/gms/service/JoinServiceTest.java
+++ b/gms/src/test/java/it/inaf/ia2/gms/service/JoinServiceTest.java
@@ -2,6 +2,7 @@ package it.inaf.ia2.gms.service;
 
 import it.inaf.ia2.gms.model.Permission;
 import it.inaf.ia2.gms.persistence.JoinDAO;
+import it.inaf.ia2.gms.persistence.LoggingDAO;
 import it.inaf.ia2.gms.persistence.MembershipsDAO;
 import it.inaf.ia2.gms.persistence.PermissionsDAO;
 import it.inaf.ia2.gms.persistence.model.GroupEntity;
@@ -37,6 +38,9 @@ public class JoinServiceTest {
     @Mock
     private JoinDAO joinDAO;
 
+    @Mock
+    private LoggingDAO loggingDAO;
+
     @InjectMocks
     private JoinService joinService;