Skip to content

Commit

Permalink
Merge pull request #142 from IzyPro/v1.4.11
Browse files Browse the repository at this point in the history
V1.4.11
  • Loading branch information
Khelechy authored Mar 31, 2024
2 parents 4b42d1a + 5af6db1 commit 69523ce
Show file tree
Hide file tree
Showing 20 changed files with 335 additions and 34 deletions.
29 changes: 17 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ It leverages `SignalR` for real-time monitoring and `LiteDb` a Serverless MongoD

## What's New

- Security Optimization
- Query Filters Fixes and Optimizations
- Package Assembly as DB Name Fix(MongoDB)
- Official support for .NET8
- Support for .NET8 UseOutputCache
- Blacklisting using Regex
- Bug Fixes

### Breaking Changes

- SqlDriverOption is now DbDriverOption (>= 1.4.0)
- SqlDriverOption is now DbDriverOption (>= v1.4.0)


## Support
Expand All @@ -43,12 +44,12 @@ It leverages `SignalR` for real-time monitoring and `LiteDb` a Serverless MongoD
Install via .NET CLI

```bash
dotnet add package WatchDog.NET --version 1.4.10
dotnet add package WatchDog.NET --version 1.4.11
```
Install via Package Manager

```bash
Install-Package WatchDog.NET --version 1.4.10
Install-Package WatchDog.NET --version 1.4.11
```


Expand Down Expand Up @@ -93,9 +94,9 @@ Add Database Connection String and Choose DbDriver Option
```c#
services.AddWatchDogServices(opt =>
{
opt.IsAutoClear = false;
opt.IsAutoClear = true;
opt.SetExternalDbConnString = "Server=localhost;Database=testDb;User Id=postgres;Password=root;";
opt.DbDriverOption = WatchDogSqlDriverEnum.PostgreSql;
opt.DbDriverOption = WatchDogDbDriverEnum.PostgreSql;
});
```

Expand Down Expand Up @@ -127,8 +128,10 @@ app.UseWatchDog(opt =>

#### Optional Configurations: `Optional`
- Blacklist: List of routes, paths or endpoints to be ignored (should be a comma separated string like below).
- Serializer: If not default, specify the type of global json serializer/converter used
- CorsPolicy: Policy Name if project uses CORS
- Serializer: If not default, specify the type of global json serializer/converter used.
- CorsPolicy: Policy Name if project uses CORS.
- UseOutputCache: If your application uses [ASP.NET Output Cache](https://learn.microsoft.com/en-us/aspnet/core/performance/caching/output). This feature is only available for .NET8 and above.
- UseRegexForBlacklisting: Enables the use of Regex to blacklist request routes, paths or endpoints.

```c#
app.UseWatchDog(opt =>
Expand All @@ -138,7 +141,9 @@ app.UseWatchDog(opt =>
//Optional
opt.Blacklist = "Test/testPost, api/auth/login"; //Prevent logging for specified endpoints
opt.Serializer = WatchDogSerializerEnum.Newtonsoft; //If your project use a global json converter
opt.CorsPolicy = "MyCorsPolicy"
opt.CorsPolicy = "MyCorsPolicy";
opt.UseOutputCache = true;
opt.UseRegexForBlacklisting = true;
});
```

Expand Down Expand Up @@ -210,5 +215,5 @@ Kelechi Onyekwere - [Github](https://github.com/Khelechy) [Twitter](https://twi
Israel Ulelu - [Github](https://github.com/IzyPro) [Twitter](https://twitter.com/IzyPro_)



<!--
### [Official Documentation](https://watchdog-3.gitbook.io/watchdog)
8 changes: 7 additions & 1 deletion WatchDog.sln
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WatchDogCompleteTestAPI", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WatchDogCompleteApiNet6", "WatchDogCompleteApiNet6\WatchDogCompleteApiNet6.csproj", "{B0EA2031-4C36-442F-BC82-618AB0A5DA16}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchDogCompleteMVCTest", "WatchDogCompleteMVCTest\WatchDogCompleteMVCTest.csproj", "{0DCCA957-8A36-405D-8092-233A3759732D}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WatchDogCompleteMVCTest", "WatchDogCompleteMVCTest\WatchDogCompleteMVCTest.csproj", "{0DCCA957-8A36-405D-8092-233A3759732D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WatchDogCompleteTestApiNet8", "WatchDogCompleteTestApiNet8\WatchDogCompleteTestApiNet8.csproj", "{BE9CC5C0-2F4F-4E8C-ADF1-5B2FE83AE13B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand All @@ -33,6 +35,10 @@ Global
{0DCCA957-8A36-405D-8092-233A3759732D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0DCCA957-8A36-405D-8092-233A3759732D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0DCCA957-8A36-405D-8092-233A3759732D}.Release|Any CPU.Build.0 = Release|Any CPU
{BE9CC5C0-2F4F-4E8C-ADF1-5B2FE83AE13B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BE9CC5C0-2F4F-4E8C-ADF1-5B2FE83AE13B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BE9CC5C0-2F4F-4E8C-ADF1-5B2FE83AE13B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BE9CC5C0-2F4F-4E8C-ADF1-5B2FE83AE13B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
15 changes: 10 additions & 5 deletions WatchDog/WatchDog.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netcoreapp3.1;net6.0</TargetFrameworks>
<TargetFrameworks>netcoreapp3.1;net6.0;net8.0</TargetFrameworks>
<Description>WatchDog is a Realtime Message, Event, HTTP (Request &amp; Response) and Exception logger and viewer for ASP.Net Core Web Apps and APIs. It allows developers log and view messages, events, http requests made to their web application and also exception caught during runtime in their web applications, all in Realtime. It leverages SignalR for real-time monitoring and LiteDb a Serverless MongoDB-like database with no configuration with the option of using your external databases.</Description>
<PackageProjectUrl>https://github.com/IzyPro/WatchDog</PackageProjectUrl>
<PackageIcon>favicon.png</PackageIcon>
Expand All @@ -12,10 +12,13 @@
<Authors>Israel Ulelu, Kelechi Onyekwere</Authors>
<PackageId>WatchDog.NET</PackageId>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<Version>1.4.10</Version>
<PackageReleaseNotes>- Security Optimization
- Query Filters Fixes and Optimizations
- Package Assembly as DB Name Fix(MongoDB)</PackageReleaseNotes>
<Version>1.4.11</Version>
<PackageReleaseNotes>
- Official support for .NET8
- Support for .NET8 UseOutputCache
- Blacklisting using Regex
- Bug Fixes
</PackageReleaseNotes>
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
<IncludeSymbols>True</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
Expand Down Expand Up @@ -53,13 +56,15 @@
<PackageReference Include="Microsoft.Extensions.FileProviders.Abstractions" Version="3.1.22" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.22" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Physical" Version="3.1.22" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" Condition="'$(TargetFramework)' == 'net8.0'" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.1.22" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.2.0" />
<PackageReference Include="MongoDB.Driver" Version="2.18.0" />
<PackageReference Include="MySql.Data" Version="8.0.29" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Npgsql" Version="[5.0.0,5.0.10]" Condition="'$(TargetFramework)' == 'netcoreapp3.1'" />
<PackageReference Include="Npgsql" Version="6.0.4" Condition="'$(TargetFramework)' == 'net6.0'" />
<PackageReference Include="Npgsql" Version="8.0.2" Condition="'$(TargetFramework)' == 'net8.0'" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.5" />
</ItemGroup>

Expand Down
6 changes: 5 additions & 1 deletion WatchDog/WatchDogExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ public static IApplicationBuilder UseWatchDog(this IApplicationBuilder app, Acti
if (!string.IsNullOrEmpty(options.CorsPolicy))
app.UseCors(options.CorsPolicy);

#if NET8_0_OR_GREATER
if (options.UseOutputCache)
app.UseOutputCache();
#endif

return app.UseEndpoints(endpoints =>
{
endpoints.MapHub<LoggerHub>("/wtchdlogger");
Expand All @@ -140,7 +145,6 @@ public static IApplicationBuilder UseWatchDog(this IApplicationBuilder app, Acti
public static IFileInfo GetFile()
{
return Provider.GetFileInfo("src.WatchPage.index.html");

}

public static string GetFolder()
Expand Down
2 changes: 1 addition & 1 deletion WatchDog/src/Helpers/PaginatedEnumerable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public static Page<T> ToPaginatedList<T>(this IFindFluent<T, T> source, int page
public static Page<T> ToPaginatedList<T>(this ILiteQueryable<T> source, int pageIndex, int pageSize = Constants.PageSize)
{
var count = source.LongCount();
var items = source.Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToEnumerable();
var items = source.Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToList();
return new Page<T>(items, count, pageIndex, pageSize);
}
}
Expand Down
16 changes: 10 additions & 6 deletions WatchDog/src/Helpers/SQLDbHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ internal static class SQLDbHelper
// WATCHLOG OPERATIONS
public static async Task<Page<WatchLog>> GetAllWatchLogs(string searchString, string verbString, string statusCode, int pageNumber)
{
searchString = searchString?.ToLower();
verbString = verbString?.ToLower();

var query = @$"SELECT * FROM {Constants.WatchLogTableName} ";

if (!string.IsNullOrEmpty(searchString) || !string.IsNullOrEmpty(verbString) || !string.IsNullOrEmpty(statusCode))
Expand All @@ -20,14 +23,14 @@ public static async Task<Page<WatchLog>> GetAllWatchLogs(string searchString, st
if (!string.IsNullOrEmpty(searchString))
{
if(GeneralHelper.IsPostgres())
query += $"({nameof(WatchLog.Path)} LIKE '%{searchString}%' OR {nameof(WatchLog.Method)} LIKE '%{searchString}%' OR {nameof(WatchLog.ResponseStatus)}::text LIKE '%{searchString}%' OR {nameof(WatchLog.QueryString)} LIKE '%{searchString}%')" + (string.IsNullOrEmpty(statusCode) && string.IsNullOrEmpty(verbString) ? "" : " AND ");
query += $"(LOWER( {nameof(WatchLog.Path)} ) LIKE '%{searchString}%' OR LOWER( {nameof(WatchLog.Method)} ) LIKE '%{searchString}%' OR {nameof(WatchLog.ResponseStatus)}::text LIKE '%{searchString}%' OR LOWER( {nameof(WatchLog.QueryString)} ) LIKE '%{searchString}%')" + (string.IsNullOrEmpty(statusCode) && string.IsNullOrEmpty(verbString) ? "" : " AND ");
else
query += $"({nameof(WatchLog.Path)} LIKE '%{searchString}%' OR {nameof(WatchLog.Method)} LIKE '%{searchString}%' OR {nameof(WatchLog.ResponseStatus)} LIKE '%{searchString}%' OR {nameof(WatchLog.QueryString)} LIKE '%{searchString}%')" + (string.IsNullOrEmpty(statusCode) && string.IsNullOrEmpty(verbString) ? "" : " AND ");
query += $"(LOWER( {nameof(WatchLog.Path)} ) LIKE '%{searchString}%' OR LOWER( {nameof(WatchLog.Method)} ) LIKE '%{searchString}%' OR {nameof(WatchLog.ResponseStatus)} LIKE '%{searchString}%' OR LOWER( {nameof(WatchLog.QueryString)} ) LIKE '%{searchString}%')" + (string.IsNullOrEmpty(statusCode) && string.IsNullOrEmpty(verbString) ? "" : " AND ");
}

if (!string.IsNullOrEmpty(verbString))
{
query += $"{nameof(WatchLog.Method)} LIKE '%{verbString}%' " + (string.IsNullOrEmpty(statusCode) ? "" : "AND ");
query += $"LOWER( {nameof(WatchLog.Method)} ) LIKE '%{verbString}%' " + (string.IsNullOrEmpty(statusCode) ? "" : "AND ");
}

if (!string.IsNullOrEmpty(statusCode))
Expand Down Expand Up @@ -91,7 +94,7 @@ public static async Task<Page<WatchExceptionLog>> GetAllWatchExceptionLogs(strin
if (!string.IsNullOrEmpty(searchString))
{
searchString = searchString.ToLower();
query += $"WHERE {nameof(WatchExceptionLog.Source)} LIKE '%{searchString}%' OR {nameof(WatchExceptionLog.Message)} LIKE '%{searchString}%' OR {nameof(WatchExceptionLog.StackTrace)} LIKE '%{searchString}%' ";
query += $"WHERE LOWER ( {nameof(WatchExceptionLog.Source)} ) LIKE '%{searchString}%' OR LOWER ( {nameof(WatchExceptionLog.Message)} ) LIKE '%{searchString}%' OR LOWER( {nameof(WatchExceptionLog.StackTrace)} ) LIKE '%{searchString}%' ";
}
query += $"ORDER BY {nameof(WatchExceptionLog.Id)} DESC";
using (var connection = ExternalDbContext.CreateSQLConnection())
Expand Down Expand Up @@ -142,12 +145,13 @@ public static async Task<Page<WatchLoggerModel>> GetAllLogs(string searchString,
if (!string.IsNullOrEmpty(searchString))
{
searchString = searchString.ToLower();
query += $"{nameof(WatchLoggerModel.CallingFrom)} LIKE '%{searchString}%' OR {nameof(WatchLoggerModel.CallingMethod)} LIKE '%{searchString}%' OR {nameof(WatchLoggerModel.Message)} LIKE '%{searchString}%' OR {nameof(WatchLoggerModel.EventId)} LIKE '%{searchString}%' " + (string.IsNullOrEmpty(logLevelString) ? "" : "AND ");
query += $"LOWER( {nameof(WatchLoggerModel.CallingFrom)} ) LIKE '%{searchString}%' OR LOWER( {nameof(WatchLoggerModel.CallingMethod)} ) LIKE '%{searchString}%' OR LOWER ( {nameof(WatchLoggerModel.Message)} ) LIKE '%{searchString}%' OR {nameof(WatchLoggerModel.EventId)} LIKE '%{searchString}%' " + (string.IsNullOrEmpty(logLevelString) ? "" : "AND ");
}

if (!string.IsNullOrEmpty(logLevelString))
{
query += $"{nameof(WatchLoggerModel.LogLevel)} LIKE '%{logLevelString}%' ";
logLevelString = logLevelString?.ToLower();
query += $"LOWER( {nameof(WatchLoggerModel.LogLevel)} ) LIKE '%{logLevelString}%' ";
}
query += $"ORDER BY {nameof(WatchLoggerModel.Id)} DESC";

Expand Down
2 changes: 2 additions & 0 deletions WatchDog/src/Models/WatchDogOptionsModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ public class WatchDogOptionsModel
public string WatchPagePassword { get; set; }
public string Blacklist { get; set; }
public string CorsPolicy { get; set; } = string.Empty;
public bool UseOutputCache { get; set; }
public bool UseRegexForBlacklisting { get; set; }
public WatchDogSerializerEnum Serializer { get; set; } = WatchDogSerializerEnum.Default;
}
}
23 changes: 21 additions & 2 deletions WatchDog/src/WatchDog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using WatchDog.src.Enums;
using WatchDog.src.Helpers;
Expand Down Expand Up @@ -36,13 +37,17 @@ public WatchDog(WatchDogOptionsModel options, RequestDelegate next, IBroadcastHe

public async Task InvokeAsync(HttpContext context)
{
var requestPath = context.Request.Path.ToString().Remove(0,1);
var requestPath = context.Request.Path.ToString();

if(requestPath.StartsWith('/'))
requestPath = requestPath.Remove(0,1);

if (!requestPath.Contains("WTCHDwatchpage") &&
!requestPath.Contains("watchdog") &&
!requestPath.Contains("WTCHDGstatics") &&
!requestPath.Contains("favicon") &&
!requestPath.Contains("wtchdlogger") &&
!WatchDogConfigModel.Blacklist.Contains(requestPath, StringComparer.OrdinalIgnoreCase))
!ShouldBlacklist(requestPath))
{
//Request handling comes here
var requestLog = await LogRequest(context);
Expand Down Expand Up @@ -145,5 +150,19 @@ private async Task<ResponseModel> LogResponse(HttpContext context)
}
}
}

private bool ShouldBlacklist(string requestPath)
{
if (_options.UseRegexForBlacklisting)
{
for (int i = 0; i < WatchDogConfigModel.Blacklist.Length; i++)
{
if (Regex.IsMatch(requestPath, WatchDogConfigModel.Blacklist[i], RegexOptions.IgnoreCase))
return true;
}
return false;
}
return WatchDogConfigModel.Blacklist.Contains(requestPath, StringComparer.OrdinalIgnoreCase);
}
}
}
Loading

0 comments on commit 69523ce

Please sign in to comment.