├── .editorconfig
├── .gitignore
├── LICENSE
├── NuGet.Config
├── README.md
├── appveyor.yml
├── artifacts
└── Serilog.Sinks.Graylog.2.1.0.snupkg
├── codecov.yml
├── deploy.yml
└── src
├── Serilog.Sinks.Graylog.Batching
├── BatchingGraylogSinkOptions.cs
├── LoggerConfigurationGrayLogExtensions.cs
├── PeriodicBatchingGraylogSink.cs
├── Serilog.Sinks.Graylog.Batching.csproj
├── Serilog.Sinks.Graylog.Batching.csproj.user
└── sign.snk
├── Serilog.Sinks.Graylog.Core.Tests
├── Extensions
│ └── StringExtensionsFixture.cs
├── GelfConverterFixture.cs
├── Helpers
│ └── MessageIdGeneratorFixture.cs
├── MessageBuilders
│ ├── ExceptionMessageBuilderFixture.cs
│ └── MessageBuilderFixture.cs
├── Serilog.Sinks.Graylog.Core.Tests.csproj
└── Transport
│ ├── DnsResolver.cs
│ ├── Http
│ ├── HttpTransportClientFixture.cs
│ └── HttpTransportFixture.cs
│ └── Udp
│ ├── DataToChunkConverterFixture.cs
│ ├── UdpTransportClientFixture.cs
│ └── UdpTransportFixture.cs
├── Serilog.Sinks.Graylog.Core
├── Extensions
│ ├── DateTimeExtensions.cs
│ ├── StringExtensions.cs
│ └── TypeExtensions.cs
├── GelfConverter.cs
├── GraylogSinkOptions.cs
├── Helpers
│ ├── HttpBasicAuthenticationGenerator.cs
│ ├── LogLevelMapper.cs
│ └── MessageIdGenerator.cs
├── MessageBuilders
│ ├── ExceptionMessageBuilder.cs
│ ├── GelfMessageBuilder.cs
│ └── IMessageBuilder.cs
├── Serilog.Sinks.Graylog.Core.csproj
├── Transport
│ ├── DnsResolver.cs
│ ├── Http
│ │ ├── HttpTransport.cs
│ │ └── HttpTransportClient.cs
│ ├── ITransport.cs
│ ├── ITransportClient'.cs
│ ├── Tcp
│ │ ├── TcpTransport.cs
│ │ └── TcpTransportClient.cs
│ ├── TransportType.cs
│ └── Udp
│ │ ├── ChunkSettings.cs
│ │ ├── DataToChunkConverter.cs
│ │ ├── UdpTransport.cs
│ │ └── UdpTransportClient.cs
├── TransportFactory.cs
└── key.snk
├── Serilog.Sinks.Graylog.Tests
├── Configurations
│ └── AppSettingsWithGraylogSinkContainingHostProperty.json
├── GraylogSinkFixture.cs
├── IntegrateSinkTestWithHttp.cs
├── IntegrateSinkTestWithTcp.cs
├── IntegrateSinkTestWithUdp.cs
├── LogEventSource.cs
├── LoggerConfigurationGrayLogExtensionsFixture.cs
├── Serilog.Sinks.Graylog.Tests.csproj
├── SerilogExceptionsFixture.cs
├── TestProfile
│ └── Profile.cs
└── altcover.xml
├── Serilog.Sinks.Graylog
├── GraylogSink.cs
├── GraylogSinkOptions.cs
├── LoggerConfigurationGrayLogExtensions.cs
├── Serilog.Sinks.Graylog.csproj
├── Serilog.Sinks.GraylogSingleTarget.csproj
├── Serilog.Sinks.GraylogSingleTarget.csproj.user
└── sign.snk
├── TestApplication
├── CustomException.cs
├── Program.cs
├── TestApplication.csproj
└── appsettings.json
├── serilog-sink-nuget.png
└── serilog-sinks-graylog.sln
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | trim_trailing_whitespace = true
5 | insert_final_newline = true
6 | indent_style = space
7 | indent_size = 4
8 | charset = utf-8
9 | end_of_line = lf
10 |
11 | [*.{csproj,json,config,yml,props}]
12 | indent_size = 2
13 |
14 | [*.sh]
15 | end_of_line = lf
16 |
17 | [*.{cmd, bat}]
18 | end_of_line = crlf
19 |
20 | # C# formatting settings - Namespace options
21 | csharp_style_namespace_declarations = file_scoped:suggestion
22 |
23 | csharp_style_prefer_switch_expression = true:suggestion
24 |
25 | # C# formatting settings - Spacing options
26 | csharp_space_after_cast = false
27 | csharp_space_after_keywords_in_control_flow_statements = true
28 | csharp_space_between_parentheses = false
29 | csharp_space_before_colon_in_inheritance_clause = true
30 | csharp_space_after_colon_in_inheritance_clause = true
31 | csharp_space_around_binary_operators = before_and_after
32 | csharp_space_between_method_declaration_parameter_list_parentheses = false
33 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
34 | csharp_space_between_method_declaration_name_and_open_parenthesis = false
35 | csharp_space_between_method_call_parameter_list_parentheses = false
36 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
37 | csharp_space_between_method_call_name_and_opening_parenthesis = false
38 | csharp_space_after_comma = true
39 | csharp_space_before_comma = false
40 | csharp_space_after_dot = false
41 | csharp_space_before_dot = false
42 | csharp_space_after_semicolon_in_for_statement = true
43 | csharp_space_before_semicolon_in_for_statement = false
44 | csharp_space_around_declaration_statements = false
45 | csharp_space_before_open_square_brackets = false
46 | csharp_space_between_empty_square_brackets = false
47 | csharp_space_between_square_brackets = false
48 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.dll
2 | *.pdb
3 | *.suo
4 | *.ncrunchproject
5 | **/packages/**
6 | **/bin/**
7 | **/obj/**
8 | *.cache
9 | *.coverage
10 | *.dg
11 | */_ReSharper.Caches
12 | *.nupkg
13 | *ncrunch*
14 | *.ide
15 | src/.vs/*
16 | *.ide-wal
17 | src/.vs/serilog-sinks-graylogvs2017/v15/sqlite3/db.lock
18 | *.ide-shm
19 | src/.vs/serilog-sinks-graylogvs2017/v15/Server/sqlite3/db.lock
20 | src/.vs/serilog-sinks-graylogvs2017/DesignTimeBuild/.dtbcache
21 | /.vs/slnx.sqlite
22 | .ionide/symbolCache.db
23 | src/.idea/
24 | *.DotSettings
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Anton Volkov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/NuGet.Config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Status
2 |
3 | [](https://ci.appveyor.com/project/whir1/serilog-sinks-graylog-j9flc/branch/master)
4 |
5 | ### serilog-sinks-graylog
6 |
7 | [](https://www.nuget.org/packages/serilog.sinks.graylog/)
8 | [](https://www.nuget.org/packages/serilog.sinks.graylog/)
9 |
10 | ### serilog-sinks-graylog-Batching
11 |
12 | [](https://www.nuget.org/packages/Serilog.Sinks.Graylog.Batching/)
13 | [](https://www.nuget.org/packages/Serilog.Sinks.Graylog.Batching/)
14 |
15 | ## What is this sink ?
16 | The Serilog Graylog sink project is a sink (basically a writer) for the Serilog logging framework. Structured log events are written to sinks and each sink is responsible for writing it to its own backend, database, store etc. This sink delivers the data to Graylog2, a NoSQL search engine.
17 |
18 | ## Quick start
19 |
20 | ```powershell
21 | Install-Package serilog.sinks.graylog
22 | ```
23 | Register the sink in code.
24 | ```csharp
25 | var loggerConfig = new LoggerConfiguration()
26 | .WriteTo.Graylog(new GraylogSinkOptions
27 | {
28 | HostnameOrAddress = "localhost",
29 | Port = 12201
30 | });
31 | ```
32 | ...or alternatively configure the sink in appsettings.json configuration like so:
33 |
34 | ```json
35 | {
36 | "Serilog": {
37 | "Using": ["Serilog.Sinks.Graylog"],
38 | "MinimumLevel": "Debug",
39 | "WriteTo": [
40 | {
41 | "Name": "Graylog",
42 | "Args": {
43 | "hostnameOrAddress": "localhost",
44 | "port": "12201",
45 | "transportType": "Udp"
46 | }
47 | }
48 | ]
49 | }
50 | }
51 | ```
52 |
53 | Note that because of the limitations of the Serilog.Settings.Configuration package, you cannot configure IGelfConverter using json.
54 |
55 | by default udp protocol is using, if you want to use http define sink options like
56 |
57 | ```csharp
58 | new GraylogSinkOptions
59 | {
60 | HostnameOrAddress = "http://localhost",
61 | Port = 12201,
62 | TransportType = TransportType.Http,
63 | }
64 | ```
65 |
66 | All options you can see at https://github.com/whir1/serilog-sinks-graylog/blob/master/src/Serilog.Sinks.Graylog.Core/GraylogSinkOptions.cs
67 |
68 | You can create your own implementation of transports or converter and set it to options. But maybe i'll delete this feature in the future
69 |
70 | PS this is my first package XD.
71 |
72 | PPS I am sorry for my language, but my second language is C#
73 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | -
2 | branches:
3 | except:
4 | - master
5 | init:
6 | - cmd: "set branch=%APPVEYOR_REPO_BRANCH%"
7 | - cmd: "echo branch:%branch%"
8 | - cmd: "set newVersion=1.0.%APPVEYOR_BUILD_NUMBER%-%branch%"
9 | - cmd: "echo newVersion:%newVersion%"
10 | - cmd: "set versonSuffix=%APPVEYOR_BUILD_NUMBER%-%branch%"
11 | - cmd: "echo versonSuffix:%versonSuffix%"
12 | - cmd: appveyor UpdateBuild -Version "%newVersion%"
13 |
14 | skip_tags: true
15 |
16 | before_build:
17 | - nuget install OpenCover -ExcludeVersion -OutputDirectory "packages"
18 | - choco install opencover.portable
19 | - choco install codecov
20 | - cmd: dotnet tool install --global altcover.global
21 |
22 | configuration: Debug
23 |
24 | image: Visual Studio 2022
25 |
26 | build_script:
27 | - cmd: dotnet restore -v n ./src/serilog-sinks-graylog.sln
28 | - cmd: dotnet build -v n -c Debug ./src/serilog-sinks-graylog.sln
29 | - cmd: dotnet pack -v n --include-symbols --include-source -o "./artifacts" --version-suffix Beta -p:SymbolPackageFormat=snupkg ./src/Serilog.Sinks.Graylog/Serilog.Sinks.Graylog.csproj
30 | - cmd: dotnet pack -v n --include-symbols --include-source -o "./artifacts" --version-suffix Beta -p:SymbolPackageFormat=snupkg ./src/Serilog.Sinks.Graylog.Batching/Serilog.Sinks.Graylog.Batching.csproj
31 |
32 | test:
33 | categories:
34 | except:
35 | - Integration
36 |
37 | test_script:
38 | - dotnet test ./src/Serilog.Sinks.Graylog.Core.Tests --filter Category!=Integration /p:AltCover=true
39 | - dotnet test ./src/Serilog.Sinks.Graylog.Tests --filter Category!=Integration /p:AltCover=true
40 |
41 | after_test:
42 | - "SET PATH=C:\\Python34;C:\\Python34\\Scripts;%PATH%"
43 | - pip install codecov
44 | - codecov -f "./src/Serilog.Sinks.Graylog.Core.Tests/coverage.xml" -t cd3f1ab1-60c6-4848-824b-466b93321d96
45 | - codecov -f "./src/Serilog.Sinks.Graylog.Tests/coverage.xml" -t cd3f1ab1-60c6-4848-824b-466b93321d96
46 |
47 | artifacts:
48 | - path: "./artifacts/*.*"
49 | -
50 | branches:
51 | only:
52 | - master
53 |
54 | configuration: Release
55 |
56 | image: Visual Studio 2022
57 |
58 | build:
59 | publish_nuget_symbols: true
60 | use_snupkg_format: true
61 |
62 | build_script:
63 | - cmd: dotnet restore -v n ./src/serilog-sinks-graylog.sln
64 | - cmd: dotnet build -v n -c Release ./src/serilog-sinks-graylog.sln
65 | - cmd: dotnet pack -v n --include-symbols --include-source -o "./artifacts" -p:SymbolPackageFormat=snupkg ./src/Serilog.Sinks.Graylog/Serilog.Sinks.Graylog.csproj
66 | - cmd: dotnet pack -v n --include-symbols --include-source -o "./artifacts" -p:SymbolPackageFormat=snupkg ./src/Serilog.Sinks.Graylog.Batching/Serilog.Sinks.Graylog.Batching.csproj
67 | test:
68 | categories:
69 | except:
70 | - Integration
71 |
72 | artifacts:
73 | - path: "./artifacts/*.*"
74 |
75 | deploy:
76 | provider: NuGet
77 | api_key:
78 | secure: J4E+ROQN+2v19RntNXpjllr1nAy/Q0tEW69Cav2ru+sYhMsvNpjOjPPd6ZoMmtf8
79 | skip_symbols: false
80 | artifact: /.*.*
81 |
--------------------------------------------------------------------------------
/artifacts/Serilog.Sinks.Graylog.2.1.0.snupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serilog-contrib/serilog-sinks-graylog/64532fa1aab7a8b3dc5c94e3294a570d6698e77d/artifacts/Serilog.Sinks.Graylog.2.1.0.snupkg
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | codecov:
2 | branch: master
3 |
4 | coverage:
5 | precision: 2
6 | round: down
7 | range: 50...100
8 |
9 | ignore:
10 | - "**/*Tests/**"
11 |
--------------------------------------------------------------------------------
/deploy.yml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serilog-contrib/serilog-sinks-graylog/64532fa1aab7a8b3dc5c94e3294a570d6698e77d/deploy.yml
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Batching/BatchingGraylogSinkOptions.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Sinks.Graylog.Core;
2 | using Serilog.Sinks.PeriodicBatching;
3 | using System;
4 |
5 | namespace Serilog.Sinks.Graylog.Batching
6 | {
7 | public class BatchingGraylogSinkOptions : GraylogSinkOptionsBase
8 | {
9 | public BatchingGraylogSinkOptions()
10 | {
11 | PeriodicOptions = new PeriodicBatchingSinkOptions()
12 | {
13 | BatchSizeLimit = 10,
14 | Period = TimeSpan.FromSeconds(1),
15 | QueueLimit = 10,
16 | };
17 | }
18 |
19 | public PeriodicBatchingSinkOptions PeriodicOptions { get; set; }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Batching/LoggerConfigurationGrayLogExtensions.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Configuration;
2 | using Serilog.Events;
3 | using Serilog.Sinks.Graylog.Core;
4 | using Serilog.Sinks.Graylog.Core.Helpers;
5 | using Serilog.Sinks.Graylog.Core.Transport;
6 | using Serilog.Sinks.PeriodicBatching;
7 | using System;
8 |
9 | namespace Serilog.Sinks.Graylog.Batching
10 | {
11 | public static class LoggerConfigurationGrayLogExtensions
12 | {
13 | /// The logger sink configuration.
14 | /// The options.
15 | public static LoggerConfiguration Graylog(this LoggerSinkConfiguration loggerSinkConfiguration,
16 | BatchingGraylogSinkOptions options)
17 | {
18 | var sink = new PeriodicBatchingGraylogSink(options);
19 |
20 | var batchingSink = new PeriodicBatchingSink(sink, options.PeriodicOptions);
21 |
22 | return loggerSinkConfiguration.Sink(batchingSink, options.MinimumLogEventLevel);
23 | }
24 |
25 | /// The logger sink configuration.
26 | /// The hostname or address.
27 | /// The port.
28 | /// Type of the transport.
29 | /// The minimum log event level.
30 | /// Type of the message identifier generator.
31 | /// Short length of the message maximum.
32 | /// The stack trace depth.
33 | /// The facility.
34 | /// The batch size limit
35 | /// The period limit default is one second
36 | /// queue limit
37 | /// the maxMessageSizeInUdp
38 | /// if set to true if include message template to graylog.
39 | /// Name of the message template field.
40 | public static LoggerConfiguration Graylog(this LoggerSinkConfiguration loggerSinkConfiguration,
41 | string hostnameOrAddress,
42 | int port,
43 | TransportType transportType,
44 | LogEventLevel minimumLogEventLevel = LevelAlias.Minimum,
45 | MessageIdGeneratorType messageIdGeneratorType = GraylogSinkOptionsBase.DefaultMessageGeneratorType,
46 | int shortMessageMaxLength = GraylogSinkOptionsBase.DefaultShortMessageMaxLength,
47 | int stackTraceDepth = GraylogSinkOptionsBase.DefaultStackTraceDepth,
48 | string facility = GraylogSinkOptionsBase.DefaultFacility,
49 | int maxMessageSizeInUdp = GraylogSinkOptionsBase.DefaultMaxMessageSizeInUdp,
50 | int batchSizeLimit = 10,
51 | TimeSpan period = default,
52 | int queueLimit = 1000,
53 | bool includeMessageTemplate = false,
54 | string messageTemplateFieldName = GraylogSinkOptionsBase.DefaultMessageTemplateFieldName
55 | )
56 | {
57 | if (period == default)
58 | {
59 | period = TimeSpan.FromSeconds(1);
60 | }
61 |
62 | // ReSharper disable once UseObjectOrCollectionInitializer
63 | var options = new BatchingGraylogSinkOptions
64 | {
65 | HostnameOrAddress = hostnameOrAddress,
66 | Port = port,
67 | TransportType = transportType,
68 | MinimumLogEventLevel = minimumLogEventLevel,
69 | MessageGeneratorType = messageIdGeneratorType,
70 | ShortMessageMaxLength = shortMessageMaxLength,
71 | StackTraceDepth = stackTraceDepth,
72 | Facility = facility,
73 | PeriodicOptions = new PeriodicBatchingSinkOptions()
74 | {
75 | BatchSizeLimit = batchSizeLimit,
76 | Period = period,
77 | QueueLimit = queueLimit,
78 | },
79 | MaxMessageSizeInUdp = maxMessageSizeInUdp,
80 | IncludeMessageTemplate = includeMessageTemplate,
81 | MessageTemplateFieldName = messageTemplateFieldName
82 | };
83 | return loggerSinkConfiguration.Graylog(options);
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Batching/PeriodicBatchingGraylogSink.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Debugging;
2 | using Serilog.Events;
3 | using Serilog.Sinks.Graylog.Core;
4 | using Serilog.Sinks.Graylog.Core.Transport;
5 | using Serilog.Sinks.PeriodicBatching;
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Linq;
9 | using System.Text.Json.Nodes;
10 | using System.Threading.Tasks;
11 |
12 | namespace Serilog.Sinks.Graylog.Batching
13 | {
14 | public class PeriodicBatchingGraylogSink : IBatchedLogEventSink
15 | {
16 | private readonly Lazy _transport;
17 | private readonly Lazy _converter;
18 |
19 | public PeriodicBatchingGraylogSink(BatchingGraylogSinkOptions options)
20 | {
21 | ISinkComponentsBuilder sinkComponentsBuilder = new SinkComponentsBuilder(options);
22 | _transport = new Lazy(sinkComponentsBuilder.MakeTransport);
23 | _converter = new Lazy(sinkComponentsBuilder.MakeGelfConverter);
24 | }
25 |
26 | public Task OnEmptyBatchAsync()
27 | {
28 | return Task.CompletedTask;
29 | }
30 |
31 | Task IBatchedLogEventSink.EmitBatchAsync(IEnumerable batch)
32 | {
33 | try
34 | {
35 | IEnumerable sendTasks = batch.Select(async logEvent =>
36 | {
37 | JsonObject json = _converter.Value.GetGelfJson(logEvent);
38 | await _transport.Value.Send(json.ToString()).ConfigureAwait(false);
39 | });
40 |
41 | return Task.WhenAll(sendTasks);
42 | } catch (Exception exc)
43 | {
44 | SelfLog.WriteLine("Oops something going wrong {0}", exc);
45 | return Task.CompletedTask;
46 | }
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Batching/Serilog.Sinks.Graylog.Batching.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 11.0
5 |
6 |
7 |
8 | net7.0;net6.0;netstandard2.0
9 |
10 | $(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage
11 |
12 | true
13 | full
14 | enable
15 | true
16 |
17 | Serilog.Sinks.Graylog.Batching
18 | Serilog.Sinks.Graylog.Batching
19 | Anton Volkov
20 | Batching version of Serilog.Sinks.Graylog Sink. The Serilog Graylog Sink project is a sink (basically a writer) for the Serilog logging framework. Structured log events are written to sinks and each sink is responsible for writing it to its own backend, database, store etc. This sink delivers the data to Graylog2, a NoSQL search engine.
21 |
22 | https://github.com/whir1/serilog-sinks-graylog
23 | https://github.com/whir1/serilog-sinks-graylog
24 | serilog-sink-nuget.png
25 |
26 | Anton Volkov Copyright © 2023
27 |
28 | http://serilog.net/images/serilog-sink-nuget.png
29 | Serilog Sink Graylog Batching
30 | en
31 | git
32 | true
33 | 3.0.3
34 | sign.snk
35 | 3.0.0.0
36 | 3.0.0.0
37 | MIT
38 | Update package dependencies
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Batching/Serilog.Sinks.Graylog.Batching.csproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | true
5 |
6 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Batching/sign.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serilog-contrib/serilog-sinks-graylog/64532fa1aab7a8b3dc5c94e3294a570d6698e77d/src/Serilog.Sinks.Graylog.Batching/sign.snk
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core.Tests/Extensions/StringExtensionsFixture.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using Serilog.Sinks.Graylog.Core.Extensions;
3 | using Xunit;
4 |
5 | namespace Serilog.Sinks.Graylog.Core.Tests.Extensions
6 | {
7 | public class StringExtensionsFixture
8 | {
9 | [Fact]
10 | public void WhenCompressMessage_ThenResultShoouldBeExpected()
11 | {
12 | var giwen = "Some string";
13 | var expected = new byte[]
14 | {
15 | 31,139,8,0,0,0,0,0,0,10,11,206,207,77,85,40,46,41,202,204,75,7,0,142,183,209,127,11,0,0,0
16 | };
17 |
18 | byte[] actual = giwen.ToGzip();
19 | actual.Should().BeEquivalentTo(expected);
20 | }
21 |
22 | [Theory]
23 | [InlineData("SomeTestString", "Some", 4)]
24 | [InlineData("SomeTestString", "SomeTest", 8)]
25 | [InlineData("SomeTestString", "SomeTestString", 200)]
26 | public void WhenShortMessage_ThenResultShouldBeExpected(string given, string expected, int length)
27 | {
28 | var actual = given.Truncate(length);
29 |
30 | actual.Should().BeEquivalentTo(expected);
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core.Tests/GelfConverterFixture.cs:
--------------------------------------------------------------------------------
1 | using Moq;
2 | using Serilog.Sinks.Graylog.Core.MessageBuilders;
3 | using Serilog.Sinks.Graylog.Tests;
4 | using System;
5 | using System.Collections.Generic;
6 | using Xunit;
7 |
8 | namespace Serilog.Sinks.Graylog.Core.Tests
9 | {
10 | public class GelfConverterFixture
11 | {
12 | [Fact]
13 | public void WhenLogEvent_ThenMessageBuilderShouldBeCalled()
14 | {
15 | var errorBuilder = new Mock();
16 | var messageBuilder = new Mock();
17 |
18 | var messageBuilders = new Dictionary>
19 | {
20 | [BuilderType.Exception] = new Lazy(() => errorBuilder.Object),
21 | [BuilderType.Message] = new Lazy(() => messageBuilder.Object)
22 | };
23 |
24 | GelfConverter target = new(messageBuilders);
25 |
26 | var simpleEvent = LogEventSource.GetSimpleLogEvent(DateTimeOffset.Now);
27 |
28 | target.GetGelfJson(simpleEvent);
29 |
30 | errorBuilder.Verify(c => c.Build(simpleEvent), Times.Never);
31 | messageBuilder.Verify(c => c.Build(simpleEvent), Times.Once);
32 | }
33 |
34 | [Fact]
35 | public void WhenLogErrorEvent_ThenErrorMessageBuilderShouldBeCalled()
36 | {
37 | var errorBuilder = new Mock();
38 | var messageBuilder = new Mock();
39 |
40 | var messageBuilders = new Dictionary>
41 | {
42 | [BuilderType.Exception] = new Lazy(() => errorBuilder.Object),
43 | [BuilderType.Message] = new Lazy(() => messageBuilder.Object)
44 | };
45 |
46 | GelfConverter target = new(messageBuilders);
47 |
48 | var simpleEvent = LogEventSource.GetErrorEvent(DateTimeOffset.Now);
49 |
50 | target.GetGelfJson(simpleEvent);
51 |
52 | errorBuilder.Verify(c => c.Build(simpleEvent), Times.Once);
53 | messageBuilder.Verify(c => c.Build(simpleEvent), Times.Never);
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core.Tests/Helpers/MessageIdGeneratorFixture.cs:
--------------------------------------------------------------------------------
1 | using AutoFixture;
2 | using FluentAssertions;
3 | using Serilog.Sinks.Graylog.Core.Helpers;
4 | using System;
5 | using System.Linq;
6 | using System.Security.Cryptography;
7 | using Xunit;
8 |
9 | namespace Serilog.Sinks.Graylog.Core.Tests.Helpers
10 | {
11 | public class MessageIdGeneratorFixture
12 | {
13 | private readonly Fixture _fixture;
14 |
15 | public MessageIdGeneratorFixture()
16 | {
17 | _fixture = new Fixture();
18 | }
19 |
20 | [Fact]
21 | public void WhenGenerateFromTimeStamp_ThenReturnsExpectedResult()
22 | {
23 | DateTime time = DateTime.UtcNow;
24 | byte[] given = _fixture.CreateMany(10).ToArray();
25 | var target = new TimestampMessageIdGenerator();
26 |
27 | byte[] actual = target.GenerateMessageId(given);
28 |
29 | var actticks = BitConverter.ToInt64(actual, 0);
30 | var actdate = DateTime.FromBinary(actticks);
31 |
32 | actdate.Should().BeCloseTo(time, TimeSpan.FromMilliseconds(200));
33 | }
34 |
35 | [Fact]
36 | public void WhenGenerateTimestampFromMd5_ThenReturnsExpected()
37 | {
38 | byte[] given = _fixture.CreateMany(10).ToArray();
39 |
40 | var target = new Md5MessageIdGenerator();
41 |
42 | MD5 md5 = MD5.Create();
43 | var expected = md5.ComputeHash(given).Take(8).ToArray();
44 |
45 | var actual = target.GenerateMessageId(given);
46 |
47 | actual.Should().BeEquivalentTo(expected);
48 | }
49 |
50 | [Fact]
51 | public void WhenResolveMd5Generator_ThenResult_ShouldBeAsExpectedType()
52 | {
53 | var resolver = new MessageIdGeneratorResolver();
54 |
55 | IMessageIdGenerator actual = resolver.Resolve(MessageIdGeneratorType.Md5);
56 |
57 | Assert.IsType(actual);
58 | }
59 |
60 | [Fact]
61 | public void WhenResolveTimeStampGenerator_ThenResult_ShouldBeAsExpectedType()
62 | {
63 | var resolver = new MessageIdGeneratorResolver();
64 |
65 | IMessageIdGenerator actual = resolver.Resolve(MessageIdGeneratorType.Timestamp);
66 |
67 | Assert.IsType(actual);
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core.Tests/MessageBuilders/ExceptionMessageBuilderFixture.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Events;
2 | using Serilog.Sinks.Graylog.Core.MessageBuilders;
3 | using Serilog.Sinks.Graylog.Tests;
4 | using System;
5 | using Xunit;
6 |
7 | namespace Serilog.Sinks.Graylog.Core.Tests.MessageBuilders
8 | {
9 | public class ExceptionMessageBuilderFixture
10 | {
11 | [Fact]
12 | public void WhenCreateException_ThenBuildShoulNotThrow()
13 | {
14 | var options = new GraylogSinkOptions();
15 |
16 | ExceptionMessageBuilder exceptionBuilder = new("localhost", options);
17 |
18 | Exception testExc = null;
19 |
20 | try
21 | {
22 | try
23 | {
24 | throw new InvalidOperationException("Level One exception");
25 | } catch (Exception exc)
26 | {
27 | throw new NotImplementedException("Nested Exception", exc);
28 | }
29 | } catch (Exception exc)
30 | {
31 | testExc = exc;
32 | }
33 |
34 |
35 | DateTimeOffset date = DateTimeOffset.Now;
36 | LogEvent logEvent = LogEventSource.GetExceptionLogEvent(date, testExc);
37 |
38 | //JObject obj = exceptionBuilder.Build(logEvent);
39 |
40 | //obj.Should().NotBeNull();
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core.Tests/MessageBuilders/MessageBuilderFixture.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Events;
2 | using Serilog.Parsing;
3 | using Serilog.Sinks.Graylog.Core.MessageBuilders;
4 | using Serilog.Sinks.Graylog.Tests;
5 | using System;
6 | using System.Collections.Generic;
7 | using Xunit;
8 |
9 | namespace Serilog.Sinks.Graylog.Core.Tests.MessageBuilders
10 | {
11 | public class GelfMessageBuilderFixture
12 | {
13 | [Fact]
14 | [Trait("Category", "Debug")]
15 | public void TryComplexEvent()
16 | {
17 | var options = new GraylogSinkOptions();
18 | var target = new GelfMessageBuilder("localhost", options);
19 |
20 | DateTimeOffset date = DateTimeOffset.Now;
21 |
22 | LogEvent logEvent = LogEventSource.GetComplexEvent(date);
23 |
24 | //string actual = target.Build(logEvent).ToString(Newtonsoft.Json.Formatting.None);
25 | }
26 |
27 | [Fact]
28 | public void GetSimpleLogEvent_GraylogSinkOptionsContainsHost_ReturnsOptionsHost()
29 | {
30 | //arrange
31 | GraylogSinkOptions options = new()
32 | {
33 | HostnameOverride = "my_host"
34 | };
35 | GelfMessageBuilder messageBuilder = new("localhost", options);
36 | DateTime date = DateTime.UtcNow;
37 | string expectedHost = "my_host";
38 |
39 | //act
40 | LogEvent logEvent = LogEventSource.GetSimpleLogEvent(date);
41 | var actual = messageBuilder.Build(logEvent);
42 | string actualHost = actual["host"].AsValue().ToString();
43 |
44 | //assert
45 | Assert.Equal(expectedHost, actualHost);
46 | }
47 |
48 |
49 | [Fact]
50 | public static void WhenTryCreateLogEventWithNullKeyOrValue_ThenThrow()
51 | {
52 | //If in future this test fail then should add check for null in GelfMessageBuilder
53 | Assert.Throws(() =>
54 | {
55 | var logEvent = new LogEvent(DateTimeOffset.Now, LogEventLevel.Information, null,
56 | new MessageTemplate("abcdef{TestProp}", new List
57 | {
58 | new TextToken("abcdef", 0),
59 | new PropertyToken("TestProp", "zxc", alignment: new Alignment(AlignmentDirection.Left, 3))
60 |
61 | }), new List
62 | {
63 | new("TestProp", new ScalarValue("zxc")),
64 | new("id", new ScalarValue("asd")),
65 | new("Oo", null),
66 | new(null, null),
67 | new("StructuredProperty",
68 | new StructureValue(new List
69 | {
70 | new("id", new ScalarValue(1)),
71 | new("_TestProp", new ScalarValue(3)),
72 | }, "TypeTag"))
73 | });
74 | });
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core.Tests/Serilog.Sinks.Graylog.Core.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net7.0;net6.0
5 | full
6 |
7 |
8 |
9 |
10 |
11 |
12 | all
13 | runtime; build; native; contentfiles; analyzers; buildtransitive
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core.Tests/Transport/DnsResolver.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Sinks.Graylog.Core.Transport;
2 | using System.Net;
3 | using System.Threading.Tasks;
4 | using Xunit;
5 |
6 | namespace Serilog.Sinks.Graylog.Core.Tests.Transport
7 | {
8 | public class DnsWrapperFixture
9 | {
10 | [Fact]
11 | public async Task Test()
12 | {
13 | var traget = new DnsWrapper();
14 |
15 | IPAddress[] actual = await traget.GetHostAddresses("github.com");
16 |
17 | Assert.NotEmpty(actual);
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core.Tests/Transport/Http/HttpTransportClientFixture.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using Serilog.Debugging;
3 | using Serilog.Sinks.Graylog.Core.Transport.Http;
4 | using System.Threading.Tasks;
5 | using Xunit;
6 |
7 | namespace Serilog.Sinks.Graylog.Core.Tests.Transport.Http
8 | {
9 | public class HttpTransportClientFixture
10 | {
11 | [Fact(Skip = "This test not work anymore because logs.aeroclub.int not exists")]
12 | [Trait("Category", "Integration")]
13 | public async Task WhenSendJson_ThenResultShouldNotThrows()
14 | {
15 | var target = new HttpTransportClient(new GraylogSinkOptions
16 | {
17 | HostnameOrAddress = "http://logs.aeroclub.int:12201/gelf"
18 | });
19 |
20 | await target.Send("{\"facility\":\"VolkovTestFacility\",\"full_message\":\"SomeComplexTestEntry TestClass { Id: 1, TestPropertyOne: \\\"1\\\", Bar: Bar { Id: 2, Prop: \\\"123\\\" }, TestPropertyTwo: \\\"2\\\", TestPropertyThree: \\\"3\\\" }\",\"host\":\"N68-MSK\",\"level\":6,\"short_message\":\"SomeComplexTestEntry TestClass { Id: 1, TestProper\",\"timestamp\":\"2017-03-24T11:18:54.1850651\",\"version\":\"1.1\",\"_stringLevel\":\"Information\",\"_test.Id\":1,\"_test.TestPropertyOne\":\"1\",\"_test.Bar.Id\":2,\"_test.Bar.Prop\":\"123\",\"_test.TestPropertyTwo\":\"2\",\"_test.TestPropertyThree\":\"3\"}");
21 | }
22 |
23 | [Fact(Skip = "This test not work anymore because logs.aeroclub.int not exists")]
24 | [Trait("Category", "Integration")]
25 | public async Task WhenSendJson_ThenResultShouldThrowException()
26 | {
27 | var target = new HttpTransportClient(new GraylogSinkOptions
28 | {
29 | HostnameOrAddress = "http://logs.aeroclub.int:12201"
30 | });
31 |
32 | LoggingFailedException exception = await Assert.ThrowsAsync(() => target.Send("{\"facility\":\"VolkovTestFacility\",\"full_message\":\"SomeComplexTestEntry TestClass { Id: 1, TestPropertyOne: \\\"1\\\", Bar: Bar { Id: 2, Prop: \\\"123\\\" }, TestPropertyTwo: \\\"2\\\", TestPropertyThree: \\\"3\\\" }\",\"host\":\"N68-MSK\",\"level\":6,\"short_message\":\"SomeComplexTestEntry TestClass { Id: 1, TestProper\",\"timestamp\":\"2017-03-24T11:18:54.1850651\",\"version\":\"1.1\",\"_stringLevel\":\"Information\",\"_test.Id\":1,\"_test.TestPropertyOne\":\"1\",\"_test.Bar.Id\":2,\"_test.Bar.Prop\":\"123\",\"_test.TestPropertyTwo\":\"2\",\"_test.TestPropertyThree\":\"3\"}"));
33 |
34 | exception.Message.Should().Be("Unable send log message to graylog via HTTP transport");
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core.Tests/Transport/Http/HttpTransportFixture.cs:
--------------------------------------------------------------------------------
1 | using AutoFixture;
2 | using Moq;
3 | using Serilog.Sinks.Graylog.Core.Transport;
4 | using Serilog.Sinks.Graylog.Core.Transport.Http;
5 | using System.Threading.Tasks;
6 | using Xunit;
7 |
8 | namespace Serilog.Sinks.Graylog.Core.Tests.Transport.Http
9 | {
10 | public class HttpTransportFixture
11 | {
12 | private readonly Fixture _fixture;
13 |
14 | public HttpTransportFixture()
15 | {
16 | _fixture = new Fixture();
17 | }
18 |
19 | [Fact]
20 | public async Task WhenCallSend_ThenCallSendWithoutAnyChanges()
21 | {
22 | var transportClient = new Mock>();
23 |
24 | var target = new HttpTransport(transportClient.Object);
25 |
26 | var payload = _fixture.Create();
27 |
28 | await target.Send(payload);
29 |
30 | transportClient.Verify(c => c.Send(payload), Times.Once);
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core.Tests/Transport/Udp/DataToChunkConverterFixture.cs:
--------------------------------------------------------------------------------
1 | using AutoFixture;
2 | using FluentAssertions;
3 | using Moq;
4 | using Serilog.Sinks.Graylog.Core.Helpers;
5 | using Serilog.Sinks.Graylog.Core.Transport.Udp;
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Linq;
9 | using Xunit;
10 |
11 | namespace Serilog.Sinks.Graylog.Core.Tests.Transport.Udp
12 | {
13 | public class DataToChunkConverterFixture
14 | {
15 | private readonly ChunkSettings _settings;
16 | private readonly Fixture _fixture;
17 | private readonly Mock _resolver;
18 |
19 | public DataToChunkConverterFixture()
20 | {
21 | _settings = new ChunkSettings(MessageIdGeneratorType.Md5, 8192);
22 | _fixture = new Fixture();
23 | _resolver = new Mock();
24 | }
25 |
26 | [Fact]
27 | public void WhenConvertToChunkWithSmallData_ThenReturnsOneChunk()
28 | {
29 | var target = new DataToChunkConverter(_settings, _resolver.Object);
30 |
31 | byte[] giwenData = _fixture.CreateMany(1000).ToArray();
32 | IList actual = target.ConvertToChunks(giwenData);
33 |
34 | var expected = new List()
35 | {
36 | giwenData
37 | };
38 |
39 | actual.Should().BeEquivalentTo(expected);
40 | }
41 |
42 | [Fact]
43 | public void WhenChunksWasTooMany_ThenThrowsException()
44 | {
45 | byte[] giwenData = new byte[10000000];
46 |
47 | var target = new DataToChunkConverter(_settings, _resolver.Object);
48 |
49 | Assert.Throws(() => target.ConvertToChunks(giwenData));
50 | }
51 |
52 | [Fact]
53 | public void WhenMessageIsLong_ThenSplitItToChunks()
54 | {
55 | byte[] giwenData = new byte[100000];
56 |
57 | var idGenerator = new Mock();
58 |
59 | var messageId = _fixture.CreateMany(8).ToArray();
60 |
61 | idGenerator.Setup(c => c.GenerateMessageId(giwenData)).Returns(messageId);
62 |
63 | _resolver.Setup(c => c.Resolve(_settings.MessageIdGeneratorType))
64 | .Returns(idGenerator.Object);
65 |
66 | var target = new DataToChunkConverter(_settings, _resolver.Object);
67 |
68 | var actual = target.ConvertToChunks(giwenData);
69 |
70 |
71 | Assert.True(actual.Count == 13);
72 |
73 | for (int i = 0; i < actual.Count; i++)
74 | {
75 | actual[i].Take(2).ToArray().Should().BeEquivalentTo(new[] { 0x1e, 0x0f });
76 | actual[i].Skip(2).Take(8).ToArray().Should().BeEquivalentTo(messageId);
77 | actual[i].Skip(10).Take(1).First().Should().Be((byte)i);
78 | actual[i].Skip(11).Take(1).First().Should().Be(13);
79 | Assert.True(actual[i].Skip(12).All(c => c == 0));
80 | }
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core.Tests/Transport/Udp/UdpTransportClientFixture.cs:
--------------------------------------------------------------------------------
1 | using AutoFixture;
2 | using Serilog.Sinks.Graylog.Core.Transport.Udp;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Xunit;
6 |
7 | namespace Serilog.Sinks.Graylog.Core.Tests.Transport.Udp
8 | {
9 | using Core.Transport;
10 |
11 | public class UdpTransportClientFixture
12 | {
13 | [Fact]
14 | public async Task TrySendSomeData()
15 | {
16 | var fixture = new Fixture();
17 | var bytes = fixture.CreateMany(128);
18 |
19 | var client = new UdpTransportClient(new GraylogSinkOptions
20 | {
21 | HostnameOrAddress = "127.0.0.1",
22 | Port = 3128
23 | }, new DnsWrapper());
24 |
25 | await client.Send(bytes.ToArray());
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core.Tests/Transport/Udp/UdpTransportFixture.cs:
--------------------------------------------------------------------------------
1 | using AutoFixture;
2 | using Moq;
3 | using Serilog.Sinks.Graylog.Core.Extensions;
4 | using Serilog.Sinks.Graylog.Core.Transport;
5 | using Serilog.Sinks.Graylog.Core.Transport.Udp;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using Xunit;
9 |
10 | namespace Serilog.Sinks.Graylog.Core.Tests.Transport.Udp
11 | {
12 | public class UdpTransportFixture
13 | {
14 | [Fact]
15 | public void WhenSend_ThenCallMethods()
16 | {
17 | var transportClient = new Mock>();
18 | var dataToChunkConverter = new Mock();
19 | var options = new GraylogSinkOptions();
20 |
21 | var fixture = new Fixture();
22 |
23 | var stringData = fixture.Create();
24 |
25 | byte[] data = stringData.ToGzip();
26 |
27 | List chunks = fixture.CreateMany(3).ToList();
28 |
29 | dataToChunkConverter.Setup(c => c.ConvertToChunks(data)).Returns(chunks);
30 |
31 | UdpTransport target = new(transportClient.Object, dataToChunkConverter.Object, options);
32 |
33 | target.Send(stringData);
34 |
35 | dataToChunkConverter.Verify(c => c.ConvertToChunks(data), Times.Once);
36 |
37 | foreach (byte[] chunk in chunks)
38 | {
39 | transportClient.Verify(c => c.Send(chunk), Times.Once);
40 | }
41 |
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core/Extensions/DateTimeExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Serilog.Sinks.Graylog.Core.Extensions
4 | {
5 | public static class DateTimeExtensions
6 | {
7 | private static readonly DateTime Epoch = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
8 |
9 | ///
10 | /// Converts to nix date time.
11 | ///
12 | ///
13 | ///
14 | public static double ConvertToNix(this DateTimeOffset dateTimeOffset)
15 | {
16 | var duration = dateTimeOffset.ToUniversalTime() - Epoch;
17 |
18 | return Math.Round(duration.TotalSeconds, 3, MidpointRounding.AwayFromZero);
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core/Extensions/StringExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.IO.Compression;
4 | using System.Text;
5 |
6 | namespace Serilog.Sinks.Graylog.Core.Extensions
7 | {
8 | public static class StringExtensions
9 | {
10 | public static byte[] ToGzip(this string source)
11 | {
12 | var resultStream = new MemoryStream();
13 |
14 | using (var gzipStream = new GZipStream(resultStream, CompressionMode.Compress))
15 | {
16 | byte[] messageBytes = ToByteArray(source);
17 |
18 | gzipStream.Write(messageBytes, 0, messageBytes.Length);
19 | }
20 |
21 | return resultStream.ToArray();
22 | }
23 |
24 | public static byte[] ToByteArray(this string source) => Encoding.UTF8.GetBytes(source);
25 |
26 | ///
27 | /// Truncates the specified maximum length.
28 | ///
29 | /// The source.
30 | /// The maximum length.
31 | ///
32 | public static string Truncate(this string source, int maxLength)
33 | {
34 | return source.Length > maxLength ? source.Substring(0, maxLength) : source;
35 | }
36 |
37 | public static string Expand(this string source)
38 | {
39 | return Environment.ExpandEnvironmentVariables(source);
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core/Extensions/TypeExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Serilog.Sinks.Graylog.Core.Extensions
4 | {
5 | ///
6 | /// Some type extensions
7 | ///
8 | public static class TypeExtensions
9 | {
10 | ///
11 | /// Determines whether [is numeric type].
12 | ///
13 | /// The type.
14 | ///
15 | /// true if [is numeric type] [the specified type]; otherwise, false.
16 | ///
17 | public static bool IsNumericType(this Type type)
18 | {
19 | return Type.GetTypeCode(type) switch
20 | {
21 | TypeCode.Byte or
22 | TypeCode.SByte or
23 | TypeCode.UInt16 or
24 | TypeCode.UInt32 or
25 | TypeCode.UInt64 or
26 | TypeCode.Int16 or
27 | TypeCode.Int32 or
28 | TypeCode.Int64 or
29 | TypeCode.Decimal or
30 | TypeCode.Double or
31 | TypeCode.Single => true,
32 | _ => false,
33 | };
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core/GelfConverter.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Events;
2 | using Serilog.Sinks.Graylog.Core.MessageBuilders;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Text.Json.Nodes;
6 |
7 | namespace Serilog.Sinks.Graylog.Core
8 | {
9 | public interface IGelfConverter
10 | {
11 | JsonObject GetGelfJson(LogEvent logEvent);
12 | }
13 |
14 | public class GelfConverter : IGelfConverter
15 | {
16 | private readonly IDictionary> _messageBuilders;
17 |
18 | public GelfConverter(IDictionary> messageBuilders)
19 | {
20 | _messageBuilders = messageBuilders;
21 | }
22 |
23 | public JsonObject GetGelfJson(LogEvent logEvent)
24 | {
25 | IMessageBuilder builder = logEvent.Exception != null
26 | ? _messageBuilders[BuilderType.Exception].Value
27 | : _messageBuilders[BuilderType.Message].Value;
28 |
29 | return builder.Build(logEvent);
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core/GraylogSinkOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Serilog.Events;
3 | using Serilog.Sinks.Graylog.Core.Helpers;
4 | using Serilog.Sinks.Graylog.Core.Transport;
5 | using System.Text.Json;
6 | // ReSharper disable InconsistentNaming
7 | // ReSharper disable MemberCanBePrivate.Global
8 | // ReSharper disable PublicConstructorInAbstractClass
9 | // ReSharper disable UnusedAutoPropertyAccessor.Global
10 | // ReSharper disable PropertyCanBeMadeInitOnly.Global
11 | namespace Serilog.Sinks.Graylog.Core
12 | {
13 |
14 | ///
15 | /// Sync options for graylog
16 | ///
17 | public abstract class GraylogSinkOptionsBase
18 | {
19 | public const string DefaultFacility = null!;
20 | public const int DefaultShortMessageMaxLength = 500;
21 | public const LogEventLevel DefaultMinimumLogEventLevel = LevelAlias.Minimum;
22 | public const int DefaultStackTraceDepth = 10;
23 | public const MessageIdGeneratorType DefaultMessageGeneratorType = MessageIdGeneratorType.Timestamp;
24 |
25 | public const int DefaultMaxMessageSizeInUdp = 8192;
26 | ///
27 | /// The default option value (null) for GELF's "host" property. DNS hostname will be used instead.
28 | ///
29 | public const string DefaultHost = null!;
30 |
31 | public const int DefaultPort = 12201;
32 |
33 | public const string DefaultMessageTemplateFieldName = "message_template";
34 |
35 | // ReSharper disable once MemberCanBePrivate.Global
36 | public static readonly JsonSerializerOptions DefaultSerializerSettings = new()
37 | {
38 | WriteIndented = false,
39 | };
40 |
41 | public GraylogSinkOptionsBase()
42 | {
43 | MessageGeneratorType = MessageIdGeneratorType.Timestamp;
44 | ShortMessageMaxLength = DefaultShortMessageMaxLength;
45 | MinimumLogEventLevel = DefaultMinimumLogEventLevel;
46 | Facility = DefaultFacility;
47 | StackTraceDepth = DefaultStackTraceDepth;
48 | MaxMessageSizeInUdp = DefaultMaxMessageSizeInUdp;
49 | HostnameOverride = DefaultHost;
50 | IncludeMessageTemplate = false;
51 | ExcludeMessageTemplateProperties = false;
52 | MessageTemplateFieldName = DefaultMessageTemplateFieldName;
53 | JsonSerializerOptions = DefaultSerializerSettings;
54 | ParseArrayValues = false;
55 | //use gzip by default
56 | UseGzip = true;
57 | Port = DefaultPort;
58 | }
59 |
60 | ///
61 | /// Should parse values in arrays
62 | ///
63 | public bool ParseArrayValues { get; set; }
64 |
65 | ///
66 | /// Gets or sets the name of the message template field.
67 | ///
68 | ///
69 | /// The name of the message template field.
70 | ///
71 | public string MessageTemplateFieldName { get; set; }
72 |
73 | ///
74 | /// Gets or sets a value indicating whether include message template to graylog.
75 | ///
76 | ///
77 | /// true if include message template; otherwise, false.
78 | ///
79 | ///
80 | public bool IncludeMessageTemplate { get; set; }
81 |
82 | ///
83 | /// Exclude message template properties.
84 | ///
85 | ///
86 | /// true if exclude message template properties; otherwise, false.
87 | ///
88 | public bool ExcludeMessageTemplateProperties { get; set; }
89 |
90 | ///
91 | /// Gets or sets the minimum log event level.
92 | ///
93 | ///
94 | /// The minimum log event level.
95 | ///
96 | public LogEventLevel MinimumLogEventLevel { get; set; }
97 |
98 | ///
99 | /// Gets or sets the hostname or address of graylog server.
100 | ///
101 | ///
102 | /// The hostname or address.
103 | ///
104 | public string? HostnameOrAddress { get; set; }
105 |
106 | ///
107 | /// Gets or sets the facility name.
108 | ///
109 | ///
110 | /// The facility.
111 | ///
112 | public string? Facility { get; set; }
113 |
114 | ///
115 | /// Gets or sets the server port.
116 | ///
117 | ///
118 | /// The port.
119 | ///
120 | public int? Port { get; set; }
121 |
122 | ///
123 | /// Gets or sets the transport.
124 | ///
125 | ///
126 | /// The transport.
127 | ///
128 | ///
129 | /// You can implement another one or use default udp transport
130 | ///
131 | public TransportType TransportType { get; set; }
132 |
133 | ///
134 | /// Gets or sets the gelf converter.
135 | ///
136 | ///
137 | /// The gelf converter.
138 | ///
139 | ///
140 | /// You can implement another one for customize fields or use default
141 | ///
142 | public IGelfConverter? GelfConverter { get; set; }
143 |
144 | ///
145 | /// Gets or sets the maximal length of the ShortMessage
146 | ///
147 | ///
148 | /// ShortMessage Length
149 | ///
150 | public int ShortMessageMaxLength { get; set; }
151 |
152 | ///
153 | /// Gets or sets the type of the message generator.
154 | ///
155 | ///
156 | /// The type of the message generator.
157 | ///
158 | ///
159 | /// its timestamp or first 8 bytes of md5 hash
160 | ///
161 | public MessageIdGeneratorType MessageGeneratorType { get; set; }
162 |
163 | ///
164 | /// Gets or sets the stack trace depth.
165 | ///
166 | ///
167 | /// The stack trace depth.
168 | ///
169 | public int StackTraceDepth { get; set; }
170 |
171 | ///
172 | /// Gets or sets the maximum udp message size.
173 | ///
174 | ///
175 | /// The maximum udp message size
176 | ///
177 | public int MaxMessageSizeInUdp { get; set; }
178 |
179 | ///
180 | /// Gets or sets the host property required by the GELF format. If set to null, DNS hostname will be used instead.
181 | /// Override computer host name in logs
182 | ///
183 | public string HostnameOverride { get; set; }
184 |
185 | ///
186 | /// Is this a secure connection (SSL)? If so, it gets validated with the host
187 | ///
188 | public bool UseSsl { get; set; }
189 |
190 | ///
191 | /// JsonSerializer options
192 | ///
193 | public JsonSerializerOptions JsonSerializerOptions { get; set; }
194 |
195 | ///
196 | /// Gets or sets the username in http
197 | ///
198 | public string? UsernameInHttp { get; set; }
199 |
200 | ///
201 | /// Gets or sets the password in http
202 | ///
203 | public string? PasswordInHttp { get; set; }
204 |
205 | ///
206 | /// For custom implementations of ITransport
207 | ///
208 | ///
209 | /// should be set to
210 | ///
211 | public Func? TransportFactory { get; set; }
212 |
213 | ///
214 | /// Should use gzip for sending logs
215 | ///
216 | public bool UseGzip { get; set; }
217 | }
218 | }
219 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core/Helpers/HttpBasicAuthenticationGenerator.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Debugging;
2 | using System;
3 | using System.Net.Http.Headers;
4 | using System.Text;
5 |
6 | namespace Serilog.Sinks.Graylog.Core.Helpers
7 | {
8 | public class HttpBasicAuthenticationGenerator
9 | {
10 | private readonly string? _usernameInHttp;
11 | private readonly string? _passwordInHttp;
12 |
13 | public HttpBasicAuthenticationGenerator(string? usernameInHttp, string? passwordInHttp)
14 | {
15 | _usernameInHttp = usernameInHttp;
16 | _passwordInHttp = passwordInHttp;
17 | }
18 |
19 | public AuthenticationHeaderValue? Generate()
20 | {
21 | if (!Validate()) return null;
22 |
23 | var byteArray = Encoding.ASCII.GetBytes(string.Concat(_usernameInHttp, ":", _passwordInHttp));
24 |
25 | return new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
26 | }
27 |
28 | private bool Validate()
29 | {
30 | if (_usernameInHttp == null && _passwordInHttp == null) return false;
31 |
32 | if (_passwordInHttp == null)
33 | {
34 | SelfLog.WriteLine("UsernameInHttp has value, but passwordInHttp is empty. Therefore basic authentication not used");
35 | return false;
36 | }
37 |
38 | if (_usernameInHttp == null)
39 | {
40 | SelfLog.WriteLine("PasswordInHttp has value, but UsernameInHttp is empty. Therefore basic authentication not used");
41 | return false;
42 | }
43 |
44 | return true;
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core/Helpers/LogLevelMapper.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Events;
2 | using System.Collections.Generic;
3 |
4 | namespace Serilog.Sinks.Graylog.Core.Helpers
5 | {
6 | public static class LogLevelMapper
7 | {
8 | private static readonly Dictionary LogLevelMap = new()
9 | {
10 | [LogEventLevel.Verbose] = 7,
11 | [LogEventLevel.Debug] = 7,
12 | [LogEventLevel.Information] = 6,
13 | [LogEventLevel.Warning] = 4,
14 | [LogEventLevel.Error] = 3,
15 | [LogEventLevel.Fatal] = 0
16 | };
17 |
18 | ///
19 | /// Gets the mapped level.
20 | ///
21 | /// The log event level.
22 | /// Syslog level
23 | ///
24 | /// SyslogLevels:
25 | /// 0 Emergency: system is unusable
26 | /// 1 Alert: action must be taken immediately
27 | /// 2 Critical: critical conditions
28 | /// 3 Error: error conditions
29 | /// 4 Warning: warning conditions
30 | /// 5 Notice: normal but significant condition
31 | /// 6 Informational: informational messages
32 | /// 7 Debug: debug-level messages
33 | ///
34 | internal static int GetMappedLevel(LogEventLevel logEventLevel)
35 | {
36 | return LogLevelMap[logEventLevel];
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core/Helpers/MessageIdGenerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Security.Cryptography;
5 |
6 | namespace Serilog.Sinks.Graylog.Core.Helpers
7 | {
8 | public interface IMessageIdGenerator
9 | {
10 | ///
11 | /// Generates the message identifier.
12 | ///
13 | ///
14 | byte[] GenerateMessageId(byte[] message);
15 | }
16 |
17 | public enum MessageIdGeneratorType
18 | {
19 | Timestamp,
20 | Md5
21 | }
22 |
23 | ///
24 | /// Generate message id from new GUID
25 | ///
26 | ///
27 | public sealed class TimestampMessageIdGenerator : IMessageIdGenerator
28 | {
29 | public byte[] GenerateMessageId(byte[] message)
30 | {
31 | return BitConverter.GetBytes(DateTime.UtcNow.Ticks);
32 | }
33 | }
34 |
35 | ///
36 | /// Generate message Id from first 8 bytes of MD5 hash
37 | ///
38 | ///
39 | public sealed class Md5MessageIdGenerator : IMessageIdGenerator
40 | {
41 | public byte[] GenerateMessageId(byte[] message)
42 | {
43 | #if NETSTANDARD2_0
44 | using MD5 md5 = MD5.Create();
45 |
46 | byte[] messageHash = md5.ComputeHash(message);
47 | #else
48 | byte[] messageHash = MD5.HashData(message);
49 | #endif
50 |
51 | return messageHash.Take(8).ToArray();
52 | }
53 | }
54 |
55 | public interface IMessageIdGeneratorResolver
56 | {
57 | IMessageIdGenerator Resolve(MessageIdGeneratorType generatorType);
58 | }
59 |
60 | public sealed class MessageIdGeneratorResolver : IMessageIdGeneratorResolver
61 | {
62 | private readonly Dictionary> _messageGenerators = new()
63 | {
64 | [MessageIdGeneratorType.Timestamp] = new Lazy(() => new TimestampMessageIdGenerator()),
65 | [MessageIdGeneratorType.Md5] = new Lazy(() => new Md5MessageIdGenerator())
66 | };
67 |
68 | /// Condition.
69 | public IMessageIdGenerator Resolve(MessageIdGeneratorType generatorType)
70 | {
71 | return _messageGenerators[generatorType].Value;
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core/MessageBuilders/ExceptionMessageBuilder.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Events;
2 | using System;
3 | using System.Text;
4 | using System.Text.Json.Nodes;
5 |
6 | namespace Serilog.Sinks.Graylog.Core.MessageBuilders
7 | {
8 | ///
9 | /// Exception builder
10 | ///
11 | ///
12 | public class ExceptionMessageBuilder : GelfMessageBuilder
13 | {
14 | private const string DefaultExceptionDelimiter = " - ";
15 | private const string DefaultStackTraceDelimiter = "--- Inner exception stack trace ---";
16 |
17 | ///
18 | /// Initializes a new instance of the class.
19 | ///
20 | /// Name of the host.
21 | /// The options.
22 | public ExceptionMessageBuilder(string hostName, GraylogSinkOptionsBase options) : base(hostName, options)
23 | {
24 | }
25 |
26 | public override JsonObject Build(LogEvent logEvent)
27 | {
28 | Tuple excMessageTuple = GetExceptionMessages(logEvent.Exception);
29 | string exceptionDetail = excMessageTuple.Item1;
30 | string stackTrace = excMessageTuple.Item2!;
31 | string source = logEvent.Exception.Source!;
32 | string type = logEvent.Exception.GetType().FullName!;
33 |
34 | logEvent.AddOrUpdateProperty(new LogEventProperty("ExceptionSource", new ScalarValue(source)));
35 | logEvent.AddOrUpdateProperty(new LogEventProperty("ExceptionType", new ScalarValue(type)));
36 | logEvent.AddOrUpdateProperty(new LogEventProperty("ExceptionMessage", new ScalarValue(exceptionDetail)));
37 | logEvent.AddOrUpdateProperty(new LogEventProperty("StackTrace", new ScalarValue(stackTrace)));
38 |
39 | return base.Build(logEvent);
40 | }
41 |
42 | ///
43 | /// Get the message details from all nested exceptions, up to 10 in depth.
44 | ///
45 | /// Exception to get details for
46 | private Tuple GetExceptionMessages(Exception ex)
47 | {
48 | var exceptionSb = new StringBuilder();
49 | var stackSb = new StringBuilder();
50 | Exception? nestedException = ex;
51 | string? stackDetail = null;
52 |
53 | var counter = 0;
54 | do
55 | {
56 | exceptionSb.Append(nestedException.Message).Append(DefaultExceptionDelimiter);
57 | if (nestedException.StackTrace != null)
58 | {
59 | stackSb.AppendLine(nestedException.StackTrace).AppendLine(DefaultStackTraceDelimiter);
60 | }
61 | nestedException = nestedException.InnerException;
62 | counter++;
63 | }
64 | while (nestedException != null && counter < Options.StackTraceDepth);
65 |
66 | string exceptionDetail = exceptionSb.ToString().Substring(0, exceptionSb.Length - DefaultExceptionDelimiter.Length).Trim();
67 |
68 | if (stackSb.Length > 0)
69 | {
70 | stackDetail = stackSb.ToString().Substring(0, stackSb.Length - DefaultStackTraceDelimiter.Length - 2).Trim();
71 | }
72 |
73 | return new Tuple(exceptionDetail, stackDetail);
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core/MessageBuilders/GelfMessageBuilder.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Events;
2 | using Serilog.Parsing;
3 | using Serilog.Sinks.Graylog.Core.Extensions;
4 | using Serilog.Sinks.Graylog.Core.Helpers;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.IO;
8 | using System.Linq;
9 | using System.Text.Json;
10 | using System.Text.Json.Nodes;
11 |
12 | namespace Serilog.Sinks.Graylog.Core.MessageBuilders
13 | {
14 | ///
15 | /// Message builder
16 | ///
17 | ///
18 | public class GelfMessageBuilder : IMessageBuilder
19 | {
20 | private const string DefaultGelfVersion = "1.1";
21 |
22 | protected GraylogSinkOptionsBase Options => _options;
23 |
24 | private readonly string _hostName;
25 | private readonly GraylogSinkOptionsBase _options;
26 |
27 | ///
28 | /// Initializes a new instance of the class.
29 | ///
30 | /// Name of the host.
31 | /// The options.
32 | public GelfMessageBuilder(string hostName, GraylogSinkOptionsBase options)
33 | {
34 | _hostName = hostName;
35 | _options = options;
36 | }
37 |
38 | ///
39 | /// Builds the specified log event.
40 | ///
41 | /// The log event.
42 | ///
43 | public virtual JsonObject Build(LogEvent logEvent)
44 | {
45 | string message = logEvent.RenderMessage();
46 | string shortMessage = message.Truncate(Options.ShortMessageMaxLength);
47 |
48 | var jsonObject = new JsonObject
49 | {
50 | ["version"] = DefaultGelfVersion,
51 | ["host"] = Options.HostnameOverride ?? _hostName,
52 | ["short_message"] = shortMessage,
53 | ["timestamp"] = logEvent.Timestamp.ConvertToNix(),
54 | ["level"] = LogLevelMapper.GetMappedLevel(logEvent.Level),
55 | ["_stringLevel"] = logEvent.Level.ToString(),
56 | ["_facility"] = Options.Facility
57 | };
58 |
59 | if (message.Length > Options.ShortMessageMaxLength)
60 | {
61 | jsonObject.Add("full_message", message);
62 | }
63 |
64 | foreach (KeyValuePair property in logEvent.Properties)
65 | {
66 | if (Options.ExcludeMessageTemplateProperties)
67 | {
68 | var propertyTokens = logEvent.MessageTemplate.Tokens.OfType();
69 |
70 | if (propertyTokens.Any(x => x.PropertyName == property.Key))
71 | {
72 | continue;
73 | }
74 | }
75 |
76 | AddAdditionalField(jsonObject, property);
77 | }
78 |
79 | if (Options.IncludeMessageTemplate)
80 | {
81 | string messageTemplate = logEvent.MessageTemplate.Text;
82 |
83 | jsonObject.Add($"_{Options.MessageTemplateFieldName}", messageTemplate);
84 | }
85 |
86 | return jsonObject;
87 | }
88 |
89 | private void AddAdditionalField(JsonObject jObject,
90 | KeyValuePair property,
91 | string memberPath = "")
92 | {
93 | string key = string.IsNullOrEmpty(memberPath)
94 | ? property.Key
95 | : $"{memberPath}.{property.Key}";
96 |
97 | switch (property.Value)
98 | {
99 | case ScalarValue scalarValue:
100 | if (key.Equals("id", StringComparison.OrdinalIgnoreCase))
101 | {
102 | key = "id_";
103 | }
104 |
105 | if (!key.StartsWith("_", StringComparison.OrdinalIgnoreCase))
106 | {
107 | key = $"_{key}";
108 | }
109 |
110 | if (scalarValue.Value == null)
111 | {
112 | jObject.Add(key, null);
113 |
114 | break;
115 | }
116 |
117 | var node = JsonSerializer.SerializeToNode(scalarValue.Value, Options.JsonSerializerOptions);
118 |
119 | jObject.Add(key, node);
120 |
121 | break;
122 | case SequenceValue sequenceValue:
123 | var sequenceValueString = RenderPropertyValue(sequenceValue);
124 |
125 | jObject.Add(key, sequenceValueString);
126 |
127 | if (Options.ParseArrayValues)
128 | {
129 | int counter = 0;
130 |
131 | foreach (var sequenceElement in sequenceValue.Elements)
132 | {
133 | AddAdditionalField(jObject, new KeyValuePair(counter.ToString(), sequenceElement), key);
134 |
135 | counter++;
136 | }
137 | }
138 |
139 | break;
140 | case StructureValue structureValue:
141 | foreach (LogEventProperty logEventProperty in structureValue.Properties)
142 | {
143 | AddAdditionalField(jObject,
144 | new KeyValuePair(logEventProperty.Name, logEventProperty.Value),
145 | key);
146 | }
147 |
148 | break;
149 | case DictionaryValue dictionaryValue:
150 | if (Options.ParseArrayValues)
151 | {
152 | foreach (KeyValuePair dictionaryValueElement in dictionaryValue.Elements)
153 | {
154 | var renderedKey = RenderPropertyValue(dictionaryValueElement.Key);
155 |
156 | AddAdditionalField(jObject, new KeyValuePair(renderedKey, dictionaryValueElement.Value), key);
157 | }
158 | } else
159 | {
160 | var dict = dictionaryValue.Elements.ToDictionary(k => k.Key.Value, v => RenderPropertyValue(v.Value));
161 | var stringDictionary = JsonSerializer.SerializeToNode(dict, Options.JsonSerializerOptions);
162 |
163 | jObject.Add(key, stringDictionary);
164 | }
165 |
166 | break;
167 | }
168 | }
169 |
170 | private static string RenderPropertyValue(LogEventPropertyValue propertyValue)
171 | {
172 | using TextWriter tw = new StringWriter();
173 |
174 | propertyValue.Render(tw);
175 |
176 | string result = tw.ToString()!;
177 | result = result.Trim('"');
178 |
179 | return result;
180 | }
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core/MessageBuilders/IMessageBuilder.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Events;
2 | using System.Text.Json.Nodes;
3 |
4 | namespace Serilog.Sinks.Graylog.Core.MessageBuilders
5 | {
6 | ///
7 | /// Build json message for graylog
8 | ///
9 | public interface IMessageBuilder
10 | {
11 | ///
12 | /// Builds the specified log event.
13 | ///
14 | /// The log event.
15 | ///
16 | JsonObject Build(LogEvent logEvent);
17 | }
18 |
19 | ///
20 | /// Builder type
21 | ///
22 | public enum BuilderType
23 | {
24 | ///
25 | /// Exception Builder
26 | ///
27 | Exception,
28 | ///
29 | /// Message Builder
30 | ///
31 | Message
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core/Serilog.Sinks.Graylog.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | 11.0
4 |
5 |
6 | net7.0;net6.0;netstandard2.0
7 | true
8 | full
9 | enable
10 | true
11 |
12 | true
13 | key.snk
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core/Transport/DnsResolver.cs:
--------------------------------------------------------------------------------
1 | using System.Net;
2 | using System.Threading.Tasks;
3 |
4 | namespace Serilog.Sinks.Graylog.Core.Transport
5 | {
6 | using System.Linq;
7 | using System.Net.Sockets;
8 |
9 | ///
10 | /// Base class for resolve dns
11 | ///
12 | public interface IDnsInfoProvider
13 | {
14 | ///
15 | /// Gets the host addresses.
16 | ///
17 | /// The host name or address.
18 | ///
19 | Task GetHostAddresses(string hostNameOrAddress);
20 | Task GetIpAddress(string hostNameOrAddress);
21 | }
22 |
23 | public class DnsWrapper : IDnsInfoProvider
24 | {
25 | ///
26 | /// Gets the host addresses.
27 | ///
28 | /// The host name or address.
29 | ///
30 | /// When resolve trows exception.
31 | public Task GetHostAddresses(string hostNameOrAddress)
32 | {
33 | return Dns.GetHostAddressesAsync(hostNameOrAddress);
34 | }
35 |
36 | public async Task GetIpAddress(string hostNameOrAddress)
37 | {
38 | if (string.IsNullOrEmpty(hostNameOrAddress))
39 | {
40 | return default;
41 | }
42 |
43 | var addresses = await GetHostAddresses(hostNameOrAddress).ConfigureAwait(false);
44 | var result = addresses.FirstOrDefault(c => c.AddressFamily == AddressFamily.InterNetwork);
45 | return result;
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core/Transport/Http/HttpTransport.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 |
4 | namespace Serilog.Sinks.Graylog.Core.Transport.Http
5 | {
6 | public class HttpTransport : ITransport
7 | {
8 | private readonly ITransportClient _transportClient;
9 |
10 | public HttpTransport(ITransportClient transportClient)
11 | {
12 | _transportClient = transportClient;
13 | }
14 |
15 | public Task Send(string message)
16 | {
17 | return _transportClient.Send(message);
18 | }
19 |
20 | public void Dispose()
21 | {
22 | Dispose(true);
23 | GC.SuppressFinalize(this);
24 | }
25 |
26 | protected virtual void Dispose(bool disposing)
27 | {
28 | if (disposing)
29 | {
30 | _transportClient?.Dispose();
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core/Transport/Http/HttpTransportClient.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Debugging;
2 | using Serilog.Sinks.Graylog.Core.Helpers;
3 | using System;
4 | using System.Net.Http;
5 | using System.Net.Http.Headers;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace Serilog.Sinks.Graylog.Core.Transport.Http
10 | {
11 | public class HttpTransportClient : ITransportClient
12 | {
13 | private const string _defaultHttpUriPath = "/gelf";
14 |
15 | private HttpClient? _httpClient;
16 |
17 | private readonly GraylogSinkOptionsBase options;
18 |
19 | public HttpTransportClient(GraylogSinkOptionsBase options)
20 | {
21 | this.options = options;
22 | }
23 |
24 | protected virtual HttpClient CreateHttpClient() => new();
25 |
26 | protected virtual void ConfigureHttpClient(HttpClient httpClient)
27 | {
28 | if (string.IsNullOrEmpty(options.HostnameOrAddress))
29 | {
30 | throw new InvalidOperationException("The HostnameOrAddress value must be set.");
31 | }
32 |
33 | var builder = new UriBuilder(options.HostnameOrAddress)
34 | {
35 | Port = options.Port.GetValueOrDefault(443)
36 | };
37 |
38 | if (options.UseSsl)
39 | {
40 | builder.Scheme = "https";
41 | }
42 |
43 | httpClient.BaseAddress = builder.Uri;
44 |
45 | httpClient.DefaultRequestHeaders.ExpectContinue = false;
46 | httpClient.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue { NoCache = true };
47 |
48 | var authenticationHeaderValue = new HttpBasicAuthenticationGenerator(options.UsernameInHttp, options.PasswordInHttp).Generate();
49 |
50 | if (authenticationHeaderValue != null)
51 | {
52 | httpClient.DefaultRequestHeaders.Authorization = authenticationHeaderValue;
53 | }
54 | }
55 |
56 | private void EnsureHttpClient()
57 | {
58 | if (_httpClient == null)
59 | {
60 | _httpClient = CreateHttpClient();
61 |
62 | ConfigureHttpClient(_httpClient);
63 | }
64 | }
65 |
66 | public async Task Send(string message)
67 | {
68 | EnsureHttpClient();
69 |
70 | var content = new StringContent(message, Encoding.UTF8, "application/json");
71 |
72 | HttpResponseMessage result = await _httpClient!.PostAsync(_defaultHttpUriPath, content).ConfigureAwait(false);
73 |
74 | if (!result.IsSuccessStatusCode)
75 | {
76 | SelfLog.WriteLine("Unable send log message to graylog via HTTP transport");
77 | }
78 | }
79 |
80 | public void Dispose()
81 | {
82 | Dispose(true);
83 | GC.SuppressFinalize(this);
84 | }
85 |
86 | protected virtual void Dispose(bool disposing)
87 | {
88 | if (disposing)
89 | {
90 | _httpClient?.Dispose();
91 | }
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core/Transport/ITransport.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 |
4 | namespace Serilog.Sinks.Graylog.Core.Transport
5 | {
6 | ///
7 | /// The Transport interface
8 | ///
9 | public interface ITransport : IDisposable
10 | {
11 | ///
12 | /// Sends the specified target.
13 | ///
14 | /// The message.
15 | Task Send(string message);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core/Transport/ITransportClient'.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 |
4 | namespace Serilog.Sinks.Graylog.Core.Transport
5 | {
6 | ///
7 | /// The Transport client interface
8 | ///
9 | ///
10 | public interface ITransportClient : IDisposable
11 | {
12 | ///
13 | /// Sends the specified payload.
14 | ///
15 | /// The payload.
16 | Task Send(T payload);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core/Transport/Tcp/TcpTransport.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 |
4 | namespace Serilog.Sinks.Graylog.Core.Transport.Tcp
5 | {
6 | public class TcpTransport : ITransport
7 | {
8 | private readonly ITransportClient _tcpClient;
9 |
10 | ///
11 | public TcpTransport(ITransportClient tcpClient)
12 | {
13 | _tcpClient = tcpClient;
14 | }
15 |
16 | ///
17 | public Task Send(string message)
18 | {
19 | #if NET
20 | var payload = new byte[message.Length + 1];
21 | System.Text.Encoding.UTF8.GetBytes(message.AsSpan(), payload.AsSpan());
22 | payload[^1] = 0x00;
23 |
24 | return _tcpClient.Send(payload);
25 | #else
26 | var payload = System.Text.Encoding.UTF8.GetBytes(message);
27 |
28 | Array.Resize(ref payload, payload.Length + 1);
29 | payload[payload.Length - 1] = 0x00;
30 |
31 | return _tcpClient.Send(payload);
32 | #endif
33 | }
34 |
35 | ///
36 | public void Dispose()
37 | {
38 | Dispose(true);
39 | GC.SuppressFinalize(this);
40 | }
41 |
42 | protected virtual void Dispose(bool disposing)
43 | {
44 | if (disposing)
45 | {
46 | _tcpClient?.Dispose();
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core/Transport/Tcp/TcpTransportClient.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Debugging;
2 | using System;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Net.Security;
7 | using System.Net.Sockets;
8 | using System.Threading.Tasks;
9 |
10 | namespace Serilog.Sinks.Graylog.Core.Transport.Tcp
11 | {
12 | public class TcpTransportClient : ITransportClient
13 | {
14 | private const int DefaultPort = 12201;
15 |
16 | private Stream? _stream;
17 |
18 | private readonly GraylogSinkOptionsBase _options;
19 | private readonly IDnsInfoProvider _dnsInfoProvider;
20 | private readonly TcpClient _client;
21 |
22 | ///
23 | public TcpTransportClient(GraylogSinkOptionsBase options, IDnsInfoProvider dnsInfoProvider)
24 | {
25 | _options = options;
26 | _dnsInfoProvider = dnsInfoProvider;
27 |
28 | _client = new TcpClient();
29 | }
30 |
31 | ///
32 | public async Task Send(byte[] payload)
33 | {
34 | await EnsureConnection().ConfigureAwait(false);
35 |
36 | #if NETSTANDARD2_0
37 | await _stream!.WriteAsync(payload, 0, payload.Length).ConfigureAwait(false);
38 | #else
39 | await _stream!.WriteAsync(payload).ConfigureAwait(false);
40 | #endif
41 |
42 | await _stream.FlushAsync().ConfigureAwait(false);
43 | }
44 |
45 | private async Task EnsureConnection()
46 | {
47 | if (!_client.Connected)
48 | {
49 | await Connect().ConfigureAwait(false);
50 | }
51 | }
52 |
53 | private async Task Connect()
54 | {
55 | IPAddress? _address = await _dnsInfoProvider.GetIpAddress(_options.HostnameOrAddress!);
56 | if (_address == default)
57 | {
58 | SelfLog.WriteLine("IP address could not be resolved.");
59 | return;
60 | }
61 |
62 | int port = _options.Port.GetValueOrDefault(DefaultPort);
63 | string? sslHost = _options.UseSsl ? _options.HostnameOrAddress : null;
64 |
65 | await _client.ConnectAsync(_address, port).ConfigureAwait(false);
66 |
67 | _stream = _client.GetStream();
68 |
69 | if (!string.IsNullOrWhiteSpace(sslHost))
70 | {
71 | var _sslStream = new SslStream(_stream, false);
72 |
73 | await _sslStream.AuthenticateAsClientAsync(sslHost).ConfigureAwait(false);
74 |
75 | if (_sslStream.RemoteCertificate != null)
76 | {
77 | SelfLog.WriteLine("Remote cert was issued to {0} and is valid from {1} until {2}.",
78 | _sslStream.RemoteCertificate.Subject,
79 | _sslStream.RemoteCertificate.GetEffectiveDateString(),
80 | _sslStream.RemoteCertificate.GetExpirationDateString());
81 |
82 | _stream = _sslStream;
83 | } else
84 | {
85 | SelfLog.WriteLine("Remote certificate is null.");
86 | }
87 | }
88 | }
89 |
90 | ///
91 | public void Dispose()
92 | {
93 | Dispose(true);
94 | GC.SuppressFinalize(this);
95 | }
96 |
97 | protected virtual void Dispose(bool disposing)
98 | {
99 | if (disposing)
100 | {
101 | _stream?.Dispose();
102 | _client.Dispose();
103 | }
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core/Transport/TransportType.cs:
--------------------------------------------------------------------------------
1 | namespace Serilog.Sinks.Graylog.Core.Transport
2 | {
3 | public enum TransportType
4 | {
5 | Udp,
6 | Http,
7 | Tcp,
8 | // Custom implementations of transports
9 | Custom
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core/Transport/Udp/ChunkSettings.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Sinks.Graylog.Core.Helpers;
2 |
3 | namespace Serilog.Sinks.Graylog.Core.Transport.Udp
4 | {
5 | public sealed class ChunkSettings
6 | {
7 | ///
8 | public ChunkSettings(MessageIdGeneratorType messageIdGeneratorType, int maxMessageSizeInUdp)
9 | {
10 | MessageIdGeneratorType = messageIdGeneratorType;
11 | MaxMessageSizeInUdp = maxMessageSizeInUdp;
12 | }
13 |
14 | public MessageIdGeneratorType MessageIdGeneratorType { get; }
15 |
16 | ///
17 | /// The prefix size
18 | ///
19 | ///
20 | public const byte PrefixSize = 12;
21 |
22 | ///
23 | /// The maximum number of chunks allowed
24 | ///
25 | ///
26 | public const byte MaxNumberOfChunksAllowed = 128;
27 |
28 | ///
29 | /// The maximum message size in UDP
30 | ///
31 | /// UDP chunks are usually limited to a size of 8192 bytes
32 | ///
33 | ///
34 | public int MaxMessageSizeInUdp { get; }
35 |
36 | public static readonly byte[] GelfMagicBytes = { 0x1e, 0x0f };
37 |
38 | ///
39 | /// The maximum message size in chunk
40 | ///
41 | public int MaxMessageSizeInChunk => MaxMessageSizeInUdp - PrefixSize;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core/Transport/Udp/DataToChunkConverter.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Sinks.Graylog.Core.Helpers;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 |
6 | namespace Serilog.Sinks.Graylog.Core.Transport.Udp
7 | {
8 | ///
9 | /// Converts Data to udp chunks
10 | ///
11 | public interface IDataToChunkConverter
12 | {
13 | ///
14 | /// Converts to chunks.
15 | ///
16 | /// The message.
17 | /// array of chunks to save
18 | /// message was too long
19 | /// message was too long
20 | IList ConvertToChunks(byte[] message);
21 | }
22 |
23 | ///
24 | public sealed class DataToChunkConverter : IDataToChunkConverter
25 | {
26 | private readonly ChunkSettings _settings;
27 | private readonly IMessageIdGeneratorResolver _generatorResolver;
28 |
29 | ///
30 | /// Initializes a new instance of the class.
31 | ///
32 | /// The settings.
33 | /// The generator resolver.
34 | public DataToChunkConverter(ChunkSettings settings,
35 | IMessageIdGeneratorResolver generatorResolver)
36 | {
37 | _settings = settings;
38 | _generatorResolver = generatorResolver;
39 | }
40 |
41 | ///
42 | /// Converts to chunks.
43 | ///
44 | /// The message.
45 | /// array of chunks to save
46 | /// message was too long
47 | /// message was too long
48 | public IList ConvertToChunks(byte[] message)
49 | {
50 | int messageLength = message.Length;
51 | if (messageLength <= _settings.MaxMessageSizeInUdp)
52 | {
53 | return new List(1) { message };
54 | }
55 |
56 | int chunksCount = messageLength / _settings.MaxMessageSizeInChunk + 1;
57 | if (chunksCount > ChunkSettings.MaxNumberOfChunksAllowed)
58 | {
59 | throw new ArgumentException("message was too long", nameof(message));
60 | }
61 |
62 | IMessageIdGenerator messageIdGenerator = _generatorResolver.Resolve(_settings.MessageIdGeneratorType);
63 | byte[] messageId = messageIdGenerator.GenerateMessageId(message);
64 |
65 | var result = new List();
66 | for (byte i = 0; i < chunksCount; i++)
67 | {
68 | IList chunkHeader = ConstructChunkHeader(messageId, i, (byte)chunksCount);
69 |
70 | int skip = i * _settings.MaxMessageSizeInChunk;
71 | byte[] chunkData = message.Skip(skip).Take(_settings.MaxMessageSizeInChunk).ToArray();
72 |
73 | var messageChunkFull = new List(chunkHeader.Count + chunkData.Length);
74 | messageChunkFull.AddRange(chunkHeader);
75 | messageChunkFull.AddRange(chunkData);
76 | result.Add(messageChunkFull.ToArray());
77 | }
78 | return result;
79 | }
80 |
81 | private static IList ConstructChunkHeader(byte[] messageId, byte chunkNumber, byte chunksCount)
82 | {
83 | var result = new List(ChunkSettings.PrefixSize);
84 | result.AddRange(ChunkSettings.GelfMagicBytes);
85 | result.AddRange(messageId);
86 | result.Add(chunkNumber);
87 | result.Add(chunksCount);
88 | return result;
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core/Transport/Udp/UdpTransport.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Sinks.Graylog.Core.Extensions;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 |
7 | namespace Serilog.Sinks.Graylog.Core.Transport.Udp
8 | {
9 | public sealed class UdpTransport : ITransport
10 | {
11 | private readonly ITransportClient _transportClient;
12 | private readonly IDataToChunkConverter _chunkConverter;
13 | private readonly GraylogSinkOptionsBase _options;
14 |
15 | ///
16 | /// Initializes a new instance of the class.
17 | ///
18 | /// The transport client.
19 | ///
20 | public UdpTransport(ITransportClient transportClient, IDataToChunkConverter chunkConverter, GraylogSinkOptionsBase options)
21 | {
22 | _transportClient = transportClient;
23 | _chunkConverter = chunkConverter;
24 | _options = options;
25 | }
26 |
27 | ///
28 | /// Sends the specified target.
29 | ///
30 | /// The message.
31 | /// message was too long
32 | public Task Send(string message)
33 | {
34 | var payload = _options.UseGzip ? message.ToGzip() : message.ToByteArray();
35 | IList chunks = _chunkConverter.ConvertToChunks(payload);
36 |
37 | IEnumerable sendTasks = chunks.Select(c => _transportClient.Send(c));
38 | return Task.WhenAll(sendTasks.ToArray());
39 | }
40 |
41 | public void Dispose()
42 | {
43 | Dispose(true);
44 | GC.SuppressFinalize(this);
45 | }
46 |
47 | private void Dispose(bool disposing)
48 | {
49 | if (disposing)
50 | {
51 | _transportClient.Dispose();
52 | }
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core/Transport/Udp/UdpTransportClient.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Net;
4 | using System.Net.Sockets;
5 | using System.Threading.Tasks;
6 |
7 | namespace Serilog.Sinks.Graylog.Core.Transport.Udp
8 | {
9 | using Debugging;
10 |
11 | ///
12 | /// Udp transport client
13 | ///
14 | ///
15 | public sealed class UdpTransportClient : ITransportClient
16 | {
17 | private IPEndPoint? _ipEndPoint;
18 |
19 | private readonly GraylogSinkOptionsBase _options;
20 | private readonly IDnsInfoProvider _dnsInfoProvider;
21 | private readonly UdpClient _client;
22 |
23 | public UdpTransportClient(GraylogSinkOptionsBase options, IDnsInfoProvider dnsInfoProvider)
24 | {
25 | _options = options;
26 | _dnsInfoProvider = dnsInfoProvider;
27 |
28 | _client = new UdpClient();
29 | }
30 |
31 | private async Task EnsureTarget()
32 | {
33 | if (_ipEndPoint == null)
34 | {
35 | var ipAddress = await _dnsInfoProvider.GetIpAddress(_options.HostnameOrAddress!).ConfigureAwait(false);
36 | if (ipAddress == default)
37 | {
38 | SelfLog.WriteLine("IP address could not be resolved.");
39 | return;
40 | }
41 | _ipEndPoint = new IPEndPoint(ipAddress, _options.Port.GetValueOrDefault());
42 | }
43 | }
44 |
45 | ///
46 | /// Sends the specified payload.
47 | ///
48 | /// The payload.
49 | public async Task Send(byte[] payload)
50 | {
51 | await EnsureTarget().ConfigureAwait(false);
52 |
53 | await _client.SendAsync(payload, payload.Length, _ipEndPoint).ConfigureAwait(false);
54 | }
55 |
56 | public void Dispose() => _client.Dispose();
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core/TransportFactory.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Sinks.Graylog.Core.Helpers;
2 | using Serilog.Sinks.Graylog.Core.MessageBuilders;
3 | using Serilog.Sinks.Graylog.Core.Transport;
4 | using Serilog.Sinks.Graylog.Core.Transport.Http;
5 | using Serilog.Sinks.Graylog.Core.Transport.Tcp;
6 | using Serilog.Sinks.Graylog.Core.Transport.Udp;
7 | using System;
8 | using System.Collections.Generic;
9 | using System.Net;
10 | using SinkTransportType = Serilog.Sinks.Graylog.Core.Transport.TransportType;
11 |
12 | namespace Serilog.Sinks.Graylog.Core
13 | {
14 | public interface ISinkComponentsBuilder
15 | {
16 | ITransport MakeTransport();
17 | IGelfConverter MakeGelfConverter();
18 | }
19 |
20 | public class SinkComponentsBuilder : ISinkComponentsBuilder
21 | {
22 | private readonly GraylogSinkOptionsBase _options;
23 | private readonly Dictionary> _builders;
24 |
25 | public SinkComponentsBuilder(GraylogSinkOptionsBase options)
26 | {
27 | _options = options;
28 |
29 | _builders = new Dictionary>
30 | {
31 | [BuilderType.Exception] = new(() =>
32 | {
33 | string hostName = Dns.GetHostName();
34 | return new ExceptionMessageBuilder(hostName, _options);
35 | }),
36 | [BuilderType.Message] = new(() =>
37 | {
38 | string hostName = Dns.GetHostName();
39 | return new GelfMessageBuilder(hostName, _options);
40 | })
41 | };
42 | }
43 |
44 | public ITransport MakeTransport()
45 | {
46 | switch (_options.TransportType)
47 | {
48 | case SinkTransportType.Udp:
49 | var chunkSettings = new ChunkSettings(_options.MessageGeneratorType, _options.MaxMessageSizeInUdp);
50 | IDataToChunkConverter chunkConverter = new DataToChunkConverter(chunkSettings, new MessageIdGeneratorResolver());
51 |
52 | var udpClient = new UdpTransportClient(_options, new DnsWrapper());
53 | var udpTransport = new UdpTransport(udpClient, chunkConverter, _options);
54 |
55 | return udpTransport;
56 | case SinkTransportType.Http:
57 | var httpClient = new HttpTransportClient(_options);
58 |
59 | return new HttpTransport(httpClient);
60 | case SinkTransportType.Tcp:
61 | var tcpClient = new TcpTransportClient(_options, new DnsWrapper());
62 |
63 | return new TcpTransport(tcpClient);
64 | case SinkTransportType.Custom:
65 | if (_options.TransportFactory == null)
66 | {
67 | throw new InvalidOperationException("The TransportFactory value must have a value.");
68 | }
69 |
70 | return _options.TransportFactory();
71 | default:
72 | throw new ArgumentOutOfRangeException(nameof(_options), _options.TransportType, null);
73 | }
74 | }
75 |
76 | public IGelfConverter MakeGelfConverter() => _options.GelfConverter ?? new GelfConverter(_builders);
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Core/key.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serilog-contrib/serilog-sinks-graylog/64532fa1aab7a8b3dc5c94e3294a570d6698e77d/src/Serilog.Sinks.Graylog.Core/key.snk
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Tests/Configurations/AppSettingsWithGraylogSinkContainingHostProperty.json:
--------------------------------------------------------------------------------
1 | {
2 | "Serilog": {
3 | "WriteTo": [
4 | {
5 | "Name": "Console",
6 | "Args": {
7 | "restrictedToMinimumLevel": "Information",
8 | "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss}] [{Level:u4}] {Message:lj} {NewLine}{Exception}"
9 | }
10 | },
11 | {
12 | "Name": "Graylog",
13 | "Args": {
14 | "facility": "MicroserviceTemplate.Host.WebApi",
15 | "hostnameOrAddress": "http://localhost",
16 | "port": 12201,
17 | "transportType": "Http",
18 | "host": "my_host",
19 | "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss}] [{Level:u4}] {Message:lj} {NewLine}{Exception}"
20 | }
21 | }
22 | ]
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Tests/GraylogSinkFixture.cs:
--------------------------------------------------------------------------------
1 | using Moq;
2 | using Newtonsoft.Json.Linq;
3 | using Serilog.Events;
4 | using Serilog.Parsing;
5 | using Serilog.Sinks.Graylog.Core;
6 | using Serilog.Sinks.Graylog.Core.Transport;
7 | using System;
8 | using System.Collections.Generic;
9 | using System.Threading.Tasks;
10 | using Xunit;
11 |
12 | namespace Serilog.Sinks.Graylog.Tests
13 | {
14 | public class GraylogSinkFixture
15 | {
16 | [Fact(Skip = "This test not work anymore because IMessageBuilder gets from internal dictionary")]
17 | public void WhenEmit_ThenSendData()
18 | {
19 | var gelfConverter = new Mock();
20 | var transport = new Mock();
21 |
22 | var options = new GraylogSinkOptions
23 | {
24 | GelfConverter = gelfConverter.Object,
25 | TransportType = TransportType.Udp,
26 | HostnameOrAddress = "localhost"
27 | };
28 |
29 | GraylogSink target = new(options);
30 |
31 | var logEvent = new LogEvent(DateTimeOffset.Now, LogEventLevel.Fatal, null,
32 | new MessageTemplate("O_o", new List()), new List());
33 |
34 | var jObject = new JObject();
35 | transport.Setup(c => c.Send(jObject.ToString(Newtonsoft.Json.Formatting.None))).Returns(Task.CompletedTask);
36 |
37 |
38 | //gelfConverter.Setup(c => c.GetGelfJson(logEvent)).Returns(jObject);
39 |
40 | target.Emit(logEvent);
41 |
42 | gelfConverter.VerifyAll();
43 |
44 | transport.Verify(c => c.Send(It.IsAny()));
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Tests/IntegrateSinkTestWithHttp.cs:
--------------------------------------------------------------------------------
1 | using AutoFixture;
2 | using Serilog.Events;
3 | using Serilog.Exceptions;
4 | using Serilog.Sinks.Graylog.Core.Helpers;
5 | using Serilog.Sinks.Graylog.Core.Transport;
6 | using Serilog.Sinks.Graylog.Tests.ComplexIntegrationTest;
7 | using System;
8 | using System.Linq;
9 | using Xunit;
10 |
11 | namespace Serilog.Sinks.Graylog.Tests
12 | {
13 | [Trait("Category", "Integration")]
14 | public class IntegrateSinkTestWithHttp
15 | {
16 | [Fact]
17 | [Trait("Category", "Integration")]
18 | public void TestComplex()
19 | {
20 | var loggerConfig = new LoggerConfiguration();
21 |
22 | loggerConfig.WriteTo.Graylog(new GraylogSinkOptions
23 | {
24 | ShortMessageMaxLength = 50,
25 | MinimumLogEventLevel = LogEventLevel.Information,
26 | TransportType = TransportType.Http,
27 | Facility = "VolkovTestFacility",
28 | HostnameOrAddress = "http://logs.aeroclub.int",
29 | Port = 12201
30 | });
31 |
32 | var logger = loggerConfig.CreateLogger();
33 |
34 | var test = new TestClass
35 | {
36 | Id = 1,
37 | Bar = new Bar
38 | {
39 | Id = 2,
40 | Prop = "123"
41 | },
42 | TestPropertyOne = "1",
43 | TestPropertyThree = "3",
44 | TestPropertyTwo = "2"
45 | };
46 |
47 | logger.Information("SomeComplexTestEntry {@test}", test);
48 | }
49 |
50 | [Fact]
51 | public void WhenHostIsWrong_ThenLoggerCreationShouldNotBeFail()
52 | {
53 | var loggerConfig = new LoggerConfiguration();
54 |
55 | loggerConfig.WriteTo.Graylog(new GraylogSinkOptions
56 | {
57 | ShortMessageMaxLength = 50,
58 | MinimumLogEventLevel = LogEventLevel.Information,
59 | TransportType = TransportType.Http,
60 | Facility = "VolkovTestFacility",
61 | HostnameOrAddress = "abracadabra",
62 | Port = 12201,
63 | //SerializerSettings = new JsonSerializerSettings()
64 | });
65 |
66 | var logger = loggerConfig.CreateLogger();
67 |
68 | var test = new TestClass
69 | {
70 | Id = 1,
71 | Bar = new Bar
72 | {
73 | Id = 2,
74 | Prop = "123"
75 | },
76 | TestPropertyOne = "1",
77 | TestPropertyThree = "3",
78 | TestPropertyTwo = "2",
79 | EnumVal = TestEnumOne.Three,
80 | SomeTestDateTime = DateTime.Now
81 |
82 |
83 | };
84 |
85 | logger.Information("SomeComplexTestEntry {@test}", test);
86 | }
87 |
88 | [Fact]
89 | [Trait("Category", "Integration")]
90 | public void LogInformationWithLevel()
91 | {
92 | var fixture = new Fixture();
93 | fixture.Behaviors.Clear();
94 | fixture.Behaviors.Add(new OmitOnRecursionBehavior(1));
95 | var profile = fixture.Create();
96 |
97 | var loggerConfig = new LoggerConfiguration();
98 |
99 | loggerConfig.WriteTo.Graylog(new GraylogSinkOptions
100 | {
101 | MinimumLogEventLevel = LogEventLevel.Error,
102 | MessageGeneratorType = MessageIdGeneratorType.Timestamp,
103 | TransportType = TransportType.Http,
104 | Facility = "VolkovTestFacility",
105 | HostnameOrAddress = "http://logs.aeroclub.int",
106 | Port = 12201
107 | });
108 |
109 | var logger = loggerConfig.CreateLogger();
110 |
111 | logger.Information("battle profile: {@BattleProfile}", profile);
112 | }
113 |
114 |
115 | [Fact]
116 | [Trait("Category", "Integration")]
117 | public void LogInformationWithOneProfile()
118 | {
119 | var fixture = new Fixture();
120 | fixture.Behaviors.Clear();
121 | fixture.Behaviors.Add(new OmitOnRecursionBehavior(1));
122 | var profile = fixture.Create();
123 |
124 | var loggerConfig = new LoggerConfiguration();
125 |
126 | loggerConfig.WriteTo.Graylog(new GraylogSinkOptions
127 | {
128 | MinimumLogEventLevel = LogEventLevel.Information,
129 | MessageGeneratorType = MessageIdGeneratorType.Timestamp,
130 | TransportType = TransportType.Http,
131 | Facility = "VolkovTestFacility",
132 | HostnameOrAddress = "http://logs.aeroclub.int",
133 | Port = 12201
134 | });
135 |
136 | var logger = loggerConfig.CreateLogger();
137 |
138 | logger.Information("battle profile: {@BattleProfile}", profile);
139 | }
140 |
141 | [Fact]
142 | [Trait("Ignore", "Integration")]
143 | public void Log10Profiles()
144 | {
145 | var fixture = new Fixture();
146 | fixture.Behaviors.Clear();
147 | fixture.Behaviors.Add(new OmitOnRecursionBehavior(1));
148 | var profiles = fixture.CreateMany(10).ToList();
149 |
150 | var loggerConfig = new LoggerConfiguration();
151 |
152 | loggerConfig.WriteTo.Graylog(new GraylogSinkOptions
153 | {
154 | MinimumLogEventLevel = LogEventLevel.Information,
155 | MessageGeneratorType = MessageIdGeneratorType.Timestamp,
156 | TransportType = TransportType.Http,
157 | Facility = "VolkovTestFacility",
158 | HostnameOrAddress = "http://logs.aeroclub.int",
159 | Port = 12201
160 | });
161 |
162 | var logger = loggerConfig.CreateLogger();
163 |
164 | profiles.AsParallel().ForAll(profile =>
165 | {
166 | logger.Information("TestSend {@BattleProfile}", profile);
167 | });
168 | }
169 |
170 | [Fact]
171 | [Trait("Category", "Integration")]
172 | public void LogInformationWithUsernamePassword()
173 | {
174 | var fixture = new Fixture();
175 | fixture.Behaviors.Clear();
176 | fixture.Behaviors.Add(new OmitOnRecursionBehavior(1));
177 | var profile = fixture.Create();
178 |
179 | var loggerConfig = new LoggerConfiguration();
180 |
181 | loggerConfig.WriteTo.Graylog(new GraylogSinkOptions
182 | {
183 | MinimumLogEventLevel = LogEventLevel.Information,
184 | MessageGeneratorType = MessageIdGeneratorType.Timestamp,
185 | TransportType = TransportType.Http,
186 | Facility = "VolkovTestFacility",
187 | HostnameOrAddress = "http://logs.aeroclub.int",
188 | Port = 12201,
189 | UsernameInHttp = "username",
190 | PasswordInHttp = "password"
191 | });
192 |
193 | var logger = loggerConfig.CreateLogger();
194 |
195 | logger.Information("battle profile: {@BattleProfile}", profile);
196 | }
197 |
198 | [Fact]
199 | [Trait("Category", "Integration")]
200 | public void LogInformationWithHostnameOrAddressEndsWithoutPath()
201 | {
202 | var fixture = new Fixture();
203 | fixture.Behaviors.Clear();
204 | fixture.Behaviors.Add(new OmitOnRecursionBehavior(1));
205 | var profile = fixture.Create();
206 |
207 | var loggerConfig = new LoggerConfiguration();
208 |
209 | loggerConfig.WriteTo.Graylog(new GraylogSinkOptions
210 | {
211 | MinimumLogEventLevel = LogEventLevel.Information,
212 | MessageGeneratorType = MessageIdGeneratorType.Timestamp,
213 | TransportType = TransportType.Http,
214 | Facility = "VolkovTestFacility",
215 | HostnameOrAddress = "http://logs.aeroclub.int",
216 | Port = 12201
217 | });
218 |
219 | var logger = loggerConfig.CreateLogger();
220 |
221 | logger.Information("battle profile: {@BattleProfile}", profile);
222 | }
223 |
224 | [Fact]
225 | [Trait("Category", "Integration")]
226 | public void LogInformationWithHostnameOrAddressEndsWithSlash()
227 | {
228 | var fixture = new Fixture();
229 | fixture.Behaviors.Clear();
230 | fixture.Behaviors.Add(new OmitOnRecursionBehavior(1));
231 | var profile = fixture.Create();
232 |
233 | var loggerConfig = new LoggerConfiguration();
234 |
235 | loggerConfig.WriteTo.Graylog(new GraylogSinkOptions
236 | {
237 | MinimumLogEventLevel = LogEventLevel.Information,
238 | MessageGeneratorType = MessageIdGeneratorType.Timestamp,
239 | TransportType = TransportType.Http,
240 | Facility = "VolkovTestFacility",
241 | HostnameOrAddress = "http://logs.aeroclub.int/",
242 | Port = 12201
243 | });
244 |
245 | var logger = loggerConfig.CreateLogger();
246 |
247 | logger.Information("battle profile: {@BattleProfile}", profile);
248 | }
249 |
250 | [Fact]
251 | [Trait("Category", "Integration")]
252 | public void LogInformationWithHostnameOrAddressEndsWithPath()
253 | {
254 | var fixture = new Fixture();
255 | fixture.Behaviors.Clear();
256 | fixture.Behaviors.Add(new OmitOnRecursionBehavior(1));
257 | var profile = fixture.Create();
258 |
259 | var loggerConfig = new LoggerConfiguration();
260 |
261 | loggerConfig.WriteTo.Graylog(new GraylogSinkOptions
262 | {
263 | MinimumLogEventLevel = LogEventLevel.Information,
264 | MessageGeneratorType = MessageIdGeneratorType.Timestamp,
265 | TransportType = TransportType.Http,
266 | Facility = "VolkovTestFacility",
267 | HostnameOrAddress = "http://logs.aeroclub.int/testgelf",
268 | Port = 12201
269 | });
270 |
271 | var logger = loggerConfig.CreateLogger();
272 |
273 | logger.Information("battle profile: {@BattleProfile}", profile);
274 | }
275 |
276 | [Fact]
277 | [Trait("Category", "Integration")]
278 | public void TestException()
279 | {
280 | var loggerConfig = new LoggerConfiguration();
281 |
282 | loggerConfig
283 | .Enrich.WithExceptionDetails()
284 | .WriteTo.Graylog(new GraylogSinkOptions
285 | {
286 | MinimumLogEventLevel = LogEventLevel.Information,
287 | MessageGeneratorType = MessageIdGeneratorType.Timestamp,
288 | TransportType = TransportType.Http,
289 | Facility = "VolkovTestFacility",
290 | HostnameOrAddress = "http://logs.aeroclub.int",
291 | Port = 12201
292 | });
293 |
294 | var test = new TestClass
295 | {
296 | Id = 1,
297 | SomeTestDateTime = DateTime.UtcNow,
298 | Bar = new Bar
299 | {
300 | Id = 2
301 | },
302 | TestPropertyOne = "1",
303 | TestPropertyThree = "3",
304 | TestPropertyTwo = "2"
305 | };
306 |
307 |
308 | var logger = loggerConfig.CreateLogger();
309 |
310 | try
311 | {
312 | try
313 | {
314 | throw new InvalidOperationException("Level One exception");
315 | } catch (Exception exc)
316 | {
317 | throw new NotImplementedException("Nested Exception", exc);
318 | }
319 | } catch (Exception exc)
320 | {
321 | logger.Error(exc, "test exception with object {@test}", test);
322 | }
323 | }
324 | }
325 | }
326 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Tests/IntegrateSinkTestWithTcp.cs:
--------------------------------------------------------------------------------
1 | using AutoFixture;
2 | using Serilog.Events;
3 | using Serilog.Sinks.Graylog.Core.Helpers;
4 | using Serilog.Sinks.Graylog.Core.Transport;
5 | using Serilog.Sinks.Graylog.Tests.ComplexIntegrationTest;
6 | using System;
7 | using System.Linq;
8 | using System.Threading.Tasks;
9 | using Xunit;
10 |
11 | namespace Serilog.Sinks.Graylog.Tests
12 | {
13 | [Trait("Category", "Integration")]
14 | public class IntegrateSinkTestWithTcp
15 | {
16 | [Fact]
17 | [Trait("Category", "Integration")]
18 | public void VerifyLoggerVerbocity()
19 | {
20 | var loggerConfig = new LoggerConfiguration();
21 |
22 | loggerConfig.WriteTo.Graylog(new GraylogSinkOptions
23 | {
24 | ShortMessageMaxLength = 50,
25 | MinimumLogEventLevel = LogEventLevel.Fatal,
26 | Facility = "VolkovTestFacility",
27 | HostnameOrAddress = "logs.aeroclub.int",
28 | Port = 12202,
29 | TransportType = TransportType.Tcp
30 |
31 | });
32 |
33 | var logger = loggerConfig.CreateLogger();
34 |
35 | var test = new TestClass
36 | {
37 | Id = 1,
38 | SomeTestDateTime = DateTime.UtcNow,
39 | Bar = new Bar
40 | {
41 | Id = 2,
42 | Prop = "123",
43 | TestBarBooleanProperty = false
44 |
45 | },
46 | TestClassBooleanProperty = true,
47 | TestPropertyOne = "1",
48 | TestPropertyThree = "3",
49 | TestPropertyTwo = "2"
50 | };
51 |
52 | logger.Information("SomeComplexTestEntry {@test}", test);
53 |
54 | logger.Debug("SomeComplexTestEntry {@test}", test);
55 |
56 | logger.Fatal("SomeComplexTestEntry {@test}", test);
57 |
58 | logger.Error("SomeComplexTestEntry {@test}", test);
59 |
60 | }
61 |
62 | [Fact]
63 | [Trait("Category", "Integration")]
64 | public void TestComplex()
65 | {
66 | var loggerConfig = new LoggerConfiguration();
67 |
68 | loggerConfig.WriteTo.Graylog(new GraylogSinkOptions
69 | {
70 | ShortMessageMaxLength = 50,
71 | MinimumLogEventLevel = LogEventLevel.Information,
72 | TransportType = TransportType.Tcp,
73 | Facility = "VolkovTestFacility",
74 | HostnameOrAddress = "logs.aeroclub.int",
75 | Port = 12202
76 | });
77 |
78 | var logger = loggerConfig.CreateLogger();
79 |
80 | var test = new TestClass
81 | {
82 | Id = 1,
83 | Type = "TCP",
84 | SomeTestDateTime = DateTime.UtcNow,
85 | Bar = new Bar
86 | {
87 | Id = 2,
88 | Prop = "123",
89 | TestBarBooleanProperty = false
90 |
91 | },
92 | TestClassBooleanProperty = true,
93 | TestPropertyOne = "1",
94 | TestPropertyThree = "3",
95 | TestPropertyTwo = "2"
96 | };
97 |
98 | logger.Information("SomeComplexTestEntry {@test}", test);
99 | }
100 |
101 | [Fact()]
102 | [Trait("Category", "Integration")]
103 | public Task SendManyMessages()
104 | {
105 | var fixture = new Fixture();
106 | fixture.Behaviors.Clear();
107 | fixture.Behaviors.Add(new OmitOnRecursionBehavior(1));
108 | var profiles = fixture.CreateMany(10).ToList();
109 |
110 | foreach (var profile in profiles)
111 | {
112 | profile.Type = "TCP";
113 | }
114 |
115 | var loggerConfig = new LoggerConfiguration();
116 |
117 | loggerConfig.WriteTo.Graylog(new GraylogSinkOptions
118 | {
119 | MinimumLogEventLevel = LogEventLevel.Information,
120 | MessageGeneratorType = MessageIdGeneratorType.Md5,
121 | TransportType = TransportType.Tcp,
122 | Facility = "VolkovTestFacility",
123 | HostnameOrAddress = "logs.aeroclub.int",
124 | Port = 12202
125 | });
126 |
127 | var logger = loggerConfig.CreateLogger();
128 |
129 | var tasks = profiles.Select(c =>
130 | {
131 | return Task.Run(() => logger.Information("TestSend {@BattleProfile}", c));
132 | });
133 |
134 |
135 | return Task.WhenAll(tasks.ToArray());
136 | }
137 |
138 | [Fact]
139 | [Trait("Category", "Integration")]
140 | public void TestSimple()
141 | {
142 | var fixture = new Fixture();
143 | fixture.Behaviors.Clear();
144 | fixture.Behaviors.Add(new OmitOnRecursionBehavior(1));
145 | var profile = fixture.Create();
146 |
147 | var loggerConfig = new LoggerConfiguration();
148 |
149 | loggerConfig.WriteTo.Graylog(new GraylogSinkOptions
150 | {
151 | MinimumLogEventLevel = LogEventLevel.Information,
152 | MessageGeneratorType = MessageIdGeneratorType.Timestamp,
153 | TransportType = TransportType.Tcp,
154 | Facility = "VolkovTestFacility",
155 | HostnameOrAddress = "logs.aeroclub.int",
156 | Port = 12202
157 | });
158 |
159 | var logger = loggerConfig.CreateLogger();
160 |
161 | logger.Information("battle profile: {@BattleProfile}", profile);
162 | }
163 |
164 | [Fact]
165 | [Trait("Category", "Integration")]
166 | public void IncludeTemplate()
167 | {
168 | var fixture = new Fixture();
169 | fixture.Behaviors.Clear();
170 | fixture.Behaviors.Add(new OmitOnRecursionBehavior(1));
171 | var profile = fixture.Create();
172 |
173 | var loggerConfig = new LoggerConfiguration();
174 |
175 | loggerConfig.WriteTo.Graylog(new GraylogSinkOptions
176 | {
177 | MinimumLogEventLevel = LogEventLevel.Information,
178 | MessageGeneratorType = MessageIdGeneratorType.Timestamp,
179 | TransportType = TransportType.Tcp,
180 | Facility = "VolkovTestFacility",
181 | HostnameOrAddress = "logs.aeroclub.int",
182 | Port = 12202,
183 | IncludeMessageTemplate = true
184 | });
185 |
186 | var logger = loggerConfig.CreateLogger();
187 |
188 | logger.Information("battle profile: {@BattleProfile}", profile);
189 | }
190 |
191 | [Fact]
192 | [Trait("Category", "Integration")]
193 | public void TestException()
194 | {
195 | var loggerConfig = new LoggerConfiguration();
196 |
197 | loggerConfig.WriteTo.Graylog(new GraylogSinkOptions
198 | {
199 | MinimumLogEventLevel = LogEventLevel.Information,
200 | MessageGeneratorType = MessageIdGeneratorType.Timestamp,
201 | TransportType = TransportType.Tcp,
202 | Facility = "VolkovTestFacility",
203 | HostnameOrAddress = "logs.aeroclub.int",
204 | Port = 12202
205 | });
206 |
207 | var test = new TestClass
208 | {
209 | Id = 1,
210 | SomeTestDateTime = DateTime.UtcNow,
211 | Bar = new Bar
212 | {
213 | Id = 2
214 | },
215 | TestPropertyOne = "1",
216 | TestPropertyThree = "3",
217 | TestPropertyTwo = "2"
218 | };
219 |
220 |
221 | var logger = loggerConfig.CreateLogger();
222 |
223 | try
224 | {
225 | try
226 | {
227 | throw new InvalidOperationException("Level One exception");
228 | } catch (Exception exc)
229 | {
230 | throw new NotImplementedException("Nested Exception", exc);
231 | }
232 | } catch (Exception exc)
233 | {
234 | logger.Error(exc, "test exception with object {@test}", test);
235 | }
236 | }
237 |
238 | [Fact]
239 | [Trait("Category", "Integration")]
240 | public void SerializeEvent()
241 | {
242 | var loggerConfig = new LoggerConfiguration();
243 |
244 | loggerConfig.WriteTo.Graylog(new GraylogSinkOptions
245 | {
246 | MinimumLogEventLevel = LogEventLevel.Information,
247 | MessageGeneratorType = MessageIdGeneratorType.Timestamp,
248 | TransportType = TransportType.Tcp,
249 | Facility = "VolkovTestFacility",
250 | HostnameOrAddress = "logs.aeroclub.int",
251 | Port = 12202
252 | });
253 |
254 | var payload = new Event("123");
255 |
256 | var logger = loggerConfig.CreateLogger();
257 |
258 | logger.Information("test event {@payload}, type:{type}", payload, "TCP");
259 | }
260 | }
261 | }
262 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Tests/IntegrateSinkTestWithUdp.cs:
--------------------------------------------------------------------------------
1 | using AutoFixture;
2 | using Serilog.Events;
3 | using Serilog.Sinks.Graylog.Core.Helpers;
4 | using Serilog.Sinks.Graylog.Core.Transport;
5 | using Serilog.Sinks.Graylog.Tests.ComplexIntegrationTest;
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Linq;
9 | using System.Text.Json;
10 | using System.Text.Json.Serialization;
11 | using System.Threading.Tasks;
12 | using Xunit;
13 |
14 | namespace Serilog.Sinks.Graylog.Tests
15 | {
16 | public enum TestEnumOne
17 | {
18 | One, Two, Three
19 | }
20 |
21 | [Trait("Category", "Integration")]
22 | public class IntegrateSinkTestWithUdp
23 | {
24 | [Fact]
25 | [Trait("Category", "Integration")]
26 | public void VerifyLoggerVerbocity()
27 | {
28 | var loggerConfig = new LoggerConfiguration();
29 |
30 | loggerConfig.WriteTo.Graylog(new GraylogSinkOptions
31 | {
32 | ShortMessageMaxLength = 50,
33 | MinimumLogEventLevel = LogEventLevel.Information,
34 | Facility = "edox-accounts",
35 | HostnameOrAddress = "msa-edor02-lg01",
36 | Port = 12209,
37 | UseGzip = false,
38 | JsonSerializerOptions = new JsonSerializerOptions
39 | {
40 | WriteIndented = true,
41 | Converters = { new JsonStringEnumConverter() }
42 | },
43 | TransportType = TransportType.Udp
44 | });
45 |
46 | var logger = loggerConfig.CreateLogger();
47 |
48 | var test = new TestClass
49 | {
50 | Id = 1,
51 | SomeTestDateTime = DateTime.UtcNow,
52 | Bar = new Bar
53 | {
54 | Id = 2,
55 | Prop = "whirlwind",
56 | TestBarBooleanProperty = false,
57 | EnumVal = TestEnumOne.Three
58 | },
59 | TestClassBooleanProperty = true,
60 | TestPropertyOne = "1",
61 | TestPropertyThree = "3",
62 | TestPropertyTwo = "2",
63 | EnumVal = TestEnumOne.Three
64 | };
65 | logger.Error("SomeComplexTestEntry {@test}", test);
66 | logger.Information("SomeComplexTestEntry {@test}", test);
67 | logger.Fatal("SomeComplexTestEntry {@test}", test);
68 | }
69 |
70 | [Fact]
71 | [Trait("Category", "Integration")]
72 | public void TestArrays()
73 | {
74 | var loggerConfig = new LoggerConfiguration();
75 |
76 | loggerConfig.WriteTo.Graylog(new GraylogSinkOptions
77 | {
78 | ShortMessageMaxLength = 50,
79 | MinimumLogEventLevel = LogEventLevel.Information,
80 | Facility = "edox-accounts",
81 | HostnameOrAddress = "msa-edor02-lg01",
82 | Port = 12209,
83 | UseGzip = false,
84 | ParseArrayValues = true,
85 | JsonSerializerOptions = new JsonSerializerOptions
86 | {
87 | WriteIndented = true,
88 | Converters = { new JsonStringEnumConverter() }
89 | },
90 | TransportType = TransportType.Udp
91 | });
92 |
93 | var logValue = new
94 | {
95 | Bars = new List
96 | {
97 | new Bar
98 | {
99 | Id = 1,
100 | Prop = "1",
101 | TestBarBooleanProperty = true
102 | },
103 | new Bar
104 | {
105 | Id = 2,
106 | Prop = "2",
107 | TestBarBooleanProperty = false
108 | },
109 | new Bar
110 | {
111 | Id = 3,
112 | Prop = "3",
113 | TestBarBooleanProperty = true
114 | }
115 | }
116 | };
117 |
118 | var logger = loggerConfig.CreateLogger();
119 |
120 | logger.Error("SomeComplexTestEntry {@test}", logValue);
121 |
122 | }
123 |
124 | [Fact]
125 | [Trait("Category", "Integration")]
126 | public void TestArrays2()
127 | {
128 | var loggerConfig = new LoggerConfiguration();
129 |
130 | loggerConfig.WriteTo.Graylog(new GraylogSinkOptions
131 | {
132 | ShortMessageMaxLength = 50,
133 | MinimumLogEventLevel = LogEventLevel.Information,
134 | Facility = "edox-accounts",
135 | HostnameOrAddress = "msa-edor02-lg01",
136 | Port = 12209,
137 | UseGzip = false,
138 | ParseArrayValues = true,
139 | IncludeMessageTemplate = true,
140 | JsonSerializerOptions = new JsonSerializerOptions
141 | {
142 | WriteIndented = true,
143 | Converters = { new JsonStringEnumConverter() }
144 | },
145 | TransportType = TransportType.Udp
146 | });
147 |
148 | var test = new[]
149 | {
150 | new Bar
151 | {
152 | Id = 1,
153 | Prop = "1",
154 | TestBarBooleanProperty = true
155 | },
156 | new Bar
157 | {
158 | Id = 2,
159 | Prop = "2",
160 | TestBarBooleanProperty = false
161 | },
162 | new Bar
163 | {
164 | Id = 3,
165 | Prop = "3",
166 | TestBarBooleanProperty = true
167 | }
168 | };
169 |
170 | var logger = loggerConfig.CreateLogger();
171 |
172 | logger.Error("SomeComplexTestEntry {@test}", test);
173 |
174 | }
175 |
176 | [Fact]
177 | [Trait("Category", "Integration")]
178 | public void TestDictionary()
179 | {
180 | var loggerConfig = new LoggerConfiguration();
181 |
182 | loggerConfig.WriteTo.Graylog(new GraylogSinkOptions
183 | {
184 | ShortMessageMaxLength = 50,
185 | MinimumLogEventLevel = LogEventLevel.Information,
186 | HostnameOrAddress = "msa-edor02-lg01",
187 | Port = 12209,
188 | UseGzip = false,
189 | ParseArrayValues = false,
190 | IncludeMessageTemplate = true,
191 | JsonSerializerOptions = new JsonSerializerOptions
192 | {
193 | WriteIndented = true,
194 | },
195 | TransportType = TransportType.Udp
196 | });
197 |
198 | var response = new Dictionary()
199 | {
200 | [0] = new Bar
201 | {
202 |
203 | Id = 1,
204 | Prop = "1",
205 | TestBarBooleanProperty = true,
206 | EnumVal = TestEnumOne.One
207 | },
208 | [1] = new Bar
209 | {
210 | Id = 2,
211 | Prop = "2",
212 | TestBarBooleanProperty = false,
213 | EnumVal = TestEnumOne.Two
214 | },
215 | [2] = new Bar
216 | {
217 | Id = 3,
218 | Prop = "3",
219 | TestBarBooleanProperty = true,
220 | EnumVal = TestEnumOne.Three
221 | }
222 | };
223 |
224 | var logger = loggerConfig.CreateLogger();
225 |
226 | logger.Information("Ответ: {@CommandResponse}", (object)response);
227 | }
228 |
229 | [Fact]
230 | [Trait("Category", "Integration")]
231 | public void TestComplex()
232 | {
233 | var loggerConfig = new LoggerConfiguration();
234 |
235 | loggerConfig.WriteTo.Graylog(new GraylogSinkOptions
236 | {
237 | ShortMessageMaxLength = 50,
238 | MinimumLogEventLevel = LogEventLevel.Information,
239 | Facility = "VolkovTestFacility",
240 | HostnameOrAddress = "logs.aeroclub.int",
241 | Port = 12201
242 | });
243 |
244 | var logger = loggerConfig.CreateLogger();
245 |
246 | var test = new TestClass
247 | {
248 | Id = 1,
249 | Type = "UDP",
250 | SomeTestDateTime = DateTime.UtcNow,
251 | Bar = new Bar
252 | {
253 | Id = 2,
254 | Prop = "123",
255 | TestBarBooleanProperty = false
256 |
257 | },
258 | TestClassBooleanProperty = true,
259 | TestPropertyOne = "1",
260 | TestPropertyThree = "3",
261 | TestPropertyTwo = "2"
262 | };
263 |
264 | logger.Information("SomeComplexTestEntry {@test}", test);
265 | }
266 |
267 | [Fact()]
268 | [Trait("Category", "Integration")]
269 | public Task SendManyMessages()
270 | {
271 | var fixture = new Fixture();
272 | fixture.Behaviors.Clear();
273 | fixture.Behaviors.Add(new OmitOnRecursionBehavior(1));
274 | var profiles = fixture.CreateMany(10).ToList();
275 |
276 | foreach (var profile in profiles)
277 | {
278 | profile.Type = "UDP";
279 | }
280 |
281 | var loggerConfig = new LoggerConfiguration();
282 |
283 | loggerConfig.WriteTo.Graylog(new GraylogSinkOptions
284 | {
285 | MinimumLogEventLevel = LogEventLevel.Information,
286 | MessageGeneratorType = MessageIdGeneratorType.Md5,
287 | Facility = "VolkovTestFacility",
288 | HostnameOrAddress = "logs.aeroclub.int",
289 | Port = 12201
290 | });
291 |
292 | var logger = loggerConfig.CreateLogger();
293 |
294 | var tasks = profiles.Select(c =>
295 | {
296 | return Task.Run(() => logger.Information("TestSend {@BattleProfile}", c));
297 | });
298 |
299 |
300 | return Task.WhenAll(tasks.ToArray());
301 | }
302 |
303 | [Fact]
304 | [Trait("Category", "Integration")]
305 | public void TestSimple()
306 | {
307 | var fixture = new Fixture();
308 | fixture.Behaviors.Clear();
309 | fixture.Behaviors.Add(new OmitOnRecursionBehavior(1));
310 | var profile = fixture.Create();
311 |
312 | var loggerConfig = new LoggerConfiguration();
313 |
314 | loggerConfig.WriteTo.Graylog(new GraylogSinkOptions
315 | {
316 | MinimumLogEventLevel = LogEventLevel.Information,
317 | MessageGeneratorType = MessageIdGeneratorType.Timestamp,
318 | Facility = "VolkovTestFacility",
319 | HostnameOrAddress = "logs.aeroclub.int",
320 | Port = 12201
321 | });
322 |
323 | var logger = loggerConfig.CreateLogger();
324 |
325 | logger.Information("battle profile: {@BattleProfile}", profile);
326 | }
327 |
328 | [Fact]
329 | [Trait("Category", "Integration")]
330 | public void IncludeTemplate()
331 | {
332 | var fixture = new Fixture();
333 | fixture.Behaviors.Clear();
334 | fixture.Behaviors.Add(new OmitOnRecursionBehavior(1));
335 | var profile = fixture.Create();
336 |
337 | var loggerConfig = new LoggerConfiguration();
338 |
339 | loggerConfig.WriteTo.Graylog(new GraylogSinkOptions
340 | {
341 | MinimumLogEventLevel = LogEventLevel.Information,
342 | MessageGeneratorType = MessageIdGeneratorType.Timestamp,
343 | Facility = "VolkovTestFacility",
344 | HostnameOrAddress = "logs.aeroclub.int",
345 | Port = 12201,
346 | IncludeMessageTemplate = true
347 | });
348 |
349 | var logger = loggerConfig.CreateLogger();
350 |
351 | logger.Information("battle profile: {@BattleProfile}", profile);
352 | }
353 |
354 | [Fact]
355 | [Trait("Category", "Integration")]
356 | public void TestException()
357 | {
358 | var loggerConfig = new LoggerConfiguration();
359 |
360 | loggerConfig.WriteTo.Graylog(new GraylogSinkOptions
361 | {
362 | MinimumLogEventLevel = LogEventLevel.Information,
363 | MessageGeneratorType = MessageIdGeneratorType.Timestamp,
364 | TransportType = TransportType.Udp,
365 | Facility = "VolkovTestFacility",
366 | HostnameOrAddress = "logs.aeroclub.int",
367 | Port = 12201
368 | });
369 |
370 | var test = new TestClass
371 | {
372 | Id = 1,
373 | SomeTestDateTime = DateTime.UtcNow,
374 | Bar = new Bar
375 | {
376 | Id = 2
377 | },
378 | TestPropertyOne = "1",
379 | TestPropertyThree = "3",
380 | TestPropertyTwo = "2"
381 | };
382 |
383 |
384 | var logger = loggerConfig.CreateLogger();
385 |
386 | try
387 | {
388 | try
389 | {
390 | throw new InvalidOperationException("Level One exception");
391 | } catch (Exception exc)
392 | {
393 | throw new NotImplementedException("Nested Exception", exc);
394 | }
395 | } catch (Exception exc)
396 | {
397 | logger.Error(exc, "test exception with object {@test}", test);
398 | }
399 | }
400 |
401 | [Fact]
402 | [Trait("Category", "Integration")]
403 | public void SerializeEvent()
404 | {
405 | var loggerConfig = new LoggerConfiguration();
406 |
407 | loggerConfig.WriteTo.Graylog(new GraylogSinkOptions
408 | {
409 | MinimumLogEventLevel = LogEventLevel.Information,
410 | MessageGeneratorType = MessageIdGeneratorType.Timestamp,
411 | TransportType = TransportType.Udp,
412 | Facility = "VolkovTestFacility",
413 | HostnameOrAddress = "logs.aeroclub.int",
414 | Port = 12201
415 | });
416 |
417 | var payload = new Event("123");
418 |
419 | var logger = loggerConfig.CreateLogger();
420 |
421 | logger.Information("test event {@payload}", payload);
422 | }
423 | }
424 |
425 | public class Event
426 | {
427 | public Event(string eventId)
428 | {
429 | EventId = eventId;
430 | Timestamp = DateTime.UtcNow;
431 | }
432 |
433 | public DateTime Timestamp { get; set; }
434 |
435 | public string EventId { get; set; }
436 | }
437 |
438 | public class Bar
439 | {
440 | public int Id { get; set; }
441 | public string Prop { get; set; }
442 |
443 | public bool TestBarBooleanProperty { get; set; }
444 | public TestEnumOne EnumVal { get; set; }
445 | }
446 |
447 | public class TestClass
448 | {
449 | public int Id { get; set; }
450 |
451 | public bool TestClassBooleanProperty { get; set; }
452 |
453 | public string TestPropertyOne { get; set; }
454 |
455 | public Bar Bar { get; set; }
456 |
457 | public string TestPropertyTwo { get; set; }
458 |
459 | public string TestPropertyThree { get; set; }
460 | public DateTime SomeTestDateTime { get; set; }
461 | public string Type { get; set; }
462 | public TestEnumOne EnumVal { get; set; }
463 | }
464 | }
465 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Tests/LogEventSource.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Events;
2 | using Serilog.Parsing;
3 | using System;
4 | using System.Collections.Generic;
5 |
6 | namespace Serilog.Sinks.Graylog.Tests
7 | {
8 | public class LogEventSource
9 | {
10 | public static LogEvent GetSimpleLogEvent(DateTimeOffset date)
11 | {
12 | var logEvent = new LogEvent(date, LogEventLevel.Information, null,
13 | new MessageTemplate("abcdef{TestProp}", new List
14 | {
15 | new TextToken("abcdef", 0),
16 | new PropertyToken("TestProp", "zxc", alignment:new Alignment(AlignmentDirection.Left, 3))
17 |
18 | }), new List
19 | {
20 | new LogEventProperty("TestProp", new ScalarValue("zxc")),
21 | new LogEventProperty("id", new ScalarValue("asd"))
22 | });
23 | return logEvent;
24 | }
25 |
26 | public static LogEvent GetErrorEvent(DateTimeOffset date)
27 | {
28 | var logEvent = new LogEvent(date, LogEventLevel.Information, new InvalidCastException("Some errror"),
29 | new MessageTemplate("", new List()),
30 | new List(new List()));
31 | return logEvent;
32 | }
33 |
34 | public static LogEvent GetComplexEvent(DateTimeOffset date)
35 | {
36 | var logEvent = new LogEvent(date, LogEventLevel.Information, null,
37 | new MessageTemplate("abcdef{TestProp}", new List
38 | {
39 | new TextToken("abcdef", 0),
40 | new PropertyToken("TestProp", "zxc", alignment:new Alignment(AlignmentDirection.Left, 3))
41 |
42 | }), new List
43 | {
44 | new LogEventProperty("TestProp", new ScalarValue("zxc")),
45 | new LogEventProperty("id", new ScalarValue("asd")),
46 | new LogEventProperty("StructuredProperty",
47 | new StructureValue(new List
48 | {
49 | new LogEventProperty("id", new ScalarValue(1)),
50 | new LogEventProperty("_TestProp", new ScalarValue(3)),
51 | }, "TypeTag"))
52 | });
53 | return logEvent;
54 | }
55 |
56 | public static LogEvent GetExceptionLogEvent(DateTimeOffset date, Exception testExc)
57 | {
58 | var logevent = new LogEvent(date, LogEventLevel.Error, testExc, new MessageTemplate("", new List()),
59 | new List(new List()));
60 | return logevent;
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Tests/LoggerConfigurationGrayLogExtensionsFixture.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using Microsoft.Extensions.Configuration;
3 | using Serilog.Events;
4 | using Serilog.Sinks.Graylog.Core.Transport;
5 | using System.IO;
6 | using System.Reflection;
7 | using Xunit;
8 |
9 | namespace Serilog.Sinks.Graylog.Tests
10 | {
11 | public class LoggerConfigurationGrayLogExtensionsFixture
12 | {
13 | [Fact]
14 | public void CanApplyExtension()
15 | {
16 | var loggerConfig = new LoggerConfiguration();
17 |
18 | loggerConfig.WriteTo.Graylog(new GraylogSinkOptions
19 | {
20 | MinimumLogEventLevel = LogEventLevel.Information,
21 | Facility = "VolkovTestFacility",
22 | HostnameOrAddress = "localhost",
23 | Port = 12201
24 | });
25 |
26 | var logger = loggerConfig.CreateLogger();
27 | logger.Should().NotBeNull();
28 | }
29 |
30 | [Fact]
31 | public void CanApplyExtensionWithIntegralParameterTypes()
32 | {
33 | var loggerConfig = new LoggerConfiguration();
34 |
35 | loggerConfig.WriteTo.Graylog("localhost", 12201, TransportType.Udp, false,
36 | LogEventLevel.Information);
37 |
38 | var logger = loggerConfig.CreateLogger();
39 | logger.Should().NotBeNull();
40 | }
41 |
42 | //[Fact(Skip="Integration test")]
43 | [Fact]
44 | public void CanReadHostPropertyConfiguration()
45 | {
46 | //arrange
47 | //
48 | IConfigurationRoot configuration;
49 | using (Stream s = Assembly.GetExecutingAssembly().GetManifestResourceStream("Serilog.Sinks.Graylog.Tests.Configurations.AppSettingsWithGraylogSinkContainingHostProperty.json"))
50 | {
51 | configuration = new ConfigurationBuilder()
52 | .AddJsonStream(s)
53 | .Build();
54 | }
55 | Log.Logger = new LoggerConfiguration()
56 | .ReadFrom.Configuration(configuration)
57 | .CreateLogger();
58 |
59 | //act
60 | Log.Information("Hello {ApplicationName}.", "SerilogGraylogSink");
61 |
62 | //assert
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Tests/Serilog.Sinks.Graylog.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net7.0;net6.0
4 | full
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | all
28 | runtime; build; native; contentfiles; analyzers; buildtransitive
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Tests/SerilogExceptionsFixture.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Events;
2 | using Serilog.Sinks.Graylog.Core.Transport;
3 | using System;
4 | using Xunit;
5 |
6 | namespace Serilog.Sinks.Graylog.Tests
7 | {
8 | public class SerilogExceptionsFixture
9 | {
10 | [Fact]
11 | [Trait("Category", "Integration")]
12 | public void WhenUseSerilogExceptions_ThenExceptionDetailsShouldBeSent()
13 | {
14 | var loggerConfig = new LoggerConfiguration();
15 |
16 | loggerConfig
17 | //.Enrich.WithExceptionDetails()
18 | .WriteTo.Graylog(new GraylogSinkOptions
19 | {
20 | ShortMessageMaxLength = 50,
21 | MinimumLogEventLevel = LogEventLevel.Information,
22 | TransportType = TransportType.Http,
23 | Facility = "VolkovTestFacility",
24 | HostnameOrAddress = "http://logs.aeroclub.int",
25 | Port = 12201
26 | });
27 |
28 | var logger = loggerConfig.CreateLogger();
29 |
30 | try
31 | {
32 | throw new InvalidOperationException("Test exception");
33 | } catch (Exception e)
34 | {
35 | var test = new TestClass
36 | {
37 | Id = 1,
38 | Bar = new Bar
39 | {
40 | Id = 2,
41 | Prop = "123"
42 | },
43 | TestPropertyOne = "1",
44 | TestPropertyThree = "3",
45 | TestPropertyTwo = "2"
46 | };
47 |
48 | logger.Error(e, "Exception {@entry}", test);
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog.Tests/TestProfile/Profile.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Serilog.Sinks.Graylog.Tests.ComplexIntegrationTest
5 | {
6 | public class Profile
7 | {
8 | public string Type { get; set; }
9 | public int Id { get; set; }
10 | public LocalizedString FirstName { get; set; }
11 | public LocalizedString MiddleName { get; set; }
12 | public LocalizedString LastName { get; set; }
13 | public DateTime? DateOfBirth { get; set; }
14 | public string Sex { get; set; }
15 | public Document[] Documents { get; set; }
16 | public Contacts Contacts { get; set; }
17 | public DateTime CreatedAt { get; set; }
18 | public DateTime? UpdatedAt { get; set; }
19 | public string Version { get; set; }
20 | public Company CompanyInfo { get; set; }
21 | public Location WorkLocationCity { get; set; }
22 | public string PhotoFingerprint { get; set; }
23 |
24 | public Preference Preference { get; set; }
25 |
26 | /// Gets or sets the additional information.
27 | /// The additional information.
28 | public IList AdditionalInfo { get; set; }
29 | }
30 |
31 | public class LocalizedString
32 | {
33 | public string Ru { get; set; }
34 | public string En { get; set; }
35 | }
36 |
37 | public class Document
38 | {
39 | public string Type { get; set; }
40 | public string Series { get; set; }
41 | public string Number { get; set; }
42 | public string PlaceOfBirth { get; set; }
43 | public DateTime? IssuedOn { get; set; }
44 | public DateTime? ExpiresOn { get; set; }
45 | public string FirstName { get; set; }
46 | public string LastName { get; set; }
47 |
48 | ///
49 | /// В рамках виз это используется как "страна, на которую выдана виза"
50 | /// В рамках остальных документов - пока никак.
51 | ///
52 | public Location CitizenshipCountry { get; set; }
53 | }
54 |
55 | public class Contacts
56 | {
57 | public EmailAddress[] EmailAddresses { get; set; }
58 | public PhoneNumberContact[] PhoneNumbers { get; set; }
59 |
60 | }
61 |
62 | public class EmailAddress
63 | {
64 | public string Type { get; set; }
65 | public string Address { get; set; }
66 | }
67 |
68 | public class PhoneNumberContact
69 | {
70 | public string Type { get; set; }
71 | public string CountryCode { get; set; }
72 | public string AreaCode { get; set; }
73 | public string PhoneNumber { get; set; }
74 | public string ExtensionNumber { get; set; }
75 | }
76 |
77 | public class Location
78 | {
79 | public string Type { get; set; }
80 | public LocalizedString Name { get; set; }
81 | public string Code { get; set; }
82 |
83 | public Location LocatedIn { get; set; }
84 |
85 | }
86 |
87 | public class Company
88 | {
89 | public LocalizedString CompanyName { get; set; }
90 | public LocalizedString HoldingName { get; set; }
91 | }
92 |
93 | public class Preference
94 | {
95 | public int Id { get; set; }
96 | public string Comments { get; set; }
97 | public string InternalComments { get; set; }
98 | public PreferenceInformation[] PreferenceInformations { get; set; }
99 | }
100 |
101 | public class PreferenceInformation
102 | {
103 | //public int Id { get; set; }
104 | public string Type { get; set; }
105 | public string Code { get; set; }
106 | public LocalizedString Name { get; set; }
107 | }
108 |
109 | public class AdditionalInfo
110 | {
111 | /// Gets or sets the name en.
112 | /// The name en.
113 | public string NameEn { get; set; }
114 | /// Gets or sets the name ru.
115 | /// The name ru.
116 | public string NameRu { get; set; }
117 | /// Gets or sets the value en.
118 | /// The value en.
119 | public string ValueEn { get; set; }
120 | /// Gets or sets the value ru.
121 | /// The value ru.
122 | public string ValueRu { get; set; }
123 | }
124 |
125 | }
126 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog/GraylogSink.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Core;
2 | using Serilog.Debugging;
3 | using Serilog.Events;
4 | using Serilog.Sinks.Graylog.Core;
5 | using Serilog.Sinks.Graylog.Core.Transport;
6 | using System;
7 | using System.Text.Json;
8 | using System.Threading.Tasks;
9 |
10 | namespace Serilog.Sinks.Graylog
11 | {
12 | public sealed class GraylogSink : ILogEventSink, IDisposable
13 | {
14 | private readonly Lazy _converter;
15 | private readonly Lazy _transport;
16 | private readonly JsonSerializerOptions _options;
17 |
18 | public GraylogSink(GraylogSinkOptions options)
19 | {
20 | ISinkComponentsBuilder sinkComponentsBuilder = new SinkComponentsBuilder(options);
21 |
22 | var jsonSerializerOptions = options.JsonSerializerOptions ?? new JsonSerializerOptions(JsonSerializerDefaults.General);
23 | _options = new JsonSerializerOptions(jsonSerializerOptions);
24 |
25 | _transport = new Lazy(sinkComponentsBuilder.MakeTransport);
26 | _converter = new Lazy(() => sinkComponentsBuilder.MakeGelfConverter());
27 | }
28 |
29 | public void Emit(LogEvent logEvent)
30 | {
31 | EmitAsync(logEvent).ContinueWith(
32 | task =>
33 | {
34 | SelfLog.WriteLine("Oops something going wrong {0}", task.Exception);
35 | },
36 | TaskContinuationOptions.OnlyOnFaulted);
37 | }
38 |
39 | private Task EmitAsync(LogEvent logEvent)
40 | {
41 | var json = _converter.Value.GetGelfJson(logEvent);
42 | var payload = json.ToJsonString(_options);
43 |
44 | return _transport.Value.Send(payload);
45 | }
46 |
47 | public void Dispose()
48 | {
49 | _transport.Value.Dispose();
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog/GraylogSinkOptions.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Sinks.Graylog.Core;
2 |
3 | namespace Serilog.Sinks.Graylog
4 | {
5 | public class GraylogSinkOptions : GraylogSinkOptionsBase
6 | {
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog/LoggerConfigurationGrayLogExtensions.cs:
--------------------------------------------------------------------------------
1 | using Serilog.Configuration;
2 | using Serilog.Core;
3 | using Serilog.Events;
4 | using Serilog.Sinks.Graylog.Core;
5 | using Serilog.Sinks.Graylog.Core.Extensions;
6 | using Serilog.Sinks.Graylog.Core.Helpers;
7 | using Serilog.Sinks.Graylog.Core.Transport;
8 |
9 | namespace Serilog.Sinks.Graylog
10 | {
11 | public static class LoggerConfigurationGrayLogExtensions
12 | {
13 | ///
14 | /// Graylogs the specified options.
15 | ///
16 | /// The logger sink configuration.
17 | /// The options.
18 | ///
19 | public static LoggerConfiguration Graylog(this LoggerSinkConfiguration loggerSinkConfiguration, GraylogSinkOptions options)
20 | {
21 | var sink = (ILogEventSink)new GraylogSink(options);
22 | return loggerSinkConfiguration.Sink(sink, options.MinimumLogEventLevel);
23 | }
24 |
25 | ///
26 | /// Graylogs the specified hostname or address.
27 | ///
28 | /// The logger sink configuration.
29 | /// The hostname or address.
30 | /// The port.
31 | /// Type of the transport.
32 | /// Use SSL in Tcp and Http
33 | /// The minimum log event level.
34 | /// Type of the message identifier generator.
35 | /// Short length of the message maximum.
36 | /// The stack trace depth.
37 | /// The facility.
38 | /// the maxMessageSizeInUdp
39 | /// The host property to use in GELF message. If null, DNS hostname will be used instead.
40 | /// if set to true if include message template to graylog.
41 | /// Name of the message template field.
42 | /// The usernameInHttp. Basic authentication property.
43 | /// The passwordInHttp. Basic authentication property.
44 | ///
45 | public static LoggerConfiguration Graylog(this LoggerSinkConfiguration loggerSinkConfiguration,
46 | string hostnameOrAddress,
47 | int port,
48 | TransportType transportType,
49 | bool useSsl = false,
50 | LogEventLevel minimumLogEventLevel = LevelAlias.Minimum,
51 | MessageIdGeneratorType messageIdGeneratorType = GraylogSinkOptionsBase.DefaultMessageGeneratorType,
52 | int shortMessageMaxLength = GraylogSinkOptionsBase.DefaultShortMessageMaxLength,
53 | int stackTraceDepth = GraylogSinkOptionsBase.DefaultStackTraceDepth,
54 | string? facility = GraylogSinkOptionsBase.DefaultFacility,
55 | int maxMessageSizeInUdp = GraylogSinkOptionsBase.DefaultMaxMessageSizeInUdp,
56 | string host = GraylogSinkOptionsBase.DefaultHost,
57 | bool includeMessageTemplate = false,
58 | string messageTemplateFieldName = GraylogSinkOptionsBase.DefaultMessageTemplateFieldName,
59 | string? usernameInHttp = null,
60 | string? passwordInHttp = null,
61 | bool parseArrayValues = false,
62 | bool useGzip = true
63 | )
64 | {
65 | // ReSharper disable once UseObjectOrCollectionInitializer
66 | var options = new GraylogSinkOptions
67 | {
68 | HostnameOrAddress = hostnameOrAddress.Expand(),
69 | Port = port,
70 | TransportType = transportType,
71 | UseSsl = useSsl,
72 | MinimumLogEventLevel = minimumLogEventLevel,
73 | MessageGeneratorType = messageIdGeneratorType,
74 | ShortMessageMaxLength = shortMessageMaxLength,
75 | StackTraceDepth = stackTraceDepth,
76 | Facility = facility?.Expand(),
77 | MaxMessageSizeInUdp = maxMessageSizeInUdp,
78 | HostnameOverride = host,
79 | IncludeMessageTemplate = includeMessageTemplate,
80 | MessageTemplateFieldName = messageTemplateFieldName,
81 | UsernameInHttp = usernameInHttp,
82 | PasswordInHttp = passwordInHttp,
83 | ParseArrayValues = parseArrayValues,
84 | UseGzip = useGzip
85 | };
86 |
87 | return loggerSinkConfiguration.Graylog(options);
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog/Serilog.Sinks.Graylog.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | 11.0
4 |
5 |
6 | net7.0;net6.0;netstandard2.0
7 |
8 | $(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage
9 | true
10 | full
11 | enable
12 | true
13 |
14 | Serilog.Sinks.Graylog
15 | Serilog.Sinks.Graylog
16 | Anton Volkov and Contributors
17 | The Serilog Graylog Sink project is a sink (basically a writer) for the Serilog logging framework. Structured log events are written to sinks and each sink is responsible for writing it to its own backend, database, store etc. This sink delivers the data to Graylog2, a NoSQL search engine.
18 |
19 | https://github.com/serilog-contrib/serilog-sinks-graylog
20 | https://github.com/serilog-contrib/serilog-sinks-graylog
21 | serilog-sink-nuget.png
22 |
23 | git
24 | Serilog Sink Graylog
25 | Anton Volkov Copyright © 2023
26 | en
27 |
28 | 3.1.1
29 | 3.0.0.0
30 | 3.0.0.0
31 | true
32 |
33 | sign.snk
34 |
35 | MIT
36 | Delete newtonsoft.json dependency and refactoring
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | README.md
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog/Serilog.Sinks.GraylogSingleTarget.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net46
4 | true
5 |
6 |
7 | library
8 | Serilog.Sinks.Graylog
9 | Serilog.Sinks.Graylog
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog/Serilog.Sinks.GraylogSingleTarget.csproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Graylog/sign.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serilog-contrib/serilog-sinks-graylog/64532fa1aab7a8b3dc5c94e3294a570d6698e77d/src/Serilog.Sinks.Graylog/sign.snk
--------------------------------------------------------------------------------
/src/TestApplication/CustomException.cs:
--------------------------------------------------------------------------------
1 | namespace TestApplication;
2 |
3 | public class CustomException : Exception
4 | {
5 | public string? CustomExceptionValue { get; set; }
6 | }
7 |
--------------------------------------------------------------------------------
/src/TestApplication/Program.cs:
--------------------------------------------------------------------------------
1 | using Serilog;
2 | using Serilog.Debugging;
3 | using Serilog.Exceptions;
4 | using TestApplication;
5 |
6 | var builder = WebApplication.CreateBuilder(args);
7 | SelfLog.Enable(Console.WriteLine);
8 | builder.Services.AddEndpointsApiExplorer();
9 | builder.Services.AddSwaggerGen();
10 |
11 | builder.Host.UseSerilog((context, config) =>
12 | {
13 | var serilogSection = context.Configuration.GetSection("Serilog");
14 | config.ReadFrom.Configuration(context.Configuration);
15 | });
16 |
17 | var app = builder.Build();
18 |
19 | if (app.Environment.IsDevelopment())
20 | {
21 | app.UseSwagger();
22 | app.UseSwaggerUI();
23 | }
24 |
25 | app.UseHttpsRedirection();
26 |
27 | var summaries = new[]
28 | {
29 | "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
30 | };
31 |
32 | app.MapPost("/simple_send", () =>
33 | {
34 | var seriloglogger = Log.ForContext();
35 |
36 | var forecast = Enumerable.Range(1, 5).Select(index =>
37 | new WeatherForecast
38 | (
39 | DateTime.Now.AddDays(index),
40 | Random.Shared.Next(-20, 55),
41 | summaries[Random.Shared.Next(summaries.Length)]
42 | ))
43 | .ToArray();
44 | seriloglogger.Information("SomeInf {@Forecast}", forecast);
45 |
46 | return forecast;
47 | })
48 | .WithName("simple_send");
49 |
50 | app.MapPost("simple_exception", () =>
51 | {
52 | var seriloglogger = Log.ForContext();
53 | try
54 | {
55 | throw new Exception("test");
56 | }
57 | catch (Exception ex)
58 | {
59 | seriloglogger.Error(ex, ex.Message);
60 | }
61 |
62 | }).WithName("simple_exception");
63 |
64 | app.MapPost("custom_exception", () =>
65 | {
66 | var seriloglogger = Log.ForContext();
67 | try
68 | {
69 | throw new CustomException()
70 | {
71 | CustomExceptionValue = "test_custom_exception_value",
72 | Data = { ["SomeData"] = "SomeValue" }
73 | };
74 | }
75 | catch (Exception ex)
76 | {
77 | seriloglogger.Error(ex, ex.Message);
78 | }
79 |
80 | }).WithName("custom_exception");
81 |
82 |
83 | app.Run();
84 |
85 | internal record WeatherForecast(DateTime Date, int TemperatureC, string? Summary)
86 | {
87 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
88 | }
89 |
--------------------------------------------------------------------------------
/src/TestApplication/TestApplication.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net7.0
5 | enable
6 | enable
7 | 10
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/TestApplication/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 |
3 | "Serilog": {
4 | "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.Graylog" ],
5 | "MinimumLevel": {
6 | "Default": "Information",
7 | "Override": {
8 | "Microsoft": "Information",
9 | "System": "Warning"
10 | }
11 | },
12 | "WriteTo": [
13 | { "Name": "Console" },
14 | {
15 | "Name": "Graylog",
16 | "Args": {
17 | "hostnameOrAddress": "localhost",
18 | "port": "12205",
19 | "transportType": "Udp"
20 | }
21 | }
22 | ],
23 | "Properties": {
24 | "Application": "GraylogIssueDemo"
25 | }
26 | },
27 |
28 | "AllowedHosts": "*"
29 | }
--------------------------------------------------------------------------------
/src/serilog-sink-nuget.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serilog-contrib/serilog-sinks-graylog/64532fa1aab7a8b3dc5c94e3294a570d6698e77d/src/serilog-sink-nuget.png
--------------------------------------------------------------------------------
/src/serilog-sinks-graylog.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29215.179
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Sinks.Graylog", "Serilog.Sinks.Graylog\Serilog.Sinks.Graylog.csproj", "{8FADC837-9237-4447-B6CE-A0FE3806610C}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Sinks.Graylog.Tests", "Serilog.Sinks.Graylog.Tests\Serilog.Sinks.Graylog.Tests.csproj", "{3C61B1BA-FCC8-4589-90BA-08FBF1CCE68B}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Sinks.Graylog.Core", "Serilog.Sinks.Graylog.Core\Serilog.Sinks.Graylog.Core.csproj", "{5A3A6717-4252-444F-BA49-86F4AFD53027}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Sinks.Graylog.Batching", "Serilog.Sinks.Graylog.Batching\Serilog.Sinks.Graylog.Batching.csproj", "{BFCDD1CB-3B20-40F9-87C3-392514398C5B}"
13 | EndProject
14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Sinks.Graylog.Core.Tests", "Serilog.Sinks.Graylog.Core.Tests\Serilog.Sinks.Graylog.Core.Tests.csproj", "{27F75D09-070E-4E18-BE81-1433BE8A5209}"
15 | EndProject
16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApplication", "TestApplication\TestApplication.csproj", "{08E240AD-E2DF-40AA-B2CD-63693674BCCF}"
17 | EndProject
18 | Global
19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
20 | Debug|Any CPU = Debug|Any CPU
21 | Debug|x64 = Debug|x64
22 | Debug|x86 = Debug|x86
23 | Release|Any CPU = Release|Any CPU
24 | Release|x64 = Release|x64
25 | Release|x86 = Release|x86
26 | EndGlobalSection
27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
28 | {8FADC837-9237-4447-B6CE-A0FE3806610C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {8FADC837-9237-4447-B6CE-A0FE3806610C}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {8FADC837-9237-4447-B6CE-A0FE3806610C}.Debug|x64.ActiveCfg = Debug|Any CPU
31 | {8FADC837-9237-4447-B6CE-A0FE3806610C}.Debug|x64.Build.0 = Debug|Any CPU
32 | {8FADC837-9237-4447-B6CE-A0FE3806610C}.Debug|x86.ActiveCfg = Debug|Any CPU
33 | {8FADC837-9237-4447-B6CE-A0FE3806610C}.Debug|x86.Build.0 = Debug|Any CPU
34 | {8FADC837-9237-4447-B6CE-A0FE3806610C}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {8FADC837-9237-4447-B6CE-A0FE3806610C}.Release|Any CPU.Build.0 = Release|Any CPU
36 | {8FADC837-9237-4447-B6CE-A0FE3806610C}.Release|x64.ActiveCfg = Release|Any CPU
37 | {8FADC837-9237-4447-B6CE-A0FE3806610C}.Release|x64.Build.0 = Release|Any CPU
38 | {8FADC837-9237-4447-B6CE-A0FE3806610C}.Release|x86.ActiveCfg = Release|Any CPU
39 | {8FADC837-9237-4447-B6CE-A0FE3806610C}.Release|x86.Build.0 = Release|Any CPU
40 | {3C61B1BA-FCC8-4589-90BA-08FBF1CCE68B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41 | {3C61B1BA-FCC8-4589-90BA-08FBF1CCE68B}.Debug|Any CPU.Build.0 = Debug|Any CPU
42 | {3C61B1BA-FCC8-4589-90BA-08FBF1CCE68B}.Debug|x64.ActiveCfg = Debug|Any CPU
43 | {3C61B1BA-FCC8-4589-90BA-08FBF1CCE68B}.Debug|x64.Build.0 = Debug|Any CPU
44 | {3C61B1BA-FCC8-4589-90BA-08FBF1CCE68B}.Debug|x86.ActiveCfg = Debug|Any CPU
45 | {3C61B1BA-FCC8-4589-90BA-08FBF1CCE68B}.Debug|x86.Build.0 = Debug|Any CPU
46 | {3C61B1BA-FCC8-4589-90BA-08FBF1CCE68B}.Release|Any CPU.ActiveCfg = Release|Any CPU
47 | {3C61B1BA-FCC8-4589-90BA-08FBF1CCE68B}.Release|Any CPU.Build.0 = Release|Any CPU
48 | {3C61B1BA-FCC8-4589-90BA-08FBF1CCE68B}.Release|x64.ActiveCfg = Release|Any CPU
49 | {3C61B1BA-FCC8-4589-90BA-08FBF1CCE68B}.Release|x64.Build.0 = Release|Any CPU
50 | {3C61B1BA-FCC8-4589-90BA-08FBF1CCE68B}.Release|x86.ActiveCfg = Release|Any CPU
51 | {3C61B1BA-FCC8-4589-90BA-08FBF1CCE68B}.Release|x86.Build.0 = Release|Any CPU
52 | {5A3A6717-4252-444F-BA49-86F4AFD53027}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
53 | {5A3A6717-4252-444F-BA49-86F4AFD53027}.Debug|Any CPU.Build.0 = Debug|Any CPU
54 | {5A3A6717-4252-444F-BA49-86F4AFD53027}.Debug|x64.ActiveCfg = Debug|Any CPU
55 | {5A3A6717-4252-444F-BA49-86F4AFD53027}.Debug|x64.Build.0 = Debug|Any CPU
56 | {5A3A6717-4252-444F-BA49-86F4AFD53027}.Debug|x86.ActiveCfg = Debug|Any CPU
57 | {5A3A6717-4252-444F-BA49-86F4AFD53027}.Debug|x86.Build.0 = Debug|Any CPU
58 | {5A3A6717-4252-444F-BA49-86F4AFD53027}.Release|Any CPU.ActiveCfg = Release|Any CPU
59 | {5A3A6717-4252-444F-BA49-86F4AFD53027}.Release|Any CPU.Build.0 = Release|Any CPU
60 | {5A3A6717-4252-444F-BA49-86F4AFD53027}.Release|x64.ActiveCfg = Release|Any CPU
61 | {5A3A6717-4252-444F-BA49-86F4AFD53027}.Release|x64.Build.0 = Release|Any CPU
62 | {5A3A6717-4252-444F-BA49-86F4AFD53027}.Release|x86.ActiveCfg = Release|Any CPU
63 | {5A3A6717-4252-444F-BA49-86F4AFD53027}.Release|x86.Build.0 = Release|Any CPU
64 | {BFCDD1CB-3B20-40F9-87C3-392514398C5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
65 | {BFCDD1CB-3B20-40F9-87C3-392514398C5B}.Debug|Any CPU.Build.0 = Debug|Any CPU
66 | {BFCDD1CB-3B20-40F9-87C3-392514398C5B}.Debug|x64.ActiveCfg = Debug|Any CPU
67 | {BFCDD1CB-3B20-40F9-87C3-392514398C5B}.Debug|x64.Build.0 = Debug|Any CPU
68 | {BFCDD1CB-3B20-40F9-87C3-392514398C5B}.Debug|x86.ActiveCfg = Debug|Any CPU
69 | {BFCDD1CB-3B20-40F9-87C3-392514398C5B}.Debug|x86.Build.0 = Debug|Any CPU
70 | {BFCDD1CB-3B20-40F9-87C3-392514398C5B}.Release|Any CPU.ActiveCfg = Release|Any CPU
71 | {BFCDD1CB-3B20-40F9-87C3-392514398C5B}.Release|Any CPU.Build.0 = Release|Any CPU
72 | {BFCDD1CB-3B20-40F9-87C3-392514398C5B}.Release|x64.ActiveCfg = Release|Any CPU
73 | {BFCDD1CB-3B20-40F9-87C3-392514398C5B}.Release|x64.Build.0 = Release|Any CPU
74 | {BFCDD1CB-3B20-40F9-87C3-392514398C5B}.Release|x86.ActiveCfg = Release|Any CPU
75 | {BFCDD1CB-3B20-40F9-87C3-392514398C5B}.Release|x86.Build.0 = Release|Any CPU
76 | {27F75D09-070E-4E18-BE81-1433BE8A5209}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
77 | {27F75D09-070E-4E18-BE81-1433BE8A5209}.Debug|Any CPU.Build.0 = Debug|Any CPU
78 | {27F75D09-070E-4E18-BE81-1433BE8A5209}.Debug|x64.ActiveCfg = Debug|Any CPU
79 | {27F75D09-070E-4E18-BE81-1433BE8A5209}.Debug|x64.Build.0 = Debug|Any CPU
80 | {27F75D09-070E-4E18-BE81-1433BE8A5209}.Debug|x86.ActiveCfg = Debug|Any CPU
81 | {27F75D09-070E-4E18-BE81-1433BE8A5209}.Debug|x86.Build.0 = Debug|Any CPU
82 | {27F75D09-070E-4E18-BE81-1433BE8A5209}.Release|Any CPU.ActiveCfg = Release|Any CPU
83 | {27F75D09-070E-4E18-BE81-1433BE8A5209}.Release|Any CPU.Build.0 = Release|Any CPU
84 | {27F75D09-070E-4E18-BE81-1433BE8A5209}.Release|x64.ActiveCfg = Release|Any CPU
85 | {27F75D09-070E-4E18-BE81-1433BE8A5209}.Release|x64.Build.0 = Release|Any CPU
86 | {27F75D09-070E-4E18-BE81-1433BE8A5209}.Release|x86.ActiveCfg = Release|Any CPU
87 | {27F75D09-070E-4E18-BE81-1433BE8A5209}.Release|x86.Build.0 = Release|Any CPU
88 | {08E240AD-E2DF-40AA-B2CD-63693674BCCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
89 | {08E240AD-E2DF-40AA-B2CD-63693674BCCF}.Debug|Any CPU.Build.0 = Debug|Any CPU
90 | {08E240AD-E2DF-40AA-B2CD-63693674BCCF}.Debug|x64.ActiveCfg = Debug|Any CPU
91 | {08E240AD-E2DF-40AA-B2CD-63693674BCCF}.Debug|x64.Build.0 = Debug|Any CPU
92 | {08E240AD-E2DF-40AA-B2CD-63693674BCCF}.Debug|x86.ActiveCfg = Debug|Any CPU
93 | {08E240AD-E2DF-40AA-B2CD-63693674BCCF}.Debug|x86.Build.0 = Debug|Any CPU
94 | {08E240AD-E2DF-40AA-B2CD-63693674BCCF}.Release|Any CPU.ActiveCfg = Release|Any CPU
95 | {08E240AD-E2DF-40AA-B2CD-63693674BCCF}.Release|Any CPU.Build.0 = Release|Any CPU
96 | {08E240AD-E2DF-40AA-B2CD-63693674BCCF}.Release|x64.ActiveCfg = Release|Any CPU
97 | {08E240AD-E2DF-40AA-B2CD-63693674BCCF}.Release|x64.Build.0 = Release|Any CPU
98 | {08E240AD-E2DF-40AA-B2CD-63693674BCCF}.Release|x86.ActiveCfg = Release|Any CPU
99 | {08E240AD-E2DF-40AA-B2CD-63693674BCCF}.Release|x86.Build.0 = Release|Any CPU
100 | EndGlobalSection
101 | GlobalSection(SolutionProperties) = preSolution
102 | HideSolutionNode = FALSE
103 | EndGlobalSection
104 | GlobalSection(ExtensibilityGlobals) = postSolution
105 | SolutionGuid = {559CB3DB-A789-4A50-88A2-BDA4348B40C7}
106 | EndGlobalSection
107 | EndGlobal
108 |
--------------------------------------------------------------------------------