├── .all-contributorsrc
├── .gitattributes
├── .gitignore
├── BuildAndPushPackage.bat
├── Changelog.md
├── Delete-BIN-OBJ-Folders.bat
├── HowToUse.md
├── Icon.png
├── License.txt
├── PolicyExample.json
├── README.md
├── Updating.md
└── src
├── .editorconfig
├── Directory.Build.props
├── Serilog.Sinks.AmazonS3.Tests
├── AmazonS3BasicTests.cs
├── GlobalUsings.cs
└── Serilog.Sinks.AmazonS3.Tests.csproj
├── Serilog.Sinks.AmazonS3.sln
├── Serilog.Sinks.AmazonS3.sln.DotSettings
└── Serilog.Sinks.AmazonS3
├── GlobalUsings.cs
├── LoggerConfigurationAmazonS3Extensions.cs
├── Serilog.Sinks.AmazonS3.csproj
└── Sinks
└── AmazonS3
├── AmazonS3Options.cs
├── AmazonS3Sink.cs
├── ConfigurationValidator.cs
├── ErrorMessageConstants.cs
├── FileInformation.cs
├── PathRoller.cs
├── RollingInterval.cs
├── RollingIntervalExtensions.cs
└── RollingLogFile.cs
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "files": [
3 | "README.md"
4 | ],
5 | "imageSize": 100,
6 | "commit": false,
7 | "contributors": [
8 | {
9 | "login": "Galouw",
10 | "name": "Galouw",
11 | "avatar_url": "https://avatars.githubusercontent.com/u/6368030?v=4",
12 | "profile": "https://github.com/Galouw",
13 | "contributions": [
14 | "code",
15 | "doc"
16 | ]
17 | },
18 | {
19 | "login": "profet23",
20 | "name": "Ben Grabkowitz",
21 | "avatar_url": "https://avatars.githubusercontent.com/u/2411974?v=4",
22 | "profile": "https://github.com/profet23",
23 | "contributions": [
24 | "code",
25 | "doc"
26 | ]
27 | },
28 | {
29 | "login": "aherrmann13",
30 | "name": "Adam Herrmann",
31 | "avatar_url": "https://avatars.githubusercontent.com/u/1924089?v=4",
32 | "profile": "https://github.com/aherrmann13",
33 | "contributions": [
34 | "code",
35 | "doc"
36 | ]
37 | },
38 | {
39 | "login": "qrczak0",
40 | "name": "qrczak0",
41 | "avatar_url": "https://avatars.githubusercontent.com/u/45206900?v=4",
42 | "profile": "https://github.com/qrczak0",
43 | "contributions": [
44 | "code",
45 | "doc"
46 | ]
47 | },
48 | {
49 | "login": "longfin",
50 | "name": "Swen Mun",
51 | "avatar_url": "https://avatars.githubusercontent.com/u/128436?v=4",
52 | "profile": "http://longfin.github.com",
53 | "contributions": [
54 | "code",
55 | "doc"
56 | ]
57 | },
58 | {
59 | "login": "codingbadger",
60 | "name": "Barry Mooring",
61 | "avatar_url": "https://avatars.githubusercontent.com/u/1089628?v=4",
62 | "profile": "https://www.linkedin.com/in/barrymooring/",
63 | "contributions": [
64 | "code",
65 | "doc"
66 | ]
67 | },
68 | {
69 | "login": "samburville",
70 | "name": "Sam Burville",
71 | "avatar_url": "https://avatars.githubusercontent.com/u/7041731?v=4",
72 | "profile": "https://github.com/samburville",
73 | "contributions": [
74 | "code",
75 | "doc"
76 | ]
77 | },
78 | {
79 | "login": "stylesm",
80 | "name": "Matt Styles",
81 | "avatar_url": "https://avatars.githubusercontent.com/u/5602910?v=4",
82 | "profile": "https://github.com/stylesm",
83 | "contributions": [
84 | "code",
85 | "doc"
86 | ]
87 | },
88 | {
89 | "login": "ioxFR",
90 | "name": "Valentin LECERF",
91 | "avatar_url": "https://avatars.githubusercontent.com/u/7376668?v=4",
92 | "profile": "http://vlecerf.com",
93 | "contributions": [
94 | "code",
95 | "doc"
96 | ]
97 | },
98 | {
99 | "login": "SeppPenner",
100 | "name": "HansM",
101 | "avatar_url": "https://avatars.githubusercontent.com/u/9639361?v=4",
102 | "profile": "https://franzhuber23.blogspot.de/",
103 | "contributions": [
104 | "code",
105 | "doc",
106 | "example",
107 | "maintenance",
108 | "projectManagement",
109 | "test"
110 | ]
111 | },
112 | {
113 | "login": "serilog-contrib",
114 | "name": "Serilog Contrib",
115 | "avatar_url": "https://avatars.githubusercontent.com/u/78050538?v=4",
116 | "profile": "https://github.com/serilog-contrib",
117 | "contributions": [
118 | "code",
119 | "doc",
120 | "example",
121 | "maintenance",
122 | "projectManagement",
123 | "test"
124 | ]
125 | },
126 | {
127 | "login": "seruminar",
128 | "name": "Yuriy Sountsov",
129 | "avatar_url": "https://avatars.githubusercontent.com/u/35008875?v=4",
130 | "profile": "https://github.com/seruminar",
131 | "contributions": [
132 | "doc"
133 | ]
134 | },
135 | {
136 | "login": "kosovrzn",
137 | "name": "Sergey Kosov",
138 | "avatar_url": "https://avatars.githubusercontent.com/u/13337834?v=4",
139 | "profile": "https://github.com/kosovrzn",
140 | "contributions": [
141 | "code",
142 | "test"
143 | ]
144 | }
145 | ],
146 | "contributorsPerLine": 7,
147 | "projectName": "Serilog.Sinks.AmazonS3",
148 | "projectOwner": "serilog-contrib",
149 | "repoType": "github",
150 | "repoHost": "https://github.com",
151 | "skipCi": true
152 | }
153 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.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 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | project.fragment.lock.json
46 | artifacts/
47 |
48 | *_i.c
49 | *_p.c
50 | *_i.h
51 | *.ilk
52 | *.meta
53 | *.obj
54 | *.pch
55 | *.pdb
56 | *.pgc
57 | *.pgd
58 | *.rsp
59 | *.sbr
60 | *.tlb
61 | *.tli
62 | *.tlh
63 | *.tmp
64 | *.tmp_proj
65 | *.log
66 | *.vspscc
67 | *.vssscc
68 | .builds
69 | *.pidb
70 | *.svclog
71 | *.scc
72 |
73 | # Chutzpah Test files
74 | _Chutzpah*
75 |
76 | # Visual C++ cache files
77 | ipch/
78 | *.aps
79 | *.ncb
80 | *.opendb
81 | *.opensdf
82 | *.sdf
83 | *.cachefile
84 | *.VC.db
85 | *.VC.VC.opendb
86 |
87 | # Visual Studio profiler
88 | *.psess
89 | *.vsp
90 | *.vspx
91 | *.sap
92 |
93 | # TFS 2012 Local Workspace
94 | $tf/
95 |
96 | # Guidance Automation Toolkit
97 | *.gpState
98 |
99 | # ReSharper is a .NET coding add-in
100 | _ReSharper*/
101 | *.[Rr]e[Ss]harper
102 | *.DotSettings.user
103 |
104 | # JustCode is a .NET coding add-in
105 | .JustCode
106 |
107 | # TeamCity is a build add-in
108 | _TeamCity*
109 |
110 | # DotCover is a Code Coverage Tool
111 | *.dotCover
112 |
113 | # NCrunch
114 | _NCrunch_*
115 | .*crunch*.local.xml
116 | nCrunchTemp_*
117 |
118 | # MightyMoose
119 | *.mm.*
120 | AutoTest.Net/
121 |
122 | # Web workbench (sass)
123 | .sass-cache/
124 |
125 | # Installshield output folder
126 | [Ee]xpress/
127 |
128 | # DocProject is a documentation generator add-in
129 | DocProject/buildhelp/
130 | DocProject/Help/*.HxT
131 | DocProject/Help/*.HxC
132 | DocProject/Help/*.hhc
133 | DocProject/Help/*.hhk
134 | DocProject/Help/*.hhp
135 | DocProject/Help/Html2
136 | DocProject/Help/html
137 |
138 | # Click-Once directory
139 | publish/
140 |
141 | # Publish Web Output
142 | *.[Pp]ublish.xml
143 | *.azurePubxml
144 | # TODO: Comment the next line if you want to checkin your web deploy settings
145 | # but database connection strings (with potential passwords) will be unencrypted
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
150 | # checkin your Azure Web App publish settings, but sensitive information contained
151 | # in these scripts will be unencrypted
152 | PublishScripts/
153 |
154 | # NuGet Packages
155 | *.nupkg
156 | # The packages folder can be ignored because of Package Restore
157 | **/packages/*
158 | # except build/, which is used as an MSBuild target.
159 | !**/packages/build/
160 | # Uncomment if necessary however generally it will be regenerated when needed
161 | #!**/packages/repositories.config
162 | # NuGet v3's project.json files produces more ignoreable files
163 | *.nuget.props
164 | *.nuget.targets
165 |
166 | # Microsoft Azure Build Output
167 | csx/
168 | *.build.csdef
169 |
170 | # Microsoft Azure Emulator
171 | ecf/
172 | rcf/
173 |
174 | # Windows Store app package directories and files
175 | AppPackages/
176 | BundleArtifacts/
177 | Package.StoreAssociation.xml
178 | _pkginfo.txt
179 |
180 | # Visual Studio cache files
181 | # files ending in .cache can be ignored
182 | *.[Cc]ache
183 | # but keep track of directories ending in .cache
184 | !*.[Cc]ache/
185 |
186 | # Others
187 | ClientBin/
188 | ~$*
189 | *~
190 | *.dbmdl
191 | *.dbproj.schemaview
192 | *.jfm
193 | *.pfx
194 | *.publishsettings
195 | node_modules/
196 | orleans.codegen.cs
197 |
198 | # Since there are multiple workflows, uncomment next line to ignore bower_components
199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
200 | #bower_components/
201 |
202 | # RIA/Silverlight projects
203 | Generated_Code/
204 |
205 | # Backup & report files from converting an old project file
206 | # to a newer Visual Studio version. Backup files are not needed,
207 | # because we have git ;-)
208 | _UpgradeReport_Files/
209 | Backup*/
210 | UpgradeLog*.XML
211 | UpgradeLog*.htm
212 |
213 | # SQL Server files
214 | *.mdf
215 | *.ldf
216 |
217 | # Business Intelligence projects
218 | *.rdl.data
219 | *.bim.layout
220 | *.bim_*.settings
221 |
222 | # Microsoft Fakes
223 | FakesAssemblies/
224 |
225 | # GhostDoc plugin setting file
226 | *.GhostDoc.xml
227 |
228 | # Node.js Tools for Visual Studio
229 | .ntvs_analysis.dat
230 |
231 | # Visual Studio 6 build log
232 | *.plg
233 |
234 | # Visual Studio 6 workspace options file
235 | *.opt
236 |
237 | # Visual Studio LightSwitch build output
238 | **/*.HTMLClient/GeneratedArtifacts
239 | **/*.DesktopClient/GeneratedArtifacts
240 | **/*.DesktopClient/ModelManifest.xml
241 | **/*.Server/GeneratedArtifacts
242 | **/*.Server/ModelManifest.xml
243 | _Pvt_Extensions
244 |
245 | # Paket dependency manager
246 | .paket/paket.exe
247 | paket-files/
248 |
249 | # FAKE - F# Make
250 | .fake/
251 |
252 | # JetBrains Rider
253 | .idea/
254 | *.sln.iml
255 |
256 | # CodeRush
257 | .cr/
258 |
259 | # Python Tools for Visual Studio (PTVS)
260 | __pycache__/
261 | *.pyc
--------------------------------------------------------------------------------
/BuildAndPushPackage.bat:
--------------------------------------------------------------------------------
1 | cd .\src
2 |
3 | @ECHO off
4 | cls
5 |
6 | FOR /d /r . %%d in (bin,obj) DO (
7 | IF EXIST "%%d" (
8 | ECHO %%d | FIND /I "\node_modules\" > Nul && (
9 | ECHO.Skipping: %%d
10 | ) || (
11 | ECHO.Deleting: %%d
12 | rd /s/q "%%d"
13 | )
14 | )
15 | )
16 |
17 | @ECHO on
18 | @ECHO.Building solution...
19 | @dotnet restore
20 | @dotnet build -c Release
21 | @cd .\Serilog.Sinks.AmazonS3\bin\Release
22 | @ECHO.Build successful.
23 | dotnet nuget push *.nupkg -s "nuget.org" --skip-duplicate -k "%NUGET_API_KEY%"
24 | dotnet nuget push *.snupkg -s "nuget.org" --skip-duplicate -k "%NUGET_API_KEY%"
25 | @ECHO.Upload success. Press any key to exit.
26 | PAUSE
--------------------------------------------------------------------------------
/Changelog.md:
--------------------------------------------------------------------------------
1 | Change history
2 | --------------
3 |
4 | * **Version 1.6.0.0 (2025-03-24)** : Fixes https://github.com/serilog-contrib/Serilog.Sinks.Postgresql.Alternative/issues/83, updates NuGet packages, deprecates `failureCallback`.
5 | * **Version 1.5.3.0 (2024-12-26)** : Removed support for Net6.0, added support for Net9.0, updated NuGet packages.
6 | * **Version 1.5.2.0 (2024-05-16)** : Removed support for Net7.0.
7 | * **Version 1.5.1.0 (2024-03-03)**: Updated NuGet packages.
8 | * **Version 1.5.0.0 (2023-11-21)** : Updated NuGet packages, removed support for netstandard, added support for Net8.0.
9 | * **Version 1.4.1.0 (2023-06-06)** : Updated NuGet packages, added option to disable Amazon S3 SigV4 payload signing (Thanks to [jawand](https://github.com/jawand)).
10 | * **Version 1.4.0.0 (2023-04-07)** : Updated NuGet packages, removed support for NetCore3.1, hopefully fixes rolling interval bug from https://github.com/serilog-contrib/Serilog.Sinks.AmazonS3/issues/52.
11 | * **Version 1.3.0.0 (2022-11-20)** : Updated NuGet packages, removed support for Net5.0, added support for Net7.0.
12 | * **Version 1.2.9.0 (2022-10-30)** : Updated NuGet packages.
13 | * **Version 1.2.8.0 (2022-08-28)** : Updated NuGet packages.
14 | * **Version 1.2.7.0 (2022-08-03)** : Updated NuGet packages, fixed issue with service url (Thanks to @kosovrzn).
15 | * **Version 1.2.6.0 (2022-06-01)** : Updated NuGet packages.
16 | * **Version 1.2.5.0 (2022-04-04)** : Updated NuGet packages.
17 | * **Version 1.2.4.0 (2022-02-16)** : Updated NuGet packages, added nullable checks, added editorconfig, added file scoped namespaces, added global usings, removed native support for Net Framework (Breaking change).
18 | * **Version 1.2.3.0 (2022-01-12)** : NuGet packages updated.
19 | * **Version 1.2.2.0 (2021-11-09)** : NuGet packages updated, added support for Net6.0.
20 | * **Version 1.2.1.0 (2021-11-04)** : NuGet packages updated.
21 | * **Version 1.2.0.0 (2021-09-12)** : Added example of loading logger config from appsettings (Thanks to @stylesm), added validation for the serviceUrl property (Thanks to @stylesm), updated icon, smaller adjustements due to move to the serilog-contrib organization, updated NuGet packages.
22 | * **Version 1.1.11.0 (2021-09-03)** : Updated license to fit the new owning repository, updated readme and so on to fit new package name.
23 | * **Version 1.1.10.0 (2021-08-29)** : Removed logging of S3 responses (Thanks to @KindOfANiceGuy), updated NuGet packages.
24 | * **Version 1.1.9.0 (2021-08-09)** : Removed support for soon deprecated NetCore 2.1.
25 | * **Version 1.1.8.0 (2021-07-25)** : Updated NuGet packages, enabled source linking for debugging.
26 | * **Version 1.1.7.0 (2021-06-04)** : Updated NuGet packages.
27 | * **Version 1.1.6.0 (2021-05-15)** : Updated NuGet packages.
28 | * **Version 1.1.5.0 (2021-04-29)** : Updated NuGet packages.
29 | * **Version 1.1.4.0 (2021-03-31)** : Updated NuGet packages, fixed a bug where the Service url wasn't injected correctly (Thanks to [aherrmann13](https://github.com/aherrmann13)).
30 | * **Version 1.1.3.0 (2021-03-31)** : Updated NuGet packages, fixed a bug where the Amazon S3 client wasn't injected correctly (Thanks to [longfin](https://github.com/longfin)).
31 | * **Version 1.1.2.0 (2021-02-27)** : Updated NuGet packages, added deletion of local files.
32 | * **Version 1.1.1.0 (2021-02-21)** : Fixed a null reference exception with the formatter property.
33 | * **Version 1.1.0.0 (2021-02-03)** : Big rework of the sink to make it work better. Check the options to configure the new sink.
34 | * **Version 1.0.12.0 (2020-07-16)** : Added default AWS S3 serviceUrl.
35 | * **Version 1.0.11.0 (2020-07-15)** : Updated NuGet packages, added new constructor taking serviceUrl instead of endpoint (Thanks to [Galouw](https://github.com/Galouw)).
36 | * **Version 1.0.10.0 (2020-06-05)** : Updated NuGet packages, adjusted build to Visual Studio, moved changelog to extra file.
37 | * **Version 1.0.9.0 (2020-05-10)** : Updated NuGet packages, added option to add standard and custom formatters.
38 | * **Version 1.0.8.0 (2020-03-26)** : Updated NuGet packages.
39 | * **Version 1.0.7.0 (2020-02-09)** : Updated NuGet packages, updated available versions.
40 | * **Version 1.0.6.0 (2019-12-09)** : Fixed NuGet package dependency bug.
41 | * **Version 1.0.5.0 (2019-12-08)** : Updated NuGet packages, added new option "bucketPath" to the sink.
42 | * **Version 1.0.4.0 (2019-11-12)** : Small updates, added new option "autoUploadEvents" to the sink.
43 | * **Version 1.0.3.0 (2019-11-08)** : Updated NuGet packages, added GitVersionTask.
44 | * **Version 1.0.2.0 (2019-07-19)** : Support of role based authorization to S3 added, failureCallback parameter added.
45 | * **Version 1.0.1.0 (2019-06-23)** : Added icon to the NuGet package.
46 | * **Version 1.0.0.0 (2019-05-31)** : 1.0 release.
--------------------------------------------------------------------------------
/Delete-BIN-OBJ-Folders.bat:
--------------------------------------------------------------------------------
1 | @ECHO off
2 | cls
3 |
4 | ECHO Deleting all BIN and OBJ folders...
5 | ECHO.
6 |
7 | FOR /d /r . %%d in (bin,obj) DO (
8 | IF EXIST "%%d" (
9 | ECHO %%d | FIND /I "\node_modules\" > Nul && (
10 | ECHO.Skipping: %%d
11 | ) || (
12 | ECHO.Deleting: %%d
13 | rd /s/q "%%d"
14 | )
15 | )
16 | )
17 |
18 | ECHO.
19 | ECHO.BIN and OBJ folders have been successfully deleted. Press any key to exit.
20 | pause > nul
--------------------------------------------------------------------------------
/HowToUse.md:
--------------------------------------------------------------------------------
1 | ## Basic usage
2 | ```csharp
3 | var logger = new LoggerConfiguration().WriteTo
4 | .AmazonS3(
5 | "log.txt",
6 | "mytestbucket-aws",
7 | Amazon.RegionEndpoint.EUWest2,
8 | "ABCDEFGHIJKLMNOP",
9 | "c3fghsrgwegfn://asdfsdfsdgfsdg",
10 | rollingInterval: RollingInterval.Minute)
11 | .CreateLogger();
12 |
13 | for (var x = 0; x < 200; x++)
14 | {
15 | var ex = new Exception("Test");
16 | logger.Error(ex.ToString());
17 | }
18 | ```
19 |
20 | ## Usage with role based authentication in AWS
21 | Use this method if you gave access to Amazon S3 from your AWS program execution machine using roles. In this case, authorization is managed by AWS and `awsAccessKeyId` and `awsSecretAccessKey` are not required.
22 |
23 | ```csharp
24 | var logger = new LoggerConfiguration().WriteTo
25 | .AmazonS3(
26 | "log.txt",
27 | "mytestbucket-aws",
28 | Amazon.RegionEndpoint.EUWest2,
29 | rollingInterval: RollingInterval.Minute)
30 | .CreateLogger();
31 |
32 | for (var x = 0; x < 200; x++)
33 | {
34 | var ex = new Exception("Test");
35 | logger.Error(ex.ToString());
36 | }
37 | ```
38 |
39 | ## Using JSON or custom formatters
40 | ```csharp
41 | var levelSwitch = new LoggingLevelSwitch { MinimumLevel = LogEventLevel.Information };
42 |
43 | var logger = new LoggerConfiguration()
44 | .WriteTo.AmazonS3(
45 | new CompactJsonFormatter(),
46 | "log.json",
47 | "mytestbucket-aws",
48 | Amazon.RegionEndpoint.EUWest2,
49 | rollingInterval: RollingInterval.Minute,
50 | )
51 | .CreateLogger();
52 |
53 | for (var x = 0; x < 200; x++)
54 | {
55 | var ex = new Exception("Test");
56 | logger.Error(ex.ToString());
57 | }
58 | ```
59 |
60 | ## Configuring from appsettings.json files
61 | ```json
62 | "Serilog": {
63 | "MinimumLevel": {
64 | "Default": "Information"
65 | },
66 | "WriteTo": [
67 | {
68 | "Name": "AmazonS3",
69 | "Args": {
70 | "path": "log.txt",
71 | "bucketName": "mybucket-aws",
72 | "rollingInterval": "Day",
73 | "serviceUrl": "https://s3.eu-west-2.amazonaws.com",
74 | "disablePayloadSigning": "false"
75 | }
76 | }
77 | ]
78 | }
79 | ```
80 |
81 | For more information regarding this use case, see [Issue number 10](https://github.com/serilog-contrib/Serilog.Sinks.AmazonS3/issues/10) and [Serilog formatting JSON](https://github.com/serilog/serilog/wiki/Formatting-Output#formatting-json).
82 |
83 |
84 | ## Exception handling
85 | You can pass a callback to the sink parameters on failure to define which action needs to be done if an exception occured on the sink side. If something is going wrong in the sink code, `failureCallback` will be executed. This is deprecated with version 1.6.0+. Use fallback logging instead. Check https://nblumhardt.com/2024/10/fallback-logging/.
86 |
87 | ```csharp
88 | var logger = new LoggerConfiguration().WriteTo
89 | .AmazonS3(
90 | "log.txt",
91 | "mytestbucket-aws",
92 | Amazon.RegionEndpoint.EUWest2,
93 | rollingInterval: RollingInterval.Minute,
94 | failureCallback: e => Console.WriteLine($"An error occured in my sink: {e.Message}")
95 | )
96 | .CreateLogger();
97 |
98 | for (var x = 0; x < 200; x++)
99 | {
100 | var ex = new Exception("Test");
101 | logger.Error(ex.ToString());
102 | }
103 | ```
104 |
105 | The project can be found on [nuget](https://www.nuget.org/packages/Serilog.Sinks.AmazonS3/).
106 |
107 | ## Configuration options
108 |
109 | |Parameter|Meaning|Example|Default value|
110 | |-|-|-|-|
111 | |client|The Amazon S3 client. It will be created in the sink from the given options if the specified one is `null`.|`new AmazonS3Client()`|`null`|
112 | |formatter|The formatter that can be implemented as desired. See [Issue number 10](https://github.com/serilog-contrib/Serilog.Sinks.AmazonS3/issues/10) and [Serilog formatting JSON](https://github.com/serilog/serilog/wiki/Formatting-Output#formatting-json) for more information.|`new CompactJsonFormatter()`|None, is optional.|
113 | |path|The main log file name used.|`"log.txt"`|None, is mandatory.|
114 | |bucketName|The name of the Amazon S3 bucket to use.
Check: https://docs.aws.amazon.com/general/latest/gr/rande.html.|`"mytestbucket-aws"`|None, is mandatory.|
115 | |endpoint|The Amazon S3 endpoint location.|`RegionEndpoint.EUWest2`|None, is mandatory. (Either `endpoint` or `serviceUrl` needs to be set.)|
116 | |serviceUrl|The Amazon S3 service URL.
Check: https://docs.aws.amazon.com/general/latest/gr/s3.html.|`https://s3.amazonaws.com`|Default is `https://s3.amazonaws.com`. (Either `endpoint` or `serviceUrl` needs to be set.)|
117 | |awsAccessKeyId|The Amazon S3 access key id.|`ABCDEFGHIJKLMNOP`|None, is mandatory. (Not required if you are using role based authentication).|
118 | |awsSecretAccessKey|The Amazon S3 secret access key.|`c3fghsrgwegfn://asdfsdfsdgfsdg`|None, is mandatory. (Not required if you are using role based authentication).|
119 | |restrictedToMinimumLevel|The minimum level for events passed through the sink. Ignored when `levelSwitch` is specified.
Check: https://github.com/serilog/serilog/blob/dev/src/Serilog/Events/LogEventLevel.cs.|`LogEventLevel.Information`|`LogEventLevel.Verbose`|
120 | |outputTemplate|A message template describing the format used to write to the sink.|`"{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}"`|`"{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}"`. If `formatter` is specified: Not needed.|
121 | |formatProvider|The `IFormatProvider` to use. Supplies culture-specific formatting information.
Check: https://docs.microsoft.com/en-us/dotnet/api/system.iformatprovider?view=netframework-4.8.|`new CultureInfo("de-DE")`|`null`. If `formatter` is specified: Not needed.|
122 | |levelSwitch|A switch allowing the pass-through minimum level to be changed at runtime.
Check: https://nblumhardt.com/2014/10/dynamically-changing-the-serilog-level/.|`var levelSwitch = new LoggingLevelSwitch(); levelSwitch.MinimumLevel = LogEventLevel.Warning;`|`null`|
123 | |rollingInterval|The interval at which logging will roll over to a new file.
Check: https://github.com/serilog/serilog-sinks-file/blob/dev/src/Serilog.Sinks.File/RollingInterval.cs.|`rollingInterval: RollingInterval.Minute`|`RollingInterval.Day`|
124 | |encoding|Character encoding used to write the text file.
Check: https://docs.microsoft.com/de-de/dotnet/api/system.text.encoding?view=netframework-4.8.|`encoding: Encoding.Unicode`|`null` meaning `Encoding.UTF8`|
125 | |~~failureCallback~~|~~Adds an option to add a failure callback action.~~ (Deprecated, use fallback logging instead.Check https://nblumhardt.com/2024/10/fallback-logging/.)|~~`failureCallback: e => Console.WriteLine($"Sink error: {e.Message}")`~~|~~`null`~~|
126 | |bucketPath|Optionally add a sub-path for the bucket. Files are stored on S3 `mytestbucket-aws/awsSubPath/log.txt` in the example below.|`bucketPath = "awsSubPath"`|`null`|
127 | |batchSizeLimit|The maximum number of events to include in a single batch. This means an upload of events as a file to S3 will contain at most this number of events.
Check: https://github.com/serilog/serilog-sinks-periodicbatching|`batchSizeLimit = 20`|`100`|
128 | |batchingPeriod|The time to wait between checking for unemitted events. If there are any unemitted events, they will then be uploaded to S3 in a batch of maximum size `batchSizeLimit`.
Check: https://github.com/serilog/serilog-sinks-periodicbatching|`batchingPeriod = TimeSpan.FromSeconds(5)`|`TimeSpan.FromSeconds(2)`|
129 | |eagerlyEmitFirstEvent|A value indicating whether the first event should be emitted immediately or not.|`eagerlyEmitFirstEvent = false`|`true`|
130 | |queueSizeLimit|The queue size limit meaning the limit until the last not emitted events are discarded (Standard mechanims to stop queue overflows).|`queueSizeLimit = 2000`|`10000`|
131 | |disablePayloadSigning|Setting `disablePayloadSigning` to `true` disables the Amazon S3 SigV4 payload signing data integrity check on each upload request. This option is provided if you are using other cloud storage providers e.g. Cloudflare R2 and they support AWS S3 APIs but currently lack support for the Streaming SigV4 implementation used by AWSSDK.S3.|`disablePayloadSigning = true`| Default is `false`. Even a null value will also result in a `false` setting.|
132 |
133 | Hint: Only `outputTemplate` and `formatProvider` together or the `formatter` can be used.
134 |
135 | ## Bigger example
136 |
137 | ```csharp
138 | var levelSwitch = new LoggingLevelSwitch();
139 | levelSwitch.MinimumLevel = LogEventLevel.Warning;
140 |
141 | var logger = new LoggerConfiguration().WriteTo
142 | .AmazonS3(
143 | "log.txt",
144 | "mytestbucket-aws",
145 | Amazon.RegionEndpoint.EUWest2,
146 | "ABCDEFGHIJKLMNOP",
147 | "c3fghsrgwegfn://asdfsdfsdgfsdg",
148 | restrictedToMinimumLevel:LogEventLevel.Verbose,
149 | outputTemplate:"{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}",
150 | new CultureInfo("de-DE"),
151 | levelSwitch: levelSwitch,
152 | rollingInterval: RollingInterval.Minute,
153 | encoding: Encoding.Unicode,
154 | bucketPath = "awsSubPath"
155 | )
156 | .CreateLogger();
157 |
158 | for (var x = 0; x < 200; x++)
159 | {
160 | var ex = new Exception("Test");
161 | logger.Error(ex.ToString());
162 | }
163 | ```
164 |
165 | ## Further links
166 |
167 | * Overview over the Amazon endpoints and locations: https://docs.aws.amazon.com/general/latest/gr/rande.html
168 | * How to prepare your S3 bucket to access it with a software: https://www.c-sharpcorner.com/article/fileupload-to-aws-s3-using-asp-net/
169 | * Example on how to use the Amazon S3 API for .Net: https://stackoverflow.com/questions/25814972/how-to-upload-a-file-to-amazon-s3-super-easy-using-c-sharp
170 | * AWS authorizations for requests: https://docs.aws.amazon.com/AmazonS3/latest/dev/access-control-auth-workflow-object-operation.html
171 |
--------------------------------------------------------------------------------
/Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serilog-contrib/Serilog.Sinks.AmazonS3/2ee9fb21a8fe4727137ca2e4022b719da8296bd4/Icon.png
--------------------------------------------------------------------------------
/License.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) SeppPenner (https://github.com/SeppPenner) and the Serilog contributors (https://github.com/serilog-contrib).
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.
--------------------------------------------------------------------------------
/PolicyExample.json:
--------------------------------------------------------------------------------
1 | {
2 | "Id": "Policy1575837688950",
3 | "Version": "2012-10-17",
4 | "Statement": [
5 | {
6 | "Sid": "Stmt1575837684072",
7 | "Action": [
8 | "s3:GetObject",
9 | "s3:PutObject"
10 | ],
11 | "Effect": "Allow",
12 | "Resource": "arn:aws:s3:::test-bucket-aws-s3-serilog/*",
13 | "Principal": "*"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Serilog.Sinks.AmazonS3
2 | ====================================
3 |
4 | Serilog.Sinks.AmazonS3 is a library to save logging information from [Serilog](https://github.com/serilog/serilog) to [Amazon S3](https://aws.amazon.com/s3/).
5 | The idea there was to upload log files to [Amazon S3](https://aws.amazon.com/s3/) to later evaluate them with [Amazon EMR](https://aws.amazon.com/emr/) services.
6 | This project makes use of the [Serilog.Sinks.File](https://github.com/serilog/serilog-sinks-file)'s code in a major part,
7 | so thanks to all the [contributors](https://github.com/serilog/serilog-sinks-file/graphs/contributors) of this project :thumbsup:.
8 |
9 | [](https://github.com/serilog-contrib/Serilog.Sinks.AmazonS3/issues)
10 | [](https://github.com/serilog-contrib/Serilog.Sinks.AmazonS3/network)
11 | [](https://github.com/serilog-contrib/Serilog.Sinks.AmazonS3/stargazers)
12 | [](https://raw.githubusercontent.com/serilog-contrib/Serilog.Sinks.AmazonS3/master/License.txt)
13 | [](https://www.nuget.org/packages/Serilog.Sinks.AmazonS3/)
14 | [](https://www.nuget.org/packages/Serilog.Sinks.AmazonS3/)
15 | [](https://snyk.io/test/github/serilog-contrib/Serilog.Sinks.AmazonS3)
16 | [](https://matrix.to/#/#Serilog-Sinks-AmazonS3_community:gitter.im)
17 |
18 | [](#contributors-)
19 |
20 |
21 | ## Available for
22 | * Net 8.0
23 | * Net 9.0
24 |
25 | ## Net Core and Net Framework latest and LTS versions
26 | * https://dotnet.microsoft.com/download/dotnet
27 |
28 | ## Basic usage
29 | Check out the how to use file [here](https://github.com/serilog-contrib/Serilog.Sinks.AmazonS3/blob/master/HowToUse.md).
30 |
31 | ## Install
32 |
33 | ```bash
34 | dotnet add package Serilog.Sinks.AmazonS3
35 | ```
36 |
37 | ## Special notice for read-only file systems (e.g. AWS Lambda)
38 | AWS Lambda exposes the `tmp` folder for writing, so prepending `/tmp/` to the path parameter of the AmazonS3 extension method allowed this sink to work with AWS Lambda.
39 | Otherwise a similar exception is thrown:
40 | ```log
41 | Exception while emitting periodic batch from Serilog.Sinks.PeriodicBatching.PeriodicBatchingSink: System.IO.IOException: Read-only file system
42 | ```
43 |
44 | Change history
45 | --------------
46 |
47 | See the [Changelog](https://github.com/serilog-contrib/Serilog.Sinks.AmazonS3/blob/master/Changelog.md).
48 |
49 | ## Contributors ✨
50 |
51 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
52 |
53 |
54 |
55 |
56 |
75 |
76 |
77 |
78 |
79 |
80 |
81 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
82 |
--------------------------------------------------------------------------------
/Updating.md:
--------------------------------------------------------------------------------
1 | ## How to update the package
2 |
3 | 1. Add your changes.
4 | 2. Update the release notes in the *.csproj file.
5 | 3. Update version using a Git tag.
6 | 4. Build the project with Visual Studio.
7 | 5. Upload nuget package.
--------------------------------------------------------------------------------
/src/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = crlf
5 | indent_style = space
6 | indent_size = 4
7 | charset = utf-8
8 | csharp_using_directive_placement=inside_namespace:warning
9 | csharp_prefer_braces=true:warning
10 | dotnet_style_allow_multiple_blank_lines_experimental=false:warning
11 | dotnet_style_allow_statement_immediately_after_block_experimental=false:warning
12 | dotnet_style_qualification_for_field=true:warning
13 | dotnet_style_qualification_for_property=true:warning
14 | dotnet_style_qualification_for_method=true:warning
15 | dotnet_style_qualification_for_event=true:warning
16 | csharp_style_allow_blank_lines_between_consecutive_braces_experimental=false:warning
17 | dotnet_sort_system_directives_first=true:warning
18 | dotnet_diagnostic.IDE0005.severity=warning
19 |
20 | [*.cs]
21 | csharp_style_namespace_declarations = file_scoped:warning
--------------------------------------------------------------------------------
/src/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.AmazonS3.Tests/AmazonS3BasicTests.cs:
--------------------------------------------------------------------------------
1 | // --------------------------------------------------------------------------------------------------------------------
2 | //
3 | // The project is licensed under the MIT license.
4 | //
5 | //
6 | // This class is used for some basic test regarding the Amazon S3 sink.
7 | //
8 | // --------------------------------------------------------------------------------------------------------------------
9 |
10 | namespace Serilog.Sinks.AmazonS3.Tests;
11 |
12 | ///
13 | /// This class is used for some basic test regarding the Amazon S3 sink.
14 | ///
15 | [TestClass]
16 | public class AmazonS3BasicTests
17 | {
18 | ///
19 | /// The Amazon S3 access key id.
20 | ///
21 | private readonly string awsAccessKeyId = Environment.GetEnvironmentVariable("AwsAccessKeyId") ?? string.Empty;
22 |
23 | ///
24 | /// The Amazon S3 secret access key.
25 | ///
26 | private readonly string awsSecretAccessKey = Environment.GetEnvironmentVariable("AwsSecretAccessKey") ?? string.Empty;
27 |
28 | ///
29 | /// The Amazon S3 bucket name.
30 | ///
31 | private readonly string awsBucketName = Environment.GetEnvironmentVariable("AwsBucketName") ?? string.Empty;
32 |
33 | ///
34 | /// Initializes global settings for the current test class.
35 | ///
36 | [ClassInitialize]
37 | public static void ClassInitialize(TestContext _)
38 | {
39 | SelfLog.Enable(Console.WriteLine);
40 | }
41 |
42 | ///
43 | /// This method is used to test a basic file upload to Amazon S3 with no credentials (IAM).
44 | ///
45 | [TestMethod]
46 | public void BasicFileUploadAuthorizedTest()
47 | {
48 | var logger = new LoggerConfiguration()
49 | .WriteTo.AmazonS3(
50 | "log.txt",
51 | this.awsBucketName,
52 | RegionEndpoint.EUWest2,
53 | LogEventLevel.Verbose,
54 | outputTemplate: null,
55 | formatProvider: null,
56 | levelSwitch: null,
57 | rollingInterval: RollingInterval.Minute,
58 | encoding: null,
59 | bucketPath: null,
60 | batchSizeLimit: null,
61 | batchingPeriod: null,
62 | eagerlyEmitFirstEvent: null,
63 | queueSizeLimit: null,
64 | disablePayloadSigning: null)
65 | .CreateLogger();
66 |
67 | for (var x = 0; x < 200; x++)
68 | {
69 | var ex = new Exception("Test");
70 | #pragma warning disable Serilog004 // Constant MessageTemplate verifier
71 | logger.Error(ex.ToString());
72 | #pragma warning restore Serilog004 // Constant MessageTemplate verifier
73 | }
74 |
75 | Log.CloseAndFlush();
76 | }
77 |
78 | ///
79 | /// This method is used to test a basic file upload to Amazon S3.
80 | ///
81 | [TestMethod]
82 | public void BasicFileUploadTest()
83 | {
84 | var logger = new LoggerConfiguration()
85 | .WriteTo.AmazonS3(
86 | "log.txt",
87 | this.awsBucketName,
88 | RegionEndpoint.EUWest2,
89 | this.awsAccessKeyId,
90 | this.awsSecretAccessKey,
91 | LogEventLevel.Verbose,
92 | outputTemplate: null,
93 | formatProvider: null,
94 | levelSwitch: null,
95 | rollingInterval: RollingInterval.Minute,
96 | encoding: null,
97 | bucketPath: null,
98 | batchSizeLimit: null,
99 | batchingPeriod: null,
100 | eagerlyEmitFirstEvent: null,
101 | queueSizeLimit: null,
102 | disablePayloadSigning: null)
103 | .CreateLogger();
104 |
105 | for (var x = 0; x < 200; x++)
106 | {
107 | var ex = new Exception("Test");
108 | #pragma warning disable Serilog004 // Constant MessageTemplate verifier
109 | logger.Error(ex.ToString());
110 | #pragma warning restore Serilog004 // Constant MessageTemplate verifier
111 | }
112 |
113 | Log.CloseAndFlush();
114 | }
115 |
116 | ///
117 | /// This method is used to test the JSON upload functionality.
118 | ///
119 | [TestMethod]
120 | public void JsonFileUploadTest()
121 | {
122 | var logger = new LoggerConfiguration()
123 | .WriteTo.AmazonS3(
124 | "log.txt",
125 | this.awsBucketName,
126 | RegionEndpoint.EUWest2,
127 | this.awsAccessKeyId,
128 | this.awsSecretAccessKey,
129 | LogEventLevel.Verbose,
130 | formatter: new CompactJsonFormatter(),
131 | levelSwitch: null,
132 | rollingInterval: RollingInterval.Minute,
133 | encoding: null,
134 | bucketPath: null,
135 | batchSizeLimit: null,
136 | batchingPeriod: null,
137 | eagerlyEmitFirstEvent: null,
138 | queueSizeLimit: null,
139 | disablePayloadSigning: null)
140 | .CreateLogger();
141 |
142 | for (var x = 0; x < 200; x++)
143 | {
144 | var ex = new Exception("Test");
145 | #pragma warning disable Serilog004 // Constant MessageTemplate verifier
146 | logger.Error(ex.ToString());
147 | #pragma warning restore Serilog004 // Constant MessageTemplate verifier
148 | }
149 |
150 | Log.CloseAndFlush();
151 | }
152 |
153 | ///
154 | /// This method is used to test the formatting functionality.
155 | ///
156 | [TestMethod]
157 | public void FormattingTest()
158 | {
159 | var logger = new LoggerConfiguration()
160 | .WriteTo.AmazonS3(
161 | "log.txt",
162 | this.awsBucketName,
163 | RegionEndpoint.EUWest2,
164 | this.awsAccessKeyId,
165 | this.awsSecretAccessKey,
166 | LogEventLevel.Verbose,
167 | "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
168 | .CreateLogger();
169 |
170 | for (var x = 0; x < 200; x++)
171 | {
172 | var ex = new Exception("Test");
173 | #pragma warning disable Serilog004 // Constant MessageTemplate verifier
174 | logger.Error(ex.ToString());
175 | #pragma warning restore Serilog004 // Constant MessageTemplate verifier
176 | }
177 |
178 | Log.CloseAndFlush();
179 | }
180 |
181 | ///
182 | /// This method is used to test a basic file upload to Amazon S3 with the settings provided from https://github.com/serilog-contrib/Serilog.Sinks.AmazonS3/issues/52
183 | /// that tests the rolling interval.
184 | ///
185 | [TestMethod]
186 | public void BasicFileUploadRollingIntervalTest()
187 | {
188 | var logger = new LoggerConfiguration()
189 | .WriteTo.AmazonS3(
190 | restrictedToMinimumLevel: LogEventLevel.Debug,
191 | path: "log.txt",
192 | bucketName: this.awsBucketName,
193 | endpoint: RegionEndpoint.EUWest2,
194 | bucketPath: null,
195 | awsAccessKeyId: this.awsAccessKeyId,
196 | awsSecretAccessKey: this.awsSecretAccessKey,
197 | rollingInterval: RollingInterval.Hour,
198 | eagerlyEmitFirstEvent: false,
199 | formatter: new JsonFormatter(),
200 | batchingPeriod: TimeSpan.FromHours(2),
201 | batchSizeLimit: 100)
202 | .CreateLogger();
203 |
204 | for (var x = 0; x < 200; x++)
205 | {
206 | var ex = new Exception("Test");
207 | #pragma warning disable Serilog004 // Constant MessageTemplate verifier
208 | logger.Error(ex.ToString());
209 | #pragma warning restore Serilog004 // Constant MessageTemplate verifier
210 | }
211 |
212 | Log.CloseAndFlush();
213 | }
214 | }
215 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.AmazonS3.Tests/GlobalUsings.cs:
--------------------------------------------------------------------------------
1 | #pragma warning disable IDE0065 // Die using-Anweisung wurde falsch platziert.
2 | global using Amazon;
3 |
4 | global using Microsoft.VisualStudio.TestTools.UnitTesting;
5 |
6 | global using Serilog.Debugging;
7 | global using Serilog.Events;
8 | global using Serilog.Formatting.Compact;
9 | global using Serilog.Formatting.Json;
10 | #pragma warning restore IDE0065 // Die using-Anweisung wurde falsch platziert.
--------------------------------------------------------------------------------
/src/Serilog.Sinks.AmazonS3.Tests/Serilog.Sinks.AmazonS3.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | latest
6 | enable
7 | enable
8 | NU1803
9 | true
10 |
11 |
12 |
13 |
14 | all
15 | runtime; build; native; contentfiles; analyzers; buildtransitive
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.AmazonS3.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29418.71
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Sinks.AmazonS3", "Serilog.Sinks.AmazonS3\Serilog.Sinks.AmazonS3.csproj", "{69627EEF-98B1-4DD5-9B15-269FC99EA24D}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Sinks.AmazonS3.Tests", "Serilog.Sinks.AmazonS3.Tests\Serilog.Sinks.AmazonS3.Tests.csproj", "{54305F2A-6706-442F-AD66-F7AF6961B8D5}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {69627EEF-98B1-4DD5-9B15-269FC99EA24D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {69627EEF-98B1-4DD5-9B15-269FC99EA24D}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {69627EEF-98B1-4DD5-9B15-269FC99EA24D}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {69627EEF-98B1-4DD5-9B15-269FC99EA24D}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {54305F2A-6706-442F-AD66-F7AF6961B8D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {54305F2A-6706-442F-AD66-F7AF6961B8D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {54305F2A-6706-442F-AD66-F7AF6961B8D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {54305F2A-6706-442F-AD66-F7AF6961B8D5}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {6D3B84F0-0AD6-4B43-89F2-40F164C98081}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.AmazonS3.sln.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | True
3 | True
4 | True
5 | True
6 | True
7 | True
--------------------------------------------------------------------------------
/src/Serilog.Sinks.AmazonS3/GlobalUsings.cs:
--------------------------------------------------------------------------------
1 | #pragma warning disable IDE0065 // Die using-Anweisung wurde falsch platziert.
2 | global using System.Globalization;
3 | global using System.Text;
4 | global using System.Text.RegularExpressions;
5 |
6 | global using Amazon;
7 | global using Amazon.S3;
8 | global using Amazon.S3.Model;
9 |
10 | global using Serilog.Configuration;
11 | global using Serilog.Core;
12 | global using Serilog.Debugging;
13 | global using Serilog.Events;
14 | global using Serilog.Formatting;
15 | global using Serilog.Formatting.Display;
16 | global using Serilog.Sinks.AmazonS3;
17 | global using Serilog.Sinks.PeriodicBatching;
18 | #pragma warning restore IDE0065 // Die using-Anweisung wurde falsch platziert.
--------------------------------------------------------------------------------
/src/Serilog.Sinks.AmazonS3/LoggerConfigurationAmazonS3Extensions.cs:
--------------------------------------------------------------------------------
1 | // --------------------------------------------------------------------------------------------------------------------
2 | //
3 | // The project is licensed under the MIT license.
4 | //
5 | //
6 | // This class contains the Amazon S3 logger configuration.
7 | //
8 | // --------------------------------------------------------------------------------------------------------------------
9 |
10 | namespace Serilog;
11 |
12 | ///
13 | /// This class contains the Amazon S3 logger configuration.
14 | ///
15 | public static class LoggerConfigurationAmazonS3Extensions
16 | {
17 | ///
18 | /// The default output template.
19 | ///
20 | private const string DefaultOutputTemplate =
21 | "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}";
22 |
23 | ///
24 | /// The default batch size limit.
25 | ///
26 | private const int DefaultBatchSizeLimit = 100;
27 |
28 | ///
29 | /// The default value to eagerly emit the first event.
30 | ///
31 | private const bool DefaultEagerlyEmitFirstEvent = true;
32 |
33 | ///
34 | /// The default queue size limit.
35 | ///
36 | private const int DefaultQueueSizeLimit = 10000;
37 |
38 | ///
39 | /// The default encoding.
40 | ///
41 | private static readonly Encoding DefaultEncoding = Encoding.UTF8;
42 |
43 | ///
44 | /// The default batching period.
45 | ///
46 | private static readonly TimeSpan DefaultBatchingPeriod = TimeSpan.FromSeconds(2);
47 |
48 | /// Write log events to the specified file.
49 | ///
50 | /// Thrown when one or more required arguments are
51 | /// null.
52 | ///
53 | /// The logger sink configuration.
54 | /// The path to the file
55 | /// The Amazon S3 bucket name.
56 | /// The Amazon S3 endpoint.
57 | /// The Amazon S3 access key id.
58 | /// The Amazon S3 access key.
59 | ///
60 | /// (Optional)
61 | /// The minimum level for
62 | /// events passed through the sink. Ignored when
63 | /// is specified.
64 | ///
65 | /// The output template.
66 | ///
67 | /// (Optional)
68 | /// Supplies culture-specific formatting information, or
69 | /// null.
70 | ///
71 | ///
72 | /// (Optional)
73 | /// A switch allowing the pass-through minimum level
74 | /// to be changed at runtime.
75 | ///
76 | ///
77 | /// (Optional)
78 | /// The interval at which logging will roll over to a new
79 | /// file.
80 | ///
81 | ///
82 | /// (Optional)
83 | /// Character encoding used to write the text file. The
84 | /// default is UTF-8 without BOM.
85 | ///
86 | /// The Amazon S3 bucket path.
87 | /// The batch size limit.
88 | /// The batching period.
89 | /// A value indicating whether the first event should be emitted immediately or not.
90 | /// The queue size limit.
91 | /// A value indicating whether the Amazon S3 SigV4 payload signing should be disabled or not (Needed for e.g. for the Cloudflare R2 API).
92 | /// The configuration object allowing method chaining.
93 | public static LoggerConfiguration AmazonS3(
94 | this LoggerSinkConfiguration sinkConfiguration,
95 | string path,
96 | string bucketName,
97 | RegionEndpoint endpoint,
98 | string awsAccessKeyId,
99 | string awsSecretAccessKey,
100 | LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
101 | string? outputTemplate = DefaultOutputTemplate,
102 | IFormatProvider? formatProvider = null,
103 | LoggingLevelSwitch? levelSwitch = null,
104 | RollingInterval rollingInterval = RollingInterval.Day,
105 | Encoding? encoding = null,
106 | string? bucketPath = null,
107 | int? batchSizeLimit = DefaultBatchSizeLimit,
108 | TimeSpan? batchingPeriod = null,
109 | bool? eagerlyEmitFirstEvent = DefaultEagerlyEmitFirstEvent,
110 | int? queueSizeLimit = DefaultQueueSizeLimit,
111 | bool? disablePayloadSigning = false)
112 | {
113 | if (sinkConfiguration is null)
114 | {
115 | throw new ArgumentNullException(nameof(sinkConfiguration));
116 | }
117 |
118 | if (string.IsNullOrWhiteSpace(path))
119 | {
120 | throw new ArgumentNullException(nameof(path));
121 | }
122 |
123 | if (string.IsNullOrWhiteSpace(bucketName))
124 | {
125 | throw new ArgumentNullException(nameof(bucketName));
126 | }
127 |
128 | if (endpoint is null)
129 | {
130 | throw new ArgumentNullException(nameof(endpoint));
131 | }
132 |
133 | if (string.IsNullOrWhiteSpace(awsAccessKeyId))
134 | {
135 | throw new ArgumentNullException(nameof(awsAccessKeyId));
136 | }
137 |
138 | if (string.IsNullOrWhiteSpace(awsSecretAccessKey))
139 | {
140 | throw new ArgumentNullException(nameof(awsSecretAccessKey));
141 | }
142 |
143 | if (string.IsNullOrWhiteSpace(outputTemplate))
144 | {
145 | outputTemplate = DefaultOutputTemplate;
146 | }
147 |
148 | encoding ??= DefaultEncoding;
149 | batchingPeriod ??= DefaultBatchingPeriod;
150 |
151 | var options = new AmazonS3Options
152 | {
153 | Path = path,
154 | BucketName = bucketName,
155 | Endpoint = endpoint,
156 | AwsAccessKeyId = awsAccessKeyId,
157 | AwsSecretAccessKey = awsSecretAccessKey,
158 | OutputTemplate = outputTemplate,
159 | FormatProvider = formatProvider,
160 | RollingInterval = rollingInterval,
161 | Encoding = encoding,
162 | BucketPath = bucketPath,
163 | DisablePayloadSigning = disablePayloadSigning
164 | };
165 |
166 | var amazonS3Sink = new AmazonS3Sink(options);
167 |
168 | var batchingOptions = new PeriodicBatchingSinkOptions
169 | {
170 | BatchSizeLimit = batchSizeLimit ?? DefaultBatchSizeLimit,
171 | Period = (TimeSpan)batchingPeriod,
172 | EagerlyEmitFirstEvent = eagerlyEmitFirstEvent ?? DefaultEagerlyEmitFirstEvent,
173 | QueueLimit = queueSizeLimit ?? DefaultQueueSizeLimit
174 | };
175 |
176 | var batchingSink = new PeriodicBatchingSink(amazonS3Sink, batchingOptions);
177 | return sinkConfiguration.Sink(batchingSink, restrictedToMinimumLevel, levelSwitch);
178 | }
179 |
180 | /// Write log events to the specified file.
181 | ///
182 | /// Thrown when one or more required arguments are
183 | /// null.
184 | ///
185 | /// The logger sink configuration.
186 | /// The path to the file.
187 | /// The Amazon S3 bucket name.
188 | /// The Amazon S3 endpoint.
189 | /// The Amazon S3 access key id.
190 | /// The Amazon S3 access key.
191 | ///
192 | /// (Optional)
193 | /// The minimum level for
194 | /// events passed through the sink. Ignored when
195 | /// is specified.
196 | ///
197 | ///
198 | /// (Optional)
199 | /// A switch allowing the pass-through minimum level
200 | /// to be changed at runtime.
201 | ///
202 | /// The formatter.
203 | ///
204 | /// (Optional)
205 | /// The interval at which logging will roll over to a new
206 | /// file.
207 | ///
208 | ///
209 | /// (Optional)
210 | /// Character encoding used to write the text file. The
211 | /// default is UTF-8 without BOM.
212 | ///
213 | /// The Amazon S3 bucket path.
214 | /// The batch size limit.
215 | /// The batching period.
216 | /// A value indicating whether the first event should be emitted immediately or not.
217 | /// The queue size limit.
218 | /// A value indicating whether the Amazon S3 SigV4 payload signing should be disabled or not (Needed for e.g. for the Cloudflare R2 API).
219 | /// The configuration object allowing method chaining.
220 | public static LoggerConfiguration AmazonS3(
221 | this LoggerSinkConfiguration sinkConfiguration,
222 | string path,
223 | string bucketName,
224 | RegionEndpoint endpoint,
225 | string awsAccessKeyId,
226 | string awsSecretAccessKey,
227 | LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
228 | LoggingLevelSwitch? levelSwitch = null,
229 | ITextFormatter? formatter = null,
230 | RollingInterval rollingInterval = RollingInterval.Day,
231 | Encoding? encoding = null,
232 | string? bucketPath = null,
233 | int? batchSizeLimit = DefaultBatchSizeLimit,
234 | TimeSpan? batchingPeriod = null,
235 | bool? eagerlyEmitFirstEvent = DefaultEagerlyEmitFirstEvent,
236 | int? queueSizeLimit = DefaultQueueSizeLimit,
237 | bool? disablePayloadSigning = false)
238 | {
239 | if (sinkConfiguration is null)
240 | {
241 | throw new ArgumentNullException(nameof(sinkConfiguration));
242 | }
243 |
244 | if (string.IsNullOrWhiteSpace(path))
245 | {
246 | throw new ArgumentNullException(nameof(path));
247 | }
248 |
249 | if (string.IsNullOrWhiteSpace(bucketName))
250 | {
251 | throw new ArgumentNullException(nameof(bucketName));
252 | }
253 |
254 | if (endpoint is null)
255 | {
256 | throw new ArgumentNullException(nameof(endpoint));
257 | }
258 |
259 | if (string.IsNullOrWhiteSpace(awsAccessKeyId))
260 | {
261 | throw new ArgumentNullException(nameof(awsAccessKeyId));
262 | }
263 |
264 | if (string.IsNullOrWhiteSpace(awsSecretAccessKey))
265 | {
266 | throw new ArgumentNullException(nameof(awsSecretAccessKey));
267 | }
268 |
269 | if (formatter is null)
270 | {
271 | throw new ArgumentNullException(nameof(formatter));
272 | }
273 |
274 | encoding ??= DefaultEncoding;
275 | batchingPeriod ??= DefaultBatchingPeriod;
276 |
277 | var options = new AmazonS3Options
278 | {
279 | Path = path,
280 | BucketName = bucketName,
281 | Endpoint = endpoint,
282 | AwsAccessKeyId = awsAccessKeyId,
283 | AwsSecretAccessKey = awsSecretAccessKey,
284 | Formatter = formatter,
285 | RollingInterval = rollingInterval,
286 | Encoding = encoding,
287 | BucketPath = bucketPath,
288 | DisablePayloadSigning = disablePayloadSigning
289 | };
290 |
291 | var amazonS3Sink = new AmazonS3Sink(options);
292 |
293 | var batchingOptions = new PeriodicBatchingSinkOptions
294 | {
295 | BatchSizeLimit = batchSizeLimit ?? DefaultBatchSizeLimit,
296 | Period = (TimeSpan)batchingPeriod,
297 | EagerlyEmitFirstEvent = eagerlyEmitFirstEvent ?? DefaultEagerlyEmitFirstEvent,
298 | QueueLimit = queueSizeLimit ?? DefaultQueueSizeLimit
299 | };
300 |
301 | var batchingSink = new PeriodicBatchingSink(amazonS3Sink, batchingOptions);
302 | return sinkConfiguration.Sink(batchingSink, restrictedToMinimumLevel, levelSwitch);
303 | }
304 |
305 | /// Write log events to the specified file.
306 | ///
307 | /// Thrown when one or more required arguments are
308 | /// null.
309 | ///
310 | /// The logger sink configuration.
311 | /// The path to the file.
312 | /// The Amazon S3 bucket name.
313 | /// The Amazon S3 endpoint.
314 | ///
315 | /// (Optional)
316 | /// The minimum level for
317 | /// events passed through the sink. Ignored when
318 | /// is specified.
319 | ///
320 | ///
321 | /// (Optional)
322 | /// A switch allowing the pass-through minimum level
323 | /// to be changed at runtime.
324 | ///
325 | ///
326 | /// (Optional)
327 | /// A message template describing the format used to
328 | /// write to the sink.
329 | /// The default is "{Timestamp:yyyy-MM-dd
330 | /// HH:mm:ss.fff zzz} [{Level:u3}]
331 | /// {Message:lj}{NewLine}{Exception}".
332 | ///
333 | ///
334 | /// (Optional)
335 | /// Supplies culture-specific formatting information, or
336 | /// null.
337 | ///
338 | ///
339 | /// (Optional)
340 | /// The interval at which logging will roll over to a new
341 | /// file.
342 | ///
343 | ///
344 | /// (Optional)
345 | /// Character encoding used to write the text file. The
346 | /// default is UTF-8 without BOM.
347 | ///
348 | /// The Amazon S3 bucket path.
349 | /// The batch size limit.
350 | /// The batching period.
351 | /// A value indicating whether the first event should be emitted immediately or not.
352 | /// The queue size limit.
353 | /// A value indicating whether the Amazon S3 SigV4 payload signing should be disabled or not (Needed for e.g. for the Cloudflare R2 API).
354 | /// The configuration object allowing method chaining.
355 | public static LoggerConfiguration AmazonS3(
356 | this LoggerSinkConfiguration sinkConfiguration,
357 | string path,
358 | string bucketName,
359 | RegionEndpoint endpoint,
360 | LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
361 | LoggingLevelSwitch? levelSwitch = null,
362 | string? outputTemplate = DefaultOutputTemplate,
363 | IFormatProvider? formatProvider = null,
364 | RollingInterval rollingInterval = RollingInterval.Day,
365 | Encoding? encoding = null,
366 | string? bucketPath = null,
367 | int? batchSizeLimit = DefaultBatchSizeLimit,
368 | TimeSpan? batchingPeriod = null,
369 | bool? eagerlyEmitFirstEvent = DefaultEagerlyEmitFirstEvent,
370 | int? queueSizeLimit = DefaultQueueSizeLimit,
371 | bool? disablePayloadSigning = false)
372 | {
373 | if (sinkConfiguration is null)
374 | {
375 | throw new ArgumentNullException(nameof(sinkConfiguration));
376 | }
377 |
378 | if (string.IsNullOrWhiteSpace(path))
379 | {
380 | throw new ArgumentNullException(nameof(path));
381 | }
382 |
383 | if (string.IsNullOrWhiteSpace(bucketName))
384 | {
385 | throw new ArgumentNullException(nameof(bucketName));
386 | }
387 |
388 | if (endpoint is null)
389 | {
390 | throw new ArgumentNullException(nameof(endpoint));
391 | }
392 |
393 | if (string.IsNullOrWhiteSpace(outputTemplate))
394 | {
395 | outputTemplate = DefaultOutputTemplate;
396 | }
397 |
398 | encoding ??= DefaultEncoding;
399 | batchingPeriod ??= DefaultBatchingPeriod;
400 |
401 | var options = new AmazonS3Options
402 | {
403 | Path = path,
404 | BucketName = bucketName,
405 | Endpoint = endpoint,
406 | OutputTemplate = outputTemplate,
407 | FormatProvider = formatProvider,
408 | RollingInterval = rollingInterval,
409 | Encoding = encoding,
410 | BucketPath = bucketPath,
411 | DisablePayloadSigning = disablePayloadSigning
412 | };
413 |
414 | var amazonS3Sink = new AmazonS3Sink(options);
415 |
416 | var batchingOptions = new PeriodicBatchingSinkOptions
417 | {
418 | BatchSizeLimit = batchSizeLimit ?? DefaultBatchSizeLimit,
419 | Period = (TimeSpan)batchingPeriod,
420 | EagerlyEmitFirstEvent = eagerlyEmitFirstEvent ?? DefaultEagerlyEmitFirstEvent,
421 | QueueLimit = queueSizeLimit ?? DefaultQueueSizeLimit
422 | };
423 |
424 | var batchingSink = new PeriodicBatchingSink(amazonS3Sink, batchingOptions);
425 | return sinkConfiguration.Sink(batchingSink, restrictedToMinimumLevel, levelSwitch);
426 | }
427 |
428 | /// Write log events to the specified file.
429 | ///
430 | /// Thrown when one or more required arguments are
431 | /// null.
432 | ///
433 | /// The logger sink configuration.
434 | /// The path to the file.
435 | /// The Amazon S3 bucket name.
436 | /// The Amazon S3 endpoint.
437 | ///
438 | /// (Optional)
439 | /// The minimum level for
440 | /// events passed through the sink. Ignored when
441 | /// is specified.
442 | ///
443 | ///
444 | /// (Optional)
445 | /// A switch allowing the pass-through minimum level
446 | /// to be changed at runtime.
447 | ///
448 | /// The formatter.
449 | ///
450 | /// (Optional)
451 | /// The interval at which logging will roll over to a new
452 | /// file.
453 | ///
454 | ///
455 | /// (Optional)
456 | /// Character encoding used to write the text file. The
457 | /// default is UTF-8 without BOM.
458 | ///
459 | /// The Amazon S3 bucket path.
460 | /// The batch size limit.
461 | /// The batching period.
462 | /// A value indicating whether the first event should be emitted immediately or not.
463 | /// The queue size limit.
464 | /// A value indicating whether the Amazon S3 SigV4 payload signing should be disabled or not (Needed for e.g. for the Cloudflare R2 API).
465 | /// The configuration object allowing method chaining.
466 | public static LoggerConfiguration AmazonS3(
467 | this LoggerSinkConfiguration sinkConfiguration,
468 | string path,
469 | string bucketName,
470 | RegionEndpoint endpoint,
471 | LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
472 | LoggingLevelSwitch? levelSwitch = null,
473 | ITextFormatter? formatter = null,
474 | RollingInterval rollingInterval = RollingInterval.Day,
475 | Encoding? encoding = null,
476 | string? bucketPath = null,
477 | int? batchSizeLimit = DefaultBatchSizeLimit,
478 | TimeSpan? batchingPeriod = null,
479 | bool? eagerlyEmitFirstEvent = DefaultEagerlyEmitFirstEvent,
480 | int? queueSizeLimit = DefaultQueueSizeLimit,
481 | bool? disablePayloadSigning = false)
482 | {
483 | if (sinkConfiguration is null)
484 | {
485 | throw new ArgumentNullException(nameof(sinkConfiguration));
486 | }
487 |
488 | if (string.IsNullOrWhiteSpace(path))
489 | {
490 | throw new ArgumentNullException(nameof(path));
491 | }
492 |
493 | if (string.IsNullOrWhiteSpace(bucketName))
494 | {
495 | throw new ArgumentNullException(nameof(bucketName));
496 | }
497 |
498 | if (endpoint is null)
499 | {
500 | throw new ArgumentNullException(nameof(endpoint));
501 | }
502 |
503 | if (formatter is null)
504 | {
505 | throw new ArgumentNullException(nameof(formatter));
506 | }
507 |
508 | encoding ??= DefaultEncoding;
509 | batchingPeriod ??= DefaultBatchingPeriod;
510 |
511 | var options = new AmazonS3Options
512 | {
513 | Path = path,
514 | BucketName = bucketName,
515 | Endpoint = endpoint,
516 | Formatter = formatter,
517 | RollingInterval = rollingInterval,
518 | Encoding = encoding,
519 | BucketPath = bucketPath,
520 | DisablePayloadSigning = disablePayloadSigning
521 | };
522 |
523 | var amazonS3Sink = new AmazonS3Sink(options);
524 |
525 | var batchingOptions = new PeriodicBatchingSinkOptions
526 | {
527 | BatchSizeLimit = batchSizeLimit ?? DefaultBatchSizeLimit,
528 | Period = (TimeSpan)batchingPeriod,
529 | EagerlyEmitFirstEvent = eagerlyEmitFirstEvent ?? DefaultEagerlyEmitFirstEvent,
530 | QueueLimit = queueSizeLimit ?? DefaultQueueSizeLimit
531 | };
532 |
533 | var batchingSink = new PeriodicBatchingSink(amazonS3Sink, batchingOptions);
534 | return sinkConfiguration.Sink(batchingSink, restrictedToMinimumLevel, levelSwitch);
535 | }
536 |
537 | /// Write log events to the specified file.
538 | ///
539 | /// Thrown when one or more required arguments are
540 | /// null.
541 | ///
542 | /// The logger sink configuration.
543 | /// The path to the file
544 | /// The Amazon S3 bucket name.
545 | /// The Amazon S3 service url.
546 | /// The Amazon S3 access key id.
547 | /// The Amazon S3 access key.
548 | ///
549 | /// (Optional)
550 | /// The minimum level for
551 | /// events passed through the sink. Ignored when
552 | /// is specified.
553 | ///
554 | /// The output template.
555 | ///
556 | /// (Optional)
557 | /// Supplies culture-specific formatting information, or
558 | /// null.
559 | ///
560 | ///
561 | /// (Optional)
562 | /// A switch allowing the pass-through minimum level
563 | /// to be changed at runtime.
564 | ///
565 | ///
566 | /// (Optional)
567 | /// The interval at which logging will roll over to a new
568 | /// file.
569 | ///
570 | ///
571 | /// (Optional)
572 | /// Character encoding used to write the text file. The
573 | /// default is UTF-8 without BOM.
574 | ///
575 | /// The Amazon S3 bucket path.
576 | /// The batch size limit.
577 | /// The batching period.
578 | /// A value indicating whether the first event should be emitted immediately or not.
579 | /// The queue size limit.
580 | /// A value indicating whether the Amazon S3 SigV4 payload signing should be disabled or not (Needed for e.g. for the Cloudflare R2 API).
581 | /// The configuration object allowing method chaining.
582 | public static LoggerConfiguration AmazonS3(
583 | this LoggerSinkConfiguration sinkConfiguration,
584 | string path,
585 | string bucketName,
586 | string serviceUrl,
587 | string awsAccessKeyId,
588 | string awsSecretAccessKey,
589 | LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
590 | string? outputTemplate = DefaultOutputTemplate,
591 | IFormatProvider? formatProvider = null,
592 | LoggingLevelSwitch? levelSwitch = null,
593 | RollingInterval rollingInterval = RollingInterval.Day,
594 | Encoding? encoding = null,
595 | string? bucketPath = null,
596 | int? batchSizeLimit = DefaultBatchSizeLimit,
597 | TimeSpan? batchingPeriod = null,
598 | bool? eagerlyEmitFirstEvent = DefaultEagerlyEmitFirstEvent,
599 | int? queueSizeLimit = DefaultQueueSizeLimit,
600 | bool? disablePayloadSigning = false)
601 | {
602 | if (sinkConfiguration is null)
603 | {
604 | throw new ArgumentNullException(nameof(sinkConfiguration));
605 | }
606 |
607 | if (string.IsNullOrWhiteSpace(path))
608 | {
609 | throw new ArgumentNullException(nameof(path));
610 | }
611 |
612 | if (string.IsNullOrWhiteSpace(bucketName))
613 | {
614 | throw new ArgumentNullException(nameof(bucketName));
615 | }
616 |
617 | if (string.IsNullOrWhiteSpace(serviceUrl))
618 | {
619 | throw new ArgumentNullException(nameof(serviceUrl));
620 | }
621 |
622 | if (!ConfigurationValidator.ValidateServiceUrl(serviceUrl))
623 | {
624 | throw new ArgumentException(ErrorMessageConstants.ServiceUrlInvalidFormat, nameof(serviceUrl));
625 | }
626 |
627 | if (string.IsNullOrWhiteSpace(awsAccessKeyId))
628 | {
629 | throw new ArgumentNullException(nameof(awsAccessKeyId));
630 | }
631 |
632 | if (string.IsNullOrWhiteSpace(awsSecretAccessKey))
633 | {
634 | throw new ArgumentNullException(nameof(awsSecretAccessKey));
635 | }
636 |
637 | if (string.IsNullOrWhiteSpace(outputTemplate))
638 | {
639 | outputTemplate = DefaultOutputTemplate;
640 | }
641 |
642 | encoding ??= DefaultEncoding;
643 | batchingPeriod ??= DefaultBatchingPeriod;
644 |
645 | var options = new AmazonS3Options
646 | {
647 | Path = path,
648 | BucketName = bucketName,
649 | ServiceUrl = serviceUrl,
650 | AwsAccessKeyId = awsAccessKeyId,
651 | AwsSecretAccessKey = awsSecretAccessKey,
652 | OutputTemplate = outputTemplate,
653 | FormatProvider = formatProvider,
654 | RollingInterval = rollingInterval,
655 | Encoding = encoding,
656 | BucketPath = bucketPath,
657 | Endpoint = null,
658 | DisablePayloadSigning = disablePayloadSigning
659 | };
660 |
661 | var amazonS3Sink = new AmazonS3Sink(options);
662 |
663 | var batchingOptions = new PeriodicBatchingSinkOptions
664 | {
665 | BatchSizeLimit = batchSizeLimit ?? DefaultBatchSizeLimit,
666 | Period = (TimeSpan)batchingPeriod,
667 | EagerlyEmitFirstEvent = eagerlyEmitFirstEvent ?? DefaultEagerlyEmitFirstEvent,
668 | QueueLimit = queueSizeLimit ?? DefaultQueueSizeLimit
669 | };
670 |
671 | var batchingSink = new PeriodicBatchingSink(amazonS3Sink, batchingOptions);
672 | return sinkConfiguration.Sink(batchingSink, restrictedToMinimumLevel, levelSwitch);
673 | }
674 |
675 | /// Write log events to the specified file.
676 | ///
677 | /// Thrown when one or more required arguments are
678 | /// null.
679 | ///
680 | /// The logger sink configuration.
681 | /// The path to the file.
682 | /// The Amazon S3 bucket name.
683 | /// The Amazon S3 service url.
684 | /// The Amazon S3 access key id.
685 | /// The Amazon S3 access key.
686 | ///
687 | /// (Optional)
688 | /// The minimum level for
689 | /// events passed through the sink. Ignored when
690 | /// is specified.
691 | ///
692 | ///
693 | /// (Optional)
694 | /// A switch allowing the pass-through minimum level
695 | /// to be changed at runtime.
696 | ///
697 | /// The formatter.
698 | ///
699 | /// (Optional)
700 | /// The interval at which logging will roll over to a new
701 | /// file.
702 | ///
703 | ///
704 | /// (Optional)
705 | /// Character encoding used to write the text file. The
706 | /// default is UTF-8 without BOM.
707 | ///
708 | /// The Amazon S3 bucket path.
709 | /// The batch size limit.
710 | /// The batching period.
711 | /// A value indicating whether the first event should be emitted immediately or not.
712 | /// The queue size limit.
713 | /// A value indicating whether the Amazon S3 SigV4 payload signing should be disabled or not (Needed for e.g. for the Cloudflare R2 API).
714 | /// The configuration object allowing method chaining.
715 | public static LoggerConfiguration AmazonS3(
716 | this LoggerSinkConfiguration sinkConfiguration,
717 | string path,
718 | string bucketName,
719 | string serviceUrl,
720 | string awsAccessKeyId,
721 | string awsSecretAccessKey,
722 | LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
723 | LoggingLevelSwitch? levelSwitch = null,
724 | ITextFormatter? formatter = null,
725 | RollingInterval rollingInterval = RollingInterval.Day,
726 | Encoding? encoding = null,
727 | string? bucketPath = null,
728 | int? batchSizeLimit = DefaultBatchSizeLimit,
729 | TimeSpan? batchingPeriod = null,
730 | bool? eagerlyEmitFirstEvent = DefaultEagerlyEmitFirstEvent,
731 | int? queueSizeLimit = DefaultQueueSizeLimit,
732 | bool? disablePayloadSigning = false)
733 | {
734 | if (sinkConfiguration is null)
735 | {
736 | throw new ArgumentNullException(nameof(sinkConfiguration));
737 | }
738 |
739 | if (string.IsNullOrWhiteSpace(path))
740 | {
741 | throw new ArgumentNullException(nameof(path));
742 | }
743 |
744 | if (string.IsNullOrWhiteSpace(bucketName))
745 | {
746 | throw new ArgumentNullException(nameof(bucketName));
747 | }
748 |
749 | if (string.IsNullOrWhiteSpace(serviceUrl))
750 | {
751 | throw new ArgumentNullException(nameof(serviceUrl));
752 | }
753 |
754 | if (!ConfigurationValidator.ValidateServiceUrl(serviceUrl))
755 | {
756 | throw new ArgumentException(ErrorMessageConstants.ServiceUrlInvalidFormat, nameof(serviceUrl));
757 | }
758 |
759 | if (string.IsNullOrWhiteSpace(awsAccessKeyId))
760 | {
761 | throw new ArgumentNullException(nameof(awsAccessKeyId));
762 | }
763 |
764 | if (string.IsNullOrWhiteSpace(awsSecretAccessKey))
765 | {
766 | throw new ArgumentNullException(nameof(awsSecretAccessKey));
767 | }
768 |
769 | if (formatter is null)
770 | {
771 | throw new ArgumentNullException(nameof(formatter));
772 | }
773 |
774 | encoding ??= DefaultEncoding;
775 | batchingPeriod ??= DefaultBatchingPeriod;
776 |
777 | var options = new AmazonS3Options
778 | {
779 | Path = path,
780 | BucketName = bucketName,
781 | ServiceUrl = serviceUrl,
782 | AwsAccessKeyId = awsAccessKeyId,
783 | AwsSecretAccessKey = awsSecretAccessKey,
784 | Formatter = formatter,
785 | RollingInterval = rollingInterval,
786 | Encoding = encoding,
787 | BucketPath = bucketPath,
788 | Endpoint = null,
789 | DisablePayloadSigning = disablePayloadSigning
790 | };
791 |
792 | var amazonS3Sink = new AmazonS3Sink(options);
793 |
794 | var batchingOptions = new PeriodicBatchingSinkOptions
795 | {
796 | BatchSizeLimit = batchSizeLimit ?? DefaultBatchSizeLimit,
797 | Period = (TimeSpan)batchingPeriod,
798 | EagerlyEmitFirstEvent = eagerlyEmitFirstEvent ?? DefaultEagerlyEmitFirstEvent,
799 | QueueLimit = queueSizeLimit ?? DefaultQueueSizeLimit
800 | };
801 |
802 | var batchingSink = new PeriodicBatchingSink(amazonS3Sink, batchingOptions);
803 | return sinkConfiguration.Sink(batchingSink, restrictedToMinimumLevel, levelSwitch);
804 | }
805 |
806 | /// Write log events to the specified file.
807 | ///
808 | /// Thrown when one or more required arguments are
809 | /// null.
810 | ///
811 | /// The logger sink configuration.
812 | /// The path to the file.
813 | /// The Amazon S3 bucket name.
814 | /// The Amazon S3 service url.
815 | ///
816 | /// (Optional)
817 | /// The minimum level for
818 | /// events passed through the sink. Ignored when
819 | /// is specified.
820 | ///
821 | ///
822 | /// (Optional)
823 | /// A switch allowing the pass-through minimum level
824 | /// to be changed at runtime.
825 | ///
826 | ///
827 | /// (Optional)
828 | /// A message template describing the format used to
829 | /// write to the sink.
830 | /// The default is "{Timestamp:yyyy-MM-dd
831 | /// HH:mm:ss.fff zzz} [{Level:u3}]
832 | /// {Message:lj}{NewLine}{Exception}".
833 | ///
834 | ///
835 | /// (Optional)
836 | /// Supplies culture-specific formatting information, or
837 | /// null.
838 | ///
839 | ///
840 | /// (Optional)
841 | /// The interval at which logging will roll over to a new
842 | /// file.
843 | ///
844 | ///
845 | /// (Optional)
846 | /// Character encoding used to write the text file. The
847 | /// default is UTF-8 without BOM.
848 | ///
849 | /// The Amazon S3 bucket path.
850 | /// The batch size limit.
851 | /// The batching period.
852 | /// A value indicating whether the first event should be emitted immediately or not.
853 | /// The queue size limit.
854 | /// A value indicating whether the Amazon S3 SigV4 payload signing should be disabled or not (Needed for e.g. for the Cloudflare R2 API).
855 | /// The configuration object allowing method chaining.
856 | public static LoggerConfiguration AmazonS3(
857 | this LoggerSinkConfiguration sinkConfiguration,
858 | string path,
859 | string bucketName,
860 | string serviceUrl,
861 | LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
862 | LoggingLevelSwitch? levelSwitch = null,
863 | string? outputTemplate = DefaultOutputTemplate,
864 | IFormatProvider? formatProvider = null,
865 | RollingInterval rollingInterval = RollingInterval.Day,
866 | Encoding? encoding = null,
867 | string? bucketPath = null,
868 | int? batchSizeLimit = DefaultBatchSizeLimit,
869 | TimeSpan? batchingPeriod = null,
870 | bool? eagerlyEmitFirstEvent = DefaultEagerlyEmitFirstEvent,
871 | int? queueSizeLimit = DefaultQueueSizeLimit,
872 | bool? disablePayloadSigning = false)
873 | {
874 | if (sinkConfiguration is null)
875 | {
876 | throw new ArgumentNullException(nameof(sinkConfiguration));
877 | }
878 |
879 | if (string.IsNullOrWhiteSpace(path))
880 | {
881 | throw new ArgumentNullException(nameof(path));
882 | }
883 |
884 | if (string.IsNullOrWhiteSpace(bucketName))
885 | {
886 | throw new ArgumentNullException(nameof(bucketName));
887 | }
888 |
889 | if (string.IsNullOrWhiteSpace(serviceUrl))
890 | {
891 | throw new ArgumentNullException(nameof(serviceUrl));
892 | }
893 |
894 | if (!ConfigurationValidator.ValidateServiceUrl(serviceUrl))
895 | {
896 | throw new ArgumentException(ErrorMessageConstants.ServiceUrlInvalidFormat, nameof(serviceUrl));
897 | }
898 |
899 | if (string.IsNullOrWhiteSpace(outputTemplate))
900 | {
901 | outputTemplate = DefaultOutputTemplate;
902 | }
903 |
904 | encoding ??= DefaultEncoding;
905 | batchingPeriod ??= DefaultBatchingPeriod;
906 |
907 | var options = new AmazonS3Options
908 | {
909 | Path = path,
910 | BucketName = bucketName,
911 | ServiceUrl = serviceUrl,
912 | OutputTemplate = outputTemplate,
913 | FormatProvider = formatProvider,
914 | RollingInterval = rollingInterval,
915 | Encoding = encoding,
916 | BucketPath = bucketPath,
917 | Endpoint = null,
918 | DisablePayloadSigning = disablePayloadSigning
919 | };
920 |
921 | var amazonS3Sink = new AmazonS3Sink(options);
922 |
923 | var batchingOptions = new PeriodicBatchingSinkOptions
924 | {
925 | BatchSizeLimit = batchSizeLimit ?? DefaultBatchSizeLimit,
926 | Period = (TimeSpan)batchingPeriod,
927 | EagerlyEmitFirstEvent = eagerlyEmitFirstEvent ?? DefaultEagerlyEmitFirstEvent,
928 | QueueLimit = queueSizeLimit ?? DefaultQueueSizeLimit
929 | };
930 |
931 | var batchingSink = new PeriodicBatchingSink(amazonS3Sink, batchingOptions);
932 | return sinkConfiguration.Sink(batchingSink, restrictedToMinimumLevel, levelSwitch);
933 | }
934 |
935 | /// Write log events to the specified file.
936 | ///
937 | /// Thrown when one or more required arguments are
938 | /// null.
939 | ///
940 | /// The logger sink configuration.
941 | /// The path to the file.
942 | /// The Amazon S3 bucket name.
943 | /// The Amazon S3 service url.
944 | ///
945 | /// (Optional)
946 | /// The minimum level for
947 | /// events passed through the sink. Ignored when
948 | /// is specified.
949 | ///
950 | ///
951 | /// (Optional)
952 | /// A switch allowing the pass-through minimum level
953 | /// to be changed at runtime.
954 | ///
955 | /// The formatter.
956 | ///
957 | /// (Optional)
958 | /// The interval at which logging will roll over to a new
959 | /// file.
960 | ///
961 | ///
962 | /// (Optional)
963 | /// Character encoding used to write the text file. The
964 | /// default is UTF-8 without BOM.
965 | ///
966 | /// The Amazon S3 bucket path.
967 | /// The batch size limit.
968 | /// The batching period.
969 | /// A value indicating whether the first event should be emitted immediately or not.
970 | /// The queue size limit.
971 | /// A value indicating whether the Amazon S3 SigV4 payload signing should be disabled or not (Needed for e.g. for the Cloudflare R2 API).
972 | /// The configuration object allowing method chaining.
973 | public static LoggerConfiguration AmazonS3(
974 | this LoggerSinkConfiguration sinkConfiguration,
975 | string path,
976 | string bucketName,
977 | string serviceUrl,
978 | LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
979 | LoggingLevelSwitch? levelSwitch = null,
980 | ITextFormatter? formatter = null,
981 | RollingInterval rollingInterval = RollingInterval.Day,
982 | Encoding? encoding = null,
983 | string? bucketPath = null,
984 | int? batchSizeLimit = DefaultBatchSizeLimit,
985 | TimeSpan? batchingPeriod = null,
986 | bool? eagerlyEmitFirstEvent = DefaultEagerlyEmitFirstEvent,
987 | int? queueSizeLimit = DefaultQueueSizeLimit,
988 | bool? disablePayloadSigning = false)
989 | {
990 | if (sinkConfiguration is null)
991 | {
992 | throw new ArgumentNullException(nameof(sinkConfiguration));
993 | }
994 |
995 | if (string.IsNullOrWhiteSpace(path))
996 | {
997 | throw new ArgumentNullException(nameof(path));
998 | }
999 |
1000 | if (string.IsNullOrWhiteSpace(bucketName))
1001 | {
1002 | throw new ArgumentNullException(nameof(bucketName));
1003 | }
1004 |
1005 | if (string.IsNullOrWhiteSpace(serviceUrl))
1006 | {
1007 | throw new ArgumentNullException(nameof(serviceUrl));
1008 | }
1009 |
1010 | if (!ConfigurationValidator.ValidateServiceUrl(serviceUrl))
1011 | {
1012 | throw new ArgumentException(ErrorMessageConstants.ServiceUrlInvalidFormat, nameof(serviceUrl));
1013 | }
1014 |
1015 | if (formatter is null)
1016 | {
1017 | throw new ArgumentNullException(nameof(formatter));
1018 | }
1019 |
1020 | encoding ??= DefaultEncoding;
1021 | batchingPeriod ??= DefaultBatchingPeriod;
1022 |
1023 | var options = new AmazonS3Options
1024 | {
1025 | Path = path,
1026 | BucketName = bucketName,
1027 | ServiceUrl = serviceUrl,
1028 | Formatter = formatter,
1029 | RollingInterval = rollingInterval,
1030 | Encoding = encoding,
1031 | BucketPath = bucketPath,
1032 | Endpoint = null,
1033 | DisablePayloadSigning = disablePayloadSigning
1034 | };
1035 |
1036 | var amazonS3Sink = new AmazonS3Sink(options);
1037 |
1038 | var batchingOptions = new PeriodicBatchingSinkOptions
1039 | {
1040 | BatchSizeLimit = batchSizeLimit ?? DefaultBatchSizeLimit,
1041 | Period = (TimeSpan)batchingPeriod,
1042 | EagerlyEmitFirstEvent = eagerlyEmitFirstEvent ?? DefaultEagerlyEmitFirstEvent,
1043 | QueueLimit = queueSizeLimit ?? DefaultQueueSizeLimit
1044 | };
1045 |
1046 | var batchingSink = new PeriodicBatchingSink(amazonS3Sink, batchingOptions);
1047 | return sinkConfiguration.Sink(batchingSink, restrictedToMinimumLevel, levelSwitch);
1048 | }
1049 |
1050 | /// Write log events to the specified file.
1051 | ///
1052 | /// Thrown when one or more required arguments are
1053 | /// null.
1054 | ///
1055 | /// The logger sink configuration.
1056 | /// The Amazon S3 client.
1057 | /// The path to the file.
1058 | /// The Amazon S3 bucket name.
1059 | ///
1060 | /// (Optional)
1061 | /// The minimum level for
1062 | /// events passed through the sink. Ignored when
1063 | /// is specified.
1064 | ///
1065 | ///
1066 | /// (Optional)
1067 | /// A message template describing the format used to
1068 | /// write to the sink.
1069 | /// The default is "{Timestamp:yyyy-MM-dd
1070 | /// HH:mm:ss.fff zzz} [{Level:u3}]
1071 | /// {Message:lj}{NewLine}{Exception}".
1072 | ///
1073 | ///
1074 | /// (Optional)
1075 | /// Supplies culture-specific formatting information, or
1076 | /// null.
1077 | ///
1078 | ///
1079 | /// (Optional)
1080 | /// A switch allowing the pass-through minimum level
1081 | /// to be changed at runtime.
1082 | ///
1083 | ///
1084 | /// (Optional)
1085 | /// The interval at which logging will roll over to a new
1086 | /// file.
1087 | ///
1088 | ///
1089 | /// (Optional)
1090 | /// Character encoding used to write the text file. The
1091 | /// default is UTF-8 without BOM.
1092 | ///
1093 | /// The Amazon S3 bucket path.
1094 | /// The batch size limit.
1095 | /// The batching period.
1096 | /// A value indicating whether the first event should be emitted immediately or not.
1097 | /// The queue size limit.
1098 | /// A value indicating whether the Amazon S3 SigV4 payload signing should be disabled or not (Needed for e.g. for the Cloudflare R2 API).
1099 | /// The configuration object allowing method chaining.
1100 | public static LoggerConfiguration AmazonS3(
1101 | this LoggerSinkConfiguration sinkConfiguration,
1102 | AmazonS3Client client,
1103 | string path,
1104 | string bucketName,
1105 | LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
1106 | string? outputTemplate = DefaultOutputTemplate,
1107 | IFormatProvider? formatProvider = null,
1108 | LoggingLevelSwitch? levelSwitch = null,
1109 | RollingInterval rollingInterval = RollingInterval.Day,
1110 | Encoding? encoding = null,
1111 | string? bucketPath = null,
1112 | int? batchSizeLimit = DefaultBatchSizeLimit,
1113 | TimeSpan? batchingPeriod = null,
1114 | bool? eagerlyEmitFirstEvent = DefaultEagerlyEmitFirstEvent,
1115 | int? queueSizeLimit = DefaultQueueSizeLimit,
1116 | bool? disablePayloadSigning = false)
1117 | {
1118 | if (sinkConfiguration is null)
1119 | {
1120 | throw new ArgumentNullException(nameof(sinkConfiguration));
1121 | }
1122 |
1123 | if (client is null)
1124 | {
1125 | throw new ArgumentNullException(nameof(client));
1126 | }
1127 |
1128 | if (string.IsNullOrWhiteSpace(path))
1129 | {
1130 | throw new ArgumentNullException(nameof(path));
1131 | }
1132 |
1133 | if (string.IsNullOrWhiteSpace(bucketName))
1134 | {
1135 | throw new ArgumentNullException(nameof(bucketName));
1136 | }
1137 |
1138 | if (string.IsNullOrWhiteSpace(outputTemplate))
1139 | {
1140 | outputTemplate = DefaultOutputTemplate;
1141 | }
1142 |
1143 | encoding ??= DefaultEncoding;
1144 | batchingPeriod ??= DefaultBatchingPeriod;
1145 |
1146 | var options = new AmazonS3Options
1147 | {
1148 | AmazonS3Client = client,
1149 | Path = path,
1150 | BucketName = bucketName,
1151 | OutputTemplate = outputTemplate,
1152 | FormatProvider = formatProvider,
1153 | RollingInterval = rollingInterval,
1154 | Encoding = encoding,
1155 | BucketPath = bucketPath,
1156 | DisablePayloadSigning = disablePayloadSigning
1157 | };
1158 |
1159 | var amazonS3Sink = new AmazonS3Sink(options);
1160 |
1161 | var batchingOptions = new PeriodicBatchingSinkOptions
1162 | {
1163 | BatchSizeLimit = batchSizeLimit ?? DefaultBatchSizeLimit,
1164 | Period = (TimeSpan)batchingPeriod,
1165 | EagerlyEmitFirstEvent = eagerlyEmitFirstEvent ?? DefaultEagerlyEmitFirstEvent,
1166 | QueueLimit = queueSizeLimit ?? DefaultQueueSizeLimit
1167 | };
1168 |
1169 | var batchingSink = new PeriodicBatchingSink(amazonS3Sink, batchingOptions);
1170 | return sinkConfiguration.Sink(batchingSink, restrictedToMinimumLevel, levelSwitch);
1171 | }
1172 |
1173 | /// Write log events to the specified file.
1174 | ///
1175 | /// Thrown when one or more required arguments are
1176 | /// null.
1177 | ///
1178 | /// The logger sink configuration.
1179 | /// The Amazon S3 client.
1180 | /// The path to the file.
1181 | /// The Amazon S3 bucket name.
1182 | ///
1183 | /// (Optional)
1184 | /// The minimum level for
1185 | /// events passed through the sink. Ignored when
1186 | /// is specified.
1187 | ///
1188 | ///
1189 | /// (Optional)
1190 | /// A switch allowing the pass-through minimum level
1191 | /// to be changed at runtime.
1192 | ///
1193 | /// The formatter.
1194 | ///
1195 | /// (Optional)
1196 | /// The interval at which logging will roll over to a new
1197 | /// file.
1198 | ///
1199 | ///
1200 | /// (Optional)
1201 | /// Character encoding used to write the text file. The
1202 | /// default is UTF-8 without BOM.
1203 | ///
1204 | /// The Amazon S3 bucket path.
1205 | /// The batch size limit.
1206 | /// The batching period.
1207 | /// A value indicating whether the first event should be emitted immediately or not.
1208 | /// The queue size limit.
1209 | /// A value indicating whether the Amazon S3 SigV4 payload signing should be disabled or not (Needed for e.g. for the Cloudflare R2 API).
1210 | /// The configuration object allowing method chaining.
1211 | public static LoggerConfiguration AmazonS3(
1212 | this LoggerSinkConfiguration sinkConfiguration,
1213 | AmazonS3Client client,
1214 | string path,
1215 | string bucketName,
1216 | LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
1217 | LoggingLevelSwitch? levelSwitch = null,
1218 | ITextFormatter? formatter = null,
1219 | RollingInterval rollingInterval = RollingInterval.Day,
1220 | Encoding? encoding = null,
1221 | string? bucketPath = null,
1222 | int? batchSizeLimit = DefaultBatchSizeLimit,
1223 | TimeSpan? batchingPeriod = null,
1224 | bool? eagerlyEmitFirstEvent = DefaultEagerlyEmitFirstEvent,
1225 | int? queueSizeLimit = DefaultQueueSizeLimit,
1226 | bool? disablePayloadSigning = false)
1227 | {
1228 | if (sinkConfiguration is null)
1229 | {
1230 | throw new ArgumentNullException(nameof(sinkConfiguration));
1231 | }
1232 |
1233 | if (client is null)
1234 | {
1235 | throw new ArgumentNullException(nameof(client));
1236 | }
1237 |
1238 | if (string.IsNullOrWhiteSpace(path))
1239 | {
1240 | throw new ArgumentNullException(nameof(path));
1241 | }
1242 |
1243 | if (string.IsNullOrWhiteSpace(bucketName))
1244 | {
1245 | throw new ArgumentNullException(nameof(bucketName));
1246 | }
1247 |
1248 | if (formatter is null)
1249 | {
1250 | throw new ArgumentNullException(nameof(formatter));
1251 | }
1252 |
1253 | encoding ??= DefaultEncoding;
1254 | batchingPeriod ??= DefaultBatchingPeriod;
1255 |
1256 | var options = new AmazonS3Options
1257 | {
1258 | AmazonS3Client = client,
1259 | Path = path,
1260 | BucketName = bucketName,
1261 | Formatter = formatter,
1262 | RollingInterval = rollingInterval,
1263 | Encoding = encoding,
1264 | BucketPath = bucketPath,
1265 | DisablePayloadSigning = disablePayloadSigning
1266 | };
1267 |
1268 | var amazonS3Sink = new AmazonS3Sink(options);
1269 |
1270 | var batchingOptions = new PeriodicBatchingSinkOptions
1271 | {
1272 | BatchSizeLimit = batchSizeLimit ?? DefaultBatchSizeLimit,
1273 | Period = (TimeSpan)batchingPeriod,
1274 | EagerlyEmitFirstEvent = eagerlyEmitFirstEvent ?? DefaultEagerlyEmitFirstEvent,
1275 | QueueLimit = queueSizeLimit ?? DefaultQueueSizeLimit
1276 | };
1277 |
1278 | var batchingSink = new PeriodicBatchingSink(amazonS3Sink, batchingOptions);
1279 | return sinkConfiguration.Sink(batchingSink, restrictedToMinimumLevel, levelSwitch);
1280 | }
1281 | }
1282 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.AmazonS3/Serilog.Sinks.AmazonS3.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0;net9.0
5 | Serilog.Sinks.AmazonS3
6 | Serilog
7 | true
8 | true
9 | Serilog.Sinks.AmazonS3
10 | SeppPenner and the Serilog contributors
11 | SeppPenner and the Serilog contributors
12 | Copyright © SeppPenner and the Serilog contributors
13 | Serilog.Sinks.AmazonS3 is a library to save logging information from https://github.com/serilog/serilog to https://aws.amazon.com/s3/. The idea there was to upload log files to https://aws.amazon.com/s3/ to later evaluate them with https://aws.amazon.com/emr/ services. This project makes use of the https://github.com/serilog/serilog-sinks-file's code in a major part, so thanks to all the https://github.com/serilog/serilog-sinks-file/graphs/contributors of this project :thumbsup:.
14 | c# csharp serilog amazon s3 amazons3 sink logging log
15 | https://www.nuget.org/packages/Serilog.Sinks.AmazonS3/
16 | https://github.com/SeppPenner/Serilog.Sinks.AmazonS3
17 | Icon.png
18 | Github
19 | Version 1.6.0.0 (2025-03-24): Fixes https://github.com/serilog-contrib/Serilog.Sinks.Postgresql.Alternative/issues/83, updates NuGet packages, deprecates `failureCallback`.
20 | MIT
21 | latest
22 | enable
23 | true
24 | true
25 | true
26 | snupkg
27 | enable
28 | NU1803
29 | true
30 | README.md
31 |
32 |
33 |
34 |
35 |
36 | all
37 | runtime; build; native; contentfiles; analyzers; buildtransitive
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | True
48 |
49 |
50 |
51 | True
52 |
53 |
54 |
55 | True
56 |
57 |
58 |
59 | True
60 |
61 |
62 |
63 | True
64 |
65 |
66 |
67 | True
68 |
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.AmazonS3/Sinks/AmazonS3/AmazonS3Options.cs:
--------------------------------------------------------------------------------
1 | // --------------------------------------------------------------------------------------------------------------------
2 | //
3 | // The project is licensed under the MIT license.
4 | //
5 | //
6 | // This class is the main options class.
7 | //
8 | // --------------------------------------------------------------------------------------------------------------------
9 |
10 | namespace Serilog.Sinks.AmazonS3;
11 |
12 | ///
13 | /// This class is the main options class.
14 | ///
15 | public class AmazonS3Options
16 | {
17 | ///
18 | /// Gets or sets the Amazon S3 client.
19 | ///
20 | public AmazonS3Client? AmazonS3Client { get; set; }
21 |
22 | ///
23 | /// Gets or sets the AWS access key identifier.
24 | ///
25 | public string AwsAccessKeyId { get; set; } = string.Empty;
26 |
27 | ///
28 | /// Gets or sets the AWS secret access key.
29 | ///
30 | public string AwsSecretAccessKey { get; set; } = string.Empty;
31 |
32 | ///
33 | /// Gets or sets the Amazon S3 bucket name.
34 | ///
35 | public string BucketName { get; set; } = string.Empty;
36 |
37 | ///
38 | /// Gets or sets the path where local files are stored.
39 | ///
40 | public string? BucketPath { get; set; }
41 |
42 | ///
43 | /// Gets or sets the encoding.
44 | ///
45 | public Encoding Encoding { get; set; } = Encoding.UTF8;
46 |
47 | ///
48 | /// Gets or sets the Amazon S3 key endpoint.
49 | ///
50 | public RegionEndpoint? Endpoint { get; set; }
51 |
52 | ///
53 | /// Gets or sets the local path where the files are stored.
54 | ///
55 | public string Path { get; set; } = string.Empty;
56 |
57 | ///
58 | /// Gets or sets the text formatter.
59 | ///
60 | public ITextFormatter? Formatter { get; set; }
61 |
62 | ///
63 | /// Gets or sets the Amazon S3 service url.
64 | ///
65 | public string ServiceUrl { get; set; } = "https://s3.amazonaws.com";
66 |
67 | ///
68 | /// Gets or sets the rolling interval.
69 | ///
70 | public RollingInterval RollingInterval { get; set; }
71 |
72 | ///
73 | /// Gets or sets the output template.
74 | ///
75 | public string? OutputTemplate { get; set; }
76 |
77 | ///
78 | /// Gets or sets the format provider.
79 | ///
80 | public IFormatProvider? FormatProvider { get; set; }
81 |
82 | ///
83 | /// Gets or sets the path roller. Internally used only, not to be set by the options.
84 | ///
85 | public PathRoller? PathRoller { get; set; }
86 |
87 | ///
88 | /// Gets or sets the next checkpoint. Internally used only, not to be set by the options.
89 | ///
90 | public DateTime? NextCheckpoint { get; set; }
91 |
92 | ///
93 | /// Gets or sets the current file sequence number. Internally used only, not to be set by the options.
94 | ///
95 | public int? CurrentFileSequence { get; set; }
96 |
97 | ///
98 | /// Gets or sets a value indicating whether the Amazon S3 SigV4 payload signing should be disabled or not (Needed for e.g. for the Cloudflare R2 API).
99 | ///
100 | public bool? DisablePayloadSigning { get; set; } = false;
101 | }
102 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.AmazonS3/Sinks/AmazonS3/AmazonS3Sink.cs:
--------------------------------------------------------------------------------
1 | // --------------------------------------------------------------------------------------------------------------------
2 | //
3 | // The project is licensed under the MIT license.
4 | //
5 | //
6 | // This class is the main class and contains all logic for the AmazonS3 sink.
7 | //
8 | // --------------------------------------------------------------------------------------------------------------------
9 |
10 | namespace Serilog.Sinks.AmazonS3;
11 |
12 | using IBatchedLogEventSink = PeriodicBatching.IBatchedLogEventSink;
13 |
14 | ///
15 | /// This class is the main class and contains all logic for the AmazonS3 sink.
16 | ///
17 | public class AmazonS3Sink : IBatchedLogEventSink
18 | {
19 | ///
20 | /// The Amazon S3 options.
21 | ///
22 | private readonly AmazonS3Options amazonS3Options = new();
23 |
24 | /// Initializes a new instance of the class.
25 | /// A given value is null.
26 | /// The Amazon S3 options.
27 | public AmazonS3Sink(AmazonS3Options amazonS3Options)
28 | {
29 | this.amazonS3Options.AmazonS3Client = amazonS3Options.AmazonS3Client;
30 | this.amazonS3Options.AwsAccessKeyId = amazonS3Options.AwsAccessKeyId;
31 | this.amazonS3Options.AwsSecretAccessKey = amazonS3Options.AwsSecretAccessKey;
32 | this.amazonS3Options.BucketName = amazonS3Options.BucketName;
33 | this.amazonS3Options.BucketPath = amazonS3Options.BucketPath;
34 | this.amazonS3Options.Encoding = amazonS3Options.Encoding;
35 | this.amazonS3Options.Endpoint = amazonS3Options.Endpoint;
36 | this.amazonS3Options.Path = amazonS3Options.Path;
37 |
38 | if (amazonS3Options.Formatter is null)
39 | {
40 | var textFormatter = new MessageTemplateTextFormatter(amazonS3Options.OutputTemplate!, amazonS3Options.FormatProvider);
41 | this.amazonS3Options.Formatter = textFormatter;
42 | }
43 | else
44 | {
45 | this.amazonS3Options.Formatter = amazonS3Options.Formatter;
46 | }
47 |
48 | this.amazonS3Options.ServiceUrl = amazonS3Options.ServiceUrl;
49 | this.amazonS3Options.PathRoller = new PathRoller(amazonS3Options.Path, amazonS3Options.RollingInterval);
50 | this.amazonS3Options.RollingInterval = amazonS3Options.RollingInterval;
51 | this.amazonS3Options.OutputTemplate = amazonS3Options.OutputTemplate;
52 | this.amazonS3Options.FormatProvider = amazonS3Options.FormatProvider;
53 | this.amazonS3Options.DisablePayloadSigning = amazonS3Options.DisablePayloadSigning;
54 | }
55 |
56 | /// Emit a batch of log events, running asynchronously.
57 | /// The batch of events to emit.
58 | /// A returning any asynchronous operation.
59 | public async Task EmitBatchAsync(IEnumerable batch)
60 | {
61 | var fileInformation = this.OpenFile();
62 |
63 | if (fileInformation.OutputWriter is null)
64 | {
65 | throw new InvalidOperationException($"The output writer was not properly set for {fileInformation.FileName}");
66 | }
67 |
68 | foreach (var logEvent in batch)
69 | {
70 | this.amazonS3Options.Formatter?.Format(logEvent, fileInformation.OutputWriter);
71 | }
72 |
73 | await fileInformation.OutputWriter.FlushAsync();
74 | fileInformation.OutputWriter.Close();
75 |
76 | _ = await this.UploadFileToS3(fileInformation.FileName);
77 | File.Delete(fileInformation.FileName);
78 | }
79 |
80 | ///
81 | /// Allows sinks to perform periodic work without requiring additional threads
82 | /// or timers (thus avoiding additional flush/shut-down complexity).
83 | ///
84 | /// A returning any asynchronous operation.
85 | public async Task OnEmptyBatchAsync()
86 | {
87 | await Task.Delay(0);
88 | }
89 |
90 | ///
91 | /// Open a file and returns the file name and file stream.
92 | ///
93 | /// The .
94 | private FileInformation OpenFile()
95 | {
96 | var fileName = this.AlignCurrentFileTo(DateTime.Now, true);
97 |
98 | var directory = Path.GetDirectoryName(this.amazonS3Options.Path);
99 |
100 | if (!string.IsNullOrWhiteSpace(directory) && !Directory.Exists(directory))
101 | {
102 | Directory.CreateDirectory(directory);
103 | }
104 |
105 | var outputStream = File.Open(fileName, FileMode.Append, FileAccess.Write, FileShare.Read);
106 |
107 | return new FileInformation
108 | {
109 | FileName = fileName,
110 | OutputWriter = new StreamWriter(outputStream, this.amazonS3Options.Encoding)
111 | };
112 | }
113 |
114 | ///
115 | /// Aligns the current file name to the sequence and returns the file name.
116 | ///
117 | /// The current date.
118 | /// The next sequence number.
119 | /// The file name as .
120 | private string AlignCurrentFileTo(DateTime now, bool nextSequence = false)
121 | {
122 | if (!this.amazonS3Options.NextCheckpoint.HasValue)
123 | {
124 | return this.GetFileName(now);
125 | }
126 |
127 | if (nextSequence || now >= this.amazonS3Options.NextCheckpoint.Value)
128 | {
129 | int? minSequence = null;
130 |
131 | if (nextSequence)
132 | {
133 | if (this.amazonS3Options.CurrentFileSequence is null)
134 | {
135 | minSequence = 1;
136 | }
137 | else
138 | {
139 | minSequence = this.amazonS3Options.CurrentFileSequence.Value + 1;
140 | }
141 | }
142 |
143 | return this.GetFileName(now, minSequence);
144 | }
145 |
146 | return string.Empty;
147 | }
148 |
149 | ///
150 | /// Gets the file name according to the rolling file sequence.
151 | ///
152 | /// The current date.
153 | /// The minimum sequence number.
154 | /// The file name as .
155 | private string GetFileName(DateTime now, int? minSequence = null)
156 | {
157 | if (this.amazonS3Options.PathRoller is null)
158 | {
159 | throw new InvalidOperationException("The path roller wasn't properly set.");
160 | }
161 |
162 | var currentCheckpoint = this.amazonS3Options.PathRoller.GetCurrentCheckpoint(now);
163 | this.amazonS3Options.NextCheckpoint = this.amazonS3Options.PathRoller.GetNextCheckpoint(now);
164 |
165 | var existingFiles = new List();
166 |
167 | try
168 | {
169 | if (Directory.Exists(this.amazonS3Options.PathRoller.LogFileDirectory))
170 | {
171 | var directoryFiles = Directory.GetFiles(this.amazonS3Options.PathRoller.LogFileDirectory, this.amazonS3Options.PathRoller.DirectorySearchPattern);
172 | var files = directoryFiles.Select(f => Path.GetFileName(f) ?? string.Empty)?.ToList() ?? new List();
173 | existingFiles = files ?? new List();
174 | }
175 | }
176 | catch (DirectoryNotFoundException)
177 | {
178 | // Ignored
179 | }
180 |
181 | var latestForThisCheckpoint = this.amazonS3Options.PathRoller
182 | .SelectMatches(existingFiles)
183 | .Where(m => m.DateTime == currentCheckpoint)
184 | .OrderByDescending(m => m.SequenceNumber)
185 | .FirstOrDefault();
186 |
187 | var sequence = latestForThisCheckpoint?.SequenceNumber;
188 |
189 | if (minSequence is not null)
190 | {
191 | if (sequence is null || sequence.Value < minSequence.Value)
192 | {
193 | sequence = minSequence;
194 | }
195 | }
196 |
197 | this.amazonS3Options.PathRoller.GetLogFilePath(now, sequence, out var localPath);
198 | this.amazonS3Options.CurrentFileSequence = sequence;
199 | return localPath;
200 | }
201 |
202 | ///
203 | /// Uploads the file to a specified Amazon S3 bucket.
204 | ///
205 | /// he file name.
206 | ///
207 | /// Thrown when an Unauthorized Access error
208 | /// condition occurs.
209 | ///
210 | ///
211 | /// Thrown when an Amazon S3 error condition
212 | /// occurs.
213 | ///
214 | ///
215 | /// Check the provided AWS credentials.
216 | ///
217 | ///
218 | /// An asynchronous result that yields a PutObjectResponse.
219 | ///
220 | private async Task UploadFileToS3(string fileName)
221 | {
222 | var client = this.amazonS3Options.AmazonS3Client;
223 |
224 | if (client is null)
225 | {
226 | if (this.amazonS3Options.Endpoint != null)
227 | {
228 | client = new AmazonS3Client(this.amazonS3Options.Endpoint);
229 | }
230 | else
231 | {
232 | client = new AmazonS3Client(
233 | new AmazonS3Config
234 | {
235 | ServiceURL = this.amazonS3Options.ServiceUrl
236 | });
237 | }
238 |
239 | // In the case that awsAccessKeyId and awsSecretAccessKey is passed, we use it. Otherwise authorization is given by roles in AWS directly.
240 | if (!string.IsNullOrEmpty(this.amazonS3Options.AwsAccessKeyId) && !string.IsNullOrEmpty(this.amazonS3Options.AwsSecretAccessKey))
241 | {
242 | if (this.amazonS3Options.Endpoint != null)
243 | {
244 | client = new AmazonS3Client(this.amazonS3Options.AwsAccessKeyId, this.amazonS3Options.AwsSecretAccessKey, this.amazonS3Options.Endpoint);
245 | }
246 | else
247 | {
248 | client = new AmazonS3Client(
249 | this.amazonS3Options.AwsAccessKeyId,
250 | this.amazonS3Options.AwsSecretAccessKey,
251 | new AmazonS3Config
252 | {
253 | ServiceURL = this.amazonS3Options.ServiceUrl
254 | });
255 | }
256 | }
257 | }
258 |
259 | try
260 | {
261 | // S3 does not support updates, files are automatically rewritten. So we will have to upload the entire file.
262 | // Open the file for shared reading and writing.
263 | using var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
264 |
265 | var key = string.IsNullOrWhiteSpace(this.amazonS3Options.BucketPath)
266 | ? Path.GetFileName(fileName).Replace("\\", "/")
267 | : Path.Combine(this.amazonS3Options.BucketPath, Path.GetFileName(fileName))
268 | .Replace("\\", "/");
269 |
270 | if (fs.Length is 0)
271 | {
272 | throw new InvalidOperationException("The file size is 0.");
273 | }
274 |
275 | var putRequest = new PutObjectRequest
276 | {
277 | BucketName = this.amazonS3Options.BucketName,
278 | Key = key,
279 | InputStream = fs,
280 | DisablePayloadSigning = this.amazonS3Options.DisablePayloadSigning
281 | };
282 |
283 | return await client.PutObjectAsync(putRequest);
284 | }
285 | catch (AmazonS3Exception amazonS3Exception)
286 | {
287 | if (amazonS3Exception.ErrorCode != null && (amazonS3Exception.ErrorCode.Equals("InvalidAccessKeyId") || amazonS3Exception.ErrorCode.Equals("InvalidSecurity")))
288 | {
289 | throw new UnauthorizedAccessException("Check the provided AWS credentials.");
290 | }
291 |
292 | throw;
293 | }
294 | }
295 | }
296 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.AmazonS3/Sinks/AmazonS3/ConfigurationValidator.cs:
--------------------------------------------------------------------------------
1 | // --------------------------------------------------------------------------------------------------------------------
2 | //
3 | // The project is licensed under the MIT license.
4 | //
5 | //
6 | // Validators for configuration parameters.
7 | //
8 | // --------------------------------------------------------------------------------------------------------------------
9 |
10 | namespace Serilog.Sinks.AmazonS3;
11 |
12 | ///
13 | /// Validators for configuration parameters.
14 | ///
15 | internal static class ConfigurationValidator
16 | {
17 | ///
18 | /// Validates the value provided for property.
19 | ///
20 | /// The provided service url.
21 | /// A value indicating whether the service url is valid or not.
22 | internal static bool ValidateServiceUrl(string serviceUrl) => Uri.TryCreate(serviceUrl, UriKind.Absolute, out _);
23 | }
24 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.AmazonS3/Sinks/AmazonS3/ErrorMessageConstants.cs:
--------------------------------------------------------------------------------
1 | // --------------------------------------------------------------------------------------------------------------------
2 | //
3 | // The project is licensed under the MIT license.
4 | //
5 | //
6 | // Constants to use in user-facing error messages.
7 | //
8 | // --------------------------------------------------------------------------------------------------------------------
9 | namespace Serilog.Sinks.AmazonS3;
10 |
11 | ///
12 | /// Constants to use in user-facing error messages.
13 | ///
14 | internal static class ErrorMessageConstants
15 | {
16 | ///
17 | /// The provided is in invalid format.
18 | ///
19 | internal const string ServiceUrlInvalidFormat = "URL must be a valid absolute URL including the protocol.";
20 | }
21 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.AmazonS3/Sinks/AmazonS3/FileInformation.cs:
--------------------------------------------------------------------------------
1 | // --------------------------------------------------------------------------------------------------------------------
2 | //
3 | // The project is licensed under the MIT license.
4 | //
5 | //
6 | // The file information.
7 | //
8 | // --------------------------------------------------------------------------------------------------------------------
9 |
10 | namespace Serilog.Sinks.AmazonS3;
11 |
12 | ///
13 | /// The file information.
14 | ///
15 | public class FileInformation
16 | {
17 | ///
18 | /// Gets or sets the output stream writer.
19 | ///
20 | public StreamWriter? OutputWriter { get; set; }
21 |
22 | ///
23 | /// Gets or sets the file name.
24 | ///
25 | public string FileName { get; set; } = string.Empty;
26 | }
27 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.AmazonS3/Sinks/AmazonS3/PathRoller.cs:
--------------------------------------------------------------------------------
1 | // --------------------------------------------------------------------------------------------------------------------
2 | //
3 | // The project is double licensed under the MIT license and the Apache License, Version 2.0.
4 | // This code is a partly modified source code of the original Serilog code.
5 | // The original license is:
6 | //
7 | // --------------------------------------------------------------------------------------------------------------------
8 | // Copyright 2013-2016 Serilog Contributors
9 | //
10 | // Licensed under the Apache License, Version 2.0 (the "License");
11 | // you may not use this file except in compliance with the License.
12 | // You may obtain a copy of the License at
13 | //
14 | // http://www.apache.org/licenses/LICENSE-2.0
15 | //
16 | // Unless required by applicable law or agreed to in writing, software
17 | // distributed under the License is distributed on an "AS IS" BASIS,
18 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 | // See the License for the specific language governing permissions and
20 | // limitations under the License.
21 | // --------------------------------------------------------------------------------------------------------------------
22 | //
23 | //
24 | //
25 | // The path roller to get the correct file sequences.
26 | //
27 | // --------------------------------------------------------------------------------------------------------------------
28 |
29 | namespace Serilog.Sinks.AmazonS3;
30 |
31 | ///
32 | /// The path roller to get the correct file sequences.
33 | ///
34 | public class PathRoller
35 | {
36 | ///
37 | /// The period match group.
38 | ///
39 | private const string PeriodMatchGroup = "period";
40 |
41 | ///
42 | /// The sequence number match group.
43 | ///
44 | private const string SequenceNumberMatchGroup = "sequence";
45 |
46 | ///
47 | /// The file name prefix.
48 | ///
49 | private readonly string fileNamePrefix;
50 |
51 | ///
52 | /// The file name suffix.
53 | ///
54 | private readonly string fileNameSuffix;
55 |
56 | ///
57 | /// The file name matcher.
58 | ///
59 | private readonly Regex fileNameMatcher;
60 |
61 | ///
62 | /// The interval.
63 | ///
64 | private readonly RollingInterval interval;
65 |
66 | ///
67 | /// The period format.
68 | ///
69 | private readonly string periodFormat;
70 |
71 | ///
72 | /// Initializes a new instance of the class.
73 | ///
74 | /// The path.
75 | /// The interval.
76 | public PathRoller(string path, RollingInterval interval)
77 | {
78 | if (path is null)
79 | {
80 | throw new ArgumentNullException(nameof(path));
81 | }
82 |
83 | this.interval = interval;
84 | this.periodFormat = interval.GetFormat();
85 |
86 | var pathDirectory = Path.GetDirectoryName(path);
87 |
88 | if (string.IsNullOrEmpty(pathDirectory))
89 | {
90 | pathDirectory = Directory.GetCurrentDirectory();
91 | }
92 |
93 | this.LogFileDirectory = Path.GetFullPath(pathDirectory);
94 | this.fileNamePrefix = Path.GetFileNameWithoutExtension(path);
95 | this.fileNameSuffix = Path.GetExtension(path);
96 | this.fileNameMatcher = new Regex(
97 | "^" +
98 | Regex.Escape(this.fileNamePrefix) +
99 | "(?<" + PeriodMatchGroup + ">\\d{" + this.periodFormat.Length + "})" +
100 | "(?<" + SequenceNumberMatchGroup + ">_[0-9]{3,}){0,1}" +
101 | Regex.Escape(this.fileNameSuffix) +
102 | "$",
103 | RegexOptions.Compiled);
104 |
105 | this.DirectorySearchPattern = $"{this.fileNamePrefix}*{this.fileNameSuffix}";
106 | }
107 |
108 | ///
109 | /// Gets the log file directory.
110 | ///
111 | public string LogFileDirectory { get; }
112 |
113 | ///
114 | /// Gets the directory search pattern.
115 | ///
116 | public string DirectorySearchPattern { get; }
117 |
118 | ///
119 | /// Gets the log file path.
120 | ///
121 | /// The date.
122 | /// The sequence number.
123 | /// The path.
124 | public void GetLogFilePath(DateTime date, int? sequenceNumber, out string path)
125 | {
126 | var currentCheckpoint = this.GetCurrentCheckpoint(date);
127 |
128 | var tok = currentCheckpoint?.ToString(this.periodFormat, CultureInfo.InvariantCulture) ?? string.Empty;
129 |
130 | if (sequenceNumber != null)
131 | {
132 | tok += "_" + sequenceNumber.Value.ToString("000", CultureInfo.InvariantCulture);
133 | }
134 |
135 | path = Path.Combine(this.LogFileDirectory, this.fileNamePrefix + tok + this.fileNameSuffix);
136 | }
137 |
138 | ///
139 | /// Selects the matches.
140 | ///
141 | /// The file names.
142 | /// A of s.
143 | public IEnumerable SelectMatches(IEnumerable fileNames)
144 | {
145 | foreach (var fileName in fileNames)
146 | {
147 | var match = this.fileNameMatcher.Match(fileName);
148 |
149 | if (!match.Success)
150 | {
151 | continue;
152 | }
153 |
154 | int? inc = null;
155 | var incGroup = match.Groups[SequenceNumberMatchGroup];
156 |
157 | if (incGroup.Captures.Count != 0)
158 | {
159 | var incPart = incGroup.Captures[0].Value.Substring(1);
160 | inc = int.Parse(incPart, CultureInfo.InvariantCulture);
161 | }
162 |
163 | DateTime? period = null;
164 | var periodGroup = match.Groups[PeriodMatchGroup];
165 | if (periodGroup.Captures.Count != 0)
166 | {
167 | var dateTimePart = periodGroup.Captures[0].Value;
168 | if (DateTime.TryParseExact(
169 | dateTimePart,
170 | this.periodFormat,
171 | CultureInfo.InvariantCulture,
172 | DateTimeStyles.None,
173 | out var dateTime))
174 | {
175 | period = dateTime;
176 | }
177 | }
178 |
179 | yield return new RollingLogFile(fileName, period, inc);
180 | }
181 | }
182 |
183 | ///
184 | /// Gets the current check point as .
185 | ///
186 | /// The date time.
187 | /// The current check point as .
188 | public DateTime? GetCurrentCheckpoint(DateTime dateTime) => this.interval.GetCurrentCheckpoint(dateTime);
189 |
190 | ///
191 | /// Gets the next check point as .
192 | ///
193 | /// The date time.
194 | /// The next check point as .
195 | public DateTime? GetNextCheckpoint(DateTime dateTime) => this.interval.GetNextCheckpoint(dateTime);
196 | }
197 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.AmazonS3/Sinks/AmazonS3/RollingInterval.cs:
--------------------------------------------------------------------------------
1 | // --------------------------------------------------------------------------------------------------------------------
2 | //
3 | // The project is double licensed under the MIT license and the Apache License, Version 2.0.
4 | // This code is a partly modified source code of the original Serilog code.
5 | // The original license is:
6 | //
7 | // --------------------------------------------------------------------------------------------------------------------
8 | // Copyright 2013-2016 Serilog Contributors
9 | //
10 | // Licensed under the Apache License, Version 2.0 (the "License");
11 | // you may not use this file except in compliance with the License.
12 | // You may obtain a copy of the License at
13 | //
14 | // http://www.apache.org/licenses/LICENSE-2.0
15 | //
16 | // Unless required by applicable law or agreed to in writing, software
17 | // distributed under the License is distributed on an "AS IS" BASIS,
18 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 | // See the License for the specific language governing permissions and
20 | // limitations under the License.
21 | // --------------------------------------------------------------------------------------------------------------------
22 | //
23 | //
24 | //
25 | // Specifies the frequency at which the log file should roll.
26 | //
27 | // --------------------------------------------------------------------------------------------------------------------
28 |
29 | namespace Serilog.Sinks.AmazonS3;
30 |
31 | ///
32 | /// Specifies the frequency at which the log file should roll.
33 | ///
34 | public enum RollingInterval
35 | {
36 | ///
37 | /// The log file will never roll; no time period information will be appended to the log file name.
38 | ///
39 | Infinite,
40 |
41 | ///
42 | /// Roll every year. File names will have a four-digit year appended in the pattern yyyy
.
43 | ///
44 | Year,
45 |
46 | ///
47 | /// Roll every calendar month. File names will have yyyyMM
appended.
48 | ///
49 | Month,
50 |
51 | ///
52 | /// Roll every day. File names will have yyyyMMdd
appended.
53 | ///
54 | Day,
55 |
56 | ///
57 | /// Roll every hour. File names will have yyyyMMddHH
appended.
58 | ///
59 | Hour,
60 |
61 | ///
62 | /// Roll every minute. File names will have yyyyMMddHHmm
appended.
63 | ///
64 | Minute
65 | }
66 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.AmazonS3/Sinks/AmazonS3/RollingIntervalExtensions.cs:
--------------------------------------------------------------------------------
1 | // --------------------------------------------------------------------------------------------------------------------
2 | //
3 | // The project is double licensed under the MIT license and the Apache License, Version 2.0.
4 | // This code is a partly modified source code of the original Serilog code.
5 | // The original license is:
6 | //
7 | // --------------------------------------------------------------------------------------------------------------------
8 | // Copyright 2013-2016 Serilog Contributors
9 | //
10 | // Licensed under the Apache License, Version 2.0 (the "License");
11 | // you may not use this file except in compliance with the License.
12 | // You may obtain a copy of the License at
13 | //
14 | // http://www.apache.org/licenses/LICENSE-2.0
15 | //
16 | // Unless required by applicable law or agreed to in writing, software
17 | // distributed under the License is distributed on an "AS IS" BASIS,
18 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 | // See the License for the specific language governing permissions and
20 | // limitations under the License.
21 | // --------------------------------------------------------------------------------------------------------------------
22 | //
23 | //
24 | //
25 | // This class contains extension methods for the enumeration.
26 | //
27 | // --------------------------------------------------------------------------------------------------------------------
28 |
29 | namespace Serilog.Sinks.AmazonS3;
30 |
31 | ///
32 | /// This class contains extension methods for the enumeration.
33 | ///
34 | public static class RollingIntervalExtensions
35 | {
36 | ///
37 | /// Gets the format .
38 | ///
39 | /// The interval.
40 | /// The format .
41 | public static string GetFormat(this RollingInterval interval)
42 | {
43 | return interval switch
44 | {
45 | RollingInterval.Infinite => "",
46 | RollingInterval.Year => "yyyy",
47 | RollingInterval.Month => "yyyyMM",
48 | RollingInterval.Day => "yyyyMMdd",
49 | RollingInterval.Hour => "yyyyMMddHH",
50 | RollingInterval.Minute => "yyyyMMddHHmm",
51 | _ => throw new ArgumentException("Invalid rolling interval"),
52 | };
53 | }
54 |
55 | ///
56 | /// Gets the current check point as .
57 | ///
58 | /// The interval.
59 | /// The date time.
60 | /// The current check point as .
61 | public static DateTime? GetCurrentCheckpoint(this RollingInterval interval, DateTime dateTime)
62 | {
63 | return interval switch
64 | {
65 | RollingInterval.Infinite => null,
66 | RollingInterval.Year => new DateTime(dateTime.Year, 1, 1, 0, 0, 0, dateTime.Kind),
67 | RollingInterval.Month => new DateTime(dateTime.Year, dateTime.Month, 1, 0, 0, 0, dateTime.Kind),
68 | RollingInterval.Day => new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, 0, 0, 0, dateTime.Kind),
69 | RollingInterval.Hour => new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, 0, 0, dateTime.Kind),
70 | RollingInterval.Minute => new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, 0, dateTime.Kind),
71 | _ => throw new ArgumentException("Invalid rolling interval"),
72 | };
73 | }
74 |
75 | ///
76 | /// Gets the next check point as .
77 | ///
78 | /// The interval.
79 | /// The date time.
80 | /// The next check point as .
81 | public static DateTime? GetNextCheckpoint(this RollingInterval interval, DateTime dateTime)
82 | {
83 | var current = GetCurrentCheckpoint(interval, dateTime);
84 |
85 | if (current is null)
86 | {
87 | return null;
88 | }
89 |
90 | return interval switch
91 | {
92 | RollingInterval.Year => current.Value.AddYears(1),
93 | RollingInterval.Month => current.Value.AddMonths(1),
94 | RollingInterval.Day => current.Value.AddDays(1),
95 | RollingInterval.Hour => current.Value.AddHours(1),
96 | RollingInterval.Minute => current.Value.AddMinutes(1),
97 | _ => throw new ArgumentException("Invalid rolling interval"),
98 | };
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/Serilog.Sinks.AmazonS3/Sinks/AmazonS3/RollingLogFile.cs:
--------------------------------------------------------------------------------
1 | // --------------------------------------------------------------------------------------------------------------------
2 | //
3 | // The project is double licensed under the MIT license and the Apache License, Version 2.0.
4 | // This code is a partly modified source code of the original Serilog code.
5 | // The original license is:
6 | //
7 | // --------------------------------------------------------------------------------------------------------------------
8 | // Copyright 2013-2016 Serilog Contributors
9 | //
10 | // Licensed under the Apache License, Version 2.0 (the "License");
11 | // you may not use this file except in compliance with the License.
12 | // You may obtain a copy of the License at
13 | //
14 | // http://www.apache.org/licenses/LICENSE-2.0
15 | //
16 | // Unless required by applicable law or agreed to in writing, software
17 | // distributed under the License is distributed on an "AS IS" BASIS,
18 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 | // See the License for the specific language governing permissions and
20 | // limitations under the License.
21 | // --------------------------------------------------------------------------------------------------------------------
22 | //
23 | //
24 | //
25 | // The rolling log file class to handle rolling log files.
26 | //
27 | // --------------------------------------------------------------------------------------------------------------------
28 |
29 | namespace Serilog.Sinks.AmazonS3;
30 |
31 | ///
32 | /// The rolling log file class to handle rolling log files.
33 | ///
34 | public class RollingLogFile
35 | {
36 | ///
37 | /// Initializes a new instance of the class.
38 | ///
39 | /// The file name.
40 | /// The date time.
41 | /// The sequence number.
42 | public RollingLogFile(string fileName, DateTime? dateTime, int? sequenceNumber)
43 | {
44 | this.FileName = fileName;
45 | this.DateTime = dateTime;
46 | this.SequenceNumber = sequenceNumber;
47 | }
48 |
49 | ///
50 | /// Gets the file name.
51 | ///
52 | public string FileName { get; }
53 |
54 | ///
55 | /// Gets the date time.
56 | ///
57 | public DateTime? DateTime { get; }
58 |
59 | ///
60 | /// Gets the sequence number.
61 | ///
62 | public int? SequenceNumber { get; }
63 | }
64 |
--------------------------------------------------------------------------------