Log Filtering#
Dify's log filtering system covers two log surfaces — chat/completion app logs (web/app/components/app/log/) and workflow logs (web/app/components/app/workflow-log/) — and shares a common set of time-period primitives. The filtering pipeline spans frontend UI components, client-side state caching, and a backend timezone conversion layer that safely converts user-local date strings to UTC before querying the database.
Key concerns:
- Time range picker UI: predefined periods and their evolution (simple select →
TimeRangePickercomponent → edition-aware variants) - Timezone-aware date queries: how the backend normalizes local times, including DST edge cases
- Filter state persistence: in-memory session cache and URL query-param strategies
- Advanced workflow log filters: recent additions for session ID, account email, and custom date ranges
Time Period Mapping & UI Components#
TIME_PERIOD_MAPPING#
Both chat and workflow log filters share TIME_PERIOD_MAPPING, defined in web/app/components/app/log/filter.tsx. It maps period IDs to day-count values:
| Key | Period | Value (days) |
|---|---|---|
1 | Today | 0 |
2 | Last 7 days | 7 (default) |
3 | Last 4 weeks | 28 |
4 | Last 3 months | dayjs.diff(3 months) |
5 | Last 12 months | dayjs.diff(12 months) |
6 | Month-to-date | dayjs.diff(startOf month) |
7 | Quarter-to-date | dayjs.diff(startOf quarter) |
8 | Year-to-date | dayjs.diff(startOf year) |
9 | All time | -1 (omits date params) |
Periods 4–8 use dayjs().diff() to compute day counts dynamically, accounting for variable month/quarter lengths. Period 9 is special-cased: when selected, no start/end params are sent to the API .
The default period on load is '2' (last 7 days) .
UI Evolution#
- Pre-Nov 2025: Simple dropdown (
SimpleSelect) inchart-view.tsxfor period selection. - Nov 2025 : Replaced with a dedicated
TimeRangePickercomponent set (index.tsx,range-selector.tsx,date-picker.tsx), enforcing a 30-day window limit and supporting localized date display. - Nov 2025 : Added
LongTimeRangePickerfor self-hosted (non-SaaS) deployments. Cloud defaults totoday; self-hosted defaults tolast 7 daysand exposes an "all time" option viaLONG_TIME_PERIOD_MAPPING. - Jun 2026 : Workflow logs UI extended with a custom
dateAfter/dateBeforedate range picker in addition to the predefined periods.
Timezone-Aware Backend Queries#
The frontend formats date boundaries as "YYYY-MM-DD HH:mm" strings in local (browser) time and passes them with the user's timezone name. The backend converts them to UTC in parse_time_range() (api/libs/datetime_utils.py) :
- Uses
pytz.timezone(tzname).localize(dt)to attach the correct offset. - DST ambiguity (clocks fall back): resolves to non-DST (
is_dst=False). - Non-existent times (clocks spring forward): advances by 1 hour before localizing.
- Validates that
start ≤ end, raisingValueErrorotherwise.
PR #32136 added Dayjs utc and timezone plugins on the frontend, ensuring that the start/end strings passed in period-based queries are computed in userProfile.timezone rather than the browser's local offset — fixing query mismatches for users in non-UTC timezones.
Note: All timestamps stored in the database are naive UTC (via
naive_utc_now()). Theparse_time_range()conversion ensures query boundaries align with stored values.
Filter State Persistence#
Chat/Completion Logs (log/index.tsx)#
Filter state is preserved across navigation using a module-level in-memory cache :
logsStateCache: Map<appId, { queryParams, currPage, limit }>
When the appId changes (navigating to another app), the current state is written to the cache. On mount, the component reads from the cache (falling back to defaultQueryParams) — this means filter selections survive clicks into log detail views and back.
Pagination (page) is also serialized to the URL via URLSearchParams, so a page-1 URL stays clean (the param is omitted when page === 1). PR #29256 introduced this cache to fix a bug where opening a log detail would clear all filter query params.
For the conversation detail drawer, conversation_id is persisted to the URL via the nuqs library , enabling shareable deep-links to specific log entries.
Workflow Logs (workflow-log/index.tsx)#
Workflow logs use React state only — no URL sync or in-memory cache. Default state is { status: 'all', period: '2' }. Filter changes reset currPage to 0. PR #37705 adds session ID, account email, and custom date range filters but keeps the same stateless approach.
workflow_run_id query param support was added in PR #34951 to allow deep-linking into a specific workflow run.
Key Source Files#
| File | Purpose |
|---|---|
web/app/components/app/log/filter.tsx | Chat log filter UI; defines TIME_PERIOD_MAPPING |
web/app/components/app/log/index.tsx | Chat log state management, logsStateCache, date range query construction |
web/app/components/app/log/list.tsx | Conversation list; conversation_id URL state via nuqs |
web/app/components/app/workflow-log/filter.tsx | Workflow log filter UI; shares TIME_PERIOD_MAPPING |
web/app/components/app/workflow-log/index.tsx | Workflow log state management (React state only) |
api/libs/datetime_utils.py | parse_time_range(), naive_utc_now(), ensure_naive_utc() |
Related PRs (chronological)#
| PR | Description |
|---|---|
Replaced SimpleSelect with TimeRangePicker component | |
Added LongTimeRangePicker for self-hosted edition | |
| Fixed filter state cleared when viewing log detail | |
Timezone-aware frontend date calculations; fixed updated_at mutation on log view | |
workflow_run_id URL param for workflow logs | |
| Advanced filters (session ID, account email, date range) for workflow logs |