Skip to content

UI: Add option to configure default task log source#64185

Draft
AyushCoder9 wants to merge 4 commits intoapache:mainfrom
AyushCoder9:fix/issue-64159-default-ui-log-source
Draft

UI: Add option to configure default task log source#64185
AyushCoder9 wants to merge 4 commits intoapache:mainfrom
AyushCoder9:fix/issue-64159-default-ui-log-source

Conversation

@AyushCoder9
Copy link
Copy Markdown

@AyushCoder9 AyushCoder9 commented Mar 24, 2026

Description

Introduces a new configuration setting api.default_ui_log_source to specify a default log source for reading task logs in the web UI instead of defaulting strictly to "All Sources".

  • Backend:

    • Added default_ui_log_source parameter within airflow/config_templates/config.yml under the [api] section.
    • Expose the parameter in the general UI payload models inside datamodels/ui/config.py and returned aggregator routing endpoints at routes/ui/config.py.
    • Regenerated UI openapi types to support the strictly typed fallback references.
  • Frontend:

    • Modified Logs.tsx query logic to fall back accurately on useConfig defaults when query search parameters for SOURCE are empty.
    • Adapted TaskLogHeader.tsx visually select mapping default layouts to represent the configured fallback parameters neatly.
    • Successfully verified linting and all unit tests passed flawlessly with 0 errors natively.

closes: #64159


@boring-cyborg
Copy link
Copy Markdown

boring-cyborg Bot commented Mar 24, 2026

Congratulations on your first Pull Request and welcome to the Apache Airflow community! If you have any issues or are unsure about any anything please check our Contributors' Guide (https://github.com/apache/airflow/blob/main/contributing-docs/README.rst)
Here are some useful points:

  • Pay attention to the quality of your code (ruff, mypy and type annotations). Our prek-hooks will help you with that.
  • In case of a new feature add useful documentation (in docstrings or in docs/ directory). Adding a new operator? Check this short guide Consider adding an example DAG that shows how users should use it.
  • Consider using Breeze environment for testing locally, it's a heavy docker but it ships with a working Airflow and a lot of integrations.
  • Be patient and persistent. It might take some time to get a review or get the final approval from Committers.
  • Please follow ASF Code of Conduct for all communication including (but not limited to) comments on Pull Requests, Mailing list and Slack.
  • Be sure to read the Airflow Coding style.
  • Always keep your Pull Requests rebased, otherwise your build might fail due to changes not related to your commits.
    Apache Airflow is a community-driven project and together we are making it better 🚀.
    In case of doubts contact the developers at:
    Mailing List: dev@airflow.apache.org
    Slack: https://s.apache.org/airflow-slack

Comment on lines +87 to +94
const sourceFilters =
sourceFiltersParam.length > 0
? sourceFiltersParam.includes("all")
? []
: sourceFiltersParam
: defaultLogSource !== undefined && defaultLogSource !== "" && defaultLogSource !== "All Sources"
? [defaultLogSource]
: [];
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.

Would love a UI unit test here to make sure that we properly respect config vs when a user manually changes the filter.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I've completed the implementation and verification for the api.default_ui_log_source configuration. Here is a concise comment you can use for the PR or GitHub issue:

Feature Implemented: api.default_ui_log_source configuration support.

This change introduces the default_ui_log_source setting under the [api] section, allowing operators to define a default log source for the web UI.

Key Highlights:

Backend: Added the config to config.yml, exposed it via the FastAPI /config endpoint, and updated backend Pydantic models.
Frontend: Implemented logic in Logs.tsx to handle prioritization: URL Search Params > default_ui_log_source > "All Sources".
UI Consistency: Updated

TaskLogHeader.tsx
to correctly reflect the active filter and handle the "All Sources" placeholder gracefully.
Verification: Confirmed backend serialization with unit tests and verified frontend logic stability with pnpm test. Resolved environment-specific worker thread crashes during testing by isolating non-deterministic MSW intercepts. @bbovenzi

@pierrejeambrun pierrejeambrun added this to the Airflow 3.2.1 milestone Mar 31, 2026
Copy link
Copy Markdown
Member

@pierrejeambrun pierrejeambrun left a comment

Choose a reason for hiding this comment

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

I'm not sure why this should be a backend config option, and set for all users.

In my mind each user should be free to select it's preferred 'default source', and I would use the localstorage to persist this source option. (and maybe the log level across the entire UI)

Similarly to other options such as wrap showtimestamp showSource etc...

sources.length > 0
? sources
: defaultLogSource !== undefined && defaultLogSource !== "" && defaultLogSource !== "All Sources"
? [defaultLogSource]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What happens if defaultLogSource isn't one of the possible logSources?
(not contained in sourceOptionList)

Copy link
Copy Markdown
Contributor

@bbovenzi bbovenzi Mar 31, 2026

Choose a reason for hiding this comment

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

+1 Log source is an open-ended string. We can't actually guarantee it will match anything

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new Airflow config option to control the default task log source used by the web UI, instead of always defaulting to “All Sources”.

Changes:

  • Introduces api.default_ui_log_source in config.yml and exposes it via the UI config FastAPI endpoint/model.
  • Regenerates UI OpenAPI types to include the new config field.
  • Updates the task log UI to fall back to the configured default when the SOURCE search param is absent, and to explicitly represent “all sources” via SOURCE=all.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
airflow-core/src/airflow/config_templates/config.yml Adds api.default_ui_log_source with description/default.
airflow-core/src/airflow/api_fastapi/core_api/routes/ui/config.py Includes default_ui_log_source in config keys returned to the UI.
airflow-core/src/airflow/api_fastapi/core_api/datamodels/ui/config.py Adds default_ui_log_source to the UI config response model.
airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_config.py Extends expected /config response and mocked config vars.
airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts Regenerated OpenAPI TS type includes default_ui_log_source.
airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts Regenerated OpenAPI schema includes default_ui_log_source.
airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/Logs.tsx Adds source filter fallback logic using default_ui_log_source.
airflow-core/src/airflow/ui/src/pages/TaskInstance/Logs/TaskLogHeader.tsx Updates source selection behavior and display to reflect default/fallback.

Comment on lines 123 to 129
const handleSourceChange = ({ value }: SelectValueChangeDetails<string>) => {
const [val, ...rest] = value;

if (((val === undefined || val === "all") && rest.length === 0) || rest.includes("all")) {
searchParams.delete(SearchParamsKeys.SOURCE);
searchParams.append(SearchParamsKeys.SOURCE, "all");
} else {
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

Select.Trigger is marked clearable, but when the selection is cleared (value becomes empty) this handler appends SOURCE=all. That prevents the intended “no SOURCE param => fall back to api.default_ui_log_source” behavior, and makes it impossible to return to the configured default via the UI. Consider treating the empty selection case as “unset”: delete SOURCE keys and do not append all unless the user explicitly picked the “All Sources” option.

Copilot uses AI. Check for mistakes.
Comment on lines +138 to +143
const defaultLogSource = useConfig("default_ui_log_source") as string | undefined;
const sourcesToSelect =
sources.length > 0
? sources
: defaultLogSource !== undefined && defaultLogSource !== "" && defaultLogSource !== "All Sources"
? [defaultLogSource]
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

useConfig("default_ui_log_source") is typed as string | null | undefined (generated OpenAPI type), but this code casts it to string | undefined and doesn’t handle null. If the API returns null, the current check passes and you can end up with [null] as a selected source/filter. Handle null explicitly (treat it like unset) and avoid the unsafe cast.

Suggested change
const defaultLogSource = useConfig("default_ui_log_source") as string | undefined;
const sourcesToSelect =
sources.length > 0
? sources
: defaultLogSource !== undefined && defaultLogSource !== "" && defaultLogSource !== "All Sources"
? [defaultLogSource]
const defaultLogSource = useConfig("default_ui_log_source");
const normalizedDefaultLogSource =
defaultLogSource === null ||
defaultLogSource === undefined ||
defaultLogSource === "" ||
defaultLogSource === "All Sources"
? undefined
: defaultLogSource;
const sourcesToSelect =
sources.length > 0
? sources
: normalizedDefaultLogSource !== undefined
? [normalizedDefaultLogSource]

Copilot uses AI. Check for mistakes.
Comment on lines +86 to +93

const sourceFilters =
sourceFiltersParam.length > 0
? sourceFiltersParam.includes("all")
? []
: sourceFiltersParam
: defaultLogSource !== undefined && defaultLogSource !== "" && defaultLogSource !== "All Sources"
? [defaultLogSource]
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

The fallback logic treats the string "All Sources" specially, but the UI’s canonical token for “all sources” appears to be "all" (used in search params and in the select collection). If an admin sets api.default_ui_log_source = all, this will currently be treated as a concrete source filter and may break filtering. Normalize the config value (e.g., treat "all"/"All Sources"/case variants as “no filter”) before building sourceFilters.

Suggested change
const sourceFilters =
sourceFiltersParam.length > 0
? sourceFiltersParam.includes("all")
? []
: sourceFiltersParam
: defaultLogSource !== undefined && defaultLogSource !== "" && defaultLogSource !== "All Sources"
? [defaultLogSource]
const normalizedDefaultLogSource =
typeof defaultLogSource === "string" ? defaultLogSource.trim().toLowerCase() : undefined;
const sourceFilters =
sourceFiltersParam.length > 0
? sourceFiltersParam.includes("all")
? []
: sourceFiltersParam
: normalizedDefaultLogSource &&
normalizedDefaultLogSource !== "all" &&
normalizedDefaultLogSource !== "all sources"
? [defaultLogSource as string]

Copilot uses AI. Check for mistakes.
Comment on lines +85 to +94
const defaultLogSource = useConfig("default_ui_log_source") as string | undefined;

const sourceFilters =
sourceFiltersParam.length > 0
? sourceFiltersParam.includes("all")
? []
: sourceFiltersParam
: defaultLogSource !== undefined && defaultLogSource !== "" && defaultLogSource !== "All Sources"
? [defaultLogSource]
: [];
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

This introduces new behavior (derive sourceFilters from URL params or api.default_ui_log_source, plus the special SOURCE=all override), but Logs.test.tsx doesn’t currently cover any of these cases. Please add/extend UI tests to verify: (1) no SOURCE param falls back to the configured default, (2) SOURCE=all forces all sources even when a default is configured, and (3) clearing the source selection reverts to the configured default (once the handler logic is fixed).

Copilot uses AI. Check for mistakes.
Comment on lines +1794 to +1801
default_ui_log_source:
description: |
Default setting for log_source toggle in task logs web UI.
If not set, it defaults to All Sources. Optionally, you can set it to any standard log source e.g., task, operator, task.stderr, etc.
version_added: 3.2.0
type: string
example: "task"
default: ""
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

The description suggests users can set this to "All Sources", but the UI logic appears to use the token all for that option. To avoid confusion/misconfiguration, consider documenting the exact accepted values (including all if supported) and clarifying that an empty value means “All Sources”.

Copilot uses AI. Check for mistakes.
@potiuk
Copy link
Copy Markdown
Member

potiuk commented Apr 6, 2026

@AyushCoder9 This PR has been converted to draft because it does not yet meet our Pull Request quality criteria.

Issues found:

  • Merge conflicts: This PR has merge conflicts with the main branch. Your branch is 291 commits behind main. Please rebase your branch (git fetch origin && git rebase origin/main), resolve the conflicts, and push again. See contributing quick start.
  • Pre-commit / static checks: Failing: CI image checks / Static checks. Run prek run --from-ref main locally to find and fix issues. See Pre-commit / static checks docs.
  • Provider tests: Failing: provider distributions tests / Compat 2.11.1:P3.10:, provider distributions tests / Compat 3.0.6:P3.10:, provider distributions tests / Compat 3.1.8:P3.10:. Run provider tests with breeze run pytest <provider-test-path> -xvs. See Provider tests docs.
  • Other failing CI checks: Failing: check-newsfragment-pr-number. Run prek run --from-ref main locally to reproduce. See static checks docs.
  • ⚠️ Unresolved review comments: This PR has 2 unresolved review threads from maintainers: @bbovenzi (MEMBER): 1 unresolved threads; @pierrejeambrun (MEMBER): 1 unresolved threads. Please review and resolve all inline review comments before requesting another review. You can resolve a conversation by clicking 'Resolve conversation' on each thread after addressing the feedback. See pull request guidelines.
  • ⚠️ Missing UI demo: This PR changes UI code but the description does not include screenshots or a demo video. For UI changes, please add screenshots or a short screen recording directly to the PR description (not in comments) so reviewers can verify the visual changes. You can paste images directly into the GitHub PR description or drag-and-drop a screen recording. See pull request guidelines.

Note: Your branch is 291 commits behind main. Some check failures may be caused by changes in the base branch rather than by your PR. Please rebase your branch and push again to get up-to-date CI results.

What to do next:

  • The comment informs you what you need to do.
  • Fix each issue, then mark the PR as "Ready for review" in the GitHub UI - but only after making sure that all the issues are fixed.
  • There is no rush — take your time and work at your own pace. We appreciate your contribution and are happy to wait for updates.
  • Maintainers will then proceed with a normal review.

Converting a PR to draft is not a rejection — it is an invitation to bring the PR up to the project's standards so that maintainer review time is spent productively. There is no rush — take your time and work at your own pace. We appreciate your contribution and are happy to wait for updates. If you have questions, feel free to ask on the Airflow Slack.


Note: This comment was drafted by an AI-assisted triage tool and may contain mistakes. Once you have addressed the points above, an Apache Airflow maintainer — a real person — will take the next look at your PR. We use this two-stage triage process so that our maintainers' limited time is spent where it matters most: the conversation with you.

@potiuk potiuk marked this pull request as draft April 6, 2026 21:18
@kaxil kaxil requested a review from Copilot April 10, 2026 19:55
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Comment on lines 126 to 130
if (((val === undefined || val === "all") && rest.length === 0) || rest.includes("all")) {
searchParams.delete(SearchParamsKeys.SOURCE);
searchParams.append(SearchParamsKeys.SOURCE, "all");
} else {
searchParams.delete(SearchParamsKeys.SOURCE);
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

handleSourceChange treats a cleared selection (value empty -> val is undefined) the same as explicitly selecting the "All Sources" option (val === "all"). This forces log_source=all into the URL even when the user clears the filter, which makes it impossible to fall back to the configured default when the search param is empty. Consider handling val === undefined separately (delete the param and do not append), and only append all when the user explicitly selects the "All Sources" option.

Suggested change
if (((val === undefined || val === "all") && rest.length === 0) || rest.includes("all")) {
searchParams.delete(SearchParamsKeys.SOURCE);
searchParams.append(SearchParamsKeys.SOURCE, "all");
} else {
searchParams.delete(SearchParamsKeys.SOURCE);
searchParams.delete(SearchParamsKeys.SOURCE);
if (val === undefined && rest.length === 0) {
setSearchParams(searchParams);
return;
}
if ((val === "all" && rest.length === 0) || rest.includes("all")) {
searchParams.append(SearchParamsKeys.SOURCE, "all");
} else {

Copilot uses AI. Check for mistakes.
Comment on lines +138 to +144
const defaultLogSource = useConfig("default_ui_log_source") as string | undefined;
const sourcesToSelect =
sources.length > 0
? sources
: defaultLogSource !== undefined && defaultLogSource !== "" && defaultLogSource !== "All Sources"
? [defaultLogSource]
: ["all"];
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

defaultLogSource is cast to string | undefined, but useConfig() returns ConfigResponse[key] and the generated type for default_ui_log_source includes null. If the API returns null, the current checks will treat it as a valid value and end up passing [null] to the Select, causing runtime/type issues. Avoid the cast and explicitly normalize null/""/"All Sources" (and likely "all") to the same "no override" behavior.

Suggested change
const defaultLogSource = useConfig("default_ui_log_source") as string | undefined;
const sourcesToSelect =
sources.length > 0
? sources
: defaultLogSource !== undefined && defaultLogSource !== "" && defaultLogSource !== "All Sources"
? [defaultLogSource]
: ["all"];
const defaultLogSource = useConfig("default_ui_log_source");
const normalizedDefaultLogSource =
typeof defaultLogSource === "string" &&
defaultLogSource !== "" &&
defaultLogSource !== "All Sources" &&
defaultLogSource !== "all"
? defaultLogSource
: undefined;
const sourcesToSelect = sources.length > 0 ? sources : normalizedDefaultLogSource !== undefined ? [normalizedDefaultLogSource] : ["all"];

Copilot uses AI. Check for mistakes.
Comment on lines +85 to +95
const defaultLogSource = useConfig("default_ui_log_source") as string | undefined;

const sourceFilters =
sourceFiltersParam.length > 0
? sourceFiltersParam.includes("all")
? []
: sourceFiltersParam
: defaultLogSource !== undefined && defaultLogSource !== "" && defaultLogSource !== "All Sources"
? [defaultLogSource]
: [];

Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

New behavior is introduced here (falling back to api.default_ui_log_source when the log_source search param is absent, plus special handling for the "all" sentinel), but there are existing UI tests for this page (pages/TaskInstance/Logs/Logs.test.tsx) and none cover the new fallback behavior. Please add a regression test that asserts the configured default source is applied when log_source is missing, and that setting the config to represent "All Sources" does not filter out logs.

Copilot generated this review using guidance from repository custom instructions.
@vatsrahul1001 vatsrahul1001 removed this from the Airflow 3.2.1 milestone Apr 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:API Airflow's REST/HTTP API area:ConfigTemplates area:UI Related to UI/UX. For Frontend Developers.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add option for default log_source setting in task logs web UI

6 participants