├── .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 | [![Build status](https://coshea.visualstudio.com/Seq.Client.EventLog/_apis/build/status/Seq.Client.EventLog-.NET%20Desktop-CI)](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 | ![](https://raw.githubusercontent.com/c0shea/Seq.Client.EventLog/master/Screenshot.png) 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 | --------------------------------------------------------------------------------