public void IfCircuitBreakerBreaksThenNoApiRequestsAreSent() { var mutex = new ManualResetEventSlim(); int logsReceived = 0; bool LogsSentCallback(int x) { Interlocked.Add(ref logsReceived, x); mutex.Set(); return(false); } var logsApi = new TestLogsApi(LogsSentCallback); var options = new BatchingSinkOptions(batchSizeLimit: 2, queueLimit: DefaultQueueLimit, period: TinyWait); var sink = new DatadogSink(logsApi, LogSettingsHelper.GetFormatter(), options); sink.Start(); for (var i = 0; i < BatchingSink.FailuresBeforeCircuitBreak; i++) { sink.EnqueueLog(new TestLogEvent(DirectSubmissionLogLevel.Debug, "A message")); mutex.Wait(10_000).Should().BeTrue(); mutex.Reset(); } // circuit should be broken sink.EnqueueLog(new TestLogEvent(DirectSubmissionLogLevel.Debug, "A message")); mutex.Wait(3_000).Should().BeFalse(); // don't expect it to be set logsReceived.Should().Be(BatchingSink.FailuresBeforeCircuitBreak); }
private ImmutableDirectLogSubmissionSettings( string host, string source, string globalTags, Uri intakeUrl, string apiKey, bool isEnabled, DirectSubmissionLogLevel minimumLevel, bool[] enabledIntegrations, List <string> validationErrors, List <string> enabledIntegrationNames, BatchingSinkOptions batchingOptions) { Host = host; Source = source; GlobalTags = globalTags; IntakeUrl = intakeUrl; ApiKey = apiKey; ValidationErrors = validationErrors; EnabledIntegrationNames = enabledIntegrationNames; MinimumLevel = minimumLevel; _enabledIntegrations = enabledIntegrations; IsEnabled = isEnabled; BatchingOptions = batchingOptions; }
public async Task EmitBatchEchoesLogsApiReturnValue(bool logsApiResponse) { var mutex = new ManualResetEventSlim(); bool LogsSentCallback(int x) { mutex.Set(); return(logsApiResponse); } var logsApi = new TestLogsApi(LogsSentCallback); var options = new BatchingSinkOptions(batchSizeLimit: 2, queueLimit: DefaultQueueLimit, period: TinyWait); var sink = new TestSink(logsApi, LogSettingsHelper.GetFormatter(), options); sink.Start(); var log = new TestLogEvent(DirectSubmissionLogLevel.Debug, "First message"); var queue = new Queue <DatadogLogEvent>(); queue.Enqueue(log); var result = await sink.CallEmitBatch(queue); result.Should().Be(logsApiResponse); }
public InMemoryBatchedSink( BatchingSinkOptions sinkOptions, Action disableSinkAction = null, bool[] emitResults = null) : base(sinkOptions, disableSinkAction) { _emitResults = emitResults ?? Array.Empty <bool>(); }
public void SinkSendsMessageAsJsonBatch() { var mutex = new ManualResetEventSlim(); int logsReceived = 0; const int expectedCount = 2; bool LogsSentCallback(int x) { if (Interlocked.Add(ref logsReceived, x) == expectedCount) { mutex.Set(); } return(true); } var logsApi = new TestLogsApi(LogsSentCallback); var options = new BatchingSinkOptions(batchSizeLimit: 2, queueLimit: DefaultQueueLimit, period: TinyWait); var sink = new DatadogSink(logsApi, LogSettingsHelper.GetFormatter(), options); sink.Start(); var firstMessage = "First message"; var secondMessage = "Second message"; sink.EnqueueLog(new TestLogEvent(DirectSubmissionLogLevel.Debug, firstMessage)); sink.EnqueueLog(new TestLogEvent(DirectSubmissionLogLevel.Information, secondMessage)); mutex.Wait(TimeSpan.FromSeconds(10)).Should().BeTrue(); logsApi.Logs.Should().NotBeEmpty(); var logs = logsApi.Logs .Select(batch => Encoding.UTF8.GetString(batch.Logs.Array)) .SelectMany(batch => JsonConvert.DeserializeObject <List <TestLogEvent> >(batch)) .ToList(); logs.Should().NotBeNull(); logs.Count.Should().Be(2); logs[0].Level.Should().Be(DirectSubmissionLogLevel.Debug); logs[0].Message.Should().Be(firstMessage); logs[1].Level.Should().Be(DirectSubmissionLogLevel.Information); logs[1].Message.Should().Be(secondMessage); }
public void SinkSendsMessagesToLogsApi() { var mutex = new ManualResetEventSlim(); var logsApi = new TestLogsApi(_ => { mutex.Set(); return(true); }); var options = new BatchingSinkOptions(batchSizeLimit: 2, queueLimit: DefaultQueueLimit, period: TinyWait); var sink = new DatadogSink(logsApi, LogSettingsHelper.GetFormatter(), options); sink.Start(); sink.EnqueueLog(new TestLogEvent(DirectSubmissionLogLevel.Debug, "First message")); // Wait for the logs to be sent, should be done in 50ms mutex.Wait(TimeSpan.FromSeconds(10)).Should().BeTrue(); logsApi.Logs.Should().ContainSingle(); }
public void SinkSendsMultipleBatches() { var mutex = new ManualResetEventSlim(); int logsReceived = 0; const int expectedCount = 5; bool LogsSentCallback(int x) { if (Interlocked.Add(ref logsReceived, x) == expectedCount) { mutex.Set(); } return(true); } var logsApi = new TestLogsApi(LogsSentCallback); var options = new BatchingSinkOptions(batchSizeLimit: 2, queueLimit: DefaultQueueLimit, period: TinyWait); var sink = new DatadogSink(logsApi, LogSettingsHelper.GetFormatter(), options); sink.Start(); sink.EnqueueLog(new TestLogEvent(DirectSubmissionLogLevel.Debug, "First message")); sink.EnqueueLog(new TestLogEvent(DirectSubmissionLogLevel.Information, "Second message")); sink.EnqueueLog(new TestLogEvent(DirectSubmissionLogLevel.Information, "Third message")); sink.EnqueueLog(new TestLogEvent(DirectSubmissionLogLevel.Information, "Fourth message")); sink.EnqueueLog(new TestLogEvent(DirectSubmissionLogLevel.Information, "Fifth message")); mutex.Wait(TimeSpan.FromSeconds(10)).Should().BeTrue(); logsApi.Logs.Should().HaveCountGreaterOrEqualTo(3); // batch size is 2, so at least 3 batches var logs = logsApi.Logs .Select(batch => Encoding.UTF8.GetString(batch.Logs.Array)) .SelectMany(batch => JsonConvert.DeserializeObject <List <TestLogEvent> >(batch)) .ToList(); logs.Count.Should().Be(5); logs.Select(x => x.Message).Should().OnlyHaveUniqueItems(); }
public void SinkRejectsGiantMessages() { var mutex = new ManualResetEventSlim(); var logsApi = new TestLogsApi(); var options = new BatchingSinkOptions(batchSizeLimit: 2, queueLimit: DefaultQueueLimit, period: TinyWait); var sink = new DatadogSink( logsApi, LogSettingsHelper.GetFormatter(), options, oversizeLogCallback: _ => mutex.Set(), sinkDisabledCallback: () => { }); sink.Start(); var message = new StringBuilder().Append('x', repeatCount: 1024 * 1024).ToString(); sink.EnqueueLog(new TestLogEvent(DirectSubmissionLogLevel.Debug, message)); // Wait for the logs to be sent, should be done in 50ms mutex.Wait(TimeSpan.FromSeconds(10)).Should().BeTrue(); logsApi.Logs.Should().BeEmpty(); }
public static ImmutableDirectLogSubmissionSettings Create( string?host, string?source, string?intakeUrl, string?apiKey, DirectSubmissionLogLevel minimumLevel, IDictionary <string, string> globalTags, ICollection <string> enabledLogShippingIntegrations, BatchingSinkOptions batchingOptions) { if (enabledLogShippingIntegrations.Count == 0) { // not trying to enable log submission, so don't log any errors and create a _null_ implementation return(CreateNullSettings()); } var isEnabled = true; var validationErrors = new List <string>(); if (string.IsNullOrWhiteSpace(host)) { isEnabled = false; validationErrors.Add($"Missing required setting '{ConfigurationKeys.DirectLogSubmission.Host}'."); } if (string.IsNullOrWhiteSpace(source)) { isEnabled = false; validationErrors.Add($"Missing required setting '{ConfigurationKeys.DirectLogSubmission.Source}'."); } if (!Uri.TryCreate(intakeUrl, UriKind.Absolute, out var intakeUri)) { isEnabled = false; validationErrors.Add($"The intake url '{intakeUrl}' was not a valid URL."); } if (string.IsNullOrWhiteSpace(apiKey)) { isEnabled = false; validationErrors.Add($"Missing required settings '{ConfigurationKeys.ApiKey}'."); } var stringifiedTags = StringifyGlobalTags(globalTags); var enabledIntegrations = new bool[IntegrationRegistry.Ids.Count]; var enabledIntegrationNames = new List <string>(SupportedIntegrations.Length); foreach (var integrationName in enabledLogShippingIntegrations) { if (!IntegrationRegistry.TryGetIntegrationId(integrationName, out var integrationId)) { validationErrors.Add( "Unknown integration: " + integrationName + ". Use a valid logs integration name: " + string.Join(", ", SupportedIntegrations.Select(x => IntegrationRegistry.GetName(x)))); continue; } if (!SupportedIntegrations.Contains(integrationId)) { validationErrors.Add( "Integration: " + integrationName + " is not a supported direct log submission integration. " + "Use one of " + string.Join(", ", SupportedIntegrations.Select(x => IntegrationRegistry.GetName(x)))); continue; } if (!enabledIntegrations[(int)integrationId]) { enabledIntegrationNames.Add(IntegrationRegistry.GetName(integrationId)); enabledIntegrations[(int)integrationId] = true; } } return(new ImmutableDirectLogSubmissionSettings( host: host ?? string.Empty, source: source ?? string.Empty, globalTags: stringifiedTags, intakeUrl: intakeUri !, apiKey: apiKey ?? string.Empty, isEnabled: isEnabled, minimumLevel: minimumLevel, enabledIntegrations: enabledIntegrations, validationErrors, enabledIntegrationNames, batchingOptions)); }
public TestSink(ILogsApi api, LogFormatter formatter, BatchingSinkOptions sinkOptions) : base(api, formatter, sinkOptions) { }