// Note: calls to ConfigureAwait(false) aren't strictly required here, // as this method is only ever called in Task.Run, so there won't be a synchronization // context, but it's simple and harmless to make it "obviously okay". private async Task RunUploader(CancellationToken cancellationToken) { TimeSpan errorDelay = _serverErrorRetrySettings.InitialBackoff; while (true) { List <LogEntryExtra> entries; // Wait/loop until there are some log entries that need uploading. while (true) { var peek = await _logQ.PeekAsync(_maxUploadBatchSize, cancellationToken).ConfigureAwait(false); var logEntriesLost = peek.Lost; entries = peek.Entries; if (logEntriesLost != null) { var lostEntryExtra = new LogEntryExtra(-1, MakeLostEntry(logEntriesLost)); entries = (new[] { lostEntryExtra }).Concat(entries ?? Enumerable.Empty <LogEntryExtra>()).ToList(); } if (entries != null && entries.Count() > 0) { // There are log entries that need uploading, so do that now. break; } await _uploadReadyEvent.WaitAsync(cancellationToken).ConfigureAwait(false); } // Upload entries to the Cloud Logging server try { await _client.WriteLogEntriesAsync((LogName)null, null, s_emptyLabels, entries.Select(x => x.Entry), cancellationToken).ConfigureAwait(false); await _logQ.RemoveUntilAsync(entries.Last().Id, cancellationToken).ConfigureAwait(false); lock (_lock) { _maxConfirmedSentId = entries.Last().Id; } _uploadCompleteEvent.Set(); errorDelay = _serverErrorRetrySettings.InitialBackoff; } catch (Exception e) { // Always retry, regardless of error, as there's nothing much else to be done. await _scheduler.Delay(errorDelay, cancellationToken).ConfigureAwait(false); // TODO: Try to use RetryAttempt for this. errorDelay = _serverErrorRetrySettings.NextBackoff(errorDelay); // Use log4net internal logging to warn user of upload error. // Internal logging can be enabled as described in the "Troubleshooting" section of the log4net FAQ. log4net.Util.LogLog.Warn(typeof(LogUploader), "Error uploading log messages to server.", e); } } }
public void NextBackoff(double current, double expectedNext) { var settings = new RetrySettings( maxAttempts: 5, initialBackoff: TimeSpan.FromSeconds(1.0), maxBackoff: TimeSpan.FromSeconds(5.0), backoffMultiplier: 2.0, e => true, RetrySettings.NoJitter); var expected = TimeSpan.FromSeconds(expectedNext); var actual = settings.NextBackoff(TimeSpan.FromSeconds(current)); Assert.Equal(expected, actual); }