runtime: Cannot create environment variable for logging category with periods

The logging configuration often uses namespaces (or namespace like strings, separated by periods) as category keys.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  }
}

The logging and configuration docs agree on creating environment variables for deep configuration structures.

Logging__LogLevel__Microsoft=Information

But, you can’t use a period in a variable or environment variable name in any shell that I know of. It’s not disallowed by the spec, apparently, but the various shells seem to avoid period in their implementations.

https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_10_02 https://tldp.org/LDP/abs/html/gotchas.html

# Logging__LogLevel__Microsoft.Hosting.Lifetime=Information
bash: Logging__LogLevel__Microsoft.Hosting.Lifetime=Information: command not found

So, how do we do this?


Document Details

Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.


Document Details

Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 2
  • Comments: 24 (9 by maintainers)

Most upvoted comments

We are running into a similar issue. We are running into a kubernetes environment and there is no way to configure the LogLevel for anything within a subsystem (e.g. this does not work Logging__LogLevel__System.Net.Http.HttpClient=Warning. Trying to change this value with appsettings.json requires a change that goes through the entire CICD pipeline, rather than a configuration change that can be done at runtime as needed for production debugging (i.e. temporarily changing the LogLevel of a service to get more information).

Can this issue be re-opened?

I think this needs to be evaluated again, specifically for apps running in containers. We are not able to set an environment variable that contains a period for a container. So, we are not able to configure logging upon deployment. In our case we only want to turn on Informational logging for a specific library, so not to flood the logs.

We are currently tracking the same issue under #35989. I will proceed to close this particular issue, but please feel free to respond here or on the linked issue if you have any further questions or require assistance.

Please note that this is a feature request that can only be implemented in a future release. However, I can provide you with a workaround code that you can utilize in your application to temporarily resolve the issue until it is officially supported by the framework. The workaround involves creating a custom configuration provider to handle the replacement of dots in the environment variable. This workaround serves as an extension to the environment variable configuration provider, making it straightforward to implement by replacing the call to IConfigurationBuilder.AddEnvironmentVariables() with the new custom method, IConfigurationBuilder.AddCustomEnvironmentVariables().

public class CustomEnvironmentVariablesConfigurationProvider : EnvironmentVariablesConfigurationProvider
{
    internal const string DefaultDotReplacement = ":_";
    private string _dotReplacement; 
    public CustomEnvironmentVariablesConfigurationProvider(string? dotReplacement = DefaultDotReplacement) : base()
    {
        _dotReplacement = dotReplacement ?? DefaultDotReplacement;
    }

    public CustomEnvironmentVariablesConfigurationProvider(string? prefix, string? dotReplacment = DefaultDotReplacement) : base(prefix)
    {
        _dotReplacement = dotReplacment ?? DefaultDotReplacement;
    }

    public override void Load()
    {
        base.Load();

        Dictionary<string, string?> data = new Dictionary<string, string?>();

        foreach (KeyValuePair<string, string?> kvp in Data)
        {
            if (kvp.Key.Contains(_dotReplacement))
            {
                data.Add(kvp.Key.Replace(_dotReplacement, ".", StringComparison.OrdinalIgnoreCase), kvp.Value);
            }
            else
            {
                data.Add(kvp.Key, kvp.Value);
            }
        }

        Data = data;
    }
}

public class CustomEnvironmentVariablesConfigurationSource : IConfigurationSource
{
    public string? Prefix { get; set; }
    public string? DotReplacement { get; set; }

    public IConfigurationProvider Build(IConfigurationBuilder builder)
    {
        return new CustomEnvironmentVariablesConfigurationProvider(Prefix, DotReplacement);
    }
}

public static class CustomEnvironmentVariablesExtensions
{
    public static IConfigurationBuilder AddCustomEnvironmentVariables(this IConfigurationBuilder configurationBuilder)
    {
        configurationBuilder.Add(new CustomEnvironmentVariablesConfigurationSource());
        return configurationBuilder;
    }

    public static IConfigurationBuilder AddCustomEnvironmentVariables(this IConfigurationBuilder configurationBuilder, string? prefix, string? dotReplacement = CustomEnvironmentVariablesConfigurationProvider.DefaultDotReplacement)
    {
        configurationBuilder.Add(new CustomEnvironmentVariablesConfigurationSource { Prefix = prefix, DotReplacement = dotReplacement });
        return configurationBuilder;
    }

    public static IConfigurationBuilder AddCustomEnvironmentVariables(this IConfigurationBuilder builder, Action<CustomEnvironmentVariablesConfigurationSource>? configureSource) => builder.Add(configureSource);
}

Using that, now you can define the environment variable with three underscores ___ in the places you want dot.

Logging__LogLevel__Microsoft___Hosting___Lifetime=Information    <-- Logging:LogLevel:Microsoft.Hosting.Lifetime=Information

I was able to set an environment variable locally in my bash environment with this name (based on this SO), it just needed quotes around it:

env "Logging__LogLevel__Microsoft.Hosting.Lifetime=Information"

I confirmed that I see it in the env vars too.

Hi @johnwc and others, I transferred this issue to the dotnet/runtime repo as this is a product issue. I believe that they’ll need to update the code itself to handle the desired substitution.

Hi @johnwc, Thanks for the follow-up. If that is the case, I’m not sure if this is a docs issue, it might need to get transferred to the product team if you believe that they’ll need to change how env vars are parsed. Let’s ask @maryamariyan, she might know.

See also https://github.com/dotnet/AspNetCore.Docs/issues/17378 - systemd also does not allow dots in env names.

I’m running into this same issue for container deployments. Any movement?

I’ve ran into exactly that this morning. We have a local development setup that overrides the appsettings through environment. It works with every variables but the log levels, which is quite ridiculous. While working with containers, you want to test your environment variables does not contain typos. Would it be possible to translate the log levels ‘.’ in ‘__’ just like the ‘:’ ?

Can you set it to

env "Logging__LogLevel__Microsoft.Hosting.Lifetime=Error"

and verify it’s working? Nice work @IEvangelist

Good find, thanks!

@IEvangelist fair points… but, we’re not supposed to use environment variables for secrets (and that’s not the only reason they’re used, see DOTNET_ENVIRONMENT and others) and we don’t always have an easy way to override or provide an appsettings file (thinking some CI/CD scenarios where your “services” like linked containers syntax is limited).

The environment variable provider doesn’t give special treatment to a .: It only substitutes __ for :. @Rick-Anderson and I looked into this before, but couldn’t find a way to make this particular setup work. I can’t see that anything has changed here, either.