Skip to content

Commit 7f6803d

Browse files
vaindwillhcr
andauthored
feat: synchronous transport (#62)
* 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 <[email protected]>
1 parent 593e79f commit 7f6803d

File tree

5 files changed

+90
-1
lines changed

5 files changed

+90
-1
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Unreleased
44

5+
### Features
6+
7+
- Send events to Sentry fully synchronously ([#59](https://github.com/SummitHosting/sentry-powershell/pull/59), [#62](https://github.com/SummitHosting/sentry-powershell/pull/62))
8+
59
### Fixes
610

711
- StackTrace parsing on Windows PowerShell 5.1 ([#50](https://github.com/getsentry/sentry-powershell/pull/50))

modules/Sentry/Sentry.psd1

+3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
# Script files (.ps1) that are run in the caller's environment prior to importing this module.
3131
ScriptsToProcess = @('assemblies-loader.ps1')
3232

33+
# Require System.Net.Http for use by SynchronousTransport
34+
RequiredAssemblies = @('System.Net.Http')
35+
3336
# 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.
3437
FunctionsToExport = @(
3538
'Add-SentryBreadcrumb',
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Take Sentry's SerializableHttpContent, convert it to a string, and send via PowerShell's Invoke-WebRequest,
2+
# then translate the response back to a .NET HttpResponseMessage.
3+
# There are limited options to perform synchronous operations in Windows PowerShell 5.1 on .NET 4.6, so this is a workaround.
4+
class SynchronousTransport : Sentry.Http.HttpTransportBase, Sentry.Extensibility.ITransport
5+
{
6+
hidden [Sentry.Extensibility.IDiagnosticLogger] $logger
7+
hidden [System.Reflection.MethodInfo] $ProcessEnvelope
8+
hidden [System.Reflection.MethodInfo] $CreateRequest
9+
hidden [System.Reflection.MethodInfo] $SerializeToStream
10+
11+
SynchronousTransport([Sentry.SentryOptions] $options) : base($options)
12+
{
13+
$this.logger = $options.DiagnosticLogger
14+
15+
# These are internal methods, so we need to use reflection to access them.
16+
$instanceMethod = [System.Reflection.BindingFlags]::Instance + [System.Reflection.BindingFlags]::NonPublic + [System.Reflection.BindingFlags]::Public;
17+
$this.ProcessEnvelope = [Sentry.Http.HttpTransportBase].GetMethod('ProcessEnvelope', $instanceMethod)
18+
$this.CreateRequest = [Sentry.Http.HttpTransportBase].GetMethod('CreateRequest', $instanceMethod)
19+
$EnvelopeHttpContentType = [Sentry.SentrySdk].Assembly.GetType('Sentry.Internal.Http.EnvelopeHttpContent')
20+
$this.SerializeToStream = $EnvelopeHttpContentType.GetMethod('SerializeToStream', $instanceMethod)
21+
}
22+
23+
[System.Threading.Tasks.Task] SendEnvelopeAsync([Sentry.Protocol.Envelopes.Envelope] $envelope, [System.Threading.CancellationToken]$cancellationToken = [System.Threading.CancellationToken]::None)
24+
{
25+
$processedEnvelope = $this.ProcessEnvelope.Invoke($this, @($envelope))
26+
if ($processedEnvelope.Items.count -gt 0)
27+
{
28+
$request = $this.CreateRequest.Invoke($this, @($processedEnvelope))
29+
30+
$headers = @{}
31+
foreach ($header in $request.Headers)
32+
{
33+
$Key = $header.Key
34+
$Value = $header.Value.Trim() -join ', '
35+
$headers[$Key] = $Value
36+
}
37+
38+
$memoryStream = [System.IO.MemoryStream]::new()
39+
$this.SerializeToStream.Invoke($request.Content, @($memoryStream, $null, $cancellationToken))
40+
$memoryStream.Position = 0
41+
42+
$reader = New-Object System.IO.StreamReader($memoryStream)
43+
$content = $reader.ReadToEnd()
44+
$reader.Close()
45+
46+
$this.logger.Log([Sentry.SentryLevel]::Debug, 'Sending content synchronously, Content-Length: {0}', $null, $content.Length)
47+
48+
$ProgressPreference = 'SilentlyContinue'
49+
$psResponse = Invoke-WebRequest -Uri $request.RequestUri -Method $request.Method.Method -Headers $headers -Body $content -UseBasicParsing
50+
51+
$response = [System.Net.Http.HttpResponseMessage]::new($psResponse.StatusCode)
52+
$contentType = $psResponse.Headers['Content-Type']
53+
if ($null -eq $contentType)
54+
{
55+
$contentType = 'application/json'
56+
}
57+
$response.Content = [System.Net.Http.StringContent]::new($psResponse.Content, [System.Text.Encoding]::UTF8, $contentType)
58+
59+
foreach ($header in $psResponse.Headers.GetEnumerator())
60+
{
61+
$response.Headers.TryAddWithoutValidation($header.Key, $header.Value)
62+
}
63+
64+
$this.HandleResponse($response, $processedEnvelope)
65+
}
66+
67+
return [System.Threading.Tasks.Task]::CompletedTask
68+
}
69+
}

modules/Sentry/public/Start-Sentry.ps1

+13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
. "$privateDir/DiagnosticLogger.ps1"
22
. "$privateDir/ScopeIntegration.ps1"
33
. "$privateDir/SynchronousWorker.ps1"
4+
. "$privateDir/SynchronousTransport.ps1"
45
. "$privateDir/EventUpdater.ps1"
56

67
function Start-Sentry
@@ -48,6 +49,18 @@ function Start-Sentry
4849
$logger = [DiagnosticLogger]::new($options.DiagnosticLevel)
4950
$options.DiagnosticLogger = $logger
5051

52+
if ($null -eq $options.Transport)
53+
{
54+
try
55+
{
56+
$options.Transport = [SynchronousTransport]::new($options)
57+
}
58+
catch
59+
{
60+
$logger.Log([Sentry.SentryLevel]::Warning, 'Failed to create a PowerShell-specific synchronous transport', $_.Exception, @())
61+
}
62+
}
63+
5164
if ($null -eq $options.BackgroundWorker)
5265
{
5366
try

samples/locate-city.ps1

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ try
6565
catch
6666
{
6767
$_ | Out-Sentry
68-
"⚠️ Error in line $($_.InvocationInfo.ScriptLineNumber): $($Error[0])"
68+
"⚠️ Error on line $($_.InvocationInfo.ScriptLineNumber): $($Error[0])"
6969
}
7070
finally
7171
{

0 commit comments

Comments
 (0)