Skip to content

fix: Don't log if --json or --quiet#4202

Merged
Klaim merged 58 commits into
mamba-org:mainfrom
Klaim:no-logging-json-quiet
Jun 17, 2026
Merged

fix: Don't log if --json or --quiet#4202
Klaim merged 58 commits into
mamba-org:mainfrom
Klaim:no-logging-json-quiet

Conversation

@Klaim

@Klaim Klaim commented Mar 19, 2026

Copy link
Copy Markdown
Member

Description

While working on #4126 when checking issues on the CI I regularly hit failing tests where the json output failed to parse because of an error (usually because of #4201) plus the error was logged while --json was used, which should never happen.
This is a known and old issue, mainly visible while PRs are not stabilized yet.

This PR relies on the recently introduced logging system by completely deactivating it when --json or --quiet is parsed or if the related params passed to the Context constructor are true.

  • If --quiet is used, the logging system is then stopped, which means whatever existing logging handler will be removed, leading to logging records being completely ignored.
  • If --json is used, a special log-handler is setup to keep track of the log-records which pass the log-level filter (with no maximum count limit) and these records will be added to an array "log_history" as member of the JSON output object.
  • --json also guarantees that a JSON output will appear even if it's an empty JSON object (when returning without a log, which should be rare).
    • This breaks compatibility with previous JSON output as previously some commands resulted in a JSON array being output instead of an object.

Some consequences of the solution implemented in this PR:

  • using current (micro)mamba will never output any log-record as long as --json or --quiet (this doesnt apply to outputs which are not logs, like the json output);
  • It is possible for a libmamba user code to inject a new log-handler after processing the output params, which will potentially renabled log-record being output if libmamba's code doesnt properly prevent it because of --json or --quiet; although in the present codebase we never do that and don't expect users to try switching log-handlers after initialization (except for testing).
  • It is possible to emit logs before reaching the point where the logging system is stopped, that is at Context construction or when --json or --quiet are parsed (we are not doing that in (micro)mamba).

Some alternative solutions we could have used here but rejected for now:

  1. Make the logging system simply ignore all log-records if one of these flags is set, even if a log-handler is installed. This is more drastic, but might be overkill. It also implies some communication between the output and logging systems.
  2. Implement the choice of handling log-records or not depending on these flags in the default log-handler. This solution moves the responsibility into the log-handler but it also is quite fragile if libmamba users want to use another log-handler (python handling for example).

Note: I also fixed an incorrect log-level filter in LogHandler_History which is now revealed by a more complete set of tests.

Type of Change

  • Bugfix
  • Feature / enhancement
  • CI / Documentation
  • Maintenance

Checklist

  • My code follows the general style and conventions of the codebase, ensuring consistency
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new warnings
  • I have run pre-commit run --all locally in the source folder and confirmed that there are no linter errors.
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing tests pass locally with my changes

@github-actions github-actions Bot added the release::bug_fixes For PRs fixing bugs label Mar 19, 2026
@Klaim

Klaim commented Mar 19, 2026

Copy link
Copy Markdown
Member Author

Might fix #3763

@codecov

codecov Bot commented Mar 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 12.06897% with 153 lines in your changes missing coverage. Please review.
✅ Project coverage is 54.58%. Comparing base (a3c2580) to head (855eb58).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
libmamba/include/mamba/core/logging_tools.hpp 0.00% 46 Missing ⚠️
micromamba/src/main.cpp 0.00% 36 Missing ⚠️
libmamba/src/core/output.cpp 2.94% 33 Missing ⚠️
libmamba/src/api/configuration.cpp 50.00% 7 Missing ⚠️
micromamba/src/env.cpp 0.00% 7 Missing ⚠️
micromamba/src/umamba.cpp 0.00% 5 Missing ⚠️
libmamba/src/api/repoquery.cpp 0.00% 4 Missing ⚠️
libmamba/src/core/context.cpp 55.55% 4 Missing ⚠️
libmamba/include/mamba/core/output.hpp 0.00% 3 Missing ⚠️
libmamba/src/api/create.cpp 0.00% 3 Missing ⚠️
... and 3 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4202      +/-   ##
==========================================
- Coverage   54.75%   54.58%   -0.18%     
==========================================
  Files         240      240              
  Lines       30145    30254     +109     
  Branches     3214     3234      +20     
==========================================
+ Hits        16505    16513       +8     
- Misses      13637    13738     +101     
  Partials        3        3              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Klaim Klaim force-pushed the no-logging-json-quiet branch from d8e6bdd to 91e8d11 Compare March 25, 2026 18:22
@Klaim

Klaim commented Mar 27, 2026

Copy link
Copy Markdown
Member Author

The failing test is actually not outputting any json, which suggests that the error is not properly handled with --json.

@Klaim Klaim force-pushed the no-logging-json-quiet branch from 475f7cd to c42b253 Compare March 30, 2026 10:06
@Klaim

Klaim commented Mar 30, 2026

Copy link
Copy Markdown
Member Author

@JohanMabille @jjerphan

I had to go a bit farther for this pr:

  1. Some commands like (micro)mamba config list --json expects a JSON or YAML output which is generated by some other code than the Console, so when they succeed I had to not output the normal JSON to avoid output 2 JSON objects.
    -> I suspect that it would be better to fusion the normal Console json output with the generated one? Otherwise no log history in these cases. If yes, how should I handle the YAML case?
  2. The errors at (micro)mamba arguments parsing step were emitted before switching to the json output mode, as expected because we only know that we will output json after doing that parsing. However that leads to harder to predict output when --json is enabled.
    -> I decided to attempt to pre-parse --json and --quiet so that we setup the JSON output (with log history) as soon as possible. This leads to this output at the moment (with a randomly named flag):
{
  "log_history": [
      {
          "level": "critical",
          "location": "E:\\QuantStack\\mamba-origin\\micromamba\\src\\main.cpp:188:9 int __cdecl main(int,char **)",
          "message": "The following argument was not expected: --fgsonsdgniodfsgndfoigndfoigndfo",
          "source": "libmamba"
      }
  ]
}

The missing part is the message to use --help, I can add it. It was injected automatically by CLI11's macro which was completely bypassing our error handling system so I changed that too.
Is this still consistent with the expectation to get JSON output whatever is happening when requestion --json? Or do we expect normal error output on arguments parsing errors? If we do, I can remove the specific commit(s) forcing json.

@jjerphan

jjerphan commented Mar 30, 2026

Copy link
Copy Markdown
Member

-> I suspect that it would be better to fusion the normal Console json output with the generated one? Otherwise no log history in these cases. If yes, how should I handle the YAML case?

Yes, the output should be handle via the Console IMO, and I would convert YAML output to JSON (#4158 once finished should eventually helped).

Is this still consistent with the expectation to get JSON output whatever is happening when requestion --json? Or do we expect normal error output on arguments parsing errors? If we do, I can remove the specific commit(s) forcing json.

To me the behavior that you describe is consistent with what we are expecting.

@Klaim Klaim force-pushed the no-logging-json-quiet branch from d07bc8e to c68cff6 Compare March 31, 2026 15:02
@Klaim

Klaim commented Mar 31, 2026

Copy link
Copy Markdown
Member Author

Yes, the output should be handle via the Console IMO, and I would convert YAML output to JSON (#4158 once finished should eventually helped).

It looks like the YAML output is only when --json is not used, so I think it should be fine.

@Klaim Klaim mentioned this pull request Apr 3, 2026
11 tasks
@Klaim Klaim force-pushed the no-logging-json-quiet branch 3 times, most recently from ae08936 to d7a5aab Compare April 8, 2026 15:59
@Klaim Klaim force-pushed the no-logging-json-quiet branch 3 times, most recently from da0d44a to 0ac5416 Compare April 17, 2026 11:21
@Klaim Klaim force-pushed the no-logging-json-quiet branch from 0ac5416 to 5e50a61 Compare April 22, 2026 09:41
@Klaim Klaim force-pushed the no-logging-json-quiet branch from 5e50a61 to f413d1c Compare May 4, 2026 09:01
@Klaim Klaim marked this pull request as ready for review June 9, 2026 13:51

@jjerphan jjerphan left a comment

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.

LGTM from a functional point of view modulo a few comments and questions.

As discussed privately, there is still some fuzz between directs usage of std::{cout,cerr} and of the Console, but this goes way beyond the scope of this PR.

Comment thread libmamba/include/mamba/core/logging_tools.hpp
Comment thread libmamba/src/api/configuration.cpp
Comment thread micromamba/tests/helpers.py
Comment thread micromamba/src/umamba.cpp
Comment thread libmamba/include/mamba/core/logging_tools.hpp Outdated
Comment thread libmamba/tests/libmamba_logging/include/mamba/testing/test_logging_common.hpp Outdated
Comment thread micromamba/src/env.cpp

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.

We can simplify / fix the explicit environment export in another PR.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I'm not 100% sure I understand what you mean, but to clarify why this file is modified:
this PR is fixing the whole json output so that it all go into the same system and we can properly make sure that no non-json (usually errors) is emitted in that case. The change in this file is necessary to achieve that (along with any other json output code that was not using Console).
Note that the resulting output was not changed, it just goes into the proper system but the output json object is the same (with just the "log_history" field added, but otherwise same).
Or am I missing something/misunderstanding your comment?

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.

I meant that there are still many direct usages of stdout here and some TODO which we can treat later in another PR.

Comment thread micromamba/tests/test_create.py Outdated
assert any(pkg["name"] == "x264" for pkg in res["actions"]["LINK"])


@pytest.mark.timeout(30)

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.

With the recent changes regarding should_be_emitted, might have logs been reduced? On my machine, it now takes 14.47s to run.

Can we try reverting this?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It's the same issue we see in the rest of the CI so I dont think it has anything to do with any of the changes here?
Reverting this will probably work as long as the issue is random.

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.

I guess I meant that we can ignore this failure and treat it independently so that the timeout does not get increased in this PR.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes will revert

Comment thread micromamba/src/main.cpp Outdated
Comment on lines +46 to +62
mamba::OutputParams output_params;
for (const char* arg : std::ranges::subrange(argv, argv + argc))
{
if (arg == "--json"sv)
{
output_params.json = true;
}
else if (arg == "--quiet"sv)
{
output_params.quiet = true;
}
}

if (output_params.json or output_params.quiet)
{
options.output_params = output_params;
}

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.

Nit: Why use an intermediary mamba::OutputParams?

@Klaim Klaim Jun 16, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

mamba::ContextOptions::output_params is an optional which is empty by default, so here we only set it if one of the parameters is set. The behavior of the program is different if that optional is empty vs not-empty with members set to false. That's why we need to set the optional only if the right condition is met.

For reading clarity I will rewrite this with .emplace.

Comment thread micromamba/src/main.cpp Outdated
Comment thread libmamba/src/core/util.cpp Outdated
Comment thread libmamba/src/api/configuration.cpp Outdated

if (this->at("print_config_only").value<bool>())
{
// TODO: fix this for the case where `--json` is used

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.

Should we open an issue which tracks all the TODO introduced in this PR such as this one?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I'll double check but I thinkt his TODO is not up to date, print_dump now outputs to the normal json output, if I'm not mistaken, when --json is used, was not the case before.

*/
void cancel_json_print();

// FIXME: documentation

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.

Did you mean to introduce a small docstring?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I forgot to add the docstring yes, will add.

@@ -67,14 +96,15 @@ namespace mamba::logging
*/
auto push_if_enabled(LogRecord& record) -> bool

@JohanMabille JohanMabille Jun 11, 2026

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.

I am still uncomfortabe with this API and the move below. Given that this function is called here and here only, that the LogRecord is passed by value and is not used after the call to push_if_enabled, I think it is safe to accept it by rvalue reference here instead of lvalue reference.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

When this function returns false, in both usage we move the record in another container, so I can't just take by r-value-reference without at least returning the object again so that it can be used by the caller, otherwise at the call point it would look like we move the object twice, which is even weirder.

if (not backtrace.push_if_enabled(std::move(record)))
{
    something_else(std::move(record)); // looks like UB
}

My goal here is to have this algorithm written only once, as having the same logic in both place lead to some annoying errors while developing. But I also agree it's quite weird api.

I'll try to find another way to achieve that goal with a nicer api. I was thinking maybe something in that direction:

// free function:
auto push_to_backtrace_or(Record&& record, BasicBacktrace& backtrace, std::invocable<Record&&> auto or_func);

// in call site:
push_to_backtrace_or(std::move(record), backtrace, [&](LogRecord&& record){ 
   // do the work if not pushed to backtrace
   something_else(std::move(record));
});

The passing of the same object to the lambda is a bit weird but maybe less weird than this current api?

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.

I was thinking of having push_to_backtrace returning the log record if not pushed (can be an optional, or a pair<bool, LogRecord>), but that would make the syntax heavier. Passing a lambda as you suggest seems the best option in the end, I don't find it weird to pass the same object to the lambda; and it is not that obvious at the call site.

@JohanMabille JohanMabille left a comment

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.

LGTM, the error on Windows is unrelated to this PR. The failure on OSX is flaky (timeout), restarting the job should fix it.

@Klaim

Klaim commented Jun 16, 2026

Copy link
Copy Markdown
Member Author

LGTM, the error on Windows is unrelated to this PR. The failure on OSX is flaky (timeout), restarting the job should fix it.

Yes, the OSX timeout was observed in other PRs (also on Linux runs, here and there) and I had doubled the timeout in this pr but we agreed with @jjerphan that it should be treated in another pr.

However, restarting the job is not guaranteed to fix it, it seems to happen often.

@jjerphan jjerphan left a comment

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.

LGTM. Can we use conventional commit for this PR title?

@Klaim

Klaim commented Jun 16, 2026

Copy link
Copy Markdown
Member Author

LGTM. Can we use conventional commit for this PR title?

Sure.
Although, with reluctance: I don't like how that "conventional" commit naming is reducing things like this pr which is not "just a bugfix" by misleadingly putting it into a narrow category. I'm also not convince that it helps tooling.
But I dont really mind otherwise, will rename👍🏽

@Klaim Klaim changed the title Don't log if --json or --quiet fix: Don't log if --json or --quiet Jun 16, 2026
@jjerphan

Copy link
Copy Markdown
Member

I agree that this the types of commit of conventional commits are a bit too coarse (I find it useful when considering doing a release), but is is this PR doing something else than fixing a wrong behavior of mamba?

@Klaim

Klaim commented Jun 16, 2026

Copy link
Copy Markdown
Member Author

I agree that this the types of commit of conventional commits are a bit too coarse (I find it useful when considering doing a release), but is is this PR doing something else than fixing a wrong behavior of mamba?

It does fix the wrong general behavior around the json output but the issue is what that implies, the consequences of the fix:

  • it changes the output of many --json cases (notably the ones which previously resulting in arrays [ ... ] instead of an object { ... })
  • it adds loggs where they were not previously, in the json object, which is an important consequence of making sure errors dont appear in json output (we still need them to appear somewhere). Taken alone you could consider this a "feature" in some ways, or an improvement.

I wouldnt call that a feature either so fix is the closest I guess.

Personally for helping with release changelogs (and general communication around releases) in other projects I worke(d) on I tend to prefer distinguishing "changes" from "fixes" only. This means "fixes" are only changing the implementation, not the interfaces, and correcting behaviors. "Changes" are everything else. Some are big, some small, some might or not break the interface or widely change the behavior.
That's just personal preference though.

But really I'm nitpicking and acting like an old man waving his cane to the clouds, don't mind me 🙈 It's not that important :)

@jjerphan

jjerphan commented Jun 16, 2026

Copy link
Copy Markdown
Member

I see and I agree. My assumption is/was that one can split changes into smaller ones fitting the types of conventional commits.

Here "enhancement" (enh) could work?

Anyway, it is not really a big deal.

@Klaim

Klaim commented Jun 16, 2026

Copy link
Copy Markdown
Member Author

I see and I agree. My assumption is/was that one can split changes into smaller ones fitting the types of conventional commits.

Yes, IFF it does make sense to split. Not the case here though IMO. The things I pointed are direct consequences of the core changes of the pr, not really separable except if we accept weird situations where main commits are allowed to not actually working correctly/pass all tests, which AFAIK we don't.

(to clarify, the only things that could be separated here is the improvement + fixes on LogHandler_History etc., which had some issues, but that doesnt change my point about the consequences which stem from the core changes)

Here "enhancement" (enh) could work?

If we could I would say it's both fix and enhancement. My understanding is that we cannot set both?

@jjerphan

Copy link
Copy Markdown
Member

To me, fixes are one kind of enhancements which themselves are more generic (enhancements as improvements of existing aspect of a project).

@Klaim Klaim merged commit 1cdb572 into mamba-org:main Jun 17, 2026
34 of 36 checks passed
@Hind-M Hind-M mentioned this pull request Jun 18, 2026
11 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release::bug_fixes For PRs fixing bugs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants