예제 #1
0
        public void LoggerCanRenderLogMessage()
        {
            var sink        = new TestSink();
            var serilogSink = new DirectSubmissionSerilogSink(sink, DirectSubmissionLogLevel.Debug);

            var level   = LogEventLevel.Error;
            var rawText = "someValue";

            GetSerilogMessageProcessor()
            .Process("This is {SomeValue}", new object[] { "someValue" }, out var parsedTemplate, out var boundProperties);
            var logEvent = new LogEvent(DateTimeOffset.Now, level, exception: null, parsedTemplate, boundProperties);

            var proxy = logEvent.DuckCast <ILogEvent>();

            serilogSink.Emit(proxy);

            var log       = sink.Events.Should().ContainSingle().Subject;
            var sb        = new StringBuilder();
            var formatter = LogSettingsHelper.GetFormatter();

            log.Format(sb, formatter);

            var formatted = sb.ToString();

            formatted.Should()
            .NotBeNullOrWhiteSpace()
            .And.Contain(rawText)
            .And.Contain($@"This is \""{rawText}\""");
        }
        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);
        }
예제 #3
0
        public void LoggerCanRenderLogMessage()
        {
            var sink      = new TestSink();
            var formatter = LogSettingsHelper.GetFormatter();
            var logger    = GetLogger(sink);

            var level = (int)DirectSubmissionLogLevel.Error;

            logger.IsEnabled(level).Should().BeTrue();
            var state   = "someValue";
            var eventId = 123;

            logger.Log(
                logLevel: level,
                eventId: eventId,
                state: state,
                exception: null,
                (s, ex) => $"This is {s}");

            var log = sink.Events.Should().ContainSingle().Subject;
            var sb  = new StringBuilder();

            log.Format(sb, formatter);

            var formatted = sb.ToString();

            formatted.Should()
            .NotBeNullOrWhiteSpace()
            .And.Contain(state)
            .And.Contain(eventId.ToString())
            .And.Contain($"This is {state}");
        }
        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);
        }
예제 #5
0
        private static DirectSubmissionLogger GetLogger(TestSink sink)
        {
            var settings = LogSettingsHelper.GetValidSettings();

            return(new DirectSubmissionLogger(
                       name: "TestLogger",
                       scopeProvider: new NullScopeProvider(),
                       sink: sink,
                       logFormatter: LogSettingsHelper.GetFormatter(),
                       minimumLogLevel: settings.MinimumLevel));
        }
예제 #6
0
        public void SerializesEventCorrectly()
        {
            var logEvent = GetLogEvent();
            var log      = logEvent.DuckCast <ILogEvent>();

            var formatter = LogSettingsHelper.GetFormatter();

            var sb = new StringBuilder();

            SerilogLogFormatter.FormatLogEvent(formatter, sb, log);
            var actual = sb.ToString();

            var expected = @"{""@t"":""2021-09-13T10:40:57.0000000Z"",""@m"":""This is a test with a 123"",""@l"":""Debug"",""@x"":""System.InvalidOperationException: Oops, just a test!"",""Value"":123,""OtherProperty"":62,""@i"":""a9a87aee"",""ddsource"":""csharp"",""service"":""MyTestService"",""dd_env"":""integration_tests"",""dd_version"":""1.0.0"",""host"":""some_host""}";

            actual.Should().Be(expected);
        }
예제 #7
0
        public void SerializesEventCorrectly()
        {
            var logEvent  = GetLogEvent();
            var formatter = LogSettingsHelper.GetFormatter();
            var sb        = new StringBuilder();

#if LOG4NET_2
            var log = logEvent.DuckCast <ILoggingEventDuck>();
            Log4NetLogFormatter.FormatLogEvent(formatter, sb, log, log.TimeStampUtc);
#else
            var log = logEvent.DuckCast <ILoggingEventLegacyDuck>();
            Log4NetLogFormatter.FormatLogEvent(formatter, sb, log, log.TimeStamp);
#endif
            var actual = sb.ToString();

            var expected = @"{""@t"":""2021-09-13T10:40:57.0000000Z"",""@m"":""This is a test with a 123"",""@l"":""Debug"",""@x"":""System.InvalidOperationException: Oops, just a test!"",""Value"":123,""@i"":""aad9c020"",""ddsource"":""csharp"",""service"":""MyTestService"",""dd_env"":""integration_tests"",""dd_version"":""1.0.0"",""host"":""some_host""}";
            actual.Should().Be(expected);
        }
        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);
        }
예제 #9
0
        public void CanDeserializeFormattedLogs()
        {
            var formatter = LogSettingsHelper.GetFormatter();
            var log1      = GetLog(formatter, "This is a {Value}", DirectSubmissionLogLevel.Warning, new Dictionary <string, string> {
                { "Value", "SomeValue" }
            });
            var log2 = GetLog(formatter, "This is another template with some other value", DirectSubmissionLogLevel.Information, new Dictionary <string, string> {
                { "Value", "SomeOtherValue" }
            });

            var serialized = $"[{log1},{log2}]";

            using var ms = new MemoryStream(Encoding.UTF8.GetBytes(serialized));

            var logs = MockLogsIntake.Log.DeserializeFromStream(ms);

            logs.Should().NotBeNull();
            logs.Should().HaveCount(2);
        }
        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();
        }
예제 #11
0
        public void SerializesEventCorrectly()
        {
            var formatter     = LogSettingsHelper.GetFormatter();
            var scopeProvider = new LoggerExternalScopeProvider();
            var logger        = new TestLogger(scopeProvider, formatter, new DateTime(2021, 09, 13, 10, 40, 57));

            scopeProvider.Push(new Dictionary <string, object> {
                { "OtherProperty", 62 }
            });
            scopeProvider.Push("Some other value");

            logger.Log(
                LogLevel.Debug,
                new InvalidOperationException("Oops, just a test!"),
                "This is a test with a {Value}",
                123);

            var expected = @"{""@t"":""2021-09-13T10:40:57.0000000Z"",""@m"":""This is a test with a 123"",""@l"":""Debug"",""@x"":""System.InvalidOperationException: Oops, just a test!"",""Value"":123,""OtherProperty"":62,""Scopes"":[""Some other value""]""@i"":""a9a87aee"",""ddsource"":""csharp"",""service"":""MyTestService"",""host"":""some_host""}";

            logger.Logs.Should().ContainSingle(expected);
        }
예제 #12
0
        public void LoggerIncludesPropertiesInLog()
        {
            var formatter      = LogSettingsHelper.GetFormatter();
            var memoryAppender = new MemoryAppender();
            var sink           = new Log4NetHelper.TestSink();
            var appender       = Log4NetHelper.GetAppender(sink, DirectSubmissionLogLevel.Debug);
            var appenderProxy  = (IAppender)appender.DuckImplement(typeof(IAppender));
            var repository     = log4net.LogManager.GetRepository();

            BasicConfigurator.Configure(repository, memoryAppender, appenderProxy);
            var logger = LogManager.GetLogger(typeof(DirectSubmissionLog4NetAppenderTests));

            // Set an ambient property
            var someKey   = "some key";
            var someValue = "some Value";

            log4net.ThreadContext.Properties[someKey] = someValue;

            // write the log
            var message = "This is a value";

            logger.Error(message);

            // remove the context before rendering
            log4net.ThreadContext.Properties.Remove(someKey);

            var logEvent = sink.Events.Should().ContainSingle().Subject;

            // get the rendered log
            var sb = new StringBuilder();

            logEvent.Format(sb, formatter);
            var log = sb.ToString();

            log.Should()
            .Contain(message)
            .And.Contain(someKey)
            .And.Contain(someValue)
            .And.Contain(DirectSubmissionLogLevelExtensions.Error);
        }
예제 #13
0
        public void DoesntAddPropertiesThatAreAlreadyAddedTwice()
        {
            var formatter     = LogSettingsHelper.GetFormatter();
            var scopeProvider = new LoggerExternalScopeProvider();
            var logger        = new TestLogger(scopeProvider, formatter, new DateTime(2021, 09, 13, 10, 40, 57));
            var properties    = new Dictionary <string, object>
            {
                { "Host", nameof(DoesntAddPropertiesThatAreAlreadyAddedTwice) + "host" },
                { "dd_service", nameof(DoesntAddPropertiesThatAreAlreadyAddedTwice) + "service" },
                { "ddsource", nameof(DoesntAddPropertiesThatAreAlreadyAddedTwice) + "source" },
                { "ddtags", nameof(DoesntAddPropertiesThatAreAlreadyAddedTwice) + ":tag" },
            };

            using (scopeProvider.Push(properties))
                using (scopeProvider.Push("Another value"))
                {
                    logger.Log(
                        LogLevel.Debug,
                        new InvalidOperationException("Oops, just a test!"),
                        "This is a test with a {Value} and a {Host}",
                        123,
                        "some host");
                }

            var log = logger.Logs.Should().ContainSingle().Subject;

            var json = JObject.Parse(log);

            // should have all the added properties
            foreach (var property in properties)
            {
                json.Properties()
                .Where(x => string.Equals(property.Key, x.Name, StringComparison.OrdinalIgnoreCase))
                .Should()
                .ContainSingle()
                .Which.Value.ToString()
                .Should()
                .Be(property.Value.ToString());
            }
        }
        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();
        }
예제 #15
0
        public void ShouldNotEmitLogWhenNotEnabled()
        {
            var sink      = new TestSink();
            var formatter = LogSettingsHelper.GetFormatter();
            var logger    = new DirectSubmissionLogger(
                name: "TestLogger",
                scopeProvider: new NullScopeProvider(),
                sink: sink,
                logFormatter: formatter,
                minimumLogLevel: DirectSubmissionLogLevel.Information);

            var level = (int)DirectSubmissionLogLevel.Debug;

            logger.IsEnabled(level).Should().BeFalse();
            logger.Log(
                logLevel: level,
                eventId: 123,
                state: "someValue",
                exception: null,
                (s, ex) => $"This is {s}");

            sink.Events.Should().BeEmpty();
        }
        public void SerializesEventCorrectlyWhenUsingFallbackProperties()
        {
            var logEvent = GetLogEvent();
            var log      = NLogHelper.GetLogEventProxy(logEvent);
            var fallback = new Dictionary <object, object>
            {
                { "Value", 123 },
                { "OtherProperty", 62 },
            };

            var wrapper = new LogEntry(log, properties: null, fallback);

            var formatter = LogSettingsHelper.GetFormatter();

            var sb = new StringBuilder();

            NLogLogFormatter.FormatLogEvent(formatter, sb, wrapper);
            var actual = sb.ToString();

            var expected = @"{""@t"":""2021-09-13T10:40:57.0000000Z"",""@m"":""This is a test with a 123"",""@l"":""Debug"",""@x"":""System.InvalidOperationException: Oops, just a test!"",""Value"":123,""OtherProperty"":62,""@i"":""e5450052"",""ddsource"":""csharp"",""service"":""MyTestService"",""dd_env"":""integration_tests"",""dd_version"":""1.0.0"",""host"":""some_host""}";

            actual.Should().Be(expected);
        }
        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 void LoggerIncludesPropertiesInLog()
        {
            var formatter   = LogSettingsHelper.GetFormatter();
            var sink        = new NLogHelper.TestSink();
            var target      = NLogHelper.CreateTarget(sink, DirectSubmissionLogLevel.Debug);
            var targetProxy = NLogCommon <LoggingConfiguration> .CreateNLogTargetProxy(target);

            var config = new LoggingConfiguration();

            NLogHelper.AddTargetToConfig(config, targetProxy);

            var logFactory = new LogFactory(config);
            var logger     = logFactory.GetLogger(nameof(LoggerIncludesPropertiesInLog));

            // We don't currently record NDC/NDLC
#if NLOG_45
            var messageTemplate = "This is a message with {Value}";
#else
            var messageTemplate = "This is a message with {0}";
#endif
            var mdcKey   = "some mdcKey";
            var mdcValue = "some mdcValue";
#if !NLOG_2
            var mdclKey   = "some mdclKey";
            var mdclValue = "some mdclValue";
#endif
            // var nestedScope = "some nested name";
            // var nestedDictionary = new Dictionary<string, object> { { "nlcKey", 657 } };
            // var dictValues = nestedDictionary.First();
            try
            {
                MappedDiagnosticsContext.Set(mdcKey, mdcValue);
#if !NLOG_2
                MappedDiagnosticsLogicalContext.Set(mdclKey, mdclValue);
#endif
                logger.Error(messageTemplate, 123);
            }
            finally
            {
                MappedDiagnosticsContext.Remove(mdcKey);
#if !NLOG_2
                MappedDiagnosticsLogicalContext.Remove(mdclKey);
#endif
            }

            var logEvent = sink.Events.Should().ContainSingle().Subject;

            // get the rendered log
            var sb = new StringBuilder();
            logEvent.Format(sb, formatter);
            var log = sb.ToString();

            log.Should()
            .Contain("This is a message with 123")
            .And.Contain(mdcKey)
            .And.Contain(mdcValue)
#if !NLOG_2
            .And.Contain(mdclKey)
            .And.Contain(mdclValue)
#endif
            // .And.Contain(nestedScope)
            // .And.Contain(dictValues.Key)
            // .And.Contain(dictValues.Value.ToString())
            .And.Contain(DirectSubmissionLogLevelExtensions.Error);
        }
 public ILoggerDuckTypingTests()
 {
     _sink                = new NullDatadogSink();
     _formatter           = LogSettingsHelper.GetFormatter();
     _iloggerProviderType = LoggerFactoryIntegrationCommon <ILoggerProvider> .ProviderInterfaces;
 }
        public void CanSetProviderUsingHelper()
        {
            var factory        = new LoggerFactory();
            var loggerProvider = new DirectSubmissionLoggerProvider(new NullDatadogSink(), LogSettingsHelper.GetFormatter(), DirectSubmissionLogLevel.Debug);

            LoggerFactoryIntegrationCommon <LoggerFactory> .AddDirectSubmissionLoggerProvider(factory, loggerProvider);
        }
        public void CanDuckTypeExternalScopeProviderAndUseWithProxyProvider()
        {
            var scopeProvider  = new LoggerExternalScopeProvider();
            var loggerProvider = new DirectSubmissionLoggerProvider(new NullDatadogSink(), LogSettingsHelper.GetFormatter(), DirectSubmissionLogLevel.Debug);
            var proxyProvider  = (ISupportExternalScope)loggerProvider.DuckImplement(_iloggerProviderType);

            proxyProvider.SetScopeProvider(scopeProvider);

            var logger = loggerProvider.CreateLogger("Test logger name");

            using var scope = logger.BeginScope(123);
            scope.Should().NotBeNull();
            logger.IsEnabled(3).Should().BeTrue();
            logger.Log(logLevel: 3, 12, state: 123, null, (state, ex) => $"This is my message with a {state}");
        }