diff --git a/apis/Google.Cloud.Diagnostics.AspNetCore3/Google.Cloud.Diagnostics.AspNetCore3.sln b/apis/Google.Cloud.Diagnostics.AspNetCore3/Google.Cloud.Diagnostics.AspNetCore3.sln index f2488c0dbc65..07c81956c86e 100644 --- a/apis/Google.Cloud.Diagnostics.AspNetCore3/Google.Cloud.Diagnostics.AspNetCore3.sln +++ b/apis/Google.Cloud.Diagnostics.AspNetCore3/Google.Cloud.Diagnostics.AspNetCore3.sln @@ -3,30 +3,29 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Cloud.Diagnostics.AspNetCore3", "Google.Cloud.Diagnostics.AspNetCore3\Google.Cloud.Diagnostics.AspNetCore3.csproj", "{23D8A49D-947C-4CA6-B551-87B2BDECD3A1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Google.Cloud.Diagnostics.AspNetCore3", "Google.Cloud.Diagnostics.AspNetCore3\Google.Cloud.Diagnostics.AspNetCore3.csproj", "{23D8A49D-947C-4CA6-B551-87B2BDECD3A1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Cloud.Diagnostics.Common", "..\Google.Cloud.Diagnostics.Common\Google.Cloud.Diagnostics.Common\Google.Cloud.Diagnostics.Common.csproj", "{CC6B069E-A4F1-4DED-8BC7-1E670C48E3DA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Google.Cloud.Diagnostics.Common", "..\Google.Cloud.Diagnostics.Common\Google.Cloud.Diagnostics.Common\Google.Cloud.Diagnostics.Common.csproj", "{CC6B069E-A4F1-4DED-8BC7-1E670C48E3DA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Cloud.Diagnostics.AspNetCore3.IntegrationTests", "Google.Cloud.Diagnostics.AspNetCore3.IntegrationTests\Google.Cloud.Diagnostics.AspNetCore3.IntegrationTests.csproj", "{CCE7A221-2FCD-46B4-BB45-8173B3475138}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Google.Cloud.Diagnostics.AspNetCore3.IntegrationTests", "Google.Cloud.Diagnostics.AspNetCore3.IntegrationTests\Google.Cloud.Diagnostics.AspNetCore3.IntegrationTests.csproj", "{CCE7A221-2FCD-46B4-BB45-8173B3475138}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Cloud.ClientTesting", "..\..\tools\Google.Cloud.ClientTesting\Google.Cloud.ClientTesting.csproj", "{48F406D2-8BEC-4FCC-A7AC-CE2A1BE9E5BF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Google.Cloud.ClientTesting", "..\..\tools\Google.Cloud.ClientTesting\Google.Cloud.ClientTesting.csproj", "{48F406D2-8BEC-4FCC-A7AC-CE2A1BE9E5BF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Cloud.Diagnostics.Common.IntegrationTests", "..\Google.Cloud.Diagnostics.Common\Google.Cloud.Diagnostics.Common.IntegrationTests\Google.Cloud.Diagnostics.Common.IntegrationTests.csproj", "{5D5E2B8B-04D4-457D-95DA-26C13351AD70}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Google.Cloud.Diagnostics.Common.IntegrationTests", "..\Google.Cloud.Diagnostics.Common\Google.Cloud.Diagnostics.Common.IntegrationTests\Google.Cloud.Diagnostics.Common.IntegrationTests.csproj", "{5D5E2B8B-04D4-457D-95DA-26C13351AD70}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Cloud.Diagnostics.Common.Tests", "..\Google.Cloud.Diagnostics.Common\Google.Cloud.Diagnostics.Common.Tests\Google.Cloud.Diagnostics.Common.Tests.csproj", "{06AAB8AE-1833-44A9-92B7-6590AFEC8542}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Google.Cloud.Diagnostics.Common.Tests", "..\Google.Cloud.Diagnostics.Common\Google.Cloud.Diagnostics.Common.Tests\Google.Cloud.Diagnostics.Common.Tests.csproj", "{06AAB8AE-1833-44A9-92B7-6590AFEC8542}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Cloud.Diagnostics.AspNetCore3.Snippets", "Google.Cloud.Diagnostics.AspNetCore3.Snippets\Google.Cloud.Diagnostics.AspNetCore3.Snippets.csproj", "{A1ADC7D3-BFF9-4FA2-AF06-AACA6CFF46E8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Google.Cloud.Diagnostics.AspNetCore3.Snippets", "Google.Cloud.Diagnostics.AspNetCore3.Snippets\Google.Cloud.Diagnostics.AspNetCore3.Snippets.csproj", "{A1ADC7D3-BFF9-4FA2-AF06-AACA6CFF46E8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Google.Cloud.Diagnostics.AspNetCore3.Tests", "Google.Cloud.Diagnostics.AspNetCore3.Tests\Google.Cloud.Diagnostics.AspNetCore3.Tests.csproj", "{7E9F338F-95C6-4C2B-A3B2-979D4D34918F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Google.Cloud.Diagnostics.AspNetCore3.Tests", "Google.Cloud.Diagnostics.AspNetCore3.Tests\Google.Cloud.Diagnostics.AspNetCore3.Tests.csproj", "{7E9F338F-95C6-4C2B-A3B2-979D4D34918F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Google.Cloud.Diagnostics.Common.Snippets", "..\Google.Cloud.Diagnostics.Common\Google.Cloud.Diagnostics.Common.Snippets\Google.Cloud.Diagnostics.Common.Snippets.csproj", "{D2D3D836-9D94-4F4E-B167-796AB085F4C8}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {23D8A49D-947C-4CA6-B551-87B2BDECD3A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {23D8A49D-947C-4CA6-B551-87B2BDECD3A1}.Debug|Any CPU.Build.0 = Debug|Any CPU @@ -60,5 +59,15 @@ Global {7E9F338F-95C6-4C2B-A3B2-979D4D34918F}.Debug|Any CPU.Build.0 = Debug|Any CPU {7E9F338F-95C6-4C2B-A3B2-979D4D34918F}.Release|Any CPU.ActiveCfg = Release|Any CPU {7E9F338F-95C6-4C2B-A3B2-979D4D34918F}.Release|Any CPU.Build.0 = Release|Any CPU + {D2D3D836-9D94-4F4E-B167-796AB085F4C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D2D3D836-9D94-4F4E-B167-796AB085F4C8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D2D3D836-9D94-4F4E-B167-796AB085F4C8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D2D3D836-9D94-4F4E-B167-796AB085F4C8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {891EF638-28FB-453F-B9AD-6D73AC83EF93} EndGlobalSection EndGlobal diff --git a/apis/Google.Cloud.Diagnostics.Common/Google.Cloud.Diagnostics.Common.Snippets/DiagnosticsSnippets.cs b/apis/Google.Cloud.Diagnostics.Common/Google.Cloud.Diagnostics.Common.Snippets/DiagnosticsSnippets.cs index 647794e01c9e..310a6942c32d 100644 --- a/apis/Google.Cloud.Diagnostics.Common/Google.Cloud.Diagnostics.Common.Snippets/DiagnosticsSnippets.cs +++ b/apis/Google.Cloud.Diagnostics.Common/Google.Cloud.Diagnostics.Common.Snippets/DiagnosticsSnippets.cs @@ -1,4 +1,4 @@ -// Copyright 2021 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -74,10 +74,32 @@ public static IHostBuilder CreateHostBuilder() => // End sample } + private static class TimedBufferHandlesTimerException + { + // Sample: TimedBufferHandlesTimerException + public static IHostBuilder CreateHostBuilder() => + new HostBuilder() + .ConfigureServices(services => + { + // Replace ProjectId with your Google Cloud Project ID. + // Replace Service with a name or identifier for the service. + // Replace Version with a version for the service. + services.AddGoogleDiagnostics(ProjectId, Service, Version, + // Configure the three components so that exceptions thrown in the timer thread + // of timed buffers are handled by the given handler. + traceOptions: TraceOptions.Create(bufferOptions: BufferOptions.TimedBuffer().WithTimerExceptionHandler(ex => Console.WriteLine(ex))), + loggingOptions: LoggingOptions.Create(bufferOptions: BufferOptions.TimedBuffer().WithTimerExceptionHandler(ex => Console.WriteLine(ex))), + errorReportingOptions: ErrorReportingOptions.Create(bufferOptions: BufferOptions.TimedBuffer().WithTimerExceptionHandler(ex => Console.WriteLine(ex)))); + // Register other services here if you need them. + }); + // End sample + } + public static TheoryData> HostBuilders => new TheoryData> { DefaultHostBuilder.CreateHostBuilder, - NoBuffersHostBuilder.CreateHostBuilder + NoBuffersHostBuilder.CreateHostBuilder, + TimedBufferHandlesTimerException.CreateHostBuilder, }; [Theory] diff --git a/apis/Google.Cloud.Diagnostics.Common/Google.Cloud.Diagnostics.Common.Tests/TimedBufferingConsumerTest.cs b/apis/Google.Cloud.Diagnostics.Common/Google.Cloud.Diagnostics.Common.Tests/TimedBufferingConsumerTest.cs index 9ce3f09ea26a..4927044cdd13 100644 --- a/apis/Google.Cloud.Diagnostics.Common/Google.Cloud.Diagnostics.Common.Tests/TimedBufferingConsumerTest.cs +++ b/apis/Google.Cloud.Diagnostics.Common/Google.Cloud.Diagnostics.Common.Tests/TimedBufferingConsumerTest.cs @@ -1,4 +1,4 @@ -// Copyright 2016 Google Inc. All Rights Reserved. +// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -using Google.Api.Gax; -using Grpc.Core; using Moq; using System; using System.Collections.Generic; @@ -31,7 +29,7 @@ public class TimedBufferingConsumerTest public void Receive() { var mockConsumer = new Mock>(); - var consumer = TimedBufferingConsumer.Create(mockConsumer.Object, _waitTime); + var consumer = TimedBufferingConsumer.Create(mockConsumer.Object, _waitTime, null); consumer.Receive(new[] { 1, 2, 3, 4, 5 }); mockConsumer.Verify(c => c.Receive(It.IsAny>()), Times.Never()); } @@ -42,7 +40,7 @@ public void Flush() int[] intArray = { 1, 2, 3, 4 }; var mockConsumer = new Mock>(); mockConsumer.Setup(c => c.Receive(intArray)); - var consumer = TimedBufferingConsumer.Create(mockConsumer.Object, _waitTime); + var consumer = TimedBufferingConsumer.Create(mockConsumer.Object, _waitTime, null); consumer.Receive(intArray); mockConsumer.Verify(c => c.Receive(It.IsAny>()), Times.Never()); @@ -56,7 +54,7 @@ public void Flush_NoTraces() { var mockConsumer = new Mock>(); mockConsumer.Setup(c => c.Receive(new int[] { })); - var consumer = TimedBufferingConsumer.Create(mockConsumer.Object, _waitTime); + var consumer = TimedBufferingConsumer.Create(mockConsumer.Object, _waitTime, null); consumer.Receive(new int[] { }); consumer.Flush(); @@ -67,7 +65,7 @@ public void Flush_NoTraces() public async Task ReceiveAsync() { var mockConsumer = new Mock>(); - var consumer = TimedBufferingConsumer.Create(mockConsumer.Object, _waitTime); + var consumer = TimedBufferingConsumer.Create(mockConsumer.Object, _waitTime, null); await consumer.ReceiveAsync(new[] { 1, 2, 3, 4, 5 }, CancellationToken.None); mockConsumer.Verify(c => c.ReceiveAsync( It.IsAny>(), CancellationToken.None), Times.Never()); @@ -81,7 +79,7 @@ public async Task FlushAsync() var mockConsumer = new Mock>(); mockConsumer.Setup(c => c.ReceiveAsync( intArray, CancellationToken.None)).Returns(CommonUtils.CompletedTask); - var consumer = TimedBufferingConsumer.Create(mockConsumer.Object, _waitTime); + var consumer = TimedBufferingConsumer.Create(mockConsumer.Object, _waitTime, null); await consumer.ReceiveAsync(intArray, CancellationToken.None); mockConsumer.Verify(c => c.ReceiveAsync( @@ -97,7 +95,7 @@ public async Task FlushAsync_NoTraces() var mockConsumer = new Mock>(); mockConsumer.Setup(c => c.ReceiveAsync( new int[] { }, CancellationToken.None)); - var consumer = TimedBufferingConsumer.Create(mockConsumer.Object, _waitTime); + var consumer = TimedBufferingConsumer.Create(mockConsumer.Object, _waitTime, null); await consumer.ReceiveAsync(new int[] { }, CancellationToken.None); await consumer.FlushAsync(); @@ -110,7 +108,7 @@ public void Timer() { var mockConsumer = new Mock>(); var fakeTimer = new FakeThreadingTimer(); - var consumer = new TimedBufferingConsumer(mockConsumer.Object, _waitTime, fakeTimer); + var consumer = new TimedBufferingConsumer(mockConsumer.Object, _waitTime, null, fakeTimer); var intArray = new[] { 1, 2, 3, 4, 5 }; consumer.Receive(intArray); @@ -129,12 +127,29 @@ public void Timer_SwallowException() { var mockConsumer = new Mock>(); var fakeTimer = new FakeThreadingTimer(); - var consumer = new TimedBufferingConsumer(mockConsumer.Object, _waitTime, fakeTimer); + var consumer = new TimedBufferingConsumer(mockConsumer.Object, _waitTime, null, fakeTimer); mockConsumer.Setup(c => c.Receive(It.IsAny>())).Throws(new Exception()); consumer.Receive(new[] { 1, 2, 3, 4, 5 }); fakeTimer.FullTick(); mockConsumer.Verify(c => c.Receive(It.IsAny>()), Times.Once()); } + + [Fact] + public void Timer_HandleException() + { + var mockConsumer = new Mock>(); + var fakeTimer = new FakeThreadingTimer(); + int exceptionsCaught = 0; + var consumer = new TimedBufferingConsumer(mockConsumer.Object, _waitTime, HandleException, fakeTimer); + + mockConsumer.Setup(c => c.Receive(It.IsAny>())).Throws(new Exception()); + consumer.Receive(new[] { 1, 2, 3, 4, 5 }); + fakeTimer.FullTick(); + mockConsumer.Verify(c => c.Receive(It.IsAny>()), Times.Once()); + Assert.Equal(1, exceptionsCaught); + + void HandleException(Exception exception) => exceptionsCaught++; + } } } diff --git a/apis/Google.Cloud.Diagnostics.Common/Google.Cloud.Diagnostics.Common/BufferOptions.cs b/apis/Google.Cloud.Diagnostics.Common/Google.Cloud.Diagnostics.Common/BufferOptions.cs index 484a988a90e3..f21bb1424233 100644 --- a/apis/Google.Cloud.Diagnostics.Common/Google.Cloud.Diagnostics.Common/BufferOptions.cs +++ b/apis/Google.Cloud.Diagnostics.Common/Google.Cloud.Diagnostics.Common/BufferOptions.cs @@ -1,4 +1,4 @@ -// Copyright 2016 Google Inc. All Rights Reserved. +// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -57,7 +57,21 @@ public sealed class BufferOptions /// The time to wait before the buffer is flushed, only used for public TimeSpan BufferWaitTime { get; } - internal BufferOptions(BufferType bufferType, int? bufferSizeBytes = null, TimeSpan? bufferWaitTime = null) + /// + /// An action that will be called whenever an exception is thrown during a timed flush of the buffer. + /// May be null. Only used for . + /// + /// + /// Throwing an exception within the timer thread risks inmediately crashing the applicaiton, so we + /// never do that, even if has been set to + /// because propagating exceptions is different than crashing + /// an application. + /// Instead, client code can specify a value for this action, that will be called when an exception is thrown + /// within the timer thread. Client code may decide what to do in such cases. + /// + public Action TimerExceptionHandler { get; } + + internal BufferOptions(BufferType bufferType, int? bufferSizeBytes = null, TimeSpan? bufferWaitTime = null, Action timerExceptionHandler = null) { BufferType = bufferType; BufferSizeBytes = bufferSizeBytes ?? default; @@ -73,14 +87,23 @@ internal BufferOptions(BufferType bufferType, int? bufferSizeBytes = null, TimeS /// Create for /// /// Optional, The buffer size in bytes. - public static BufferOptions SizedBuffer(int bufferSizeBytes = DefaultBufferSize) - => new BufferOptions(BufferType.Sized, bufferSizeBytes); + public static BufferOptions SizedBuffer(int bufferSizeBytes = DefaultBufferSize) => + new BufferOptions(BufferType.Sized, bufferSizeBytes); /// /// Create for /// /// Optional, The minimum amount of time between flushes. - public static BufferOptions TimedBuffer(TimeSpan? bufferWaitTime = null) - => new BufferOptions(BufferType.Timed, null, bufferWaitTime ?? DefaultWaitTime); + public static BufferOptions TimedBuffer(TimeSpan? bufferWaitTime = null) => + new BufferOptions(BufferType.Timed, null, bufferWaitTime ?? DefaultWaitTime); + + /// + /// Creates a new instance which is identical to this one + /// but with the given timer exception handler. + /// + public BufferOptions WithTimerExceptionHandler(Action handler) => + BufferType == BufferType.Timed + ? new BufferOptions(BufferType.Timed, null, BufferWaitTime, handler) + : throw new InvalidOperationException($"This buffer is of type {BufferType.Sized} and timer exception handlers can only be added to buffers of type {BufferType.Timed}"); } -} \ No newline at end of file +} diff --git a/apis/Google.Cloud.Diagnostics.Common/Google.Cloud.Diagnostics.Common/ConsumerFactory.cs b/apis/Google.Cloud.Diagnostics.Common/Google.Cloud.Diagnostics.Common/ConsumerFactory.cs index 9844882cf530..58dadd1a6808 100644 --- a/apis/Google.Cloud.Diagnostics.Common/Google.Cloud.Diagnostics.Common/ConsumerFactory.cs +++ b/apis/Google.Cloud.Diagnostics.Common/Google.Cloud.Diagnostics.Common/ConsumerFactory.cs @@ -1,4 +1,4 @@ -// Copyright 2016 Google Inc. All Rights Reserved. +// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -48,7 +48,7 @@ internal static IConsumer GetConsumer(IConsumer consumer, Func siz case BufferType.Sized: return SizedBufferingConsumer.Create(retryConsumer, sizer, bufferOptions.BufferSizeBytes); case BufferType.Timed: - return TimedBufferingConsumer.Create(retryConsumer, bufferOptions.BufferWaitTime); + return TimedBufferingConsumer.Create(retryConsumer, bufferOptions.BufferWaitTime, bufferOptions.TimerExceptionHandler); case BufferType.None: return retryConsumer; default: diff --git a/apis/Google.Cloud.Diagnostics.Common/Google.Cloud.Diagnostics.Common/TimedBufferingConsumer.cs b/apis/Google.Cloud.Diagnostics.Common/Google.Cloud.Diagnostics.Common/TimedBufferingConsumer.cs index 6f9fe3ea80b3..f4fe06c8f22b 100644 --- a/apis/Google.Cloud.Diagnostics.Common/Google.Cloud.Diagnostics.Common/TimedBufferingConsumer.cs +++ b/apis/Google.Cloud.Diagnostics.Common/Google.Cloud.Diagnostics.Common/TimedBufferingConsumer.cs @@ -1,4 +1,4 @@ -// Copyright 2016 Google Inc. All Rights Reserved. +// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,7 +15,6 @@ using Google.Api.Gax; using System; using System.Collections.Generic; -using System.Linq; namespace Google.Cloud.Diagnostics.Common { @@ -33,7 +32,7 @@ internal class TimedBufferingConsumer : FlushableConsumerBase /// The timer to automatically flush the buffer. private IThreadingTimer _timer; - internal TimedBufferingConsumer(IConsumer consumer, TimeSpan waitTime, IThreadingTimer timer) + internal TimedBufferingConsumer(IConsumer consumer, TimeSpan waitTime, Action timerExceptionHandler, IThreadingTimer timer) : base(consumer) { _timer = GaxPreconditions.CheckNotNull(timer, nameof(timer)); @@ -43,10 +42,14 @@ internal TimedBufferingConsumer(IConsumer consumer, TimeSpan waitTime, IThrea { Flush(); } - catch (Exception) + catch (Exception ex) { - // TODO(atarafamas): This is a short term solution to ensure - // we do not kill a process. See issue #2182 (currently in backlog) to track the long solution. + // Rethrowing exceptions here on the timer thread risks inmediately + // crashing the application, so we don't do that even if the library + // has been configured to propagate exceptions. + // Instead calling code can specify an exception handling action + // through options. + timerExceptionHandler?.Invoke(ex); } }, waitTime); } @@ -57,8 +60,9 @@ internal TimedBufferingConsumer(IConsumer consumer, TimeSpan waitTime, IThrea /// /// The consumer to flush to. Must not be null. /// The amount of time between automatic flushes. - public static TimedBufferingConsumer Create(IConsumer consumer, TimeSpan waitTime) - => new TimedBufferingConsumer(consumer, waitTime, new SimpleThreadingTimer()); + /// The action to handle exceptions thrown within the timer thread. May be null. + internal static TimedBufferingConsumer Create(IConsumer consumer, TimeSpan waitTime, Action timerExceptionHandler) + => new TimedBufferingConsumer(consumer, waitTime, timerExceptionHandler, new SimpleThreadingTimer()); /// public override void Dispose() diff --git a/apis/Google.Cloud.Diagnostics.Common/docs/index.md b/apis/Google.Cloud.Diagnostics.Common/docs/index.md index 960c0550f30b..b3725a8417b3 100644 --- a/apis/Google.Cloud.Diagnostics.Common/docs/index.md +++ b/apis/Google.Cloud.Diagnostics.Common/docs/index.md @@ -101,6 +101,17 @@ Below you can see sample code that forces all three components to use no bufferi {{sample:Diagnostics.ConfigureBuffers}} +#### Exceptions thrown in timed buffers' timer thread + +Rethrowing exceptions thrown in timed buffers' timer thread may immediately crash the application. These exceptions are not propagated +by default, even if the library has been configured to propagate all other exceptions. If you need to handle these exceptions, for +diagnostic or other purposes, you can provide your own exception handler when configuring a timed buffer. + +Below you can see sample code that configures all three components to use timed buffers that will write timer thread exceptions +to the console. + +{{sample:Diagnostics.TimedBufferHandlesTimerException}} + ## Error reporting The error reporting component allows you to create error reports from exceptions thrown in your application.