/// <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); }
/// <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(); }