pierrejeambrun commented on code in PR #45609:
URL: https://github.com/apache/airflow/pull/45609#discussion_r1922562571


##########
airflow/api_fastapi/common/db/common.py:
##########
@@ -179,3 +185,91 @@ def paginated_select(
     statement = apply_filters_to_select(statement=statement, 
filters=[order_by, offset, limit])
 
     return statement, total_entries
+
+
+def action_logging(event: str | None):
+    async def log_action(
+        request: Request,
+        session: SessionDep,
+        user: Annotated[BaseUser, Depends(get_user_with_exception_handling)],
+    ):
+        """Log user actions."""
+        if not user:
+            user_name = "anonymous"
+            user_display = ""
+        else:
+            # user_name = user.role
+            # user_display = user.username
+            user_name = getattr(user, "role", "unknown_role")
+            user_display = getattr(user, "username", "unknown_user")
+
+        # Extract basic request details
+        query_params = dict(request.query_params)
+        extra_fields = {
+            "path": request.url.path,
+            "method": request.method,
+            "query_params": query_params,
+        }
+
+        # Add JSON body if present
+        json_body = {}
+        try:
+            if request.headers.get("content-type") == "application/json":
+                json_body = await request.json()
+                extra_fields.update({k: secrets_masker.redact(v, k) for k, v 
in json_body.items()})
+        except Exception as e:
+            extra_fields["json_error"] = str(e)
+
+        # Merge query parameters and JSON body to extract key fields
+        params = {**query_params, **json_body}
+
+        # Extract relevant fields for logging
+        task_id = params.get("task_id")
+        dag_id = params.get("dag_id")
+        run_id = params.get("run_id") or params.get("dag_run_id")
+        logical_date = params.get("logical_date")
+
+        parsed_logical_date = None
+        if logical_date:
+            try:
+                parsed_date = pendulum.parse(logical_date, strict=False)
+                if isinstance(parsed_date, (pendulum.DateTime, pendulum.Date)):
+                    parsed_logical_date = parsed_date.isoformat()
+                else:
+                    extra_fields["logical_date_error"] = (
+                        f"Unsupported type for logical_date: 
{type(parsed_date).__name__}"
+                    )
+            except pendulum.exceptions.ParserError:
+                extra_fields["logical_date_error"] = f"Invalid logical_date: 
{logical_date}"
+
+        # Mask sensitive fields
+        fields_skip_logging = {
+            "csrf_token",
+            "_csrf_token",
+            "is_paused",
+        }
+        extra_fields = {k: v for k, v in extra_fields.items() if k not in 
fields_skip_logging}
+
+        # Set event name dynamically if not provided
+        event_name = event or request.url.path.strip("/").replace("/", ".")

Review Comment:
   You are using the `path` to get the `event_name` how will we dissociate a 
GET and a POST on for instance `/connections`. Both will end up with the same 
name. `connections`.
   
   I think we should keep using the function name, or log the method as well. 
`HTTP_METHOD some.url`. If we do that I'll add this to the breaking change list.



-- 
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]

Reply via email to