Skip to content

Commit

Permalink
Merge pull request #22 from microsoft/main
Browse files Browse the repository at this point in the history
2021 01 28 Release
  • Loading branch information
bill-long authored Jan 28, 2021
2 parents f4984e1 + d861f32 commit 55ca230
Show file tree
Hide file tree
Showing 17 changed files with 2,602 additions and 209,168 deletions.
25 changes: 25 additions & 0 deletions .build/Build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,31 @@ $scriptFiles | ForEach-Object {
}
}

# Expand Get-Content calls for local files that are marked -AsByteStream -Raw
for ($i = 0; $i -lt $scriptContent.Count; $i++) {
$line = $scriptContent[$i].Trim()
$m = $line | Select-String "\`$(.+) = Get-Content `"?(\`$PSScriptRoot|\.)\\([\w|\d|\.|\\]+)`"? -AsByteStream -Raw"
if ($m.Matches.Count -gt 0) {
$parentPath = [IO.Path]::GetDirectoryName($_)
$filePath = [IO.Path]::Combine($parentPath, $m.Matches[0].Groups[3].Value)
$fileAsBase64 = [Convert]::ToBase64String(([IO.File]::ReadAllBytes($filePath)), "InsertLineBreaks")
$scriptContent.RemoveAt($i)
[string[]]$linesToInsert = @()
$linesToInsert += "`$$($m.Matches[0].Groups[1].Value)Base64 = @'"
$linesToInsert += $fileAsBase64
$linesToInsert += "'@"
$linesToInsert += ""
$linesToInsert += "`$$($m.Matches[0].Groups[1].Value) = [Convert]::FromBase64String(`$$($m.Matches[0].Groups[1].Value)Base64)"
$scriptContent.InsertRange($i, $linesToInsert)
$commitTimeTest = [DateTime]::Parse((git log -n 1 --format="%ad" --date=rfc $dotloadedScriptPath))

if ($commitTimeTest -gt $commitTime) {
$commitTime = $commitTimeTest
Write-Host ("Changing commit time to: $($commitTime.ToString("yy.MM.dd.HHmm"))")
}
}
}

Write-Host ("Setting version for script '$_' to '$($commitTime.ToString("yy.MM.dd.HHmm"))'")
$version = "# Version $($commitTime.ToString("yy.MM.dd.HHmm"))"
# Stamp version
Expand Down
158 changes: 158 additions & 0 deletions ExTRA/ExTRA.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
$tagFileBytes = Get-Content "$PSScriptRoot\tags2016.txt" -AsByteStream -Raw

$htmlFileBytes = Get-Content "$PSScriptRoot\ui.html" -AsByteStream -Raw

$tagFileContent = [System.Text.Encoding]::UTF8.GetString($tagFileBytes)

$htmlFileContent = [System.Text.Encoding]::UTF8.GetString($htmlFileBytes)

$uri = "http://localhost:5002/"

$outputPath = Join-Path $PSScriptRoot "EnabledTraces.config"

function GetTagsFromFile($file) {
$tags = $file | ForEach-Object {
if ($_ -match "(^TraceLevels|^InMemoryTracing|^FilteredTracing)" -or $_.Length -lt 1) {
# Skip these lines
} else {
[PSCustomObject]@{
name = $_.Substring(0, $_.IndexOf(':'))
isSelected = $false
tags = @($_.Substring($_.IndexOf(':') + 1).Split(',') | Sort-Object | ForEach-Object {
[PSCustomObject]@{
name = $_
isSelected = $false
}
})
}
}
}

return $tags
}

$extraTags = GetTagsFromFile $tagFileContent.Split([System.Environment]::NewLine)

$alreadySelectedTags = $null

if (Test-Path $outputPath) {
$alreadySelectedTags = GetTagsFromFile $outputPath
}

if ($null -ne $alreadySelectedTags) {
foreach ($category in $alreadySelectedTags) {
$selectedTags = $category.tags | ForEach-Object { $_.name }
$categoryToUpdate = $extraTags | Where-Object { $_.name -eq $category.name }
$categoryToUpdate.tags | ForEach-Object {
if ($selectedTags.Contains($_.name)) {
$_.isSelected = $true
}
}
}
}

$extraTags = ConvertTo-Json $extraTags -Depth 3

$httpListener = New-Object System.Net.HttpListener
$httpListener.Prefixes.Add($uri)
$httpListener.Start()

& explorer.exe $uri

try {
while ($httpListener.IsListening) {
$task = $httpListener.GetContextAsync()
while (-not $task.AsyncWaitHandle.WaitOne(100)) {
Start-Sleep -Milliseconds 100
}

$context = $task.GetAwaiter().GetResult()

if ($context.Request.HttpMethod -eq "PUT") {
$context.Response.StatusCode = 200
$context.Response.Close()

# The user might have closed the tab, or might have clicked Refresh.
# They both fire the same event. So, wait a moment and see if we get
# another request. If we don't, tab was closed.
$task = $httpListener.GetContextAsync()
if (-not $task.AsyncWaitHandle.WaitOne(1000)) {
Write-Host "Browser tab was closed without saving changes."
break
} else {
$context = $task.GetAwaiter().GetResult()
}
}

if ($context.Request.HttpMethod -eq "GET") {
Write-Host "Showing tag selector UI in the default browser."
$pageContent = $htmlFileContent.Replace("var exchange2016Tags = [];", "var exchange2016Tags = $extraTags;")
$pageContentUTF8 = [System.Text.Encoding]::UTF8.GetBytes($pageContent)
$context.Response.StatusCode = 200
$context.Response.OutputStream.Write($pageContentUTF8, 0, $pageContentUTF8.Length)
$context.Response.Close()
} elseif ($context.Request.HttpMethod -eq "POST") {
$reader = New-Object System.IO.StreamReader($context.Request.InputStream, "UTF8")
$body = $reader.ReadToEnd()
$context.Response.StatusCode = 200
$context.Response.Close()
$tagInfo = ConvertFrom-Json $body
$selectedTags = @()
foreach ($category in $tagInfo) {
$selectedTagsForThisCategory = $category.tags | Where-Object { $_.isSelected }
if ($null -ne $selectedTagsForThisCategory) {
$tagString = [string]::Join(',', ($selectedTagsForThisCategory | ForEach-Object { $_.name }))
$selectedTags += $category.name + ":" + $tagString
}
}

$linesToSave = @()
$linesToSave += "TraceLevels:Debug,Warning,Error,Fatal,Info,Performance,Function,Pfd"

Write-Host
Write-Host "Selected tags:"
foreach ($line in $selectedTags) {
Write-Host $line
$linesToSave += $line
}

$linesToSave += "FilteredTracing:No"
$linesToSave += "InMemoryTracing:No"

Write-Host

$outputPath = Join-Path $PSScriptRoot "EnabledTraces.config"

Write-Host "Saving" $outputPath

[IO.File]::WriteAllLines($outputPath, $linesToSave)

break
}
}
} finally {
$httpListener.Close()
}

if (Test-Path $outputPath) {
Write-Host "The trace can be created, started, and stopped from the command line. Note that"
Write-Host "the `"logman create`" commands should be run from CMD, not PowerShell."
Write-Host
Write-Host "To create a data collector which is non-circular and stops at 1 GB:"
Write-Host
Write-Host "logman create trace ExchangeDebugTraces -p {79bb49e6-2a2c-46e4-9167-fa122525d540} -o c:\tracing\trace.etl -ow -f bin -max 1024" -ForegroundColor Green
Write-Host
Write-Host "To create a data collector which is circular and stops at 2 GB:"
Write-Host
Write-Host "logman create trace ExchangeDebugTraces -p {79bb49e6-2a2c-46e4-9167-fa122525d540} -o c:\tracing\trace.etl -ow -f bincirc -max 2048" -ForegroundColor Green
Write-Host
Write-Host "To start the trace:"
Write-Host
Write-Host "logman start ExchangeDebugTraces" -ForegroundColor Green
Write-Host
Write-Host "To stop the trace:"
Write-Host
Write-Host "logman stop ExchangeDebugTraces" -ForegroundColor Green
Write-Host
Write-Host "The collector can also be started and stopped from Perfmon."
}
34 changes: 34 additions & 0 deletions ExTRA/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# ExTRA

The goal of this script is to replace the ExTRA UI that was included with older versions of Exchange.
The script can be run on any machine where a modern browser (Edge/Chrome/Firefox) is set as the default
browser. It does not need to be run on an Exchange server. It will _not_ work if Internet Explorer
is the default browser.

## Download

Get the latest release here:

https://github.com/microsoft/CSS-Exchange/releases/latest/download/ExTRA.ps1

## Usage

Generally, you will want to run this script on a user workstation and use it to generate the
EnabledTraces.config file. Then, that file can be copied to the Exchange server, and a logman command
can be used to start and stop the ExTRA trace.

The script can be run directly on a server if desired, but remember that IE cannot be the default
browser in that case.

To use, download the latest release. Then run the script with no parameters:

```
.\ExTRA.ps1
```

The default browser will launch with a tag selection interface. Once the desired tags are selected,
click Save and go back to the PowerShell window. You should see some output indicating that the
EnabledTraces.config file was saved in the folder. That EnabledTraces.config should be placed at
the root of C:\ on the server being traced.

The output also provides example logman syntax for creating, starting, and stopping the trace.
Loading

0 comments on commit 55ca230

Please sign in to comment.