├── .editorconfig
├── .env
├── .gitattributes
├── .github
├── ISSUE_TEMPLATE.md
├── PULL_REQUEST_TEMPLATE.md
├── dependabot.yml
└── workflows
│ ├── cicd.yaml
│ └── codeql-analysis.yml
├── .gitignore
├── .idea
├── modules.xml
├── vcs.xml
└── workspace.xml
├── .ionide
└── symbolCache.db
├── .vscode
├── launch.json
├── settings.json
├── tasks.json
└── tasks.json.old
├── CHANGES.md
├── GitVersion.yml
├── LICENSE
├── README.md
├── assets
├── CommonAssemblyInfo.cs
├── Serilog.snk
└── serilog-sink-nuget.png
├── docker-compose.readme.txt
├── docker-compose.yml
├── nuget.config
├── sample
└── Serilog.Sinks.Elasticsearch.Sample
│ ├── Program.cs
│ ├── Serilog.Sinks.Elasticsearch.Sample.csproj
│ └── appsettings.json
├── serilog-sinks-elasticsearch.sln
├── serilog-sinks-elasticsearch.sln.DotSettings
├── serilog-sinks-elasticsearch.sln.iml
├── src
├── Serilog.Formatting.Elasticsearch
│ ├── DefaultJsonFormatter.cs
│ ├── ElasticsearchJsonFormatter.cs
│ ├── ExceptionAsObjectJsonFormatter.cs
│ ├── ISerializer.cs
│ └── Serilog.Formatting.Elasticsearch.csproj
└── Serilog.Sinks.Elasticsearch
│ ├── LoggerConfigurationElasticSearchExtensions.cs
│ ├── Serilog.Sinks.Elasticsearch.csproj
│ └── Sinks
│ └── ElasticSearch
│ ├── Durable
│ ├── APayloadReader.cs
│ ├── BookmarkFile.cs
│ ├── ControlledLevelSwitch.cs
│ ├── Elasticsearch
│ │ ├── DurableElasticsearchSink.cs
│ │ ├── ElasticsearchLogClient.cs
│ │ ├── ElasticsearchLogShipper.cs
│ │ ├── ElasticsearchPayloadReader.cs
│ │ └── RollingIntervalExtensions.cs
│ ├── ExponentialBackoffConnectionSchedule.cs
│ ├── FileSet.cs
│ ├── FileSetPosition.cs
│ ├── ILogClient.cs
│ ├── IPayloadReader.cs
│ ├── LogShipper.cs
│ └── PortableTimer.cs
│ ├── ElasticSearchSink.cs
│ ├── ElasticSearchTemplateProvider.cs
│ ├── ElasticsearchSinkOptions.cs
│ ├── ElasticsearchSinkState.cs
│ ├── ElasticsearchVersionManager.cs
│ └── SerializerAdapter.cs
└── test
├── Serilog.Sinks.Elasticsearch.IntegrationTests
├── Bootstrap
│ ├── ClientTestClusterBase.cs
│ ├── ElasticsearchSinkOptionsFactory.cs
│ ├── ProxyDetection.cs
│ ├── SerilogSinkElasticsearchXunitRunOptions.cs
│ └── XunitBootstrap.cs
├── Elasticsearch6
│ ├── Bootstrap
│ │ ├── Elasticsearch6XCluster.cs
│ │ └── Elasticsearch6XTestBase.cs
│ ├── Elasticsearch6X.cs
│ └── Elasticsearch6XUsing7X.cs
├── Elasticsearch7
│ ├── Bootstrap
│ │ ├── Elasticsearch7XCluster.cs
│ │ └── Elasticsearch7XTestBase.cs
│ ├── Elasticsearch7X.cs
│ └── Elasticsearch7XUsing6X.cs
└── Serilog.Sinks.Elasticsearch.IntegrationTests.csproj
└── Serilog.Sinks.Elasticsearch.Tests
├── BulkActionTests.cs
├── CustomIndexTypeNameTests.cs
├── Discrepancies
├── ElasticsearchDefaultSerializerTests.cs
├── ElasticsearchSinkUniformityTestsBase.cs
├── JsonNetSerializerTests.cs
└── NoSerializerTests.cs
├── Domain
└── BulkAction.cs
├── ElasticSearchLogShipperTests.cs
├── ElasticsearchJsonFormatterTests.cs
├── ElasticsearchPayloadReaderTests.cs
├── ElasticsearchSinkTests.cs
├── ExceptionAsJsonObjectFormatterTests.cs
├── FileSetTests.cs
├── IndexDeciderTests.cs
├── InlineFieldsTests.cs
├── Properties
└── AssemblyInfo.cs
├── PropertyNameTests.cs
├── RealExceptionNoSerializerTests.cs
├── RealExceptionTests.cs
├── Serilog.Sinks.Elasticsearch.Tests.csproj
├── Stubs
├── ConnectionStub.cs
└── ElasticsearchSinkTestsBase.cs
├── Templating
├── DiscoverVersionHandlesUnavailableServerTests.cs
├── DiscoverVersionTests.cs
├── DoNotRegisterTemplateIfItExists.cs
├── OverwriteTemplateTests.cs
├── RegisterCustomTemplateTests.cs
├── SendsTemplateHandlesUnavailableServerTests.cs
├── SendsTemplateTests.cs
├── Sendsv6TemplateTests.cs
├── Sendsv7TemplateTests.cs
├── Sendsv8TemplateTests.cs
├── SetElasticsearchSinkOptions.cs
├── SetFiveReplicasInTemplateTests.cs
├── SetTwoShardsInTemplateTests.cs
├── SetZeroReplicasInTemplateTests.cs
├── TemplateMatchTests.cs
├── template_v6.json
├── template_v7.json
├── template_v7_no-aliases.json
├── template_v8.json
├── template_v8_no-aliases_0replicas.json
├── template_v8_no-aliases_2shards.json
└── template_v8_no-aliases_5replicas.json
└── TestDataHelper.cs
/.editorconfig:
--------------------------------------------------------------------------------
1 | root=true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 4
6 |
7 | [*.csproj]
8 | indent_size = 2
9 |
10 | # Code files
11 | [*.{cs,csx,vb,vbx}]
12 | ###############################
13 | # Naming Conventions #
14 | ###############################
15 | # Underscore for private fields
16 | dotnet_naming_rule.private_members_with_underscore.symbols = private_fields
17 | dotnet_naming_rule.private_members_with_underscore.style = prefix_underscore
18 | dotnet_naming_rule.private_members_with_underscore.severity = suggestion
19 |
20 | dotnet_naming_symbols.private_fields.applicable_kinds = field
21 | dotnet_naming_symbols.private_fields.applicable_accessibilities = private
22 |
23 | dotnet_naming_style.prefix_underscore.capitalization = camel_case
24 | dotnet_naming_style.prefix_underscore.required_prefix = _
25 |
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | TAG=7.5.0
2 | ELASTIC_VERSION=7.5.0
3 | ELASTIC_PASSWORD=changeme
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 |
3 | * text=auto
4 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | **A few questions before you begin:**
2 |
3 | > Is this an issue related to the [Serilog core project](https://github.com/serilog/serilog) or one of the [sinks](https://github.com/serilog/serilog/wiki/Provided-Sinks) or
4 | [community projects](https://github.com/serilog/serilog/wiki/Community-Projects).
5 | This issue list is intended for Serilog Elasticsearch Sink issues. If this issue relates to another sink or to the code project,
6 | please log on the related repository. Please use [Gitter chat](https://gitter.im/serilog/serilog) and [Stack Overflow](http://stackoverflow.com/questions/tagged/serilog) for discussions and questions.
7 |
8 |
9 | **Does this issue relate to a new *feature* or an existing *bug*?**
10 | - [ ] Bug
11 | - [ ] New Feature
12 |
13 | **What version of Serilog.Sinks.Elasticsearch is affected? Please list the related NuGet package.**
14 |
15 |
16 | **What is the target framework and operating system? See [target frameworks](https://docs.microsoft.com/en-us/nuget/schema/target-frameworks) & [net standard matrix](https://docs.microsoft.com/en-us/dotnet/standard/net-standard).**
17 |
18 | - [ ] netCore 2.0
19 | - [ ] netCore 1.0
20 | - [ ] 4.7
21 | - [ ] 4.6.x
22 | - [ ] 4.5.x
23 |
24 | **Please describe the current behavior?**
25 |
26 |
27 | **Please describe the expected behavior?**
28 |
29 |
30 | **If the current behavior is a bug, please provide the steps to reproduce the issue and if possible a minimal demo of the problem**
31 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | **What issue does this PR address?**
2 |
3 |
4 | **Does this PR introduce a breaking change?**
5 |
6 |
7 | **Please check if the PR fulfills these requirements**
8 | - [ ] The commit follows our [guidelines](https://github.com/serilog/serilog/blob/dev/CONTRIBUTING.md)
9 | - [ ] Unit Tests for the changes have been added (for bug fixes / features)
10 |
11 | **Other information**:
12 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "nuget"
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: "daily"
12 |
13 | - package-ecosystem: "github-actions"
14 | directory: "/" # Location of package manifests
15 | schedule:
16 | interval: "daily"
--------------------------------------------------------------------------------
/.github/workflows/cicd.yaml:
--------------------------------------------------------------------------------
1 | name: Continuous Integration
2 |
3 | on:
4 | push:
5 | pull_request:
6 | release:
7 | types:
8 | - published
9 | env:
10 | Configuration: Release
11 | ContinuousIntegrationBuild: true
12 | DOTNET_CLI_TELEMETRY_OPTOUT: true
13 | DOTNET_NOLOGO: true
14 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
15 |
16 | # GitHub Packages Feed settings
17 | GITHUB_FEED: https://nuget.pkg.github.com/serilog-contrib/
18 | GITHUB_USER: mivano
19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
20 |
21 | jobs:
22 | package:
23 | runs-on: ubuntu-latest
24 | name: Run tests and create NuGet package
25 | outputs:
26 | coverage-reports: ${{ steps.dotnet-test.outputs.coverage-reports }}
27 | version: ${{ steps.dotnet-pack.outputs.version }}
28 | nupkg-filename: ${{ steps.dotnet-pack.outputs.nupkg-filename }}
29 | release-body: ${{ steps.tag-message.outputs.release-notes }}
30 | steps:
31 | - name: Checkout git repository
32 | uses: actions/checkout@v3
33 | with:
34 | fetch-depth: 0
35 |
36 | - name: Install .NET SDK
37 | uses: actions/setup-dotnet@v3.0.3
38 | with:
39 | dotnet-version: '7.0.x'
40 |
41 | - name: Retrieve cached NuGet packages
42 | uses: actions/cache@v3
43 | with:
44 | path: ~/.nuget/packages
45 | key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
46 |
47 | - name: Restore NuGet packages
48 | run: dotnet restore
49 |
50 | - name: Build solution
51 | run: dotnet build --no-restore -c Release
52 |
53 | - name: Run tests
54 | run: dotnet test --no-build -c Release --logger "html;LogFileName=TestResults-${{ runner.os }}.html" --logger "trx;LogFileName=TestResults-${{ runner.os }}.trx" --logger GitHubActions
55 | id: dotnet-test
56 |
57 | - name: Upload received files from failing tests
58 | uses: actions/upload-artifact@v3
59 | if: failure()
60 | with:
61 | name: Received-${{ runner.os }}
62 | path: "**/*.received.*"
63 |
64 | - name: Upload test results
65 | uses: actions/upload-artifact@v3
66 | if: always()
67 | with:
68 | name: TestResults-${{ runner.os }}
69 | path: test/Serilog.Sinks.Elasticsearch.Tests/TestResults/TestResults-${{ runner.os }}.html
70 |
71 | - name: Test Report
72 | uses: dorny/test-reporter@v1
73 | if: always()
74 | with:
75 | name: Test Results (${{ runner.os }})
76 | path: '**.trx'
77 | reporter: dotnet-trx
78 |
79 | - name: Create NuGet packages
80 | run: dotnet pack --no-build -c Release --version-suffix "ci-$GITHUB_RUN_ID" --include-symbols --include-source --output .
81 | id: dotnet-pack
82 |
83 | - name: Upload NuGet package artifact
84 | uses: actions/upload-artifact@v3
85 | with:
86 | name: nuget
87 | path: '**/*.nupkg'
88 |
89 | prerelease:
90 | needs: package
91 | name: Create prerelease
92 | if: github.ref == 'refs/heads/dev'
93 | runs-on: ubuntu-latest
94 | steps:
95 | - name: Download Artifact
96 | uses: actions/download-artifact@v3
97 | with:
98 | name: nuget
99 | path: nuget
100 | - name: Push to GitHub Feed
101 | run: |
102 | dotnet nuget add source --username USERNAME --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/serilog-contrib/index.json"
103 |
104 | for f in ./nuget/*.nupkg
105 | do
106 | echo $f
107 | dotnet nuget push $f --source "github" --api-key ${{ secrets.GITHUB_TOKEN }} --skip-duplicate
108 | done
109 |
110 | publish:
111 | runs-on: ubuntu-latest
112 | needs: package
113 | if: github.event_name == 'release'
114 | name: Publish NuGet package
115 | steps:
116 | - name: Checkout git repository
117 | uses: actions/checkout@v3
118 | with:
119 | fetch-depth: 0
120 |
121 | - name: Install .NET SDK
122 | uses: actions/setup-dotnet@v3.0.3
123 |
124 | - name: Retrieve cached NuGet packages
125 | uses: actions/cache@v3
126 | with:
127 | path: ~/.nuget/packages
128 | key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
129 |
130 | - name: Restore NuGet packages
131 | run: dotnet restore
132 |
133 | - name: Create Release NuGet package
134 | run: |
135 | arrTag=(${GITHUB_REF//\// })
136 | VERSION="${arrTag[2]}"
137 | VERSION="${VERSION//v}"
138 | dotnet pack -v normal -c Release --include-symbols --include-source -p:Version=$VERSION -o ./nuget
139 |
140 | - name: Push to GitHub Feed
141 | run: |
142 | dotnet nuget add source --username $GITHUB_USER --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/serilog-contrib/index.json"
143 |
144 | for f in ./nuget/*.nupkg
145 | do
146 | dotnet nuget push $f --source "github" --api-key ${{ secrets.GITHUB_TOKEN }} --skip-duplicate
147 | done
148 |
149 | - name: Publish NuGet package on nuget.org
150 | run: dotnet nuget push ./nuget/*.nupkg --api-key "${{ secrets.NUGET_API_KEY }}" --skip-duplicate
151 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | # ******** NOTE ********
12 |
13 | name: "CodeQL"
14 |
15 | on:
16 | push:
17 | branches: [ dev, master ]
18 | pull_request:
19 | # The branches below must be a subset of the branches above
20 | branches: [ dev ]
21 | schedule:
22 | - cron: '27 7 * * 6'
23 |
24 | jobs:
25 | analyze:
26 | name: Analyze
27 | runs-on: ubuntu-latest
28 |
29 | strategy:
30 | fail-fast: false
31 | matrix:
32 | language: [ 'csharp' ]
33 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
34 | # Learn more...
35 | # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
36 |
37 | steps:
38 | - name: Checkout repository
39 | uses: actions/checkout@v3
40 |
41 | - name: Setup dotnet
42 | uses: actions/setup-dotnet@v3.0.3
43 | with:
44 | dotnet-version: '6.0.x'
45 |
46 | # Initializes the CodeQL tools for scanning.
47 | - name: Initialize CodeQL
48 | uses: github/codeql-action/init@v2
49 | with:
50 | languages: ${{ matrix.language }}
51 | # If you wish to specify custom queries, you can do so here or in a config file.
52 | # By default, queries listed here will override any specified in a config file.
53 | # Prefix the list here with "+" to use these queries and those in the config file.
54 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
55 |
56 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
57 | # If this step fails, then you should remove it and run the build manually (see below)
58 | - name: Autobuild
59 | uses: github/codeql-action/autobuild@v2
60 |
61 | # ℹ️ Command-line programs to run using the OS shell.
62 | # 📚 https://git.io/JvXDl
63 |
64 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
65 | # and modify them (or add more) to build your code if your project
66 | # uses a compiled language
67 |
68 | #- run: |
69 | # make bootstrap
70 | # make release
71 |
72 | - name: Perform CodeQL Analysis
73 | uses: github/codeql-action/analyze@v2
74 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.sln.docstates
8 |
9 | # Build results
10 | [Dd]ebug/
11 | [Dd]ebugPublic/
12 | [Rr]elease/
13 | [Rr]eleases/
14 | x64/
15 | x86/
16 | build/
17 | bld/
18 | [Bb]in/
19 | [Oo]bj/
20 |
21 | # Roslyn cache directories
22 | *.ide/
23 |
24 | # MSTest test Results
25 | [Tt]est[Rr]esult*/
26 | [Bb]uild[Ll]og.*
27 |
28 | #NUNIT
29 | *.VisualState.xml
30 | TestResult.xml
31 |
32 | # Build Results of an ATL Project
33 | [Dd]ebugPS/
34 | [Rr]eleasePS/
35 | dlldata.c
36 |
37 | buffer*.json
38 | *.bookmark
39 | failures.txt
40 |
41 | *_i.c
42 | *_p.c
43 | *_i.h
44 | *.ilk
45 | *.meta
46 | *.obj
47 | *.pch
48 | *.pdb
49 | *.pgc
50 | *.pgd
51 | *.rsp
52 | *.sbr
53 | *.tlb
54 | *.tli
55 | *.tlh
56 | *.tmp
57 | *.tmp_proj
58 | *.log
59 | *.vspscc
60 | *.vssscc
61 | .builds
62 | *.pidb
63 | *.svclog
64 | *.scc
65 |
66 | # Chutzpah Test files
67 | _Chutzpah*
68 |
69 | # Visual C++ cache files
70 | ipch/
71 | *.aps
72 | *.ncb
73 | *.opensdf
74 | *.sdf
75 | *.cachefile
76 |
77 | # Visual Studio profiler
78 | *.psess
79 | *.vsp
80 | *.vspx
81 |
82 | # TFS 2012 Local Workspace
83 | $tf/
84 |
85 | # Guidance Automation Toolkit
86 | *.gpState
87 |
88 | # ReSharper is a .NET coding add-in
89 | _ReSharper*/
90 | *.[Rr]e[Ss]harper
91 | *.DotSettings.user
92 |
93 | # JustCode is a .NET coding addin-in
94 | .JustCode
95 |
96 | # TeamCity is a build add-in
97 | _TeamCity*
98 |
99 | # DotCover is a Code Coverage Tool
100 | *.dotCover
101 |
102 | # NCrunch
103 | _NCrunch_*
104 | .*crunch*.local.xml
105 | *.ncrunch*
106 |
107 | # MightyMoose
108 | *.mm.*
109 | AutoTest.Net/
110 |
111 | # Web workbench (sass)
112 | .sass-cache/
113 |
114 | # Installshield output folder
115 | [Ee]xpress/
116 |
117 | # DocProject is a documentation generator add-in
118 | DocProject/buildhelp/
119 | DocProject/Help/*.HxT
120 | DocProject/Help/*.HxC
121 | DocProject/Help/*.hhc
122 | DocProject/Help/*.hhk
123 | DocProject/Help/*.hhp
124 | DocProject/Help/Html2
125 | DocProject/Help/html
126 |
127 | # Click-Once directory
128 | publish/
129 |
130 | # Publish Web Output
131 | *.[Pp]ublish.xml
132 | *.azurePubxml
133 | # TODO: Comment the next line if you want to checkin your web deploy settings
134 | # but database connection strings (with potential passwords) will be unencrypted
135 | *.pubxml
136 | *.publishproj
137 |
138 | # NuGet Packages
139 | *.nupkg
140 | # The packages folder can be ignored because of Package Restore
141 | **/packages/*
142 | # except build/, which is used as an MSBuild target.
143 | !**/packages/build/
144 | # If using the old MSBuild-Integrated Package Restore, uncomment this:
145 | #!**/packages/repositories.config
146 |
147 | # Windows Azure Build Output
148 | csx/
149 | *.build.csdef
150 |
151 | # Windows Store app package directory
152 | AppPackages/
153 |
154 | # Others
155 | sql/
156 | *.Cache
157 | ClientBin/
158 | [Ss]tyle[Cc]op.*
159 | ~$*
160 | *~
161 | *.dbmdl
162 | *.dbproj.schemaview
163 | *.pfx
164 | *.publishsettings
165 | node_modules/
166 |
167 | # RIA/Silverlight projects
168 | Generated_Code/
169 |
170 | # Backup & report files from converting an old project file
171 | # to a newer Visual Studio version. Backup files are not needed,
172 | # because we have git ;-)
173 | _UpgradeReport_Files/
174 | Backup*/
175 | UpgradeLog*.XML
176 | UpgradeLog*.htm
177 |
178 | # SQL Server files
179 | *.mdf
180 | *.ldf
181 |
182 | # Business Intelligence projects
183 | *.rdl.data
184 | *.bim.layout
185 | *.bim_*.settings
186 |
187 | # Microsoft Fakes
188 | FakesAssemblies/
189 |
190 | project.lock.json
191 | .vs
192 |
193 | # JetBrains Rider
194 | .idea/
195 | *.sln.iml
196 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.ionide/symbolCache.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serilog-contrib/serilog-sinks-elasticsearch/1e9777c3034c2d8d078f60822c77b9caad5b7870/.ionide/symbolCache.db
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to find out which attributes exist for C# debugging
3 | // Use hover for the description of the existing attributes
4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": ".NET Core Launch (console)",
9 | "type": "coreclr",
10 | "request": "launch",
11 | "preLaunchTask": "build",
12 | // If you have changed target frameworks, make sure to update the program path.
13 | "program": "${workspaceRoot}/sample/Serilog.Sinks.Elasticsearch.Sample/bin/Debug/netcoreapp1.1/Serilog.Sinks.Elasticsearch.Sample.dll",
14 | "args": [],
15 | "cwd": "${workspaceRoot}/sample/Serilog.Sinks.Elasticsearch.Sample",
16 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window
17 | "console": "internalConsole",
18 | "stopAtEntry": false,
19 | "internalConsoleOptions": "openOnSessionStart"
20 | },
21 | {
22 | "name": ".NET Core Attach",
23 | "type": "coreclr",
24 | "request": "attach",
25 | "processId": "${command:pickProcess}"
26 | }
27 | ]
28 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "powershell.codeFormatting.addWhitespaceAroundPipe": true
3 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "command": "dotnet",
4 | "args": [],
5 | "tasks": [
6 | {
7 | "label": "build",
8 | "type": "shell",
9 | "command": "dotnet",
10 | "args": [
11 | "build",
12 | "${workspaceRoot}/sample/Serilog.Sinks.Elasticsearch.Sample/Serilog.Sinks.Elasticsearch.Sample.csproj"
13 | ],
14 | "problemMatcher": "$msCompile",
15 | "group": {
16 | "_id": "build",
17 | "isDefault": false
18 | }
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json.old:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.1.0",
3 | "command": "dotnet",
4 | "isShellCommand": true,
5 | "args": [],
6 | "tasks": [
7 | {
8 | "taskName": "build",
9 | "args": [
10 | "${workspaceRoot}/sample/Serilog.Sinks.Elasticsearch.Sample/Serilog.Sinks.Elasticsearch.Sample.csproj"
11 | ],
12 | "isBuildCommand": true,
13 | "problemMatcher": "$msCompile"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/GitVersion.yml:
--------------------------------------------------------------------------------
1 | next-version: 8.3.0
2 | branches: {}
3 | ignore:
4 | sha: []
5 |
--------------------------------------------------------------------------------
/assets/CommonAssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 |
3 | [assembly: AssemblyVersion("0.0.0.0")]
4 | [assembly: AssemblyFileVersion("0.0.0.0")]
5 | [assembly: AssemblyInformationalVersion("0.0.0")]
6 |
--------------------------------------------------------------------------------
/assets/Serilog.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serilog-contrib/serilog-sinks-elasticsearch/1e9777c3034c2d8d078f60822c77b9caad5b7870/assets/Serilog.snk
--------------------------------------------------------------------------------
/assets/serilog-sink-nuget.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serilog-contrib/serilog-sinks-elasticsearch/1e9777c3034c2d8d078f60822c77b9caad5b7870/assets/serilog-sink-nuget.png
--------------------------------------------------------------------------------
/docker-compose.readme.txt:
--------------------------------------------------------------------------------
1 | Elastic (http://localhost:9200)= elastic:changeme
2 | Elastic Head: http://localhost:9100/?auth_user=elastic&auth_password=changeme
3 | Kibana: http://localhost:5601
4 |
5 | To start the components:
6 | docker-compose up
7 |
8 | add the -d option to run in the background
9 |
10 | To scale the nodes:
11 |
12 | docker-compose up --scale elasticsearch=3
13 |
14 | To shutdown:
15 |
16 | docker-compose down
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 |
3 | services:
4 |
5 | elasticsearch:
6 | image: docker.elastic.co/elasticsearch/elasticsearch:${TAG}
7 | volumes:
8 | - esdata2:/usr/share/elasticsearch/data
9 | ports:
10 | - "9200:9200"
11 | - "9300"
12 | ulimits:
13 | memlock:
14 | soft: -1
15 | hard: -1
16 | mem_limit: 1g
17 | environment:
18 | - cluster.name=docker-cluster
19 | - discovery.type=single-node
20 | - bootstrap.memory_lock=true
21 | - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
22 | - node.master=true
23 | - node.data=true
24 | - http.cors.enabled=true
25 | - http.cors.allow-origin=*
26 | - http.cors.allow-headers=Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With
27 | - http.cors.allow-credentials=true
28 | networks:
29 | - elk
30 |
31 | kibana:
32 | image: docker.elastic.co/kibana/kibana:${TAG}
33 | environment:
34 | - "ELASTICSEARCH_URL=http://elasticsearch:9200"
35 | - server.name=kibana
36 | - server.host="0"
37 | - xpack.security.enabled=true
38 | - xpack.monitoring.enabled=true
39 | - xpack.ml.enabled=false
40 | - xpack.graph.enabled=false
41 | - xpack.reporting.enabled=false
42 | - xpack.grokdebugger.enabled=false
43 | ports:
44 | - "5601:5601"
45 | networks:
46 | - esnet
47 | depends_on:
48 | - elasticsearch
49 |
50 | head:
51 | image: mobz/elasticsearch-head:5
52 | ulimits:
53 | memlock:
54 | soft: -1
55 | hard: -1
56 | mem_limit: 1g
57 | ports:
58 | - "9100:9100"
59 | networks:
60 | - elk
61 |
62 | kibana:
63 | image: docker.elastic.co/kibana/kibana:${TAG}
64 | environment:
65 | - "ELASTICSEARCH_URL=http://elasticsearch:9200"
66 | ports:
67 | - "5601:5601"
68 | networks:
69 | - elk
70 | depends_on:
71 | - elasticsearch
72 |
73 | volumes:
74 | esdata1:
75 | driver: local
76 | esdata2:
77 | driver: local
78 | #esconfig1:
79 | # driver: local
80 | #esconfig2:
81 | # driver: local
82 |
83 | networks:
84 | elk:
85 | driver: bridge
--------------------------------------------------------------------------------
/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/sample/Serilog.Sinks.Elasticsearch.Sample/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Reflection.Metadata.Ecma335;
5 | using System.Threading;
6 | using Microsoft.Extensions.Configuration;
7 | using Serilog;
8 | using Serilog.Core;
9 | using Serilog.Debugging;
10 | using Serilog.Events;
11 | using Serilog.Formatting.Json;
12 | using Serilog.Sinks.File;
13 | using Serilog.Sinks.SystemConsole.Themes;
14 |
15 | namespace Serilog.Sinks.Elasticsearch.Sample
16 | {
17 | class Program
18 | {
19 | private static IConfiguration Configuration { get; } = new ConfigurationBuilder()
20 | .SetBasePath(Directory.GetCurrentDirectory())
21 | .AddJsonFile("appsettings.json", true, true)
22 | .AddEnvironmentVariables()
23 | .Build();
24 |
25 | static void Main(string[] args)
26 | {
27 |
28 | // Enable the selflog output
29 | SelfLog.Enable(Console.Error);
30 | Log.Logger = new LoggerConfiguration()
31 | .MinimumLevel.Debug()
32 | .WriteTo.Console(theme: SystemConsoleTheme.Literate)
33 | .WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri(Configuration.GetConnectionString("elasticsearch"))) // for the docker-compose implementation
34 | {
35 | AutoRegisterTemplate = true,
36 | OverwriteTemplate = true,
37 | DetectElasticsearchVersion = true,
38 | AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv7,
39 | NumberOfReplicas = 1,
40 | NumberOfShards = 2,
41 | //BufferBaseFilename = "./buffer",
42 | // RegisterTemplateFailure = RegisterTemplateRecovery.FailSink,
43 | FailureCallback = (e, ex) => Console.WriteLine("Unable to submit event " + e.MessageTemplate),
44 | EmitEventFailure = EmitEventFailureHandling.WriteToSelfLog |
45 | EmitEventFailureHandling.WriteToFailureSink |
46 | EmitEventFailureHandling.RaiseCallback,
47 | FailureSink = new FileSink("./fail-{Date}.txt", new JsonFormatter(), null, null)
48 | })
49 | .CreateLogger();
50 |
51 | Log.Information("Hello, world!");
52 |
53 | int a = 10, b = 0;
54 | try
55 | {
56 | Log.Debug("Dividing {A} by {B}", a, b);
57 | Console.WriteLine(a / b);
58 | }
59 | catch (Exception ex)
60 | {
61 | Log.Error(ex, "Something went wrong");
62 | }
63 |
64 | // Introduce a failure by storing a field as a different type
65 | Log.Debug("Reusing {A} by {B}", "string", true);
66 |
67 | Log.CloseAndFlush();
68 | Console.WriteLine("Press any key to continue...");
69 | while (!Console.KeyAvailable)
70 | {
71 | Thread.Sleep(500);
72 | }
73 | }
74 |
75 |
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/sample/Serilog.Sinks.Elasticsearch.Sample/Serilog.Sinks.Elasticsearch.Sample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | Always
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/sample/Serilog.Sinks.Elasticsearch.Sample/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "ConnectionStrings": {
3 | "elasticsearch": "http://elastic:changeme@localhost:9200"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/serilog-sinks-elasticsearch.sln:
--------------------------------------------------------------------------------
1 | Microsoft Visual Studio Solution File, Format Version 12.00
2 | # Visual Studio 15
3 | VisualStudioVersion = 15.0.26730.3
4 | MinimumVisualStudioVersion = 10.0.40219.1
5 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Files", "Files", "{148431F6-5BA9-4987-80CF-DF9F23F54947}"
6 | ProjectSection(SolutionItems) = preProject
7 | .editorconfig = .editorconfig
8 | .gitattributes = .gitattributes
9 | .gitignore = .gitignore
10 | CHANGES.md = CHANGES.md
11 | LICENSE = LICENSE
12 | nuget.config = nuget.config
13 | README.md = README.md
14 | GitVersion.yml = GitVersion.yml
15 | EndProjectSection
16 | EndProject
17 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Sinks.Elasticsearch", "src\Serilog.Sinks.Elasticsearch\Serilog.Sinks.Elasticsearch.csproj", "{EEB0D119-687E-444E-BF14-9BDAEC9BA3EF}"
18 | EndProject
19 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Sinks.Elasticsearch.Tests", "test\Serilog.Sinks.Elasticsearch.Tests\Serilog.Sinks.Elasticsearch.Tests.csproj", "{CA358673-C4B6-49D0-8EC5-D6CB50A11CC0}"
20 | EndProject
21 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serilog.Sinks.Elasticsearch.Sample", "sample\Serilog.Sinks.Elasticsearch.Sample\Serilog.Sinks.Elasticsearch.Sample.csproj", "{253B37AB-D82E-4A5F-BA16-F1BE398818C8}"
22 | EndProject
23 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serilog.Formatting.Elasticsearch", "src\Serilog.Formatting.Elasticsearch\Serilog.Formatting.Elasticsearch.csproj", "{0E6D34BF-322A-4803-94D1-355F6D5024BE}"
24 | EndProject
25 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Sinks.Elasticsearch.IntegrationTests", "test\Serilog.Sinks.Elasticsearch.IntegrationTests\Serilog.Sinks.Elasticsearch.IntegrationTests.csproj", "{23BC3821-E028-48B4-8F2C-83BB1B8B5525}"
26 | EndProject
27 | Global
28 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
29 | Debug|Any CPU = Debug|Any CPU
30 | Release|Any CPU = Release|Any CPU
31 | EndGlobalSection
32 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
33 | {EEB0D119-687E-444E-BF14-9BDAEC9BA3EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
34 | {EEB0D119-687E-444E-BF14-9BDAEC9BA3EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
35 | {EEB0D119-687E-444E-BF14-9BDAEC9BA3EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
36 | {EEB0D119-687E-444E-BF14-9BDAEC9BA3EF}.Release|Any CPU.Build.0 = Release|Any CPU
37 | {CA358673-C4B6-49D0-8EC5-D6CB50A11CC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
38 | {CA358673-C4B6-49D0-8EC5-D6CB50A11CC0}.Debug|Any CPU.Build.0 = Debug|Any CPU
39 | {CA358673-C4B6-49D0-8EC5-D6CB50A11CC0}.Release|Any CPU.ActiveCfg = Release|Any CPU
40 | {CA358673-C4B6-49D0-8EC5-D6CB50A11CC0}.Release|Any CPU.Build.0 = Release|Any CPU
41 | {253B37AB-D82E-4A5F-BA16-F1BE398818C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
42 | {253B37AB-D82E-4A5F-BA16-F1BE398818C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
43 | {253B37AB-D82E-4A5F-BA16-F1BE398818C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
44 | {253B37AB-D82E-4A5F-BA16-F1BE398818C8}.Release|Any CPU.Build.0 = Release|Any CPU
45 | {0E6D34BF-322A-4803-94D1-355F6D5024BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
46 | {0E6D34BF-322A-4803-94D1-355F6D5024BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
47 | {0E6D34BF-322A-4803-94D1-355F6D5024BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
48 | {0E6D34BF-322A-4803-94D1-355F6D5024BE}.Release|Any CPU.Build.0 = Release|Any CPU
49 | {23BC3821-E028-48B4-8F2C-83BB1B8B5525}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
50 | {23BC3821-E028-48B4-8F2C-83BB1B8B5525}.Debug|Any CPU.Build.0 = Debug|Any CPU
51 | {23BC3821-E028-48B4-8F2C-83BB1B8B5525}.Release|Any CPU.ActiveCfg = Release|Any CPU
52 | {23BC3821-E028-48B4-8F2C-83BB1B8B5525}.Release|Any CPU.Build.0 = Release|Any CPU
53 | EndGlobalSection
54 | GlobalSection(SolutionProperties) = preSolution
55 | HideSolutionNode = FALSE
56 | EndGlobalSection
57 | GlobalSection(ExtensibilityGlobals) = postSolution
58 | SolutionGuid = {2A076FF0-B90E-451A-B858-3144CE509516}
59 | EndGlobalSection
60 | EndGlobal
61 |
--------------------------------------------------------------------------------
/serilog-sinks-elasticsearch.sln.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | True
3 | True
4 | True
5 | True
6 | True
7 | True
8 | True
--------------------------------------------------------------------------------
/serilog-sinks-elasticsearch.sln.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/Serilog.Formatting.Elasticsearch/ExceptionAsObjectJsonFormatter.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2016 Serilog Contributors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 |
16 | using System;
17 | using System.IO;
18 |
19 | namespace Serilog.Formatting.Elasticsearch
20 | {
21 | ///
22 | /// A JSON formatter which plays nice with Kibana,
23 | /// by serializing any exception into an exception object, instead of relying on
24 | /// an array of the exceptions and the inner exception.
25 | ///
26 | /// Note that using this formatter comes at the cost that the exception tree
27 | /// with inner exceptions can grow deep.
28 | ///
29 | public class ExceptionAsObjectJsonFormatter : ElasticsearchJsonFormatter
30 | {
31 | ///
32 | /// Constructs a .
33 | ///
34 | /// If true, the properties of the event will be written to
35 | /// the output without enclosing braces. Otherwise, if false, each event will be written as a well-formed
36 | /// JSON object.
37 | /// A string that will be written after each log event is formatted.
38 | /// If null, will be used. Ignored if
39 | /// is true.
40 | /// If true, the message will be rendered and written to the output as a
41 | /// property named RenderedMessage.
42 | /// Supplies culture-specific formatting information, or null.
43 | /// Inject a serializer to force objects to be serialized over being ToString()
44 | /// When set to true values will be written at the root of the json document
45 | /// If true, the message template will be rendered and written to the output as a
46 | /// property named RenderedMessageTemplate.
47 | /// If true, splits the StackTrace by new line and writes it as a an array of strings
48 | public ExceptionAsObjectJsonFormatter(bool omitEnclosingObject = false,
49 | string closingDelimiter = null,
50 | bool renderMessage = false,
51 | IFormatProvider formatProvider = null,
52 | ISerializer serializer = null,
53 | bool inlineFields = false,
54 | bool renderMessageTemplate = true,
55 | bool formatStackTraceAsArray = false)
56 | : base(omitEnclosingObject, closingDelimiter, renderMessage, formatProvider, serializer, inlineFields, renderMessageTemplate, formatStackTraceAsArray)
57 | {
58 | }
59 |
60 | ///
61 | /// Writes out the attached exception
62 | ///
63 | protected override void WriteException(Exception exception, ref string delim, TextWriter output)
64 | {
65 | output.Write(delim);
66 | output.Write("\"");
67 | output.Write("exception");
68 | output.Write("\":{");
69 | WriteExceptionTree(exception, ref delim, output, 0);
70 | output.Write("}");
71 | }
72 |
73 | private void WriteExceptionTree(Exception exception, ref string delim, TextWriter output, int depth)
74 | {
75 | delim = "";
76 | WriteSingleException(exception, ref delim, output, depth);
77 | exception = exception.InnerException;
78 | if (exception != null)
79 | {
80 | output.Write(",");
81 | output.Write("\"innerException\":{");
82 | WriteExceptionTree(exception, ref delim, output, depth + 1);
83 | output.Write("}");
84 | }
85 | }
86 | }
87 | }
--------------------------------------------------------------------------------
/src/Serilog.Formatting.Elasticsearch/ISerializer.cs:
--------------------------------------------------------------------------------
1 | namespace Serilog.Formatting.Elasticsearch
2 | {
3 | ///
4 | /// Defines a method to serialize custom value to string
5 | ///
6 | public interface ISerializer
7 | {
8 | ///
9 | /// Serializes object to string
10 | ///
11 | /// Object to serialization
12 | /// String representation of object
13 | string SerializeToString(object value);
14 | }
15 | }
--------------------------------------------------------------------------------
/src/Serilog.Formatting.Elasticsearch/Serilog.Formatting.Elasticsearch.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Michiel van Oudheusden, Martijn Laarman, Mogens Heller Grabe, Serilog Contributors
4 | Serilog.Sinks.Elasticsearch
5 | Serilog sink for Elasticsearch
6 | Copyright © Serilog Contributors 2023
7 |
8 |
9 |
10 | netstandard2.0
11 | true
12 | true
13 | Serilog.Formatting.Elasticsearch
14 | ../../assets/Serilog.snk
15 | true
16 | false
17 | Serilog.Formatting.Elasticsearch
18 | serilog;elasticsearch;logging;event;formatting
19 | https://github.com/serilog-contrib/serilog-sinks-elasticsearch/blob/master/CHANGES.md
20 | serilog-sink-nuget.png
21 | https://github.com/serilog-contrib/serilog-sinks-elasticsearch
22 | Apache-2.0
23 | https://github.com/serilog-contrib/serilog-sinks-elasticsearch
24 | git
25 | Serilog.Formatting.ElasticSearch
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Elasticsearch/Serilog.Sinks.Elasticsearch.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Michiel van Oudheusden, Martijn Laarman, Mogens Heller Grabe, Serilog Contributors
4 | Serilog.Sinks.Elasticsearch
5 | Serilog sink for Elasticsearch
6 | Copyright © Serilog Contributors 2023
7 | netstandard2.0;net6.0
8 | true
9 | latest
10 | true
11 | Serilog.Sinks.Elasticsearch
12 | ../../assets/Serilog.snk
13 | true
14 | false
15 | Serilog.Sinks.Elasticsearch
16 | serilog;elasticsearch;logging;event;collector
17 | https://github.com/serilog-contrib/serilog-sinks-elasticsearch/blob/master/CHANGES.md
18 | serilog-sink-nuget.png
19 | https://github.com/serilog-contrib/serilog-sinks-elasticsearch
20 | https://github.com/serilog-contrib/serilog-sinks-elasticsearch
21 | Apache-2.0
22 | git
23 | Serilog
24 | README.md
25 |
26 |
27 |
28 | 1591;1701;1702
29 | $(DefineConstants);DURABLE;THREADING_TIMER
30 |
31 |
32 |
33 | 1591;1701;1702
34 | $(DefineConstants);DURABLE;THREADING_TIMER;ASYNC_DISPOSABLE
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Elasticsearch/Sinks/ElasticSearch/Durable/APayloadReader.cs:
--------------------------------------------------------------------------------
1 | // Serilog.Sinks.Seq Copyright 2017 Serilog Contributors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #if DURABLE
16 |
17 | using System;
18 | using System.IO;
19 | using System.Text;
20 | using Serilog.Debugging;
21 |
22 | namespace Serilog.Sinks.Elasticsearch.Durable
23 | {
24 | ///
25 | /// Abstract payload reader
26 | /// Generic version of https://github.com/serilog/serilog-sinks-seq/blob/v4.0.0/src/Serilog.Sinks.Seq/Sinks/Seq/Durable/PayloadReader.cs
27 | ///
28 | ///
29 | public abstract class APayloadReader : IPayloadReader
30 | {
31 | ///
32 | ///
33 | ///
34 | ///
35 | public abstract TPayload GetNoPayload();
36 |
37 | ///
38 | ///
39 | ///
40 | ///
41 | ///
42 | ///
43 | ///
44 | ///
45 | ///
46 | public TPayload ReadPayload(int batchPostingLimit, long? eventBodyLimitBytes, ref FileSetPosition position, ref int count,string fileName)
47 | {
48 | InitPayLoad(fileName);
49 |
50 | using (var current = System.IO.File.Open(position.File, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
51 | {
52 | var nextLineStart = position.NextLineStart;
53 | while (count < batchPostingLimit && TryReadLine(current, ref nextLineStart, out var nextLine))
54 | {
55 | position = new FileSetPosition(nextLineStart, position.File);
56 |
57 | // Count is the indicator that work was done, so advances even in the (rare) case an
58 | // oversized event is dropped.
59 | ++count;
60 |
61 | if (eventBodyLimitBytes.HasValue && Encoding.UTF8.GetByteCount(nextLine) > eventBodyLimitBytes.Value)
62 | {
63 | SelfLog.WriteLine(
64 | "Event JSON representation exceeds the byte size limit of {0} and will be dropped; data: {1}",
65 | eventBodyLimitBytes, nextLine);
66 | }
67 | else
68 | {
69 | AddToPayLoad(nextLine);
70 | }
71 | }
72 | }
73 | return FinishPayLoad();
74 | }
75 | ///
76 | ///
77 | ///
78 | ///
79 | protected abstract void InitPayLoad(string fileName);
80 | ///
81 | ///
82 | ///
83 | ///
84 | protected abstract TPayload FinishPayLoad();
85 | ///
86 | ///
87 | ///
88 | ///
89 | protected abstract void AddToPayLoad(string nextLine);
90 |
91 | // It would be ideal to chomp whitespace here, but not required.
92 | private static bool TryReadLine(Stream current, ref long nextStart, out string nextLine)
93 | {
94 | var includesBom = nextStart == 0;
95 |
96 | if (current.Length <= nextStart)
97 | {
98 | nextLine = null;
99 | return false;
100 | }
101 |
102 | current.Position = nextStart;
103 |
104 | // Important not to dispose this StreamReader as the stream must remain open.
105 | var reader = new StreamReader(current, Encoding.UTF8, false, 128);
106 | nextLine = reader.ReadLine();
107 |
108 | if (nextLine == null)
109 | return false;
110 |
111 | nextStart += Encoding.UTF8.GetByteCount(nextLine) + Encoding.UTF8.GetByteCount(Environment.NewLine);
112 | if (includesBom)
113 | nextStart += 3;
114 |
115 | return true;
116 | }
117 | }
118 | }
119 |
120 | #endif
121 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Elasticsearch/Sinks/ElasticSearch/Durable/BookmarkFile.cs:
--------------------------------------------------------------------------------
1 | // Serilog.Sinks.Seq Copyright 2017 Serilog Contributors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #if DURABLE
16 |
17 | using System;
18 | using System.IO;
19 | using System.Text;
20 |
21 | namespace Serilog.Sinks.Elasticsearch.Durable
22 | {
23 | ///
24 | /// https://github.com/serilog/serilog-sinks-seq/blob/v4.0.0/src/Serilog.Sinks.Seq/Sinks/Seq/Durable/BookmarkFile.cs
25 | ///
26 | sealed class BookmarkFile : IDisposable
27 | {
28 | readonly FileStream _bookmark;
29 |
30 | public BookmarkFile(string bookmarkFilename)
31 | {
32 | _bookmark = System.IO.File.Open(bookmarkFilename, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read);
33 | }
34 |
35 | public FileSetPosition TryReadBookmark()
36 | {
37 | if (_bookmark.Length != 0)
38 | {
39 | _bookmark.Position = 0;
40 |
41 | // Important not to dispose this StreamReader as the stream must remain open.
42 | var reader = new StreamReader(_bookmark, Encoding.UTF8, false, 128);
43 | var current = reader.ReadLine();
44 |
45 | if (current != null)
46 | {
47 | var parts = current.Split(new[] { ":::" }, StringSplitOptions.RemoveEmptyEntries);
48 | if (parts.Length == 2)
49 | {
50 | return new FileSetPosition(long.Parse(parts[0]), parts[1]);
51 | }
52 | }
53 | }
54 |
55 | return FileSetPosition.None;
56 | }
57 |
58 | public void WriteBookmark(FileSetPosition bookmark)
59 | {
60 | if (bookmark.File == null)
61 | return;
62 |
63 | // Don't need to truncate, since we only ever read a single line and
64 | // writes are always newline-terminated
65 | _bookmark.Position = 0;
66 |
67 | // Cannot dispose, as `leaveOpen` is not available on all target platforms
68 | var writer = new StreamWriter(_bookmark);
69 | writer.WriteLine("{0}:::{1}", bookmark.NextLineStart, bookmark.File);
70 | writer.Flush();
71 | }
72 |
73 | public void Dispose()
74 | {
75 | _bookmark.Dispose();
76 | }
77 | }
78 | }
79 |
80 | #endif
81 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Elasticsearch/Sinks/ElasticSearch/Durable/ControlledLevelSwitch.cs:
--------------------------------------------------------------------------------
1 | // Serilog.Sinks.Seq Copyright 2016 Serilog Contributors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using Serilog.Core;
16 | using Serilog.Events;
17 |
18 | namespace Serilog.Sinks.Elasticsearch.Durable
19 | {
20 | ///
21 | /// Instances of this type are single-threaded, generally only updated on a background
22 | /// timer thread. An exception is , which may be called
23 | /// concurrently but performs no synchronization.
24 | /// https://github.com/serilog/serilog-sinks-seq/blob/v4.0.0/src/Serilog.Sinks.Seq/Sinks/Seq/ControlledLevelSwitch.cs
25 | ///
26 | class ControlledLevelSwitch
27 | {
28 | // If non-null, then background level checks will be performed; set either through the constructor
29 | // or in response to a level specification from the server. Never set to null after being made non-null.
30 | LoggingLevelSwitch _controlledSwitch;
31 | LogEventLevel? _originalLevel;
32 |
33 | public ControlledLevelSwitch(LoggingLevelSwitch controlledSwitch = null)
34 | {
35 | _controlledSwitch = controlledSwitch;
36 | }
37 |
38 | public bool IsActive => _controlledSwitch != null;
39 |
40 | public bool IsIncluded(LogEvent evt)
41 | {
42 | // Concurrent, but not synchronized.
43 | var controlledSwitch = _controlledSwitch;
44 | return controlledSwitch == null ||
45 | (int)controlledSwitch.MinimumLevel <= (int)evt.Level;
46 | }
47 |
48 | public void Update(LogEventLevel? minimumAcceptedLevel)
49 | {
50 | if (minimumAcceptedLevel == null)
51 | {
52 | if (_controlledSwitch != null && _originalLevel.HasValue)
53 | _controlledSwitch.MinimumLevel = _originalLevel.Value;
54 |
55 | return;
56 | }
57 |
58 | if (_controlledSwitch == null)
59 | {
60 | // The server is controlling the logging level, but not the overall logger. Hence, if the server
61 | // stops controlling the level, the switch should become transparent.
62 | _originalLevel = LevelAlias.Minimum;
63 | _controlledSwitch = new LoggingLevelSwitch(minimumAcceptedLevel.Value);
64 | return;
65 | }
66 |
67 | if (!_originalLevel.HasValue)
68 | _originalLevel = _controlledSwitch.MinimumLevel;
69 |
70 | _controlledSwitch.MinimumLevel = minimumAcceptedLevel.Value;
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Elasticsearch/Sinks/ElasticSearch/Durable/Elasticsearch/DurableElasticsearchSink.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2014 Serilog Contributors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using System;
16 | using System.Collections.Generic;
17 | using System.Text;
18 | using Serilog.Core;
19 | using Serilog.Events;
20 |
21 |
22 | namespace Serilog.Sinks.Elasticsearch.Durable
23 | {
24 | class DurableElasticsearchSink : ILogEventSink, IDisposable
25 | {
26 | // we rely on the date in the filename later!
27 | const string FileNameSuffix = "-.json";
28 |
29 | readonly Logger _sink;
30 | readonly LogShipper> _shipper;
31 | readonly ElasticsearchSinkState _state;
32 |
33 | public DurableElasticsearchSink(ElasticsearchSinkOptions options)
34 | {
35 | _state = ElasticsearchSinkState.Create(options);
36 |
37 | if (string.IsNullOrWhiteSpace(options.BufferBaseFilename))
38 | {
39 | throw new ArgumentException("Cannot create the durable ElasticSearch sink without a buffer base file name!");
40 | }
41 |
42 | _sink = new LoggerConfiguration()
43 | .MinimumLevel.Verbose()
44 | .WriteTo.File(_state.DurableFormatter,
45 | options.BufferBaseFilename + FileNameSuffix,
46 | rollingInterval: options.BufferFileRollingInterval,
47 | fileSizeLimitBytes: options.BufferFileSizeLimitBytes,
48 | rollOnFileSizeLimit: true,
49 | retainedFileCountLimit: options.BufferFileCountLimit,
50 | levelSwitch: _state.Options.LevelSwitch,
51 | encoding: Encoding.UTF8)
52 | .CreateLogger();
53 |
54 | var elasticSearchLogClient = new ElasticsearchLogClient(
55 | elasticLowLevelClient: _state.Client,
56 | cleanPayload: _state.Options.BufferCleanPayload,
57 | elasticOpType: _state.Options.BatchAction);
58 |
59 | var payloadReader = new ElasticsearchPayloadReader(
60 | pipelineName: _state.Options.PipelineName,
61 | typeName:_state.Options.TypeName,
62 | serialize:_state.Serialize,
63 | getIndexForEvent: _state.GetBufferedIndexForEvent,
64 | elasticOpType: _state.Options.BatchAction,
65 | rollingInterval: options.BufferFileRollingInterval);
66 |
67 | _shipper = new ElasticsearchLogShipper(
68 | bufferBaseFilename: _state.Options.BufferBaseFilename,
69 | batchPostingLimit: _state.Options.BatchPostingLimit,
70 | period: _state.Options.BufferLogShippingInterval ?? TimeSpan.FromSeconds(5),
71 | eventBodyLimitBytes: _state.Options.SingleEventSizePostingLimit,
72 | levelControlSwitch: _state.Options.LevelSwitch,
73 | logClient: elasticSearchLogClient,
74 | payloadReader: payloadReader,
75 | retainedInvalidPayloadsLimitBytes: _state.Options.BufferRetainedInvalidPayloadsLimitBytes,
76 | bufferSizeLimitBytes: _state.Options.BufferFileSizeLimitBytes,
77 | registerTemplateIfNeeded: _state.RegisterTemplateIfNeeded,
78 | rollingInterval: options.BufferFileRollingInterval);
79 |
80 | }
81 |
82 | public void Emit(LogEvent logEvent)
83 | {
84 | // This is a lagging indicator, but the network bandwidth usage benefits
85 | // are worth the ambiguity.
86 | if (_shipper.IsIncluded(logEvent))
87 | {
88 | _sink.Write(logEvent);
89 | }
90 | }
91 |
92 | public void Dispose()
93 | {
94 | _sink.Dispose();
95 | _shipper.Dispose();
96 | }
97 | }
98 | }
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Elasticsearch/Sinks/ElasticSearch/Durable/Elasticsearch/ElasticsearchLogClient.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Elasticsearch.Net;
6 | using Serilog.Debugging;
7 |
8 | namespace Serilog.Sinks.Elasticsearch.Durable
9 | {
10 | ///
11 | ///
12 | ///
13 | public class ElasticsearchLogClient : ILogClient>
14 | {
15 | private readonly IElasticLowLevelClient _elasticLowLevelClient;
16 | private readonly Func _cleanPayload;
17 | private readonly ElasticOpType _elasticOpType;
18 |
19 | ///
20 | ///
21 | ///
22 | ///
23 | ///
24 | ///
25 | public ElasticsearchLogClient(IElasticLowLevelClient elasticLowLevelClient,
26 | Func cleanPayload,
27 | ElasticOpType elasticOpType)
28 | {
29 | _elasticLowLevelClient = elasticLowLevelClient;
30 | _cleanPayload = cleanPayload;
31 | _elasticOpType = elasticOpType;
32 | }
33 |
34 | public async Task SendPayloadAsync(List payload)
35 | {
36 | return await SendPayloadAsync(payload, true);
37 | }
38 |
39 | public async Task SendPayloadAsync(List payload, bool first)
40 | {
41 | try
42 | {
43 | if (payload == null || !payload.Any()) return new SentPayloadResult(null, true);
44 | var response = await _elasticLowLevelClient.BulkAsync(PostData.MultiJson(payload));
45 |
46 | if (response.Success)
47 | {
48 | var cleanPayload = new List();
49 | var invalidPayload = GetInvalidPayloadAsync(response, payload, out cleanPayload);
50 | if ((cleanPayload?.Any() ?? false) && first)
51 | {
52 | await SendPayloadAsync(cleanPayload, false);
53 | }
54 |
55 | return new SentPayloadResult(response, true, invalidPayload);
56 | }
57 | else
58 | {
59 | SelfLog.WriteLine("Received failed ElasticSearch shipping result {0}: {1}", response.HttpStatusCode,
60 | response.OriginalException);
61 | return new SentPayloadResult(response, false,
62 | new InvalidResult()
63 | {
64 | StatusCode = response.HttpStatusCode ?? 500,
65 | Content = response.OriginalException.ToString()
66 | });
67 | }
68 | }
69 | catch (Exception ex)
70 | {
71 | SelfLog.WriteLine("Exception while emitting periodic batch from {0}: {1}", this, ex);
72 | return new SentPayloadResult(null, false, null, ex);
73 | }
74 |
75 |
76 | }
77 |
78 | private InvalidResult GetInvalidPayloadAsync(DynamicResponse baseResult, List payload, out List cleanPayload)
79 | {
80 | int i = 0;
81 | cleanPayload = new List();
82 | var items = baseResult.Body["items"];
83 | if (items == null) return null;
84 | List badPayload = new List();
85 |
86 | bool hasErrors = false;
87 | foreach (dynamic item in items)
88 | {
89 | var itemIndex = item?[BatchedElasticsearchSink.BulkAction(_elasticOpType)];
90 | long? status = itemIndex?["status"];
91 | i++;
92 | if (!status.HasValue || status < 300)
93 | {
94 | continue;
95 | }
96 |
97 | hasErrors = true;
98 | var id = itemIndex?["_id"];
99 | var error = itemIndex?["error"];
100 | var errorString = $"type: {error?["type"] ?? "Unknown"}, reason: {error?["reason"] ?? "Unknown"}";
101 |
102 | if (int.TryParse(id.Split('_')[0], out int index))
103 | {
104 | SelfLog.WriteLine("Received failed ElasticSearch shipping result {0}: {1}. Failed payload : {2}.", status, errorString, payload.ElementAt(index * 2 + 1));
105 | badPayload.Add(payload.ElementAt(index * 2));
106 | badPayload.Add(payload.ElementAt(index * 2 + 1));
107 | if (_cleanPayload != null)
108 | {
109 | cleanPayload.Add(payload.ElementAt(index * 2));
110 | cleanPayload.Add(_cleanPayload(payload.ElementAt(index * 2 + 1), status, errorString));
111 | }
112 | }
113 | else
114 | {
115 | SelfLog.WriteLine($"Received failed ElasticSearch shipping result {status}: {errorString}.");
116 | }
117 | }
118 |
119 | if (!hasErrors)
120 | return null;
121 | return new InvalidResult()
122 | {
123 | StatusCode = baseResult.HttpStatusCode ?? 500,
124 | Content = baseResult.ToString(),
125 | BadPayLoad = String.Join(Environment.NewLine, badPayload)
126 | };
127 | }
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Elasticsearch/Sinks/ElasticSearch/Durable/Elasticsearch/ElasticsearchLogShipper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Serilog.Core;
7 | using Serilog.Debugging;
8 | using Serilog.Sinks.Elasticsearch.Durable;
9 |
10 | namespace Serilog.Sinks.Elasticsearch.Durable
11 | {
12 | ///
13 | ///
14 | ///
15 | public class ElasticsearchLogShipper : LogShipper>
16 | {
17 | private readonly Action _registerTemplateIfNeeded;
18 | bool _didRegisterTemplateIfNeeded = false;
19 |
20 | ///
21 | ///
22 | ///
23 | ///
24 | ///
25 | ///
26 | ///
27 | ///
28 | ///
29 | ///
30 | ///
31 | ///
32 | ///
33 | ///
34 | public ElasticsearchLogShipper(string bufferBaseFilename, int batchPostingLimit, TimeSpan period,
35 | long? eventBodyLimitBytes, LoggingLevelSwitch levelControlSwitch, ILogClient> logClient,
36 | IPayloadReader> payloadReader, long? retainedInvalidPayloadsLimitBytes,
37 | long? bufferSizeLimitBytes, Action registerTemplateIfNeeded, RollingInterval rollingInterval)
38 | : base(bufferBaseFilename, batchPostingLimit, period, eventBodyLimitBytes, levelControlSwitch, logClient,
39 | payloadReader, retainedInvalidPayloadsLimitBytes, bufferSizeLimitBytes, rollingInterval)
40 | {
41 | _registerTemplateIfNeeded = registerTemplateIfNeeded;
42 | }
43 |
44 | ///
45 | ///
46 | ///
47 | ///
48 | protected override async Task OnTick()
49 | {
50 | bool success = true;
51 | try
52 | {
53 | if (!_didRegisterTemplateIfNeeded)
54 | {
55 | if (_registerTemplateIfNeeded != null)
56 | {
57 | _registerTemplateIfNeeded();
58 | _didRegisterTemplateIfNeeded = true;
59 | }
60 | }
61 | }
62 | catch (Exception ex)
63 | {
64 | SelfLog.WriteLine("Exception while emitting periodic batch from {0}: {1}", this, ex);
65 | _connectionSchedule.MarkFailure();
66 | success = false;
67 | }
68 | if (success)
69 | await base.OnTick();
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.Elasticsearch/Sinks/ElasticSearch/Durable/Elasticsearch/ElasticsearchPayloadReader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Globalization;
4 | using System.IO;
5 | using System.Linq;
6 | using Elasticsearch.Net;
7 |
8 | namespace Serilog.Sinks.Elasticsearch.Durable
9 | {
10 | ///
11 | ///
12 | ///
13 | public class ElasticsearchPayloadReader: APayloadReader>
14 | {
15 | private readonly string _pipelineName;
16 | private readonly string _typeName;
17 | private readonly Func