Skip to content

adding elastic search as provider #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# serilog-ui
A simple log viewer to see logs saved by [Serilog.Sinks.MSSqlServer](https://github.com/serilog/serilog-sinks-mssqlserver), [Serilog.Sinks.Postgresql](https://github.com/b00ted/serilog-sinks-postgresql) and [Serilog.Sinks.MongoDB](https://github.com/serilog/serilog-sinks-mongodb) (other sinks will be added in the future).
A simple log viewer to see logs saved by [Serilog.Sinks.MSSqlServer](https://github.com/serilog/serilog-sinks-mssqlserver), [Serilog.Sinks.Postgresql](https://github.com/b00ted/serilog-sinks-postgresql), [Serilog.Sinks.MongoDB](https://github.com/serilog/serilog-sinks-mongodb) and [Serilog.Sinks.ElasticSearch](https://github.com/serilog/serilog-sinks-elasticsearch) (other sinks will be added in the future).

![serilog ui](https://raw.githubusercontent.com/mo-esmp/serilog-ui/master/assets/serilog-ui.jpg)

Expand All @@ -26,6 +26,12 @@ of _Serilog.UI.MongoDbProvider_ [Nuget package](https://www.nuget.org/packages/S
Install-Package Serilog.UI.MongoDbProvider
```

of _Serilog.UI.ElasticSearchDbProvider_ [Nuget package](https://www.nuget.org/packages/Serilog.Ui.ElasticSearchDbProvider):

```powershell
Install-Package Serilog.UI.ElasticSearchDbProvider
```

Then, add `AddSerilogUi()` to `IServiceCollection` in `Startup.ConfigureServices` method:

```csharp
Expand All @@ -37,6 +43,8 @@ public void ConfigureServices(IServiceCollection services)
// services.AddSerilogUi(options => options.UseNpgSql("ConnectionString", "LogTableName"));
// or
// services.AddSerilogUi(options => options.UseMongoDb("ConnectionString", "DatabaseName", "CollectionName"))
// or
// services.AddSerilogUi(options => options.UseElasticSearchDb(endpoint: new System.Uri("http://localhost:9200"), indexName: "logging-index"))
}
```

Expand Down
7 changes: 7 additions & 0 deletions Serilog.Ui.sln
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{157C
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleWebApp", "samples\SampleWebApp\SampleWebApp.csproj", "{30C8AE36-8117-4E52-8140-8440D3C9AD39}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Ui.ElasticSearchProvider", "src\Serilog.Ui.ElasticSearchProvider\Serilog.Ui.ElasticSearchProvider.csproj", "{4CC537BE-8695-4976-BA43-B8484664C169}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -49,6 +51,10 @@ Global
{30C8AE36-8117-4E52-8140-8440D3C9AD39}.Debug|Any CPU.Build.0 = Debug|Any CPU
{30C8AE36-8117-4E52-8140-8440D3C9AD39}.Release|Any CPU.ActiveCfg = Release|Any CPU
{30C8AE36-8117-4E52-8140-8440D3C9AD39}.Release|Any CPU.Build.0 = Release|Any CPU
{4CC537BE-8695-4976-BA43-B8484664C169}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4CC537BE-8695-4976-BA43-B8484664C169}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4CC537BE-8695-4976-BA43-B8484664C169}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4CC537BE-8695-4976-BA43-B8484664C169}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -60,6 +66,7 @@ Global
{0004A882-82C9-40C3-9D6C-CD297D1A50E7} = {ACA69857-2E3E-468C-B0B0-A86852E3492D}
{A6195106-0818-4CE1-A889-BB734960DD8D} = {ACA69857-2E3E-468C-B0B0-A86852E3492D}
{30C8AE36-8117-4E52-8140-8440D3C9AD39} = {157CA77C-513A-409F-8045-E68739AAC8C8}
{4CC537BE-8695-4976-BA43-B8484664C169} = {ACA69857-2E3E-468C-B0B0-A86852E3492D}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {88374732-FEAD-4375-9CF1-75331A37CF07}
Expand Down
6 changes: 5 additions & 1 deletion samples/SampleWebApp/SampleWebApp.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
Expand All @@ -16,12 +16,16 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
<PackageReference Include="Serilog.Formatting.Elasticsearch" Version="8.4.1" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.Elasticsearch" Version="8.4.1" />
<PackageReference Include="Serilog.Sinks.MSSqlServer" Version="5.6.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Serilog.Ui.ElasticSearchProvider\Serilog.Ui.ElasticSearchProvider.csproj" />
<ProjectReference Include="..\..\src\Serilog.Ui.MongoDbProvider\Serilog.Ui.MongoDbProvider.csproj" />
<ProjectReference Include="..\..\src\Serilog.Ui.MsSqlServerProvider\Serilog.Ui.MsSqlServerProvider.csproj" />
<ProjectReference Include="..\..\src\Serilog.Ui.PostgreSqlProvider\Serilog.Ui.PostgreSqlProvider.csproj" />
Expand Down
1 change: 1 addition & 0 deletions samples/SampleWebApp/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Microsoft.IdentityModel.Tokens;
using SampleWebApp.Authentication.Jwt;
using SampleWebApp.Data;
using Serilog.Ui.ElasticSearchProvider.Extensions;
using Serilog.Ui.MsSqlServerProvider;
using Serilog.Ui.Web;

Expand Down
27 changes: 26 additions & 1 deletion samples/SampleWebApp/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

"Serilog": {
"Using": [ "Serilog.Sinks.MSSqlServer" ],
"MinimumLevel": "Warning",
"MinimumLevel": "Debug",
"WriteTo": [
{
"Name": "MSSqlServer",
Expand All @@ -25,6 +25,31 @@
"autoCreateSqlTable": true
}
}
},
{
"Name": "Console",
"Args": {
"theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console",
"outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} <s:{SourceContext}>{NewLine}{Exception}"
}
},
{
"Name": "Elasticsearch",
"Args": {
"nodeUris": "http://localhost:9200",
"indexFormat": "logging-index",
"batchPostingLimit": 50,
"batchAction": "Create",
"period": 2,
"inlineFields": false,
"restrictedToMinimumLevel": "Debug",
"bufferBaseFilename": "C:/Temp/docker-elk-serilog-web-buffer",
"connectionTimeout": 5,
"emitEventFailure": "WriteToSelfLog",
"queueSizeLimit": "100000",
"registerTemplateFailure": "IndexAnyway",
"deadLetterIndexName": "deadletter-{0:yyyy.MM}"
}
}
]
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using Nest;
using Serilog.Ui.Core;
using Serilog.Ui.MongoDbProvider;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Serilog.Ui.ElasticSearchProvider
{
public class ElasticSearchDbDataProvider : IDataProvider
{
private readonly IElasticClient _client;

private readonly ElasticSearchDbOptions _options;

public ElasticSearchDbDataProvider(IElasticClient client, ElasticSearchDbOptions options)
{
_client = client ?? throw new ArgumentNullException(nameof(client));
_options = options ?? throw new ArgumentNullException(nameof(options));
}

public Task<(IEnumerable<LogModel>, int)> FetchDataAsync(int page,
int count,
string level = null,
string searchCriteria = null)
{
return GetLogsAsync(page - 1, count, level, searchCriteria);
}

private async Task<(IEnumerable<LogModel>, int)> GetLogsAsync(
int page,
int count,
string level,
string searchCriteria,
CancellationToken cancellationToken = default)
{
var descriptor = new SearchDescriptor<ElasticSearchDbLogModel>()
.Index(_options.IndexName)
.Size(count)
.Skip(page * count);

if (!string.IsNullOrEmpty(level))
descriptor.Query(q => q
.Match(m => m
.Field(f => f.Level)
.Query(level))
);
if (!string.IsNullOrEmpty(searchCriteria))
descriptor.Query(q => q
.Match(m => m
.Field(f => f.Message)
.Query(searchCriteria)
) || q
.Match(m => m
.Field(f => f.Exceptions)
.Query(searchCriteria)
)
);
//descriptor = descriptor.Fields(f => f.Field(z => z.Message == searchCriteria));

var result = await _client.SearchAsync<ElasticSearchDbLogModel>(descriptor, cancellationToken);

int.TryParse(result?.Total.ToString(), out int total);

return (result?.Documents.Select((x, index) => x.ToLogModel(index)).ToList(), total);
}
}
}
44 changes: 44 additions & 0 deletions src/Serilog.Ui.ElasticSearchProvider/ElasticSearchDbLogModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Serilog.Ui.Core;
using System;
using System.Collections.Generic;

namespace Serilog.Ui.MongoDbProvider
{
public class ElasticSearchDbLogModel
{
[JsonProperty("level")]
public string Level { get; set; }

[JsonProperty("message")]
public string Message { get; set; }

[JsonProperty("@timestamp")]
public DateTime Timestamp { get; set; }

public JArray Exceptions { get; set; }

[JsonProperty("fields")]
public Dictionary<string, object> Fields { get; set; }

internal LogModel ToLogModel(int index)
{
return new LogModel
{
RowNo = index,
Level = Level,
Message = Message,
Timestamp = Timestamp,
Exception = Exceptions?.Count > 0 ? BuildExceptionMessage(Exceptions[0]) : string.Empty,
Properties = JsonConvert.SerializeObject(Fields),
PropertyType = "json"
};
}

static string BuildExceptionMessage(JToken jObjet)
{
return $"Exception: {jObjet.Value<string>("Message")}. StackTrace: {jObjet.Value<string>("StackTraceString")}.";
}
}
}
13 changes: 13 additions & 0 deletions src/Serilog.Ui.ElasticSearchProvider/ElasticSearchDbOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Serilog.Ui.ElasticSearchProvider
{
public class ElasticSearchDbOptions
{
public string IndexName { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using Elasticsearch.Net;
using Microsoft.Extensions.DependencyInjection;
using Nest;
using Newtonsoft.Json;
using Serilog.Ui.Core;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace Serilog.Ui.ElasticSearchProvider.Extensions
{
public static class SerilogUiOptionBuilderExtensions
{
public static void UseElasticSearchDb(this SerilogUiOptionsBuilder optionsBuilder,
Uri endpoint,
string indexName)
{
if (endpoint == null) throw new ArgumentNullException(nameof(endpoint));
if (string.IsNullOrEmpty(indexName)) throw new ArgumentNullException(nameof(indexName));

var options = new ElasticSearchDbOptions
{
IndexName = indexName
};

var builder = ((ISerilogUiOptionsBuilder)optionsBuilder);

builder.Services.AddSingleton(options);

var pool = new SingleNodeConnectionPool(endpoint);
var connectionSettings = new ConnectionSettings(pool, sourceSerializer: (builtin, values) => new VanillaSerializer());

builder.Services.AddSingleton<IElasticClient>(o => new ElasticClient(connectionSettings));
builder.Services.AddScoped<IDataProvider, ElasticSearchDbDataProvider>();
}
}

public class VanillaSerializer : IElasticsearchSerializer
{
public T Deserialize<T>(Stream stream)
=> (T)Deserialize(typeof(T), stream);


public object Deserialize(Type type, Stream stream)
{
var reader = new StreamReader(stream);
using (var jreader = new JsonTextReader(reader))
{
var serializer = new JsonSerializer();
return serializer.Deserialize(jreader, type);
}
}

public Task<T> DeserializeAsync<T>(Stream stream, CancellationToken cancellationToken = default(CancellationToken)) =>
Task.FromResult(Deserialize<T>(stream));

public Task<object> DeserializeAsync(Type type, Stream stream, CancellationToken cancellationToken = default(CancellationToken)) =>
Task.FromResult(Deserialize(type, stream));

public void Serialize<T>(T data, Stream stream, SerializationFormatting formatting = SerializationFormatting.Indented)
{
var writer = new StreamWriter(stream);
using (var jWriter = new JsonTextWriter(writer))
{
var serializer = new JsonSerializer
{
Formatting = formatting == SerializationFormatting.Indented ? Formatting.Indented : Formatting.None
};
serializer.Serialize(jWriter, data);
}
}


public Task SerializeAsync<T>(T data, Stream stream, SerializationFormatting formatting = SerializationFormatting.Indented,
CancellationToken cancellationToken = default(CancellationToken))
{
Serialize<T>(data, stream, formatting);
return Task.CompletedTask;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" />
<PackageReference Include="NEST" Version="7.11.1" />
<PackageReference Include="NEST.JsonNetSerializer" Version="7.11.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Serilog.Ui.Core\Serilog.Ui.Core.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata>
<id>Serilog.Ui.ElasticSearchProvider</id>
<version>$version$</version>
<title>Serilog.Ui.ElasticSearchProvider</title>
<authors>Mohsen Esmailpour - rmauro.dev</authors>
<owners>mo.esmp</owners>
<projectUrl>https://github.com/mo-esmp/serilog-ui</projectUrl>
<license type="expression">MIT</license>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>ElasticSearch data provider for Serilog UI.</description>
<releaseNotes></releaseNotes>
<copyright></copyright>
<tags>serilog serilog-ui serilog.sinks.elasticsearch</tags>
<dependencies>
<group targetFramework=".NETStandard2.0">
<dependency id="Microsoft.Extensions.DependencyInjection.Abstractions" version="3.1.3" exclude="Build,Analyzers" />
<dependency id="Microsoft.Extensions.Logging.Abstractions" version="5.0.0" exclude="Build,Analyzers" />
<dependency id="NEST" version="7.11.1" exclude="Build,Analyzers" />
<dependency id="NEST.JsonNetSerializer" version="7.11.1" exclude="Build,Analyzers" />
<dependency id="Newtonsoft.Json" version="12.0.3" exclude="Build,Analyzers" />
</group>
</dependencies>
</metadata>
<files>
<file src="bin\Release\netstandard2.0\*.dll" target="lib/netstandard2.0" />
</files>
</package>