Currently, console color is disabled by checking to see if the console output is redirected or not.
I understand why this was done because there are scenarios that don't support ANSI escape sequences and the output looks really ugly in those scenarios when a bunch of non-rendered ANSI escape sequences are mixed into the output of the CLI.
There is a standard that a lot of other CLI systems use to disable these ANSI escape sequences using an environment variable called NO_COLOR here: https://no-color.org/
This may seem trivial, but when all CI systems like Jenkins, AppVeyor, GitHub Actions and pretty much every single other CI system simply record the redirected console output of the build resulting in a big blob of monotone text and a human is expected to look at that output and quickly identify the issues then it becomes a big problem for usability with .NET Core as a whole.
I am proposing that .NET Core is changed to enable ANSI color sequences by default and that it checks for the NO_COLOR environment variable to disable them.
This issue is being created after a discussion about this topic here: https://github.com/microsoft/vstest/issues/2370
Also, I think it is relevant to show what other CLI tools are doing to detect color support in the NodeJS world. Here is the supports-color NPM package: https://github.com/chalk/supports-color/blob/master/index.js
Was thinking that the most likely reason why this was introduced in the first place is that, until recently, Windows didn't even support ANSI escape sequences. So maybe this could be implemented as a smart default where it's automatically turned it off on Windows versions less than 10.10586 which is the first build of Windows that started supporting ANSI.
I think there are a lot of problems flipped from "default off" to "default on" that would break a lot of systems. I suggest re-titling the issue to be a bit broader to not narrow into a specific fix, e.g. "Allow a way to enable dotnet colorized output, even if console output is redirected". That's really what we're trying to solve - and for .NET that generally needs to be in a non-breaking way.
This could be an environmental variable, or just like on the NO_COLOR site it could be dotnet * --color=true (or whatever is semantically "familiar") as a CLI option...or something else :)
I think there are a lot of problems flipped from "default off" to "default on" that would break a lot of systems. I suggest re-titling the issue to be a bit broader to not narrow into a specific fix, e.g. "Allow a way to enable dotnet colorized output, even if console output is redirected". That's really what we're trying to solve - and for .NET that generally needs to be in a non-breaking way.
I agree. When the output is not a tty (or "when the console output is redirected") is often because you're writing to a file. You would almost _never_ want inband control characters / ANSI color codes in that output. Running in a CI system is the exception here, not the rule.
You may want to honor NO_COLOR _for interactive use_ - that would be a welcome improvement for people (like me) who started using text consoles before we had these high-falutin' colors and likes it that way. (Also, please get off my lawn.) But NO_COLOR is - as best I can understand - about _interactive use_, not redirected use / when isatty is false.
For non-interactive use, you really must default to no colors and then opt-in the CI systems explicitly. As a CI vendor, this is what I want and expect you to do for our customers in common.
Mentioned in https://github.com/microsoft/vstest/issues/2370:
I think introducing heuristics similar to Chalk's is quite dangerous as they will become maintenance hell and will effectively turn dotnet into a bottleneck when new CIs are introduced/need to be added.
Yes, this is true. (Though I'd suggest abstracting it out into a separate package.) As a CI vendor, we want to make sure that our shared users have colors. We will take on this burden of contributing support.
Speaking of a separate package - I'm like 99% sure that cake already has this logic to detect when it's running in CI. Whether that's already abstracted out into a separate package or could be, I don't know, but it may be worth looking there first.
I'd think in 2020, we'd be smart enough to detect our environments and show color automatically for a really nice experience instead of opting into it. I don't opt into it in other programming languages, it just works.
Also, the cool kids have color and we need it too 馃挴
For automatic CI detection, here is a list of environment variables from different services:
| Variable | Check value | Service(s) |
|---|---|---|
| CI | /TRUE/i or 1 | TravisCI, AppVeyor, CirrusCI, CircleCI, GitLab CI |
| TF_BUILD | /TRUE/i | Azure DevOPS |
| DOCKERFILE_PATH | non empty string | Docker Hub CI |
| GITHUB_SHA | non empty string | GitHub Actions |
| JENKINS_URL | non empty string | Jenkins |
| bamboo.buildKey | non empty string | Bamboo |
| TEAMCITY_VERSION | non empty string | TeamCity |
I think there are a lot of problems flipped from "default off" to "default on" that would break a lot of systems. I suggest re-titling the issue to be a bit broader to not narrow into a specific fix, e.g. "Allow a way to enable
dotnetcolorized output, even if console output is redirected". That's really what we're trying to solve - and for .NET that generally needs to be in a non-breaking way.
I understand that the bar is high for breaking changes in .NET, but not sure that this is actually a breaking change. It would end up being ugly output in the few (IMO) cases where ANSI is not supported. I think it's REALLY important for .NET to have a great experience out of the box for people. The CLI is for developers to use and for developers to look at the output and be able to easily interpret it. I would think that the cases where you wouldn't want ANSI colors are far far less than the cases you do want it. A normal developer is not going to know they need to opt into having color. So we would either need all CI services to agree on some environment variable or we would need the code to check for all of the known CI services which as someone pointed out in the other thread seems pretty hacky and not something that I think the .NET team is going to want in their code.
Yes, this is true. (Though I'd suggest abstracting it out into a separate package.) As a CI vendor, we want to make sure that our shared users have colors. We will take on this burden of contributing support.
I am pretty sure that the .NET team isn't going to add a nuget package for something in the core runtime code. So that leaves us with adding code that needs to be maintained to detect all of the known CI services which seems also unlikely that the .NET team would be OK with in the core runtime code.
That is why I think whatever we do it's going to have to be kept simple. Either an opt-in or an opt-out command line flag and an environment variable. I personally feel like it should be opt-out or some sort of smart default.
I personally feel like it should be opt-out or some sort of smart default.
Smart defaults are the status quo: color on the console, no color when going to log files. This means that interactive users get delightful colors (yay!) and nobody gets ANSI color codes spammed all over their log files (boo).
What you want is an option to turn off colors on the console (the NO_COLOR environment variable approach). And an option to enable colors when the output is redirected. Or more accurately, something that isn't a tty, like a CI system.
(The title of the issue is misleading - at the moment, color is always enabled in the console when the output is not redirected. https://github.com/microsoft/vstest/issues/2370 is asking to enable color when the console _is_ redirected.)
(The title of the issue is misleading - at the moment, color is _always_ enabled in the console when the output is not redirected. microsoft/vstest#2370 is asking to enable color when the console _is_ redirected.)
Fixed. Thanks!
CC. @carlossanlop @eiriktsarpalis
Bump. @davidfowl @DamianEdwards any chance you guys can ask someone to look at this? It would make such a huge difference in quality of life running dotnet from CI services.
cc @stephentoub
Bump
What is the actual request at this point? Is it to respect an environment variable for disabling ANSI sequences when we'd otherwise output them and another for enabling ANSI sequences when we'd otherwise not? Or is it to use ANSI escape sequences on Windows?
Was thinking that the most likely reason why this was introduced in the first place is that, until recently, Windows didn't even support ANSI escape sequences.
The .NET Core implementation today never uses ANSI escape sequences on Windows; rather it uses https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute just as does .NET Framework. Rather, it was added because of complaints that we were spamming log files and the like, and not just in CI.
@stephentoub Ideally we would enable ANSI sequences by default in more scenarios like CI builds. The majority of CI providers (including GitHub Actions) set an environment variable called CI now. If all we did was check for that in ConsolePal.Unix.cs that would be great. I know Windows would need more checks to see if ANSI escape sequences are supported.
Alternatively, if we had a CLI flag to force ANSI support on that would work as well.
I know Windows would need more checks to see if ANSI escape sequences are supported.
More than checks; there's zero code today to emit ANSI escape sequences on Windows ;-)
The majority of CI providers (including GitHub Actions) set an environment variable called CI now.
And they all deal well with ANSI escape sequences?
Has anyone looked to see what go, rust, node, and others do here?
I'm also not convinced this is as easy as just checking for an environment variable. Consider if my app launched another .NET process that wrote to stdout and had its output redirected. Today any use of Console.*Color in that child process is effectively going to be ignored. If we start changing that, either by default or arguably worse only when this "CI" environment variable is set, the app's going to behave differently, maybe only in CI.
I understand that it鈥檚 a potentially breaking and not simple. And I also understand that adding a check for env variable seems a bit hacky. I鈥檇 be ok with adding a CLI flag and a DOTNET_ env variable to specifically opt into using ANSI. Just really anything other than it being hard-coded to off like it is now.
Thanks. Have you already researched what other languages/frameworks do here?
@stephentoub Checked a few languages:
NodeJS has colors and detects CI using this package supports-color
Rust has colors enabled by default
Deno has colors enabled by default and you have to use a standard environment variable to disable them.
Ideally it would be great if the dotnet CLI had a good experience by default, but again I understand it could be a breaking change and would happily settle for a way to opt in. I think it鈥檚 important that it can be done through an env variable so that something like the dotnet GitHub action could turn it on by default in their environment.
@stephentoub if we agree on some direction, I can try to create a PR.
@ejssmith, thanks, somehow I missed your response last week. Let me read up on this a bit and I'll get back to you.
NodeJS has colors and detects CI using this package supports-color
supports-color has a number of levels:
.level = 1 and .hasBasic = true: Basic color support (16 colors)
.level = 2 and .has256 = true: 256 color support
.level = 3 and .has16m = true: Truecolor support (16 million colors)
A level can be chosen as an envvar (FORCE_COLOR) or set as a cli arg. It looks like the CI systems default to level 1: https://github.com/chalk/supports-color/blob/651576a5fabfa489660b48f50a7f2e5ed7676997/index.js#L85.
I am really hoping something can happen for this for the .NET 5.0 release.
.level = 1 and .hasBasic = true: Basic color support (16 colors)
I hadn't realized ConsoleColor has 16 colors. So that is the 'level1'.
Suggestion for envvar name: DOTNET_CONSOLE_ANSICOLOR=1/true.
Setting this envvar will enable output of ANSI color sequences by System.Console in environments where Console.IsOutputRedirected=true.
Does that cover the relevant use-cases mentioned in this issue?
Yeah, @tmds I think that is the easiest and safest solution.
Is the complete proposal here simply to augment WriteSetColorString and WriteResetColorString in ConsolePal.Unix.cs to effectively change:
```C#
if (Console.IsOutputRedirected)
return;
... // write out ANSI string
to instead be:
```C#
if (Console.IsOutputRedirected && !RelevantEnvVarSet)
return;
... // write out ANSI string
such that when running on Linux/macOS with output redirected we still emit ANSI escape sequences for color if the environment variable is set?
If so, I'd be ok with that.
Yeah, @stephentoub I think that is good. Anything else is going to be a breaking change. We can get that environment variable set in different CI environments that support ANSI escape sequences.
Would we want to make this more generic and have an environment variable that enables all of ANSI escape sequence support so that things like SetCursorPosition would work as well? Maybe something like DOTNET_CONSOLE_ANSI_ENABLED? If I was writing a nice unit test console output formatter then I might want to show an in progress indication for a test while it's running and then be able to move the cursor back and change it to a green checkbox when its completed. Seems like either an environment supports ANSI escape sequences or it doesn't. Enabling just a portion of the sequences seems like it could cause weird results.
Seems like either an environment supports ANSI escape sequences or it doesn't.
Note that the escape sequences can differ based on the terminal being used, so a replay scenario could be limited to being replayed in the same terminal from which it was recorded. It would also need to be careful to limit itself to only a subset of where ANSI sequences are used, e.g. getting the current cursor position also works by writing out a sequence but then expects to read in a sequence from stdin as well, and that's not going to go well in a redirected environment since it's the terminal that sends the response to stdin based on having read the stdout sequence. We'd need to decide on a case-by-case basis which ones we wanted to opt-in.
DOTNET_CONSOLE_ANSI_ENABLED
Is this extending the proposal such that setting this to 0/false would forcefully disable escape sequences? If not, I think the name needs to convey that it's one direction only. (Separately, of course, this is not going to be applicable on Windows, at least not any time soon.)
@stephentoub yeah, I'm not sure. It makes sense that not all ANSI escape sequences would be supported in a replay scenario. I just checked Mocha on NodeJs and the Deno test runners and they aren't moving the cursor around. So maybe this is just purely about colors.
Would we want to make this more generic and have an environment variable that enables all of ANSI escape sequence support so that things like SetCursorPosition would work as well?
The main use-case brought up is CI. It's important CI produces readable logs, and adding color may improve readability. It doesn't seem useful to be moving the cursor around, which may clutter the logs unless the rendering is smart (which I wouldn't assume).
Tooling like test runners could distinguish between the interactive/non-interactive case using IsOutputRedirected, and avoid doing animations in the non-interactive case.
if (Console.IsOutputRedirected && !RelevantEnvVarSet)
@stephentoub probably extended with some handling related to TerminalFormatStrings.Instance.
@tmds I agree. Rendering colored output in CI logs to make it more readable is the primary improvement I would love to see.
@tmds I agree. Rendering colored output in CI logs to make it more readable is the primary improvement I would love to see.
Taking a step back here: does this plan meet this goal?
The original request here was that dotnet test would emit color on CI providers that know how to handle color. Who is setting this DOTNET_CONSOLE_ANSI_ENABLED variable?
Are you expecting CI providers to set it? Or is there some middleware in dotnet test and friends that will inspect the environment that they're running in, set an environment variable DOTNET_CONSOLE_ANSI_ENABLED and then you act on it at this lower layer?
The original request here was that dotnet test would emit color on CI providers that know how to handle color. Who is setting this DOTNET_CONSOLE_ANSI_ENABLED variable?
Either a CI vendor sets this, or this is set by the user as part of the CI configuration.
Or is there some middleware in dotnet test and friends that will inspect the environment that they're running in, set an environment variable DOTNET_CONSOLE_ANSI_ENABLED and then you act on it at this lower layer?
The implementation will probably cache the result of checking the envvar in a static, so it won't be possible to enable colouring once this it has been initialized.
I'm not sure what guarantees the implementation wants to make as to when this static gets initialized (e.g. before Console usage, or certain usage of Console color related functions).
Either a CI vendor sets this, or this is set by the user as part of the CI configuration.
Right - to set expectations, we won't set this. We're not going to set environment variables for every possible program that we might run so that it knows how to run in our environment. We expect that programs should know the environment that they're running in and how to cope with it.
So we're shifting the burden to our shared users to turn on color with an environment variable.
It would be nice if the front end programs responsible for doing things in a CI environment could:
Which I think was the original request, and what tools in all the other ecosystems do.
It would be nice if the front end programs responsible for doing things in a CI environment could:
I guess by front-end you mean a program like dotnet test?
Doesn't the envvar allow this? What is missing?
It would be nice if there was a standard way to indicate ansi color sequences are supported. Then we'd use that in Console.
Doesn't the envvar allow this? What is missing?
From the runtime perspective? Presumably nothing, I just wanted to note that there's work to be done in dotnet test to really complete this scenario end-to-end, and it might make sense to consider that before completing the work in the runtime.
Yes, in an ideal world this would just work as expected without having to set an environment variable, but that is a much bigger change and also possibly a breaking change. So the hope here is that we can just keep this really simple to where it can actually get done and maybe even included in the .NET 5 release. :-)
So I think just keeping this really simple and adding support for checking DOTNET_CONSOLE_ANSICOLOR and allowing color escape sequences to be used would be a great step forward for making dotnet build and dotnet test output more readable.
@KathleenDollard, can you chime in here on:
Thanks!
@KathleenDollard, @terrajobst, @shirhatti, can one of you please decide what should be done here?
I think @shirhatti should drive this and sync with @KathleenDollard.
Sorry I missed this. I'll catch up with some folks.
Thanks @KathleenDollard. Was really hoping a simple fix for this could be included with .NET 5.0 as it seems like it would be really nice to be able to have color in CI build outputs. Seems like time is running out though. I saw @davidfowl was getting some last minute changes in though. Maybe there is hope still?
If I was writing a nice unit test console output formatter then I might want to show an in progress indication for a test while it's running and then be able to move the cursor back and change it to a green checkbox when its completed. Seems like either an environment supports ANSI escape sequences or it doesn't. Enabling just a portion of the sequences seems like it could cause weird results.
@ejsmith, a great example of how this could be advantageous is RoboCopy logging. While I realize RoboCopy is not a .NET application, how it logs might be completely relatable to this discussion. When you have both the ETA and Tee the logging so that you can see on the screen and log the results, the logs contain these progress numbers. If ANSI codes were processed but didn't flush the buffer for a line, that might be an interesting capability. Realistically a .NET version of RoboCopy should have a filelog and a consolelog, where percentage updates would maintain cursor position so you could have a progress bar, but the file logger just wouldn't record any of that noise. It becomes less clear how this would be handled if the window was resized while doing this sort of thing.
I believe that without a lot of consideration for how these sort of scenarios would be handled appropriately, color sequences should be added, preferably for .NET 5.0, but additional ANSI support might not be so obvious. I expect it could be just a different concrete implementation so that if there were a DOTNET_CONSOLE_ANSI_ENABLED environment variable, an AnsiColorConsole would just provide color support and AnsiConsole might provide additional capabilities selectable by a provider.
Most helpful comment
Thanks @KathleenDollard. Was really hoping a simple fix for this could be included with .NET 5.0 as it seems like it would be really nice to be able to have color in CI build outputs. Seems like time is running out though. I saw @davidfowl was getting some last minute changes in though. Maybe there is hope still?