public async Task ApplicationStatusTest()
        {
            var          utcnow        = DateTime.UtcNow.Date;
            var          mysqlLogStore = new MySqlLogStore(() => utcnow);
            const string appPath       = "###rather_not_existing_application_path###";
            var          hash          = BitConverter.ToString(MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(appPath))).Replace("-", String.Empty);
            var          startDate     = DateTime.UtcNow.AddMinutes(-1);

            var expectedAppStatus = new LastApplicationStatus {
                ApplicationPath   = appPath,
                Server            = "SRV1",
                LastUpdateTimeUtc = DateTime.UtcNow
            };
            await mysqlLogStore.UpdateApplicationStatusAsync(expectedAppStatus);

            var actualAppStatus = (await mysqlLogStore.GetApplicationStatusesAsync(startDate)).FirstOrDefault(
                st => string.Equals(st.ApplicationPath, expectedAppStatus.ApplicationPath, StringComparison.Ordinal));

            Assert.NotNull(actualAppStatus);
            Assert.Equal(expectedAppStatus.ApplicationPath, actualAppStatus.ApplicationPath);
            Assert.Equal(expectedAppStatus.Cpu, 0);
            Assert.Equal(expectedAppStatus.Memory, 0);
            Assert.Null(actualAppStatus.LastErrorTimeUtc);
            Assert.Null(actualAppStatus.LastPerformanceDataUpdateTimeUtc);

            expectedAppStatus.LastErrorType    = "TestException";
            expectedAppStatus.LastErrorTimeUtc = DateTime.UtcNow;
            await mysqlLogStore.UpdateApplicationStatusAsync(expectedAppStatus);

            actualAppStatus = (await mysqlLogStore.GetApplicationStatusesAsync(startDate)).FirstOrDefault(
                st => string.Equals(st.ApplicationPath, expectedAppStatus.ApplicationPath, StringComparison.Ordinal));
            Assert.NotNull(actualAppStatus);
            Assert.Equal(expectedAppStatus.ApplicationPath, actualAppStatus.ApplicationPath);
            Assert.Equal(expectedAppStatus.Cpu, 0);
            Assert.Equal(expectedAppStatus.Memory, 0);
            Assert.Equal(expectedAppStatus.LastErrorType, actualAppStatus.LastErrorType);
            Assert.NotNull(actualAppStatus.LastErrorTimeUtc);
            Assert.Null(actualAppStatus.LastPerformanceDataUpdateTimeUtc);

            expectedAppStatus.Cpu    = 10f;
            expectedAppStatus.Memory = 1000f;
            expectedAppStatus.LastPerformanceDataUpdateTimeUtc = DateTime.UtcNow;
            await mysqlLogStore.UpdateApplicationStatusAsync(expectedAppStatus);

            actualAppStatus = (await mysqlLogStore.GetApplicationStatusesAsync(startDate)).FirstOrDefault(
                st => string.Equals(st.ApplicationPath, expectedAppStatus.ApplicationPath, StringComparison.Ordinal));
            Assert.NotNull(actualAppStatus);
            Assert.Equal(expectedAppStatus.ApplicationPath, actualAppStatus.ApplicationPath);
            Assert.Equal(expectedAppStatus.LastErrorType, actualAppStatus.LastErrorType);
            Assert.NotNull(actualAppStatus.LastErrorTimeUtc);
            Assert.Equal(expectedAppStatus.Cpu, 10f);
            Assert.Equal(expectedAppStatus.Memory, 1000f);
            Assert.NotNull(actualAppStatus.LastPerformanceDataUpdateTimeUtc);
        }
        public async Task TestAddLogRecord()
        {
            var          utcnow        = DateTime.UtcNow.Date;
            var          mysqlLogStore = new MySqlLogStore(() => utcnow);
            const string appPath       = "###rather_not_existing_application_path###";
            var          hash          = BitConverter.ToString(MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(appPath))).Replace("-", String.Empty);

            var logrec = new LogRecord {
                LoggerName              = "TestLogger",
                ApplicationPath         = appPath,
                LogLevel                = LogRecord.ELogLevel.Error,
                TimeUtc                 = DateTime.UtcNow,
                ProcessId               = 123,
                ThreadId                = 456,
                Server                  = "TestServer",
                Identity                = "TestIdentity",
                CorrelationId           = Guid.NewGuid().ToString(),
                Message                 = "Test log message to store in the log",
                ExceptionMessage        = "Test exception log message",
                ExceptionType           = "TestException",
                ExceptionAdditionalInfo = "Additinal info for the test exception",
                AdditionalFields        = new Dictionary <String, Object>
                {
                    { "Host", "testhost.com" },
                    { "LoggedUser", "testloggeduser" },
                    { "HttpStatusCode", "200.1" },
                    { "Url", "http://testhost.com" },
                    { "Referer", "http://prevtesthost.com" },
                    { "ClientIP", null },
                    { "RequestData", "test test test" },
                    { "ResponseData", null },
                    { "ServiceName", "TestService" },
                    { "ServiceDisplayName", "Test service generating logs" },
                    { "NotExisting", null }
                },
                PerformanceData = new Dictionary <String, float>
                {
                    { "CPU", 2.0f },
                    { "Memory", 20000000f }
                }
            };

            // add log
            await mysqlLogStore.AddLogRecordAsync(logrec);

            using (var conn = new MySqlConnection(ConfigurationManager.ConnectionStrings["mysqlconn"].ConnectionString))
            {
                conn.Open();

                // check if app tables were created
                var tables = conn.Query <String>("select table_name from information_schema.tables where table_schema = @db", new { db = conn.Database }).ToArray();

                Assert.Contains(MySqlLogStore.AppLogTablePrefix + hash, tables, StringComparer.OrdinalIgnoreCase);

                // check partitions
                var expectedPartitionNames = new[] { String.Format("{0}{1:yyyyMMdd}", Partition.PartitionPrefix, utcnow.Date.AddDays(1)),
                                                     String.Format("{0}{1:yyyyMMdd}", Partition.PartitionPrefix, utcnow.Date.AddDays(2)) };
                foreach (var prefix in new[] { MySqlLogStore.AppLogTablePrefix })
                {
                    var partitions = conn.Query <String>("select partition_name From information_schema.partitions where table_name = @tableName and table_schema = @db order by partition_name",
                                                         new { tableName = prefix + hash, db = conn.Database });
                    Assert.Equal(expectedPartitionNames, partitions);
                }

                // check logs content
                var dbLogRecs = conn.Query <DbAppLogRecord>("select * from " + MySqlLogStore.AppLogTablePrefix + hash).ToArray();

                Assert.True(dbLogRecs.Length == 1);
                var dbLogRec = dbLogRecs[0];

                Assert.Equal(logrec.LoggerName, dbLogRec.LoggerName);
                Assert.Equal(logrec.ApplicationPath, dbLogRec.ApplicationPath);
                Assert.Equal(logrec.LogLevel, dbLogRec.LogLevel);
                Assert.Equal(logrec.TimeUtc.ToShortDateString(), dbLogRec.TimeUtc.ToShortDateString());
                Assert.Equal(logrec.ProcessId, dbLogRec.ProcessId);
                Assert.Equal(logrec.ThreadId, dbLogRec.ThreadId);
                Assert.Equal(logrec.Server, dbLogRec.Server);
                Assert.Equal(logrec.Identity, dbLogRec.Identity);
                Assert.Equal(logrec.CorrelationId, dbLogRec.CorrelationId);
                Assert.Equal(logrec.Message, dbLogRec.Message);
                Assert.Equal(logrec.ExceptionMessage, dbLogRec.ExceptionMessage);
                Assert.Equal(logrec.ExceptionType, dbLogRec.ExceptionType);
                Assert.Equal(logrec.ExceptionAdditionalInfo, dbLogRec.ExceptionAdditionalInfo);
                Assert.Equal((String)logrec.AdditionalFields["Host"], dbLogRec.Host);
                Assert.Equal((String)logrec.AdditionalFields["LoggedUser"], dbLogRec.LoggedUser);
                Assert.Equal((String)logrec.AdditionalFields["HttpStatusCode"], dbLogRec.HttpStatusCode);
                Assert.Equal((String)logrec.AdditionalFields["Url"], dbLogRec.Url);
                Assert.Equal((String)logrec.AdditionalFields["Referer"], dbLogRec.Referer);
                Assert.Equal((String)logrec.AdditionalFields["ClientIP"], dbLogRec.ClientIP);
                Assert.Equal((String)logrec.AdditionalFields["RequestData"], dbLogRec.RequestData);
                Assert.Equal((String)logrec.AdditionalFields["ResponseData"], dbLogRec.ResponseData);
                Assert.Equal((String)logrec.AdditionalFields["ServiceName"], dbLogRec.ServiceName);
                Assert.Equal((String)logrec.AdditionalFields["ServiceDisplayName"], dbLogRec.ServiceDisplayName);

                var dbPerfLogs = JsonConvert.DeserializeObject <IDictionary <String, float> >(dbLogRec.PerfData);
                Assert.True(dbPerfLogs.Count == 2);

                float r;
                Assert.True(dbPerfLogs.TryGetValue("CPU", out r));
                Assert.Equal(r, logrec.PerformanceData["CPU"]);

                Assert.True(dbPerfLogs.TryGetValue("Memory", out r));
                Assert.Equal(r, logrec.PerformanceData["Memory"]);
            }
        }
        public async Task MaintainPartitionsTest()
        {
            var          utcnow        = DateTime.UtcNow.Date;
            var          mysqlLogStore = new MySqlLogStore(() => utcnow);
            const string appPath       = "###rather_not_existing_application_path3###";
            var          hash          = BitConverter.ToString(MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(appPath))).Replace("-", String.Empty);


            using (var conn = new MySqlConnection(ConfigurationManager.ConnectionStrings["mysqlconn"].ConnectionString))
            {
                conn.Open();

                // timespan = 2 days - one partition to remove, one to create
                var partitionDefs = new StringBuilder();
                partitionDefs.Append(GetPartitionDefinition(utcnow.AddDays(-4))).Append(',');
                partitionDefs.Append(GetPartitionDefinition(utcnow.AddDays(-3))).Append(',');
                partitionDefs.Append(GetPartitionDefinition(utcnow.AddDays(-2))).Append(',');
                partitionDefs.Append(GetPartitionDefinition(utcnow.AddDays(-1))).Append(',');
                partitionDefs.Append(GetPartitionDefinition(utcnow));

                conn.Execute(String.Format("create table {0} (Id int unsigned auto_increment not null, TimeUtc datetime not null, " +
                                           "primary key (Id, TimeUtc)) partition by range columns (TimeUtc) ({1})", MySqlLogStore.AppLogTablePrefix + hash,
                                           partitionDefs));

                await mysqlLogStore.MaintainAsync(TimeSpan.FromDays(0));

                // no partitions should be removed
                foreach (var tbl in new[] { MySqlLogStore.AppLogTablePrefix })
                {
                    var partitions = conn.Query <String>("select partition_name from information_schema.partitions where table_name = @Table and table_schema = @Database",
                                                         new { conn.Database, Table = tbl + hash });

                    Assert.Contains(Partition.ForDay(utcnow.AddDays(-4)).Name, partitions, StringComparer.OrdinalIgnoreCase);
                    Assert.Contains(Partition.ForDay(utcnow.AddDays(-3)).Name, partitions, StringComparer.OrdinalIgnoreCase);
                    Assert.Contains(Partition.ForDay(utcnow.AddDays(-2)).Name, partitions, StringComparer.OrdinalIgnoreCase);
                    Assert.Contains(Partition.ForDay(utcnow.AddDays(-1)).Name, partitions, StringComparer.OrdinalIgnoreCase);
                    Assert.Contains(Partition.ForDay(utcnow).Name, partitions, StringComparer.OrdinalIgnoreCase);
                    Assert.Contains(Partition.ForDay(utcnow.AddDays(1)).Name, partitions, StringComparer.OrdinalIgnoreCase);
                }

                await mysqlLogStore.MaintainAsync(TimeSpan.FromDays(3));

                foreach (var tbl in new[] { MySqlLogStore.AppLogTablePrefix })
                {
                    var partitions = conn.Query <String>("select partition_name from information_schema.partitions where table_name = @Table and table_schema = @Database",
                                                         new { conn.Database, Table = tbl + hash });

                    Assert.DoesNotContain(Partition.ForDay(utcnow.AddDays(-4)).Name, partitions, StringComparer.OrdinalIgnoreCase);
                    Assert.Contains(Partition.ForDay(utcnow.AddDays(-3)).Name, partitions, StringComparer.OrdinalIgnoreCase);
                    Assert.Contains(Partition.ForDay(utcnow.AddDays(-2)).Name, partitions, StringComparer.OrdinalIgnoreCase);
                    Assert.Contains(Partition.ForDay(utcnow.AddDays(-1)).Name, partitions, StringComparer.OrdinalIgnoreCase);
                    Assert.Contains(Partition.ForDay(utcnow).Name, partitions, StringComparer.OrdinalIgnoreCase);
                    Assert.Contains(Partition.ForDay(utcnow.AddDays(1)).Name, partitions, StringComparer.OrdinalIgnoreCase);
                }

                // drop current and future partition and check if it will be recreated
                conn.Execute(String.Format("alter table {0} drop partition {1}", MySqlLogStore.AppLogTablePrefix + hash, Partition.ForDay(utcnow.AddDays(1)).Name));
                conn.Execute(String.Format("alter table {0} drop partition {1}", MySqlLogStore.AppLogTablePrefix + hash, Partition.ForDay(utcnow).Name));
                await mysqlLogStore.MaintainAsync(TimeSpan.FromDays(3));

                foreach (var tbl in new[] { MySqlLogStore.AppLogTablePrefix })
                {
                    var partitions = conn.Query <String>("select partition_name from information_schema.partitions where table_name = @Table and table_schema = @Database",
                                                         new { conn.Database, Table = tbl + hash });

                    Assert.Contains(Partition.ForDay(utcnow).Name, partitions, StringComparer.OrdinalIgnoreCase);
                    Assert.Contains(Partition.ForDay(utcnow.AddDays(1)).Name, partitions, StringComparer.OrdinalIgnoreCase);
                }

                // check if configuration per table is working
                mysqlLogStore.MaintainAsync(TimeSpan.FromDays(3), new Dictionary <String, TimeSpan> {
                    { appPath, TimeSpan.FromDays(2) }
                }).Wait();
                foreach (var tbl in new[] { MySqlLogStore.AppLogTablePrefix })
                {
                    var partitions = conn.Query <String>("select partition_name from information_schema.partitions where table_name = @Table and table_schema = @Database",
                                                         new { conn.Database, Table = tbl + hash });

                    Assert.DoesNotContain(Partition.ForDay(utcnow.AddDays(-3)).Name, partitions, StringComparer.OrdinalIgnoreCase);
                }

                await Assert.ThrowsAsync <ArgumentException>(async() => await mysqlLogStore.MaintainAsync(TimeSpan.FromDays(-2)));

                await Assert.ThrowsAsync <ArgumentException>(async() => await mysqlLogStore.MaintainAsync(TimeSpan.FromDays(2),
                                                                                                          new Dictionary <String, TimeSpan> {
                    { appPath, TimeSpan.FromDays(-1) }
                }));
            }
        }
        public async Task LogFilteringTest()
        {
            // add a test log record
            var          utcnow        = DateTime.UtcNow.Date;
            var          mysqlLogStore = new MySqlLogStore(() => utcnow);
            const string appPath       = "###rather_not_existing_application_path2###";

            var logrec = new LogRecord {
                LoggerName              = "TestLogger",
                ApplicationPath         = appPath,
                LogLevel                = LogRecord.ELogLevel.Error,
                TimeUtc                 = DateTime.UtcNow,
                ProcessId               = 123,
                ThreadId                = 456,
                Server                  = "TestServer",
                Identity                = "TestIdentity",
                CorrelationId           = Guid.NewGuid().ToString(),
                Message                 = "Test log message to store in the log",
                ExceptionMessage        = "Test exception log message",
                ExceptionType           = "TestException",
                ExceptionAdditionalInfo = "Additinal info for the test exception",
                AdditionalFields        = new Dictionary <String, Object>
                {
                    { "Host", "testhost.com" },
                    { "LoggedUser", "testloggeduser" },
                    { "HttpStatusCode", "200.1" },
                    { "Url", "http://testhost.com" },
                    { "Referer", "http://prevtesthost.com" },
                    { "ClientIP", null },
                    { "RequestData", "test test test" },
                    { "ResponseData", null },
                    { "ServiceName", "TestService" },
                    { "ServiceDisplayName", "Test service generating logs" },
                    { "NotExisting", null }
                },
                PerformanceData = new Dictionary <String, float>
                {
                    { "CPU", 2.0f },
                    { "Memory", 20000000f }
                }
            };

            // add log
            await mysqlLogStore.AddLogRecordAsync(logrec);

            var searchResults = await mysqlLogStore.FilterLogsAsync(new LogSearchCriteria {
                FromUtc         = DateTime.UtcNow.AddMinutes(-10),
                ToUtc           = DateTime.UtcNow.AddMinutes(10),
                ApplicationPath = appPath,
                Levels          = new[] { LogRecord.ELogLevel.Error, LogRecord.ELogLevel.Info },
                Limit           = 10,
                Offset          = 0,
                Server          = "TestServer",
                Keywords        = new KeywordsParsed()
                {
                    Url = "http://testhost.com"
                }
            });

            Assert.NotNull(searchResults.FoundItems);
            var foundItems = searchResults.FoundItems.ToArray();

            Assert.True(foundItems.Length == 1);
            var logrec2 = foundItems[0];

            Assert.Equal(logrec.LoggerName, logrec2.LoggerName);
            Assert.Equal(logrec.ApplicationPath, logrec2.ApplicationPath);
            Assert.Equal(logrec.LogLevel, logrec2.LogLevel);
            Assert.Equal(logrec.TimeUtc.ToShortDateString(), logrec2.TimeUtc.ToShortDateString());
            Assert.Equal(logrec.ProcessId, logrec2.ProcessId);
            Assert.Equal(logrec.ThreadId, logrec2.ThreadId);
            Assert.Equal(logrec.Server, logrec2.Server);
            Assert.Equal(logrec.Identity, logrec2.Identity);
            Assert.Equal(logrec.CorrelationId, logrec2.CorrelationId);
            Assert.Equal(logrec.Message, logrec2.Message);
            Assert.Equal(logrec.ExceptionMessage, logrec2.ExceptionMessage);
            Assert.Equal(logrec.ExceptionType, logrec2.ExceptionType);
            Assert.Equal(logrec.ExceptionAdditionalInfo, logrec2.ExceptionAdditionalInfo);
            Assert.Equal(logrec.AdditionalFields["Host"], logrec2.AdditionalFields["Host"]);
            Assert.Equal(logrec.AdditionalFields["LoggedUser"], logrec2.AdditionalFields["LoggedUser"]);
            Assert.Equal(logrec.AdditionalFields["HttpStatusCode"], logrec2.AdditionalFields["HttpStatusCode"]);
            Assert.Equal(logrec.AdditionalFields["Url"], logrec2.AdditionalFields["Url"]);
            Assert.Equal(logrec.AdditionalFields["Referer"], logrec2.AdditionalFields["Referer"]);
            Assert.Equal(logrec.AdditionalFields["RequestData"], logrec2.AdditionalFields["RequestData"]);
            Assert.Equal(logrec.AdditionalFields["ServiceName"], logrec2.AdditionalFields["ServiceName"]);
            Assert.Equal(logrec.AdditionalFields["ServiceDisplayName"], logrec2.AdditionalFields["ServiceDisplayName"]);
        }