Skip to content

Update macro_log typespec #10010

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

Conversation

lukaszsamson
Copy link
Contributor

log_allowed which is called by macro_log actually accepts location() or #{}. I noticed this issue when running dialyzer on elixir codebase. There are justified usages of macro_log with an empty map argument and dialyzer emits:

lib/logger.ex:1005:41: The call 'Elixir.Logger':'__do_log__'
         (_level@2 ::
              'alert' | 'critical' | 'debug' | 'emergency' | 'error' |
              'info' | 'notice' | 'warning',
          _message_or_fun@1 :: any(),
          #{},
          map()) will never return since it differs in the 3rd argument from the success typing arguments: 
         ('alert' | 'critical' | 'debug' | 'emergency' | 'error' |
          'info' | 'notice' | 'warning',
          any(),
          #{'file' := string(),
            'line' := non_neg_integer(),
            'mfa' := {atom(), atom(), non_neg_integer()}},
          map())

`log_allowed` which is called by `macro_log` actually accepts `location()` or `#{}`. I noticed this issue when running dialyzer on elixir codebase. There are justified usages of `macro_log` with an empty map argument and dialyzer emits:

```
lib/logger.ex:1005:41: The call 'Elixir.Logger':'__do_log__'
         (_level@2 ::
              'alert' | 'critical' | 'debug' | 'emergency' | 'error' |
              'info' | 'notice' | 'warning',
          _message_or_fun@1 :: any(),
          #{},
          map()) will never return since it differs in the 3rd argument from the success typing arguments: 
         ('alert' | 'critical' | 'debug' | 'emergency' | 'error' |
          'info' | 'notice' | 'warning',
          any(),
          #{'file' := string(),
            'line' := non_neg_integer(),
            'mfa' := {atom(), atom(), non_neg_integer()}},
          map())
```
Copy link
Contributor

github-actions bot commented Jul 1, 2025

CT Test Results

    2 files     70 suites   1h 6m 22s ⏱️
1 588 tests 1 333 ✅ 254 💤 1 ❌
1 840 runs  1 527 ✅ 312 💤 1 ❌

For more details on these failures, see this check.

Results for commit 8063623.

♻️ This comment has been updated with latest results.

To speed up review, make sure that you have read Contributing to Erlang/OTP and that all checks pass.

See the TESTING and DEVELOPMENT HowTo guides for details about how to run test locally.

Artifacts

// Erlang/OTP Github Action Bot

@IngelaAndin IngelaAndin added the team:VM Assigned to OTP team VM label Jul 2, 2025
@jhogberg
Copy link
Contributor

jhogberg commented Jul 7, 2025

There are justified usages of macro_log with an empty map argument and dialyzer emits:

macro_log/3,4,5 is undocumented (and explicitly noted as off-limits in logger.hrl), so there are no "justified usages." If we change how the macros work, the Elixir Logger will break because it's using this function. :-/

Since the calls land in log_allowed/4 immediately anyway, why not call that directly instead?

@jhogberg jhogberg self-assigned this Jul 7, 2025
@lukaszsamson
Copy link
Contributor Author

I'm not the autor of the elixir code so I don't know the reason macro_log was used. I can raise an issue on elixir tracker to make sure the elixir Logger depends on the right API.

Since the calls land in log_allowed/4 immediately anyway, why not call that directly instead?

I see that allow_log is not exported from https://github.com/erlang/otp/blob/master/lib/kernel/src/logger.erl

@jhogberg
Copy link
Contributor

jhogberg commented Jul 7, 2025

I'm not the autor of the elixir code so I don't know the reason macro_log was used. I can raise an issue on elixir tracker to make sure the elixir Logger depends on the right API.

Since the calls land in log_allowed/4 immediately anyway, why not call that directly instead?

I see that allow_log is not exported from https://github.com/erlang/otp/blob/master/lib/kernel/src/logger.erl

Sorry, I misread the code. log_allowed is indeed not exported, but as far as I can tell the same effect can be had by merging the location into the metadata before calling logger:log.

@lukaszsamson
Copy link
Contributor Author

@josevalim Do you know the reason elixir is using hidden macro_log?
@jhogberg What is stopping us from improving the spec?

@josevalim
Copy link
Contributor

Looking at the code I can see we have some logic between checking if something is allowed and then doing the logging but perhaps it is no longer necessary, since we have closed many of the gaps between Elixir and Erlang logging. I will take a another look.

@josevalim
Copy link
Contributor

josevalim commented Jul 27, 2025

We need to call logger:macro_log for the same reason that Erlang does it:

-define(DO_LOG(Level,Args),

We need to check for logger:allow and only then expand/evaluate the arguments given to the log macros. And, once we do so, we already checked if it is allowed, so no reason to do it again. Of course, it is no excuse to use private APIs and we would be on the hook if this changes, but I am not sure if this is enough to add a similar public function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
team:VM Assigned to OTP team VM
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants