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);
        }
예제 #4
0
 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)
 {
 }