diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 6bee01dfa59..2d824ca5280 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -2113,7 +2113,6 @@ webpage websites websockets wekyb -WEOF wex wextest wextestclass diff --git a/doc/specs/drafts/#642 - Buffer Exporting and Logging/#642 - Buffer Exporting and Logging.md b/doc/specs/drafts/#642 - Buffer Exporting and Logging/#642 - Buffer Exporting and Logging.md new file mode 100644 index 00000000000..5e66e762c87 --- /dev/null +++ b/doc/specs/drafts/#642 - Buffer Exporting and Logging/#642 - Buffer Exporting and Logging.md @@ -0,0 +1,380 @@ +--- +author: Mike Griese @zadjii-msft +created on: 2021-08-31 +last updated: 2021-08-31 +issue id: #642 +--- + +# Buffer Exporting and Logging + +## Abstract + +A common user need is the ability to export the history of a terminal session to +a file, for later inspection or validation. This is something that could be +triggered manually. Many terminal emulators provide the ability to automatically +log the output of a session to a file, so the history is always captured. This +spec will address improvements to the Windows Terminal to enable these kinds of +exporting and logging scenarios. + +## Background + +### Inspiration + +Below are screenshots from the settings pages of three different terminal +emulators with similar features - PuTTY, SecureCRT, and ConEmu: + +![PuTTY settings](PuTTY-logging-settings.png) + +_figure 1: PuTTY settings_ + +![SecureCRT settings](SecureCRT-logging-settings.png) + +_figure 2: SecureCRT settings_ + +![ConEmu settings](ConEmu-logging-settings.png) + +_figure 3: ConEmu settings_ + +These applications all offer some settings in common. Primarily, the important +feature is the ability to specify a path to a log file which contains some +special string formatting. This allows the user to log to different files based +on the time & date of the session, or based on the session name. + +### User Stories + +* **Story A**: The user is able to use a context menu entry on the tab to export + the contents of the buffer to a file, which they are prompted for. + - This is explicitly what was requested in [#642] +* **Story B**: The user can bind an action to export the contents of the buffer + to a file, which they are prompted for. + - Very similar to **A**, but via the command palette or a keybinding. +* **Story C**: The user can export to an explicit file via an action + - similar to **B**, but allowing for declaring the path to a file rather than + prompting at runtime. +* **Story D**: The user can choose to append to a file when exporting, rather + than overwriting. +* **Story E**: The user can specify a format string in the path to the file to + export to, which the Terminal will automatically replace with variables like + the time, date, and profile name. +* **Story F**: When opening a specific profile, the user can automatically log + to a file +* **Story G**: The user can execute an action to start or stop logging to a + given file. + + +## Solution Design + +I'm proposing the following actions and profile settings + +* New Action: `exportBuffer()`. + - Export the contents of the buffer to a file. + - `path` (string, defaults to `""`): When empty, prompt the user for a name of + a file to export to, using a file picker. This path accepts special + formatting strings that will be substituted with certain variables + (discussed [below](#path-formatting)). + - `append` (boolean, defaults to `false`): When `false`, the file's contents + will be overwritten. When `true`, the buffer contents will be appended to + the end of the file. +* New Profile Settings object: `logSettings` + - This is an object that describes a set of behavior for logging a profile. + - `path`: Same as the `path` in the `ExportBufferArgs` above + - `append`: Same as the `append` in the `ExportBufferArgs` above + - `captureAllOutput`: (boolean, defaults to `false`) When true, don't log only + printable characters, also log non-printable escape characters written to + the Terminal. + - `captureInput`: (boolean, defaults to `false`) Additionally log input to the + Terminal to the file. Input will be formatted as the traditional VT + sequences, rather than the full `win32-input` encoding. + - `newFileEveryDay`: (boolean, defaults to `false`) This requires the `day` to + be an element of the path format string. When logging with this setting, + opens a new file at midnight and starts writing that one. + +* New Profile setting: `logAutomatically` (boolean, default `false`). When true, + terminals with this profile will begin logging automatically. +* New Action: `toggleLogging()`. + - Start or stop logging to the configured file. If the terminal is already + logging with different settings than in this action, then stop logging + regardless (don't just start logging to the new file) + - This action accepts all the same args the profile's `logSettings` object. + - If _any_ args are provided, use those args. If _none_ are provided, then use + the logging settings present in the profile (if there are any). + - If there's not path provided (either in the args to the action or in the + profile), prompt the user to pick a file to log to. + +### Examples +```json +{ + "actions": [ + { "keys": "f1", "command": "exportBuffer" }, + { "keys": "f2", "command": { "action": "exportBuffer", "path": "c:\\logs\\${year}-${month}-${date}\\{profile}.txt" } }, + + { "keys": "f3", "command": "toggleLogging" }, + { "keys": "f4", "command": { "action": "toggleLogging", "path": "c:\\logs\\${profile}.log", "append": true } }, + ], + "profiles": [ + { + "name": "foo", + "logging": { + "path": "c:\\foo.txt", + "append": true + }, + "automaticallyLog": false + }, + { + "name": "bar", + "logging": { + "path": "c:\\logs\\${date}\\bar.txt", + "append": false + }, + "automaticallyLog": true + } + ] +} +``` + +Revisiting our original stories: + +* **Story A**: This is already implemented in [#11062] +* **Story B**: This is the action bound to f1. +* **Story C**: This is the action bound to f2. +* **Story D**: This is the `append` property in the actions, profile settings. +* **Story E**: An example of this is in the action bound to f2, + f4, and in the profile "bar"'s logging settings. +* **Story F**: The profile "bar" is configured to automatically log when opened. +* **Story G**: This is the action bound to f4. + +In addition, +* When opening the profile "foo", it will not automatically log to a file. + - Pressing f3 will begin logging to `c:\foo.txt` + - Pressing f4 will begin logging to `c:\logs\foo.log` + +### Path formatting + +[TODO!]: # TODO! + +For discussion: What syntax do we want? +* PuTTY uses `&Y`, `&M`, `&D`, `&T`, `&H`, `&P` for year, month, day, time, host + and port respectively. +* SecureCRT uses: + - `%H` – hostname + - `%S` – session name + - `%Y` – four-digit year + - `%M` – two-digit month + - `%D` – two-digit day of the month + - `%h` – two-digit hour + - `%m` – two-digit minute + - `%s` – two-digit seconds + - `%t` – three-digit milliseconds + - `%%` – percent (%) + - `%envvar%` – environment variable (for instance `%USERNAME%`) + +We have some precedent for formatting with `${braces}`, a la the iterable +command in the Command Palette (e.g `${profile.name}`). Additionally, [#9287] +implements support for environment variables in the Terminal with the +`${env:VARIABLE}` syntax. + +What variables do we want exposed, and how do we want users to be able to format +them? + +This doc was initially authored assuming we'd go with a `${braces}` syntax, like: + - `${profile}` – profile name + - `${year}` – four-digit year + - `${month}` – two-digit month + - `${day}` – two-digit day of the month + - `${hour}` – two-digit hour + - `${minute}` – two-digit minute + - `${second}` – two-digit second + - `${ms}` – three-digit milliseconds + - `${env:variable}` – environment variable (for instance `${env:USERPROFILE}`) + (inspired by [#9287]) + +### Exporting vs Logging +As far as specific implementation details goes, exporting is the easier work to +do. [#11062] already wires up the `TerminalApp` to retrieve the buffer contents +from the `TermControl`, so writing them at request is easy. + +Logging is harder. We don't want the `TermControl` telling the `TerminalApp` +layer about every piece of output logged. Especially in the post-[#5000] world +where that's a cross-process hop. Instead, we'll want the `ControlCore` / +`ControlInteractivity` to do _logging_ themselves. + +### Logging Mechanics + +#### When do we log? + +[TODO!]: # TODO! + +When do we decide to actually log? Take for example typing in a `pwsh` or +`bash` prompt. Imagine the user types +what, then hits +BkspBksp, such that the prompt is just `wh`. What should +the log contain? `what^h ^h^h ^h`[[1]](#footnote-1)? `wh`? + +My worry with logging the backspaces is that conpty is sometimes a bit noisier +than it needs to be with using `^H` as a cursor positioning sequence. Should we +only log lines when the cursor newlines or otherwise moves from the line it is +currently on? + +I'll need to look at what PuTTY emits for the "Printable output" option. + +#### What happens when we _start_ logging? + +If the user has a terminal that did not start with logging enabled, but then +started logging with `toggleLogging`, what should we log? All future output? Or +should we log the current buffer contents as well? + +I'm inclined to lean towards simply "all future output", and ignore any current +buffer content. If the user rally wants to log the current buffer contents _and_ +start logging, they can use a `multipleActions` action ([#11045]) to +`exportBuffer` to a file, then `toggleLogging` to that same file with +`"append":true`. + +## Potential Issues + +
Compatibility | ++ +Since this functionality is entirely new, nothing here should negatively affect +existing functionality. + + | +
Performance, Power, and Efficiency | ++ +When logging, it's expected there will be a measurable performance hit. We can +try to mitigate this by only writing to the file on a background thread, +separate from the connection or rendering thread. Since auto-logging will only +take place in the content process, we're not worried about the file writing +occurring on the UI thread. + + | +