├── .gitattributes
├── .gitignore
├── LICENSE.md
├── README.md
├── Screenshot.png
└── src
├── Seq.Client.EventLog.sln
└── Seq.Client.EventLog
├── App.config
├── EventLogClient.cs
├── EventLogListener.cs
├── EventLogListeners.json
├── Extensions.cs
├── Program.cs
├── ProjectInstaller.Designer.cs
├── ProjectInstaller.cs
├── ProjectInstaller.resx
├── Properties
├── AssemblyInfo.cs
├── Settings.Designer.cs
└── Settings.settings
├── RawEvent.cs
├── RawEvents.cs
├── Seq.Client.EventLog.csproj
├── SeqApi.cs
├── Service.Designer.cs
├── Service.cs
├── Service.resx
└── packages.config
/.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 | [Xx]64/
19 | [Xx]86/
20 | [Bb]uild/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
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 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 | *.VC.db
84 |
85 | # Visual Studio profiler
86 | *.psess
87 | *.vsp
88 | *.vspx
89 | *.sap
90 |
91 | # TFS 2012 Local Workspace
92 | $tf/
93 |
94 | # Guidance Automation Toolkit
95 | *.gpState
96 |
97 | # ReSharper is a .NET coding add-in
98 | _ReSharper*/
99 | *.[Rr]e[Ss]harper
100 | *.DotSettings.user
101 |
102 | # JustCode is a .NET coding add-in
103 | .JustCode
104 |
105 | # TeamCity is a build add-in
106 | _TeamCity*
107 |
108 | # DotCover is a Code Coverage Tool
109 | *.dotCover
110 |
111 | # NCrunch
112 | _NCrunch_*
113 | .*crunch*.local.xml
114 | nCrunchTemp_*
115 |
116 | # MightyMoose
117 | *.mm.*
118 | AutoTest.Net/
119 |
120 | # Web workbench (sass)
121 | .sass-cache/
122 |
123 | # Installshield output folder
124 | [Ee]xpress/
125 |
126 | # DocProject is a documentation generator add-in
127 | DocProject/buildhelp/
128 | DocProject/Help/*.HxT
129 | DocProject/Help/*.HxC
130 | DocProject/Help/*.hhc
131 | DocProject/Help/*.hhk
132 | DocProject/Help/*.hhp
133 | DocProject/Help/Html2
134 | DocProject/Help/html
135 |
136 | # Click-Once directory
137 | publish/
138 |
139 | # Publish Web Output
140 | *.[Pp]ublish.xml
141 | *.azurePubxml
142 |
143 | # TODO: Un-comment the next line if you do not want to checkin
144 | # your web deploy settings because they may include unencrypted
145 | # passwords
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # NuGet Packages
150 | *.nupkg
151 | # The packages folder can be ignored because of Package Restore
152 | **/packages/*
153 | # except build/, which is used as an MSBuild target.
154 | !**/packages/build/
155 | # Uncomment if necessary however generally it will be regenerated when needed
156 | #!**/packages/repositories.config
157 | # NuGet v3's project.json files produces more ignoreable files
158 | *.nuget.props
159 | *.nuget.targets
160 |
161 | # Microsoft Azure Build Output
162 | csx/
163 | *.build.csdef
164 |
165 | # Microsoft Azure Emulator
166 | ecf/
167 | rcf/
168 |
169 | # Windows Store app package directory
170 | AppPackages/
171 | BundleArtifacts/
172 |
173 | # Visual Studio cache files
174 | # files ending in .cache can be ignored
175 | *.[Cc]ache
176 | # but keep track of directories ending in .cache
177 | !*.[Cc]ache/
178 |
179 | # Others
180 | ClientBin/
181 | [Ss]tyle[Cc]op.*
182 | ~$*
183 | *~
184 | *.dbmdl
185 | *.dbproj.schemaview
186 | *.pfx
187 | *.publishsettings
188 | node_modules/
189 | orleans.codegen.cs
190 |
191 | # RIA/Silverlight projects
192 | Generated_Code/
193 |
194 | # Backup & report files from converting an old project file
195 | # to a newer Visual Studio version. Backup files are not needed,
196 | # because we have git ;-)
197 | _UpgradeReport_Files/
198 | Backup*/
199 | UpgradeLog*.XML
200 | UpgradeLog*.htm
201 |
202 | # SQL Server files
203 | *.mdf
204 | *.ldf
205 |
206 | # Business Intelligence projects
207 | *.rdl.data
208 | *.bim.layout
209 | *.bim_*.settings
210 |
211 | # Microsoft Fakes
212 | FakesAssemblies/
213 |
214 | # GhostDoc plugin setting file
215 | *.GhostDoc.xml
216 |
217 | # Node.js Tools for Visual Studio
218 | .ntvs_analysis.dat
219 |
220 | # Visual Studio 6 build log
221 | *.plg
222 |
223 | # Visual Studio 6 workspace options file
224 | *.opt
225 |
226 | # Visual Studio LightSwitch build output
227 | **/*.HTMLClient/GeneratedArtifacts
228 | **/*.DesktopClient/GeneratedArtifacts
229 | **/*.DesktopClient/ModelManifest.xml
230 | **/*.Server/GeneratedArtifacts
231 | **/*.Server/ModelManifest.xml
232 | _Pvt_Extensions
233 |
234 | # LightSwitch generated files
235 | GeneratedArtifacts/
236 | ModelManifest.xml
237 |
238 | # Paket dependency manager
239 | .paket/paket.exe
240 |
241 | # FAKE - F# Make
242 | .fake/
243 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Connor O'Shea
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.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Seq.Client.EventLog
2 |
3 | [](https://coshea.visualstudio.com/Seq.Client.EventLog/_build/latest?definitionId=-1)
4 |
5 | [Seq](https://getseq.net/) is a fantastic tool for handling structured logs in .NET apps. There's a lot of value in having a centralized log repository that can ingest events from many sources.
6 |
7 | The trouble, however, is that applications beyond your control write useful information to the Windows Event Logs. That's where the EventLog service comes in. Define the logs and filters you care about and the service takes care of ingesting them into Seq.
8 |
9 | ## Get Started
10 |
11 | 1. [Download the latest release](https://github.com/c0shea/Seq.Client.EventLog/releases) of Seq.Client.EventLog.
12 | 2. Extract it to your preferred install directory.
13 | 3. Edit the ```Seq.Client.EventLog.exe.config``` file, replacing the ```SeqUri``` with the URL of your Seq server. If you configured Seq to use API keys, also specify your key in the config file.
14 | 4. Edit the ```EventLogListeners.json``` file. There are sensible defaults in place, but you can change them you suit your needs.
15 | 5. From the command line, run ```Seq.Client.EventLog.exe /install```. This will install the Windows Service and set it to start automatically at boot.
16 | 6. From the command line, run ```net start Seq.Client.EventLog``` to start the service.
17 | 7. Click the refresh button in Seq as you wait anxiously for the events to start flooding in!
18 |
19 | ## Enriched Events
20 |
21 | Events are ingested into Seq with a few useful properties that allow for easy searching.
22 |
23 | 
24 |
25 | ## Event Log Listeners
26 |
27 | The JSON config file allows for multiple listeners to be defined. Each one should be a new object in the array.
28 |
29 | - **LogName**: The Windows Event Log name to listen to, e.g. Application, Security, etc.
30 | - **MachineName**: If specified, the hostname of the machine to listen to events from for the log name. Omitting this value defaults to the machine the service is running on.
31 | - **LogLevels**: A list of the integer severity levels of the entry. 1 = Error, 2 = Warning, 4 = Information, 8 = Success Audit, 16 = Failure Audit. If not specified, all events will be sent.
32 | - **EventIds**: A list of the integer Event IDs of the entry. If not specified, all events will be sent.
33 | - **Sources**: A list of source names to filter the events sent to Seq. If not specified, all events will be sent.
34 | - **ProcessRetroactiveEntries**: If true, this will cause the service to send all matching event log entries that were written before the service started in addition to new entries. If false, only new entries written that meet the filter critera above since the service was started will be sent.
35 |
36 | ### Sample
37 |
38 | ```json
39 | [
40 | {
41 | "LogName": "Application",
42 | "LogLevels": [ 1, 2 ],
43 | "ProcessRetroactiveEntries": true
44 | }
45 | ]
46 | ```
--------------------------------------------------------------------------------
/Screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/c0shea/Seq.Client.EventLog/87b568d97b0f1235ce53c3ae80194b349ff10d23/Screenshot.png
--------------------------------------------------------------------------------
/src/Seq.Client.EventLog.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.28010.2050
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Seq.Client.EventLog", "Seq.Client.EventLog\Seq.Client.EventLog.csproj", "{B14232BD-B051-4255-9DEC-81EA174660E8}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{32DED70B-174F-4C3F-BC48-B776AB83C1BE}"
9 | ProjectSection(SolutionItems) = preProject
10 | ..\README.md = ..\README.md
11 | EndProjectSection
12 | EndProject
13 | Global
14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
15 | Debug|Any CPU = Debug|Any CPU
16 | Release|Any CPU = Release|Any CPU
17 | EndGlobalSection
18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
19 | {B14232BD-B051-4255-9DEC-81EA174660E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
20 | {B14232BD-B051-4255-9DEC-81EA174660E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
21 | {B14232BD-B051-4255-9DEC-81EA174660E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
22 | {B14232BD-B051-4255-9DEC-81EA174660E8}.Release|Any CPU.Build.0 = Release|Any CPU
23 | EndGlobalSection
24 | GlobalSection(SolutionProperties) = preSolution
25 | HideSolutionNode = FALSE
26 | EndGlobalSection
27 | GlobalSection(ExtensibilityGlobals) = postSolution
28 | SolutionGuid = {D3E72DDE-EBE2-42EB-BA3E-3E7F80DDD4ED}
29 | EndGlobalSection
30 | EndGlobal
31 |
--------------------------------------------------------------------------------
/src/Seq.Client.EventLog/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | http://localhost:5341
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/Seq.Client.EventLog/EventLogClient.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using System.Reflection;
4 | using Newtonsoft.Json;
5 | using Serilog;
6 |
7 | namespace Seq.Client.EventLog
8 | {
9 | class EventLogClient
10 | {
11 | private List _eventLogListeners;
12 |
13 | public void Start(string configuration = null)
14 | {
15 | LoadListeners(configuration);
16 | ValidateListeners();
17 | StartListeners();
18 | }
19 |
20 | public void Stop()
21 | {
22 | StopListeners();
23 | }
24 |
25 | private void LoadListeners(string configuration)
26 | {
27 | string filePath;
28 | if (configuration == null)
29 | {
30 | var directory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
31 | filePath = Path.Combine(directory ?? ".", "EventLogListeners.json");
32 | }
33 | else
34 | {
35 | filePath = configuration;
36 | }
37 |
38 | Log.Information("Loading listener configuration from {ConfigurationFilePath}", filePath);
39 | var file = File.ReadAllText(filePath);
40 |
41 | _eventLogListeners = JsonConvert.DeserializeObject>(file);
42 | }
43 |
44 | private void ValidateListeners()
45 | {
46 | foreach (var listener in _eventLogListeners)
47 | {
48 | listener.Validate();
49 | }
50 | }
51 |
52 | private void StartListeners()
53 | {
54 | foreach (var listener in _eventLogListeners)
55 | {
56 | listener.Start();
57 | }
58 | }
59 |
60 | private void StopListeners()
61 | {
62 | foreach (var listener in _eventLogListeners)
63 | {
64 | listener.Stop();
65 | }
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/Seq.Client.EventLog/EventLogListener.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 |
7 | namespace Seq.Client.EventLog
8 | {
9 | public class EventLogListener
10 | {
11 | public string LogName { get; set; }
12 | public string MachineName { get; set; }
13 | public bool ProcessRetroactiveEntries { get; set; }
14 |
15 | // These properties allow for the filtering of events that will be sent to Seq.
16 | // If they are not specified in the JSON, all events in the log will be sent.
17 | public List LogLevels { get; set; }
18 | public List EventIds { get; set; }
19 | public List Sources { get; set; }
20 |
21 | private System.Diagnostics.EventLog _eventLog;
22 | private readonly CancellationTokenSource _cancel = new CancellationTokenSource();
23 | private Task _retroactiveLoadingTask;
24 | private volatile bool _started;
25 |
26 | public void Validate()
27 | {
28 | if (string.IsNullOrWhiteSpace(LogName))
29 | {
30 | throw new InvalidOperationException($"A {nameof(LogName)} must be specified for the listener.");
31 | }
32 | }
33 |
34 | public void Start()
35 | {
36 | try
37 | {
38 | Serilog.Log.Information("Starting listener for {LogName} on {MachineName}", LogName, MachineName ?? ".");
39 |
40 | _eventLog = OpenEventLog();
41 |
42 | if (ProcessRetroactiveEntries)
43 | {
44 | // Start as a new task so it doesn't block the startup of the service. This has
45 | // to go on its own thread to avoid deadlocking via `Wait()`/`Result`.
46 | _retroactiveLoadingTask = Task.Factory.StartNew(SendRetroactiveEntries, TaskCreationOptions.LongRunning);
47 | }
48 |
49 | _eventLog.EntryWritten += OnEntryWritten;
50 | _eventLog.EnableRaisingEvents = true;
51 | _started = true;
52 | }
53 | catch (Exception ex)
54 | {
55 | Serilog.Log.Error(ex, "Failed to start listener for {LogName} on {MachineName}", LogName, MachineName ?? ".");
56 | }
57 | }
58 |
59 | System.Diagnostics.EventLog OpenEventLog()
60 | {
61 | var eventLog = new System.Diagnostics.EventLog(LogName);
62 | if (!string.IsNullOrWhiteSpace(MachineName))
63 | {
64 | eventLog.MachineName = MachineName;
65 | }
66 |
67 | return eventLog;
68 | }
69 |
70 | public void Stop()
71 | {
72 | try
73 | {
74 | if (!_started)
75 | return;
76 |
77 | _cancel.Cancel();
78 | _eventLog.EnableRaisingEvents = false;
79 |
80 | // This would be a little racy if start and stop were ever called on different threads, but
81 | // this isn't done, currently.
82 | _retroactiveLoadingTask?.Wait();
83 |
84 | _eventLog.Close();
85 | _eventLog.Dispose();
86 |
87 | Serilog.Log.Information("Listener stopped");
88 | }
89 | catch (Exception ex)
90 | {
91 | Serilog.Log.Error(ex, "Failed to stop listener");
92 | }
93 | }
94 |
95 | private void SendRetroactiveEntries()
96 | {
97 | try
98 | {
99 | using (var eventLog = OpenEventLog())
100 | {
101 | Serilog.Log.Information("Processing {EntryCount} retroactive entries in {LogName}", eventLog.Entries.Count, LogName);
102 |
103 | foreach (EventLogEntry entry in eventLog.Entries)
104 | {
105 | if (_cancel.IsCancellationRequested)
106 | {
107 | Serilog.Log.Warning("Canceling retroactive event loading");
108 | return;
109 | }
110 |
111 | HandleEventLogEntry(entry, eventLog.Log).GetAwaiter().GetResult();
112 | }
113 | }
114 | }
115 | catch (Exception ex)
116 | {
117 | Serilog.Log.Error(ex, "Failed to send retroactive entries in {LogName} on {MachineName}", LogName, MachineName ?? ".");
118 | }
119 | }
120 |
121 | private void OnEntryWritten(object sender, EntryWrittenEventArgs args)
122 | {
123 | try
124 | {
125 | HandleEventLogEntry(args.Entry, _eventLog.Log).GetAwaiter().GetResult();
126 | }
127 | catch (Exception ex)
128 | {
129 | Serilog.Log.Error(ex, "Failed to handle an event log entry");
130 | }
131 | }
132 |
133 | private async Task HandleEventLogEntry(EventLogEntry entry, string logName)
134 | {
135 | // Don't send the entry to Seq if it doesn't match the filtered log levels, event IDs, or sources
136 | if (LogLevels != null && LogLevels.Count > 0 && !LogLevels.Contains(entry.EntryType))
137 | return;
138 |
139 | // EventID is obsolete
140 | #pragma warning disable 618
141 | if (EventIds != null && EventIds.Count > 0 && !EventIds.Contains(entry.EventID))
142 | #pragma warning restore 618
143 | return;
144 |
145 | if (Sources != null && Sources.Count > 0 && !Sources.Contains(entry.Source))
146 | return;
147 |
148 | await SeqApi.PostRawEvents(entry.ToDto(logName));
149 | }
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/src/Seq.Client.EventLog/EventLogListeners.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "LogName": "Application",
4 | "LogLevels": [ 1, 2 ],
5 | "ProcessRetroactiveEntries": true
6 | },
7 | {
8 | "LogName": "Security",
9 | "LogLevels": [ 16 ],
10 | "ProcessRetroactiveEntries": true
11 | },
12 | {
13 | "LogName": "Setup",
14 | "LogLevels": [ 1, 2 ],
15 | "ProcessRetroactiveEntries": true
16 | },
17 | {
18 | "LogName": "System",
19 | "LogLevels": [ 1, 2 ],
20 | "ProcessRetroactiveEntries": true
21 | }
22 | ]
23 |
--------------------------------------------------------------------------------
/src/Seq.Client.EventLog/Extensions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Diagnostics;
3 |
4 | namespace Seq.Client.EventLog
5 | {
6 | public static class Extensions
7 | {
8 | private static string MapLogLevel(EventLogEntryType type)
9 | {
10 | switch (type)
11 | {
12 | case EventLogEntryType.Information:
13 | return "Information";
14 | case EventLogEntryType.Warning:
15 | return "Warning";
16 | case EventLogEntryType.Error:
17 | return "Error";
18 | case EventLogEntryType.SuccessAudit:
19 | return "Information";
20 | case EventLogEntryType.FailureAudit:
21 | return "Warning";
22 | default:
23 | return "Debug";
24 | }
25 | }
26 |
27 | public static RawEvents ToDto(this EventLogEntry entry, string logName)
28 | {
29 | return new RawEvents
30 | {
31 | Events = new[]
32 | {
33 | new RawEvent
34 | {
35 | Timestamp = entry.TimeGenerated,
36 | Level = MapLogLevel(entry.EntryType),
37 | MessageTemplate = entry.Message,
38 | Properties = new Dictionary
39 | {
40 | { "MachineName", entry.MachineName },
41 | #pragma warning disable 618
42 | { "EventId", entry.EventID },
43 | #pragma warning restore 618
44 | { "InstanceId", entry.InstanceId },
45 | { "Source", entry.Source },
46 | { "Category", entry.CategoryNumber },
47 | { "EventLogName", logName }
48 | }
49 | },
50 | }
51 | };
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Seq.Client.EventLog/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Configuration.Install;
3 | using System.IO;
4 | using System.Reflection;
5 | using System.ServiceProcess;
6 | using System.Threading;
7 | using Serilog;
8 |
9 | namespace Seq.Client.EventLog
10 | {
11 | static class Program
12 | {
13 | ///
14 | /// The main entry point for the application.
15 | /// The service can be installed or uninstalled from the command line
16 | /// by passing the /install or /uninstall argument, and can be run
17 | /// interactively by specifying the path to the JSON configuration file.
18 | ///
19 | public static void Main(string[] args)
20 | {
21 | // Allows the installation and uninstallation via the command line
22 | if (Environment.UserInteractive)
23 | {
24 | var parameter = string.Concat(args);
25 | if (string.IsNullOrWhiteSpace(parameter))
26 | {
27 | parameter = null;
28 | }
29 |
30 | switch (parameter)
31 | {
32 | case "/install":
33 | ManagedInstallerClass.InstallHelper(new[] { Assembly.GetExecutingAssembly().Location });
34 | break;
35 | case "/uninstall":
36 | ManagedInstallerClass.InstallHelper(new[] { "/u", Assembly.GetExecutingAssembly().Location });
37 | break;
38 | default:
39 | RunInteractive(parameter);
40 | break;
41 | }
42 | }
43 | else
44 | {
45 | RunService();
46 | }
47 | }
48 |
49 | static void RunInteractive(string configFilePath)
50 | {
51 | Log.Logger = new LoggerConfiguration()
52 | .WriteTo.Console()
53 | .CreateLogger();
54 |
55 | try
56 | {
57 | Log.Information("Running interactively");
58 |
59 | var client = new EventLogClient();
60 | client.Start(configFilePath);
61 |
62 | var done = new ManualResetEvent(false);
63 | Console.CancelKeyPress += (s, e) =>
64 | {
65 | Log.Information("Ctrl+C pressed, stopping");
66 | client.Stop();
67 | done.Set();
68 | };
69 |
70 | done.WaitOne();
71 | Log.Information("Stopped");
72 | }
73 | catch (Exception ex)
74 | {
75 | Log.Fatal(ex, "An unhandled exception occurred");
76 | Environment.ExitCode = 1;
77 | }
78 | finally
79 | {
80 | Log.CloseAndFlush();
81 | }
82 | }
83 |
84 | static void RunService()
85 | {
86 | var logFile = Path.Combine(
87 | Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
88 | typeof(Program).Assembly.GetName().Name,
89 | "ServiceLog.txt");
90 |
91 | Log.Logger = new LoggerConfiguration()
92 | .WriteTo.File(
93 | logFile,
94 | rollingInterval: RollingInterval.Day,
95 | rollOnFileSizeLimit: true,
96 | retainedFileCountLimit: 7,
97 | fileSizeLimitBytes: 10_000_000,
98 | shared: true)
99 | .CreateLogger();
100 |
101 | try
102 | {
103 | Log.Information("Running as service");
104 | ServiceBase.Run(new Service());
105 | Log.Information("Stopped");
106 | }
107 | catch (Exception ex)
108 | {
109 | Log.Fatal(ex, "Exception thrown from service host");
110 | }
111 | finally
112 | {
113 | Log.CloseAndFlush();
114 | }
115 | }
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/src/Seq.Client.EventLog/ProjectInstaller.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace Seq.Client.EventLog
2 | {
3 | partial class ProjectInstaller
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Component Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.serviceProcessInstaller = new System.ServiceProcess.ServiceProcessInstaller();
32 | this.serviceInstaller = new System.ServiceProcess.ServiceInstaller();
33 | //
34 | // serviceProcessInstaller
35 | //
36 | this.serviceProcessInstaller.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
37 | this.serviceProcessInstaller.Password = null;
38 | this.serviceProcessInstaller.Username = null;
39 | //
40 | // serviceInstaller
41 | //
42 | this.serviceInstaller.Description = "Writes Windows Event Log entries to Seq";
43 | this.serviceInstaller.DisplayName = "Seq.Client.EventLog";
44 | this.serviceInstaller.ServiceName = "Seq.Client.EventLog";
45 | this.serviceInstaller.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
46 | //
47 | // ProjectInstaller
48 | //
49 | this.Installers.AddRange(new System.Configuration.Install.Installer[] {
50 | this.serviceProcessInstaller,
51 | this.serviceInstaller});
52 |
53 | }
54 |
55 | #endregion
56 |
57 | private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller;
58 | private System.ServiceProcess.ServiceInstaller serviceInstaller;
59 | }
60 | }
--------------------------------------------------------------------------------
/src/Seq.Client.EventLog/ProjectInstaller.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.ComponentModel;
5 | using System.Configuration.Install;
6 | using System.Linq;
7 | using System.Threading.Tasks;
8 |
9 | namespace Seq.Client.EventLog
10 | {
11 | [RunInstaller(true)]
12 | public partial class ProjectInstaller : System.Configuration.Install.Installer
13 | {
14 | public ProjectInstaller()
15 | {
16 | InitializeComponent();
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Seq.Client.EventLog/ProjectInstaller.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | 17, 56
122 |
123 |
124 | 196, 17
125 |
126 |
127 | False
128 |
129 |
--------------------------------------------------------------------------------
/src/Seq.Client.EventLog/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("Seq.Client.EventLog")]
8 | [assembly: AssemblyDescription("Writes Windows Event Log entries to Seq")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("Seq.Client.EventLog")]
12 | [assembly: AssemblyCopyright("Copyright © 2018 Connor O'Shea")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("b14232bd-b051-4255-9dec-81ea174660e8")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("2.0.0.0")]
35 | [assembly: AssemblyFileVersion("2.0.0.0")]
36 |
--------------------------------------------------------------------------------
/src/Seq.Client.EventLog/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace Seq.Client.EventLog.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 |
26 | [global::System.Configuration.ApplicationScopedSettingAttribute()]
27 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
28 | [global::System.Configuration.DefaultSettingValueAttribute("http://SERVER:5341")]
29 | public string SeqUri {
30 | get {
31 | return ((string)(this["SeqUri"]));
32 | }
33 | }
34 |
35 | [global::System.Configuration.ApplicationScopedSettingAttribute()]
36 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
37 | [global::System.Configuration.DefaultSettingValueAttribute("")]
38 | public string ApiKey {
39 | get {
40 | return ((string)(this["ApiKey"]));
41 | }
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Seq.Client.EventLog/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | http://SERVER:5341
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/Seq.Client.EventLog/RawEvent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Seq.Client.EventLog
5 | {
6 | public class RawEvent
7 | {
8 | public DateTimeOffset Timestamp { get; set; }
9 |
10 | // Uses the Serilog level names
11 | public string Level { get; set; }
12 |
13 | public string MessageTemplate { get; set; }
14 |
15 | public Dictionary Properties { get; set; }
16 |
17 | public string Exception { get; set; }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Seq.Client.EventLog/RawEvents.cs:
--------------------------------------------------------------------------------
1 | namespace Seq.Client.EventLog
2 | {
3 | public class RawEvents
4 | {
5 | public RawEvent[] Events { get; set; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/Seq.Client.EventLog/Seq.Client.EventLog.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {B14232BD-B051-4255-9DEC-81EA174660E8}
8 | Exe
9 | Properties
10 | Seq.Client.EventLog
11 | Seq.Client.EventLog
12 | v4.6.2
13 | 512
14 | true
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | full
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 | true
35 |
36 |
37 |
38 |
39 |
40 |
41 | ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll
42 | True
43 |
44 |
45 | ..\packages\Serilog.2.7.1\lib\net46\Serilog.dll
46 |
47 |
48 | ..\packages\Serilog.Sinks.Console.3.1.1\lib\net45\Serilog.Sinks.Console.dll
49 |
50 |
51 | ..\packages\Serilog.Sinks.File.4.0.0\lib\net45\Serilog.Sinks.File.dll
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | Component
72 |
73 |
74 | ProjectInstaller.cs
75 |
76 |
77 | True
78 | True
79 | Settings.settings
80 |
81 |
82 |
83 |
84 |
85 | Component
86 |
87 |
88 | Service.cs
89 |
90 |
91 |
92 |
93 |
94 |
95 | Designer
96 |
97 |
98 | PreserveNewest
99 |
100 |
101 | Designer
102 |
103 |
104 | SettingsSingleFileGenerator
105 | Settings.Designer.cs
106 |
107 |
108 |
109 |
110 | ProjectInstaller.cs
111 |
112 |
113 | Service.cs
114 |
115 |
116 |
117 |
124 |
--------------------------------------------------------------------------------
/src/Seq.Client.EventLog/SeqApi.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http;
2 | using System.Text;
3 | using System.Threading.Tasks;
4 | using Newtonsoft.Json;
5 | using Seq.Client.EventLog.Properties;
6 |
7 | namespace Seq.Client.EventLog
8 | {
9 | static class SeqApi
10 | {
11 | public static readonly HttpClient HttpClient = new HttpClient();
12 |
13 | public static async Task PostRawEvents(RawEvents rawEvents)
14 | {
15 | var uri = Settings.Default.SeqUri + "/api/events/raw";
16 |
17 | if (!string.IsNullOrWhiteSpace(Settings.Default.ApiKey))
18 | {
19 | uri += "?apiKey=" + Settings.Default.ApiKey;
20 | }
21 |
22 | var content = new StringContent(
23 | JsonConvert.SerializeObject(rawEvents, Formatting.None),
24 | Encoding.UTF8,
25 | "application/json");
26 |
27 | var result = await HttpClient.PostAsync(uri, content).ConfigureAwait(false);
28 | if (!result.IsSuccessStatusCode)
29 | {
30 | Serilog.Log.Error("Received failure status code {StatusCode} from Seq: {ReasonPhrase}", result.StatusCode, result.ReasonPhrase);
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Seq.Client.EventLog/Service.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace Seq.Client.EventLog
2 | {
3 | partial class Service
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Component Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | //
32 | // Service
33 | //
34 | this.ServiceName = "Seq.Client.EventLog";
35 |
36 | }
37 |
38 | #endregion
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Seq.Client.EventLog/Service.cs:
--------------------------------------------------------------------------------
1 | using System.ServiceProcess;
2 |
3 | namespace Seq.Client.EventLog
4 | {
5 | public partial class Service : ServiceBase
6 | {
7 | private readonly EventLogClient _client = new EventLogClient();
8 |
9 | #region Windows Service Base
10 | public Service()
11 | {
12 | InitializeComponent();
13 | }
14 |
15 | protected override void OnStart(string[] args)
16 | {
17 | _client.Start();
18 | }
19 |
20 | protected override void OnStop()
21 | {
22 | _client.Stop();
23 | }
24 | #endregion
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Seq.Client.EventLog/Service.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | False
122 |
123 |
--------------------------------------------------------------------------------
/src/Seq.Client.EventLog/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------