Skip to content

Commit 7bab257

Browse files
authored
feat: change HTTP transport to synchronous (#39)
* feat: change HTTP transport to synchronous * chore: update changelog * cleanup
1 parent 0094bd9 commit 7bab257

File tree

7 files changed

+97
-16
lines changed

7 files changed

+97
-16
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
### Features
6+
7+
- Send events synchronously so they're not lost when the script exits ([#39](https://github.com/getsentry/sentry-powershell/pull/39))
8+
39
## 0.0.2
410

511
### Various fixes & improvements
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Wrapper to expose Sentry.Internal.SdkComposer::CreateHttpTransport()
2+
function New-HttpTransport
3+
{
4+
[OutputType([Sentry.Extensibility.ITransport])]
5+
[CmdletBinding()]
6+
param(
7+
[Parameter(Mandatory)]
8+
[Sentry.SentryOptions] $options
9+
)
10+
11+
$assembly = [Sentry.SentrySdk].Assembly
12+
$type = $assembly.GetType('Sentry.Internal.SdkComposer')
13+
$composer = [Activator]::CreateInstance($type, @($options))
14+
15+
$method = $type.GetMethod('CreateHttpTransport', [System.Reflection.BindingFlags]::Instance + [System.Reflection.BindingFlags]::NonPublic + [System.Reflection.BindingFlags]::Public)
16+
return $method.Invoke($composer, @())
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
. "$privateDir/New-HttpTransport.ps1"
2+
3+
class SynchronousWorker : Sentry.Extensibility.IBackgroundWorker
4+
{
5+
hidden [Sentry.Extensibility.ITransport] $transport
6+
hidden [Sentry.SentryOptions] $options
7+
hidden $unfinishedTasks = [System.Collections.Generic.List[System.Threading.Tasks.Task]]::new()
8+
9+
SynchronousWorker([Sentry.SentryOptions] $options)
10+
{
11+
$this.options = $options
12+
13+
# Start from either the transport given on options, or create a new HTTP transport.
14+
$this.transport = $options.Transport;
15+
if ($null -eq $this.transport)
16+
{
17+
$this.transport = New-HttpTransport($options)
18+
}
19+
}
20+
21+
[bool] EnqueueEnvelope([Sentry.Protocol.Envelopes.Envelope] $envelope)
22+
{
23+
$task = $this.transport.SendEnvelopeAsync($envelope, [System.Threading.CancellationToken]::None)
24+
if (-not $task.Wait($this.options.FlushTimeout))
25+
{
26+
$this.unfinishedTasks.Add($task)
27+
}
28+
return $true
29+
}
30+
31+
[System.Threading.Tasks.Task] FlushAsync([System.TimeSpan] $timeout)
32+
{
33+
[System.Threading.Tasks.Task]::WhenAll($this.unfinishedTasks).Wait($timeout)
34+
$this.unfinishedTasks.Clear()
35+
return [System.Threading.Tasks.Task]::CompletedTask
36+
}
37+
38+
[int] get_QueuedItems()
39+
{
40+
return $this.unfinishedTasks.Count
41+
}
42+
}

modules/Sentry/public/Start-Sentry.ps1

+17-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
. "$privateDir/DiagnosticLogger.ps1"
22
. "$privateDir/ScopeIntegration.ps1"
3+
. "$privateDir/SynchronousWorker.ps1"
34
. "$privateDir/EventUpdater.ps1"
45

56
function Start-Sentry
@@ -16,6 +17,8 @@ function Start-Sentry
1617
begin
1718
{
1819
$options = [Sentry.SentryOptions]::new()
20+
$options.FlushTimeout = [System.TimeSpan]::FromSeconds(10)
21+
$options.ShutDownTimeout = $options.FlushTimeout
1922
$options.ReportAssembliesMode = [Sentry.ReportAssembliesMode]::None
2023
$options.IsGlobalModeEnabled = $true
2124
[Sentry.sentryOptionsExtensions]::AddIntegration($options, [ScopeIntegration]::new())
@@ -42,7 +45,20 @@ function Start-Sentry
4245
$options | ForEach-Object $EditOptions
4346
}
4447

45-
$options.DiagnosticLogger = [DiagnosticLogger]::new($options.DiagnosticLevel)
48+
$logger = [DiagnosticLogger]::new($options.DiagnosticLevel)
49+
$options.DiagnosticLogger = $logger
50+
51+
if ($null -eq $options.BackgroundWorker)
52+
{
53+
try
54+
{
55+
$options.BackgroundWorker = [SynchronousWorker]::new($options)
56+
}
57+
catch
58+
{
59+
$logger.Log([Sentry.SentryLevel]::Warning, 'Failed to create a PowerShell-specific synchronous worker', $_.Exception, @())
60+
}
61+
}
4662

4763
# Workaround for https://github.com/getsentry/sentry-dotnet/issues/3141
4864
[Sentry.SentryOptionsExtensions]::DisableAppDomainProcessExitFlush($options)
@@ -52,4 +68,3 @@ function Start-Sentry
5268
[Sentry.SentrySdk]::init($options) | Out-Null
5369
}
5470
}
55-

tests/userfeedback.tests.ps1 renamed to tests/out-sentry.tests.ps1

+9-6
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ BeforeAll {
22
. "$PSScriptRoot/utils.ps1"
33
}
44

5-
Describe 'UserFeedback' {
5+
Describe 'Out-Sentry' {
66
BeforeEach {
77
$events = [System.Collections.Generic.List[Sentry.SentryEvent]]::new();
88
$transport = [RecordingTransport]::new()
@@ -15,13 +15,13 @@ Describe 'UserFeedback' {
1515
Stop-Sentry
1616
}
1717

18-
It 'Out-Sentry returns an event ID for messages' {
18+
It 'returns an event ID for messages' {
1919
$eventId = 'msg' | Out-Sentry
2020
$eventId | Should -BeOfType [Sentry.SentryId]
2121
$eventId.ToString().Length | Should -Be 32
2222
}
2323

24-
It 'Out-Sentry returns an event ID for an error record' {
24+
It 'returns an event ID for an error record' {
2525
try
2626
{
2727
throw 'error'
@@ -34,15 +34,13 @@ Describe 'UserFeedback' {
3434
$eventId.ToString().Length | Should -Be 32
3535
}
3636

37-
It 'Feedback gets captured' {
37+
It 'captures feedback' {
3838
$eventId = 'msg' | Out-Sentry
3939

4040
$eventId | Should -BeOfType [Sentry.SentryId]
41-
[Sentry.SentrySdk]::Flush()
4241
$transport.Envelopes.Count | Should -Be 1
4342

4443
[Sentry.SentrySdk]::CaptureUserFeedback($eventId, '[email protected]', 'comments', 'name')
45-
[Sentry.SentrySdk]::Flush()
4644
$transport.Envelopes.Count | Should -Be 2
4745
$envelopeItem = $transport.Envelopes.ToArray()[1].Items[0]
4846
$envelopeItem.Header['type'] | Should -Be 'user_report'
@@ -52,4 +50,9 @@ Describe 'UserFeedback' {
5250
$envelopeItem.Payload.Source.Comments | Should -Be 'comments'
5351
}
5452

53+
It 'sends synchronously' {
54+
$eventId = 'msg' | Out-Sentry
55+
$eventId | Should -Not -Be $null
56+
$transport.Envelopes.Count | Should -Be 1
57+
}
5558
}

tests/scope.tests.ps1

-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ Describe 'Edit-SentryScope' {
1919
[Sentry.ScopeExtensions]::AddAttachment($_, $PSCommandPath)
2020
}
2121
'message' | Out-Sentry
22-
[Sentry.SentrySdk]::Flush()
2322
$transport.Envelopes.Count | Should -Be 1
2423
[Sentry.Protocol.Envelopes.Envelope]$envelope = $transport.Envelopes.ToArray()[0]
2524
$envelope.Items.Count | Should -Be 2
@@ -32,7 +31,6 @@ Describe 'Edit-SentryScope' {
3231
[byte[]] $data = 1, 2, 3, 4, 5
3332
[Sentry.ScopeExtensions]::AddAttachment($_, $data, 'filename.bin')
3433
}
35-
[Sentry.SentrySdk]::Flush()
3634
$transport.Envelopes.Count | Should -Be 1
3735
[Sentry.Protocol.Envelopes.Envelope]$envelope = $transport.Envelopes.ToArray()[0]
3836
$envelope.Items.Count | Should -Be 2

tests/utils.ps1

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
class RecordingTransport:Sentry.Extensibility.ITransport
22
{
3-
$envelopes = [System.Collections.Concurrent.ConcurrentQueue[Sentry.Protocol.Envelopes.Envelope]]::new();
3+
$envelopes = [System.Collections.Concurrent.ConcurrentQueue[Sentry.Protocol.Envelopes.Envelope]]::new()
44

55
[System.Threading.Tasks.Task]SendEnvelopeAsync([Sentry.Protocol.Envelopes.Envelope] $envelope, [System.Threading.CancellationToken] $cancellationToken)
66
{
7-
$this.envelopes.Enqueue($envelope);
8-
return [System.Threading.Tasks.Task]::CompletedTask;
7+
$this.envelopes.Enqueue($envelope)
8+
return [System.Threading.Tasks.Task]::CompletedTask
99
}
1010

1111
[void] Clear()
@@ -18,9 +18,9 @@ class TestLogger:Sentry.Infrastructure.DiagnosticLogger
1818
{
1919
TestLogger([Sentry.SentryLevel]$level) : base($level) {}
2020

21-
$entries = [System.Collections.Concurrent.ConcurrentQueue[string]]::new();
21+
$entries = [System.Collections.Concurrent.ConcurrentQueue[string]]::new()
2222

23-
[void]LogMessage([string] $message) { $this.entries.Enqueue($message); }
23+
[void]LogMessage([string] $message) { $this.entries.Enqueue($message) }
2424
}
2525

2626
class TestIntegration : Sentry.Integrations.ISdkIntegration
@@ -45,7 +45,7 @@ function StartSentryForEventTests([ref] $events, [ref] $transport)
4545
param([Sentry.SentryEvent]$e)
4646
$events.Add($e)
4747
return $e
48-
});
48+
})
4949

5050
# If events are not sent, there's a client report sent at the end and it blocks the process for the default flush
5151
# timeout because it cannot connect to the server. Let's just replace the transport too.

0 commit comments

Comments
 (0)