Skip to content

feat: enhance tracing with HTTP client instrumentation and UI improvement#25

Open
qinsehm1128 wants to merge 2 commits intodoganarif:mainfrom
qinsehm1128:feature/tracing-enhancements
Open

feat: enhance tracing with HTTP client instrumentation and UI improvement#25
qinsehm1128 wants to merge 2 commits intodoganarif:mainfrom
qinsehm1128:feature/tracing-enhancements

Conversation

@qinsehm1128
Copy link
Copy Markdown
Contributor

@qinsehm1128 qinsehm1128 commented Oct 5, 2025

Summary

This PR enhances the tracing system with three major improvements:

  1. HTTP Client Instrumentation - Automatic tracing for outbound HTTP requests
  2. 🌍 Tracing UI Localization - Complete i18n support for the tracing interface
  3. 🔍 Search Functionality - Filter traces by operation name

Changes

1. HTTP Client Instrumentation

Added automatic instrumentation for popular Python HTTP clients:

  • requests - Sync HTTP client
  • httpx - Sync and async HTTP client
  • aiohttp - Async HTTP client

Each HTTP request now automatically creates a span with:

  • HTTP method and URL
  • Host information
  • Status code and response size
  • Error tracking with exception details

Example span tags:

{
  "component": "http",
  "http.method": "GET",
  "http.url": "https://api.example.com/users",
  "http.host": "api.example.com",
  "http.status_code": 200,
  "http.response_content_length": "1234"
}

2. Tracing UI Localization (i18n)

Complete internationalization support for the TracesList component:

Translated elements:
- Search placeholder: "Search by operation name..." / "按操作名称搜索..."
- Status filters: "All statuses", "Success", "Error" / "所有状态", "成功", "错误"
- Time ranges: "Last hour", "Last 6 hours", etc. / "最近1小时", "最近6小时"
- Error messages and empty states
- Table headers: "Service", "Spans", "Duration" / "服务", "Spans", "持续时间"

3. Search Functionality

Added search capability to filter traces by operation name:

Backend changes:
- Added search query parameter to GET /api/traces
- Filter traces using SQL ILIKE for case-insensitive matching
- Example: /api/traces?search=HTTP%20GET

Frontend changes:
- Wire search input to API calls in TracesList.tsx
- Update TypeScript client interface with search parameter
- Real-time filtering as user types

Technical Implementation

Session Management Fix

- Refactored get_waterfall_data to use session factory pattern
- Fixes DuckDB transaction conflicts in dev mode
- Separate session instances for parallel queries in /stats endpoint

Code Quality

- Improved error handling in trace detail endpoints
- Better logging for debugging trace queries

Testing

Tested with:
- Manual testing of HTTP instrumentation with httpx and requests
- UI testing with Chinese and English locales
- Search filtering with various operation names
- Multiple concurrent trace requests

Screenshots

Add screenshots of the localized UI and search functionality if desired

Breaking Changes

None. This is a backward-compatible enhancement.

## Summary by Sourcery

Enhance tracing by adding automatic outbound HTTP request instrumentation, UI localization, and operation-name search, while refactoring session management and improving error handling and logging in trace endpoints.

New Features:
- Automatically instrument outbound HTTP requests for requests, httpx, and aiohttp clients
- Localize tracing UI with English and Chinese translations for filters, placeholders, and labels
- Add search filter for operation names in the traces API and UI

Enhancements:
- Refactor get_waterfall_data to use session factory pattern and isolate sessions to prevent DuckDB transaction conflicts
- Improve error handling and logging in trace detail and waterfall endpoints
- Initialize HTTP client instrumentation during radar startup
- Extend TypeScript API client to support the new search parameter

Tests:
- Add smoke tests triggering HTTP client requests via httpx and requests to verify instrumentation

…ments

  This commit introduces three major improvements to the tracing system:

  1. **HTTP Client Instrumentation**
     - Add automatic tracing for outbound HTTP requests
     - Support for requests, httpx (sync/async), and aiohttp clients
     - Capture HTTP method, URL, host, status code, and response size
     - Automatic span creation and error tracking for HTTP calls

  2. **Tracing UI Localization**
     - Add complete i18n support for TracesList component
     - Translate search placeholder, status filters, time ranges
     - Translate error messages and empty states
     - Support both English and Chinese languages

  3. **Search Functionality**
     - Add search parameter to /api/traces endpoint
     - Enable filtering traces by operation name (ILIKE)
     - Wire up search input in frontend to backend API
     - Update TypeScript client with search parameter support

  Technical changes:
  - Instrument HTTP clients via monkey-patching in tracing.py
  - Refactor get_waterfall_data to use session factory pattern
  - Fix DuckDB transaction conflicts in trace detail endpoints
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Oct 5, 2025

Reviewer's Guide

This PR enhances the tracing system by integrating automatic HTTP client instrumentation, refactoring session management for waterfall data queries, adding search capabilities both in the API and UI, and fully localizing the tracing interface for English and Chinese.

Sequence diagram for automatic HTTP client instrumentation in tracing

sequenceDiagram
participant App
participant "requests/httpx/aiohttp"
participant Tracing
App->>"requests/httpx/aiohttp": Make HTTP request
activate "requests/httpx/aiohttp"
"requests/httpx/aiohttp"->>Tracing: _create_http_span(method, url)
Tracing-->>"requests/httpx/aiohttp": span_id
"requests/httpx/aiohttp"->>"requests/httpx/aiohttp": Perform HTTP request
alt Success
    "requests/httpx/aiohttp"->>Tracing: _finish_http_span(span_id, response)
else Error
    "requests/httpx/aiohttp"->>Tracing: _finish_http_span(span_id, error)
end
"requests/httpx/aiohttp"-->>App: Return response/error
Loading

File-Level Changes

Change Details Files
Automatic instrumentation of outbound HTTP clients
  • Define helpers to extract host, status code, and content length
  • Wrap requests, httpx (sync/async), and aiohttp methods to create and finish spans with tags
  • Trigger instrumentation in Radar initialization when tracing is enabled
fastapi_radar/tracing.py
fastapi_radar/radar.py
tests/test_async_radar.py
Refactor session management for waterfall data and API handlers
  • Convert get_waterfall_data to a classmethod using injected session factory
  • Update API router to use separate SessionLocal instances for stats and trace endpoints
  • Add get_session_factory method to expose sessionmaker for independent sessions
fastapi_radar/tracing.py
fastapi_radar/api.py
fastapi_radar/radar.py
Search functionality for traces
  • Add optional search parameter to GET /api/traces and apply SQL ILIKE filter
  • Extend TypeScript API client to append search param to requests
  • Wire search input in TracesList component for real-time filtering
fastapi_radar/api.py
fastapi_radar/dashboard/src/api/client.ts
fastapi_radar/dashboard/src/components/TracesList.tsx
Tracing UI localization (i18n)
  • Add English and Chinese translations for placeholders, filters, time ranges, messages and headers
  • Replace hard-coded strings in TracesList with translation hook invocations
fastapi_radar/dashboard/src/i18n/translations.ts
fastapi_radar/dashboard/src/components/TracesList.tsx

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes - here's some feedback:

  • Use FastAPI’s dependency injection for DB sessions instead of manually creating and closing SessionLocal in each endpoint to reduce boilerplate and improve safety.
  • Replace ad-hoc print and commented-out debug statements with a structured logger to keep production code clean and configurable.
  • Consider refactoring get_waterfall_data to accept a Session instance rather than a session factory for a more intuitive and consistent API.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Use FastAPI’s dependency injection for DB sessions instead of manually creating and closing SessionLocal in each endpoint to reduce boilerplate and improve safety.
- Replace ad-hoc print and commented-out debug statements with a structured logger to keep production code clean and configurable.
- Consider refactoring get_waterfall_data to accept a Session instance rather than a session factory for a more intuitive and consistent API.

## Individual Comments

### Comment 1
<location> `fastapi_radar/tracing.py:264-265` </location>
<code_context>
+            return rows
+
+        except Exception as e:
+            print(f"[ERROR] Exception in get_waterfall_data: {str(e)}")
+            print(f"[ERROR] Traceback: {traceback.format_exc()}")
+            session.rollback()
+            raise
</code_context>

<issue_to_address>
**suggestion:** Error logging uses print statements.

Switch to a logging framework to improve error tracking and observability in production.

Suggested implementation:

```python
        except Exception as e:
            import logging
            logger = logging.getLogger(__name__)
            logger.error(f"Exception in get_waterfall_data: {str(e)}")
            logger.error(f"Traceback: {traceback.format_exc()}")
            session.rollback()
            raise

```

If logging is already configured globally in your project, you can remove the `import logging` and `logger = logging.getLogger(__name__)` lines and use the existing logger instance. If not, consider configuring logging at the application entry point for consistent log formatting and output.
</issue_to_address>

### Comment 2
<location> `fastapi_radar/api.py:458-459` </location>
<code_context>
+        except HTTPException:
+            raise  # 直接抛出 HTTPException
+        except Exception as e:
+            print(f"[ERROR] Exception in get_trace_detail: {str(e)}")
+            print(f"[ERROR] Traceback: {traceback.format_exc()}")
+            session.rollback()
+            raise HTTPException(status_code=500, detail=str(e))
</code_context>

<issue_to_address>
**suggestion:** Error logging uses print statements in API endpoints.

Use a logging library instead of print statements to handle errors more effectively in production environments.

Suggested implementation:

```python
            import logging
            logging.error(f"Exception in get_trace_detail: {str(e)}")
            logging.error(f"Traceback: {traceback.format_exc()}")

```

If `logging` is already imported at the top of the file, you can remove the `import logging` line from inside the function.  
For production readiness, consider configuring the logging level and handlers at the application entry point (not shown in this snippet).
</issue_to_address>

### Comment 3
<location> `fastapi_radar/api.py:455-463` </location>
<code_context>
+                created_at=trace.created_at,
+                spans=[WaterfallSpan(**span) for span in waterfall_spans],
+            )
+        except HTTPException:
+            raise  # 直接抛出 HTTPException
+        except Exception as e:
+            print(f"[ERROR] Exception in get_trace_detail: {str(e)}")
</code_context>

<issue_to_address>
**suggestion:** Catching and re-raising HTTPException is redundant.

Remove the except block for HTTPException and allow it to propagate without catching.

```suggestion
        except Exception as e:
            print(f"[ERROR] Exception in get_trace_detail: {str(e)}")
            print(f"[ERROR] Traceback: {traceback.format_exc()}")
            session.rollback()
            raise HTTPException(status_code=500, detail=str(e))
        finally:
            session.close()
```
</issue_to_address>

### Comment 4
<location> `fastapi_radar/dashboard/src/components/TracesList.tsx:68` </location>
<code_context>

 export function TracesList({ className }: TracesListProps) {
   const { openDetail } = useDetailDrawer();
+  const t = useT();

   const [filters, setFilters] = useState({
</code_context>

<issue_to_address>
**suggestion:** Internationalization is applied to UI strings.

Please check that all user-facing strings, including hardcoded fallbacks like 'Unknown', use the translation function for complete localization.

Suggested implementation:

```typescript
          <div className="text-center">
            <AlertTriangle className="h-12 w-12 text-destructive mx-auto mb-4" />

```

```typescript
            <p className="text-muted-foreground">{t("no_traces_found", "No traces found")}</p>

```

```typescript
                <span className="font-mono text-xs text-muted-foreground">
                  {trace.service_name || t("unknown", "Unknown")}
                </span>

```

```typescript
                <span className="font-mono text-xs text-muted-foreground">
                  {trace.operation_name || t("unknown", "Unknown")}
                </span>

```
</issue_to_address>

### Comment 5
<location> `fastapi_radar/tracing.py:366` </location>
<code_context>
    trace_ctx.finish_span(span_id, status=status, tags=tags if tags else None)

</code_context>

<issue_to_address>
**suggestion (code-quality):** Replace if-expression with `or` ([`or-if-exp-identity`](https://docs.sourcery.ai/Reference/Rules-and-In-Line-Suggestions/Python/Default-Rules/or-if-exp-identity))

```suggestion
    trace_ctx.finish_span(span_id, status=status, tags=tags or None)
```

<br/><details><summary>Explanation</summary>Here we find ourselves setting a value if it evaluates to `True`, and otherwise
using a default.

The 'After' case is a bit easier to read and avoids the duplication of
`input_currency`.

It works because the left-hand side is evaluated first. If it evaluates to
true then `currency` will be set to this and the right-hand side will not be
evaluated. If it evaluates to false the right-hand side will be evaluated and
`currency` will be set to `DEFAULT_CURRENCY`.
</details>
</issue_to_address>

### Comment 6
<location> `fastapi_radar/tracing.py:239` </location>
<code_context>
    @classmethod
    def get_waterfall_data(
        cls, session_local, trace_id: str
    ) -> List[Dict[str, Any]]:
        """Return data for the waterfall view using the provided session."""


        #print(f"[DEBUG] get_waterfall_data called with trace_id: {trace_id}")

        waterfall_query = text(
            """
            WITH span_timeline AS (
                SELECT
                    s.span_id,
                    s.parent_span_id,
                    s.operation_name,
                    s.service_name,
                    s.start_time,
                    s.end_time,
                    s.duration_ms,
                    s.status,
                    s.tags,
                    COALESCE(r.depth, 0) as depth,
                    -- Offset relative to trace start
                    EXTRACT(EPOCH FROM (
                        s.start_time - MIN(s.start_time)
                            OVER (PARTITION BY s.trace_id)
                    )) * 1000 as offset_ms
                FROM radar_spans s
                LEFT JOIN radar_span_relations r ON s.span_id = r.child_span_id
                WHERE s.trace_id = :trace_id
            )
            SELECT * FROM span_timeline
            ORDER BY offset_ms, depth
        """
        )

        #print(f"[DEBUG] Getting SessionLocal from session_local function")
        SessionLocal = session_local()  # 获取SessionLocal (sessionmaker)
        #print(f"[DEBUG] SessionLocal type: {type(SessionLocal)}")

        session = SessionLocal()  # 创建实际的session实例
        #print(f"[DEBUG] Session created, type: {type(session)}")

        try:
            #print(f"[DEBUG] Executing waterfall query for trace_id: {trace_id}")
            result = session.execute(waterfall_query, {"trace_id": trace_id})
            #print(f"[DEBUG] Query executed successfully")

            # 不需要 commit,因为这是 SELECT 查询
            rows = []
            for row in result:
                rows.append({
                    "span_id": row.span_id,
                    "parent_span_id": row.parent_span_id,
                    "operation_name": row.operation_name,
                    "service_name": row.service_name,
                    "start_time": row.start_time.isoformat() if row.start_time else None,
                    "end_time": row.end_time.isoformat() if row.end_time else None,
                    "duration_ms": row.duration_ms,
                    "status": row.status,
                    "tags": row.tags,
                    "depth": row.depth,
                    "offset_ms": float(row.offset_ms) if row.offset_ms else 0.0,
                })

            #print(f"[DEBUG] Processed {len(rows)} rows from query result")
            return rows

        except Exception as e:
            print(f"[ERROR] Exception in get_waterfall_data: {str(e)}")
            print(f"[ERROR] Traceback: {traceback.format_exc()}")
            session.rollback()
            raise
        finally:
            #print(f"[DEBUG] Closing session")
            session.close()

</code_context>

<issue_to_address>
**issue (code-quality):** We've found these issues:

- Convert for loop into list comprehension ([`list-comprehension`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/list-comprehension/))
- Inline variable that is immediately returned ([`inline-immediately-returned-variable`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/inline-immediately-returned-variable/))
</issue_to_address>

### Comment 7
<location> `fastapi_radar/tracing.py:299-300` </location>
<code_context>
def _create_http_span(
    method: Optional[str], url: Optional[str]
) -> Tuple[Optional["TraceContext"], Optional[str]]:
    trace_ctx = get_current_trace_context()
    if not trace_ctx:
        return None, None

    method_name = str(method).upper() if method else "GET"
    url_value = str(url) if url is not None else ""
    tags: Dict[str, Any] = {
        "component": "http",
        "http.method": method_name,
        "http.url": url_value,
    }

    host = _extract_http_host(url_value)
    if host:
        tags["http.host"] = host

    span_id = trace_ctx.create_span(
        operation_name=f"HTTP {method_name}",
        span_kind="client",
        tags=tags,
    )

    return trace_ctx, span_id

</code_context>

<issue_to_address>
**suggestion (code-quality):** Use named expression to simplify assignment and conditional ([`use-named-expression`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-named-expression/))

```suggestion
    if host := _extract_http_host(url_value):
```
</issue_to_address>

### Comment 8
<location> `fastapi_radar/tracing.py:351-352` </location>
<code_context>
def _finish_http_span(
    trace_ctx: Optional["TraceContext"],
    span_id: Optional[str],
    response: Any = None,
    error: Optional[BaseException] = None,
) -> None:
    if not trace_ctx or not span_id:
        return

    tags: Dict[str, Any] = {}
    status = "ok"

    if response is not None:
        status_code = _extract_status_code(response)
        if status_code is not None:
            tags["http.status_code"] = status_code

        content_length = _extract_content_length(response)
        if content_length:
            tags["http.response_content_length"] = content_length

    if error is not None:
        status = "error"
        tags["error.type"] = type(error).__name__
        tags["error.message"] = str(error)
        trace_ctx.add_span_log(
            span_id,
            f"HTTP request failed: {error}",
            level="error",
            exception_type=type(error).__name__,
        )

    trace_ctx.finish_span(span_id, status=status, tags=tags if tags else None)

</code_context>

<issue_to_address>
**suggestion (code-quality):** Use named expression to simplify assignment and conditional ([`use-named-expression`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/use-named-expression/))

```suggestion
        if content_length := _extract_content_length(response):
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread fastapi_radar/tracing.py
Comment on lines +264 to +265
print(f"[ERROR] Exception in get_waterfall_data: {str(e)}")
print(f"[ERROR] Traceback: {traceback.format_exc()}")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Error logging uses print statements.

Switch to a logging framework to improve error tracking and observability in production.

Suggested implementation:

        except Exception as e:
            import logging
            logger = logging.getLogger(__name__)
            logger.error(f"Exception in get_waterfall_data: {str(e)}")
            logger.error(f"Traceback: {traceback.format_exc()}")
            session.rollback()
            raise

If logging is already configured globally in your project, you can remove the import logging and logger = logging.getLogger(__name__) lines and use the existing logger instance. If not, consider configuring logging at the application entry point for consistent log formatting and output.

Comment thread fastapi_radar/api.py
Comment on lines +458 to +459
print(f"[ERROR] Exception in get_trace_detail: {str(e)}")
print(f"[ERROR] Traceback: {traceback.format_exc()}")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Error logging uses print statements in API endpoints.

Use a logging library instead of print statements to handle errors more effectively in production environments.

Suggested implementation:

            import logging
            logging.error(f"Exception in get_trace_detail: {str(e)}")
            logging.error(f"Traceback: {traceback.format_exc()}")

If logging is already imported at the top of the file, you can remove the import logging line from inside the function.
For production readiness, consider configuring the logging level and handlers at the application entry point (not shown in this snippet).

Comment thread fastapi_radar/api.py
Comment on lines +455 to +463
except HTTPException:
raise # 直接抛出 HTTPException
except Exception as e:
print(f"[ERROR] Exception in get_trace_detail: {str(e)}")
print(f"[ERROR] Traceback: {traceback.format_exc()}")
session.rollback()
raise HTTPException(status_code=500, detail=str(e))
finally:
session.close()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Catching and re-raising HTTPException is redundant.

Remove the except block for HTTPException and allow it to propagate without catching.

Suggested change
except HTTPException:
raise # 直接抛出 HTTPException
except Exception as e:
print(f"[ERROR] Exception in get_trace_detail: {str(e)}")
print(f"[ERROR] Traceback: {traceback.format_exc()}")
session.rollback()
raise HTTPException(status_code=500, detail=str(e))
finally:
session.close()
except Exception as e:
print(f"[ERROR] Exception in get_trace_detail: {str(e)}")
print(f"[ERROR] Traceback: {traceback.format_exc()}")
session.rollback()
raise HTTPException(status_code=500, detail=str(e))
finally:
session.close()


export function TracesList({ className }: TracesListProps) {
const { openDetail } = useDetailDrawer();
const t = useT();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Internationalization is applied to UI strings.

Please check that all user-facing strings, including hardcoded fallbacks like 'Unknown', use the translation function for complete localization.

Suggested implementation:

          <div className="text-center">
            <AlertTriangle className="h-12 w-12 text-destructive mx-auto mb-4" />
            <p className="text-muted-foreground">{t("no_traces_found", "No traces found")}</p>
                <span className="font-mono text-xs text-muted-foreground">
                  {trace.service_name || t("unknown", "Unknown")}
                </span>
                <span className="font-mono text-xs text-muted-foreground">
                  {trace.operation_name || t("unknown", "Unknown")}
                </span>

Comment thread fastapi_radar/tracing.py
exception_type=type(error).__name__,
)

trace_ctx.finish_span(span_id, status=status, tags=tags if tags else None)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Replace if-expression with or (or-if-exp-identity)

Suggested change
trace_ctx.finish_span(span_id, status=status, tags=tags if tags else None)
trace_ctx.finish_span(span_id, status=status, tags=tags or None)


ExplanationHere we find ourselves setting a value if it evaluates to True, and otherwise
using a default.

The 'After' case is a bit easier to read and avoids the duplication of
input_currency.

It works because the left-hand side is evaluated first. If it evaluates to
true then currency will be set to this and the right-hand side will not be
evaluated. If it evaluates to false the right-hand side will be evaluated and
currency will be set to DEFAULT_CURRENCY.

Comment thread fastapi_radar/tracing.py
Comment on lines +299 to +300
host = _extract_http_host(url_value)
if host:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Use named expression to simplify assignment and conditional (use-named-expression)

Suggested change
host = _extract_http_host(url_value)
if host:
if host := _extract_http_host(url_value):

Comment thread fastapi_radar/tracing.py
Comment on lines +351 to +352
content_length = _extract_content_length(response)
if content_length:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Use named expression to simplify assignment and conditional (use-named-expression)

Suggested change
content_length = _extract_content_length(response)
if content_length:
if content_length := _extract_content_length(response):

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 8 files

Prompt for AI agents (all 3 issues)

Understand the root cause of the following 3 issues and fix them.


<file name="tests/test_async_radar.py">

<violation number="1" location="tests/test_async_radar.py:50">
This synchronous HTTP client call runs inside an async FastAPI handler, so it blocks the event loop until the network request finishes. Use an async client (e.g. `httpx.AsyncClient`) or offload to a thread instead.</violation>

<violation number="2" location="tests/test_async_radar.py:51">
This test now depends on reaching http://www.baidu.com, which makes it flaky in offline or firewalled environments. Replace the external call with a local mock or fixture so the test stays deterministic.</violation>
</file>

<file name="fastapi_radar/dashboard/src/components/TracesList.tsx">

<violation number="1" location="fastapi_radar/dashboard/src/components/TracesList.tsx:230">
Localize the fallback string instead of hardcoding &quot;Unknown&quot; so the UI remains fully translated (e.g., use t(&quot;pages.tracing.unknown&quot;, &quot;Unknown&quot;)).</violation>
</file>

React with 👍 or 👎 to teach cubic. Mention @cubic-dev-ai to give feedback, ask questions, or re-run the review.

Comment thread tests/test_async_radar.py
rows = result.mappings().all()
import httpx,requests
res1 = httpx.get("http://www.baidu.com")
res2 = requests.get("http://www.baidu.com")
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Oct 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test now depends on reaching http://www.baidu.com, which makes it flaky in offline or firewalled environments. Replace the external call with a local mock or fixture so the test stays deterministic.

Prompt for AI agents
Address the following comment on tests/test_async_radar.py at line 51:

<comment>This test now depends on reaching http://www.baidu.com, which makes it flaky in offline or firewalled environments. Replace the external call with a local mock or fixture so the test stays deterministic.</comment>

<file context>
@@ -46,6 +46,9 @@ async def get_users():
         rows = result.mappings().all()
+    import httpx,requests
+    res1 = httpx.get(&quot;http://www.baidu.com&quot;)
+    res2 = requests.get(&quot;http://www.baidu.com&quot;)
 
     return {&quot;users&quot;: [dict(row) for row in rows]}
</file context>
Fix with Cubic

Comment thread tests/test_async_radar.py
result = await session.execute(select(users_table))
rows = result.mappings().all()
import httpx,requests
res1 = httpx.get("http://www.baidu.com")
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Oct 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This synchronous HTTP client call runs inside an async FastAPI handler, so it blocks the event loop until the network request finishes. Use an async client (e.g. httpx.AsyncClient) or offload to a thread instead.

Prompt for AI agents
Address the following comment on tests/test_async_radar.py at line 50:

<comment>This synchronous HTTP client call runs inside an async FastAPI handler, so it blocks the event loop until the network request finishes. Use an async client (e.g. `httpx.AsyncClient`) or offload to a thread instead.</comment>

<file context>
@@ -46,6 +46,9 @@ async def get_users():
         result = await session.execute(select(users_table))
         rows = result.mappings().all()
+    import httpx,requests
+    res1 = httpx.get(&quot;http://www.baidu.com&quot;)
+    res2 = requests.get(&quot;http://www.baidu.com&quot;)
 
</file context>
Fix with Cubic

<div className="flex items-center gap-4 text-sm text-muted-foreground">
<span>Service: {trace.service_name || "Unknown"}</span>
<span>Spans: {trace.span_count}</span>
<span>{t("pages.tracing.service")}: {trace.service_name || "Unknown"}</span>
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Oct 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Localize the fallback string instead of hardcoding "Unknown" so the UI remains fully translated (e.g., use t("pages.tracing.unknown", "Unknown")).

Prompt for AI agents
Address the following comment on fastapi_radar/dashboard/src/components/TracesList.tsx at line 230:

<comment>Localize the fallback string instead of hardcoding &quot;Unknown&quot; so the UI remains fully translated (e.g., use t(&quot;pages.tracing.unknown&quot;, &quot;Unknown&quot;)).</comment>

<file context>
@@ -224,10 +227,10 @@ export function TracesList({ className }: TracesListProps) {
                       &lt;div className=&quot;flex items-center gap-4 text-sm text-muted-foreground&quot;&gt;
-                        &lt;span&gt;Service: {trace.service_name || &quot;Unknown&quot;}&lt;/span&gt;
-                        &lt;span&gt;Spans: {trace.span_count}&lt;/span&gt;
+                        &lt;span&gt;{t(&quot;pages.tracing.service&quot;)}: {trace.service_name || &quot;Unknown&quot;}&lt;/span&gt;
+                        &lt;span&gt;{t(&quot;pages.tracing.spans&quot;)}: {trace.span_count}&lt;/span&gt;
                         &lt;span&gt;
</file context>
Fix with Cubic

Comment thread fastapi_radar/api.py Outdated
Comment on lines +307 to +309
requests_session = SessionLocal() # 创建实际的session实例
queries_session = SessionLocal() # 创建实际的session实例
exceptions_session = SessionLocal() # 创建实际的session实例
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why create 3 database sessions?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want this either... Since duckdb does not support multiple begin, in order to use the sqlalchemy unified interface, I have no choice but to adopt this solution... Do you have any good methods? I really have no other good solutions for this place. Either I give up duckdb and just use sqlite directly

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The monitoring part was very simple, but I spent three days dealing with the part that had the 'begin' conflict, and ultimately chose this solution.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the issue you mention applies in this case, as these are all SELECT queries within the same session, so nothing is being commit.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can give this part a try. It might actually be my fault, but I really don't have a better solution for it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我认为您提到的问题不适用于这种情况,因为这些都是SELECT同一会话内的查询,因此没有提交任何内容。

As long as "execute" is performed, it means that "begin" has been activated. I saw someone mention this issue in the issues section of DuckDB. Perhaps it's because of the "with" dependency injection that automatically manages the lifecycle.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might that be related to the problem I reported? #45
The error (see details) looks like it would point in the general "no multiple '.begin' allowed" direction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants