protected override async Task EmitBatchAsync(IEnumerable <LogEvent> events) { _nextRequiredLevelCheckUtc = DateTime.UtcNow.Add(RequiredLevelCheckInterval); string payload, payloadContentType; if (_useCompactFormat) { payloadContentType = SeqApi.CompactLogEventFormatMimeType; payload = FormatCompactPayload(events, _eventBodyLimitBytes); } else { payloadContentType = SeqApi.RawEventFormatMimeType; payload = FormatRawPayload(events, _eventBodyLimitBytes); } var content = new StringContent(payload, Encoding.UTF8, payloadContentType); if (!string.IsNullOrWhiteSpace(_apiKey)) { content.Headers.Add(SeqApi.ApiKeyHeaderName, _apiKey); } var result = await _httpClient.PostAsync(SeqApi.BulkUploadResource, content).ConfigureAwait(false); if (!result.IsSuccessStatusCode) { throw new LoggingFailedException($"Received failed result {result.StatusCode} when posting events to Seq"); } var returned = await result.Content.ReadAsStringAsync(); _controlledSwitch.Update(SeqApi.ReadEventInputResult(returned)); }
async Task OnTick() { LogEventLevel?minimumAcceptedLevel = null; try { int count; do { count = 0; // Locking the bookmark ensures that though there may be multiple instances of this // class running, only one will ship logs at a time. using (var bookmark = IOFile.Open(_bookmarkFilename, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read)) { long nextLineBeginsAtOffset; string currentFile; TryReadBookmark(bookmark, out nextLineBeginsAtOffset, out currentFile); var fileSet = GetFileSet(); if (currentFile == null || !IOFile.Exists(currentFile)) { nextLineBeginsAtOffset = 0; currentFile = fileSet.FirstOrDefault(); } if (currentFile == null) { continue; } var payload = ReadPayload(currentFile, ref nextLineBeginsAtOffset, ref count); if (count > 0 || _controlledSwitch.IsActive && _nextRequiredLevelCheckUtc < DateTime.UtcNow) { lock (_stateLock) { _nextRequiredLevelCheckUtc = DateTime.UtcNow.Add(RequiredLevelCheckInterval); } var content = new StringContent(payload, Encoding.UTF8, "application/json"); if (!string.IsNullOrWhiteSpace(_apiKey)) { content.Headers.Add(SeqApi.ApiKeyHeaderName, _apiKey); } var result = await _httpClient.PostAsync(SeqApi.BulkUploadResource, content).ConfigureAwait(false); if (result.IsSuccessStatusCode) { _connectionSchedule.MarkSuccess(); WriteBookmark(bookmark, nextLineBeginsAtOffset, currentFile); var returned = await result.Content.ReadAsStringAsync().ConfigureAwait(false); minimumAcceptedLevel = SeqApi.ReadEventInputResult(returned); } else if (result.StatusCode == HttpStatusCode.BadRequest || result.StatusCode == HttpStatusCode.RequestEntityTooLarge) { // The connection attempt was successful - the payload we sent was the problem. _connectionSchedule.MarkSuccess(); await DumpInvalidPayload(result, payload).ConfigureAwait(false); WriteBookmark(bookmark, nextLineBeginsAtOffset, currentFile); } else { _connectionSchedule.MarkFailure(); SelfLog.WriteLine("Received failed HTTP shipping result {0}: {1}", result.StatusCode, await result.Content.ReadAsStringAsync().ConfigureAwait(false)); break; } } else { // For whatever reason, there's nothing waiting to send. This means we should try connecting again at the // regular interval, so mark the attempt as successful. _connectionSchedule.MarkSuccess(); // Only advance the bookmark if no other process has the // current file locked, and its length is as we found it. if (fileSet.Length == 2 && fileSet.First() == currentFile && IsUnlockedAtLength(currentFile, nextLineBeginsAtOffset)) { WriteBookmark(bookmark, 0, fileSet[1]); } if (fileSet.Length > 2) { // Once there's a third file waiting to ship, we do our // best to move on, though a lock on the current file // will delay this. IOFile.Delete(fileSet[0]); } } } }while (count == _batchPostingLimit); } catch (Exception ex) { SelfLog.WriteLine("Exception while emitting periodic batch from {0}: {1}", this, ex); _connectionSchedule.MarkFailure(); } finally { lock (_stateLock) { _controlledSwitch.Update(minimumAcceptedLevel); if (!_unloading) { SetTimer(); } } } }