Skip to content

Commit

Permalink
feat: synchronous transport (#62)
Browse files Browse the repository at this point in the history
* Add SynchronousTransport implementation, enabled similarly to SynchronousWorker

* Switch to using directive for loading .NET assembly

Add CHANGELOG.md entry for new SynchronousTransport feature

* Require System.Net.Http assembly

* Update CHANGELOG.md

* move SynchronousTransport reflection to constructor

* use content-type from response headers

* disable progress indicator

* fix typo

* respect request.method

* chore: update changelog

* cleanup

* fixup progress preference setting

* use local ProgressPreference

* fix the typo fix

---------

Co-authored-by: Will Horne <will.horne@cloudrunner.com>
  • Loading branch information
vaind and willhcr authored Oct 22, 2024
1 parent 593e79f commit 7f6803d
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 1 deletion.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Features

- Send events to Sentry fully synchronously ([#59](https://github.com/SummitHosting/sentry-powershell/pull/59), [#62](https://github.com/SummitHosting/sentry-powershell/pull/62))

### Fixes

- StackTrace parsing on Windows PowerShell 5.1 ([#50](https://github.com/getsentry/sentry-powershell/pull/50))
Expand Down
3 changes: 3 additions & 0 deletions modules/Sentry/Sentry.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
# Script files (.ps1) that are run in the caller's environment prior to importing this module.
ScriptsToProcess = @('assemblies-loader.ps1')

# Require System.Net.Http for use by SynchronousTransport
RequiredAssemblies = @('System.Net.Http')

# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
FunctionsToExport = @(
'Add-SentryBreadcrumb',
Expand Down
69 changes: 69 additions & 0 deletions modules/Sentry/private/SynchronousTransport.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Take Sentry's SerializableHttpContent, convert it to a string, and send via PowerShell's Invoke-WebRequest,
# then translate the response back to a .NET HttpResponseMessage.
# There are limited options to perform synchronous operations in Windows PowerShell 5.1 on .NET 4.6, so this is a workaround.
class SynchronousTransport : Sentry.Http.HttpTransportBase, Sentry.Extensibility.ITransport
{
hidden [Sentry.Extensibility.IDiagnosticLogger] $logger
hidden [System.Reflection.MethodInfo] $ProcessEnvelope
hidden [System.Reflection.MethodInfo] $CreateRequest
hidden [System.Reflection.MethodInfo] $SerializeToStream

SynchronousTransport([Sentry.SentryOptions] $options) : base($options)
{
$this.logger = $options.DiagnosticLogger

# These are internal methods, so we need to use reflection to access them.
$instanceMethod = [System.Reflection.BindingFlags]::Instance + [System.Reflection.BindingFlags]::NonPublic + [System.Reflection.BindingFlags]::Public;
$this.ProcessEnvelope = [Sentry.Http.HttpTransportBase].GetMethod('ProcessEnvelope', $instanceMethod)
$this.CreateRequest = [Sentry.Http.HttpTransportBase].GetMethod('CreateRequest', $instanceMethod)
$EnvelopeHttpContentType = [Sentry.SentrySdk].Assembly.GetType('Sentry.Internal.Http.EnvelopeHttpContent')
$this.SerializeToStream = $EnvelopeHttpContentType.GetMethod('SerializeToStream', $instanceMethod)
}

[System.Threading.Tasks.Task] SendEnvelopeAsync([Sentry.Protocol.Envelopes.Envelope] $envelope, [System.Threading.CancellationToken]$cancellationToken = [System.Threading.CancellationToken]::None)
{
$processedEnvelope = $this.ProcessEnvelope.Invoke($this, @($envelope))
if ($processedEnvelope.Items.count -gt 0)
{
$request = $this.CreateRequest.Invoke($this, @($processedEnvelope))

$headers = @{}
foreach ($header in $request.Headers)
{
$Key = $header.Key
$Value = $header.Value.Trim() -join ', '
$headers[$Key] = $Value
}

$memoryStream = [System.IO.MemoryStream]::new()
$this.SerializeToStream.Invoke($request.Content, @($memoryStream, $null, $cancellationToken))
$memoryStream.Position = 0

$reader = New-Object System.IO.StreamReader($memoryStream)
$content = $reader.ReadToEnd()
$reader.Close()

$this.logger.Log([Sentry.SentryLevel]::Debug, 'Sending content synchronously, Content-Length: {0}', $null, $content.Length)

$ProgressPreference = 'SilentlyContinue'
$psResponse = Invoke-WebRequest -Uri $request.RequestUri -Method $request.Method.Method -Headers $headers -Body $content -UseBasicParsing

$response = [System.Net.Http.HttpResponseMessage]::new($psResponse.StatusCode)
$contentType = $psResponse.Headers['Content-Type']
if ($null -eq $contentType)
{
$contentType = 'application/json'
}
$response.Content = [System.Net.Http.StringContent]::new($psResponse.Content, [System.Text.Encoding]::UTF8, $contentType)

foreach ($header in $psResponse.Headers.GetEnumerator())
{
$response.Headers.TryAddWithoutValidation($header.Key, $header.Value)
}

$this.HandleResponse($response, $processedEnvelope)
}

return [System.Threading.Tasks.Task]::CompletedTask
}
}
13 changes: 13 additions & 0 deletions modules/Sentry/public/Start-Sentry.ps1
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
. "$privateDir/DiagnosticLogger.ps1"
. "$privateDir/ScopeIntegration.ps1"
. "$privateDir/SynchronousWorker.ps1"
. "$privateDir/SynchronousTransport.ps1"
. "$privateDir/EventUpdater.ps1"

function Start-Sentry
Expand Down Expand Up @@ -48,6 +49,18 @@ function Start-Sentry
$logger = [DiagnosticLogger]::new($options.DiagnosticLevel)
$options.DiagnosticLogger = $logger

if ($null -eq $options.Transport)
{
try
{
$options.Transport = [SynchronousTransport]::new($options)
}
catch
{
$logger.Log([Sentry.SentryLevel]::Warning, 'Failed to create a PowerShell-specific synchronous transport', $_.Exception, @())
}
}

if ($null -eq $options.BackgroundWorker)
{
try
Expand Down
2 changes: 1 addition & 1 deletion samples/locate-city.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ try
catch
{
$_ | Out-Sentry
"⚠️ Error in line $($_.InvocationInfo.ScriptLineNumber): $($Error[0])"
"⚠️ Error on line $($_.InvocationInfo.ScriptLineNumber): $($Error[0])"
}
finally
{
Expand Down

0 comments on commit 7f6803d

Please sign in to comment.