How to Use Lifecyle Hooks for Audit Logs in Strapi

Keeping track of user activities in Strapi applications is possible with Strapi middleware. Developers can easily configure the Strapi application to log every action performed by each user, either through the admin dashboard or via API, into a custom collection type.


This is a companion discussion topic for the original entry at https://strapi.io/blog/how-to-use-lifecyle-hooks-for-audit-logs-in-strapi
2 Likes

Nice work with this article, if certainly fills in a few gaps on how this should be achieved in v4. One thing I noticed was the afterDelete hook did not include who did the actual deleting. I’d think that would be a very important piece of information to have, especially for an audit log! Is it possible to find the user that performed the delete action?

I’d also note that afterDelete is not called when the user clicks the check mark on the left side of the row, and then the delete button that pops up, but only when the user clicks the trash can icon on the right end of the item that is being deleted. To catch the other, you have to use the AfterDeleteMany lifecycle hook. That means in additon to the hooks used in this article, you probably also want to dcode for AfterDeleteMany, afterUpdateMany, and afterCreateMany to get full coverage

3 Likes

some minor mistakes in the document
if the Content Type is AuditLog, then the slug will be generated as audit-log, so

strapi.entityService.create('api::audit-log.audit-log, {

and also need async await

1 Like

Hello, I’m just chiming in to say I would like this feature as well. I think it is very important to know who was the author/user who deleted an entry.

1 Like

hi @sonya this feature is currently being developed per https://feedback.strapi.io/

2 Likes

AWESOME! Thanks for the quick response! :smiley:

I just created an article about how to get user information on DELETE actions using a middleware: Audit logs on Strapi v4.6.x. Alternative to perform audit logs in… | by Lucas Keller | Feb, 2023 | Medium

For people who still have this problem or face it in the future, here is a solution:

  afterDelete(event, ctx) {
    const { result, params } = event;
    ctx = strapi.requestContext.get(); // info about request to delete a row in Article
    const deletedBy = ctx.state.user; // user who executed the delete operation

    strapi.entityService.create("api::auditlog.auditlog", {
      data: {
        contentType: "Article",
        action: "Delete Content",
        content: result,
        author: result.createdBy,
        method: ctx.request.method,
        params: params,
        request: event,
        editedBy: deletedBy,
      },
    });
  },

In the previous procedure we took the data of the user who executed the deletion point. The log will show the user’s complete information once the Article record is deleted and the log is generated. You will be able to see information that may be sensitive for your purposes such as the password (most likely you have it encrypted), however if you do not want to show this information you could do something like the following, within the same function AfterDelete() or you can create a new one that is called inside afterDelete():

  afterDelete(event, ctx) {
    const { result, params } = event;
    ctx = strapi.requestContext.get(); // info about request to delete a row in Article
    const user = ctx.state.user; // user who executed the delete operation
    const { id, username, email, isActive, createdAt, updatedAt } = userInfo
    const deletedBy = {
         id,
         username,
         email,
         isActive,
         createdAt,
         updatedAt,
    };

    strapi.entityService.create("api::auditlog.auditlog", {
      data: {
        contentType: "Article",
        action: "Delete Content",
        content: result,
        author: result.createdBy,
        method: ctx.request.method,
        params: params,
        request: event,
        editedBy: deletedBy,
      },
    });
  },

Remember that you can create a separate function that is responsible for transforming your user to make cleaner code and shorter and more readable functions.