Skip to content

Commit 50750cf

Browse files
authored
Merge pull request #6 from ricardodemauro/master
adding elastic search as provider
2 parents 22ff130 + 50f6cfa commit 50750cf

11 files changed

+304
-3
lines changed

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# serilog-ui
2-
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).
2+
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).
33

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

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

29+
of _Serilog.UI.ElasticSearchDbProvider_ [Nuget package](https://www.nuget.org/packages/Serilog.Ui.ElasticSearchDbProvider):
30+
31+
```powershell
32+
Install-Package Serilog.UI.ElasticSearchDbProvider
33+
```
34+
2935
Then, add `AddSerilogUi()` to `IServiceCollection` in `Startup.ConfigureServices` method:
3036

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

Serilog.Ui.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{157C
1919
EndProject
2020
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleWebApp", "samples\SampleWebApp\SampleWebApp.csproj", "{30C8AE36-8117-4E52-8140-8440D3C9AD39}"
2121
EndProject
22+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Ui.ElasticSearchProvider", "src\Serilog.Ui.ElasticSearchProvider\Serilog.Ui.ElasticSearchProvider.csproj", "{4CC537BE-8695-4976-BA43-B8484664C169}"
23+
EndProject
2224
Global
2325
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2426
Debug|Any CPU = Debug|Any CPU
@@ -49,6 +51,10 @@ Global
4951
{30C8AE36-8117-4E52-8140-8440D3C9AD39}.Debug|Any CPU.Build.0 = Debug|Any CPU
5052
{30C8AE36-8117-4E52-8140-8440D3C9AD39}.Release|Any CPU.ActiveCfg = Release|Any CPU
5153
{30C8AE36-8117-4E52-8140-8440D3C9AD39}.Release|Any CPU.Build.0 = Release|Any CPU
54+
{4CC537BE-8695-4976-BA43-B8484664C169}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
55+
{4CC537BE-8695-4976-BA43-B8484664C169}.Debug|Any CPU.Build.0 = Debug|Any CPU
56+
{4CC537BE-8695-4976-BA43-B8484664C169}.Release|Any CPU.ActiveCfg = Release|Any CPU
57+
{4CC537BE-8695-4976-BA43-B8484664C169}.Release|Any CPU.Build.0 = Release|Any CPU
5258
EndGlobalSection
5359
GlobalSection(SolutionProperties) = preSolution
5460
HideSolutionNode = FALSE
@@ -60,6 +66,7 @@ Global
6066
{0004A882-82C9-40C3-9D6C-CD297D1A50E7} = {ACA69857-2E3E-468C-B0B0-A86852E3492D}
6167
{A6195106-0818-4CE1-A889-BB734960DD8D} = {ACA69857-2E3E-468C-B0B0-A86852E3492D}
6268
{30C8AE36-8117-4E52-8140-8440D3C9AD39} = {157CA77C-513A-409F-8045-E68739AAC8C8}
69+
{4CC537BE-8695-4976-BA43-B8484664C169} = {ACA69857-2E3E-468C-B0B0-A86852E3492D}
6370
EndGlobalSection
6471
GlobalSection(ExtensibilityGlobals) = postSolution
6572
SolutionGuid = {88374732-FEAD-4375-9CF1-75331A37CF07}

samples/SampleWebApp/SampleWebApp.csproj

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk.Web">
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
22

33
<PropertyGroup>
44
<TargetFramework>net5.0</TargetFramework>
@@ -16,12 +16,16 @@
1616
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1717
</PackageReference>
1818
<PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
19+
<PackageReference Include="Serilog.Formatting.Elasticsearch" Version="8.4.1" />
1920
<PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" />
21+
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
22+
<PackageReference Include="Serilog.Sinks.Elasticsearch" Version="8.4.1" />
2023
<PackageReference Include="Serilog.Sinks.MSSqlServer" Version="5.6.0" />
2124
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
2225
</ItemGroup>
2326

2427
<ItemGroup>
28+
<ProjectReference Include="..\..\src\Serilog.Ui.ElasticSearchProvider\Serilog.Ui.ElasticSearchProvider.csproj" />
2529
<ProjectReference Include="..\..\src\Serilog.Ui.MongoDbProvider\Serilog.Ui.MongoDbProvider.csproj" />
2630
<ProjectReference Include="..\..\src\Serilog.Ui.MsSqlServerProvider\Serilog.Ui.MsSqlServerProvider.csproj" />
2731
<ProjectReference Include="..\..\src\Serilog.Ui.PostgreSqlProvider\Serilog.Ui.PostgreSqlProvider.csproj" />

samples/SampleWebApp/Startup.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using Microsoft.IdentityModel.Tokens;
1010
using SampleWebApp.Authentication.Jwt;
1111
using SampleWebApp.Data;
12+
using Serilog.Ui.ElasticSearchProvider.Extensions;
1213
using Serilog.Ui.MsSqlServerProvider;
1314
using Serilog.Ui.Web;
1415

samples/SampleWebApp/appsettings.json

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
"Serilog": {
1616
"Using": [ "Serilog.Sinks.MSSqlServer" ],
17-
"MinimumLevel": "Warning",
17+
"MinimumLevel": "Debug",
1818
"WriteTo": [
1919
{
2020
"Name": "MSSqlServer",
@@ -25,6 +25,31 @@
2525
"autoCreateSqlTable": true
2626
}
2727
}
28+
},
29+
{
30+
"Name": "Console",
31+
"Args": {
32+
"theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console",
33+
"outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} <s:{SourceContext}>{NewLine}{Exception}"
34+
}
35+
},
36+
{
37+
"Name": "Elasticsearch",
38+
"Args": {
39+
"nodeUris": "http://localhost:9200",
40+
"indexFormat": "logging-index",
41+
"batchPostingLimit": 50,
42+
"batchAction": "Create",
43+
"period": 2,
44+
"inlineFields": false,
45+
"restrictedToMinimumLevel": "Debug",
46+
"bufferBaseFilename": "C:/Temp/docker-elk-serilog-web-buffer",
47+
"connectionTimeout": 5,
48+
"emitEventFailure": "WriteToSelfLog",
49+
"queueSizeLimit": "100000",
50+
"registerTemplateFailure": "IndexAnyway",
51+
"deadLetterIndexName": "deadletter-{0:yyyy.MM}"
52+
}
2853
}
2954
]
3055
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using Nest;
2+
using Serilog.Ui.Core;
3+
using Serilog.Ui.MongoDbProvider;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Threading;
8+
using System.Threading.Tasks;
9+
10+
namespace Serilog.Ui.ElasticSearchProvider
11+
{
12+
public class ElasticSearchDbDataProvider : IDataProvider
13+
{
14+
private readonly IElasticClient _client;
15+
16+
private readonly ElasticSearchDbOptions _options;
17+
18+
public ElasticSearchDbDataProvider(IElasticClient client, ElasticSearchDbOptions options)
19+
{
20+
_client = client ?? throw new ArgumentNullException(nameof(client));
21+
_options = options ?? throw new ArgumentNullException(nameof(options));
22+
}
23+
24+
public Task<(IEnumerable<LogModel>, int)> FetchDataAsync(int page,
25+
int count,
26+
string level = null,
27+
string searchCriteria = null)
28+
{
29+
return GetLogsAsync(page - 1, count, level, searchCriteria);
30+
}
31+
32+
private async Task<(IEnumerable<LogModel>, int)> GetLogsAsync(
33+
int page,
34+
int count,
35+
string level,
36+
string searchCriteria,
37+
CancellationToken cancellationToken = default)
38+
{
39+
var descriptor = new SearchDescriptor<ElasticSearchDbLogModel>()
40+
.Index(_options.IndexName)
41+
.Size(count)
42+
.Skip(page * count);
43+
44+
if (!string.IsNullOrEmpty(level))
45+
descriptor.Query(q => q
46+
.Match(m => m
47+
.Field(f => f.Level)
48+
.Query(level))
49+
);
50+
if (!string.IsNullOrEmpty(searchCriteria))
51+
descriptor.Query(q => q
52+
.Match(m => m
53+
.Field(f => f.Message)
54+
.Query(searchCriteria)
55+
) || q
56+
.Match(m => m
57+
.Field(f => f.Exceptions)
58+
.Query(searchCriteria)
59+
)
60+
);
61+
//descriptor = descriptor.Fields(f => f.Field(z => z.Message == searchCriteria));
62+
63+
var result = await _client.SearchAsync<ElasticSearchDbLogModel>(descriptor, cancellationToken);
64+
65+
int.TryParse(result?.Total.ToString(), out int total);
66+
67+
return (result?.Documents.Select((x, index) => x.ToLogModel(index)).ToList(), total);
68+
}
69+
}
70+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using Newtonsoft.Json;
2+
using Newtonsoft.Json.Linq;
3+
using Serilog.Ui.Core;
4+
using System;
5+
using System.Collections.Generic;
6+
7+
namespace Serilog.Ui.MongoDbProvider
8+
{
9+
public class ElasticSearchDbLogModel
10+
{
11+
[JsonProperty("level")]
12+
public string Level { get; set; }
13+
14+
[JsonProperty("message")]
15+
public string Message { get; set; }
16+
17+
[JsonProperty("@timestamp")]
18+
public DateTime Timestamp { get; set; }
19+
20+
public JArray Exceptions { get; set; }
21+
22+
[JsonProperty("fields")]
23+
public Dictionary<string, object> Fields { get; set; }
24+
25+
internal LogModel ToLogModel(int index)
26+
{
27+
return new LogModel
28+
{
29+
RowNo = index,
30+
Level = Level,
31+
Message = Message,
32+
Timestamp = Timestamp,
33+
Exception = Exceptions?.Count > 0 ? BuildExceptionMessage(Exceptions[0]) : string.Empty,
34+
Properties = JsonConvert.SerializeObject(Fields),
35+
PropertyType = "json"
36+
};
37+
}
38+
39+
static string BuildExceptionMessage(JToken jObjet)
40+
{
41+
return $"Exception: {jObjet.Value<string>("Message")}. StackTrace: {jObjet.Value<string>("StackTraceString")}.";
42+
}
43+
}
44+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace Serilog.Ui.ElasticSearchProvider
8+
{
9+
public class ElasticSearchDbOptions
10+
{
11+
public string IndexName { get; set; }
12+
}
13+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
using Elasticsearch.Net;
2+
using Microsoft.Extensions.DependencyInjection;
3+
using Nest;
4+
using Newtonsoft.Json;
5+
using Serilog.Ui.Core;
6+
using System;
7+
using System.IO;
8+
using System.Threading;
9+
using System.Threading.Tasks;
10+
11+
namespace Serilog.Ui.ElasticSearchProvider.Extensions
12+
{
13+
public static class SerilogUiOptionBuilderExtensions
14+
{
15+
public static void UseElasticSearchDb(this SerilogUiOptionsBuilder optionsBuilder,
16+
Uri endpoint,
17+
string indexName)
18+
{
19+
if (endpoint == null) throw new ArgumentNullException(nameof(endpoint));
20+
if (string.IsNullOrEmpty(indexName)) throw new ArgumentNullException(nameof(indexName));
21+
22+
var options = new ElasticSearchDbOptions
23+
{
24+
IndexName = indexName
25+
};
26+
27+
var builder = ((ISerilogUiOptionsBuilder)optionsBuilder);
28+
29+
builder.Services.AddSingleton(options);
30+
31+
var pool = new SingleNodeConnectionPool(endpoint);
32+
var connectionSettings = new ConnectionSettings(pool, sourceSerializer: (builtin, values) => new VanillaSerializer());
33+
34+
builder.Services.AddSingleton<IElasticClient>(o => new ElasticClient(connectionSettings));
35+
builder.Services.AddScoped<IDataProvider, ElasticSearchDbDataProvider>();
36+
}
37+
}
38+
39+
public class VanillaSerializer : IElasticsearchSerializer
40+
{
41+
public T Deserialize<T>(Stream stream)
42+
=> (T)Deserialize(typeof(T), stream);
43+
44+
45+
public object Deserialize(Type type, Stream stream)
46+
{
47+
var reader = new StreamReader(stream);
48+
using (var jreader = new JsonTextReader(reader))
49+
{
50+
var serializer = new JsonSerializer();
51+
return serializer.Deserialize(jreader, type);
52+
}
53+
}
54+
55+
public Task<T> DeserializeAsync<T>(Stream stream, CancellationToken cancellationToken = default(CancellationToken)) =>
56+
Task.FromResult(Deserialize<T>(stream));
57+
58+
public Task<object> DeserializeAsync(Type type, Stream stream, CancellationToken cancellationToken = default(CancellationToken)) =>
59+
Task.FromResult(Deserialize(type, stream));
60+
61+
public void Serialize<T>(T data, Stream stream, SerializationFormatting formatting = SerializationFormatting.Indented)
62+
{
63+
var writer = new StreamWriter(stream);
64+
using (var jWriter = new JsonTextWriter(writer))
65+
{
66+
var serializer = new JsonSerializer
67+
{
68+
Formatting = formatting == SerializationFormatting.Indented ? Formatting.Indented : Formatting.None
69+
};
70+
serializer.Serialize(jWriter, data);
71+
}
72+
}
73+
74+
75+
public Task SerializeAsync<T>(T data, Stream stream, SerializationFormatting formatting = SerializationFormatting.Indented,
76+
CancellationToken cancellationToken = default(CancellationToken))
77+
{
78+
Serialize<T>(data, stream, formatting);
79+
return Task.CompletedTask;
80+
}
81+
}
82+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netstandard2.0</TargetFramework>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" />
9+
<PackageReference Include="NEST" Version="7.11.1" />
10+
<PackageReference Include="NEST.JsonNetSerializer" Version="7.11.1" />
11+
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
12+
</ItemGroup>
13+
14+
<ItemGroup>
15+
<ProjectReference Include="..\Serilog.Ui.Core\Serilog.Ui.Core.csproj" />
16+
</ItemGroup>
17+
18+
</Project>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
3+
<metadata>
4+
<id>Serilog.Ui.ElasticSearchProvider</id>
5+
<version>$version$</version>
6+
<title>Serilog.Ui.ElasticSearchProvider</title>
7+
<authors>Mohsen Esmailpour - rmauro.dev</authors>
8+
<owners>mo.esmp</owners>
9+
<projectUrl>https://github.com/mo-esmp/serilog-ui</projectUrl>
10+
<license type="expression">MIT</license>
11+
<requireLicenseAcceptance>false</requireLicenseAcceptance>
12+
<description>ElasticSearch data provider for Serilog UI.</description>
13+
<releaseNotes></releaseNotes>
14+
<copyright></copyright>
15+
<tags>serilog serilog-ui serilog.sinks.elasticsearch</tags>
16+
<dependencies>
17+
<group targetFramework=".NETStandard2.0">
18+
<dependency id="Microsoft.Extensions.DependencyInjection.Abstractions" version="3.1.3" exclude="Build,Analyzers" />
19+
<dependency id="Microsoft.Extensions.Logging.Abstractions" version="5.0.0" exclude="Build,Analyzers" />
20+
<dependency id="NEST" version="7.11.1" exclude="Build,Analyzers" />
21+
<dependency id="NEST.JsonNetSerializer" version="7.11.1" exclude="Build,Analyzers" />
22+
<dependency id="Newtonsoft.Json" version="12.0.3" exclude="Build,Analyzers" />
23+
</group>
24+
</dependencies>
25+
</metadata>
26+
<files>
27+
<file src="bin\Release\netstandard2.0\*.dll" target="lib/netstandard2.0" />
28+
</files>
29+
</package>

0 commit comments

Comments
 (0)