Esempio n. 1
0
        /// <summary>
        /// Adds a sink that sends log events to Datadog.
        /// </summary>
        /// <param name="loggerConfiguration">The logger configuration.</param>
        /// <param name="apiKey">Your Datadog API key.</param>
        /// <param name="source">The integration name.</param>
        /// <param name="service">The service name.</param>
        /// <param name="host">The host name.</param>
        /// <param name="tags">Custom tags.</param>
        /// <param name="configuration">The Datadog logs client configuration.</param>
        /// <param name="logLevel">The minimum log level for the sink.</param>
        /// <param name="batchSizeLimit">The maximum number of events to emit in a single batch.</param>
        /// <param name="batchPeriod">The time to wait before emitting a new event batch.</param>
        /// <param name="queueLimit">
        /// Maximum number of events to hold in the sink's internal queue, or <c>null</c>
        /// for an unbounded queue. The default is <c>10000</c>
        /// </param>
        /// <param name="exceptionHandler">This function is called when an exception occurs when using
        /// DatadogConfiguration.UseTCP=false (the default configuration)</param>
        /// <param name="detectTCPDisconnection">Detect when the TCP connection is lost and recreate a new connection.</param>
        /// <returns>Logger configuration</returns>
        /// <exception cref="ArgumentNullException">A required parameter is null.</exception>
        public static LoggerConfiguration DatadogLogs(
            this LoggerSinkConfiguration loggerConfiguration,
            string apiKey,
            string source  = null,
            string service = null,
            string host    = null,
            string[] tags  = null,
            DatadogConfiguration configuration = null,
            LogEventLevel logLevel             = LevelAlias.Minimum,
            int?batchSizeLimit   = null,
            TimeSpan?batchPeriod = null,
            int?queueLimit       = null,
            Action <Exception> exceptionHandler = null,
            bool detectTCPDisconnection         = false)
        {
            if (loggerConfiguration == null)
            {
                throw new ArgumentNullException(nameof(loggerConfiguration));
            }
            if (string.IsNullOrWhiteSpace(apiKey))
            {
                throw new ArgumentNullException(nameof(apiKey));
            }

            configuration = (configuration != null) ? configuration : new DatadogConfiguration();
            var sink = DatadogSink.Create(apiKey, source, service, host, tags, configuration, batchSizeLimit, batchPeriod, queueLimit, exceptionHandler, detectTCPDisconnection);

            return(loggerConfiguration.Sink(sink, logLevel));
        }
        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);
        }
Esempio n. 3
0
        /// <summary>
        /// Adds a sink that sends log events to Datadog.
        /// </summary>
        /// <param name="loggerConfiguration">The logger configuration.</param>
        /// <param name="apiKey">Your Datadog API key.</param>
        /// <param name="source">The integration name.</param>
        /// <param name="service">The service name.</param>
        /// <param name="host">The host name.</param>
        /// <param name="tags">Custom tags.</param>
        /// <param name="configuration">The Datadog logs client configuration.</param>
        /// <param name="configurationSection">A config section defining the datadog configuration.</param>
        /// <param name="logLevel">The minimum log level for the sink.</param>
        /// <param name="batchSizeLimit">The maximum number of events to emit in a single batch.</param>
        /// <param name="batchPeriod">The time to wait before emitting a new event batch.</param>
        /// <param name="queueLimit">
        /// Maximum number of events to hold in the sink's internal queue, or <c>null</c>
        /// for an unbounded queue. The default is <c>10000</c>
        /// </param>
        /// <param name="exceptionHandler">This function is called when an exception occurs when using
        /// DatadogConfiguration.UseTCP=false (the default configuration)</param>
        /// <returns>Logger configuration</returns>
        /// <exception cref="ArgumentNullException">A required parameter is null.</exception>
        public static LoggerConfiguration DatadogLogs(
            this LoggerSinkConfiguration loggerConfiguration,
            string apiKey,
            string source  = null,
            string service = null,
            string host    = null,
            string[] tags  = null,
            DatadogConfiguration configuration         = null,
            IConfigurationSection configurationSection = null,
            LogEventLevel logLevel = LevelAlias.Minimum,
            int?batchSizeLimit     = null,
            TimeSpan?batchPeriod   = null,
            int?queueLimit         = null,
            Action <Exception> exceptionHandler = null)
        {
            if (loggerConfiguration == null)
            {
                throw new ArgumentNullException(nameof(loggerConfiguration));
            }
            if (string.IsNullOrWhiteSpace(apiKey))
            {
                throw new ArgumentNullException(nameof(apiKey));
            }

            var config = ApplyMicrosoftExtensionsConfiguration.ConfigureDatadogConfiguration(configuration, configurationSection);
            var sink   = DatadogSink.Create(apiKey, source, service, host, tags, config, batchSizeLimit, batchPeriod, queueLimit, exceptionHandler);

            return(loggerConfiguration.Sink(sink, logLevel));
        }
        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();
        }
        /// <summary>
        /// Adds a sink that sends log events via datadog.
        /// </summary>
        /// <param name="loggerConfiguration">The logger configuration.</param>
        /// <param name="datadogConfiguration">The configuration used for writing events to datadog.</param>
        /// <param name="outputTemplate">A message template describing the format used to write to the sink.
        /// the default is "{Timestamp} [{Level}] {Message}{NewLine}{Exception}".</param>
        /// <param name="restrictedToMinimumLevel">The minimum log event level required in order to write an event to the sink.</param>
        /// <param name="batchPostingLimit">The maximum number of events to post in a single batch.</param>
        /// <param name="period">The time to wait between checking for event batches.</param>
        /// <param name="formatProvider">Supplies culture-specific formatting information, or null.</param>
        /// <returns>Logger configuration, allowing datadogConfiguration to continue.</returns>
        /// <exception cref="ArgumentNullException">A required parameter is null.</exception>
        public static LoggerConfiguration Datadog(
            this LoggerSinkConfiguration loggerConfiguration,
            DatadogConfiguration datadogConfiguration,
            string outputTemplate = DefaultOutputTemplate,
            LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
            int batchPostingLimit          = DatadogSink.DefaultBatchPostingLimit,
            TimeSpan?period                = null,
            IFormatProvider formatProvider = null)
        {
            if (datadogConfiguration == null)
            {
                throw new ArgumentNullException("datadogConfiguration");
            }

            var defaultedPeriod = period ?? DatadogSink.DefaultPeriod;
            var formatter       = new MessageTemplateTextFormatter(outputTemplate, formatProvider);
            var datadogSink     = new DatadogSink(datadogConfiguration, batchPostingLimit, defaultedPeriod, formatter);

            return(loggerConfiguration.Sink(datadogSink, restrictedToMinimumLevel));
        }
        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();
        }