dosubot[bot] commented on issue #35594:
URL: https://github.com/apache/superset/issues/35594#issuecomment-3395615562

   <!-- Answer -->
   Here's a concrete example for robust user management action logging in 
Superset, avoiding transaction errors and capturing audit details like actor 
and IP address:
   
   Subclass the relevant API class (e.g., `SupersetUserApi` or `UserRestApi`), 
override the CRUD methods (`post`, `put`, `delete`), and use the 
`event_logger.log_this_with_context` decorator to log actions with full request 
context. This ensures logging happens after the transaction is committed and 
avoids the "transaction is closed" error you saw with SQLAlchemy event 
listeners 
[reference](https://github.com/apache/superset/blob/829e4d92d91ceae4b43b1ed3b063ffe45377799c/superset/views/users/api.py).
   
   Example:
   
   ```python
   from flask import g, request
   from superset.extensions import event_logger
   from superset.views.users.api import UserRestApi
   
   class CustomUserRestApi(UserRestApi):
       @event_logger.log_this_with_context(
           action="user_create",
           log_to_statsd=False,
       )
       def post(self):
           # Your user creation logic here
           response = super().post()
           # Optionally add extra audit details
           actor = getattr(g, "user", None)
           ip_address = request.headers.get("X-Forwarded-For", 
request.remote_addr)
           event_logger.log(
               user_id=actor.id if actor else None,
               action="user_create",
               json={
                   "actor_username": getattr(actor, "username", None),
                   "ip_address": ip_address,
                   "details": "User created",
               },
               referrer=ip_address,
           )
           return response
   
       @event_logger.log_this_with_context(
           action="user_update",
           log_to_statsd=False,
       )
       def put(self, user_id):
           # Your user update logic here
           response = super().put(user_id)
           actor = getattr(g, "user", None)
           ip_address = request.headers.get("X-Forwarded-For", 
request.remote_addr)
           event_logger.log(
               user_id=actor.id if actor else None,
               action="user_update",
               json={
                   "actor_username": getattr(actor, "username", None),
                   "ip_address": ip_address,
                   "details": f"User {user_id} updated",
               },
               referrer=ip_address,
           )
           return response
   
       @event_logger.log_this_with_context(
           action="user_delete",
           log_to_statsd=False,
       )
       def delete(self, user_id):
           # Your user deletion logic here
           response = super().delete(user_id)
           actor = getattr(g, "user", None)
           ip_address = request.headers.get("X-Forwarded-For", 
request.remote_addr)
           event_logger.log(
               user_id=actor.id if actor else None,
               action="user_delete",
               json={
                   "actor_username": getattr(actor, "username", None),
                   "ip_address": ip_address,
                   "details": f"User {user_id} deleted",
               },
               referrer=ip_address,
           )
           return response
   ```
   
   This pattern is upgrade-safe and integrates with Superset's Action Log. You 
can register your custom API class in your fork or extension by replacing the 
default view registration for user management.
   
   The `event_logger.log_this_with_context` decorator automatically captures 
request context and user info, and you can add extra details to the log entry 
as shown above 
[reference](https://github.com/apache/superset/blob/829e4d92d91ceae4b43b1ed3b063ffe45377799c/superset/utils/log.py).
   
   Let me know if you need help with registering your custom API or want an 
example for a different method!
   
   
   <!-- Dosu Comment Footer -->
   *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).*
   
   ---
   <sup>How did I do? 
[Good](https://app.dosu.dev/response-feedback/74fd352f-1e9f-4541-8347-9be9322bd03b?feedback_type=great_response)
 | 
[Irrelevant](https://app.dosu.dev/response-feedback/74fd352f-1e9f-4541-8347-9be9322bd03b?feedback_type=irrelevant_answer)
 | 
[Incorrect](https://app.dosu.dev/response-feedback/74fd352f-1e9f-4541-8347-9be9322bd03b?feedback_type=incorrect_sources)
 | 
[Verbose](https://app.dosu.dev/response-feedback/74fd352f-1e9f-4541-8347-9be9322bd03b?feedback_type=too_verbose)
 | 
[Hallucination](https://app.dosu.dev/response-feedback/74fd352f-1e9f-4541-8347-9be9322bd03b?feedback_type=hallucination)
 | [Report 
🐛](https://app.dosu.dev/response-feedback/74fd352f-1e9f-4541-8347-9be9322bd03b?feedback_type=bug_report)
 | 
[Other](https://app.dosu.dev/response-feedback/74fd352f-1e9f-4541-8347-9be9322bd03b?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat
 with 
Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/a28d3c7e-a9d3-459e-9fb6-3a6f9ff4f357/ask?utm_source=githu
 b)&nbsp;[![Join 
Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share
 on 
X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/apache/superset/issues/35594)


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to