Compare commits
224 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f22c83ddf1 | ||
|
|
9d9dbb582d | ||
|
|
460aa3229e | ||
|
|
b9758affac | ||
|
|
c90869592b | ||
|
|
2c9d998794 | ||
|
|
7d43c2cb67 | ||
|
|
45e4e80488 | ||
|
|
097ffd34c3 | ||
|
|
147e243478 | ||
|
|
8694ede85d | ||
|
|
7cf1230d7a | ||
|
|
4fe5cec9e2 | ||
|
|
9308f91316 | ||
|
|
28d20199ab | ||
|
|
5692d2ec42 | ||
|
|
4dbb926698 | ||
|
|
92f23c1117 | ||
|
|
cd31cf47f5 | ||
|
|
8fe3b1815c | ||
|
|
58ae2db05c | ||
|
|
f1c4343cf6 | ||
|
|
3669acbb26 | ||
|
|
6600bb2b85 | ||
|
|
50473a1f5a | ||
|
|
dcbb079101 | ||
|
|
091fe9e7e4 | ||
|
|
6db530b526 | ||
|
|
4ebe539ea8 | ||
|
|
75619a239a | ||
|
|
fd67c271b5 | ||
|
|
1f3aaf90fd | ||
|
|
dcfdf73842 | ||
|
|
dcd104e4f4 | ||
|
|
2377d4ebd2 | ||
|
|
64979df012 | ||
|
|
5eb75ab143 | ||
|
|
3e9ef6ae88 | ||
|
|
7df4dede90 | ||
|
|
d9509757e3 | ||
|
|
edcac22ea0 | ||
|
|
e98ef89fe2 | ||
|
|
6461369d39 | ||
|
|
f5b2858c1c | ||
|
|
3d0bfc248d | ||
|
|
dbaa52a12e | ||
|
|
7e69175f19 | ||
|
|
baa70eebbc | ||
|
|
29308446a9 | ||
|
|
a0cccc0b7f | ||
|
|
ec2ec66915 | ||
|
|
98ef675f9c | ||
|
|
1468a6d0e6 | ||
|
|
44104f1e59 | ||
|
|
22baef9838 | ||
|
|
1b51fcd989 | ||
|
|
0b3204efe8 | ||
|
|
80f8f9ee0c | ||
|
|
9d08fc2b28 | ||
|
|
349b0bf031 | ||
|
|
8b431f92eb | ||
|
|
770ac1b7b1 | ||
|
|
5d07acad5b | ||
|
|
065aaa3ec4 | ||
|
|
835f4f6b61 | ||
|
|
2f84f46c39 | ||
|
|
77a2258aff | ||
|
|
8983916f6c | ||
|
|
775f33c843 | ||
|
|
3b58f768f4 | ||
|
|
c0dbe8f6e9 | ||
|
|
e4bd5be8b1 | ||
|
|
786b6b4777 | ||
|
|
51dc9ee54c | ||
|
|
796ca51f31 | ||
|
|
ef7e50b1b9 | ||
|
|
b43e26fc01 | ||
|
|
7f6e563238 | ||
|
|
e8a54782e4 | ||
|
|
4e5af689f6 | ||
|
|
77f8c0d303 | ||
|
|
9aeee16499 | ||
|
|
9ef79978a6 | ||
|
|
47f233f863 | ||
|
|
a6affcb3c9 | ||
|
|
fa1009af51 | ||
|
|
b937d6ef45 | ||
|
|
c16331ac10 | ||
|
|
ac10640edf | ||
|
|
e9c27c8c19 | ||
|
|
7be95a976e | ||
|
|
f2b0f1a85d | ||
|
|
eaba99144e | ||
|
|
3208da6488 | ||
|
|
fb473909e7 | ||
|
|
b7095471fb | ||
|
|
d7fa582191 | ||
|
|
6ea3e581fd | ||
|
|
99b51d240d | ||
|
|
fd123e3a86 | ||
|
|
42741fbe1e | ||
|
|
cdc2d09150 | ||
|
|
2e28a50222 | ||
|
|
92a9adeca8 | ||
|
|
91cf59612c | ||
|
|
e0aac878ff | ||
|
|
dc89ac996a | ||
|
|
57b29a5425 | ||
|
|
e5237e8e59 | ||
|
|
fb61a49fe5 | ||
|
|
775935683f | ||
|
|
024fa68e34 | ||
|
|
4654d7dbc1 | ||
|
|
d1e5224ba3 | ||
|
|
5a34b687bb | ||
|
|
2982482f25 | ||
|
|
4d9aa4fd54 | ||
|
|
5b0d28ce16 | ||
|
|
4b255bfd27 | ||
|
|
e7a8ff3eb7 | ||
|
|
3f227e0914 | ||
|
|
42301b5c9f | ||
|
|
b1c22ff0ca | ||
|
|
6f3d854cbe | ||
|
|
dec51efccd | ||
|
|
884efffb25 | ||
|
|
b032ffc8fa | ||
|
|
7b2aee8d13 | ||
|
|
d2483aa991 | ||
|
|
771aa791f1 | ||
|
|
0da192167a | ||
|
|
1eaa5a4657 | ||
|
|
cc28914690 | ||
|
|
27f3e114ef | ||
|
|
8a69b58dfd | ||
|
|
1063f493b5 | ||
|
|
afbc1e2294 | ||
|
|
1f65e45726 | ||
|
|
9734f3516e | ||
|
|
11762b6136 | ||
|
|
8ba2db7cf4 | ||
|
|
f4f8f0b50b | ||
|
|
9d25a8302b | ||
|
|
5f7b96206e | ||
|
|
20c16a1ec3 | ||
|
|
89684844dd | ||
|
|
0c283224bd | ||
|
|
f1412c6b76 | ||
|
|
ef4893691c | ||
|
|
aa3b0f6440 | ||
|
|
cbf80e94e5 | ||
|
|
ad21e34b29 | ||
|
|
8e45b0399b | ||
|
|
849e038550 | ||
|
|
85fcf11503 | ||
|
|
e25024da9d | ||
|
|
5c7f0e5ab9 | ||
|
|
5656f5b03f | ||
|
|
2c4e0700eb | ||
|
|
194ee43bfd | ||
|
|
9761b38fdb | ||
|
|
c67ff21859 | ||
|
|
6df3992110 | ||
|
|
05142d2a8f | ||
|
|
fc1d7402d0 | ||
|
|
61ab116327 | ||
|
|
5485081dc6 | ||
|
|
b35e195b49 | ||
|
|
c94b6ac1d7 | ||
|
|
d5f76dacbc | ||
|
|
387bd6c7f9 | ||
|
|
448a5613b2 | ||
|
|
e28e893120 | ||
|
|
a0c571b9c0 | ||
|
|
ff9026095a | ||
|
|
7583a586fd | ||
|
|
17a9e382fe | ||
|
|
87dc01b385 | ||
|
|
303d21d241 | ||
|
|
6a483946ef | ||
|
|
30f61b61f1 | ||
|
|
bcf5195427 | ||
|
|
3e3157c40b | ||
|
|
d25b62823c | ||
|
|
e02624a8b0 | ||
|
|
3762d20949 | ||
|
|
8e62188566 | ||
|
|
0f660fb937 | ||
|
|
19b33a0941 | ||
|
|
dbf15b2d0e | ||
|
|
a18561d92f | ||
|
|
21c4f8e3b8 | ||
|
|
69c142c63b | ||
|
|
0b725f32d9 | ||
|
|
e5c78f1efd | ||
|
|
19d4770523 | ||
|
|
5dd0f5de34 | ||
|
|
37f09601dd | ||
|
|
e42d99147e | ||
|
|
2e82d8d7b5 | ||
|
|
e15ded5405 | ||
|
|
81d57db90d | ||
|
|
60fdea78e3 | ||
|
|
0f51312081 | ||
|
|
d56017e3f5 | ||
|
|
19f8a496f8 | ||
|
|
e1f87678d0 | ||
|
|
b7340fecdb | ||
|
|
9279046d63 | ||
|
|
91857b9003 | ||
|
|
a23354bc0a | ||
|
|
b174511fad | ||
|
|
f5c6001865 | ||
|
|
ab96ad3ea8 | ||
|
|
472903f5ae | ||
|
|
b607bedcaa | ||
|
|
1fb9d8ff51 | ||
|
|
162cff9d97 | ||
|
|
7f5927050e | ||
|
|
ebd11ad3bd | ||
|
|
be3447307c | ||
|
|
2ae633a4a6 | ||
|
|
313d499b80 | ||
|
|
03ccfc5d2c |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -154,3 +154,5 @@ $RECYCLE.BIN/
|
|||||||
|
|
||||||
# Mac desktop service store files
|
# Mac desktop service store files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
packages
|
||||||
|
*.msi
|
||||||
|
|||||||
6
.nuget/NuGet.Config
Normal file
6
.nuget/NuGet.Config
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration>
|
||||||
|
<solution>
|
||||||
|
<add key="disableSourceControlIntegration" value="true" />
|
||||||
|
</solution>
|
||||||
|
</configuration>
|
||||||
BIN
.nuget/NuGet.exe
Normal file
BIN
.nuget/NuGet.exe
Normal file
Binary file not shown.
144
.nuget/NuGet.targets
Normal file
144
.nuget/NuGet.targets
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">$(MSBuildProjectDirectory)\..\</SolutionDir>
|
||||||
|
|
||||||
|
<!-- Enable the restore command to run before builds -->
|
||||||
|
<RestorePackages Condition=" '$(RestorePackages)' == '' ">false</RestorePackages>
|
||||||
|
|
||||||
|
<!-- Property that enables building a package from a project -->
|
||||||
|
<BuildPackage Condition=" '$(BuildPackage)' == '' ">false</BuildPackage>
|
||||||
|
|
||||||
|
<!-- Determines if package restore consent is required to restore packages -->
|
||||||
|
<RequireRestoreConsent Condition=" '$(RequireRestoreConsent)' != 'false' ">true</RequireRestoreConsent>
|
||||||
|
|
||||||
|
<!-- Download NuGet.exe if it does not already exist -->
|
||||||
|
<DownloadNuGetExe Condition=" '$(DownloadNuGetExe)' == '' ">false</DownloadNuGetExe>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup Condition=" '$(PackageSources)' == '' ">
|
||||||
|
<!-- Package sources used to restore packages. By default, registered sources under %APPDATA%\NuGet\NuGet.Config will be used -->
|
||||||
|
<!-- The official NuGet package source (https://www.nuget.org/api/v2/) will be excluded if package sources are specified and it does not appear in the list -->
|
||||||
|
<!--
|
||||||
|
<PackageSource Include="https://www.nuget.org/api/v2/" />
|
||||||
|
<PackageSource Include="https://my-nuget-source/nuget/" />
|
||||||
|
-->
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(OS)' == 'Windows_NT'">
|
||||||
|
<!-- Windows specific commands -->
|
||||||
|
<NuGetToolsPath>$([System.IO.Path]::Combine($(SolutionDir), ".nuget"))</NuGetToolsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(OS)' != 'Windows_NT'">
|
||||||
|
<!-- We need to launch nuget.exe with the mono command if we're not on windows -->
|
||||||
|
<NuGetToolsPath>$(SolutionDir).nuget</NuGetToolsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<PackagesProjectConfig Condition=" '$(OS)' == 'Windows_NT'">$(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config</PackagesProjectConfig>
|
||||||
|
<PackagesProjectConfig Condition=" '$(OS)' != 'Windows_NT'">$(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config</PackagesProjectConfig>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<PackagesConfig Condition="Exists('$(MSBuildProjectDirectory)\packages.config')">$(MSBuildProjectDirectory)\packages.config</PackagesConfig>
|
||||||
|
<PackagesConfig Condition="Exists('$(PackagesProjectConfig)')">$(PackagesProjectConfig)</PackagesConfig>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<!-- NuGet command -->
|
||||||
|
<NuGetExePath Condition=" '$(NuGetExePath)' == '' ">$(NuGetToolsPath)\NuGet.exe</NuGetExePath>
|
||||||
|
<PackageSources Condition=" $(PackageSources) == '' ">@(PackageSource)</PackageSources>
|
||||||
|
|
||||||
|
<NuGetCommand Condition=" '$(OS)' == 'Windows_NT'">"$(NuGetExePath)"</NuGetCommand>
|
||||||
|
<NuGetCommand Condition=" '$(OS)' != 'Windows_NT' ">mono --runtime=v4.0.30319 "$(NuGetExePath)"</NuGetCommand>
|
||||||
|
|
||||||
|
<PackageOutputDir Condition="$(PackageOutputDir) == ''">$(TargetDir.Trim('\\'))</PackageOutputDir>
|
||||||
|
|
||||||
|
<RequireConsentSwitch Condition=" $(RequireRestoreConsent) == 'true' ">-RequireConsent</RequireConsentSwitch>
|
||||||
|
<NonInteractiveSwitch Condition=" '$(VisualStudioVersion)' != '' AND '$(OS)' == 'Windows_NT' ">-NonInteractive</NonInteractiveSwitch>
|
||||||
|
|
||||||
|
<PaddedSolutionDir Condition=" '$(OS)' == 'Windows_NT'">"$(SolutionDir) "</PaddedSolutionDir>
|
||||||
|
<PaddedSolutionDir Condition=" '$(OS)' != 'Windows_NT' ">"$(SolutionDir)"</PaddedSolutionDir>
|
||||||
|
|
||||||
|
<!-- Commands -->
|
||||||
|
<RestoreCommand>$(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)</RestoreCommand>
|
||||||
|
<BuildCommand>$(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols</BuildCommand>
|
||||||
|
|
||||||
|
<!-- We need to ensure packages are restored prior to assembly resolve -->
|
||||||
|
<BuildDependsOn Condition="$(RestorePackages) == 'true'">
|
||||||
|
RestorePackages;
|
||||||
|
$(BuildDependsOn);
|
||||||
|
</BuildDependsOn>
|
||||||
|
|
||||||
|
<!-- Make the build depend on restore packages -->
|
||||||
|
<BuildDependsOn Condition="$(BuildPackage) == 'true'">
|
||||||
|
$(BuildDependsOn);
|
||||||
|
BuildPackage;
|
||||||
|
</BuildDependsOn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Target Name="CheckPrerequisites">
|
||||||
|
<!-- Raise an error if we're unable to locate nuget.exe -->
|
||||||
|
<Error Condition="'$(DownloadNuGetExe)' != 'true' AND !Exists('$(NuGetExePath)')" Text="Unable to locate '$(NuGetExePath)'" />
|
||||||
|
<!--
|
||||||
|
Take advantage of MsBuild's build dependency tracking to make sure that we only ever download nuget.exe once.
|
||||||
|
This effectively acts as a lock that makes sure that the download operation will only happen once and all
|
||||||
|
parallel builds will have to wait for it to complete.
|
||||||
|
-->
|
||||||
|
<MsBuild Targets="_DownloadNuGet" Projects="$(MSBuildThisFileFullPath)" Properties="Configuration=NOT_IMPORTANT;DownloadNuGetExe=$(DownloadNuGetExe)" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<Target Name="_DownloadNuGet">
|
||||||
|
<DownloadNuGet OutputFilename="$(NuGetExePath)" Condition=" '$(DownloadNuGetExe)' == 'true' AND !Exists('$(NuGetExePath)')" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<Target Name="RestorePackages" DependsOnTargets="CheckPrerequisites">
|
||||||
|
<Exec Command="$(RestoreCommand)"
|
||||||
|
Condition="'$(OS)' != 'Windows_NT' And Exists('$(PackagesConfig)')" />
|
||||||
|
|
||||||
|
<Exec Command="$(RestoreCommand)"
|
||||||
|
LogStandardErrorAsError="true"
|
||||||
|
Condition="'$(OS)' == 'Windows_NT' And Exists('$(PackagesConfig)')" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<Target Name="BuildPackage" DependsOnTargets="CheckPrerequisites">
|
||||||
|
<Exec Command="$(BuildCommand)"
|
||||||
|
Condition=" '$(OS)' != 'Windows_NT' " />
|
||||||
|
|
||||||
|
<Exec Command="$(BuildCommand)"
|
||||||
|
LogStandardErrorAsError="true"
|
||||||
|
Condition=" '$(OS)' == 'Windows_NT' " />
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<UsingTask TaskName="DownloadNuGet" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
|
||||||
|
<ParameterGroup>
|
||||||
|
<OutputFilename ParameterType="System.String" Required="true" />
|
||||||
|
</ParameterGroup>
|
||||||
|
<Task>
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Using Namespace="System" />
|
||||||
|
<Using Namespace="System.IO" />
|
||||||
|
<Using Namespace="System.Net" />
|
||||||
|
<Using Namespace="Microsoft.Build.Framework" />
|
||||||
|
<Using Namespace="Microsoft.Build.Utilities" />
|
||||||
|
<Code Type="Fragment" Language="cs">
|
||||||
|
<![CDATA[
|
||||||
|
try {
|
||||||
|
OutputFilename = Path.GetFullPath(OutputFilename);
|
||||||
|
|
||||||
|
Log.LogMessage("Downloading latest version of NuGet.exe...");
|
||||||
|
WebClient webClient = new WebClient();
|
||||||
|
webClient.DownloadFile("https://www.nuget.org/nuget.exe", OutputFilename);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
Log.LogErrorFromException(ex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
]]>
|
||||||
|
</Code>
|
||||||
|
</Task>
|
||||||
|
</UsingTask>
|
||||||
|
</Project>
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="NUnit.Runners" version="2.6.3" />
|
<package id="Newtonsoft.Json" version="6.0.4" targetFramework="net40" />
|
||||||
|
<package id="NUnit.Runners" version="2.6.4" />
|
||||||
|
<package id="RapidRegex.Core" version="1.0.0.4" targetFramework="net40" />
|
||||||
|
<package id="System.Linq.Dynamic" version="1.0.4" targetFramework="net40" />
|
||||||
</packages>
|
</packages>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
Copyright 2014 Vistaprint
|
Copyright 2014 Cimpress
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -10,4 +10,4 @@ Unless required by applicable law or agreed to in writing, software
|
|||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
|||||||
21
Performance1.psess
Normal file
21
Performance1.psess
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<VSPerformanceSession Version="1.00">
|
||||||
|
<Options>
|
||||||
|
<CollectionMethod>Sampling</CollectionMethod>
|
||||||
|
<AllocationMethod>None</AllocationMethod>
|
||||||
|
<AddReport>true</AddReport>
|
||||||
|
<UniqueReport>Timestamp</UniqueReport>
|
||||||
|
<SamplingMethod>Cycles</SamplingMethod>
|
||||||
|
<CycleCount>10000000</CycleCount>
|
||||||
|
<PageFaultCount>10</PageFaultCount>
|
||||||
|
<SysCallCount>10</SysCallCount>
|
||||||
|
<SamplingCounter PlatformID="00000000" CounterID="0000000000000000" ReloadValue="000000000000000a" />
|
||||||
|
<RelocateBinaries>false</RelocateBinaries>
|
||||||
|
<HardwareCounters EnableHWCounters="false" />
|
||||||
|
</Options>
|
||||||
|
<PreinstrumentEvent>
|
||||||
|
<InstrEventExclude>false</InstrEventExclude>
|
||||||
|
</PreinstrumentEvent>
|
||||||
|
<PostinstrumentEvent>
|
||||||
|
<InstrEventExclude>false</InstrEventExclude>
|
||||||
|
</PostinstrumentEvent>
|
||||||
|
</VSPerformanceSession>
|
||||||
152
README.md
152
README.md
@@ -7,35 +7,108 @@ TimberWinR is a native .NET implementation utilizing Microsoft's [LogParser](htt
|
|||||||
no JVM/JRuby is required, and LogParser does all the heavy lifting. TimberWinR collects
|
no JVM/JRuby is required, and LogParser does all the heavy lifting. TimberWinR collects
|
||||||
the data from LogParser and ships it to Logstash via Redis (or can ship direcly to Elasticsearch)
|
the data from LogParser and ships it to Logstash via Redis (or can ship direcly to Elasticsearch)
|
||||||
|
|
||||||
|
## Release Notes
|
||||||
|
|
||||||
|
[View Version History](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/ReleaseNotes.md)
|
||||||
|
|
||||||
## Basics
|
## Basics
|
||||||
TimberWinR uses a configuration file to control how the logs are collected, filtered and shipped off.
|
TimberWinR uses a configuration file to control how the logs are collected, filtered and shipped off.
|
||||||
These are broken down into:
|
These are broken down into:
|
||||||
1. Inputs (Collect data from different sources)
|
1. Inputs (Collect data from different sources)
|
||||||
2. Filters (Are applied to all Inputs)
|
2. Filters (Are applied to all Inputs)
|
||||||
3. Outputs (Currently ships only to Redis)
|
3. Outputs (e.g. Redis, Elasticsearch, Stdout, StatsD)
|
||||||
|
|
||||||
## Input Formats
|
### Support ###
|
||||||
|
Please use the TimberWinR Google Group for discussion and support:
|
||||||
|
|
||||||
|
https://groups.google.com/forum/#!forum/timberwinr
|
||||||
|
|
||||||
|
Latest Build:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
## Inputs
|
||||||
The current supported Input format sources are:
|
The current supported Input format sources are:
|
||||||
1. [Logs](https://github.com/efontana/TimberWinR/blob/master/TimberWinR/mdocs/Logs.md) (Files, a.k.a Tailing a file)
|
1. [Logs](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/Logs.md) (Files, a.k.a Tailing a file)
|
||||||
2. [Tcp](https://github.com/efontana/TimberWinR/blob/master/TimberWinR/mdocs/TcpInput.md) (listens on a port for JSON messages)
|
2. [Tcp](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/TcpInput.md) (listens on TCP port for JSON messages)
|
||||||
3. [IISW3C](https://github.com/efontana/TimberWinR/blob/master/TimberWinR/mdocs/IISW3CInput.md)(Internet Information Services W3C Format)
|
3. [IISW3C](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/IISW3CInput.md)(Internet Information Services W3C Format)
|
||||||
4. [WindowsEvents](https://github.com/efontana/TimberWinR/blob/master/TimberWinR/mdocs/WindowsEvents.md) (Windows Event Viewer)
|
4. [WindowsEvents](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/WindowsEvents.md) (Windows Event Viewer)
|
||||||
5. [Stdin](https://github.com/efontana/TimberWinR/blob/master/TimberWinR/mdocs/StdinInput.md) (Standard Input for Debugging)
|
5. [Stdin](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/StdinInput.md) (Standard Input for Debugging)
|
||||||
|
6. [W3C](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/W3CInput.md)(Internet Information Services W3C Advanced/Custom Format)
|
||||||
|
7. [Udp](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/UdpInput.md) (listens for UDP on port for JSON messages)
|
||||||
|
8. [TailFiles](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/TailFiles.md) (Tails log files efficiently)
|
||||||
|
8. [Generator](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/Generator.md) (Generate logs for testing *New*)
|
||||||
|
|
||||||
|
## Codecs
|
||||||
|
The current list of supported codecs are:
|
||||||
|
1. [Multiline](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/Codec.md)
|
||||||
|
|
||||||
## Filters
|
## Filters
|
||||||
The current list of supported filters are:
|
The current list of supported filters are:
|
||||||
1. [Grok](https://github.com/efontana/TimberWinR/blob/master/TimberWinR/mdocs/GrokFilter.md)
|
1. [Grok](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/GrokFilter.md)
|
||||||
2. [Mutate](https://github.com/efontana/TimberWinR/blob/master/TimberWinR/mdocs/MutateFilter.md)
|
2. [Mutate](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/MutateFilter.md)
|
||||||
3. [Date](https://github.com/efontana/TimberWinR/blob/master/TimberWinR/mdocs/DateFilter.md)
|
3. [Date](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/DateFilter.md)
|
||||||
|
4. [Json](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/JsonFilter.md)
|
||||||
|
5. [GeoIP](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/GeoIPFilter.md)
|
||||||
|
|
||||||
|
Note that there are now two syntaxes for filters, array and single, if you want more than one filter of the same
|
||||||
|
type then you must use the array tag instead of the singular tag. i.e:
|
||||||
|
|
||||||
|
A single Json filter using the single tag (this is only provided as a convienience, the array syntax is preferred)
|
||||||
|
```json
|
||||||
|
"Filters": [
|
||||||
|
{
|
||||||
|
"json": {
|
||||||
|
"type": "Win32-FileLog",
|
||||||
|
"condition": "\"[logSource]\" == \"dev\"",
|
||||||
|
"source": "Text",
|
||||||
|
"add_field": [
|
||||||
|
"_index",
|
||||||
|
"dev-%{yyyy.MM.dd}"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
Multiple Json filters must use the jsonFilters and array syntax, also mutateFilters, grokFilters, dateFilters, geoipFilters.
|
||||||
|
```json
|
||||||
|
"Filters": [
|
||||||
|
{
|
||||||
|
"jsonFilters": [
|
||||||
|
{
|
||||||
|
"type": "Win32-FileLog",
|
||||||
|
"condition": "\"[logSource]\" == \"dev\"",
|
||||||
|
"source": "Text",
|
||||||
|
"add_field": [
|
||||||
|
"_index",
|
||||||
|
"dev-%{yyyy.MM.dd}"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Win32-FileLog",
|
||||||
|
"condition": "\"[logSource]\" == \"sta\"",
|
||||||
|
"source": "Text",
|
||||||
|
"add_field": [
|
||||||
|
"_index",
|
||||||
|
"sta-%{yyyy.MM.dd}"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
## JSON
|
## JSON
|
||||||
Since TimberWinR only ships to Redis, the format generated by TimberWinR is JSON. All fields referenced by TimberWinR can be
|
Since TimberWinR only ships to Redis and Elasticsearch, the format generated by TimberWinR is JSON. All fields referenced by TimberWinR can be
|
||||||
represented as a JSON Property or Array.
|
represented as a JSON Property or Array.
|
||||||
|
|
||||||
## Supported Output Formats
|
## Outputs
|
||||||
1. [Redis](https://github.com/efontana/TimberWinR/blob/master/TimberWinR/mdocs/RedisOutput.md)
|
1. [Redis](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/RedisOutput.md)
|
||||||
2. [Elasticsearch](https://github.com/efontana/TimberWinR/blob/master/TimberWinR/mdocs/ElasticsearchOutput.md)
|
2. [Elasticsearch](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/ElasticsearchOutput.md)
|
||||||
3. [Stdout](https://github.com/efontana/TimberWinR/blob/master/TimberWinR/mdocs/StdoutOutput.md)
|
3. [Stdout](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/StdoutOutput.md)
|
||||||
|
4. [File](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/FileOutput.md)
|
||||||
|
5. [StatsD](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/StatsD.md)
|
||||||
|
|
||||||
## Sample Configuration
|
## Sample Configuration
|
||||||
TimberWinR reads a JSON configuration file, an example file is shown here:
|
TimberWinR reads a JSON configuration file, an example file is shown here:
|
||||||
@@ -54,7 +127,7 @@ TimberWinR reads a JSON configuration file, an example file is shown here:
|
|||||||
"Filters": [
|
"Filters": [
|
||||||
{
|
{
|
||||||
"grok": {
|
"grok": {
|
||||||
"condition": "[type] == \"Win32-Eventlog\"",
|
"condition": "\"[type]\" == \"Win32-Eventlog\"",
|
||||||
"match": [
|
"match": [
|
||||||
"Message",
|
"Message",
|
||||||
""
|
""
|
||||||
@@ -83,12 +156,14 @@ This configuration:
|
|||||||
2. Filters: Removes the ComputerName field
|
2. Filters: Removes the ComputerName field
|
||||||
3. Sends the event to Redis services (server1.host.com, server2.host.com) in a shuffling manner (balanced).
|
3. Sends the event to Redis services (server1.host.com, server2.host.com) in a shuffling manner (balanced).
|
||||||
|
|
||||||
## Installation
|
## Installation from Source
|
||||||
You must first install LogParser, then install TimberWinR. Install LogParser from here:
|
If installing from source, you must first install LogParser, then install TimberWinR. Install LogParser from here:
|
||||||
|
|
||||||
[Install LogParser](http://www.microsoft.com/en-us/download/details.aspx?id=24659) from Microsoft.
|
[Install LogParser](http://www.microsoft.com/en-us/download/details.aspx?id=24659) from Microsoft.
|
||||||
|
|
||||||
After installing, follow the remaining directions here.
|
After installing, follow the remaining directions here. If you install from [Chocolatey](https://chocolatey.org/packages/TimberWinR) then LogParser will automatically
|
||||||
|
be installed first.
|
||||||
|
|
||||||
## Running Interactively
|
## Running Interactively
|
||||||
You can run TimberWinR interactively when you are developing your JSON config file, to do so use the
|
You can run TimberWinR interactively when you are developing your JSON config file, to do so use the
|
||||||
following options:
|
following options:
|
||||||
@@ -96,7 +171,13 @@ following options:
|
|||||||
TimberWinR.ServiceHost.exe -configFile:myconfig.json -logLevel:Debug
|
TimberWinR.ServiceHost.exe -configFile:myconfig.json -logLevel:Debug
|
||||||
```
|
```
|
||||||
|
|
||||||
## Installation as a Windows Service
|
## Automatic Installation via Chocolatey (embedded)
|
||||||
|
|
||||||
|
[TimbeWinR Chocolatey](https://chocolatey.org/packages/TimberWinR)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Manual Installation as a Windows Service
|
||||||
TimberWinR uses [TopShelf](http://topshelf-project.com/) to install as a service, so all the documentation
|
TimberWinR uses [TopShelf](http://topshelf-project.com/) to install as a service, so all the documentation
|
||||||
for installing and configuring the service is show here [TopShelf Doc](http://docs.topshelf-project.com/en/latest/)
|
for installing and configuring the service is show here [TopShelf Doc](http://docs.topshelf-project.com/en/latest/)
|
||||||
|
|
||||||
@@ -130,8 +211,8 @@ Options:
|
|||||||
-configFile: Specifies the path to the JSON config file, or directory which contains .json file(s).
|
-configFile: Specifies the path to the JSON config file, or directory which contains .json file(s).
|
||||||
Default is -configFile:default.json
|
Default is -configFile:default.json
|
||||||
-diagnosticPort: Specifies the diagnostic port which can be used to get a health check of the service.
|
-diagnosticPort: Specifies the diagnostic port which can be used to get a health check of the service.
|
||||||
Default Port is 5142, A value of 0 will disable it. Open a browser
|
Default Port is 5141, A value of 0 will disable it. Open a browser
|
||||||
http://localhost:5142
|
http://localhost:5141
|
||||||
```
|
```
|
||||||
#### -configFile
|
#### -configFile
|
||||||
This may be a single .json file or a directory containing .json file(s). If it is a directory, all
|
This may be a single .json file or a directory containing .json file(s). If it is a directory, all
|
||||||
@@ -142,8 +223,8 @@ order on disk.
|
|||||||
If you really just want to try it out, grab the binary distribution, extract the .zip file
|
If you really just want to try it out, grab the binary distribution, extract the .zip file
|
||||||
into a directory, e.g. C:\TimberWinR
|
into a directory, e.g. C:\TimberWinR
|
||||||
|
|
||||||
Grab the [JSON example file](https://github.com/efontana/TimberWinR/blob/master/TimberWinR.ServiceHost/default.json) and place it into C:\TimberWinR\default.json.
|
Grab the [JSON example file](https://raw.githubusercontent.com/Cimpress-MCP/TimberWinR/master/TimberWinR.ServiceHost/default.json) and place it into C:\TimberWinR\default.json.
|
||||||
Edit the default.json file and change the Redis instance to match yours, replace 'tstlexiceapp006.vistaprint.svc' with the IP or DNS name
|
Edit the default.json file and change the Redis instance to match yours, replace 'tstlexiceapp006.mycompany.svc' with the IP or DNS name
|
||||||
of the machine running redis. Fire up the collector, enable the verbose debugging to see some Windows Events.
|
of the machine running redis. Fire up the collector, enable the verbose debugging to see some Windows Events.
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -160,6 +241,25 @@ TimberWinR.ServiceHost.exe start
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Builds ###
|
### Builds ###
|
||||||
Builds are here.
|
TimberWinR is distributed as an installable package via Chocolatey, and it is dependent on
|
||||||
Soon
|
|
||||||
|
[TimbeWinR Chocolatey](https://chocolatey.org/packages/TimberWinR)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#### Notes ####
|
||||||
|
After you install it via Chocolatey, you will have a folder:
|
||||||
|
```
|
||||||
|
C:\Program Files (x86)\TimberWinR
|
||||||
|
```
|
||||||
|
TimberWinR will be configured to read the file default.json located in the folder above. You can edit this file
|
||||||
|
to customize your installation.
|
||||||
|
|
||||||
|
Use these commands to Stop/Start the service.
|
||||||
|
|
||||||
|
```
|
||||||
|
sc stop TimberWinR ; stop the service
|
||||||
|
sc start TimberWinR; start the service
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
105
TimberWinR.ExtractID/Program.cs
Normal file
105
TimberWinR.ExtractID/Program.cs
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.Win32.SafeHandles;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace TimberWinR.ExtractID
|
||||||
|
{
|
||||||
|
class MsiHandle : SafeHandleMinusOneIsInvalid
|
||||||
|
{
|
||||||
|
public MsiHandle()
|
||||||
|
: base(true)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
protected override bool ReleaseHandle()
|
||||||
|
{
|
||||||
|
return NativeMethods.MsiCloseHandle(handle) == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NativeMethods
|
||||||
|
{
|
||||||
|
const string MsiDll = "Msi.dll";
|
||||||
|
|
||||||
|
[DllImport(MsiDll, CharSet = CharSet.Unicode, ExactSpelling = true)]
|
||||||
|
public extern static uint MsiOpenPackageW(string szPackagePath, out MsiHandle product);
|
||||||
|
|
||||||
|
[DllImport(MsiDll, ExactSpelling = true)]
|
||||||
|
public extern static uint MsiCloseHandle(IntPtr hAny);
|
||||||
|
|
||||||
|
[DllImport(MsiDll, CharSet = CharSet.Unicode, ExactSpelling = true)]
|
||||||
|
static extern uint MsiGetProductPropertyW(MsiHandle hProduct, string szProperty, StringBuilder value, ref int length);
|
||||||
|
|
||||||
|
|
||||||
|
[DllImport(MsiDll, ExactSpelling = true)]
|
||||||
|
public static extern int MsiSetInternalUI(int value, IntPtr hwnd);
|
||||||
|
|
||||||
|
public static uint MsiGetProductProperty(MsiHandle hProduct, string szProperty, out string value)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder(1024);
|
||||||
|
int length = sb.Capacity;
|
||||||
|
uint err;
|
||||||
|
value = null;
|
||||||
|
if (0 == (err = MsiGetProductPropertyW(hProduct, szProperty, sb, ref length)))
|
||||||
|
{
|
||||||
|
sb.Length = length;
|
||||||
|
value = sb.ToString();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
[STAThread]
|
||||||
|
static int Main(string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length < 2)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine("Expecting MSI and Tempolate file arguments");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
string msiDirectory = args[0];
|
||||||
|
string updateFile = args[1];
|
||||||
|
string newFile = args[2];
|
||||||
|
|
||||||
|
string msiFile = Directory.GetFiles(msiDirectory, "TimberWinR*.msi").FirstOrDefault();
|
||||||
|
|
||||||
|
NativeMethods.MsiSetInternalUI(2, IntPtr.Zero); // Hide all UI. Without this you get a MSI dialog
|
||||||
|
|
||||||
|
MsiHandle msi;
|
||||||
|
uint err;
|
||||||
|
if (0 != (err = NativeMethods.MsiOpenPackageW(msiFile, out msi)))
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine("Can't open MSI, error {0}", err);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strings available in all MSIs
|
||||||
|
string productCode;
|
||||||
|
using (msi)
|
||||||
|
{
|
||||||
|
if (0 != NativeMethods.MsiGetProductProperty(msi, "ProductCode", out productCode))
|
||||||
|
throw new InvalidOperationException("Can't obtain product code");
|
||||||
|
|
||||||
|
string contents = File.ReadAllText(args[1]);
|
||||||
|
|
||||||
|
contents = contents.Replace("${PROJECTGUID}", productCode);
|
||||||
|
|
||||||
|
File.WriteAllText(args[2], contents);
|
||||||
|
|
||||||
|
Console.WriteLine("Updated {0} ProductID: {1}", args[2], productCode);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
36
TimberWinR.ExtractID/Properties/AssemblyInfo.cs
Normal file
36
TimberWinR.ExtractID/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("TimberWinR.ExtractID")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("TimberWinR.ExtractID")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © 2014")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
|
[assembly: Guid("cfb670ee-743d-49c7-b2bf-456bac3a88ef")]
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
|
// by using the '*' as shown below:
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||||
55
TimberWinR.ExtractID/TimberWinR.ExtractID.csproj
Normal file
55
TimberWinR.ExtractID/TimberWinR.ExtractID.csproj
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{99096939-E9DD-4499-883D-4726745A5843}</ProjectGuid>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>TimberWinR.ExtractID</RootNamespace>
|
||||||
|
<AssemblyName>TimberWinR.ExtractID</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Program.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
<Target Name="BeforeBuild">
|
||||||
|
</Target>
|
||||||
|
<Target Name="AfterBuild">
|
||||||
|
</Target>
|
||||||
|
-->
|
||||||
|
</Project>
|
||||||
@@ -7,7 +7,9 @@ using System.Text;
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Xml;
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
|
|
||||||
using TimberWinR.Outputs;
|
using TimberWinR.Outputs;
|
||||||
using TimberWinR.ServiceHost;
|
using TimberWinR.ServiceHost;
|
||||||
using TimberWinR.Inputs;
|
using TimberWinR.Inputs;
|
||||||
@@ -23,11 +25,11 @@ namespace TimberWinR.ServiceHost
|
|||||||
{
|
{
|
||||||
const string KeyPath = @"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\TimberWinR";
|
const string KeyPath = @"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\TimberWinR";
|
||||||
const string KeyName = "ImagePath";
|
const string KeyName = "ImagePath";
|
||||||
|
|
||||||
private static void Main(string[] args)
|
private static void Main(string[] args)
|
||||||
{
|
{
|
||||||
Arguments arguments = new Arguments();
|
Arguments arguments = new Arguments();
|
||||||
|
|
||||||
HostFactory.Run(hostConfigurator =>
|
HostFactory.Run(hostConfigurator =>
|
||||||
{
|
{
|
||||||
string cmdLine = Environment.CommandLine;
|
string cmdLine = Environment.CommandLine;
|
||||||
@@ -38,11 +40,12 @@ namespace TimberWinR.ServiceHost
|
|||||||
serviceConfigurator.WhenStarted(myService => myService.Start());
|
serviceConfigurator.WhenStarted(myService => myService.Start());
|
||||||
serviceConfigurator.WhenStopped(myService => myService.Stop());
|
serviceConfigurator.WhenStopped(myService => myService.Stop());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
hostConfigurator.AddCommandLineDefinition("liveMonitor", c => arguments.LiveMonitor = bool.Parse(c.ToString()));
|
||||||
hostConfigurator.AddCommandLineDefinition("configFile", c => arguments.ConfigFile = c);
|
hostConfigurator.AddCommandLineDefinition("configFile", c => arguments.ConfigFile = c);
|
||||||
hostConfigurator.AddCommandLineDefinition("logLevel", c => arguments.LogLevel = c);
|
hostConfigurator.AddCommandLineDefinition("logLevel", c => arguments.LogLevel = c);
|
||||||
hostConfigurator.AddCommandLineDefinition("logDir", c => arguments.LogfileDir = c);
|
hostConfigurator.AddCommandLineDefinition("logDir", c => arguments.LogfileDir = c);
|
||||||
hostConfigurator.AddCommandLineDefinition("diagnosticPort", c => arguments.DiagnosticPort = int.Parse(c));
|
hostConfigurator.AddCommandLineDefinition("diagnosticPort", c => arguments.DiagnosticPort = int.Parse(c));
|
||||||
|
|
||||||
hostConfigurator.ApplyCommandLine();
|
hostConfigurator.ApplyCommandLine();
|
||||||
hostConfigurator.RunAsLocalSystem();
|
hostConfigurator.RunAsLocalSystem();
|
||||||
@@ -53,13 +56,14 @@ namespace TimberWinR.ServiceHost
|
|||||||
hostConfigurator.SetServiceName("TimberWinR");
|
hostConfigurator.SetServiceName("TimberWinR");
|
||||||
|
|
||||||
hostConfigurator.AfterInstall(() =>
|
hostConfigurator.AfterInstall(() =>
|
||||||
{
|
{
|
||||||
var currentValue = Registry.GetValue(KeyPath, KeyName, "").ToString();
|
var currentValue = Registry.GetValue(KeyPath, KeyName, "").ToString();
|
||||||
if (!string.IsNullOrEmpty(currentValue))
|
if (!string.IsNullOrEmpty(currentValue))
|
||||||
{
|
{
|
||||||
AddServiceParameter("-configFile", arguments.ConfigFile);
|
AddServiceParameter("-configFile", arguments.ConfigFile);
|
||||||
AddServiceParameter("-logLevel", arguments.LogLevel);
|
AddServiceParameter("-logLevel", arguments.LogLevel);
|
||||||
AddServiceParameter("-logDir", arguments.LogfileDir);
|
AddServiceParameter("-logDir", arguments.LogfileDir);
|
||||||
|
AddServiceParameter("-liveMonitor", arguments.LiveMonitor);
|
||||||
if (arguments.DiagnosticPort > 0)
|
if (arguments.DiagnosticPort > 0)
|
||||||
AddServiceParameter("-diagnosticPort", arguments.DiagnosticPort);
|
AddServiceParameter("-diagnosticPort", arguments.DiagnosticPort);
|
||||||
}
|
}
|
||||||
@@ -69,19 +73,17 @@ namespace TimberWinR.ServiceHost
|
|||||||
|
|
||||||
private static void AddServiceParameter(string paramName, string value)
|
private static void AddServiceParameter(string paramName, string value)
|
||||||
{
|
{
|
||||||
|
|
||||||
string currentValue = Registry.GetValue(KeyPath, KeyName, "").ToString();
|
string currentValue = Registry.GetValue(KeyPath, KeyName, "").ToString();
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(paramName) && !currentValue.Contains(string.Format("{0} ", paramName)))
|
if (!string.IsNullOrEmpty(paramName) && !currentValue.Contains(string.Format("{0} ", paramName)))
|
||||||
{
|
{
|
||||||
currentValue += string.Format(" {0} \"{1}\"", paramName, value.Replace("\\\\", "\\"));
|
currentValue += string.Format(" {0} \"{1}\"", paramName, value.Replace("\\\\", "\\"));
|
||||||
Registry.SetValue(KeyPath, KeyName, currentValue);
|
Registry.SetValue(KeyPath, KeyName, currentValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddServiceParameter(string paramName, int value)
|
private static void AddServiceParameter(string paramName, int value)
|
||||||
{
|
{
|
||||||
|
|
||||||
string currentValue = Registry.GetValue(KeyPath, KeyName, "").ToString();
|
string currentValue = Registry.GetValue(KeyPath, KeyName, "").ToString();
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(paramName) && !currentValue.Contains(string.Format("{0}:", paramName)))
|
if (!string.IsNullOrEmpty(paramName) && !currentValue.Contains(string.Format("{0}:", paramName)))
|
||||||
@@ -91,6 +93,16 @@ namespace TimberWinR.ServiceHost
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void AddServiceParameter(string paramName, bool value)
|
||||||
|
{
|
||||||
|
string currentValue = Registry.GetValue(KeyPath, KeyName, "").ToString();
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(paramName) && !currentValue.Contains(string.Format("{0}:", paramName)))
|
||||||
|
{
|
||||||
|
currentValue += string.Format(" {0} \"{1}\"", paramName, value.ToString());
|
||||||
|
Registry.SetValue(KeyPath, KeyName, currentValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class Arguments
|
internal class Arguments
|
||||||
@@ -99,9 +111,10 @@ namespace TimberWinR.ServiceHost
|
|||||||
public string LogLevel { get; set; }
|
public string LogLevel { get; set; }
|
||||||
public string LogfileDir { get; set; }
|
public string LogfileDir { get; set; }
|
||||||
public int DiagnosticPort { get; set; }
|
public int DiagnosticPort { get; set; }
|
||||||
|
public bool LiveMonitor { get; set; }
|
||||||
public Arguments()
|
public Arguments()
|
||||||
{
|
{
|
||||||
|
LiveMonitor = false;
|
||||||
DiagnosticPort = 5141;
|
DiagnosticPort = 5141;
|
||||||
ConfigFile = "default.json";
|
ConfigFile = "default.json";
|
||||||
LogLevel = "Info";
|
LogLevel = "Info";
|
||||||
@@ -118,13 +131,15 @@ namespace TimberWinR.ServiceHost
|
|||||||
private readonly Arguments _args;
|
private readonly Arguments _args;
|
||||||
private TimberWinR.Diagnostics.Diagnostics _diags;
|
private TimberWinR.Diagnostics.Diagnostics _diags;
|
||||||
private TimberWinR.Manager _manager;
|
private TimberWinR.Manager _manager;
|
||||||
|
public bool StartingUp { get; set; }
|
||||||
|
public bool Started { get; set; }
|
||||||
|
|
||||||
public TimberWinRService(Arguments args)
|
public TimberWinRService(Arguments args)
|
||||||
{
|
{
|
||||||
_args = args;
|
_args = args;
|
||||||
_cancellationTokenSource = new CancellationTokenSource();
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
_cancellationToken = _cancellationTokenSource.Token;
|
_cancellationToken = _cancellationTokenSource.Token;
|
||||||
_serviceTask = new Task(RunService, _cancellationToken);
|
_serviceTask = new Task(RunService, _cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start()
|
public void Start()
|
||||||
@@ -134,22 +149,41 @@ namespace TimberWinR.ServiceHost
|
|||||||
|
|
||||||
public void Stop()
|
public void Stop()
|
||||||
{
|
{
|
||||||
|
WaitForStartupToComplete();
|
||||||
|
|
||||||
_cancellationTokenSource.Cancel();
|
_cancellationTokenSource.Cancel();
|
||||||
if (_diags != null)
|
if (_diags != null)
|
||||||
_diags.Shutdown();
|
_diags.Shutdown();
|
||||||
|
|
||||||
if (_manager != null)
|
if (_manager != null)
|
||||||
_manager.Shutdown();
|
_manager.Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If you bounce the service too quickly, the shutdown can occur
|
||||||
|
// before the service has started, which results in a hang, this blocks until
|
||||||
|
// all thread have properly started (waiting up to 10 seconds max)
|
||||||
|
private void WaitForStartupToComplete()
|
||||||
|
{
|
||||||
|
int tries = 100; // 10 seconds max
|
||||||
|
if (StartingUp)
|
||||||
|
{
|
||||||
|
while (!Started && tries-- >= 0)
|
||||||
|
{
|
||||||
|
Thread.Sleep(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Main body of the Service Worker Thread
|
/// The Main body of the Service Worker Thread
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void RunService()
|
private void RunService()
|
||||||
{
|
{
|
||||||
_manager = new TimberWinR.Manager(_args.ConfigFile, _args.LogLevel, _args.LogfileDir, _cancellationToken);
|
StartingUp = true;
|
||||||
|
_manager = new TimberWinR.Manager(_args.ConfigFile, _args.LogLevel, _args.LogfileDir, _args.LiveMonitor, _cancellationToken);
|
||||||
if (_args.DiagnosticPort > 0)
|
if (_args.DiagnosticPort > 0)
|
||||||
_diags = new Diagnostics.Diagnostics(_manager, _cancellationToken, _args.DiagnosticPort);
|
_diags = new Diagnostics.Diagnostics(_manager, _cancellationToken, _args.DiagnosticPort);
|
||||||
|
Started = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ using System.Runtime.InteropServices;
|
|||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCompany("")]
|
[assembly: AssemblyCompany("")]
|
||||||
[assembly: AssemblyProduct("TimberWinR.ServiceHost")]
|
[assembly: AssemblyProduct("TimberWinR.ServiceHost")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2014")]
|
[assembly: AssemblyCopyright("Copyright © 2014-2015")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
|||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion("1.1.0.0")]
|
[assembly: AssemblyVersion("1.3.27.0")]
|
||||||
[assembly: AssemblyFileVersion("1.1.0.0")]
|
[assembly: AssemblyFileVersion("1.3.27.0")]
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<TargetFrameworkProfile />
|
<TargetFrameworkProfile />
|
||||||
|
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
||||||
|
<RestorePackages>true</RestorePackages>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
@@ -32,10 +34,20 @@
|
|||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<ApplicationIcon>timberwinr.ico</ApplicationIcon>
|
||||||
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="RapidRegex.Core, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="RapidRegex.Core, Version=1.0.0.4, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>..\packages\RapidRegex.Core.1.0.0.2\lib\net40\RapidRegex.Core.dll</HintPath>
|
<HintPath>..\packages\RapidRegex.Core.1.0.0.4\lib\net40\RapidRegex.Core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="StatsdClient, Version=1.0.0.19, Culture=neutral, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\packages\StatsdClient.1.0.0.19\lib\net35\StatsdClient.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="StatsdClient.Configuration">
|
||||||
|
<HintPath>..\packages\StatsdClient.1.0.0.19\lib\net35\StatsdClient.Configuration.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Configuration.Install" />
|
<Reference Include="System.Configuration.Install" />
|
||||||
@@ -46,9 +58,8 @@
|
|||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
<Reference Include="Topshelf, Version=3.1.122.0, Culture=neutral, PublicKeyToken=b800c4cfcdeea87b, processorArchitecture=MSIL">
|
<Reference Include="Topshelf">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<HintPath>..\packages\Topshelf.3.1.4\lib\net40-full\Topshelf.dll</HintPath>
|
||||||
<HintPath>..\packages\Topshelf.3.1.3\lib\net40-full\Topshelf.dll</HintPath>
|
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -62,6 +73,7 @@
|
|||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Content Include="timberwinr.ico" />
|
||||||
<None Include="App.config" />
|
<None Include="App.config" />
|
||||||
<Content Include="config.json">
|
<Content Include="config.json">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
@@ -72,7 +84,9 @@
|
|||||||
<None Include="loopback.json">
|
<None Include="loopback.json">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\TimberWinR\TimberWinR.csproj">
|
<ProjectReference Include="..\TimberWinR\TimberWinR.csproj">
|
||||||
@@ -81,6 +95,13 @@
|
|||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
|
||||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
|
||||||
|
</Target>
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
Other similar extension points exist, see Microsoft.Common.targets.
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
<Target Name="BeforeBuild">
|
<Target Name="BeforeBuild">
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
"Filters": [
|
"Filters": [
|
||||||
{
|
{
|
||||||
"grok": {
|
"grok": {
|
||||||
"condition": "[type] == \"Win32-Eventlog\"",
|
"condition": "\"[type]\" == \"Win32-Eventlog\"",
|
||||||
"match": [
|
"match": [
|
||||||
"Message",
|
"Message",
|
||||||
""
|
""
|
||||||
@@ -63,7 +63,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"date": {
|
"date": {
|
||||||
"condition": "[type] == \"Win32-FileLog\"",
|
"condition": "\"[type]\" == \"Win32-FileLog\"",
|
||||||
"match": [
|
"match": [
|
||||||
"timestamp",
|
"timestamp",
|
||||||
"MMM d HH:mm:sss",
|
"MMM d HH:mm:sss",
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
"interval": 5000,
|
"interval": 5000,
|
||||||
"batch_count": 500,
|
"batch_count": 500,
|
||||||
"host": [
|
"host": [
|
||||||
"tstlexiceapp006.vistaprint.svc"
|
"tstlexiceapp006.mycompany.svc"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -99,7 +99,7 @@
|
|||||||
"threads": 1,
|
"threads": 1,
|
||||||
"interval": 5000,
|
"interval": 5000,
|
||||||
"host": [
|
"host": [
|
||||||
"tstlexiceapp003.vistaprint.svc"
|
"tstlexiceapp003.mycompany.svc"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,30 +1,42 @@
|
|||||||
{
|
{
|
||||||
"TimberWinR": {
|
"TimberWinR": {
|
||||||
"Inputs": {
|
"Inputs": {
|
||||||
"WindowsEvents": [
|
"WindowsEvents": [
|
||||||
{
|
{
|
||||||
"source": "System,Application",
|
"source": "Application,System",
|
||||||
"binaryFormat": "PRINT",
|
"binaryFormat": "PRINT",
|
||||||
"resolveSIDS": true
|
"resolveSIDS": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Tcp": [
|
"Tcp": [
|
||||||
{
|
{
|
||||||
"_comment": "Output from NLog",
|
"_comment": "Output from NLog",
|
||||||
"port": 5140
|
"port": 5140
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"Filters": [
|
||||||
|
{
|
||||||
|
"grok": {
|
||||||
|
"condition": "\"[EventTypeName]\" == \"Information Event\"",
|
||||||
|
"match": [
|
||||||
|
"Text",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"drop": "true"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
"Outputs": {
|
"Outputs": {
|
||||||
"Redis": [
|
"Redis": [
|
||||||
{
|
{
|
||||||
"_comment": "Change the host to your Redis instance",
|
"_comment": "Change the host to your Redis instance",
|
||||||
"port": 6379,
|
"port": 6379,
|
||||||
"host": [
|
"host": [
|
||||||
"tstlexiceapp006.vistaprint.svc"
|
"logaggregator.mycompany.svc"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="RapidRegex.Core" version="1.0.0.2" targetFramework="net40" />
|
<package id="RapidRegex.Core" version="1.0.0.4" targetFramework="net40" />
|
||||||
<package id="Topshelf" version="3.1.3" targetFramework="net40" />
|
<package id="StatsdClient" version="1.0.0.19" targetFramework="net40" />
|
||||||
|
<package id="Topshelf" version="3.1.4" targetFramework="net40" />
|
||||||
</packages>
|
</packages>
|
||||||
BIN
TimberWinR.ServiceHost/timberwinr.ico
Normal file
BIN
TimberWinR.ServiceHost/timberwinr.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 83 KiB |
88
TimberWinR.TestGenerator/CommandLineOptions.cs
Normal file
88
TimberWinR.TestGenerator/CommandLineOptions.cs
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using CommandLine;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using CommandLine.Text;
|
||||||
|
|
||||||
|
namespace TimberWinR.TestGenerator
|
||||||
|
{
|
||||||
|
class CommandLineOptions
|
||||||
|
{
|
||||||
|
// [Option('r', "read", Required = true, HelpText = "Input file to be processed.")]
|
||||||
|
// public string InputFile { get; set; }
|
||||||
|
[Option("timberWinRConfig", DefaultValue = "default.json", HelpText = "Config file/directory to use")]
|
||||||
|
public string TimberWinRConfigFile { get; set; }
|
||||||
|
|
||||||
|
[Option("start", HelpText = "Start an instance of TimberWinR")]
|
||||||
|
public bool StartTimberWinR { get; set; }
|
||||||
|
|
||||||
|
[Option("testDir", DefaultValue = ".", HelpText = "Test directory to use (created if necessary)")]
|
||||||
|
public string TestDir { get; set; }
|
||||||
|
|
||||||
|
[Option("testFile", DefaultValue = "", HelpText = "Config file/directory to use")]
|
||||||
|
public string TestFile { get; set; }
|
||||||
|
|
||||||
|
[Option("resultsFile", HelpText = "Expected results Results json file")]
|
||||||
|
public string ExpectedResultsFile { get; set; }
|
||||||
|
|
||||||
|
[Option("totalMessages", DefaultValue = 0, HelpText = "The total number of messages to send to the output(s)")]
|
||||||
|
public int TotalMessages { get; set; }
|
||||||
|
|
||||||
|
[Option('n', "numMessages", DefaultValue = 1000, HelpText = "The number of messages to send to the output(s)")]
|
||||||
|
public int NumMessages { get; set; }
|
||||||
|
|
||||||
|
[Option('l', "logLevel", DefaultValue = "debug", HelpText = "Logging Level Debug|Error|Fatal|Info|Off|Trace|Warn")]
|
||||||
|
public string LogLevel { get; set; }
|
||||||
|
|
||||||
|
[Option('v', "verbose", DefaultValue = true, HelpText = "Prints all messages to standard output.")]
|
||||||
|
public bool Verbose { get; set; }
|
||||||
|
|
||||||
|
[Option("jsonLogDir", DefaultValue = ".", HelpText = "Json LogGenerator Log directory")]
|
||||||
|
public string JsonLogDir { get; set; }
|
||||||
|
|
||||||
|
[OptionArray('j', "json", DefaultValue = new string[] {})]
|
||||||
|
public string[] JsonLogFiles { get; set; }
|
||||||
|
|
||||||
|
[OptionArray("jroll", DefaultValue = new string[] { })]
|
||||||
|
public string[] JsonRollingLogFiles { get; set; }
|
||||||
|
|
||||||
|
[Option("jsonRate", DefaultValue = 30, HelpText = "Json Rate in Milliseconds between generation of log lines")]
|
||||||
|
public int JsonRate { get; set; }
|
||||||
|
|
||||||
|
[Option('u', "udp", DefaultValue = 0, HelpText = "Enable UDP generator on this Port")]
|
||||||
|
public int Udp { get; set; }
|
||||||
|
|
||||||
|
[Option("udp-host", DefaultValue = "localhost", HelpText = "Host to send Udp data to")]
|
||||||
|
public string UdpHost { get; set; }
|
||||||
|
|
||||||
|
[Option("udp-rate", DefaultValue = 10, HelpText = "Udp Rate in Milliseconds between generation of log lines")]
|
||||||
|
public int UdpRate { get; set; }
|
||||||
|
|
||||||
|
[Option('t', "tcp", DefaultValue = 0, HelpText = "Enable Tcp generator on this Port")]
|
||||||
|
public int Tcp { get; set; }
|
||||||
|
|
||||||
|
[Option("tcp-host", DefaultValue = "localhost", HelpText = "Host to send Tcp data to")]
|
||||||
|
public string TcpHost { get; set; }
|
||||||
|
|
||||||
|
[Option("tcp-rate", DefaultValue = 10, HelpText = "Tcp Rate in Milliseconds between generation of log lines")]
|
||||||
|
public int TcpRate { get; set; }
|
||||||
|
|
||||||
|
[Option('r', "redis", DefaultValue = 0, HelpText = "Enable Redis generator on this Port")]
|
||||||
|
public int Redis { get; set; }
|
||||||
|
|
||||||
|
[Option("redis-host", DefaultValue = "", HelpText = "Host to send Redis data to")]
|
||||||
|
public string RedisHost { get; set; }
|
||||||
|
|
||||||
|
[ParserState]
|
||||||
|
public IParserState LastParserState { get; set; }
|
||||||
|
|
||||||
|
[HelpOption]
|
||||||
|
public string GetUsage()
|
||||||
|
{
|
||||||
|
return HelpText.AutoBuild(this,
|
||||||
|
(HelpText current) => HelpText.DefaultParsingErrorsHandler(this, current));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2407
TimberWinR.TestGenerator/Dynamic.cs
Normal file
2407
TimberWinR.TestGenerator/Dynamic.cs
Normal file
File diff suppressed because it is too large
Load Diff
251
TimberWinR.TestGenerator/JsonLogFileGenerator.cs
Normal file
251
TimberWinR.TestGenerator/JsonLogFileGenerator.cs
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Remoting.Messaging;
|
||||||
|
using System.Threading;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using NLog;
|
||||||
|
using NLog.Config;
|
||||||
|
using NLog.Targets;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Text;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace TimberWinR.TestGenerator
|
||||||
|
{
|
||||||
|
class JsonLogFileTestParameters
|
||||||
|
{
|
||||||
|
public int NumMessages { get; set; }
|
||||||
|
public string LogFileDir { get; set; }
|
||||||
|
public string LogFileName { get; set; }
|
||||||
|
public int SleepTimeMilliseconds { get; set; }
|
||||||
|
public JsonLogFileTestParameters()
|
||||||
|
{
|
||||||
|
SleepTimeMilliseconds = 30;
|
||||||
|
LogFileDir = ".";
|
||||||
|
NumMessages = 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class JsonLogFileGenerator
|
||||||
|
{
|
||||||
|
public static int Generate(JsonLogFileTestParameters parms)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Start JSON LogFile Generation for: {0} on Thread: {1}", Path.GetFullPath(parms.LogFileName), Thread.CurrentThread.ManagedThreadId);
|
||||||
|
|
||||||
|
var logFilePath = Path.Combine(parms.LogFileDir, parms.LogFileName);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (File.Exists(logFilePath))
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Deleting file: {0}", logFilePath);
|
||||||
|
File.Delete(logFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Error(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var hostName = System.Environment.MachineName + "." +
|
||||||
|
Microsoft.Win32.Registry.LocalMachine.OpenSubKey(
|
||||||
|
"SYSTEM\\CurrentControlSet\\services\\Tcpip\\Parameters").GetValue("Domain", "").ToString();
|
||||||
|
|
||||||
|
var watch = Stopwatch.StartNew();
|
||||||
|
|
||||||
|
// This text is always added, making the file longer over time
|
||||||
|
// if it is not deleted.
|
||||||
|
using (StreamWriter sw = File.AppendText(logFilePath))
|
||||||
|
{
|
||||||
|
for (int i = 0; i < parms.NumMessages; i++)
|
||||||
|
{
|
||||||
|
JObject o = new JObject
|
||||||
|
{
|
||||||
|
{"LineNumber", i+1},
|
||||||
|
{"Application", "jsonlogfile-generator"},
|
||||||
|
{"Host", hostName},
|
||||||
|
{"UtcTimestamp", DateTime.UtcNow.ToString("o")},
|
||||||
|
{"Type", "jsonlog"},
|
||||||
|
{"Message", string.Format("{0}: Testgenerator jsonlogfile message {1}", i+1, DateTime.UtcNow.ToString("o"))},
|
||||||
|
{"Index", "logstash"}
|
||||||
|
};
|
||||||
|
sw.WriteLine(o.ToString(Formatting.None));
|
||||||
|
|
||||||
|
Thread.Sleep(parms.SleepTimeMilliseconds);
|
||||||
|
}
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Elapsed Time for {0} was {1} seconds", Path.GetFullPath(parms.LogFileName), watch.Elapsed);
|
||||||
|
watch.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Finished JSON Log File Generation for: {0} elapsed: {1}", Path.GetFullPath(parms.LogFileName), watch.Elapsed);
|
||||||
|
|
||||||
|
return parms.NumMessages;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class JsonRollingLogFileGenerator
|
||||||
|
{
|
||||||
|
public static int Generate(JsonLogFileTestParameters parms)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Start JSON RollingLogFile Generation for: {0} on Thread: {1}", Path.GetFullPath(parms.LogFileName), Thread.CurrentThread.ManagedThreadId);
|
||||||
|
|
||||||
|
var logFilePath = Path.Combine(parms.LogFileDir, parms.LogFileName);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (File.Exists(logFilePath))
|
||||||
|
File.Delete(logFilePath);
|
||||||
|
|
||||||
|
if (File.Exists(logFilePath + ".rolled"))
|
||||||
|
File.Delete(logFilePath + ".rolled");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Error(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var hostName = System.Environment.MachineName + "." +
|
||||||
|
Microsoft.Win32.Registry.LocalMachine.OpenSubKey(
|
||||||
|
"SYSTEM\\CurrentControlSet\\services\\Tcpip\\Parameters").GetValue("Domain", "").ToString();
|
||||||
|
|
||||||
|
|
||||||
|
int quarters = parms.NumMessages/4;
|
||||||
|
|
||||||
|
int[] segments = new int[] {quarters, quarters, quarters, quarters + parms.NumMessages%4};
|
||||||
|
var watch = Stopwatch.StartNew();
|
||||||
|
|
||||||
|
|
||||||
|
int recordNumber = 0;
|
||||||
|
int currentTotal = 0;
|
||||||
|
for (int segment = 0; segment < 4; segment++)
|
||||||
|
{
|
||||||
|
currentTotal += segments[segment];
|
||||||
|
|
||||||
|
// This text is always added, making the file longer over time
|
||||||
|
// if it is not deleted.
|
||||||
|
using (StreamWriter sw = File.AppendText(logFilePath))
|
||||||
|
{
|
||||||
|
sw.AutoFlush = true;
|
||||||
|
|
||||||
|
var lwatch = Stopwatch.StartNew();
|
||||||
|
|
||||||
|
// The Rolling Generator will roll 1/2 way through the log
|
||||||
|
for (int i = 0; i < segments[segment]; i++)
|
||||||
|
{
|
||||||
|
JObject o = new JObject
|
||||||
|
{
|
||||||
|
{"LineNumber", recordNumber + 1},
|
||||||
|
{"Application", "jsonrollinglogfile-generator"},
|
||||||
|
{"Host", hostName},
|
||||||
|
{"UtcTimestamp", DateTime.UtcNow.ToString("o")},
|
||||||
|
{"Type", "jsonrollinglog"},
|
||||||
|
{
|
||||||
|
"Message",
|
||||||
|
string.Format("{0}: Testgenerator jsonrollinglogfile message {1}", recordNumber + 1,
|
||||||
|
DateTime.UtcNow.ToString("o"))
|
||||||
|
},
|
||||||
|
{"Index", "logstash"}
|
||||||
|
};
|
||||||
|
sw.WriteLine(o.ToString(Formatting.None));
|
||||||
|
recordNumber++;
|
||||||
|
Thread.Sleep(parms.SleepTimeMilliseconds);
|
||||||
|
}
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Elapsed Time for {0} was {1} seconds for {2} logs", Path.GetFullPath(parms.LogFileName), lwatch.Elapsed, segments[segment]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// We might not have yet processed all the lines from the first file, so wait till
|
||||||
|
// we catch up before rolling the log file.
|
||||||
|
//
|
||||||
|
LogManager.GetCurrentClassLogger().Info("{0}: Waiting for output to catch up: {1} {2}", Thread.CurrentThread.ManagedThreadId, logFilePath, currentTotal);
|
||||||
|
WaitOutputToCatchUp(logFilePath, currentTotal);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Roll the log + wait for the reader to catch up.
|
||||||
|
//
|
||||||
|
|
||||||
|
LogManager.GetCurrentClassLogger().Info("{0}: Rolling Log File: {1} {2}", Thread.CurrentThread.ManagedThreadId, logFilePath, File.GetCreationTimeUtc(logFilePath));
|
||||||
|
|
||||||
|
RollLogFile(logFilePath);
|
||||||
|
|
||||||
|
LogManager.GetCurrentClassLogger().Info("{0}: Finished Rolling Log File: {1}", Thread.CurrentThread.ManagedThreadId, logFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
watch.Stop();
|
||||||
|
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Finished JSON RollingLogFile File Generation for: {0} elapsed: {1}", Path.GetFullPath(parms.LogFileName), watch.Elapsed);
|
||||||
|
|
||||||
|
return parms.NumMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void WaitOutputToCatchUp(string logFilePath, int firstPart)
|
||||||
|
{
|
||||||
|
bool caughtUp = false;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
var json = Program.Diagnostics.DiagnosticsOutput();
|
||||||
|
|
||||||
|
IList<JToken> inputs = json["timberwinr"]["inputs"].Children().ToList();
|
||||||
|
foreach (JToken t in inputs)
|
||||||
|
{
|
||||||
|
JProperty inputProp = t.First as JProperty;
|
||||||
|
if (inputProp.Name == "taillog" || inputProp.Name == "log")
|
||||||
|
{
|
||||||
|
var files = inputProp.Value["filedb"].Children().ToList();
|
||||||
|
foreach (var file in files)
|
||||||
|
{
|
||||||
|
var fileName = file["FileName"].ToString();
|
||||||
|
FileInfo fi1 = new FileInfo(fileName);
|
||||||
|
FileInfo fi2 = new FileInfo(logFilePath);
|
||||||
|
if (fi1.FullName == fi2.FullName)
|
||||||
|
{
|
||||||
|
var linesProcessed = file["LinesProcessed"].Value<int>();
|
||||||
|
if (linesProcessed >= firstPart)
|
||||||
|
{
|
||||||
|
caughtUp = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread.Sleep(300);
|
||||||
|
} while (!caughtUp);
|
||||||
|
|
||||||
|
LogManager.GetCurrentClassLogger().Info("{0}: Finished Waiting for output to catch up: {1} {2}", Thread.CurrentThread.ManagedThreadId, logFilePath, firstPart);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RollLogFile(string logFilePath)
|
||||||
|
{
|
||||||
|
bool moved = false;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (File.Exists(logFilePath + ".rolled"))
|
||||||
|
File.Delete(logFilePath + ".rolled");
|
||||||
|
|
||||||
|
File.Move(logFilePath, logFilePath + ".rolled");
|
||||||
|
moved = true;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
Thread.Sleep(100);
|
||||||
|
}
|
||||||
|
} while (!moved);
|
||||||
|
Thread.Sleep(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
93
TimberWinR.TestGenerator/LogFileGenerator.cs
Normal file
93
TimberWinR.TestGenerator/LogFileGenerator.cs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Remoting.Messaging;
|
||||||
|
using System.Threading;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using NLog;
|
||||||
|
using NLog.Config;
|
||||||
|
using NLog.Targets;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Text;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
|
||||||
|
namespace TimberWinR.TestGenerator
|
||||||
|
{
|
||||||
|
class LogFileTestParameters
|
||||||
|
{
|
||||||
|
public int NumMessages { get; set; }
|
||||||
|
public string LogFileDir { get; set; }
|
||||||
|
public string LogFileName { get; set; }
|
||||||
|
public int SleepTimeMilliseconds { get; set; }
|
||||||
|
public LogFileTestParameters()
|
||||||
|
{
|
||||||
|
SleepTimeMilliseconds = 30;
|
||||||
|
LogFileDir = ".";
|
||||||
|
NumMessages = 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LogFileGenerator
|
||||||
|
{
|
||||||
|
public static int Generate(JsonLogFileTestParameters parms)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Start LogFile Generation for: {0} on Thread: {1}", Path.GetFullPath(parms.LogFileName), Thread.CurrentThread.ManagedThreadId);
|
||||||
|
|
||||||
|
var logFilePath = Path.Combine(parms.LogFileDir, parms.LogFileName);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (File.Exists(logFilePath))
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Deleting file: {0}", logFilePath);
|
||||||
|
File.Delete(logFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Error(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var hostName = System.Environment.MachineName + "." +
|
||||||
|
Microsoft.Win32.Registry.LocalMachine.OpenSubKey(
|
||||||
|
"SYSTEM\\CurrentControlSet\\services\\Tcpip\\Parameters").GetValue("Domain", "").ToString();
|
||||||
|
|
||||||
|
var watch = Stopwatch.StartNew();
|
||||||
|
|
||||||
|
// This text is always added, making the file longer over time
|
||||||
|
// if it is not deleted.
|
||||||
|
using (StreamWriter sw = File.AppendText(logFilePath))
|
||||||
|
{
|
||||||
|
for (int i = 0; i < parms.NumMessages; i++)
|
||||||
|
{
|
||||||
|
JObject o = new JObject
|
||||||
|
{
|
||||||
|
{"LineNumber", i+1},
|
||||||
|
{"Application", "logfile-generator"},
|
||||||
|
{"Host", hostName},
|
||||||
|
{"UtcTimestamp", DateTime.UtcNow.ToString("o")},
|
||||||
|
{"Type", "log"},
|
||||||
|
{"Message", string.Format("{0}: Testgenerator logfile message {1}", i+1, DateTime.UtcNow.ToString("o"))},
|
||||||
|
{"Index", "logstash"}
|
||||||
|
};
|
||||||
|
sw.WriteLine(o.ToString(Formatting.None));
|
||||||
|
|
||||||
|
Thread.Sleep(parms.SleepTimeMilliseconds);
|
||||||
|
}
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Elapsed Time for {0} was {1} seconds", Path.GetFullPath(parms.LogFileName), watch.Elapsed);
|
||||||
|
watch.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Finished LogFile Generation for: {0} elapsed: {1}", Path.GetFullPath(parms.LogFileName), watch.Elapsed);
|
||||||
|
|
||||||
|
return parms.NumMessages;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
646
TimberWinR.TestGenerator/Program.cs
Normal file
646
TimberWinR.TestGenerator/Program.cs
Normal file
@@ -0,0 +1,646 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.CodeDom.Compiler;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
using NLog;
|
||||||
|
using NLog.Config;
|
||||||
|
using NLog.Targets;
|
||||||
|
using ServiceStack.Text.Jsv;
|
||||||
|
using TimberWinR.Parser;
|
||||||
|
|
||||||
|
|
||||||
|
namespace TimberWinR.TestGenerator
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
private static List<Task> _tasks = new List<Task>();
|
||||||
|
private static CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
private static Manager _timberWinR;
|
||||||
|
|
||||||
|
public static Diagnostics.Diagnostics Diagnostics { get; set; }
|
||||||
|
|
||||||
|
private static PerformanceCounter cpuCounter = new PerformanceCounter();
|
||||||
|
private static PerformanceCounter ramCounter = new PerformanceCounter();
|
||||||
|
private static Task _monitorTask;
|
||||||
|
|
||||||
|
private static int _totalMessagesToSend;
|
||||||
|
private static int _cpuSampleCount;
|
||||||
|
private static double _avgCpuUsage;
|
||||||
|
private static double _totalCpuUsage;
|
||||||
|
private static double _maxCpuUsage;
|
||||||
|
|
||||||
|
private static int _memSampleCount;
|
||||||
|
private static double _avgMemUsage;
|
||||||
|
private static double _totalMemUsage;
|
||||||
|
private static double _maxMemUsage;
|
||||||
|
|
||||||
|
private static CommandLineOptions Options;
|
||||||
|
|
||||||
|
static int Main(string[] args)
|
||||||
|
{
|
||||||
|
_totalMessagesToSend = 0;
|
||||||
|
|
||||||
|
cpuCounter.CategoryName = "Processor";
|
||||||
|
cpuCounter.CounterName = "% Processor Time";
|
||||||
|
cpuCounter.InstanceName = "_Total";
|
||||||
|
|
||||||
|
ramCounter.CategoryName = "Memory";
|
||||||
|
ramCounter.CounterName = "% Committed Bytes In Use";
|
||||||
|
|
||||||
|
Options = new CommandLineOptions();
|
||||||
|
|
||||||
|
if (CommandLine.Parser.Default.ParseArguments(args, Options))
|
||||||
|
{
|
||||||
|
var testFile = Options.TestFile;
|
||||||
|
if (!string.IsNullOrEmpty(testFile))
|
||||||
|
{
|
||||||
|
if (!File.Exists(Options.TestFile))
|
||||||
|
throw new Exception(string.Format("No such test file: {0} found", Options.TestFile));
|
||||||
|
|
||||||
|
var fargs = ParseTestArguments(testFile, ref Options);
|
||||||
|
if (!CommandLine.Parser.Default.ParseArguments(fargs, Options))
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetupTestDirectory(Options);
|
||||||
|
|
||||||
|
var swOverall = Stopwatch.StartNew();
|
||||||
|
swOverall.Start();
|
||||||
|
|
||||||
|
InitializeLogging(Options.LogLevel);
|
||||||
|
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Starting CPU Usage: {0}, RAM Usage: {1}", getCurrentCpuUsage(), getAvailableRAM());
|
||||||
|
|
||||||
|
// Reset the tests.
|
||||||
|
ResetTests(Options);
|
||||||
|
|
||||||
|
var sw = Stopwatch.StartNew();
|
||||||
|
|
||||||
|
// Startup TimberWinR
|
||||||
|
if (Options.StartTimberWinR)
|
||||||
|
StartTimberWinR(Options.TimberWinRConfigFile, Options.LogLevel, ".", false);
|
||||||
|
|
||||||
|
// Run the Generators
|
||||||
|
var arrayOfTasks = RunGenerators(Options);
|
||||||
|
|
||||||
|
// Wait for all Generators to finish
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Task.WaitAll(arrayOfTasks);
|
||||||
|
}
|
||||||
|
catch (AggregateException aex)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Error(aex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Generation Finished: " + sw.Elapsed);
|
||||||
|
sw.Reset();
|
||||||
|
sw.Start();
|
||||||
|
|
||||||
|
// All generators are finished, wait till senders are done.
|
||||||
|
WaitForOutputTransmission();
|
||||||
|
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Finished Transmission: " + sw.Elapsed);
|
||||||
|
sw.Reset();
|
||||||
|
sw.Start();
|
||||||
|
|
||||||
|
// Get all the stats
|
||||||
|
JObject jsonTimberWinr;
|
||||||
|
|
||||||
|
if (Options.StartTimberWinR)
|
||||||
|
jsonTimberWinr = ShutdownTimberWinR();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
jsonTimberWinr = GetDiagnosticsOutput();
|
||||||
|
if (jsonTimberWinr == null)
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Finished Shutdown: " + sw.Elapsed);
|
||||||
|
sw.Stop();
|
||||||
|
|
||||||
|
swOverall.Stop();
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Total Elapsed Time: {0}", swOverall.Elapsed);
|
||||||
|
|
||||||
|
int results = VerifyResults(Options, jsonTimberWinr);
|
||||||
|
|
||||||
|
Console.ReadKey();
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GET(string url)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
|
||||||
|
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
|
||||||
|
Stream stream = response.GetResponseStream();
|
||||||
|
StreamReader reader = new StreamReader(stream);
|
||||||
|
|
||||||
|
string data = reader.ReadToEnd();
|
||||||
|
|
||||||
|
reader.Close();
|
||||||
|
stream.Close();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().ErrorException("Error in GET", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CopySourceFile(string fileName, string outputDir)
|
||||||
|
{
|
||||||
|
FileInfo fi = new FileInfo(fileName);
|
||||||
|
if (fi.Exists)
|
||||||
|
File.Copy(fileName, Path.Combine(outputDir, fi.Name));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetupTestDirectory(CommandLineOptions options)
|
||||||
|
{
|
||||||
|
if (options.TestDir != "." && Directory.Exists(options.TestDir))
|
||||||
|
Directory.Delete(options.TestDir, true);
|
||||||
|
|
||||||
|
if (!Directory.Exists(options.TestDir))
|
||||||
|
Directory.CreateDirectory(options.TestDir);
|
||||||
|
|
||||||
|
CopySourceFile(options.TestFile, options.TestDir);
|
||||||
|
CopySourceFile(options.TimberWinRConfigFile, options.TestDir);
|
||||||
|
CopySourceFile(options.ExpectedResultsFile, options.TestDir);
|
||||||
|
|
||||||
|
Directory.SetCurrentDirectory(options.TestDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string[] ParseTestArguments(string testFile, ref CommandLineOptions options)
|
||||||
|
{
|
||||||
|
options = new CommandLineOptions();
|
||||||
|
JObject jtest = JObject.Parse(File.ReadAllText(testFile));
|
||||||
|
IList<JToken> inputs = jtest["arguments"].Children().ToList();
|
||||||
|
List<string> testargs = new List<string>();
|
||||||
|
foreach (JProperty it in inputs)
|
||||||
|
{
|
||||||
|
testargs.Add(it.Name);
|
||||||
|
|
||||||
|
var cc = it.Value.Children().Count();
|
||||||
|
if (cc > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < cc; i++)
|
||||||
|
{
|
||||||
|
testargs.Add(it.Value[i].ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
testargs.Add(it.Value.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var fargs = testargs.ToArray();
|
||||||
|
return fargs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int VerifyResults(CommandLineOptions options, JObject json)
|
||||||
|
{
|
||||||
|
var jresult = JObject.Parse(File.ReadAllText(options.ExpectedResultsFile));
|
||||||
|
|
||||||
|
json["maxCpuUsage"] = _maxCpuUsage;
|
||||||
|
json["avgCpuUsage"] = _avgCpuUsage;
|
||||||
|
|
||||||
|
json["maxMemUsage"] = _maxMemUsage;
|
||||||
|
json["avgMemUsage"] = _avgMemUsage;
|
||||||
|
|
||||||
|
// TailLogs
|
||||||
|
|
||||||
|
IList<JToken> inputs = json["timberwinr"]["inputs"].Children().ToList();
|
||||||
|
foreach (JToken t in inputs)
|
||||||
|
{
|
||||||
|
JProperty inputProp = t.First as JProperty;
|
||||||
|
switch (inputProp.Name)
|
||||||
|
{
|
||||||
|
case "udp":
|
||||||
|
if (VerifyConditions(json, new string[] { "udp" }, inputProp, jresult) != 0)
|
||||||
|
return 1;
|
||||||
|
break;
|
||||||
|
case "tcp":
|
||||||
|
if (VerifyConditions(json, new string[] { "tcp" }, inputProp, jresult) != 0)
|
||||||
|
return 1;
|
||||||
|
break;
|
||||||
|
case "log":
|
||||||
|
case "taillog":
|
||||||
|
if (VerifyConditions(json, new string[] { "log", "taillog" }, inputProp, jresult) != 0)
|
||||||
|
return 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int VerifyConditions(JObject json, string[] logTypes, JProperty inputProp, JObject jresult)
|
||||||
|
{
|
||||||
|
var ttail = inputProp.Value as JObject;
|
||||||
|
foreach (var resultInput in jresult["Results"]["Inputs"].Children().ToList())
|
||||||
|
{
|
||||||
|
JProperty rinputProp = resultInput.First as JProperty;
|
||||||
|
if (logTypes.Contains(rinputProp.Name))
|
||||||
|
{
|
||||||
|
foreach (JProperty testProp in rinputProp.Value)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var cond1 = testProp.Value.ToString();
|
||||||
|
IList<string> tkeys = ttail.Properties().Select(pn => pn.Name).ToList();
|
||||||
|
foreach (string tkey in tkeys)
|
||||||
|
cond1 = cond1.Replace(string.Format("[{0}]", tkey), string.Format("{0}", ttail[tkey].ToString()));
|
||||||
|
|
||||||
|
// Add builtins
|
||||||
|
cond1 = cond1.Replace("[avgCpuUsage]", json["avgCpuUsage"].ToString());
|
||||||
|
cond1 = cond1.Replace("[maxCpuUsage]", json["maxCpuUsage"].ToString());
|
||||||
|
cond1 = cond1.Replace("[avgMemUsage]", json["avgMemUsage"].ToString());
|
||||||
|
cond1 = cond1.Replace("[maxMemUsage]", json["maxMemUsage"].ToString());
|
||||||
|
|
||||||
|
var p1 = Expression.Parameter(typeof(JObject), "json");
|
||||||
|
var e1 = System.Linq.Dynamic.DynamicExpression.ParseLambda(new[] { p1 },
|
||||||
|
typeof(bool), cond1);
|
||||||
|
bool r1 = (bool)e1.Compile().DynamicInvoke(ttail);
|
||||||
|
if (!r1)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Error("Test Failed: '{0}: ({1})'", testProp.Name, cond1);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger()
|
||||||
|
.Info("PASSED({0}): '{1}: ({2})'", inputProp.Name, testProp.Name, cond1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger()
|
||||||
|
.Error("Error parsing expression '{0}': {1}", testProp.Value.ToString(),
|
||||||
|
ex.Message);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static JObject GetDiagnosticsOutput()
|
||||||
|
{
|
||||||
|
if (Diagnostics != null)
|
||||||
|
return Diagnostics.DiagnosticsOutput();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var jsonDiag = GET("http://localhost:5141");
|
||||||
|
if (jsonDiag == null)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Error("TimberWinR diagnostics port not responding.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return JObject.Parse(jsonDiag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait till all output has been transmitted.
|
||||||
|
private static void WaitForOutputTransmission()
|
||||||
|
{
|
||||||
|
bool completed = false;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
var json = GetDiagnosticsOutput();
|
||||||
|
if (json == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
//Console.WriteLine(json.ToString(Formatting.Indented));
|
||||||
|
|
||||||
|
IList<JToken> inputs = json["timberwinr"]["inputs"].Children().ToList();
|
||||||
|
foreach (var so in inputs.Children())
|
||||||
|
{
|
||||||
|
var token = so.First;
|
||||||
|
var messages = token["messages"].Value<int>();
|
||||||
|
// Console.WriteLine("{0} messages", messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
IList<JToken> outputs = json["timberwinr"]["outputs"].Children().ToList();
|
||||||
|
foreach (var so in outputs.Children())
|
||||||
|
{
|
||||||
|
var outputToken = so.First;
|
||||||
|
|
||||||
|
var mbc = outputToken["queuedMessageCount"].Value<int>();
|
||||||
|
var smc = outputToken["sentMessageCount"].Value<int>();
|
||||||
|
|
||||||
|
//LogManager.GetCurrentClassLogger().Info("Output: {2} Queued: {0}, Sent: {1}", mbc, smc, outputToken.ToString());
|
||||||
|
|
||||||
|
completed = mbc == 0 && smc >= _totalMessagesToSend;
|
||||||
|
}
|
||||||
|
Thread.Sleep(250);
|
||||||
|
} while (!completed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void sampleUsages()
|
||||||
|
{
|
||||||
|
getCurrentCpuUsage();
|
||||||
|
getAvailableRAM();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string getCurrentCpuUsage()
|
||||||
|
{
|
||||||
|
_cpuSampleCount++;
|
||||||
|
var v = cpuCounter.NextValue();
|
||||||
|
if (v > _maxCpuUsage)
|
||||||
|
_maxCpuUsage = v;
|
||||||
|
|
||||||
|
_totalCpuUsage += v;
|
||||||
|
_avgCpuUsage = _totalCpuUsage / _cpuSampleCount;
|
||||||
|
|
||||||
|
return v + "%";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string getAvailableRAM()
|
||||||
|
{
|
||||||
|
_memSampleCount++;
|
||||||
|
var v = ramCounter.NextValue();
|
||||||
|
if (v > _maxMemUsage)
|
||||||
|
_maxMemUsage = v;
|
||||||
|
|
||||||
|
_totalMemUsage += v;
|
||||||
|
_avgMemUsage = _totalMemUsage / _memSampleCount;
|
||||||
|
return v + "MB";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JObject ShutdownTimberWinR()
|
||||||
|
{
|
||||||
|
if (_timberWinR != null)
|
||||||
|
{
|
||||||
|
// Cancel any/all other threads
|
||||||
|
_cancellationTokenSource.Cancel();
|
||||||
|
|
||||||
|
_timberWinR.Shutdown();
|
||||||
|
|
||||||
|
var json = Diagnostics.DiagnosticsOutput();
|
||||||
|
|
||||||
|
LogManager.GetCurrentClassLogger()
|
||||||
|
.Info("Average CPU Usage: {0}%, Average RAM Usage: {1}MB, Max CPU: {2}%, Max Mem: {3}MB",
|
||||||
|
_avgCpuUsage, _avgMemUsage, _maxCpuUsage, _maxMemUsage);
|
||||||
|
|
||||||
|
LogManager.GetCurrentClassLogger().Info(json.ToString());
|
||||||
|
|
||||||
|
Diagnostics.Shutdown();
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void StartTimberWinR(string configFile, string logLevel, string logFileDir, bool enableLiveMonitor)
|
||||||
|
{
|
||||||
|
_timberWinR = new TimberWinR.Manager(configFile, logLevel, logFileDir, enableLiveMonitor, _cancellationTokenSource.Token, false);
|
||||||
|
_timberWinR.OnConfigurationProcessed += TimberWinROnOnConfigurationProcessed;
|
||||||
|
_timberWinR.Start(_cancellationTokenSource.Token);
|
||||||
|
Diagnostics = new Diagnostics.Diagnostics(_timberWinR, _cancellationTokenSource.Token, 5141);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void TimberWinROnOnConfigurationProcessed(Configuration configuration)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(Options.RedisHost) && configuration.RedisOutputs != null && configuration.RedisOutputs.Count() > 0)
|
||||||
|
{
|
||||||
|
foreach (var ro in configuration.RedisOutputs)
|
||||||
|
{
|
||||||
|
ro.Host = new string[] { Options.RedisHost };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void InitializeLogging(string logLevel)
|
||||||
|
{
|
||||||
|
var loggingConfiguration = new LoggingConfiguration();
|
||||||
|
|
||||||
|
// Create our default targets
|
||||||
|
var coloredConsoleTarget = new ColoredConsoleTarget();
|
||||||
|
|
||||||
|
var logFileDir = ".";
|
||||||
|
|
||||||
|
Target fileTarget = CreateDefaultFileTarget(logFileDir);
|
||||||
|
|
||||||
|
loggingConfiguration.AddTarget("Console", coloredConsoleTarget);
|
||||||
|
loggingConfiguration.AddTarget("DailyFile", fileTarget);
|
||||||
|
|
||||||
|
// The LogLevel.Trace means has to be at least Trace to show up on console
|
||||||
|
loggingConfiguration.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, coloredConsoleTarget));
|
||||||
|
// LogLevel.Debug means has to be at least Debug to show up in logfile
|
||||||
|
loggingConfiguration.LoggingRules.Add(new LoggingRule("*", LogLevel.Debug, fileTarget));
|
||||||
|
|
||||||
|
LogManager.Configuration = loggingConfiguration;
|
||||||
|
LogManager.EnableLogging();
|
||||||
|
|
||||||
|
LogManager.GlobalThreshold = LogLevel.FromString(logLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
static FileTarget CreateDefaultFileTarget(string logPath)
|
||||||
|
{
|
||||||
|
return new FileTarget
|
||||||
|
{
|
||||||
|
ArchiveEvery = FileArchivePeriod.None,
|
||||||
|
ArchiveAboveSize = 5 * 1024 * 1024,
|
||||||
|
MaxArchiveFiles = 5,
|
||||||
|
BufferSize = 10,
|
||||||
|
FileName = Path.Combine(logPath, "TimberWinR.TestGenerator", "TimberWinRTestGen.log"),
|
||||||
|
ArchiveFileName = Path.Combine(logPath, "TimberWinR-TestGenerator_log-{#######}.log"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ResetTests(CommandLineOptions options)
|
||||||
|
{
|
||||||
|
if (File.Exists(".timberwinrdb"))
|
||||||
|
File.Delete(".timberwinrdb");
|
||||||
|
|
||||||
|
if (File.Exists("TimberWinR.TestGenerator\\TimberWinRTestGen.log"))
|
||||||
|
File.Delete("TimberWinR.TestGenerator\\TimberWinRTestGen.log");
|
||||||
|
|
||||||
|
if (File.Exists("TimberWinR\\TimberWinR.log"))
|
||||||
|
File.Delete("TimberWinR\\TimberWinR.log");
|
||||||
|
|
||||||
|
if (options.JsonLogFiles.Length > 0)
|
||||||
|
{
|
||||||
|
foreach (var logFile in options.JsonLogFiles)
|
||||||
|
{
|
||||||
|
if (File.Exists(logFile))
|
||||||
|
File.Delete(logFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.JsonRollingLogFiles.Length > 0)
|
||||||
|
{
|
||||||
|
foreach (var logFile in options.JsonRollingLogFiles)
|
||||||
|
{
|
||||||
|
if (File.Exists(logFile))
|
||||||
|
File.Delete(logFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Task[] RunGenerators(CommandLineOptions options)
|
||||||
|
{
|
||||||
|
_totalMessagesToSend = options.TotalMessages;
|
||||||
|
|
||||||
|
_monitorTask = Task.Factory.StartNew(() =>
|
||||||
|
{
|
||||||
|
using (var syncHandle = new ManualResetEventSlim())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Execute the query
|
||||||
|
while (!_cancellationTokenSource.Token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
sampleUsages();
|
||||||
|
// LogManager.GetCurrentClassLogger().Info("Starting CPU Usage: {0}, RAM Usage: {1}", getCurrentCpuUsage(), getAvailableRAM());
|
||||||
|
syncHandle.Wait(TimeSpan.FromMilliseconds(options.JsonRate), _cancellationTokenSource.Token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Error(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, _cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Current);
|
||||||
|
|
||||||
|
StartJson(options);
|
||||||
|
StartJsonRolling(options);
|
||||||
|
StartUdp(options);
|
||||||
|
StartTcp(options);
|
||||||
|
|
||||||
|
return _tasks.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void StartJson(CommandLineOptions options)
|
||||||
|
{
|
||||||
|
if (options.JsonLogFiles.Length > 0)
|
||||||
|
{
|
||||||
|
foreach (var logFile in options.JsonLogFiles)
|
||||||
|
{
|
||||||
|
_totalMessagesToSend += options.NumMessages;
|
||||||
|
|
||||||
|
if (options.Verbose)
|
||||||
|
LogManager.GetCurrentClassLogger()
|
||||||
|
.Info("Starting LogFile Generator for {0}",
|
||||||
|
Path.GetFullPath(Path.Combine(options.JsonLogDir, logFile)));
|
||||||
|
_tasks.Add(Task.Factory.StartNew(() =>
|
||||||
|
{
|
||||||
|
var p = new JsonLogFileTestParameters()
|
||||||
|
{
|
||||||
|
NumMessages = options.NumMessages,
|
||||||
|
LogFileDir = options.JsonLogDir,
|
||||||
|
LogFileName = logFile,
|
||||||
|
SleepTimeMilliseconds = options.JsonRate
|
||||||
|
};
|
||||||
|
JsonLogFileGenerator.Generate(p);
|
||||||
|
Thread.Sleep(250);
|
||||||
|
}));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void StartJsonRolling(CommandLineOptions options)
|
||||||
|
{
|
||||||
|
if (options.JsonRollingLogFiles.Length > 0)
|
||||||
|
{
|
||||||
|
foreach (var logFile in options.JsonRollingLogFiles)
|
||||||
|
{
|
||||||
|
_totalMessagesToSend += options.NumMessages;
|
||||||
|
|
||||||
|
if (options.Verbose)
|
||||||
|
LogManager.GetCurrentClassLogger()
|
||||||
|
.Info("Starting RollingLogFile Generator for {0}",
|
||||||
|
Path.GetFullPath(Path.Combine(options.JsonLogDir, logFile)));
|
||||||
|
_tasks.Add(Task.Factory.StartNew(() =>
|
||||||
|
{
|
||||||
|
var p = new JsonLogFileTestParameters()
|
||||||
|
{
|
||||||
|
NumMessages = options.NumMessages,
|
||||||
|
LogFileDir = options.JsonLogDir,
|
||||||
|
LogFileName = logFile,
|
||||||
|
SleepTimeMilliseconds = options.JsonRate
|
||||||
|
};
|
||||||
|
JsonRollingLogFileGenerator.Generate(p);
|
||||||
|
Thread.Sleep(250);
|
||||||
|
}));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void StartUdp(CommandLineOptions options)
|
||||||
|
{
|
||||||
|
if (options.Udp > 0)
|
||||||
|
{
|
||||||
|
if (options.Verbose)
|
||||||
|
LogManager.GetCurrentClassLogger()
|
||||||
|
.Info("Starting UDP Generator for {0}:{1}", options.UdpHost, options.Udp);
|
||||||
|
|
||||||
|
_tasks.Add(Task.Factory.StartNew(() =>
|
||||||
|
{
|
||||||
|
var p = new UdpTestParameters()
|
||||||
|
{
|
||||||
|
Port = options.Udp,
|
||||||
|
Host = options.UdpHost,
|
||||||
|
NumMessages = options.NumMessages,
|
||||||
|
SleepTimeMilliseconds = options.UdpRate
|
||||||
|
};
|
||||||
|
_totalMessagesToSend += UdpTestGenerator.Generate(p);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void StartTcp(CommandLineOptions options)
|
||||||
|
{
|
||||||
|
if (options.Tcp > 0)
|
||||||
|
{
|
||||||
|
if (options.Verbose)
|
||||||
|
LogManager.GetCurrentClassLogger()
|
||||||
|
.Info("Starting Tcp Generator for {0}:{1}", options.TcpHost, options.Tcp);
|
||||||
|
|
||||||
|
_totalMessagesToSend += options.NumMessages;
|
||||||
|
|
||||||
|
_tasks.Add(Task.Factory.StartNew(() =>
|
||||||
|
{
|
||||||
|
var p = new TcpTestParameters()
|
||||||
|
{
|
||||||
|
Port = options.Tcp,
|
||||||
|
Host = options.TcpHost,
|
||||||
|
NumMessages = options.NumMessages,
|
||||||
|
SleepTimeMilliseconds = options.TcpRate
|
||||||
|
};
|
||||||
|
TcpTestGenerator.Generate(p);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
36
TimberWinR.TestGenerator/Properties/AssemblyInfo.cs
Normal file
36
TimberWinR.TestGenerator/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("TimberWinR.TestGenerator")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("TimberWinR.TestGenerator")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © 2015")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
|
[assembly: Guid("a56bf91c-c5f8-4771-8ef8-ab9ad28179c4")]
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
|
// by using the '*' as shown below:
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||||
49
TimberWinR.TestGenerator/RedisTestGenerator.cs
Normal file
49
TimberWinR.TestGenerator/RedisTestGenerator.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using ServiceStack.Redis;
|
||||||
|
|
||||||
|
namespace TimberWinR.TestGenerator
|
||||||
|
{
|
||||||
|
class RedisTestParameters
|
||||||
|
{
|
||||||
|
public int Port { get; set; }
|
||||||
|
public string Host { get; set; }
|
||||||
|
public int NumMessages { get; set; }
|
||||||
|
public RedisTestParameters()
|
||||||
|
{
|
||||||
|
NumMessages = 100;
|
||||||
|
Port = 6379;
|
||||||
|
Host = "localhost";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RedisTestGenerator
|
||||||
|
{
|
||||||
|
public static void Generate(RedisTestParameters parms)
|
||||||
|
{
|
||||||
|
var hostName = System.Environment.MachineName + "." +
|
||||||
|
Microsoft.Win32.Registry.LocalMachine.OpenSubKey(
|
||||||
|
"SYSTEM\\CurrentControlSet\\services\\Tcpip\\Parameters").GetValue("Domain", "").ToString();
|
||||||
|
|
||||||
|
var rc = new RedisClient(parms.Host, parms.Port);
|
||||||
|
|
||||||
|
for (int i = 0; i < parms.NumMessages; i++)
|
||||||
|
{
|
||||||
|
JObject o = new JObject
|
||||||
|
{
|
||||||
|
{"Application", "redis-generator"},
|
||||||
|
{"Host", hostName},
|
||||||
|
{"UtcTimestamp", DateTime.UtcNow.ToString("o")},
|
||||||
|
{"Type", "redis"},
|
||||||
|
{"Message", "redis message " + DateTime.UtcNow.ToString("o")},
|
||||||
|
{"Index", "logstash"}
|
||||||
|
};
|
||||||
|
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(o.ToString());
|
||||||
|
var restult = rc.RPush("logstash", bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
62
TimberWinR.TestGenerator/TcpTestGenerator.cs
Normal file
62
TimberWinR.TestGenerator/TcpTestGenerator.cs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
using System.Threading;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Text;
|
||||||
|
using ServiceStack.Text;
|
||||||
|
|
||||||
|
namespace TimberWinR.TestGenerator
|
||||||
|
{
|
||||||
|
class TcpTestParameters
|
||||||
|
{
|
||||||
|
public int Port { get; set; }
|
||||||
|
public string Host { get; set; }
|
||||||
|
public int NumMessages { get; set; }
|
||||||
|
public int SleepTimeMilliseconds { get; set; }
|
||||||
|
public TcpTestParameters()
|
||||||
|
{
|
||||||
|
NumMessages = 100;
|
||||||
|
Port = 5140;
|
||||||
|
Host = "localhost";
|
||||||
|
SleepTimeMilliseconds = 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TcpTestGenerator
|
||||||
|
{
|
||||||
|
public static int Generate(TcpTestParameters parms)
|
||||||
|
{
|
||||||
|
TcpClient server = new TcpClient(parms.Host, parms.Port);
|
||||||
|
|
||||||
|
var hostName = System.Environment.MachineName + "." +
|
||||||
|
Microsoft.Win32.Registry.LocalMachine.OpenSubKey(
|
||||||
|
"SYSTEM\\CurrentControlSet\\services\\Tcpip\\Parameters").GetValue("Domain", "").ToString();
|
||||||
|
|
||||||
|
|
||||||
|
using (NetworkStream stream = server.GetStream())
|
||||||
|
{
|
||||||
|
for (int i = 0; i < parms.NumMessages; i++)
|
||||||
|
{
|
||||||
|
JObject o = new JObject
|
||||||
|
{
|
||||||
|
{"Application", "tcp-generator"},
|
||||||
|
{"Host", hostName},
|
||||||
|
{"UtcTimestamp", DateTime.UtcNow.ToString("o")},
|
||||||
|
{"Type", "tcp"},
|
||||||
|
{"Message", "tcp message " + DateTime.UtcNow.ToString("o")},
|
||||||
|
{"Index", "logstash"}
|
||||||
|
};
|
||||||
|
byte[] data = Encoding.UTF8.GetBytes(string.Format("{0}\n", o.ToString()));
|
||||||
|
stream.Write(data, 0, data.Length);
|
||||||
|
Thread.Sleep(parms.SleepTimeMilliseconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return parms.NumMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
163
TimberWinR.TestGenerator/TimberWinR.TestGenerator.csproj
Normal file
163
TimberWinR.TestGenerator/TimberWinR.TestGenerator.csproj
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{F3960D6E-1EA0-4F4E-8F08-82FC185A0D29}</ProjectGuid>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>TimberWinR.TestGenerator</RootNamespace>
|
||||||
|
<AssemblyName>TimberWinR.TestGenerator</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
||||||
|
<RestorePackages>true</RestorePackages>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="CommandLine">
|
||||||
|
<HintPath>..\packages\CommandLineParser.1.9.71\lib\net40\CommandLine.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\packages\Newtonsoft.Json.6.0.8\lib\net40\Newtonsoft.Json.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="NLog, Version=3.2.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\packages\NLog.3.2.0.0\lib\net40\NLog.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="ServiceStack.Common">
|
||||||
|
<HintPath>..\packages\ServiceStack.Common.Signed.4.0.38\lib\net40\ServiceStack.Common.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="ServiceStack.Interfaces">
|
||||||
|
<HintPath>..\packages\ServiceStack.Interfaces.4.0.38\lib\portable-wp80+sl5+net40+win8+monotouch+monoandroid\ServiceStack.Interfaces.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="ServiceStack.Redis">
|
||||||
|
<HintPath>..\packages\ServiceStack.Redis.Signed.4.0.38\lib\net40\ServiceStack.Redis.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="ServiceStack.Text">
|
||||||
|
<HintPath>..\packages\ServiceStack.Text.Signed.4.0.38\lib\net40\ServiceStack.Text.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="CommandLineOptions.cs" />
|
||||||
|
<Compile Include="Dynamic.cs" />
|
||||||
|
<Compile Include="LogFileGenerator.cs" />
|
||||||
|
<Compile Include="Program.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="RedisTestGenerator.cs" />
|
||||||
|
<Compile Include="TcpTestGenerator.cs" />
|
||||||
|
<Compile Include="JsonLogFileGenerator.cs" />
|
||||||
|
<Compile Include="UdpTestGenerator.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="default.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="results1.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="test1.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="test1-twconfig.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
<Content Include="test2-tw.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="test2.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="results2.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="test3.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="test3-tw.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="results3.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="test4-tw.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="results4.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="test4.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="test5-twconfig.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="test5.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="results5.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="test7-tw.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="test7.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="results7.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<None Include="sample-apache.log">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\TimberWinR\TimberWinR.csproj">
|
||||||
|
<Project>{4ef96a08-21db-4178-be44-70dae594632c}</Project>
|
||||||
|
<Name>TimberWinR</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
|
||||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
|
||||||
|
</Target>
|
||||||
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
<Target Name="BeforeBuild">
|
||||||
|
</Target>
|
||||||
|
<Target Name="AfterBuild">
|
||||||
|
</Target>
|
||||||
|
-->
|
||||||
|
</Project>
|
||||||
93
TimberWinR.TestGenerator/UdpTestGenerator.cs
Normal file
93
TimberWinR.TestGenerator/UdpTestGenerator.cs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Threading;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using NLog;
|
||||||
|
using NLog.Config;
|
||||||
|
using NLog.Targets;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace TimberWinR.TestGenerator
|
||||||
|
{
|
||||||
|
class UdpTestParameters
|
||||||
|
{
|
||||||
|
public int Port { get; set; }
|
||||||
|
public string Host { get; set; }
|
||||||
|
public int NumMessages { get; set; }
|
||||||
|
public int SleepTimeMilliseconds { get; set; }
|
||||||
|
public UdpTestParameters()
|
||||||
|
{
|
||||||
|
NumMessages = 100;
|
||||||
|
Port = 6379;
|
||||||
|
Host = "localhost";
|
||||||
|
SleepTimeMilliseconds = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UdpTestGenerator
|
||||||
|
{
|
||||||
|
public static int Generate(UdpTestParameters parms)
|
||||||
|
{
|
||||||
|
var hostName = System.Environment.MachineName + "." +
|
||||||
|
Microsoft.Win32.Registry.LocalMachine.OpenSubKey(
|
||||||
|
"SYSTEM\\CurrentControlSet\\services\\Tcpip\\Parameters").GetValue("Domain", "").ToString();
|
||||||
|
|
||||||
|
IPAddress broadcast;
|
||||||
|
if (!IPAddress.TryParse(parms.Host, out broadcast))
|
||||||
|
broadcast = Dns.GetHostEntry(parms.Host).AddressList[0];
|
||||||
|
|
||||||
|
Socket s = new Socket(broadcast.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
|
||||||
|
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Start UDP Generation");
|
||||||
|
|
||||||
|
for (int i = 0; i < parms.NumMessages; i++)
|
||||||
|
{
|
||||||
|
JObject o = new JObject
|
||||||
|
{
|
||||||
|
{"Application", "udp-generator"},
|
||||||
|
{"Executable", "VP.Common.SvcFrm.Services.Host, Version=29.7.0.0, Culture=neutral, PublicKeyToken=null"},
|
||||||
|
{"RenderedMessage", "Responding to RequestSchedule message from 10.1.230.36 with Ack because: PRJ byte array is null."},
|
||||||
|
{"Team", "Manufacturing Software"},
|
||||||
|
{"RecordNumber", i},
|
||||||
|
{"Host", hostName},
|
||||||
|
{"UtcTimestamp", DateTime.UtcNow.ToString("o")},
|
||||||
|
{"Type", "VP.Fulfillment.Direct.Initialization.LogWrapper"},
|
||||||
|
{"Message", "Testgenerator udp message " + DateTime.UtcNow.ToString("o")},
|
||||||
|
{"Index", "logstash"}
|
||||||
|
};
|
||||||
|
|
||||||
|
string hashedString = "";
|
||||||
|
foreach(var key in o)
|
||||||
|
{
|
||||||
|
hashedString += key.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
var source = ASCIIEncoding.ASCII.GetBytes(hashedString);
|
||||||
|
var md5 = new MD5CryptoServiceProvider().ComputeHash(source);
|
||||||
|
var hash = string.Concat(md5.Select(x => x.ToString("X2")));
|
||||||
|
|
||||||
|
o["md5"] = hash;
|
||||||
|
|
||||||
|
byte[] sendbuf = Encoding.UTF8.GetBytes(o.ToString(Formatting.None));
|
||||||
|
IPEndPoint ep = new IPEndPoint(broadcast, parms.Port);
|
||||||
|
s.SendTo(sendbuf, ep);
|
||||||
|
|
||||||
|
if (i % 1000 == 0)
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Sent {0} of {1} messages", i, parms.NumMessages);
|
||||||
|
|
||||||
|
Thread.Sleep(parms.SleepTimeMilliseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Finished UDP Generation");
|
||||||
|
|
||||||
|
return parms.NumMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
45
TimberWinR.TestGenerator/default.json
Normal file
45
TimberWinR.TestGenerator/default.json
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"TimberWinR": {
|
||||||
|
"Inputs": {
|
||||||
|
"Udp": [
|
||||||
|
{
|
||||||
|
"_comment": "Output from NLog",
|
||||||
|
"port": 5140
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TailFiles": [
|
||||||
|
{
|
||||||
|
"interval": 5,
|
||||||
|
"logSource": "log files",
|
||||||
|
"location": "*.jlog",
|
||||||
|
"recurse": -1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Filters": [
|
||||||
|
{
|
||||||
|
"grok": {
|
||||||
|
"condition": "\"[EventTypeName]\" == \"Information Event\"",
|
||||||
|
"match": [
|
||||||
|
"Text",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"drop": "true"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Outputs": {
|
||||||
|
"Redis": [
|
||||||
|
{
|
||||||
|
"_comment": "Change the host to your Redis instance",
|
||||||
|
"port": 6379,
|
||||||
|
"batch_count": 500,
|
||||||
|
"threads": 2,
|
||||||
|
"host": [
|
||||||
|
"tstlexiceapp006.mycompany.svc"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
TimberWinR.TestGenerator/packages.config
Normal file
10
TimberWinR.TestGenerator/packages.config
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="CommandLineParser" version="1.9.71" targetFramework="net40" />
|
||||||
|
<package id="Newtonsoft.Json" version="6.0.8" targetFramework="net40" />
|
||||||
|
<package id="NLog" version="3.2.0.0" targetFramework="net40" />
|
||||||
|
<package id="ServiceStack.Common.Signed" version="4.0.38" targetFramework="net40" />
|
||||||
|
<package id="ServiceStack.Interfaces" version="4.0.38" targetFramework="net40" />
|
||||||
|
<package id="ServiceStack.Redis.Signed" version="4.0.38" targetFramework="net40" />
|
||||||
|
<package id="ServiceStack.Text.Signed" version="4.0.38" targetFramework="net40" />
|
||||||
|
</packages>
|
||||||
20
TimberWinR.TestGenerator/results1.json
Normal file
20
TimberWinR.TestGenerator/results1.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"Results": {
|
||||||
|
"Inputs": [
|
||||||
|
{
|
||||||
|
"taillog": {
|
||||||
|
"test1: message sent count": "[messages] == 7404",
|
||||||
|
"test2: average cpu": "[avgCpuUsage] <= 30",
|
||||||
|
"test3: maximum memory": "[maxMemUsage] <= 30"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"udp": {
|
||||||
|
"test1: message sent count": "[messages] == 1234",
|
||||||
|
"test2: average cpu": "[avgCpuUsage] <= 30",
|
||||||
|
"test3: maximum memory": "[maxMemUsage] <= 30"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
20
TimberWinR.TestGenerator/results2.json
Normal file
20
TimberWinR.TestGenerator/results2.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"Results": {
|
||||||
|
"Inputs": [
|
||||||
|
{
|
||||||
|
"taillog": {
|
||||||
|
"test1: message sent count": "[messages] == 7404",
|
||||||
|
"test2: average cpu": "[avgCpuUsage] <= 30",
|
||||||
|
"test3: maximum memory": "[maxMemUsage] <= 20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"udp": {
|
||||||
|
"test1: message sent count": "[messages] == 1234",
|
||||||
|
"test2: average cpu": "[avgCpuUsage] <= 30",
|
||||||
|
"test3: maximum memory": "[maxMemUsage] <= 20"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
20
TimberWinR.TestGenerator/results3.json
Normal file
20
TimberWinR.TestGenerator/results3.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"Results": {
|
||||||
|
"Inputs": [
|
||||||
|
{
|
||||||
|
"taillog": {
|
||||||
|
"test1: message sent count": "[messages] == 7404",
|
||||||
|
"test2: average cpu": "[avgCpuUsage] <= 30",
|
||||||
|
"test3: maximum memory": "[maxMemUsage] <= 15"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcp": {
|
||||||
|
"test4: message sent count": "[messages] == 1234",
|
||||||
|
"test5: average cpu": "[avgCpuUsage] <= 30",
|
||||||
|
"test6: maximum memory": "[maxMemUsage] <= 15"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
20
TimberWinR.TestGenerator/results4.json
Normal file
20
TimberWinR.TestGenerator/results4.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"Results": {
|
||||||
|
"Inputs": [
|
||||||
|
{
|
||||||
|
"taillog": {
|
||||||
|
"test1: message sent count": "[messages] == 7404",
|
||||||
|
"test2: average cpu": "[avgCpuUsage] <= 30",
|
||||||
|
"test3: maximum memory": "[maxMemUsage] <= 15"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tcp": {
|
||||||
|
"test4: message sent count": "[messages] == 1234",
|
||||||
|
"test5: average cpu": "[avgCpuUsage] <= 30",
|
||||||
|
"test6: maximum memory": "[maxMemUsage] <= 15"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
13
TimberWinR.TestGenerator/results5.json
Normal file
13
TimberWinR.TestGenerator/results5.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"Results": {
|
||||||
|
"Inputs": [
|
||||||
|
{
|
||||||
|
"udp": {
|
||||||
|
"test1: message sent count": "[messages] == 80000",
|
||||||
|
"test2: average cpu": "[avgCpuUsage] <= 30",
|
||||||
|
"test3: maximum memory": "[maxMemUsage] <= 30"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
13
TimberWinR.TestGenerator/results7.json
Normal file
13
TimberWinR.TestGenerator/results7.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"Results": {
|
||||||
|
"Inputs": [
|
||||||
|
{
|
||||||
|
"udp": {
|
||||||
|
"test1: message sent count": "[messages] == 80000",
|
||||||
|
"test2: average cpu": "[avgCpuUsage] <= 30",
|
||||||
|
"test3: maximum memory": "[maxMemUsage] <= 30"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
2224
TimberWinR.TestGenerator/sample-apache.log
Normal file
2224
TimberWinR.TestGenerator/sample-apache.log
Normal file
File diff suppressed because it is too large
Load Diff
50
TimberWinR.TestGenerator/test1-twconfig.json
Normal file
50
TimberWinR.TestGenerator/test1-twconfig.json
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
"TimberWinR": {
|
||||||
|
"Inputs": {
|
||||||
|
"Udp": [
|
||||||
|
{
|
||||||
|
"_comment": "Output from NLog",
|
||||||
|
"port": 5140
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TailFiles": [
|
||||||
|
{
|
||||||
|
"interval": 5,
|
||||||
|
"logSource": "log files",
|
||||||
|
"location": "*.jlog",
|
||||||
|
"recurse": -1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Filters": [
|
||||||
|
{
|
||||||
|
"grok": {
|
||||||
|
"condition": "\"[EventTypeName]\" == \"Information Event\"",
|
||||||
|
"match": [
|
||||||
|
"Text",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"drop": "true"
|
||||||
|
},
|
||||||
|
"json": {
|
||||||
|
"type": "Win32-TailFile",
|
||||||
|
"source": "Text",
|
||||||
|
"promote": "Text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Outputs": {
|
||||||
|
"Redis": [
|
||||||
|
{
|
||||||
|
"_comment": "Change the host to your Redis instance",
|
||||||
|
"port": 6379,
|
||||||
|
"batch_count": 500,
|
||||||
|
"threads": 1,
|
||||||
|
"host": [
|
||||||
|
"tstlexiceapp006.mycompany.svc"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
TimberWinR.TestGenerator/test1.json
Normal file
16
TimberWinR.TestGenerator/test1.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"test": "Test 1",
|
||||||
|
"arguments": {
|
||||||
|
"--start": "",
|
||||||
|
"--testFile": "test1.json",
|
||||||
|
"--testDir": "test1",
|
||||||
|
"--timberWinRConfig": "test1-twconfig.json",
|
||||||
|
"--numMessages": 1234,
|
||||||
|
"--logLevel": "debug",
|
||||||
|
"--udp-host": "localhost",
|
||||||
|
"--udp": "5140",
|
||||||
|
"--jroll": ["r1.jlog", "r2.jlog"],
|
||||||
|
"--json": ["1.jlog", "2.jlog", "3.jlog", "4.jlog"],
|
||||||
|
"--resultsFile": "results1.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
45
TimberWinR.TestGenerator/test2-tw.json
Normal file
45
TimberWinR.TestGenerator/test2-tw.json
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"TimberWinR": {
|
||||||
|
"Inputs": {
|
||||||
|
"Udp": [
|
||||||
|
{
|
||||||
|
"_comment": "Output from NLog",
|
||||||
|
"port": 5140
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Logs": [
|
||||||
|
{
|
||||||
|
"interval": 5,
|
||||||
|
"logSource": "log files",
|
||||||
|
"location": "*.jlog",
|
||||||
|
"recurse": -1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Filters": [
|
||||||
|
{
|
||||||
|
"grok": {
|
||||||
|
"condition": "\"[EventTypeName]\" == \"Information Event\"",
|
||||||
|
"match": [
|
||||||
|
"Text",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"drop": "true"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Outputs": {
|
||||||
|
"Redis": [
|
||||||
|
{
|
||||||
|
"_comment": "Change the host to your Redis instance",
|
||||||
|
"port": 6379,
|
||||||
|
"batch_count": 500,
|
||||||
|
"threads": 2,
|
||||||
|
"host": [
|
||||||
|
"tstlexiceapp006.mycompany.svc"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
TimberWinR.TestGenerator/test2.json
Normal file
15
TimberWinR.TestGenerator/test2.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"test": "Test 2",
|
||||||
|
"arguments": {
|
||||||
|
"--start": "",
|
||||||
|
"--testFile": "test2.json",
|
||||||
|
"--testDir": "test2",
|
||||||
|
"--timberWinRConfig": "test2-tw.json",
|
||||||
|
"--numMessages": 1234,
|
||||||
|
"--logLevel": "debug",
|
||||||
|
"--udp": "5140",
|
||||||
|
"--jroll": ["r1.jlog", "r2.jlog"],
|
||||||
|
"--json": ["1.jlog", "2.jlog", "3.jlog", "4.jlog"],
|
||||||
|
"--resultsFile": "results2.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
50
TimberWinR.TestGenerator/test3-tw.json
Normal file
50
TimberWinR.TestGenerator/test3-tw.json
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
"TimberWinR": {
|
||||||
|
"Inputs": {
|
||||||
|
"Udp": [
|
||||||
|
{
|
||||||
|
"_comment": "Output from NLog",
|
||||||
|
"port": 5140
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TailFiles": [
|
||||||
|
{
|
||||||
|
"interval": 5,
|
||||||
|
"logSource": "log files",
|
||||||
|
"location": "d:\\logs\\sta\\sta.log",
|
||||||
|
"recurse": -1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Filters": [
|
||||||
|
{
|
||||||
|
"grok": {
|
||||||
|
"condition": "\"[EventTypeName]\" == \"Information Event\"",
|
||||||
|
"match": [
|
||||||
|
"Text",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"drop": "true"
|
||||||
|
},
|
||||||
|
"json": {
|
||||||
|
"type": "Win32-TailFile",
|
||||||
|
"source": "Text",
|
||||||
|
"promote": "Text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Outputs": {
|
||||||
|
"Redis": [
|
||||||
|
{
|
||||||
|
"_comment": "Change the host to your Redis instance",
|
||||||
|
"port": 6379,
|
||||||
|
"batch_count": 500,
|
||||||
|
"threads": 2,
|
||||||
|
"host": [
|
||||||
|
"tstlexiceapp006.mycompany.svc"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
TimberWinR.TestGenerator/test3.json
Normal file
12
TimberWinR.TestGenerator/test3.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"test": "Test 3",
|
||||||
|
"arguments": {
|
||||||
|
"--start": "",
|
||||||
|
"--testFile": "test3.json",
|
||||||
|
"--testDir": "test3",
|
||||||
|
"--timberWinRConfig": "test3-tw.json",
|
||||||
|
"--numMessages": 1234,
|
||||||
|
"--logLevel": "debug",
|
||||||
|
"--resultsFile": "results3.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
50
TimberWinR.TestGenerator/test4-tw.json
Normal file
50
TimberWinR.TestGenerator/test4-tw.json
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
"TimberWinR": {
|
||||||
|
"Inputs": {
|
||||||
|
"Udp": [
|
||||||
|
{
|
||||||
|
"_comment": "Output from NLog",
|
||||||
|
"port": 5140
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TailFiles": [
|
||||||
|
{
|
||||||
|
"interval": 5,
|
||||||
|
"logSource": "log files",
|
||||||
|
"location": "d:\\logs\\sta\\sta.log",
|
||||||
|
"recurse": -1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Filters": [
|
||||||
|
{
|
||||||
|
"grok": {
|
||||||
|
"condition": "\"[EventTypeName]\" == \"Information Event\"",
|
||||||
|
"match": [
|
||||||
|
"Text",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"drop": "true"
|
||||||
|
},
|
||||||
|
"json": {
|
||||||
|
"type": "Win32-TailFile",
|
||||||
|
"source": "Text",
|
||||||
|
"promote": "Text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Outputs": {
|
||||||
|
"Redis": [
|
||||||
|
{
|
||||||
|
"_comment": "Change the host to your Redis instance",
|
||||||
|
"port": 6379,
|
||||||
|
"batch_count": 500,
|
||||||
|
"threads": 2,
|
||||||
|
"host": [
|
||||||
|
"tstlexiceapp006.mycompany.svc"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
TimberWinR.TestGenerator/test4.json
Normal file
11
TimberWinR.TestGenerator/test4.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"test": "Test 4",
|
||||||
|
"arguments": {
|
||||||
|
"--testFile": "test4.json",
|
||||||
|
"--testDir": "test4",
|
||||||
|
"--timberWinRConfig": "test4-tw.json",
|
||||||
|
"--numMessages": 1234,
|
||||||
|
"--logLevel": "debug",
|
||||||
|
"--resultsFile": "results4.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
36
TimberWinR.TestGenerator/test5-twconfig.json
Normal file
36
TimberWinR.TestGenerator/test5-twconfig.json
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"TimberWinR": {
|
||||||
|
"Inputs": {
|
||||||
|
"Udp": [
|
||||||
|
{
|
||||||
|
"_comment": "Output from NLog",
|
||||||
|
"port": 5140,
|
||||||
|
"add_field": [
|
||||||
|
"Environment",
|
||||||
|
"PLANT_TST_TIMBERWINR"
|
||||||
|
],
|
||||||
|
"rename": [
|
||||||
|
"Type",
|
||||||
|
"type"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TailFiles": [
|
||||||
|
{
|
||||||
|
"interval": 5,
|
||||||
|
"logSource": "log files",
|
||||||
|
"location": "*.jlog",
|
||||||
|
"recurse": -1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Outputs": {
|
||||||
|
"File": [
|
||||||
|
{
|
||||||
|
"format": "indented",
|
||||||
|
"file_name": "test5_output.txt"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
TimberWinR.TestGenerator/test5.json
Normal file
15
TimberWinR.TestGenerator/test5.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"test": "Test 5",
|
||||||
|
"arguments": {
|
||||||
|
"--start": "",
|
||||||
|
"--testFile": "test5.json",
|
||||||
|
"--testDir": "test5",
|
||||||
|
"--timberWinRConfig": "test5-twconfig.json",
|
||||||
|
"--numMessages": 80000,
|
||||||
|
"--logLevel": "debug",
|
||||||
|
"--udp-host": "localhost",
|
||||||
|
"--udp": "5140",
|
||||||
|
"--udp-rate": 1,
|
||||||
|
"--resultsFile": "results5.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
37
TimberWinR.TestGenerator/test7-tw.json
Normal file
37
TimberWinR.TestGenerator/test7-tw.json
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"TimberWinR": {
|
||||||
|
"Inputs": {
|
||||||
|
"TailFiles": [
|
||||||
|
{
|
||||||
|
"interval": 5,
|
||||||
|
"logSource": "apache log files",
|
||||||
|
"location": "..\\sample-apache.log",
|
||||||
|
"recurse": -1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Filters": [
|
||||||
|
{
|
||||||
|
"grok": {
|
||||||
|
"type": "Win32-TailLog",
|
||||||
|
"match": [
|
||||||
|
"Text",
|
||||||
|
"%{COMBINEDAPACHELOG}"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Outputs": {
|
||||||
|
"StatsD": [
|
||||||
|
{
|
||||||
|
"type": "Win32-TailLog",
|
||||||
|
"namespace": "timberwinrtest",
|
||||||
|
"port": 8125,
|
||||||
|
"host": "devlexicesnu003.mycompany.svc",
|
||||||
|
"increment": ["apache.response.%{response}"],
|
||||||
|
"count": ["apache.bytes", "%{bytes}"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
TimberWinR.TestGenerator/test7.json
Normal file
13
TimberWinR.TestGenerator/test7.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"test": "Test 7",
|
||||||
|
"arguments": {
|
||||||
|
"--totalMessages": 2223,
|
||||||
|
"--start": "",
|
||||||
|
"--testFile": "test7.json",
|
||||||
|
"--testDir": "test7",
|
||||||
|
"--timberWinRConfig": "test7-tw.json",
|
||||||
|
"--numMessages": 1234,
|
||||||
|
"--logLevel": "debug",
|
||||||
|
"--resultsFile": "results7.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -85,7 +85,7 @@ namespace TimberWinR.UnitTests
|
|||||||
[{
|
[{
|
||||||
""host"":
|
""host"":
|
||||||
[
|
[
|
||||||
""logaggregator.vistaprint.svc""
|
""logaggregator.mycompany.svc""
|
||||||
]
|
]
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
@@ -94,7 +94,7 @@ namespace TimberWinR.UnitTests
|
|||||||
|
|
||||||
|
|
||||||
Configuration c = Configuration.FromString(redisJson);
|
Configuration c = Configuration.FromString(redisJson);
|
||||||
RedisOutput redis = c.RedisOutputs.First() as RedisOutput;
|
RedisOutputParameters redis = c.RedisOutputs.First() as RedisOutputParameters;
|
||||||
Assert.IsTrue(redis.Host.Length >= 1);
|
Assert.IsTrue(redis.Host.Length >= 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
126
TimberWinR.UnitTests/FakeRediServer.cs
Normal file
126
TimberWinR.UnitTests/FakeRediServer.cs
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using TimberWinR.Parser;
|
||||||
|
using System.Text;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace TimberWinR.UnitTests
|
||||||
|
{
|
||||||
|
// Class which implements a Fake redis server for test purposes.
|
||||||
|
class FakeRediServer
|
||||||
|
{
|
||||||
|
private readonly System.Net.Sockets.TcpListener _tcpListenerV4;
|
||||||
|
private readonly System.Net.Sockets.TcpListener _tcpListenerV6;
|
||||||
|
private Thread _listenThreadV4;
|
||||||
|
private Thread _listenThreadV6;
|
||||||
|
private readonly int _port;
|
||||||
|
private CancellationToken _cancelToken;
|
||||||
|
private bool _shutdown;
|
||||||
|
|
||||||
|
public FakeRediServer(CancellationToken cancelToken, int port = 6379)
|
||||||
|
{
|
||||||
|
_port = port;
|
||||||
|
_cancelToken = cancelToken;
|
||||||
|
_shutdown = false;
|
||||||
|
|
||||||
|
_tcpListenerV6 = new System.Net.Sockets.TcpListener(IPAddress.IPv6Any, port);
|
||||||
|
_tcpListenerV4 = new System.Net.Sockets.TcpListener(IPAddress.Any, port);
|
||||||
|
|
||||||
|
_listenThreadV4 = new Thread(new ParameterizedThreadStart(ListenForClients));
|
||||||
|
_listenThreadV4.Start(_tcpListenerV4);
|
||||||
|
|
||||||
|
_listenThreadV6 = new Thread(new ParameterizedThreadStart(ListenForClients));
|
||||||
|
_listenThreadV6.Start(_tcpListenerV6);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Shutdown()
|
||||||
|
{
|
||||||
|
_shutdown = true;
|
||||||
|
this._tcpListenerV4.Stop();
|
||||||
|
this._tcpListenerV6.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void ListenForClients(object olistener)
|
||||||
|
{
|
||||||
|
System.Net.Sockets.TcpListener listener = olistener as System.Net.Sockets.TcpListener;
|
||||||
|
|
||||||
|
listener.Start();
|
||||||
|
|
||||||
|
|
||||||
|
while (!_cancelToken.IsCancellationRequested && !_shutdown)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//blocks until a client has connected to the server
|
||||||
|
TcpClient client = listener.AcceptTcpClient();
|
||||||
|
|
||||||
|
// Wait for a client, spin up a thread.
|
||||||
|
var clientThread = new Thread(new ParameterizedThreadStart(HandleNewClient));
|
||||||
|
clientThread.Start(client);
|
||||||
|
}
|
||||||
|
catch (SocketException ex)
|
||||||
|
{
|
||||||
|
if (ex.SocketErrorCode == SocketError.Interrupted)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleNewClient(object client)
|
||||||
|
{
|
||||||
|
var tcpClient = (TcpClient)client;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
NetworkStream clientStream = tcpClient.GetStream();
|
||||||
|
int i;
|
||||||
|
Byte[] bytes = new Byte[16535];
|
||||||
|
String data = null;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Loop to receive all the data sent by the client.
|
||||||
|
while ((i = clientStream.Read(bytes, 0, bytes.Length)) != 0)
|
||||||
|
{
|
||||||
|
// Translate data bytes to a ASCII string.
|
||||||
|
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
|
||||||
|
//System.Diagnostics.Debug.WriteLine(String.Format("Received: {0}", data));
|
||||||
|
|
||||||
|
// Process the data sent by the client.
|
||||||
|
data = ":1000\r\n";
|
||||||
|
|
||||||
|
byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);
|
||||||
|
|
||||||
|
// Send back a response.
|
||||||
|
clientStream.Write(msg, 0, msg.Length);
|
||||||
|
// System.Diagnostics.Debug.WriteLine(String.Format("Sent: {0}", data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
} while (true);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine(ex.ToString());
|
||||||
|
}
|
||||||
|
tcpClient.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessJson(JObject json)
|
||||||
|
{
|
||||||
|
Console.WriteLine(json.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
58
TimberWinR.UnitTests/GeoIPFilterTests.cs
Normal file
58
TimberWinR.UnitTests/GeoIPFilterTests.cs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using TimberWinR.Parser;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace TimberWinR.UnitTests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class GeoIPFilterTests
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestDropConditions()
|
||||||
|
{
|
||||||
|
JObject jsonInputLine1 = new JObject
|
||||||
|
{
|
||||||
|
{"type", "Win32-FileLog"},
|
||||||
|
{"IP", "8.8.8.8"}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
string jsonFilter = @"{
|
||||||
|
""TimberWinR"":{
|
||||||
|
""Filters"":[
|
||||||
|
{
|
||||||
|
""geoip"":{
|
||||||
|
""type"": ""Win32-FileLog"",
|
||||||
|
""target"": ""mygeoip"",
|
||||||
|
""source"": ""IP""
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
// Positive Tests
|
||||||
|
Configuration c = Configuration.FromString(jsonFilter);
|
||||||
|
GeoIP jf = c.Filters.First() as GeoIP;
|
||||||
|
Assert.IsTrue(jf.Apply(jsonInputLine1));
|
||||||
|
|
||||||
|
JObject stuff = jsonInputLine1["mygeoip"] as JObject;
|
||||||
|
Assert.IsNotNull(stuff);
|
||||||
|
|
||||||
|
Assert.AreEqual("8.8.8.8", stuff["ip"].ToString());
|
||||||
|
Assert.AreEqual("US", stuff["country_code2"].ToString());
|
||||||
|
Assert.AreEqual("United States", stuff["country_name"].ToString());
|
||||||
|
Assert.AreEqual("CA", stuff["region_name"].ToString());
|
||||||
|
Assert.AreEqual("Mountain View", stuff["city_name"].ToString());
|
||||||
|
Assert.AreEqual("California", stuff["real_region_name"].ToString());
|
||||||
|
Assert.AreEqual(37.386f, (float)stuff["latitude"]);
|
||||||
|
Assert.AreEqual(-122.0838f, (float) stuff["longitude"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@ namespace TimberWinR.UnitTests
|
|||||||
{"Index", 7},
|
{"Index", 7},
|
||||||
{"Text", null},
|
{"Text", null},
|
||||||
{"type", "Win32-FileLog"},
|
{"type", "Win32-FileLog"},
|
||||||
{"ComputerName", "dev.vistaprint.net"}
|
{"ComputerName", "dev.mycompany.net"}
|
||||||
};
|
};
|
||||||
|
|
||||||
string grokJson = @"{
|
string grokJson = @"{
|
||||||
@@ -30,7 +30,7 @@ namespace TimberWinR.UnitTests
|
|||||||
""Filters"":[
|
""Filters"":[
|
||||||
{
|
{
|
||||||
""grok"":{
|
""grok"":{
|
||||||
""condition"": ""[type] == \""Win32-FileLog\"""",
|
""type"": ""Win32-FileLog"",
|
||||||
""match"":[
|
""match"":[
|
||||||
""Text"",
|
""Text"",
|
||||||
""""
|
""""
|
||||||
@@ -55,7 +55,7 @@ namespace TimberWinR.UnitTests
|
|||||||
Assert.IsTrue(grok.Apply(json));
|
Assert.IsTrue(grok.Apply(json));
|
||||||
|
|
||||||
// Verify host field added
|
// Verify host field added
|
||||||
Assert.AreEqual(json["host"].ToString(), "dev.vistaprint.net");
|
Assert.AreEqual(json["host"].ToString(), "dev.mycompany.net");
|
||||||
|
|
||||||
// Verify two tags added
|
// Verify two tags added
|
||||||
Assert.AreEqual(json["tags"][0].ToString(), "rn_7");
|
Assert.AreEqual(json["tags"][0].ToString(), "rn_7");
|
||||||
@@ -69,9 +69,9 @@ namespace TimberWinR.UnitTests
|
|||||||
{
|
{
|
||||||
{"LogFilename", @"C:\\Logs1\\test1.log"},
|
{"LogFilename", @"C:\\Logs1\\test1.log"},
|
||||||
{"Index", 7},
|
{"Index", 7},
|
||||||
{"Text", null},
|
{"Text", "crap"},
|
||||||
{"type", "Win32-FileLog"},
|
{"type", "Win32-FileLog"},
|
||||||
{"ComputerName", "dev.vistaprint.net"}
|
{"ComputerName", "dev.mycompany.net"}
|
||||||
};
|
};
|
||||||
|
|
||||||
string grokJson = @"{
|
string grokJson = @"{
|
||||||
@@ -79,10 +79,10 @@ namespace TimberWinR.UnitTests
|
|||||||
""Filters"":[
|
""Filters"":[
|
||||||
{
|
{
|
||||||
""grok"":{
|
""grok"":{
|
||||||
""condition"": ""[type] == \""Win32-FileLog\"""",
|
""condition"": ""\""[type]\"" == \""Win32-FileLog\"""",
|
||||||
""match"":[
|
""match"":[
|
||||||
""Text"",
|
""Text"",
|
||||||
""""
|
""crap""
|
||||||
],
|
],
|
||||||
""remove_field"":[
|
""remove_field"":[
|
||||||
""Index"",
|
""Index"",
|
||||||
@@ -106,6 +106,108 @@ namespace TimberWinR.UnitTests
|
|||||||
Assert.IsNull(json["LogFilename"]);
|
Assert.IsNull(json["LogFilename"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDropConditions()
|
||||||
|
{
|
||||||
|
JObject json1 = new JObject
|
||||||
|
{
|
||||||
|
{"LogFilename", @"C:\\Logs1\\test1.log"},
|
||||||
|
{"EventType", 1},
|
||||||
|
{"Index", 7},
|
||||||
|
{"Text", null},
|
||||||
|
{
|
||||||
|
"tags", new JArray
|
||||||
|
{
|
||||||
|
"tag1",
|
||||||
|
"tag2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{"type", "Win32-FileLog"},
|
||||||
|
{"ComputerName", "dev.mycompany.net"}
|
||||||
|
};
|
||||||
|
|
||||||
|
JObject json2 = new JObject
|
||||||
|
{
|
||||||
|
{"LogFilename", @"C:\\Logs1\\test1.log"},
|
||||||
|
{"EventType", 2},
|
||||||
|
{"Index", 7},
|
||||||
|
{"Text", null},
|
||||||
|
{
|
||||||
|
"tags", new JArray
|
||||||
|
{
|
||||||
|
"tag1",
|
||||||
|
"tag2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{"type", "Win32-FileLog"},
|
||||||
|
{"ComputerName", "dev.mycompany.net"}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
string grokJsonDropIf1 = @"{
|
||||||
|
""TimberWinR"":{
|
||||||
|
""Filters"":[
|
||||||
|
{
|
||||||
|
""grok"":{
|
||||||
|
""condition"": ""[EventType] == 1"",
|
||||||
|
""drop"": ""true"",
|
||||||
|
""match"":[
|
||||||
|
""Text"",
|
||||||
|
""""
|
||||||
|
],
|
||||||
|
""remove_tag"":[
|
||||||
|
""tag1""
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
string grokJsonDropIfNot1 = @"{
|
||||||
|
""TimberWinR"":{
|
||||||
|
""Filters"":[
|
||||||
|
{
|
||||||
|
""grok"":{
|
||||||
|
""condition"": ""[EventType] != 1"",
|
||||||
|
""drop"": ""true"",
|
||||||
|
""match"":[
|
||||||
|
""Text"",
|
||||||
|
""""
|
||||||
|
],
|
||||||
|
""remove_tag"":[
|
||||||
|
// ""tag1""
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
|
||||||
|
// Positive Tests
|
||||||
|
Configuration c = Configuration.FromString(grokJsonDropIf1);
|
||||||
|
Grok grok = c.Filters.First() as Grok;
|
||||||
|
Assert.IsFalse(grok.Apply(json1));
|
||||||
|
|
||||||
|
c = Configuration.FromString(grokJsonDropIf1);
|
||||||
|
grok = c.Filters.First() as Grok;
|
||||||
|
Assert.IsTrue(grok.Apply(json2));
|
||||||
|
|
||||||
|
// Negative Tests
|
||||||
|
c = Configuration.FromString(grokJsonDropIfNot1);
|
||||||
|
grok = c.Filters.First() as Grok;
|
||||||
|
Assert.IsFalse(grok.Apply(json2));
|
||||||
|
|
||||||
|
c = Configuration.FromString(grokJsonDropIfNot1);
|
||||||
|
grok = c.Filters.First() as Grok;
|
||||||
|
Assert.IsTrue(grok.Apply(json1));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestConditions()
|
public void TestConditions()
|
||||||
{
|
{
|
||||||
@@ -121,7 +223,8 @@ namespace TimberWinR.UnitTests
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{"type", "Win32-FileLog"},
|
{"type", "Win32-FileLog"},
|
||||||
{"ComputerName", "dev.vistaprint.net"}
|
{"Type", "Win32-MyType"},
|
||||||
|
{"ComputerName", "dev.mycompany.net"}
|
||||||
};
|
};
|
||||||
|
|
||||||
string grokJson1 = @"{
|
string grokJson1 = @"{
|
||||||
@@ -129,7 +232,7 @@ namespace TimberWinR.UnitTests
|
|||||||
""Filters"":[
|
""Filters"":[
|
||||||
{
|
{
|
||||||
""grok"":{
|
""grok"":{
|
||||||
""condition"": ""[type] == \""Win32-FileLog\"""",
|
""condition"": ""\""[type]\"" == \""Win32-FileLog\"""",
|
||||||
""match"":[
|
""match"":[
|
||||||
""Text"",
|
""Text"",
|
||||||
""""
|
""""
|
||||||
@@ -147,7 +250,7 @@ namespace TimberWinR.UnitTests
|
|||||||
""Filters"":[
|
""Filters"":[
|
||||||
{
|
{
|
||||||
""grok"":{
|
""grok"":{
|
||||||
""condition"": ""[type].Contains(\""Win32-FileLog\"")"",
|
""condition"": ""\""[type]\"".Contains(\""Win32-FileLog\"")"",
|
||||||
""match"":[
|
""match"":[
|
||||||
""Text"",
|
""Text"",
|
||||||
""""
|
""""
|
||||||
@@ -166,7 +269,7 @@ namespace TimberWinR.UnitTests
|
|||||||
""Filters"":[
|
""Filters"":[
|
||||||
{
|
{
|
||||||
""grok"":{
|
""grok"":{
|
||||||
""condition"": ""[type].Contains(\""Win32-Filelog\"")"",
|
""condition"": ""\""[type]\"".Contains(\""Win32-Filelog\"")"",
|
||||||
""match"":[
|
""match"":[
|
||||||
""Text"",
|
""Text"",
|
||||||
""""
|
""""
|
||||||
@@ -179,11 +282,35 @@ namespace TimberWinR.UnitTests
|
|||||||
}
|
}
|
||||||
}";
|
}";
|
||||||
|
|
||||||
// Positive Tests
|
string grokJson4 = @"{
|
||||||
Configuration c = Configuration.FromString(grokJson1);
|
""TimberWinR"":{
|
||||||
|
""Filters"":[
|
||||||
|
{
|
||||||
|
""grok"":{
|
||||||
|
""condition"": ""!\""[Type]\"".StartsWith(\""[\"") && !\""[Type]\"".EndsWith(\""]\"") && (\""[type]\"" == \""Win32-FileLog\"")"",
|
||||||
|
""match"":[
|
||||||
|
""Text"",
|
||||||
|
""""
|
||||||
|
],
|
||||||
|
""remove_tag"":[
|
||||||
|
""tag1""
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
|
||||||
|
Configuration c = Configuration.FromString(grokJson4);
|
||||||
Grok grok = c.Filters.First() as Grok;
|
Grok grok = c.Filters.First() as Grok;
|
||||||
Assert.IsTrue(grok.Apply(json));
|
Assert.IsTrue(grok.Apply(json));
|
||||||
|
|
||||||
|
|
||||||
|
// Positive Tests
|
||||||
|
c = Configuration.FromString(grokJson1);
|
||||||
|
grok = c.Filters.First() as Grok;
|
||||||
|
Assert.IsTrue(grok.Apply(json));
|
||||||
|
|
||||||
c = Configuration.FromString(grokJson2);
|
c = Configuration.FromString(grokJson2);
|
||||||
grok = c.Filters.First() as Grok;
|
grok = c.Filters.First() as Grok;
|
||||||
Assert.IsTrue(grok.Apply(json));
|
Assert.IsTrue(grok.Apply(json));
|
||||||
@@ -191,7 +318,7 @@ namespace TimberWinR.UnitTests
|
|||||||
// Negative Test
|
// Negative Test
|
||||||
c = Configuration.FromString(grokJson3);
|
c = Configuration.FromString(grokJson3);
|
||||||
grok = c.Filters.First() as Grok;
|
grok = c.Filters.First() as Grok;
|
||||||
Assert.IsFalse(grok.Apply(json));
|
Assert.IsTrue(grok.Apply(json));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@@ -209,7 +336,7 @@ namespace TimberWinR.UnitTests
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{"type", "Win32-FileLog"},
|
{"type", "Win32-FileLog"},
|
||||||
{"ComputerName", "dev.vistaprint.net"}
|
{"ComputerName", "dev.mycompany.net"}
|
||||||
};
|
};
|
||||||
|
|
||||||
string grokJson = @"{
|
string grokJson = @"{
|
||||||
@@ -217,7 +344,7 @@ namespace TimberWinR.UnitTests
|
|||||||
""Filters"":[
|
""Filters"":[
|
||||||
{
|
{
|
||||||
""grok"":{
|
""grok"":{
|
||||||
""condition"": ""[type] == \""Win32-FileLog\"""",
|
""condition"": ""\""[type]\"" == \""Win32-FileLog\"""",
|
||||||
""match"":[
|
""match"":[
|
||||||
""Text"",
|
""Text"",
|
||||||
""""
|
""""
|
||||||
@@ -248,7 +375,7 @@ namespace TimberWinR.UnitTests
|
|||||||
""Filters"":[
|
""Filters"":[
|
||||||
{
|
{
|
||||||
""grok"":{
|
""grok"":{
|
||||||
""condition"": ""[type] == \""Win32-FileLog\"""",
|
""condition"": ""\""[type]\"" == \""Win32-FileLog\"""",
|
||||||
""match"":[
|
""match"":[
|
||||||
""Text"",
|
""Text"",
|
||||||
""""
|
""""
|
||||||
@@ -274,7 +401,7 @@ namespace TimberWinR.UnitTests
|
|||||||
""Filters"":[
|
""Filters"":[
|
||||||
{
|
{
|
||||||
""grok"":{
|
""grok"":{
|
||||||
""condition"": ""[type] == \""Win32-FileLog\"""",
|
""condition"": ""\""[type]\"" == \""Win32-FileLog\"""",
|
||||||
""match"":[
|
""match"":[
|
||||||
""Text"",
|
""Text"",
|
||||||
""""
|
""""
|
||||||
@@ -300,7 +427,7 @@ namespace TimberWinR.UnitTests
|
|||||||
""Filters"":[
|
""Filters"":[
|
||||||
{
|
{
|
||||||
""grok"":{
|
""grok"":{
|
||||||
""condition"": ""[type] == \""Win32-FileLog\"""",
|
""condition"": ""\""[type]\"" == \""Win32-FileLog\"""",
|
||||||
""match"":[
|
""match"":[
|
||||||
""Text"",
|
""Text"",
|
||||||
""""
|
""""
|
||||||
@@ -330,7 +457,7 @@ namespace TimberWinR.UnitTests
|
|||||||
""Filters"":[
|
""Filters"":[
|
||||||
{
|
{
|
||||||
""grok"":{
|
""grok"":{
|
||||||
""condition"": ""[type] == \""Win32-FileLog\"""",
|
""condition"": ""\""[type]\"" == \""Win32-FileLog\"""",
|
||||||
""match"":[
|
""match"":[
|
||||||
""Text"",
|
""Text"",
|
||||||
""""
|
""""
|
||||||
|
|||||||
132
TimberWinR.UnitTests/JsonFilterTests.cs
Normal file
132
TimberWinR.UnitTests/JsonFilterTests.cs
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using TimberWinR.Parser;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace TimberWinR.UnitTests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class JsonFilterTests
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestDropConditions()
|
||||||
|
{
|
||||||
|
JObject jsonInputLine1 = new JObject
|
||||||
|
{
|
||||||
|
{"type", "Win32-FileLog"},
|
||||||
|
{"ComputerName", "dev.mycompany.net"},
|
||||||
|
{"Text", "{\"Email\":\"james@example.com\",\"Active\":true,\"CreatedDate\":\"2013-01-20T00:00:00Z\",\"Roles\":[\"User\",\"Admin\"]}"}
|
||||||
|
};
|
||||||
|
|
||||||
|
JObject jsonInputLine2 = new JObject
|
||||||
|
{
|
||||||
|
{"type", "Win32-FileLog"},
|
||||||
|
{"ComputerName", "dev.mycompany.net"},
|
||||||
|
{"Text", "{\"Email\":\"james@example.com\",\"Active\":true,\"CreatedDate\":\"2013-01-20T00:00:00Z\",\"Roles\":[\"User\",\"Admin\"]}"}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
string jsonFilter = @"{
|
||||||
|
""TimberWinR"":{
|
||||||
|
""Filters"":[
|
||||||
|
{
|
||||||
|
""json"":{
|
||||||
|
""type"": ""Win32-FileLog"",
|
||||||
|
""target"": ""stuff"",
|
||||||
|
""source"": ""Text""
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
string jsonFilterNoTarget = @"{
|
||||||
|
""TimberWinR"":{
|
||||||
|
""Filters"":[
|
||||||
|
{
|
||||||
|
""json"":{
|
||||||
|
""type"": ""Win32-FileLog"",
|
||||||
|
""source"": ""Text""
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
// Positive Tests
|
||||||
|
Configuration c = Configuration.FromString(jsonFilter);
|
||||||
|
Json jf = c.Filters.First() as Json;
|
||||||
|
Assert.IsTrue(jf.Apply(jsonInputLine1));
|
||||||
|
|
||||||
|
JObject stuff = jsonInputLine1["stuff"] as JObject;
|
||||||
|
Assert.IsNotNull(stuff);
|
||||||
|
|
||||||
|
// 4 fields, Email, Active, CreatedDate, Roles
|
||||||
|
Assert.AreEqual(4, stuff.Count);
|
||||||
|
|
||||||
|
// Now, merge it into the root (starts as 3 fields, ends up with 7 fields)
|
||||||
|
Assert.AreEqual(3, jsonInputLine2.Count);
|
||||||
|
c = Configuration.FromString(jsonFilterNoTarget);
|
||||||
|
jf = c.Filters.First() as Json;
|
||||||
|
Assert.IsTrue(jf.Apply(jsonInputLine2));
|
||||||
|
JObject nostuff = jsonInputLine2["stuff"] as JObject;
|
||||||
|
Assert.IsNull(nostuff);
|
||||||
|
Assert.AreEqual(6, jsonInputLine2.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRenameAndAdds()
|
||||||
|
{
|
||||||
|
JObject jsonInputLine1 = new JObject
|
||||||
|
{
|
||||||
|
{"type", "Win32-FileLog"},
|
||||||
|
{"ComputerName", "dev.mycompany.net"},
|
||||||
|
{"Text", "{\"log4net:Username\" : \"NT AUTHORITY\",\"Email\":\"james@example.com\",\"Active\":true,\"CreatedDate\":\"2013-01-20T00:00:00Z\",\"Roles\":[\"User\",\"Admin\"]}"}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
string jsonFilter = @"{
|
||||||
|
""TimberWinR"":{
|
||||||
|
""Filters"":[
|
||||||
|
{
|
||||||
|
""json"":{
|
||||||
|
""type"": ""Win32-FileLog"",
|
||||||
|
""source"": ""Text"",
|
||||||
|
""promote"": ""Text"",
|
||||||
|
""add_field"":[
|
||||||
|
""test1"",
|
||||||
|
""value1"",
|
||||||
|
""test2"",
|
||||||
|
""value2""
|
||||||
|
],
|
||||||
|
""rename"":[
|
||||||
|
""Active"",
|
||||||
|
""active"",
|
||||||
|
""log4net:Username"",
|
||||||
|
""lusername""
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
|
||||||
|
// Positive Tests
|
||||||
|
Configuration c = Configuration.FromString(jsonFilter);
|
||||||
|
Json jf = c.Filters.First() as Json;
|
||||||
|
Assert.IsTrue(jf.Apply(jsonInputLine1));
|
||||||
|
|
||||||
|
Assert.AreEqual("NT AUTHORITY", jsonInputLine1["lusername"].ToString());
|
||||||
|
Assert.AreEqual("True", jsonInputLine1["active"].ToString());
|
||||||
|
Assert.IsNotNull(jsonInputLine1["test1"]);
|
||||||
|
Assert.IsNotNull(jsonInputLine1["test2"]);
|
||||||
|
Assert.AreEqual("value1", jsonInputLine1["test1"].ToString());
|
||||||
|
Assert.AreEqual("value2", jsonInputLine1["test2"].ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
13
TimberWinR.UnitTests/Multiline1.txt
Normal file
13
TimberWinR.UnitTests/Multiline1.txt
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
multiline1 \
|
||||||
|
ml1_1 \
|
||||||
|
ml1_2 \
|
||||||
|
ml1_2
|
||||||
|
singleline1
|
||||||
|
singleline2
|
||||||
|
multiline2 \
|
||||||
|
ml2_1 \
|
||||||
|
ml2_2
|
||||||
|
multiline3 \
|
||||||
|
ml3_1 \
|
||||||
|
ml3_2
|
||||||
|
singleline3
|
||||||
19
TimberWinR.UnitTests/Multiline2.txt
Normal file
19
TimberWinR.UnitTests/Multiline2.txt
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
2015-01-07 13:14:26,572 TEST DEBUG [THREAD : 25] - Sending message to TServer - tcp://10.1111.11.111:1111
|
||||||
|
'RequestAttachUserData' ('30')
|
||||||
|
message attributes:
|
||||||
|
AttributeConnID [long] = 00890
|
||||||
|
AttributeReferenceID [int] = 88
|
||||||
|
AttributeThisDN [str] = "2214"
|
||||||
|
AttributeUserData [bstr] = KVList:
|
||||||
|
'ActivityID' [str] = "1-XXXXXX"
|
||||||
|
2015-01-07 13:14:26,574 TEST DEBUG [THREAD : 25] - Writing message RequestAttachUserData in 'proxy1' via '.StatePrimary proxy: proxy1'
|
||||||
|
2015-01-07 13:14:26,575 TEST DEBUG [THREAD : 25] - sending RequestAttachUserData to Test.Platform.Commons.Connection.CommonConnection
|
||||||
|
2015-01-07 13:20:31,665 TEST DEBUG [THREAD : SelectorThread] - Proxy got message 'EventOnHook' ('87')
|
||||||
|
message attributes:
|
||||||
|
AttributeEventSequenceNumber [long] = 4899493
|
||||||
|
Time = ComplexClass(TimeStamp):
|
||||||
|
AttributeTimeinuSecs [int] = 573000
|
||||||
|
AttributeTimeinSecs [int] = 1420644031
|
||||||
|
AttributeThisDN [str] = "2214"
|
||||||
|
. Processing with state .StatePrimary proxy: proxy1
|
||||||
|
2015-01-07 14:14:26,666 TEST DEBUG [THREAD : 25] - sending RequestAttachUserData to Test.Platform.Commons.Connection.CommonConnection
|
||||||
118
TimberWinR.UnitTests/MultilineTests.cs
Normal file
118
TimberWinR.UnitTests/MultilineTests.cs
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using TimberWinR.Inputs;
|
||||||
|
using TimberWinR.Parser;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace TimberWinR.UnitTests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class MultilineTests
|
||||||
|
{
|
||||||
|
// [Test(Description = "Test using next")]
|
||||||
|
public void TestMultiline1()
|
||||||
|
{
|
||||||
|
using (StreamReader sr = new StreamReader("Multiline1.txt"))
|
||||||
|
{
|
||||||
|
List<JObject> events = new List<JObject>();
|
||||||
|
|
||||||
|
Console.SetIn(sr);
|
||||||
|
|
||||||
|
Stdin sin = new Stdin();
|
||||||
|
|
||||||
|
sin.CodecArguments = new CodecArguments();
|
||||||
|
sin.CodecArguments.Pattern = "\\\\$";
|
||||||
|
sin.CodecArguments.What = CodecArguments.WhatType.next;
|
||||||
|
sin.CodecArguments.Type = CodecArguments.CodecType.multiline;
|
||||||
|
|
||||||
|
var cancelTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
|
using (var syncHandle = new ManualResetEventSlim())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
StdinListener sl = new StdinListener(sin, cancelTokenSource.Token);
|
||||||
|
|
||||||
|
sl.OnMessageRecieved += o =>
|
||||||
|
{
|
||||||
|
events.Add(o);
|
||||||
|
if (events.Count >= 6)
|
||||||
|
cancelTokenSource.Cancel();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!cancelTokenSource.Token.IsCancellationRequested)
|
||||||
|
syncHandle.Wait(TimeSpan.FromSeconds(10000), cancelTokenSource.Token);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.AreEqual(events.Count, 6);
|
||||||
|
Assert.AreEqual(events[0]["message"].ToString(), "multiline1 \\\nml1_1 \\\nml1_2 \\\nml1_2 ");
|
||||||
|
Assert.AreEqual(events[1]["message"].ToString(), "singleline1");
|
||||||
|
Assert.AreEqual(events[2]["message"].ToString(), "singleline2");
|
||||||
|
Assert.AreEqual(events[3]["message"].ToString(), "multiline2 \\\nml2_1 \\\nml2_2");
|
||||||
|
Assert.AreEqual(events[4]["message"].ToString(), "multiline3 \\\nml3_1 \\\nml3_2");
|
||||||
|
Assert.AreEqual(events[5]["message"].ToString(), "singleline3");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// [Test(Description = "Test using previous")]
|
||||||
|
public void TestMultiline2()
|
||||||
|
{
|
||||||
|
using (StreamReader sr = new StreamReader("Multiline2.txt"))
|
||||||
|
{
|
||||||
|
List<JObject> events = new List<JObject>();
|
||||||
|
|
||||||
|
Console.SetIn(sr);
|
||||||
|
|
||||||
|
Stdin sin = new Stdin();
|
||||||
|
|
||||||
|
sin.CodecArguments = new CodecArguments();
|
||||||
|
sin.CodecArguments.Pattern = "^(\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2},\\d{3})(.*)$";
|
||||||
|
sin.CodecArguments.What = CodecArguments.WhatType.previous;
|
||||||
|
sin.CodecArguments.Type = CodecArguments.CodecType.multiline;
|
||||||
|
sin.CodecArguments.Negate = true;
|
||||||
|
|
||||||
|
var cancelTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
|
using (var syncHandle = new ManualResetEventSlim())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
StdinListener sl = new StdinListener(sin, cancelTokenSource.Token);
|
||||||
|
|
||||||
|
sl.OnMessageRecieved += o =>
|
||||||
|
{
|
||||||
|
events.Add(o);
|
||||||
|
if (events.Count >= 4)
|
||||||
|
cancelTokenSource.Cancel();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!cancelTokenSource.Token.IsCancellationRequested)
|
||||||
|
syncHandle.Wait(TimeSpan.FromSeconds(10000), cancelTokenSource.Token);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.AreEqual(events.Count, 4);
|
||||||
|
Assert.AreEqual(events[0]["message"].ToString(), "2015-01-07 13:14:26,572 TEST DEBUG [THREAD : 25] - Sending message to TServer - tcp://10.1111.11.111:1111\n'RequestAttachUserData' ('30')\nmessage attributes:\nAttributeConnID [long] = 00890\nAttributeReferenceID [int] = 88\nAttributeThisDN [str] = \"2214\"\nAttributeUserData [bstr] = KVList: \n\t\t'ActivityID' [str] = \"1-XXXXXX\"");
|
||||||
|
Assert.AreEqual(events[1]["message"].ToString(), "2015-01-07 13:14:26,574 TEST DEBUG [THREAD : 25] - Writing message RequestAttachUserData in 'proxy1' via '.StatePrimary proxy: proxy1'");
|
||||||
|
Assert.AreEqual(events[2]["message"].ToString(), "2015-01-07 13:14:26,575 TEST DEBUG [THREAD : 25] - sending RequestAttachUserData to Test.Platform.Commons.Connection.CommonConnection");
|
||||||
|
Assert.AreEqual(events[3]["message"].ToString(), "2015-01-07 13:20:31,665 TEST DEBUG [THREAD : SelectorThread] - Proxy got message 'EventOnHook' ('87')\nmessage attributes:\nAttributeEventSequenceNumber [long] = 4899493\nTime = ComplexClass(TimeStamp):\n\tAttributeTimeinuSecs [int] = 573000\n\tAttributeTimeinSecs [int] = 1420644031\nAttributeThisDN [str] = \"2214\"\n. Processing with state .StatePrimary proxy: proxy1");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
55
TimberWinR.UnitTests/Parser/ElasticsearchOutputTests.cs
Normal file
55
TimberWinR.UnitTests/Parser/ElasticsearchOutputTests.cs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
namespace TimberWinR.UnitTests.Parser
|
||||||
|
{
|
||||||
|
using System;
|
||||||
|
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
using TimberWinR.Parser;
|
||||||
|
|
||||||
|
public class ElasticsearchOutputTests
|
||||||
|
{
|
||||||
|
private ElasticsearchOutputParameters parser;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
this.parser = new ElasticsearchOutputParameters();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Given_no_index_returns_default_index_name()
|
||||||
|
{
|
||||||
|
this.parser.Index = "someindex";
|
||||||
|
var json = new JObject();
|
||||||
|
|
||||||
|
var result = this.parser.GetIndexName(json);
|
||||||
|
|
||||||
|
Assert.AreEqual("someindex", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Given_index_with_date_format_and_timestamp_returns_name_by_timestamp()
|
||||||
|
{
|
||||||
|
this.parser.Index = "someindex-%{yyyy.MM.dd}";
|
||||||
|
var json = new JObject();
|
||||||
|
json.Add(new JProperty("@timestamp", "2011-11-30T18:45:32.450Z"));
|
||||||
|
|
||||||
|
var result = this.parser.GetIndexName(json);
|
||||||
|
|
||||||
|
Assert.AreEqual("someindex-2011.11.30", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Given_index_with_date_format_and_no_timestamp_returns_name_by_current_date()
|
||||||
|
{
|
||||||
|
this.parser.Index = "someindex-%{yyyy.MM.dd}";
|
||||||
|
var json = new JObject();
|
||||||
|
|
||||||
|
var result = this.parser.GetIndexName(json);
|
||||||
|
|
||||||
|
Assert.AreEqual("someindex-" + DateTime.UtcNow.ToString("yyyy.MM.dd"), result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
89
TimberWinR.UnitTests/TailFileTests.cs
Normal file
89
TimberWinR.UnitTests/TailFileTests.cs
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using TimberWinR.Inputs;
|
||||||
|
using TimberWinR.Parser;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace TimberWinR.UnitTests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TailFileTests
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestTailFile()
|
||||||
|
{
|
||||||
|
List<JObject> events = new List<JObject>();
|
||||||
|
|
||||||
|
if (File.Exists(".timberwinrdb"))
|
||||||
|
File.Delete(".timberwinrdb");
|
||||||
|
|
||||||
|
var mgr = new Manager();
|
||||||
|
mgr.LogfileDir = ".";
|
||||||
|
|
||||||
|
var tf = new TailFileArguments();
|
||||||
|
var cancelTokenSource = new CancellationTokenSource();
|
||||||
|
tf.Location = "TestTailFile1.log";
|
||||||
|
|
||||||
|
|
||||||
|
if (File.Exists(tf.Location))
|
||||||
|
File.Delete(tf.Location);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var listener = new TailFileListener(tf, cancelTokenSource.Token);
|
||||||
|
|
||||||
|
listener.OnMessageRecieved += o =>
|
||||||
|
{
|
||||||
|
events.Add(o);
|
||||||
|
if (events.Count >= 100)
|
||||||
|
cancelTokenSource.Cancel();
|
||||||
|
};
|
||||||
|
|
||||||
|
GenerateLogFile(tf.Location);
|
||||||
|
|
||||||
|
bool createdFile = false;
|
||||||
|
while (!listener.Stop && !cancelTokenSource.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
Thread.Sleep(100);
|
||||||
|
if (!createdFile)
|
||||||
|
{
|
||||||
|
GenerateLogFile(tf.Location);
|
||||||
|
createdFile = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Done!");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine(ex.ToString());
|
||||||
|
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Assert.AreEqual(100, events.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GenerateLogFile(string fileName)
|
||||||
|
{
|
||||||
|
using (System.IO.StreamWriter file = new System.IO.StreamWriter(fileName))
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 100; i++)
|
||||||
|
{
|
||||||
|
file.WriteLine("Log Line Number {0}", i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
89
TimberWinR.UnitTests/TestDynamicBatchCount.cs
Normal file
89
TimberWinR.UnitTests/TestDynamicBatchCount.cs
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using TimberWinR.Parser;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
|
||||||
|
namespace TimberWinR.UnitTests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestDynamicBatchCount
|
||||||
|
{
|
||||||
|
// [Test]
|
||||||
|
public void TestDynamicBatch()
|
||||||
|
{
|
||||||
|
var mgr = new Manager();
|
||||||
|
mgr.LogfileDir = ".";
|
||||||
|
|
||||||
|
mgr.Config = new Configuration();
|
||||||
|
|
||||||
|
CancellationTokenSource cancelTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
|
var cancelToken = cancelTokenSource.Token;
|
||||||
|
|
||||||
|
FakeRediServer fr = new FakeRediServer(cancelToken);
|
||||||
|
|
||||||
|
var redisParams = new RedisOutputParameters();
|
||||||
|
redisParams.BatchCount = 10;
|
||||||
|
redisParams.MaxBatchCount = 40;
|
||||||
|
redisParams.Interval = 100;
|
||||||
|
|
||||||
|
var redisOutput = new Outputs.RedisOutput(mgr, redisParams, cancelToken);
|
||||||
|
|
||||||
|
|
||||||
|
// Message is irrelavant
|
||||||
|
JObject jsonMessage = new JObject
|
||||||
|
{
|
||||||
|
{"type", "Win32-FileLog"},
|
||||||
|
{"ComputerName", "dev.vistaprint.net"},
|
||||||
|
{"Text", "{\"Email\":\"james@example.com\",\"Active\":true,\"CreatedDate\":\"2013-01-20T00:00:00Z\",\"Roles\":[\"User\",\"Admin\"]}"}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Send 1000 messages at max throttle
|
||||||
|
for (int i = 0; i < 1000; i++)
|
||||||
|
{
|
||||||
|
Thread.Sleep(10);
|
||||||
|
redisOutput.Startup(jsonMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (redisOutput.SentMessages < 1000)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine(redisOutput.SentMessages);
|
||||||
|
Thread.Sleep(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
fr.Shutdown();
|
||||||
|
|
||||||
|
cancelTokenSource.Cancel();
|
||||||
|
|
||||||
|
System.Diagnostics.Debug.WriteLine(redisOutput.ToJson());
|
||||||
|
System.Diagnostics.Debug.WriteLine(redisOutput.QueueDepth);
|
||||||
|
|
||||||
|
JObject json = redisOutput.ToJson();
|
||||||
|
var mbc = json["redis"]["reachedMaxBatchCountTimes"].Value<int>();
|
||||||
|
var sm = json["redis"]["sentMessageCount"].Value<int>();
|
||||||
|
var errs = json["redis"]["errors"].Value<int>();
|
||||||
|
var cbc = json["redis"]["currentBatchCount"].Value<int>();
|
||||||
|
|
||||||
|
// No errors
|
||||||
|
Assert.AreEqual(0, errs);
|
||||||
|
|
||||||
|
// Should have reached max at least 1 time
|
||||||
|
Assert.GreaterOrEqual(mbc, 1);
|
||||||
|
|
||||||
|
// Should have sent 1000 messages
|
||||||
|
Assert.AreEqual(1000, sm);
|
||||||
|
|
||||||
|
// Should reset back down to original
|
||||||
|
Assert.AreEqual(cbc, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,8 @@
|
|||||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<TargetFrameworkProfile />
|
<TargetFrameworkProfile />
|
||||||
|
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
|
||||||
|
<RestorePackages>true</RestorePackages>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
@@ -33,13 +35,18 @@
|
|||||||
<Prefer32Bit>false</Prefer32Bit>
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Reference Include="Interop.MSUtil, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<EmbedInteropTypes>False</EmbedInteropTypes>
|
||||||
|
<HintPath>..\TimberWinR\lib\com-logparser\Interop.MSUtil.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>..\packages\Newtonsoft.Json.6.0.4\lib\net40\Newtonsoft.Json.dll</HintPath>
|
<HintPath>..\packages\Newtonsoft.Json.6.0.8\lib\net40\Newtonsoft.Json.dll</HintPath>
|
||||||
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="nunit.framework, Version=2.6.3.13283, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
|
<Reference Include="nunit.framework">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<HintPath>..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
|
||||||
<HintPath>..\packages\NUnit.2.6.3\lib\nunit.framework.dll</HintPath>
|
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
@@ -52,8 +59,15 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Configuration.cs" />
|
<Compile Include="Configuration.cs" />
|
||||||
<Compile Include="DateFilterTests.cs" />
|
<Compile Include="DateFilterTests.cs" />
|
||||||
|
<Compile Include="FakeRediServer.cs" />
|
||||||
|
<Compile Include="GeoIPFilterTests.cs" />
|
||||||
|
<Compile Include="JsonFilterTests.cs" />
|
||||||
<Compile Include="GrokFilterTests.cs" />
|
<Compile Include="GrokFilterTests.cs" />
|
||||||
|
<Compile Include="MultilineTests.cs" />
|
||||||
|
<Compile Include="Parser\ElasticsearchOutputTests.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="TailFileTests.cs" />
|
||||||
|
<Compile Include="TestDynamicBatchCount.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\TimberWinR\TimberWinR.csproj">
|
<ProjectReference Include="..\TimberWinR\TimberWinR.csproj">
|
||||||
@@ -65,11 +79,30 @@
|
|||||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<None Include="app.config" />
|
||||||
<None Include="packages.config">
|
<None Include="packages.config">
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Multiline2.txt">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="Multiline1.txt">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Inputs\" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
|
||||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
|
||||||
|
</Target>
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
Other similar extension points exist, see Microsoft.Common.targets.
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
<Target Name="BeforeBuild">
|
<Target Name="BeforeBuild">
|
||||||
|
|||||||
11
TimberWinR.UnitTests/app.config
Normal file
11
TimberWinR.UnitTests/app.config
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration>
|
||||||
|
<runtime>
|
||||||
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-4.5.0.0" newVersion="4.5.0.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
</assemblyBinding>
|
||||||
|
</runtime>
|
||||||
|
</configuration>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="Newtonsoft.Json" version="6.0.4" targetFramework="net40" />
|
<package id="Newtonsoft.Json" version="6.0.8" targetFramework="net40" />
|
||||||
<package id="NUnit" version="2.6.3" targetFramework="net40" />
|
<package id="NUnit" version="2.6.4" targetFramework="net40" />
|
||||||
</packages>
|
</packages>
|
||||||
@@ -7,6 +7,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TimberWinR", "TimberWinR\Ti
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{2C4AC7DB-018D-4CCA-9579-06AC5AD2C9D9}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{2C4AC7DB-018D-4CCA-9579-06AC5AD2C9D9}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
.nuget\NuGet.Config = .nuget\NuGet.Config
|
||||||
|
.nuget\NuGet.exe = .nuget\NuGet.exe
|
||||||
|
.nuget\NuGet.targets = .nuget\NuGet.targets
|
||||||
.nuget\packages.config = .nuget\packages.config
|
.nuget\packages.config = .nuget\packages.config
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
@@ -17,13 +20,25 @@ EndProject
|
|||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1B2F600B-2400-45B9-A28E-CFC391D9EFA9}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1B2F600B-2400-45B9-A28E-CFC391D9EFA9}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
appveyor.yml = appveyor.yml
|
appveyor.yml = appveyor.yml
|
||||||
tools\chocolateyInstall.ps1.template = tools\chocolateyInstall.ps1.template
|
chocolatey.png = chocolatey.png
|
||||||
|
chocolateyInstall.ps1.template = chocolateyInstall.ps1.template
|
||||||
|
chocolateyUninstall.ps1.guid = chocolateyUninstall.ps1.guid
|
||||||
|
chocolateyUninstall.ps1.template = chocolateyUninstall.ps1.template
|
||||||
|
chocolateyUninstall.ps1.template.orig = chocolateyUninstall.ps1.template.orig
|
||||||
LICENSE.txt = LICENSE.txt
|
LICENSE.txt = LICENSE.txt
|
||||||
|
Performance1.psess = Performance1.psess
|
||||||
README.md = README.md
|
README.md = README.md
|
||||||
timberwinr.nuspec.template = timberwinr.nuspec.template
|
timberwinr.nuspec.template = timberwinr.nuspec.template
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "TimberWinR.Wix", "TimberWix\TimberWinR.Wix.wixproj", "{82A39B31-61EC-468D-AA71-0D949AC6528F}"
|
Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "TimberWinR.Wix", "TimberWix\TimberWinR.Wix.wixproj", "{82A39B31-61EC-468D-AA71-0D949AC6528F}"
|
||||||
|
ProjectSection(ProjectDependencies) = postProject
|
||||||
|
{99096939-E9DD-4499-883D-4726745A5843} = {99096939-E9DD-4499-883D-4726745A5843}
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TimberWinR.ExtractID", "TimberWinR.ExtractID\TimberWinR.ExtractID.csproj", "{99096939-E9DD-4499-883D-4726745A5843}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TimberWinR.TestGenerator", "TimberWinR.TestGenerator\TimberWinR.TestGenerator.csproj", "{F3960D6E-1EA0-4F4E-8F08-82FC185A0D29}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@@ -77,8 +92,31 @@ Global
|
|||||||
{82A39B31-61EC-468D-AA71-0D949AC6528F}.Release|Mixed Platforms.Build.0 = Release|x86
|
{82A39B31-61EC-468D-AA71-0D949AC6528F}.Release|Mixed Platforms.Build.0 = Release|x86
|
||||||
{82A39B31-61EC-468D-AA71-0D949AC6528F}.Release|x86.ActiveCfg = Release|x86
|
{82A39B31-61EC-468D-AA71-0D949AC6528F}.Release|x86.ActiveCfg = Release|x86
|
||||||
{82A39B31-61EC-468D-AA71-0D949AC6528F}.Release|x86.Build.0 = Release|x86
|
{82A39B31-61EC-468D-AA71-0D949AC6528F}.Release|x86.Build.0 = Release|x86
|
||||||
|
{99096939-E9DD-4499-883D-4726745A5843}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{99096939-E9DD-4499-883D-4726745A5843}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{99096939-E9DD-4499-883D-4726745A5843}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{99096939-E9DD-4499-883D-4726745A5843}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{99096939-E9DD-4499-883D-4726745A5843}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{99096939-E9DD-4499-883D-4726745A5843}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{99096939-E9DD-4499-883D-4726745A5843}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{99096939-E9DD-4499-883D-4726745A5843}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
|
{99096939-E9DD-4499-883D-4726745A5843}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
|
{99096939-E9DD-4499-883D-4726745A5843}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{F3960D6E-1EA0-4F4E-8F08-82FC185A0D29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{F3960D6E-1EA0-4F4E-8F08-82FC185A0D29}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{F3960D6E-1EA0-4F4E-8F08-82FC185A0D29}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{F3960D6E-1EA0-4F4E-8F08-82FC185A0D29}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{F3960D6E-1EA0-4F4E-8F08-82FC185A0D29}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{F3960D6E-1EA0-4F4E-8F08-82FC185A0D29}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{F3960D6E-1EA0-4F4E-8F08-82FC185A0D29}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{F3960D6E-1EA0-4F4E-8F08-82FC185A0D29}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
|
{F3960D6E-1EA0-4F4E-8F08-82FC185A0D29}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
|
{F3960D6E-1EA0-4F4E-8F08-82FC185A0D29}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
|
GlobalSection(Performance) = preSolution
|
||||||
|
HasPerformanceSessions = true
|
||||||
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
26
TimberWinR/Codecs/JsonCodec.cs
Normal file
26
TimberWinR/Codecs/JsonCodec.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using TimberWinR.Parser;
|
||||||
|
|
||||||
|
namespace TimberWinR.Codecs
|
||||||
|
{
|
||||||
|
class JsonCodec : ICodec
|
||||||
|
{
|
||||||
|
private CodecArguments _codecArguments;
|
||||||
|
|
||||||
|
public void Apply(string msg, Inputs.InputListener listener)
|
||||||
|
{
|
||||||
|
JObject jobject = JObject.Parse(msg);
|
||||||
|
listener.AddDefaultFields(jobject);
|
||||||
|
listener.ProcessJson(jobject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonCodec(CodecArguments args)
|
||||||
|
{
|
||||||
|
_codecArguments = args;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
90
TimberWinR/Codecs/Multiline.cs
Normal file
90
TimberWinR/Codecs/Multiline.cs
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using TimberWinR.Inputs;
|
||||||
|
using TimberWinR.Parser;
|
||||||
|
|
||||||
|
namespace TimberWinR.Codecs
|
||||||
|
{
|
||||||
|
public class Multiline : ICodec
|
||||||
|
{
|
||||||
|
private CodecArguments _codecArguments;
|
||||||
|
private List<string> _multiline { get; set; }
|
||||||
|
|
||||||
|
// return true to cancel codec
|
||||||
|
public Multiline(CodecArguments args)
|
||||||
|
{
|
||||||
|
_codecArguments = args;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Apply(string msg, InputListener listener)
|
||||||
|
{
|
||||||
|
if (_codecArguments.Re == null)
|
||||||
|
_codecArguments.Re = new Regex(_codecArguments.Pattern);
|
||||||
|
|
||||||
|
Match match = _codecArguments.Re.Match(msg);
|
||||||
|
|
||||||
|
bool isMatch = (match.Success && !_codecArguments.Negate) || (!match.Success && _codecArguments.Negate);
|
||||||
|
|
||||||
|
switch (_codecArguments.What)
|
||||||
|
{
|
||||||
|
case CodecArguments.WhatType.previous:
|
||||||
|
if (isMatch)
|
||||||
|
{
|
||||||
|
if (_multiline == null)
|
||||||
|
_multiline = new List<string>();
|
||||||
|
|
||||||
|
_multiline.Add(msg);
|
||||||
|
}
|
||||||
|
else // No Match
|
||||||
|
{
|
||||||
|
if (_multiline != null)
|
||||||
|
{
|
||||||
|
string single = string.Join("\n", _multiline.ToArray());
|
||||||
|
_multiline = null;
|
||||||
|
JObject jo = new JObject();
|
||||||
|
jo["message"] = single;
|
||||||
|
jo.Add("tags", new JArray(_codecArguments.MultilineTag));
|
||||||
|
listener.AddDefaultFields(jo);
|
||||||
|
listener.ProcessJson(jo);
|
||||||
|
}
|
||||||
|
_multiline = new List<string>();
|
||||||
|
_multiline.Add(msg);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CodecArguments.WhatType.next:
|
||||||
|
if (isMatch)
|
||||||
|
{
|
||||||
|
if (_multiline == null)
|
||||||
|
_multiline = new List<string>();
|
||||||
|
_multiline.Add(msg);
|
||||||
|
}
|
||||||
|
else // No match
|
||||||
|
{
|
||||||
|
if (_multiline != null)
|
||||||
|
{
|
||||||
|
_multiline.Add(msg);
|
||||||
|
string single = string.Join("\n", _multiline.ToArray());
|
||||||
|
_multiline = null;
|
||||||
|
JObject jo = new JObject();
|
||||||
|
jo["message"] = single;
|
||||||
|
jo.Add("tags", new JArray(_codecArguments.MultilineTag));
|
||||||
|
listener.AddDefaultFields(jo);
|
||||||
|
listener.ProcessJson(jo);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JObject jo = new JObject();
|
||||||
|
jo["message"] = msg;
|
||||||
|
listener.AddDefaultFields(jo);
|
||||||
|
listener.ProcessJson(jo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
39
TimberWinR/Codecs/PlainCodec.cs
Normal file
39
TimberWinR/Codecs/PlainCodec.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using TimberWinR.Parser;
|
||||||
|
|
||||||
|
namespace TimberWinR.Codecs
|
||||||
|
{
|
||||||
|
public class PlainCodec : ICodec
|
||||||
|
{
|
||||||
|
private CodecArguments _codecArguments;
|
||||||
|
|
||||||
|
public void Apply(string msg, Inputs.InputListener listener)
|
||||||
|
{
|
||||||
|
JObject json = new JObject();
|
||||||
|
listener.AddDefaultFields(json);
|
||||||
|
json["message"] = ExpandField(msg, json);
|
||||||
|
listener.ProcessJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected string ExpandField(string fieldName, JObject json)
|
||||||
|
{
|
||||||
|
foreach (var token in json.Children())
|
||||||
|
{
|
||||||
|
string replaceString = "%{" + token.Path + "}";
|
||||||
|
fieldName = fieldName.Replace(replaceString, json[token.Path].ToString());
|
||||||
|
}
|
||||||
|
return fieldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public PlainCodec(CodecArguments args)
|
||||||
|
{
|
||||||
|
_codecArguments = args;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,8 @@ using System.Data.Odbc;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
@@ -16,60 +18,95 @@ using Newtonsoft.Json.Linq;
|
|||||||
using TimberWinR.Inputs;
|
using TimberWinR.Inputs;
|
||||||
using TimberWinR.Filters;
|
using TimberWinR.Filters;
|
||||||
|
|
||||||
|
|
||||||
using NLog;
|
using NLog;
|
||||||
using TimberWinR.Parser;
|
using TimberWinR.Parser;
|
||||||
using Topshelf.Configurators;
|
using Topshelf.Configurators;
|
||||||
using IISW3CLog = TimberWinR.Parser.IISW3CLog;
|
|
||||||
using WindowsEvent = TimberWinR.Parser.WindowsEvent;
|
using WindowsEvent = TimberWinR.Parser.WindowsEvent;
|
||||||
|
|
||||||
namespace TimberWinR
|
namespace TimberWinR
|
||||||
{
|
{
|
||||||
public class Configuration
|
public class Configuration
|
||||||
{
|
{
|
||||||
|
private CancellationToken _cancelToken;
|
||||||
|
|
||||||
|
private FileSystemWatcher _dirWatcher;
|
||||||
|
private Manager _manager;
|
||||||
|
|
||||||
private List<WindowsEvent> _events = new List<WindowsEvent>();
|
private List<WindowsEvent> _events = new List<WindowsEvent>();
|
||||||
public IEnumerable<WindowsEvent> Events
|
public IEnumerable<WindowsEvent> Events
|
||||||
{
|
{
|
||||||
get { return _events; }
|
get { return _events; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<RedisOutput> _redisOutputs = new List<RedisOutput>();
|
private List<StatsDOutputParameters> _statsdOutputs = new List<StatsDOutputParameters>();
|
||||||
public IEnumerable<RedisOutput> RedisOutputs
|
public IEnumerable<StatsDOutputParameters> StatsDOutputs
|
||||||
|
{
|
||||||
|
get { return _statsdOutputs; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<RedisOutputParameters> _redisOutputs = new List<RedisOutputParameters>();
|
||||||
|
public IEnumerable<RedisOutputParameters> RedisOutputs
|
||||||
{
|
{
|
||||||
get { return _redisOutputs; }
|
get { return _redisOutputs; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private List<ElasticsearchOutputParameters> _elasticsearchOutputs = new List<ElasticsearchOutputParameters>();
|
||||||
private List<ElasticsearchOutput> _elasticsearchOutputs = new List<ElasticsearchOutput>();
|
public IEnumerable<ElasticsearchOutputParameters> ElasticsearchOutputs
|
||||||
public IEnumerable<ElasticsearchOutput> ElasticsearchOutputs
|
|
||||||
{
|
{
|
||||||
get { return _elasticsearchOutputs; }
|
get { return _elasticsearchOutputs; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<StdoutOutput> _stdoutOutputs = new List<StdoutOutput>();
|
private List<StdoutOutputParameters> _stdoutOutputs = new List<StdoutOutputParameters>();
|
||||||
public IEnumerable<StdoutOutput> StdoutOutputs
|
public IEnumerable<StdoutOutputParameters> StdoutOutputs
|
||||||
{
|
{
|
||||||
get { return _stdoutOutputs; }
|
get { return _stdoutOutputs; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Tcp> _tcps = new List<Tcp>();
|
private List<FileOutputParameters> _fileOutputs = new List<FileOutputParameters>();
|
||||||
public IEnumerable<Tcp> Tcps
|
public IEnumerable<FileOutputParameters> FileOutputs
|
||||||
|
{
|
||||||
|
get { return _fileOutputs; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<TcpParameters> _tcps = new List<TcpParameters>();
|
||||||
|
public IEnumerable<TcpParameters> Tcps
|
||||||
{
|
{
|
||||||
get { return _tcps; }
|
get { return _tcps; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Log> _logs = new List<Log>();
|
private List<UdpParameters> _udps = new List<UdpParameters>();
|
||||||
public IEnumerable<Log> Logs
|
public IEnumerable<UdpParameters> Udps
|
||||||
|
{
|
||||||
|
get { return _udps; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<LogParameters> _logs = new List<LogParameters>();
|
||||||
|
public IEnumerable<LogParameters> Logs
|
||||||
{
|
{
|
||||||
get { return _logs; }
|
get { return _logs; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<IISW3CLog> _iisw3clogs = new List<IISW3CLog>();
|
private List<TailFileArguments> _tails = new List<TailFileArguments>();
|
||||||
|
public IEnumerable<TailFileArguments> TailFiles
|
||||||
|
{
|
||||||
|
get { return _tails; }
|
||||||
|
}
|
||||||
|
|
||||||
public IEnumerable<IISW3CLog> IISW3C
|
private List<IISW3CLogParameters> _iisw3clogs = new List<IISW3CLogParameters>();
|
||||||
|
|
||||||
|
public IEnumerable<IISW3CLogParameters> IISW3C
|
||||||
{
|
{
|
||||||
get { return _iisw3clogs; }
|
get { return _iisw3clogs; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<W3CLogParameters> _w3clogs = new List<W3CLogParameters>();
|
||||||
|
|
||||||
|
public IEnumerable<W3CLogParameters> W3C
|
||||||
|
{
|
||||||
|
get { return _w3clogs; }
|
||||||
|
}
|
||||||
|
|
||||||
private List<Stdin> _stdins = new List<Stdin>();
|
private List<Stdin> _stdins = new List<Stdin>();
|
||||||
public IEnumerable<Stdin> Stdins
|
public IEnumerable<Stdin> Stdins
|
||||||
@@ -77,6 +114,12 @@ namespace TimberWinR
|
|||||||
get { return _stdins; }
|
get { return _stdins; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<GeneratorParameters> _generators = new List<GeneratorParameters>();
|
||||||
|
public IEnumerable<GeneratorParameters> Generators
|
||||||
|
{
|
||||||
|
get { return _generators; }
|
||||||
|
}
|
||||||
|
|
||||||
private List<LogstashFilter> _filters = new List<LogstashFilter>();
|
private List<LogstashFilter> _filters = new List<LogstashFilter>();
|
||||||
|
|
||||||
public IEnumerable<LogstashFilter> Filters
|
public IEnumerable<LogstashFilter> Filters
|
||||||
@@ -84,11 +127,87 @@ namespace TimberWinR
|
|||||||
get { return _filters; }
|
get { return _filters; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void MonitorDirectory(string directoryToWatch, CancellationToken cancelToken, Manager manager)
|
||||||
|
{
|
||||||
|
_manager = manager;
|
||||||
|
_cancelToken = cancelToken;
|
||||||
|
if (_dirWatcher == null)
|
||||||
|
{
|
||||||
|
_dirWatcher = new FileSystemWatcher();
|
||||||
|
_dirWatcher.Path = directoryToWatch;
|
||||||
|
_dirWatcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
|
||||||
|
// Only watch json files.
|
||||||
|
_dirWatcher.Filter = "*.json";
|
||||||
|
_dirWatcher.Created += DirWatcherOnCreated;
|
||||||
|
_dirWatcher.Changed += DirWatcherOnChanged;
|
||||||
|
_dirWatcher.Renamed += DirWatcherOnRenamed;
|
||||||
|
_dirWatcher.EnableRaisingEvents = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static Configuration FromDirectory(string jsonDirectory)
|
private void DirWatcherOnRenamed(object sender, RenamedEventArgs e)
|
||||||
|
{
|
||||||
|
// The Renamed file could be a different name from .json
|
||||||
|
FileInfo fi = new FileInfo(e.FullPath);
|
||||||
|
if (fi.Extension == ".json")
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Info("File: OnRenamed " + e.FullPath + " " + e.ChangeType);
|
||||||
|
ProcessNewJson(e.FullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DirWatcherOnCreated(object sender, FileSystemEventArgs e)
|
||||||
|
{
|
||||||
|
FileInfo fi = new FileInfo(e.FullPath);
|
||||||
|
if (fi.Extension == ".json")
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Info("File: OnCreated " + e.FullPath + " " + e.ChangeType);
|
||||||
|
ProcessNewJson(e.FullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DirWatcherOnChanged(object sender, FileSystemEventArgs e)
|
||||||
|
{
|
||||||
|
FileInfo fi = new FileInfo(e.FullPath);
|
||||||
|
if (fi.Extension == ".json")
|
||||||
|
{
|
||||||
|
// Specify what is done when a file is changed, created, or deleted.
|
||||||
|
LogManager.GetCurrentClassLogger()
|
||||||
|
.Info("File: OnChanged " + e.ChangeType.ToString() + " " + e.FullPath + " " + e.ChangeType);
|
||||||
|
ProcessNewJson(e.FullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessNewJson(string fileName)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Configuration c = new Configuration();
|
||||||
|
var config = Configuration.FromFile(fileName, c);
|
||||||
|
_manager.ProcessConfiguration(_cancelToken, config);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Error(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShutdownDirectoryMonitor()
|
||||||
|
{
|
||||||
|
_dirWatcher.EnableRaisingEvents = false;
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Stopping Directory Monitor");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DirectoryWatcher(string directoryToWatch)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Starting Directory Monitor {0}", directoryToWatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Configuration FromDirectory(string jsonDirectory, CancellationToken cancelToken, Manager manager)
|
||||||
{
|
{
|
||||||
Configuration c = null;
|
Configuration c = null;
|
||||||
|
|
||||||
foreach (string jsonConfFile in Directory.GetFiles(jsonDirectory, "*.json"))
|
foreach (string jsonConfFile in Directory.GetFiles(jsonDirectory, "*.json"))
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(jsonConfFile))
|
if (!string.IsNullOrEmpty(jsonConfFile))
|
||||||
@@ -97,6 +216,10 @@ namespace TimberWinR
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Startup Directory Monitor
|
||||||
|
if (manager.LiveMonitor)
|
||||||
|
c.MonitorDirectory(jsonDirectory, cancelToken, manager);
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,29 +250,41 @@ namespace TimberWinR
|
|||||||
if (x.TimberWinR.Inputs != null)
|
if (x.TimberWinR.Inputs != null)
|
||||||
{
|
{
|
||||||
if (x.TimberWinR.Inputs.WindowsEvents != null)
|
if (x.TimberWinR.Inputs.WindowsEvents != null)
|
||||||
c._events = x.TimberWinR.Inputs.WindowsEvents.ToList();
|
c._events.AddRange(x.TimberWinR.Inputs.WindowsEvents.ToList());
|
||||||
|
if (x.TimberWinR.Inputs.W3CLogs != null)
|
||||||
|
c._w3clogs.AddRange(x.TimberWinR.Inputs.W3CLogs.ToList());
|
||||||
if (x.TimberWinR.Inputs.IISW3CLogs != null)
|
if (x.TimberWinR.Inputs.IISW3CLogs != null)
|
||||||
c._iisw3clogs = x.TimberWinR.Inputs.IISW3CLogs.ToList();
|
c._iisw3clogs.AddRange(x.TimberWinR.Inputs.IISW3CLogs.ToList());
|
||||||
if (x.TimberWinR.Inputs.Stdins != null)
|
if (x.TimberWinR.Inputs.Stdins != null)
|
||||||
c._stdins = x.TimberWinR.Inputs.Stdins.ToList();
|
c._stdins.AddRange(x.TimberWinR.Inputs.Stdins.ToList());
|
||||||
|
if (x.TimberWinR.Inputs.Generators != null)
|
||||||
|
c._generators.AddRange(x.TimberWinR.Inputs.Generators.ToList());
|
||||||
if (x.TimberWinR.Inputs.Logs != null)
|
if (x.TimberWinR.Inputs.Logs != null)
|
||||||
c._logs = x.TimberWinR.Inputs.Logs.ToList();
|
c._logs.AddRange(x.TimberWinR.Inputs.Logs.ToList());
|
||||||
|
if (x.TimberWinR.Inputs.TailFilesArguments != null)
|
||||||
|
c._tails.AddRange(x.TimberWinR.Inputs.TailFilesArguments.ToList());
|
||||||
if (x.TimberWinR.Inputs.Tcps != null)
|
if (x.TimberWinR.Inputs.Tcps != null)
|
||||||
c._tcps = x.TimberWinR.Inputs.Tcps.ToList();
|
c._tcps.AddRange(x.TimberWinR.Inputs.Tcps.ToList());
|
||||||
|
if (x.TimberWinR.Inputs.Udps != null)
|
||||||
|
c._udps.AddRange(x.TimberWinR.Inputs.Udps.ToList());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x.TimberWinR.Outputs != null)
|
if (x.TimberWinR.Outputs != null)
|
||||||
{
|
{
|
||||||
|
if (x.TimberWinR.Outputs.StatsD != null)
|
||||||
|
c._statsdOutputs.AddRange(x.TimberWinR.Outputs.StatsD.ToList());
|
||||||
if (x.TimberWinR.Outputs.Redis != null)
|
if (x.TimberWinR.Outputs.Redis != null)
|
||||||
c._redisOutputs = x.TimberWinR.Outputs.Redis.ToList();
|
c._redisOutputs.AddRange(x.TimberWinR.Outputs.Redis.ToList());
|
||||||
if (x.TimberWinR.Outputs.Elasticsearch != null)
|
if (x.TimberWinR.Outputs.Elasticsearch != null)
|
||||||
c._elasticsearchOutputs = x.TimberWinR.Outputs.Elasticsearch.ToList();
|
c._elasticsearchOutputs.AddRange(x.TimberWinR.Outputs.Elasticsearch.ToList());
|
||||||
if (x.TimberWinR.Outputs.Stdout != null)
|
if (x.TimberWinR.Outputs.Stdout != null)
|
||||||
c._stdoutOutputs = x.TimberWinR.Outputs.Stdout.ToList();
|
c._stdoutOutputs.AddRange(x.TimberWinR.Outputs.Stdout.ToList());
|
||||||
}
|
if (x.TimberWinR.Outputs.File != null)
|
||||||
|
c._fileOutputs.AddRange(x.TimberWinR.Outputs.File.ToList());
|
||||||
|
}
|
||||||
|
|
||||||
if (x.TimberWinR.Filters != null)
|
if (x.TimberWinR.Filters != null)
|
||||||
c._filters = x.TimberWinR.AllFilters.ToList();
|
c._filters.AddRange(x.TimberWinR.AllFilters.ToList());
|
||||||
|
|
||||||
c.Validate(c);
|
c.Validate(c);
|
||||||
|
|
||||||
@@ -178,12 +313,15 @@ namespace TimberWinR
|
|||||||
{
|
{
|
||||||
_filters = new List<LogstashFilter>();
|
_filters = new List<LogstashFilter>();
|
||||||
_events = new List<WindowsEvent>();
|
_events = new List<WindowsEvent>();
|
||||||
_iisw3clogs = new List<IISW3CLog>();
|
_iisw3clogs = new List<IISW3CLogParameters>();
|
||||||
_logs = new List<Log>();
|
_logs = new List<LogParameters>();
|
||||||
_redisOutputs = new List<RedisOutput>();
|
_statsdOutputs = new List<StatsDOutputParameters>();
|
||||||
_elasticsearchOutputs = new List<ElasticsearchOutput>();
|
_redisOutputs = new List<RedisOutputParameters>();
|
||||||
_stdoutOutputs = new List<StdoutOutput>();
|
_elasticsearchOutputs = new List<ElasticsearchOutputParameters>();
|
||||||
_tcps = new List<Tcp>();
|
_stdoutOutputs = new List<StdoutOutputParameters>();
|
||||||
|
_fileOutputs = new List<FileOutputParameters>();
|
||||||
|
_tcps = new List<TcpParameters>();
|
||||||
|
_udps = new List<UdpParameters>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object GetPropValue(String name, Object obj)
|
public static Object GetPropValue(String name, Object obj)
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.Remoting.Messaging;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq.Expressions;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
using NLog;
|
using NLog;
|
||||||
@@ -19,11 +21,8 @@ namespace TimberWinR.Diagnostics
|
|||||||
private CancellationToken CancelToken { get; set; }
|
private CancellationToken CancelToken { get; set; }
|
||||||
public int Port { get; set; }
|
public int Port { get; set; }
|
||||||
public Manager Manager { get; set; }
|
public Manager Manager { get; set; }
|
||||||
|
public bool Stop { get; set; }
|
||||||
private readonly System.Net.Sockets.TcpListener _tcpListenerV4;
|
private HttpListener web;
|
||||||
private readonly System.Net.Sockets.TcpListener _tcpListenerV6;
|
|
||||||
private Thread _listenThreadV4;
|
|
||||||
private Thread _listenThreadV6;
|
|
||||||
|
|
||||||
public Diagnostics(Manager manager, CancellationToken cancelToken, int port = 5141)
|
public Diagnostics(Manager manager, CancellationToken cancelToken, int port = 5141)
|
||||||
{
|
{
|
||||||
@@ -33,15 +32,119 @@ namespace TimberWinR.Diagnostics
|
|||||||
|
|
||||||
LogManager.GetCurrentClassLogger().Info("Diagnostic(v4/v6) on Port {0} Ready", Port);
|
LogManager.GetCurrentClassLogger().Info("Diagnostic(v4/v6) on Port {0} Ready", Port);
|
||||||
|
|
||||||
_tcpListenerV6 = new System.Net.Sockets.TcpListener(IPAddress.IPv6Any, Port);
|
var hl = new Thread(new ParameterizedThreadStart(HttpListen));
|
||||||
_tcpListenerV4 = new System.Net.Sockets.TcpListener(IPAddress.Any, Port);
|
hl.Start(null);
|
||||||
|
}
|
||||||
|
|
||||||
_listenThreadV4 = new Thread(new ParameterizedThreadStart(ListenForClients));
|
void processRequest()
|
||||||
_listenThreadV4.Start(_tcpListenerV4);
|
{
|
||||||
|
var result = web.BeginGetContext(DiagnosticCallback, web);
|
||||||
|
result.AsyncWaitHandle.WaitOne();
|
||||||
|
}
|
||||||
|
|
||||||
_listenThreadV6 = new Thread(new ParameterizedThreadStart(ListenForClients));
|
private Assembly GetAssemblyByName(string name)
|
||||||
_listenThreadV6.Start(_tcpListenerV6);
|
{
|
||||||
|
return AppDomain.CurrentDomain.GetAssemblies().
|
||||||
|
SingleOrDefault(assembly => assembly.GetName().Name == name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public JObject DiagnosticsOutput()
|
||||||
|
{
|
||||||
|
JObject json = new JObject(
|
||||||
|
new JProperty("timberwinr",
|
||||||
|
new JObject(
|
||||||
|
new JProperty("version", Assembly.GetEntryAssembly().GetName().Version.ToString()),
|
||||||
|
new JProperty("messages", Manager.NumMessages),
|
||||||
|
new JProperty("startedon", Manager.StartedOn),
|
||||||
|
new JProperty("configfile", Manager.JsonConfig),
|
||||||
|
new JProperty("logdir", Manager.LogfileDir),
|
||||||
|
new JProperty("logginglevel", LogManager.GlobalThreshold.ToString())
|
||||||
|
)));
|
||||||
|
AddDiagnosis(json);
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void AddDiagnosis(JObject wrapper)
|
||||||
|
{
|
||||||
|
wrapper.Add("inputs", GetDiagnosisByType("inputs", Manager.Listeners.ToList<IDiagnosable>()));
|
||||||
|
wrapper.Add("filters", GetDiagnosisByType("filters", Manager.Config.Filters.ToList<IDiagnosable>()));
|
||||||
|
wrapper.Add("outputs", GetDiagnosisByType("inputs", Manager.Outputs.ToList<IDiagnosable>()));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected JObject GetDiagnosisByType(String type, List<IDiagnosable> diags)
|
||||||
|
{
|
||||||
|
JObject category = new JObject();
|
||||||
|
foreach(IDiagnosable diag in diags)
|
||||||
|
{
|
||||||
|
JArray array = GetTypeArray(diag.GetType().Name.ToString(), category);
|
||||||
|
array.Add(diag.ToJson());
|
||||||
|
}
|
||||||
|
return category;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected JArray GetTypeArray(String name, JObject category)
|
||||||
|
{
|
||||||
|
JArray ret = (JArray)category.GetValue(name);
|
||||||
|
if (ret == null)
|
||||||
|
{
|
||||||
|
ret = new JArray();
|
||||||
|
category.Add(new JProperty(name, ret));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DiagnosticCallback(IAsyncResult result)
|
||||||
|
{
|
||||||
|
if (web == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var context = web.EndGetContext(result);
|
||||||
|
var response = context.Response;
|
||||||
|
var json = DiagnosticsOutput();
|
||||||
|
|
||||||
|
response.StatusCode = (int) HttpStatusCode.OK;
|
||||||
|
response.StatusDescription = HttpStatusCode.OK.ToString();
|
||||||
|
byte[] buffer = Encoding.UTF8.GetBytes(json.ToString());
|
||||||
|
response.ContentLength64 = buffer.Length;
|
||||||
|
response.OutputStream.Write(buffer, 0, buffer.Length);
|
||||||
|
response.OutputStream.Close();
|
||||||
|
}
|
||||||
|
catch (SocketException)
|
||||||
|
{
|
||||||
|
// Shutdown
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (!Stop)
|
||||||
|
LogManager.GetCurrentClassLogger().Error(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HttpListen(object o)
|
||||||
|
{
|
||||||
|
web = new HttpListener();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
web.Prefixes.Add(string.Format("http://*:{0}/", Port));
|
||||||
|
web.Start();
|
||||||
|
|
||||||
|
while (web != null && web.IsListening)
|
||||||
|
{
|
||||||
|
processRequest();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (SocketException)
|
||||||
|
{
|
||||||
|
// Shutdown
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (!Stop)
|
||||||
|
LogManager.GetCurrentClassLogger().Error("Diagnostic Listener Error: {0}", ex.ToString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ListenForClients(object olistener)
|
private void ListenForClients(object olistener)
|
||||||
@@ -49,7 +152,7 @@ namespace TimberWinR.Diagnostics
|
|||||||
var listener = olistener as System.Net.Sockets.TcpListener;
|
var listener = olistener as System.Net.Sockets.TcpListener;
|
||||||
|
|
||||||
listener.Start();
|
listener.Start();
|
||||||
|
|
||||||
while (!CancelToken.IsCancellationRequested)
|
while (!CancelToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -71,11 +174,13 @@ namespace TimberWinR.Diagnostics
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void HandleNewClient(object client)
|
private void HandleNewClient(object client)
|
||||||
{
|
{
|
||||||
var tcpClient = (TcpClient)client;
|
var tcpClient = (TcpClient)client;
|
||||||
NetworkStream clientStream = null;
|
NetworkStream clientStream = null;
|
||||||
|
|
||||||
|
// Console.WriteLine("Handle new diag client: {0}, {1}", tcpClient.Connected, tcpClient.Client.RemoteEndPoint.ToString());
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (clientStream = tcpClient.GetStream())
|
using (clientStream = tcpClient.GetStream())
|
||||||
@@ -87,17 +192,22 @@ namespace TimberWinR.Diagnostics
|
|||||||
new JProperty("messages", Manager.NumMessages),
|
new JProperty("messages", Manager.NumMessages),
|
||||||
new JProperty("startedon", Manager.StartedOn),
|
new JProperty("startedon", Manager.StartedOn),
|
||||||
new JProperty("configfile", Manager.JsonConfig),
|
new JProperty("configfile", Manager.JsonConfig),
|
||||||
new JProperty("logdir", Manager.LogfileDir),
|
new JProperty("logdir", Manager.LogfileDir),
|
||||||
new JProperty("logginglevel", LogManager.GlobalThreshold.ToString()),
|
new JProperty("logginglevel", LogManager.GlobalThreshold.ToString()),
|
||||||
new JProperty("inputs",
|
new JProperty("inputs",
|
||||||
new JArray(
|
new JArray(
|
||||||
from i in Manager.Listeners
|
from i in Manager.Listeners
|
||||||
select new JObject(i.ToJson()))),
|
select new JObject(i.ToJson()))),
|
||||||
|
new JProperty("filters",
|
||||||
|
new JArray(
|
||||||
|
from f in Manager.Config.Filters
|
||||||
|
select new JObject(f.ToJson()))),
|
||||||
new JProperty("outputs",
|
new JProperty("outputs",
|
||||||
new JArray(
|
new JArray(
|
||||||
from o in Manager.Outputs
|
from o in Manager.Outputs
|
||||||
select new JObject(o.ToJson()))))));
|
select new JObject(o.ToJson()))))));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
sw.WriteLine(json.ToString());
|
sw.WriteLine(json.ToString());
|
||||||
sw.Flush();
|
sw.Flush();
|
||||||
@@ -113,9 +223,19 @@ namespace TimberWinR.Diagnostics
|
|||||||
|
|
||||||
public void Shutdown()
|
public void Shutdown()
|
||||||
{
|
{
|
||||||
_tcpListenerV4.Stop();
|
Stop = true;
|
||||||
_tcpListenerV6.Stop();
|
try
|
||||||
|
{
|
||||||
|
if (web != null && web.IsListening)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Shutting down diagnostics listener");
|
||||||
|
web.Close();
|
||||||
|
web = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
13
TimberWinR/Diagnostics/IDiagnosable.cs
Normal file
13
TimberWinR/Diagnostics/IDiagnosable.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace TimberWinR.Diagnostics
|
||||||
|
{
|
||||||
|
public interface IDiagnosable
|
||||||
|
{
|
||||||
|
JObject ToJson();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,8 +16,15 @@ namespace TimberWinR.Parser
|
|||||||
{
|
{
|
||||||
public override bool Apply(JObject json)
|
public override bool Apply(JObject json)
|
||||||
{
|
{
|
||||||
|
if (!string.IsNullOrEmpty(Type))
|
||||||
|
{
|
||||||
|
JToken json_type = json["type"];
|
||||||
|
if (json_type != null && json_type.ToString() != Type)
|
||||||
|
return true; // Filter does not apply.
|
||||||
|
}
|
||||||
|
|
||||||
if (Condition != null && !EvaluateCondition(json, Condition))
|
if (Condition != null && !EvaluateCondition(json, Condition))
|
||||||
return false;
|
return true;
|
||||||
|
|
||||||
if (Matches(json))
|
if (Matches(json))
|
||||||
{
|
{
|
||||||
@@ -25,7 +32,19 @@ namespace TimberWinR.Parser
|
|||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override JObject ToJson()
|
||||||
|
{
|
||||||
|
JObject json = new JObject(
|
||||||
|
new JProperty("date",
|
||||||
|
new JObject(
|
||||||
|
new JProperty("condition", Condition),
|
||||||
|
new JProperty("type", Type),
|
||||||
|
new JProperty("addfields", AddField)
|
||||||
|
)));
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
// copy_field "field1" -> "field2"
|
// copy_field "field1" -> "field2"
|
||||||
private void AddFields(Newtonsoft.Json.Linq.JObject json)
|
private void AddFields(Newtonsoft.Json.Linq.JObject json)
|
||||||
|
|||||||
188
TimberWinR/Filters/GeoIPFilter.cs
Normal file
188
TimberWinR/Filters/GeoIPFilter.cs
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using MaxMind.GeoIP2;
|
||||||
|
using MaxMind.Db;
|
||||||
|
using MaxMind.GeoIP2.Exceptions;
|
||||||
|
using NLog;
|
||||||
|
|
||||||
|
namespace TimberWinR.Parser
|
||||||
|
{
|
||||||
|
public partial class GeoIP : LogstashFilter
|
||||||
|
{
|
||||||
|
private string DatabaseFileName { get; set; }
|
||||||
|
private DatabaseReader dr;
|
||||||
|
public override JObject ToJson()
|
||||||
|
{
|
||||||
|
JObject json = new JObject(
|
||||||
|
new JProperty("geoip",
|
||||||
|
new JObject(
|
||||||
|
new JProperty("source", Source),
|
||||||
|
new JProperty("type", Type),
|
||||||
|
new JProperty("condition", Condition),
|
||||||
|
new JProperty("target", Target)
|
||||||
|
)));
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GeoIP()
|
||||||
|
{
|
||||||
|
Target = "geoip";
|
||||||
|
DatabaseFileName = Path.Combine(AssemblyDirectory, "GeoLite2City.mmdb");
|
||||||
|
dr = new DatabaseReader(DatabaseFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string AssemblyDirectory
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
|
||||||
|
UriBuilder uri = new UriBuilder(codeBase);
|
||||||
|
string path = Uri.UnescapeDataString(uri.Path);
|
||||||
|
return Path.GetDirectoryName(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Apply(JObject json)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(Type))
|
||||||
|
{
|
||||||
|
JToken json_type = json["type"];
|
||||||
|
if (json_type != null && json_type.ToString() != Type)
|
||||||
|
return true; // Filter does not apply.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Condition != null)
|
||||||
|
{
|
||||||
|
var expr = EvaluateCondition(json, Condition);
|
||||||
|
if (!expr)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var source = json[Source];
|
||||||
|
|
||||||
|
if (source != null && !string.IsNullOrEmpty(source.ToString()))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var l = dr.City(source.ToString());
|
||||||
|
if (l != null)
|
||||||
|
{
|
||||||
|
JObject geo_json = new JObject(
|
||||||
|
new JProperty(Target,
|
||||||
|
new JObject(
|
||||||
|
new JProperty("ip", source.ToString()),
|
||||||
|
new JProperty("country_code2", l.Country.IsoCode),
|
||||||
|
new JProperty("country_name", l.Country.Name),
|
||||||
|
new JProperty("continent_code", l.Continent.Code),
|
||||||
|
new JProperty("region_name", l.MostSpecificSubdivision.IsoCode),
|
||||||
|
new JProperty("city_name", l.City.Name),
|
||||||
|
new JProperty("postal_code", l.Postal.Code),
|
||||||
|
new JProperty("latitude", l.Location.Latitude),
|
||||||
|
new JProperty("longitude", l.Location.Longitude),
|
||||||
|
new JProperty("dma_code", l.Location.MetroCode),
|
||||||
|
new JProperty("timezone", l.Location.TimeZone),
|
||||||
|
new JProperty("real_region_name", l.MostSpecificSubdivision.Name),
|
||||||
|
new JProperty("location",
|
||||||
|
new JArray(l.Location.Longitude, l.Location.Latitude)
|
||||||
|
))));
|
||||||
|
|
||||||
|
json.Merge(geo_json, new JsonMergeSettings
|
||||||
|
{
|
||||||
|
MergeArrayHandling = MergeArrayHandling.Union
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
json["_geoiperror"] = string.Format("IP Address not found: {0}", source.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
json["_geoiperror"] = string.Format("IP Address not found: {0} ({1})", source.ToString(), ex.ToString());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AddFields(json);
|
||||||
|
AddTags(json);
|
||||||
|
RemoveFields(json);
|
||||||
|
RemoveTags(json);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddFields(Newtonsoft.Json.Linq.JObject json)
|
||||||
|
{
|
||||||
|
if (AddField != null && AddField.Length > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < AddField.Length; i += 2)
|
||||||
|
{
|
||||||
|
string fieldName = ExpandField(AddField[i], json);
|
||||||
|
string fieldValue = ExpandField(AddField[i + 1], json);
|
||||||
|
AddOrModify(json, fieldName, fieldValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveFields(Newtonsoft.Json.Linq.JObject json)
|
||||||
|
{
|
||||||
|
if (RemoveField != null && RemoveField.Length > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < RemoveField.Length; i++)
|
||||||
|
{
|
||||||
|
string fieldName = ExpandField(RemoveField[i], json);
|
||||||
|
RemoveProperties(json, new string[] { fieldName });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddTags(Newtonsoft.Json.Linq.JObject json)
|
||||||
|
{
|
||||||
|
if (AddTag != null && AddTag.Length > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < AddTag.Length; i++)
|
||||||
|
{
|
||||||
|
string value = ExpandField(AddTag[i], json);
|
||||||
|
|
||||||
|
JToken tags = json["tags"];
|
||||||
|
if (tags == null)
|
||||||
|
json.Add("tags", new JArray(value));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JArray a = tags as JArray;
|
||||||
|
a.Add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveTags(Newtonsoft.Json.Linq.JObject json)
|
||||||
|
{
|
||||||
|
if (RemoveTag != null && RemoveTag.Length > 0)
|
||||||
|
{
|
||||||
|
JToken tags = json["tags"];
|
||||||
|
if (tags != null)
|
||||||
|
{
|
||||||
|
List<JToken> children = tags.Children().ToList();
|
||||||
|
for (int i = 0; i < RemoveTag.Length; i++)
|
||||||
|
{
|
||||||
|
string tagName = ExpandField(RemoveTag[i], json);
|
||||||
|
foreach (JToken token in children)
|
||||||
|
{
|
||||||
|
if (token.ToString() == tagName)
|
||||||
|
token.Remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,48 +32,95 @@ namespace TimberWinR.Parser
|
|||||||
|
|
||||||
public partial class Grok : LogstashFilter
|
public partial class Grok : LogstashFilter
|
||||||
{
|
{
|
||||||
public override bool Apply(JObject json)
|
public override JObject ToJson()
|
||||||
{
|
|
||||||
if (Condition != null && !EvaluateCondition(json, Condition))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (Matches(json))
|
|
||||||
{
|
|
||||||
AddFields(json);
|
|
||||||
AddTags(json);
|
|
||||||
RemoveFields(json);
|
|
||||||
RemoveTags(json);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool Matches(Newtonsoft.Json.Linq.JObject json)
|
|
||||||
{
|
{
|
||||||
string field = Match[0];
|
string field = Match[0];
|
||||||
string expr = Match[1];
|
string expr = Match[1];
|
||||||
|
|
||||||
JToken token = null;
|
JObject json = new JObject(
|
||||||
if (json.TryGetValue(field, out token))
|
new JProperty("grok",
|
||||||
|
new JObject(
|
||||||
|
new JProperty("match", field),
|
||||||
|
new JProperty("expr", expr),
|
||||||
|
new JProperty("condition", Condition),
|
||||||
|
new JProperty("addfields", AddField),
|
||||||
|
new JProperty("addtags", AddTag),
|
||||||
|
new JProperty("drop", DropIfMatch),
|
||||||
|
new JProperty("type", Type),
|
||||||
|
new JProperty("removefields", RemoveField),
|
||||||
|
new JProperty("removetag", RemoveTag)
|
||||||
|
)));
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns: true - Filter does not apply or has been applied successfully
|
||||||
|
// Returns: false - Drop this object
|
||||||
|
public override bool Apply(JObject json)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(Type))
|
||||||
{
|
{
|
||||||
string text = token.ToString();
|
JToken json_type = json["type"];
|
||||||
if (!string.IsNullOrEmpty(text))
|
if (json_type != null && json_type.ToString() != Type)
|
||||||
|
return true; // Filter does not apply.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Matches(json))
|
||||||
|
{
|
||||||
|
if (Condition != null)
|
||||||
{
|
{
|
||||||
var resolver = new RegexGrokResolver();
|
var expr = EvaluateCondition(json, Condition);
|
||||||
var pattern = resolver.ResolveToRegex(expr);
|
if (expr)
|
||||||
var match = Regex.Match(text, pattern);
|
|
||||||
if (match.Success)
|
|
||||||
{
|
{
|
||||||
var regex = new Regex(pattern);
|
if (DropIfMatch)
|
||||||
var namedCaptures = regex.MatchNamedCaptures(text);
|
return false; // drop this one
|
||||||
foreach (string fieldName in namedCaptures.Keys)
|
|
||||||
{
|
|
||||||
AddOrModify(json, fieldName, namedCaptures[fieldName]);
|
|
||||||
}
|
|
||||||
return true; // Yes!
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DropIfMatch)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
AddFields(json);
|
||||||
|
AddTags(json);
|
||||||
|
RemoveFields(json);
|
||||||
|
RemoveTags(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test for any true matching condition(s)
|
||||||
|
private bool Matches(Newtonsoft.Json.Linq.JObject json)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Match.Length; i += 2)
|
||||||
|
{
|
||||||
|
string field = Match[i];
|
||||||
|
string expr = Match[i + 1];
|
||||||
|
|
||||||
|
JToken token = null;
|
||||||
|
if (json.TryGetValue(field, out token))
|
||||||
|
{
|
||||||
|
string text = token.ToString();
|
||||||
|
if (!string.IsNullOrEmpty(text))
|
||||||
|
{
|
||||||
|
var resolver = new RegexGrokResolver();
|
||||||
|
var pattern = resolver.ResolveToRegex(expr);
|
||||||
|
var match = Regex.Match(text, pattern);
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
var regex = new Regex(pattern);
|
||||||
|
var namedCaptures = regex.MatchNamedCaptures(text);
|
||||||
|
foreach (string fieldName in namedCaptures.Keys)
|
||||||
|
{
|
||||||
|
AddOrModify(json, fieldName, namedCaptures[fieldName]);
|
||||||
|
}
|
||||||
|
return true; // Yes!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (string.IsNullOrEmpty(expr))
|
||||||
|
return true; // Empty field is no match
|
||||||
}
|
}
|
||||||
return true; // Empty field is no match
|
|
||||||
}
|
}
|
||||||
return false; // Not specified is failure
|
return false; // Not specified is failure
|
||||||
}
|
}
|
||||||
@@ -89,9 +136,7 @@ namespace TimberWinR.Parser
|
|||||||
AddOrModify(json, fieldName, fieldValue);
|
AddOrModify(json, fieldName, fieldValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void RemoveFields(Newtonsoft.Json.Linq.JObject json)
|
private void RemoveFields(Newtonsoft.Json.Linq.JObject json)
|
||||||
{
|
{
|
||||||
@@ -132,17 +177,17 @@ namespace TimberWinR.Parser
|
|||||||
JToken tags = json["tags"];
|
JToken tags = json["tags"];
|
||||||
if (tags != null)
|
if (tags != null)
|
||||||
{
|
{
|
||||||
List<JToken> children = tags.Children().ToList();
|
List<JToken> children = tags.Children().ToList();
|
||||||
for (int i = 0; i < RemoveTag.Length; i++)
|
for (int i = 0; i < RemoveTag.Length; i++)
|
||||||
{
|
{
|
||||||
string tagName = ExpandField(RemoveTag[i], json);
|
string tagName = ExpandField(RemoveTag[i], json);
|
||||||
foreach(JToken token in children)
|
foreach (JToken token in children)
|
||||||
{
|
{
|
||||||
if (token.ToString() == tagName)
|
if (token.ToString() == tagName)
|
||||||
token.Remove();
|
token.Remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
192
TimberWinR/Filters/JsonFilter.cs
Normal file
192
TimberWinR/Filters/JsonFilter.cs
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using NLog;
|
||||||
|
|
||||||
|
namespace TimberWinR.Parser
|
||||||
|
{
|
||||||
|
public partial class Json : LogstashFilter
|
||||||
|
{
|
||||||
|
private long _errorCount;
|
||||||
|
|
||||||
|
public Json()
|
||||||
|
{
|
||||||
|
RemoveSource = true;
|
||||||
|
}
|
||||||
|
public override JObject ToJson()
|
||||||
|
{
|
||||||
|
JObject json = new JObject(
|
||||||
|
new JProperty("json",
|
||||||
|
new JObject(
|
||||||
|
new JProperty("condition", Condition),
|
||||||
|
new JProperty("errors", _errorCount),
|
||||||
|
new JProperty("source", Source),
|
||||||
|
new JProperty("promote", Source),
|
||||||
|
new JProperty("target", Target),
|
||||||
|
new JProperty("type", Type),
|
||||||
|
new JProperty("addfields", AddField),
|
||||||
|
new JProperty("addtags", AddTag),
|
||||||
|
new JProperty("removefields", RemoveField),
|
||||||
|
new JProperty("removetag", RemoveTag)
|
||||||
|
)));
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Apply(JObject json)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(Type))
|
||||||
|
{
|
||||||
|
JToken json_type = json["type"];
|
||||||
|
if (json_type != null && json_type.ToString() != Type)
|
||||||
|
return true; // Filter does not apply.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Condition != null)
|
||||||
|
{
|
||||||
|
var expr = EvaluateCondition(json, Condition);
|
||||||
|
if (!expr)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var source = json[Source];
|
||||||
|
if (source == null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
var jsonOrig = source.ToString();
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(source.ToString()))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
JObject subJson;
|
||||||
|
|
||||||
|
if (Target != null && !string.IsNullOrEmpty(Target))
|
||||||
|
{
|
||||||
|
subJson = new JObject();
|
||||||
|
subJson[Target] = JObject.Parse(source.ToString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
subJson = JObject.Parse(source.ToString());
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(Promote))
|
||||||
|
{
|
||||||
|
var promotedJson = subJson[Promote];
|
||||||
|
RemoveProperties(subJson, new string[] { Promote });
|
||||||
|
|
||||||
|
subJson.Merge(promotedJson, new JsonMergeSettings
|
||||||
|
{
|
||||||
|
MergeArrayHandling = MergeArrayHandling.Replace
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
json.Merge(subJson, new JsonMergeSettings
|
||||||
|
{
|
||||||
|
MergeArrayHandling = MergeArrayHandling.Replace
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Rename != null && Rename.Length > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Rename.Length; i += 2)
|
||||||
|
{
|
||||||
|
string oldName = ExpandField(Rename[i], json);
|
||||||
|
string newName = ExpandField(Rename[i+1], json);
|
||||||
|
RenameProperty(json, oldName, newName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RemoveSource)
|
||||||
|
{
|
||||||
|
RemoveProperties(json, new string[] { Source });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Error(ex);
|
||||||
|
Interlocked.Increment(ref _errorCount);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AddFields(json);
|
||||||
|
AddTags(json);
|
||||||
|
RemoveFields(json);
|
||||||
|
RemoveTags(json);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddFields(Newtonsoft.Json.Linq.JObject json)
|
||||||
|
{
|
||||||
|
if (AddField != null && AddField.Length > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < AddField.Length; i += 2)
|
||||||
|
{
|
||||||
|
string fieldName = ExpandField(AddField[i], json);
|
||||||
|
string fieldValue = ExpandField(AddField[i + 1], json);
|
||||||
|
AddOrModify(json, fieldName, fieldValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveFields(Newtonsoft.Json.Linq.JObject json)
|
||||||
|
{
|
||||||
|
if (RemoveField != null && RemoveField.Length > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < RemoveField.Length; i++)
|
||||||
|
{
|
||||||
|
string fieldName = ExpandField(RemoveField[i], json);
|
||||||
|
RemoveProperties(json, new string[] { fieldName });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddTags(Newtonsoft.Json.Linq.JObject json)
|
||||||
|
{
|
||||||
|
if (AddTag != null && AddTag.Length > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < AddTag.Length; i++)
|
||||||
|
{
|
||||||
|
string value = ExpandField(AddTag[i], json);
|
||||||
|
|
||||||
|
JToken tags = json["tags"];
|
||||||
|
if (tags == null)
|
||||||
|
json.Add("tags", new JArray(value));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JArray a = tags as JArray;
|
||||||
|
a.Add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveTags(Newtonsoft.Json.Linq.JObject json)
|
||||||
|
{
|
||||||
|
if (RemoveTag != null && RemoveTag.Length > 0)
|
||||||
|
{
|
||||||
|
JToken tags = json["tags"];
|
||||||
|
if (tags != null)
|
||||||
|
{
|
||||||
|
List<JToken> children = tags.Children().ToList();
|
||||||
|
for (int i = 0; i < RemoveTag.Length; i++)
|
||||||
|
{
|
||||||
|
string tagName = ExpandField(RemoveTag[i], json);
|
||||||
|
foreach (JToken token in children)
|
||||||
|
{
|
||||||
|
if (token.ToString() == tagName)
|
||||||
|
token.Remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,17 +11,56 @@ namespace TimberWinR.Parser
|
|||||||
{
|
{
|
||||||
public partial class Mutate : LogstashFilter
|
public partial class Mutate : LogstashFilter
|
||||||
{
|
{
|
||||||
|
public override JObject ToJson()
|
||||||
|
{
|
||||||
|
JObject json = new JObject(
|
||||||
|
new JProperty("mutate",
|
||||||
|
new JObject(
|
||||||
|
new JProperty("condition", Condition),
|
||||||
|
new JProperty("splits", Split),
|
||||||
|
new JProperty("type", Type),
|
||||||
|
new JProperty("remove", Remove),
|
||||||
|
new JProperty("rename", Rename),
|
||||||
|
new JProperty("replace", Replace)
|
||||||
|
)));
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
public override bool Apply(JObject json)
|
public override bool Apply(JObject json)
|
||||||
{
|
{
|
||||||
if (Condition != null && !EvaluateCondition(json, Condition))
|
if (!string.IsNullOrEmpty(Type))
|
||||||
return false;
|
{
|
||||||
|
JToken json_type = json["type"];
|
||||||
|
if (json_type != null && json_type.ToString() != Type)
|
||||||
|
return true; // Filter does not apply.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Condition != null)
|
||||||
|
{
|
||||||
|
var expr = EvaluateCondition(json, Condition);
|
||||||
|
if (!expr)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
ApplySplits(json);
|
ApplySplits(json);
|
||||||
|
ApplyRemoves(json);
|
||||||
ApplyRenames(json);
|
ApplyRenames(json);
|
||||||
ApplyReplace(json);
|
ApplyReplace(json);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ApplyRemoves(JObject json)
|
||||||
|
{
|
||||||
|
if (Remove != null && Remove.Length > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Remove.Length; i += 1)
|
||||||
|
{
|
||||||
|
string name = ExpandField(Remove[i], json);
|
||||||
|
RemoveProperty(json, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ApplyRenames(JObject json)
|
private void ApplyRenames(JObject json)
|
||||||
{
|
{
|
||||||
if (Rename != null && Rename.Length > 0)
|
if (Rename != null && Rename.Length > 0)
|
||||||
|
|||||||
BIN
TimberWinR/GeoLite2City.mmdb
Normal file
BIN
TimberWinR/GeoLite2City.mmdb
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 MiB |
13
TimberWinR/ICodec.cs
Normal file
13
TimberWinR/ICodec.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using TimberWinR.Inputs;
|
||||||
|
|
||||||
|
namespace TimberWinR
|
||||||
|
{
|
||||||
|
public interface ICodec
|
||||||
|
{
|
||||||
|
void Apply(string msg, InputListener listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
88
TimberWinR/Inputs/GeneratorInput.cs
Normal file
88
TimberWinR/Inputs/GeneratorInput.cs
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using NLog;
|
||||||
|
using RestSharp.Extensions;
|
||||||
|
using TimberWinR.Codecs;
|
||||||
|
using TimberWinR.Parser;
|
||||||
|
|
||||||
|
|
||||||
|
namespace TimberWinR.Inputs
|
||||||
|
{
|
||||||
|
public class GeneratorInput : InputListener
|
||||||
|
{
|
||||||
|
public override JObject ToJson()
|
||||||
|
{
|
||||||
|
JObject json = new JObject(
|
||||||
|
new JProperty("message", _params.Message),
|
||||||
|
new JProperty("messages", _sentMessages),
|
||||||
|
new JProperty("generator", "enabled"));
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TimberWinR.Parser.GeneratorParameters _params;
|
||||||
|
private Thread _listenThread;
|
||||||
|
private ICodec _codec;
|
||||||
|
private int _sentMessages;
|
||||||
|
|
||||||
|
public GeneratorInput(TimberWinR.Parser.GeneratorParameters parameters, CancellationToken cancelToken)
|
||||||
|
: base(cancelToken, "Win32-InputGen")
|
||||||
|
{
|
||||||
|
_params = parameters;
|
||||||
|
|
||||||
|
if (_params.CodecArguments != null)
|
||||||
|
{
|
||||||
|
switch (_params.CodecArguments.Type)
|
||||||
|
{
|
||||||
|
case CodecArguments.CodecType.json:
|
||||||
|
_codec = new JsonCodec(_params.CodecArguments);
|
||||||
|
break;
|
||||||
|
case CodecArguments.CodecType.multiline:
|
||||||
|
_codec = new Multiline(_params.CodecArguments);
|
||||||
|
break;
|
||||||
|
case CodecArguments.CodecType.plain:
|
||||||
|
_codec = new PlainCodec(_params.CodecArguments);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_listenThread = new Thread(new ThreadStart(GenerateData));
|
||||||
|
_listenThread.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GenerateData()
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Generator Creating {0} Lines", _params.Count);
|
||||||
|
|
||||||
|
int numMessages = _params.Count;
|
||||||
|
|
||||||
|
// Infinite or until done.
|
||||||
|
for (int i = 0; (_params.Count == 0 || i < numMessages); i++)
|
||||||
|
{
|
||||||
|
if (CancelToken.IsCancellationRequested)
|
||||||
|
break;
|
||||||
|
|
||||||
|
string msg = ToPrintable(_params.Message);
|
||||||
|
|
||||||
|
if (_codec != null)
|
||||||
|
_codec.Apply(msg, this);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JObject jo = new JObject();
|
||||||
|
jo["Message"] = msg;
|
||||||
|
AddDefaultFields(jo);
|
||||||
|
ProcessJson(jo);
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread.Sleep(_params.Rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
Finished();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.AccessControl;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.IO;
|
|
||||||
using Interop.MSUtil;
|
|
||||||
|
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Newtonsoft.Json.Serialization;
|
|
||||||
using NLog;
|
using NLog;
|
||||||
using LogQuery = Interop.MSUtil.LogQueryClassClass;
|
using LogQuery = Interop.MSUtil.LogQueryClassClass;
|
||||||
using IISW3CLogInputFormat = Interop.MSUtil.COMIISW3CInputContextClassClass;
|
using IISW3CLogInputFormat = Interop.MSUtil.COMIISW3CInputContextClassClass;
|
||||||
@@ -20,22 +15,31 @@ namespace TimberWinR.Inputs
|
|||||||
{
|
{
|
||||||
public class IISW3CInputListener : InputListener
|
public class IISW3CInputListener : InputListener
|
||||||
{
|
{
|
||||||
private int _pollingIntervalInSeconds = 1;
|
private readonly int _pollingIntervalInSeconds;
|
||||||
private TimberWinR.Parser.IISW3CLog _arguments;
|
private readonly Parser.IISW3CLogParameters _arguments;
|
||||||
private long _receivedMessages;
|
private long _receivedMessages;
|
||||||
|
public bool Stop { get; set; }
|
||||||
|
private IisW3CRowReader rowReader;
|
||||||
|
|
||||||
public IISW3CInputListener(TimberWinR.Parser.IISW3CLog arguments, CancellationToken cancelToken, int pollingIntervalInSeconds = 1)
|
public IISW3CInputListener(Parser.IISW3CLogParameters arguments, CancellationToken cancelToken, int pollingIntervalInSeconds = 5)
|
||||||
: base(cancelToken, "Win32-IISLog")
|
: base(cancelToken, "Win32-IISLog")
|
||||||
{
|
{
|
||||||
_arguments = arguments;
|
_arguments = arguments;
|
||||||
_receivedMessages = 0;
|
_receivedMessages = 0;
|
||||||
_pollingIntervalInSeconds = pollingIntervalInSeconds;
|
_pollingIntervalInSeconds = pollingIntervalInSeconds;
|
||||||
var task = new Task(IISW3CWatcher, cancelToken);
|
this.rowReader = new IisW3CRowReader(_arguments.Fields);
|
||||||
task.Start();
|
|
||||||
|
foreach (string loc in _arguments.Location.Split(','))
|
||||||
|
{
|
||||||
|
string hive = loc.Trim();
|
||||||
|
Task.Factory.StartNew(() => IISW3CWatcher(loc));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Shutdown()
|
public override void Shutdown()
|
||||||
{
|
{
|
||||||
|
Stop = true;
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Shutting Down {0}", InputType);
|
||||||
base.Shutdown();
|
base.Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,18 +60,18 @@ namespace TimberWinR.Inputs
|
|||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void IISW3CWatcher(string location)
|
||||||
private void IISW3CWatcher()
|
|
||||||
{
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Info("IISW3Listener Ready For {0}", location);
|
||||||
|
|
||||||
var oLogQuery = new LogQuery();
|
var oLogQuery = new LogQuery();
|
||||||
|
|
||||||
var iFmt = new IISW3CLogInputFormat()
|
var iFmt = new IISW3CLogInputFormat()
|
||||||
{
|
{
|
||||||
codepage = _arguments.CodePage,
|
codepage = _arguments.CodePage,
|
||||||
consolidateLogs = _arguments.ConsolidateLogs,
|
consolidateLogs = true,
|
||||||
dirTime = _arguments.DirTime,
|
dirTime = _arguments.DirTime,
|
||||||
dQuotes = _arguments.DoubleQuotes,
|
dQuotes = _arguments.DoubleQuotes,
|
||||||
iCheckpoint = CheckpointFileName,
|
|
||||||
recurse = _arguments.Recurse,
|
recurse = _arguments.Recurse,
|
||||||
useDoubleQuotes = _arguments.DoubleQuotes
|
useDoubleQuotes = _arguments.DoubleQuotes
|
||||||
};
|
};
|
||||||
@@ -75,58 +79,84 @@ namespace TimberWinR.Inputs
|
|||||||
if (_arguments.MinDateMod.HasValue)
|
if (_arguments.MinDateMod.HasValue)
|
||||||
iFmt.minDateMod = _arguments.MinDateMod.Value.ToString("yyyy-MM-dd hh:mm:ss");
|
iFmt.minDateMod = _arguments.MinDateMod.Value.ToString("yyyy-MM-dd hh:mm:ss");
|
||||||
|
|
||||||
// Create the query
|
Dictionary<string, Int64> logFileMaxRecords = new Dictionary<string, Int64>();
|
||||||
var query = string.Format("SELECT * FROM {0}", _arguments.Location);
|
|
||||||
|
|
||||||
var firstQuery = true;
|
using (var syncHandle = new ManualResetEventSlim())
|
||||||
// Execute the query
|
|
||||||
while (!CancelToken.IsCancellationRequested)
|
|
||||||
{
|
{
|
||||||
try
|
// Execute the query
|
||||||
|
while (!Stop)
|
||||||
{
|
{
|
||||||
var rs = oLogQuery.Execute(query, iFmt);
|
// Execute the query
|
||||||
Dictionary<string, int> colMap = new Dictionary<string, int>();
|
if (!CancelToken.IsCancellationRequested)
|
||||||
for (int col = 0; col < rs.getColumnCount(); col++)
|
|
||||||
{
|
{
|
||||||
string colName = rs.getColumnName(col);
|
try
|
||||||
colMap[colName] = col;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Browse the recordset
|
|
||||||
for (; !rs.atEnd(); rs.moveNext())
|
|
||||||
{
|
|
||||||
// We want to "tail" the log, so skip the first query results.
|
|
||||||
if (!firstQuery)
|
|
||||||
{
|
{
|
||||||
var record = rs.getRecord();
|
oLogQuery = new LogQuery();
|
||||||
var json = new JObject();
|
|
||||||
foreach (var field in _arguments.Fields)
|
|
||||||
{
|
|
||||||
if (!colMap.ContainsKey(field.Name))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
object v = record.getValue(field.Name);
|
var qfiles = string.Format("SELECT Distinct [LogFilename] FROM {0}", location);
|
||||||
if (field.DataType == typeof(DateTime))
|
var rsfiles = oLogQuery.Execute(qfiles, iFmt);
|
||||||
|
for (; !rsfiles.atEnd(); rsfiles.moveNext())
|
||||||
|
{
|
||||||
|
var record = rsfiles.getRecord();
|
||||||
|
string fileName = record.getValue("LogFilename") as string;
|
||||||
|
if (!logFileMaxRecords.ContainsKey(fileName))
|
||||||
{
|
{
|
||||||
DateTime dt = DateTime.Parse(v.ToString());
|
var qcount = string.Format("SELECT max(LogRow) as MaxRecordNumber FROM {0}",
|
||||||
json.Add(new JProperty(field.Name, dt));
|
fileName);
|
||||||
|
var rcount = oLogQuery.Execute(qcount, iFmt);
|
||||||
|
var qr = rcount.getRecord();
|
||||||
|
var lrn = (Int64) qr.getValueEx("MaxRecordNumber");
|
||||||
|
logFileMaxRecords[fileName] = lrn;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
json.Add(new JProperty(field.Name, v));
|
|
||||||
}
|
}
|
||||||
ProcessJson(json);
|
|
||||||
_receivedMessages++;
|
foreach (string fileName in logFileMaxRecords.Keys.ToList())
|
||||||
|
{
|
||||||
|
var lastRecordNumber = logFileMaxRecords[fileName];
|
||||||
|
var query = string.Format("SELECT * FROM '{0}' Where LogRow > {1}", fileName,
|
||||||
|
lastRecordNumber);
|
||||||
|
|
||||||
|
var rs = oLogQuery.Execute(query, iFmt);
|
||||||
|
rowReader.ReadColumnMap(rs);
|
||||||
|
|
||||||
|
// Browse the recordset
|
||||||
|
for (; !rs.atEnd(); rs.moveNext())
|
||||||
|
{
|
||||||
|
var record = rs.getRecord();
|
||||||
|
var json = rowReader.ReadToJson(record);
|
||||||
|
ProcessJson(json);
|
||||||
|
_receivedMessages++;
|
||||||
|
var lrn = (Int64) record.getValueEx("LogRow");
|
||||||
|
logFileMaxRecords[fileName] = lrn;
|
||||||
|
record = null;
|
||||||
|
json = null;
|
||||||
|
}
|
||||||
|
// Close the recordset
|
||||||
|
rs.close();
|
||||||
|
GC.Collect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Error(ex);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!Stop)
|
||||||
|
syncHandle.Wait(TimeSpan.FromSeconds(_pollingIntervalInSeconds), CancelToken);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Close the recordset
|
|
||||||
rs.close();
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LogManager.GetCurrentClassLogger().Error(ex);
|
|
||||||
}
|
|
||||||
firstQuery = false;
|
|
||||||
System.Threading.Thread.Sleep(_pollingIntervalInSeconds * 1000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Finished();
|
Finished();
|
||||||
|
|||||||
69
TimberWinR/Inputs/IISW3CRowReader.cs
Normal file
69
TimberWinR/Inputs/IISW3CRowReader.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
namespace TimberWinR.Inputs
|
||||||
|
{
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using Interop.MSUtil;
|
||||||
|
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
using TimberWinR.Parser;
|
||||||
|
|
||||||
|
public class IisW3CRowReader
|
||||||
|
{
|
||||||
|
private readonly List<Field> fields;
|
||||||
|
private IDictionary<string, int> columnMap;
|
||||||
|
|
||||||
|
public IisW3CRowReader(List<Field> fields)
|
||||||
|
{
|
||||||
|
this.fields = fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JObject ReadToJson(ILogRecord row)
|
||||||
|
{
|
||||||
|
var json = new JObject();
|
||||||
|
foreach (var field in this.fields)
|
||||||
|
{
|
||||||
|
if (this.columnMap.ContainsKey(field.Name))
|
||||||
|
{
|
||||||
|
object v = row.getValue(field.Name);
|
||||||
|
if (field.DataType == typeof(DateTime))
|
||||||
|
{
|
||||||
|
DateTime dt = DateTime.Parse(v.ToString());
|
||||||
|
json.Add(new JProperty(field.Name, dt));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
json.Add(new JProperty(field.Name, v));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AddTimestamp(json);
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReadColumnMap(ILogRecordset rs)
|
||||||
|
{
|
||||||
|
this.columnMap = new Dictionary<string, int>();
|
||||||
|
for (int col = 0; col < rs.getColumnCount(); col++)
|
||||||
|
{
|
||||||
|
string colName = rs.getColumnName(col);
|
||||||
|
this.columnMap[colName] = col;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddTimestamp(JObject json)
|
||||||
|
{
|
||||||
|
if (json["date"] != null && json["time"] != null)
|
||||||
|
{
|
||||||
|
var date = DateTime.Parse(json["date"].ToString());
|
||||||
|
var time = DateTime.Parse(json["time"].ToString());
|
||||||
|
date = new DateTime(date.Year, date.Month, date.Day, time.Hour, time.Minute, time.Second, time.Millisecond);
|
||||||
|
|
||||||
|
json.Add(new JProperty("@timestamp", date.ToString("yyyy-MM-ddTHH:mm:ss.fffZ")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using Microsoft.Win32;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -7,10 +8,11 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
using TimberWinR.Diagnostics;
|
||||||
|
|
||||||
namespace TimberWinR.Inputs
|
namespace TimberWinR.Inputs
|
||||||
{
|
{
|
||||||
public abstract class InputListener
|
public abstract class InputListener: IDiagnosable
|
||||||
{
|
{
|
||||||
public CancellationToken CancelToken { get; set; }
|
public CancellationToken CancelToken { get; set; }
|
||||||
public event Action<JObject> OnMessageRecieved;
|
public event Action<JObject> OnMessageRecieved;
|
||||||
@@ -18,6 +20,8 @@ namespace TimberWinR.Inputs
|
|||||||
private string _typeName;
|
private string _typeName;
|
||||||
public AutoResetEvent FinishedEvent { get; set; }
|
public AutoResetEvent FinishedEvent { get; set; }
|
||||||
public string CheckpointFileName { get; set; }
|
public string CheckpointFileName { get; set; }
|
||||||
|
private object _locker = new object();
|
||||||
|
public List<string> Files { get; set; }
|
||||||
|
|
||||||
public string InputType
|
public string InputType
|
||||||
{
|
{
|
||||||
@@ -28,6 +32,7 @@ namespace TimberWinR.Inputs
|
|||||||
|
|
||||||
public InputListener(CancellationToken token, string typeName)
|
public InputListener(CancellationToken token, string typeName)
|
||||||
{
|
{
|
||||||
|
Files = new List<string>();
|
||||||
CheckpointFileName = Path.Combine(System.IO.Path.GetTempPath(), string.Format("{0}.lpc", Guid.NewGuid().ToString()));
|
CheckpointFileName = Path.Combine(System.IO.Path.GetTempPath(), string.Format("{0}.lpc", Guid.NewGuid().ToString()));
|
||||||
|
|
||||||
this.FinishedEvent = new AutoResetEvent(false);
|
this.FinishedEvent = new AutoResetEvent(false);
|
||||||
@@ -40,6 +45,24 @@ namespace TimberWinR.Inputs
|
|||||||
.ToString();
|
.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetTypeName(string newType)
|
||||||
|
{
|
||||||
|
_typeName = newType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HaveSeenFile(string fileName)
|
||||||
|
{
|
||||||
|
return Files.Contains(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void SaveVisitedFileName(string fileName)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
if (!HaveSeenFile(fileName))
|
||||||
|
Files.Add(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
protected string ToPrintable(string inputString)
|
protected string ToPrintable(string inputString)
|
||||||
{
|
{
|
||||||
string asAscii = Encoding.ASCII.GetString(
|
string asAscii = Encoding.ASCII.GetString(
|
||||||
@@ -58,11 +81,17 @@ namespace TimberWinR.Inputs
|
|||||||
|
|
||||||
public void Finished()
|
public void Finished()
|
||||||
{
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Info("{0}: Signalling Event Shutdown {1}", Thread.CurrentThread.ManagedThreadId, InputType);
|
||||||
FinishedEvent.Set();
|
FinishedEvent.Set();
|
||||||
|
LogManager.GetCurrentClassLogger().Info("{0}: Finished signalling Shutdown {1}", Thread.CurrentThread.ManagedThreadId, InputType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void Shutdown()
|
public virtual void Shutdown()
|
||||||
{
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Info("{0}: Shutting Down {1}", Thread.CurrentThread.ManagedThreadId, InputType);
|
||||||
|
|
||||||
FinishedEvent.WaitOne();
|
FinishedEvent.WaitOne();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (File.Exists(CheckpointFileName))
|
if (File.Exists(CheckpointFileName))
|
||||||
@@ -73,8 +102,63 @@ namespace TimberWinR.Inputs
|
|||||||
LogManager.GetCurrentClassLogger().Error(ex);
|
LogManager.GetCurrentClassLogger().Error(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void AddOrModify(JObject json, string fieldName, string fieldValue)
|
||||||
|
{
|
||||||
|
if (json[fieldName] == null)
|
||||||
|
json.Add(fieldName, fieldValue);
|
||||||
|
else
|
||||||
|
json[fieldName] = fieldValue;
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual void AddDefaultFields(JObject json)
|
protected void RenameProperty(JObject json, string oldName, string newName)
|
||||||
|
{
|
||||||
|
JToken token = json[oldName];
|
||||||
|
if (token != null)
|
||||||
|
{
|
||||||
|
json.Add(newName, token.DeepClone());
|
||||||
|
json.Remove(oldName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected string ExpandField(string fieldName, JObject json)
|
||||||
|
{
|
||||||
|
foreach (var token in json.Children().ToList())
|
||||||
|
{
|
||||||
|
string replaceString = "%{" + token.Path + "}";
|
||||||
|
fieldName = fieldName.Replace(replaceString, json[token.Path].ToString());
|
||||||
|
}
|
||||||
|
return fieldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void EnsureRollingCaught()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const string mteKey = @"SYSTEM\CurrentControlSet\Control\FileSystem";
|
||||||
|
|
||||||
|
var mte = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(mteKey).GetValue("MaximumTunnelEntries");
|
||||||
|
if (mte == null || (int)mte != 0)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger()
|
||||||
|
.Error(
|
||||||
|
"HKLM\\{0}\\MaximumTunnelEntries is not set to accurately detect log rolling, a DWORD value of 0 is required.",
|
||||||
|
mteKey);
|
||||||
|
Microsoft.Win32.Registry.LocalMachine.CreateSubKey(mteKey).SetValue("MaximumTunnelEntries", 0, RegistryValueKind.DWord);
|
||||||
|
LogManager.GetCurrentClassLogger()
|
||||||
|
.Error(
|
||||||
|
"HKLM\\{0}\\MaximumTunnelEntries is now set to 0, A reboot is now required to fix this issue. See http://support.microsoft.com/en-us/kb/172190 for details",
|
||||||
|
mteKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Error(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public virtual void AddDefaultFields(JObject json)
|
||||||
{
|
{
|
||||||
if (json["type"] == null)
|
if (json["type"] == null)
|
||||||
json.Add(new JProperty("type", _typeName));
|
json.Add(new JProperty("type", _typeName));
|
||||||
@@ -94,7 +178,7 @@ namespace TimberWinR.Inputs
|
|||||||
json.Add(new JProperty("UtcTimestamp", utc.ToString("o")));
|
json.Add(new JProperty("UtcTimestamp", utc.ToString("o")));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void ProcessJson(JObject json)
|
public void ProcessJson(JObject json)
|
||||||
{
|
{
|
||||||
if (OnMessageRecieved != null)
|
if (OnMessageRecieved != null)
|
||||||
{
|
{
|
||||||
|
|||||||
278
TimberWinR/Inputs/LogsFileDatabase.cs
Normal file
278
TimberWinR/Inputs/LogsFileDatabase.cs
Normal file
@@ -0,0 +1,278 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using NLog;
|
||||||
|
using TimberWinR.Parser;
|
||||||
|
|
||||||
|
|
||||||
|
namespace TimberWinR.Inputs
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Maintain persistent state for Log files (to be used across restarts)
|
||||||
|
//
|
||||||
|
public class LogsFileDatabase
|
||||||
|
{
|
||||||
|
private static readonly object _locker = new object();
|
||||||
|
private List<LogsFileDatabaseEntry> Entries { get; set; }
|
||||||
|
private string DatabaseDirectory { get; set; }
|
||||||
|
public string DatabaseFileName
|
||||||
|
{
|
||||||
|
get { return Path.Combine(DatabaseDirectory, ".timberwinrdb"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Manager Manager { get; set; }
|
||||||
|
|
||||||
|
private static LogsFileDatabase instance;
|
||||||
|
|
||||||
|
private bool ExistingFile(string logName)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
return ExistingFileTest(logName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lookup the database entry for this log file, returns null if there isnt one.
|
||||||
|
//
|
||||||
|
private LogsFileDatabaseEntry FindFile(string logName)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
var existingEntry = (from e in Entries where e.FileName == logName select e).FirstOrDefault();
|
||||||
|
return existingEntry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ExistingFileTest(string logName)
|
||||||
|
{
|
||||||
|
var existingEntry = (from e in Entries where e.FileName == logName select e).FirstOrDefault();
|
||||||
|
return existingEntry != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveFileEntry(string logName)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
var existingEntry = (from e in Entries where e.FileName == logName select e).FirstOrDefault();
|
||||||
|
if (existingEntry != null)
|
||||||
|
{
|
||||||
|
Entries.Remove(existingEntry);
|
||||||
|
WriteDatabaseFileNoLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private LogsFileDatabaseEntry AddFileEntry(string logName)
|
||||||
|
{
|
||||||
|
var de = new LogsFileDatabaseEntry();
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
var fi = new FileInfo(logName);
|
||||||
|
de.FileName = logName;
|
||||||
|
de.LogFileExists = fi.Exists;
|
||||||
|
de.Previous = "";
|
||||||
|
de.NewFile = true;
|
||||||
|
de.ProcessedFile = false;
|
||||||
|
de.LastPosition = fi.Length;
|
||||||
|
de.SampleTime = DateTime.UtcNow;
|
||||||
|
de.CreationTimeUtc = fi.CreationTimeUtc;
|
||||||
|
|
||||||
|
Entries.Add(de);
|
||||||
|
WriteDatabaseFileNoLock();
|
||||||
|
}
|
||||||
|
return de;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LogsFileDatabaseEntry LookupLogFile(string logName)
|
||||||
|
{
|
||||||
|
LogsFileDatabaseEntry dbe = Instance.FindFile(logName);
|
||||||
|
if (dbe == null)
|
||||||
|
dbe = Instance.AddFileEntry(logName);
|
||||||
|
|
||||||
|
FileInfo fi = new FileInfo(logName);
|
||||||
|
|
||||||
|
dbe.LogFileExists = fi.Exists;
|
||||||
|
var creationTime = fi.CreationTimeUtc;
|
||||||
|
|
||||||
|
if (dbe.LogFileExists && creationTime != dbe.CreationTimeUtc)
|
||||||
|
{
|
||||||
|
dbe.NewFile = true;
|
||||||
|
dbe.Previous = "";
|
||||||
|
}
|
||||||
|
dbe.CreationTimeUtc = creationTime;
|
||||||
|
|
||||||
|
return dbe;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all the non-existent entries and remove them.
|
||||||
|
private void PruneFiles()
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
foreach(var entry in Entries.ToList())
|
||||||
|
{
|
||||||
|
FileInfo fi = new FileInfo(entry.FileName);
|
||||||
|
if (!fi.Exists)
|
||||||
|
Entries.Remove(entry);
|
||||||
|
}
|
||||||
|
WriteDatabaseFileNoLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Update(LogsFileDatabaseEntry dbe, bool processedFile, long lastOffset)
|
||||||
|
{
|
||||||
|
dbe.ProcessedFile = processedFile;
|
||||||
|
dbe.LogFileExists = File.Exists(dbe.FileName);
|
||||||
|
Instance.UpdateEntry(dbe, lastOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Roll(LogsFileDatabaseEntry dbe)
|
||||||
|
{
|
||||||
|
dbe.ProcessedFile = false;
|
||||||
|
dbe.LastPosition = 0;
|
||||||
|
dbe.Previous = "";
|
||||||
|
Instance.UpdateEntry(dbe, 0);
|
||||||
|
dbe.NewFile = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateEntry(LogsFileDatabaseEntry dbe, long lastOffset)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
var fi = new FileInfo(dbe.FileName);
|
||||||
|
dbe.NewFile = !fi.Exists;
|
||||||
|
dbe.CreationTimeUtc = fi.CreationTimeUtc;
|
||||||
|
dbe.SampleTime = DateTime.UtcNow;
|
||||||
|
dbe.LastPosition = lastOffset;
|
||||||
|
|
||||||
|
WriteDatabaseFileNoLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static LogsFileDatabase Instance
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (instance == null)
|
||||||
|
{
|
||||||
|
instance = new LogsFileDatabase(Manager.LogfileDir);
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
if (!Directory.Exists(instance.DatabaseDirectory))
|
||||||
|
Directory.CreateDirectory(instance.DatabaseDirectory);
|
||||||
|
// If it exists, read the current state, otherwise create an empty database.
|
||||||
|
if (File.Exists(instance.DatabaseFileName))
|
||||||
|
instance.ReadDatabaseNoLock();
|
||||||
|
else
|
||||||
|
instance.WriteDatabaseFileNoLock();
|
||||||
|
|
||||||
|
if (instance.Entries == null)
|
||||||
|
instance.Entries = new List<LogsFileDatabaseEntry>();
|
||||||
|
|
||||||
|
instance.PruneFiles();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Serialize in the Database
|
||||||
|
private void ReadDatabaseNoLock()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var serializer = new JsonSerializer();
|
||||||
|
if (File.Exists(DatabaseFileName))
|
||||||
|
Entries =
|
||||||
|
JsonConvert.DeserializeObject<List<LogsFileDatabaseEntry>>(File.ReadAllText(DatabaseFileName));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger()
|
||||||
|
.Error("Error reading database '{0}': {1}", DatabaseFileName, ex.ToString());
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (File.Exists(DatabaseFileName))
|
||||||
|
File.Delete(DatabaseFileName);
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Creating New Database '{0}'", DatabaseFileName);
|
||||||
|
WriteDatabaseLock();
|
||||||
|
}
|
||||||
|
catch (Exception ex2)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Error Creating New Database '{0}': {1}", DatabaseFileName, ex2.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void WriteDatabaseFileNoLock()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.WriteAllText(DatabaseFileName, JsonConvert.SerializeObject(instance.Entries), Encoding.UTF8);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger()
|
||||||
|
.Error("Error saving database '{0}': {1}", DatabaseFileName, ex.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void ReadDatabaseLock()
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
ReadDatabaseNoLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
private void WriteDatabaseLock()
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
WriteDatabaseFileNoLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private LogsFileDatabase(string databaseDirectory)
|
||||||
|
{
|
||||||
|
DatabaseDirectory = databaseDirectory;
|
||||||
|
Entries = new List<LogsFileDatabaseEntry>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Represents a log file to be tailed
|
||||||
|
//
|
||||||
|
public class LogsFileDatabaseEntry
|
||||||
|
{
|
||||||
|
[JsonIgnore]
|
||||||
|
public bool NewFile { get; set; }
|
||||||
|
public bool ProcessedFile { get; set; }
|
||||||
|
public bool LogFileExists { get; set; }
|
||||||
|
public string FileName { get; set; }
|
||||||
|
public DateTime CreationTimeUtc { get; set; }
|
||||||
|
public DateTime SampleTime { get; set; }
|
||||||
|
public long LastPosition { get; set; }
|
||||||
|
public long LinesProcessed
|
||||||
|
{
|
||||||
|
get { return _linesProcessed; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private int _linesProcessed;
|
||||||
|
public void IncrementLineCount()
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref _linesProcessed);
|
||||||
|
}
|
||||||
|
public string Previous { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,19 +1,26 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net.Configuration;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Interop.MSUtil;
|
using Interop.MSUtil;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Newtonsoft.Json.Serialization;
|
using Newtonsoft.Json.Serialization;
|
||||||
using NLog;
|
|
||||||
|
|
||||||
|
using NLog;
|
||||||
|
using TimberWinR.Codecs;
|
||||||
using LogQuery = Interop.MSUtil.LogQueryClassClass;
|
using LogQuery = Interop.MSUtil.LogQueryClassClass;
|
||||||
using TextLineInputFormat = Interop.MSUtil.COMTextLineInputContextClass;
|
using TextLineInputFormat = Interop.MSUtil.COMTextLineInputContextClass;
|
||||||
using LogRecordSet = Interop.MSUtil.ILogRecordset;
|
using LogRecordSet = Interop.MSUtil.ILogRecordset;
|
||||||
|
using TimberWinR.Parser;
|
||||||
|
|
||||||
namespace TimberWinR.Inputs
|
namespace TimberWinR.Inputs
|
||||||
{
|
{
|
||||||
@@ -22,114 +29,261 @@ namespace TimberWinR.Inputs
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class LogsListener : InputListener
|
public class LogsListener : InputListener
|
||||||
{
|
{
|
||||||
|
private object _locker = new object();
|
||||||
private int _pollingIntervalInSeconds;
|
private int _pollingIntervalInSeconds;
|
||||||
private TimberWinR.Parser.Log _arguments;
|
private TimberWinR.Parser.LogParameters _arguments;
|
||||||
private long _receivedMessages;
|
private long _receivedMessages;
|
||||||
|
private CodecArguments _codecArguments;
|
||||||
public LogsListener(TimberWinR.Parser.Log arguments, CancellationToken cancelToken, int pollingIntervalInSeconds = 3)
|
private ICodec _codec;
|
||||||
|
|
||||||
|
public bool Stop { get; set; }
|
||||||
|
public bool IsWildcardFilePattern { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public LogsListener(TimberWinR.Parser.LogParameters arguments, CancellationToken cancelToken)
|
||||||
: base(cancelToken, "Win32-FileLog")
|
: base(cancelToken, "Win32-FileLog")
|
||||||
{
|
{
|
||||||
|
Stop = false;
|
||||||
|
|
||||||
|
EnsureRollingCaught();
|
||||||
|
|
||||||
|
_codecArguments = arguments.CodecArguments;
|
||||||
|
|
||||||
|
_codecArguments = arguments.CodecArguments;
|
||||||
|
if (_codecArguments != null && _codecArguments.Type == CodecArguments.CodecType.multiline)
|
||||||
|
_codec = new Multiline(_codecArguments);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(arguments.Type))
|
||||||
|
SetTypeName(arguments.Type);
|
||||||
|
|
||||||
_receivedMessages = 0;
|
_receivedMessages = 0;
|
||||||
_arguments = arguments;
|
_arguments = arguments;
|
||||||
_pollingIntervalInSeconds = pollingIntervalInSeconds;
|
_pollingIntervalInSeconds = arguments.Interval;
|
||||||
var task = new Task(FileWatcher, cancelToken);
|
|
||||||
task.Start();
|
IsWildcardFilePattern = arguments.Location.Contains('*');
|
||||||
|
|
||||||
|
foreach (string srcFile in _arguments.Location.Split(','))
|
||||||
|
{
|
||||||
|
string file = srcFile.Trim();
|
||||||
|
string dir = Path.GetDirectoryName(file);
|
||||||
|
if (string.IsNullOrEmpty(dir))
|
||||||
|
dir = Environment.CurrentDirectory;
|
||||||
|
string fileSpec = Path.Combine(dir, file);
|
||||||
|
|
||||||
|
Task.Factory.StartNew(() => FileWatcher(fileSpec));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Shutdown()
|
public override void Shutdown()
|
||||||
{
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Shutting Down {0}", InputType);
|
||||||
|
Stop = true;
|
||||||
base.Shutdown();
|
base.Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public override JObject ToJson()
|
public override JObject ToJson()
|
||||||
{
|
{
|
||||||
JObject json = new JObject(
|
JObject json = new JObject(
|
||||||
new JProperty("log",
|
new JProperty("log",
|
||||||
new JObject(
|
new JObject(
|
||||||
new JProperty("messages", _receivedMessages),
|
new JProperty("messages", _receivedMessages),
|
||||||
|
new JProperty("type", InputType),
|
||||||
new JProperty("location", _arguments.Location),
|
new JProperty("location", _arguments.Location),
|
||||||
|
new JProperty("logSource", _arguments.LogSource),
|
||||||
new JProperty("codepage", _arguments.CodePage),
|
new JProperty("codepage", _arguments.CodePage),
|
||||||
new JProperty("splitLongLines", _arguments.SplitLongLines),
|
new JProperty("splitLongLines", _arguments.SplitLongLines),
|
||||||
new JProperty("recurse", _arguments.Recurse)
|
new JProperty("recurse", _arguments.Recurse),
|
||||||
|
new JProperty("filedb",
|
||||||
|
new JArray(from f in Files.ToList()
|
||||||
|
select JObject.FromObject(LogsFileDatabase.LookupLogFile(f)))),
|
||||||
|
new JProperty("files",
|
||||||
|
new JArray(from f in Files.ToList()
|
||||||
|
select new JValue(f)))
|
||||||
)));
|
)));
|
||||||
|
|
||||||
|
|
||||||
|
if (_codecArguments != null)
|
||||||
|
{
|
||||||
|
var cp = new JProperty("codec",
|
||||||
|
new JArray(
|
||||||
|
new JObject(
|
||||||
|
new JProperty("type", _codecArguments.Type.ToString()),
|
||||||
|
new JProperty("what", _codecArguments.What.ToString()),
|
||||||
|
new JProperty("negate", _codecArguments.Negate),
|
||||||
|
new JProperty("multilineTag", _codecArguments.MultilineTag),
|
||||||
|
new JProperty("pattern", _codecArguments.Pattern))));
|
||||||
|
json.Add(cp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FileWatcher()
|
private void FileWatcher(string fileToWatch)
|
||||||
{
|
{
|
||||||
var iFmt = new TextLineInputFormat()
|
var iFmt = new TextLineInputFormat()
|
||||||
{
|
{
|
||||||
iCodepage = _arguments.CodePage,
|
iCodepage = _arguments.CodePage,
|
||||||
splitLongLines = _arguments.SplitLongLines,
|
splitLongLines = _arguments.SplitLongLines,
|
||||||
iCheckpoint = CheckpointFileName,
|
|
||||||
recurse = _arguments.Recurse
|
recurse = _arguments.Recurse
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create the query
|
Dictionary<string, string> _fnfmap = new Dictionary<string, string>();
|
||||||
var query = string.Format("SELECT * FROM {0}", _arguments.Location);
|
|
||||||
|
|
||||||
var firstQuery = true;
|
using (var syncHandle = new ManualResetEventSlim())
|
||||||
// Execute the query
|
|
||||||
while (!CancelToken.IsCancellationRequested)
|
|
||||||
{
|
{
|
||||||
var oLogQuery = new LogQuery();
|
// Execute the query
|
||||||
try
|
while (!Stop)
|
||||||
{
|
{
|
||||||
var rs = oLogQuery.Execute(query, iFmt);
|
var oLogQuery = new LogQuery();
|
||||||
Dictionary<string, int> colMap = new Dictionary<string, int>();
|
if (!CancelToken.IsCancellationRequested)
|
||||||
for (int col=0; col<rs.getColumnCount(); col++)
|
|
||||||
{
|
{
|
||||||
string colName = rs.getColumnName(col);
|
try
|
||||||
colMap[colName] = col;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Browse the recordset
|
|
||||||
for (; !rs.atEnd(); rs.moveNext())
|
|
||||||
{
|
|
||||||
// We want to "tail" the log, so skip the first query results.
|
|
||||||
if (!firstQuery)
|
|
||||||
{
|
{
|
||||||
var record = rs.getRecord();
|
var qfiles = string.Format("SELECT Distinct [LogFilename] FROM {0}", fileToWatch);
|
||||||
var json = new JObject();
|
var rsfiles = oLogQuery.Execute(qfiles, iFmt);
|
||||||
foreach (var field in _arguments.Fields)
|
for (; !rsfiles.atEnd(); rsfiles.moveNext())
|
||||||
{
|
{
|
||||||
if (!colMap.ContainsKey(field.Name))
|
var record = rsfiles.getRecord();
|
||||||
continue;
|
string logName = record.getValue("LogFilename") as string;
|
||||||
|
FileInfo fi = new FileInfo(logName);
|
||||||
|
|
||||||
object v = record.getValue(field.Name);
|
var dbe = LogsFileDatabase.LookupLogFile(logName);
|
||||||
if (field.DataType == typeof (DateTime))
|
|
||||||
|
SaveVisitedFileName(dbe.FileName);
|
||||||
|
|
||||||
|
DateTime creationTime = fi.CreationTimeUtc;
|
||||||
|
bool logHasRolled = dbe.NewFile || (creationTime != dbe.CreationTimeUtc || fi.Length < dbe.LastPosition);
|
||||||
|
|
||||||
|
if (logHasRolled)
|
||||||
{
|
{
|
||||||
DateTime dt = DateTime.Parse(v.ToString());
|
LogManager.GetCurrentClassLogger().Info("Log {0} has rolled", logName);
|
||||||
json.Add(new JProperty(field.Name, dt));
|
LogsFileDatabase.Roll(dbe);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
json.Add(new JProperty(field.Name, v));
|
// Log has rolled or this is a new file, or we haven't processed yet.
|
||||||
|
bool processWholeFile = logHasRolled || !dbe.ProcessedFile;
|
||||||
|
|
||||||
|
if (processWholeFile)
|
||||||
|
LogsFileDatabase.Update(dbe, true, 0);
|
||||||
|
|
||||||
}
|
}
|
||||||
string msg = json["Text"].ToString();
|
rsfiles.close();
|
||||||
if (!string.IsNullOrEmpty(msg))
|
foreach (string fileName in Files.ToList())
|
||||||
{
|
{
|
||||||
ProcessJson(json);
|
var dbe = LogsFileDatabase.LookupLogFile(fileName);
|
||||||
_receivedMessages++;
|
|
||||||
|
var lastRecordNumber = dbe.LastPosition;
|
||||||
|
var query = string.Format("SELECT * FROM {0} where Index > {1}", fileName,
|
||||||
|
lastRecordNumber);
|
||||||
|
|
||||||
|
var rs = oLogQuery.Execute(query, iFmt);
|
||||||
|
Dictionary<string, int> colMap = new Dictionary<string, int>();
|
||||||
|
for (int col = 0; col < rs.getColumnCount(); col++)
|
||||||
|
{
|
||||||
|
string colName = rs.getColumnName(col);
|
||||||
|
colMap[colName] = col;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Browse the recordset
|
||||||
|
for (; !rs.atEnd(); rs.moveNext())
|
||||||
|
{
|
||||||
|
var record = rs.getRecord();
|
||||||
|
|
||||||
|
var json = new JObject();
|
||||||
|
foreach (var field in _arguments.Fields)
|
||||||
|
{
|
||||||
|
if (!colMap.ContainsKey(field.Name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (json["logSource"] == null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(_arguments.LogSource))
|
||||||
|
json.Add(new JProperty("logSource", fileName));
|
||||||
|
else
|
||||||
|
json.Add(new JProperty("logSource", _arguments.LogSource));
|
||||||
|
}
|
||||||
|
|
||||||
|
object v = record.getValue(field.Name);
|
||||||
|
if (field.DataType == typeof(DateTime))
|
||||||
|
{
|
||||||
|
DateTime dt = DateTime.Parse(v.ToString());
|
||||||
|
json.Add(new JProperty(field.Name, dt));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
json.Add(new JProperty(field.Name, v));
|
||||||
|
}
|
||||||
|
string msg = json["Text"].ToString();
|
||||||
|
if (!string.IsNullOrEmpty(msg))
|
||||||
|
{
|
||||||
|
if (_codecArguments != null && _codecArguments.Type == CodecArguments.CodecType.multiline)
|
||||||
|
{
|
||||||
|
_codec.Apply(msg, this);
|
||||||
|
_receivedMessages++;
|
||||||
|
dbe.IncrementLineCount();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ProcessJson(json);
|
||||||
|
dbe.IncrementLineCount();
|
||||||
|
_receivedMessages++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var lrn = (Int64)record.getValueEx("Index");
|
||||||
|
LogsFileDatabase.Update(dbe, true, lrn);
|
||||||
|
GC.Collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
colMap.Clear();
|
||||||
|
// Close the recordset
|
||||||
|
rs.close();
|
||||||
|
rs = null;
|
||||||
|
GC.Collect();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException fnfex)
|
||||||
|
{
|
||||||
|
string fn = fnfex.FileName;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(fn) && !_fnfmap.ContainsKey(fn))
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Warn(fnfex.Message);
|
||||||
|
_fnfmap[fn] = fn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Error(ex);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
oLogQuery = null;
|
||||||
|
// Sleep
|
||||||
|
if (!Stop)
|
||||||
|
syncHandle.Wait(TimeSpan.FromSeconds(_pollingIntervalInSeconds), CancelToken);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
catch (Exception ex1)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Warn(ex1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Close the recordset
|
|
||||||
rs.close();
|
|
||||||
rs = null;
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
Finished();
|
||||||
{
|
|
||||||
LogManager.GetCurrentClassLogger().Error(ex);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
oLogQuery = null;
|
|
||||||
}
|
|
||||||
firstQuery = false;
|
|
||||||
System.Threading.Thread.Sleep(_pollingIntervalInSeconds * 1000);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Finished();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,37 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
using TimberWinR.Codecs;
|
||||||
|
using TimberWinR.Parser;
|
||||||
|
|
||||||
namespace TimberWinR.Inputs
|
namespace TimberWinR.Inputs
|
||||||
{
|
{
|
||||||
public class StdinListener : InputListener
|
public class StdinListener : InputListener
|
||||||
{
|
{
|
||||||
|
[DllImport("User32.Dll", EntryPoint = "PostMessageA")]
|
||||||
|
private static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
|
||||||
|
|
||||||
private Thread _listenThread;
|
private Thread _listenThread;
|
||||||
|
private CodecArguments _codecArguments;
|
||||||
|
private ICodec _codec;
|
||||||
|
const int VK_RETURN = 0x0D;
|
||||||
|
const int WM_KEYDOWN = 0x100;
|
||||||
|
|
||||||
public StdinListener(CancellationToken cancelToken)
|
public StdinListener(TimberWinR.Parser.Stdin arguments, CancellationToken cancelToken)
|
||||||
: base(cancelToken, "Win32-Console")
|
: base(cancelToken, "Win32-Console")
|
||||||
{
|
{
|
||||||
|
_codecArguments = arguments.CodecArguments;
|
||||||
|
if (_codecArguments != null && _codecArguments.Type == CodecArguments.CodecType.multiline)
|
||||||
|
_codec = new Multiline(_codecArguments);
|
||||||
|
|
||||||
_listenThread = new Thread(new ThreadStart(ListenToStdin));
|
_listenThread = new Thread(new ThreadStart(ListenToStdin));
|
||||||
_listenThread.Start();
|
_listenThread.Start();
|
||||||
}
|
}
|
||||||
@@ -23,12 +39,35 @@ namespace TimberWinR.Inputs
|
|||||||
public override JObject ToJson()
|
public override JObject ToJson()
|
||||||
{
|
{
|
||||||
JObject json = new JObject(
|
JObject json = new JObject(
|
||||||
new JProperty("stdin", "enabled"));
|
new JProperty("stdin", "enabled"));
|
||||||
|
|
||||||
|
|
||||||
|
if (_codecArguments != null)
|
||||||
|
{
|
||||||
|
var cp = new JProperty("codec",
|
||||||
|
new JArray(
|
||||||
|
new JObject(
|
||||||
|
new JProperty("type", _codecArguments.Type.ToString()),
|
||||||
|
new JProperty("what", _codecArguments.What.ToString()),
|
||||||
|
new JProperty("negate", _codecArguments.Negate),
|
||||||
|
new JProperty("multilineTag", _codecArguments.MultilineTag),
|
||||||
|
new JProperty("pattern", _codecArguments.Pattern))));
|
||||||
|
json.Add(cp);
|
||||||
|
}
|
||||||
|
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Shutdown()
|
public override void Shutdown()
|
||||||
{
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Shutting Down {0}", InputType);
|
||||||
|
// This must come from another thread.
|
||||||
|
ThreadPool.QueueUserWorkItem((o) =>
|
||||||
|
{
|
||||||
|
Thread.Sleep(100);
|
||||||
|
var hWnd = System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle;
|
||||||
|
PostMessage(hWnd, WM_KEYDOWN, VK_RETURN, 0);
|
||||||
|
});
|
||||||
base.Shutdown();
|
base.Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,18 +76,22 @@ namespace TimberWinR.Inputs
|
|||||||
LogManager.GetCurrentClassLogger().Info("StdIn Ready");
|
LogManager.GetCurrentClassLogger().Info("StdIn Ready");
|
||||||
|
|
||||||
while (!CancelToken.IsCancellationRequested)
|
while (!CancelToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
string line = Console.ReadLine();
|
string line = Console.ReadLine();
|
||||||
if (line != null)
|
if (line != null)
|
||||||
{
|
{
|
||||||
string msg = ToPrintable(line);
|
string msg = ToPrintable(line);
|
||||||
JObject jo = new JObject();
|
|
||||||
jo["message"] = msg;
|
if (_codecArguments != null && _codecArguments.Type == CodecArguments.CodecType.multiline)
|
||||||
AddDefaultFields(jo);
|
_codec.Apply(msg, this);
|
||||||
ProcessJson(jo);
|
else
|
||||||
}
|
{
|
||||||
else
|
JObject jo = new JObject();
|
||||||
break;
|
jo["message"] = msg;
|
||||||
|
AddDefaultFields(jo);
|
||||||
|
ProcessJson(jo);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Finished();
|
Finished();
|
||||||
}
|
}
|
||||||
|
|||||||
321
TimberWinR/Inputs/TailFileListener.cs
Normal file
321
TimberWinR/Inputs/TailFileListener.cs
Normal file
@@ -0,0 +1,321 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Configuration;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.IO;
|
||||||
|
using Interop.MSUtil;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
|
||||||
|
using NLog;
|
||||||
|
using NLog.LayoutRenderers;
|
||||||
|
using TimberWinR.Codecs;
|
||||||
|
using TimberWinR.Parser;
|
||||||
|
|
||||||
|
namespace TimberWinR.Inputs
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Tail a file.
|
||||||
|
/// </summary>
|
||||||
|
public class TailFileListener : InputListener
|
||||||
|
{
|
||||||
|
private object _locker = new object();
|
||||||
|
private int _pollingIntervalInSeconds;
|
||||||
|
private TimberWinR.Parser.TailFileArguments _arguments;
|
||||||
|
private long _receivedMessages;
|
||||||
|
private long _errorCount;
|
||||||
|
private CodecArguments _codecArguments;
|
||||||
|
private ICodec _codec;
|
||||||
|
|
||||||
|
public bool Stop { get; set; }
|
||||||
|
|
||||||
|
public TailFileListener(TimberWinR.Parser.TailFileArguments arguments,
|
||||||
|
CancellationToken cancelToken)
|
||||||
|
: base(cancelToken, "Win32-TailLog")
|
||||||
|
{
|
||||||
|
Stop = false;
|
||||||
|
|
||||||
|
EnsureRollingCaught();
|
||||||
|
|
||||||
|
_codecArguments = arguments.CodecArguments;
|
||||||
|
if (_codecArguments != null && _codecArguments.Type == CodecArguments.CodecType.multiline)
|
||||||
|
_codec = new Multiline(_codecArguments);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(arguments.Type))
|
||||||
|
SetTypeName(arguments.Type);
|
||||||
|
|
||||||
|
_receivedMessages = 0;
|
||||||
|
_arguments = arguments;
|
||||||
|
_pollingIntervalInSeconds = arguments.Interval;
|
||||||
|
|
||||||
|
foreach (string srcFile in _arguments.Location.Split(','))
|
||||||
|
{
|
||||||
|
string file = srcFile.Trim();
|
||||||
|
Task.Factory.StartNew(() => TailFileWatcher(file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Shutdown()
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger()
|
||||||
|
.Info("{0}: Shutting Down {1} for {2}", Thread.CurrentThread.ManagedThreadId, InputType,
|
||||||
|
_arguments.Location);
|
||||||
|
Stop = true;
|
||||||
|
base.Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override JObject ToJson()
|
||||||
|
{
|
||||||
|
JObject json = new JObject(
|
||||||
|
new JProperty("taillog",
|
||||||
|
new JObject(
|
||||||
|
new JProperty("messages", _receivedMessages),
|
||||||
|
new JProperty("errors", _errorCount),
|
||||||
|
new JProperty("type", InputType),
|
||||||
|
new JProperty("location", _arguments.Location),
|
||||||
|
new JProperty("logSource", _arguments.LogSource),
|
||||||
|
new JProperty("recurse", _arguments.Recurse),
|
||||||
|
new JProperty("files",
|
||||||
|
new JArray(from f in Files
|
||||||
|
select new JValue(f))),
|
||||||
|
new JProperty("filedb",
|
||||||
|
new JArray(from f in Files
|
||||||
|
select JObject.FromObject(LogsFileDatabase.LookupLogFile(f))))
|
||||||
|
)));
|
||||||
|
|
||||||
|
|
||||||
|
if (_codecArguments != null)
|
||||||
|
{
|
||||||
|
var cp = new JProperty("codec",
|
||||||
|
new JArray(
|
||||||
|
new JObject(
|
||||||
|
new JProperty("type", _codecArguments.Type.ToString()),
|
||||||
|
new JProperty("what", _codecArguments.What.ToString()),
|
||||||
|
new JProperty("negate", _codecArguments.Negate),
|
||||||
|
new JProperty("multilineTag", _codecArguments.MultilineTag),
|
||||||
|
new JProperty("pattern", _codecArguments.Pattern))));
|
||||||
|
json.Add(cp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TailFileContents(string fileName, long offset, LogsFileDatabaseEntry dbe)
|
||||||
|
{
|
||||||
|
const int bufSize = 16535;
|
||||||
|
long prevLen = offset;
|
||||||
|
|
||||||
|
FileInfo fi = new FileInfo(fileName);
|
||||||
|
if (!fi.Exists)
|
||||||
|
return;
|
||||||
|
|
||||||
|
LogManager.GetCurrentClassLogger().Trace(":{0} Tailing File: {1} as Pos: {2}", Thread.CurrentThread.ManagedThreadId, fileName, prevLen);
|
||||||
|
|
||||||
|
using (var stream = new FileStream(fi.FullName, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.ReadWrite))
|
||||||
|
{
|
||||||
|
stream.Seek(prevLen, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
char[] buffer = new char[bufSize];
|
||||||
|
StringBuilder current = new StringBuilder();
|
||||||
|
using (StreamReader sr = new StreamReader(stream))
|
||||||
|
{
|
||||||
|
int nRead;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// Read a buffered amount
|
||||||
|
nRead = sr.ReadBlock(buffer, 0, bufSize);
|
||||||
|
for (int i = 0; i < nRead; ++i)
|
||||||
|
{
|
||||||
|
// We need the terminator!
|
||||||
|
if (buffer[i] == '\n' || buffer[i] == '\r')
|
||||||
|
{
|
||||||
|
if (current.Length > 0)
|
||||||
|
{
|
||||||
|
string line = string.Concat(dbe.Previous, current);
|
||||||
|
var json = new JObject();
|
||||||
|
|
||||||
|
if (json["logSource"] == null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(_arguments.LogSource))
|
||||||
|
json.Add(new JProperty("logSource", fileName));
|
||||||
|
else
|
||||||
|
json.Add(new JProperty("logSource", _arguments.LogSource));
|
||||||
|
}
|
||||||
|
|
||||||
|
//LogManager.GetCurrentClassLogger().Debug(":{0} File: {1}:{2} {3}", Thread.CurrentThread.ManagedThreadId, fileName, dbe.LinesProcessed, line);
|
||||||
|
|
||||||
|
// We've processed the partial input
|
||||||
|
dbe.Previous = "";
|
||||||
|
json["Text"] = line;
|
||||||
|
json["Index"] = dbe.LinesProcessed;
|
||||||
|
json["LogFileName"] = fileName;
|
||||||
|
if (_codecArguments != null && _codecArguments.Type == CodecArguments.CodecType.multiline)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_codec.Apply(line, this);
|
||||||
|
Interlocked.Increment(ref _receivedMessages);
|
||||||
|
dbe.IncrementLineCount();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref _errorCount);
|
||||||
|
LogManager.GetCurrentClassLogger().ErrorException("Filter Error", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ProcessJson(json);
|
||||||
|
dbe.IncrementLineCount();
|
||||||
|
Interlocked.Increment(ref _receivedMessages);
|
||||||
|
LogsFileDatabase.Update(dbe, true, sr.BaseStream.Position);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref _errorCount);
|
||||||
|
LogManager.GetCurrentClassLogger().ErrorException("Process Error", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
current = new StringBuilder();
|
||||||
|
}
|
||||||
|
else // Copy character into the buffer
|
||||||
|
{
|
||||||
|
current.Append(buffer[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (nRead > 0);
|
||||||
|
|
||||||
|
// We didn't encounter the newline, so save it.
|
||||||
|
if (current.Length > 0)
|
||||||
|
{
|
||||||
|
dbe.Previous = current.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// One thread for each kind of file to watch, i.e. "*.log,*.txt" would be two separate
|
||||||
|
// threads.
|
||||||
|
private void TailFileWatcher(string fileToWatch)
|
||||||
|
{
|
||||||
|
Dictionary<string, string> _fnfmap = new Dictionary<string, string>();
|
||||||
|
|
||||||
|
using (var syncHandle = new ManualResetEventSlim())
|
||||||
|
{
|
||||||
|
// Execute the query
|
||||||
|
while (!Stop && !CancelToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!CancelToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
var isWildcardPattern = fileToWatch.Contains('*');
|
||||||
|
string path = Path.GetDirectoryName(fileToWatch);
|
||||||
|
string name = Path.GetFileName(fileToWatch);
|
||||||
|
if (string.IsNullOrEmpty(path))
|
||||||
|
path = ".";
|
||||||
|
|
||||||
|
LogManager.GetCurrentClassLogger().Trace(":{0} Tailing File: {1}", Thread.CurrentThread.ManagedThreadId, Path.Combine(path, name));
|
||||||
|
|
||||||
|
// Ok, we have a potential file filter here as 'fileToWatch' could be foo.log or *.log
|
||||||
|
|
||||||
|
SearchOption so = SearchOption.TopDirectoryOnly;
|
||||||
|
if (_arguments.Recurse == -1)
|
||||||
|
so = SearchOption.AllDirectories;
|
||||||
|
|
||||||
|
foreach (string fileName in Directory.GetFiles(path, name, so))
|
||||||
|
{
|
||||||
|
var dbe = LogsFileDatabase.LookupLogFile(fileName);
|
||||||
|
|
||||||
|
// We only spin up 1 thread for a file we haven't yet seen.
|
||||||
|
if (isWildcardPattern && !HaveSeenFile(fileName) && dbe.NewFile)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Debug(":{0} Starting Thread Tailing File: {1}", Thread.CurrentThread.ManagedThreadId, dbe.FileName);
|
||||||
|
LogsFileDatabase.Update(dbe, false, dbe.LastPosition);
|
||||||
|
|
||||||
|
Task.Factory.StartNew(() => TailFileWatcher(fileName));
|
||||||
|
}
|
||||||
|
else if (!isWildcardPattern)
|
||||||
|
{
|
||||||
|
FileInfo fi = new FileInfo(dbe.FileName);
|
||||||
|
SaveVisitedFileName(fileName);
|
||||||
|
|
||||||
|
//LogManager.GetCurrentClassLogger().Info("Located File: {0}, New: {1}", dbe.FileName, dbe.NewFile);
|
||||||
|
long length = fi.Length;
|
||||||
|
bool logHasRolled = false;
|
||||||
|
if (fi.Length < dbe.LastPosition || fi.CreationTimeUtc != dbe.CreationTimeUtc)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Info("{0}: Log has Rolled: {1}", Thread.CurrentThread.ManagedThreadId, dbe.FileName);
|
||||||
|
logHasRolled = true;
|
||||||
|
LogsFileDatabase.Roll(dbe);
|
||||||
|
}
|
||||||
|
// Log has rolled or this is a file we are seeing for the first time.
|
||||||
|
bool processWholeFile = logHasRolled || !dbe.ProcessedFile || dbe.NewFile;
|
||||||
|
if (processWholeFile)
|
||||||
|
{
|
||||||
|
LogsFileDatabase.Update(dbe, true, 0);
|
||||||
|
LogManager.GetCurrentClassLogger().Debug("{0}: Process Whole File: {1}", Thread.CurrentThread.ManagedThreadId, dbe.FileName);
|
||||||
|
TailFileContents(dbe.FileName, 0, dbe);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TailFileContents(dbe.FileName, dbe.LastPosition, dbe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException fnfex)
|
||||||
|
{
|
||||||
|
string fn = fnfex.FileName;
|
||||||
|
|
||||||
|
if (!_fnfmap.ContainsKey(fn))
|
||||||
|
LogManager.GetCurrentClassLogger().Warn(fnfex.Message);
|
||||||
|
_fnfmap[fn] = fn;
|
||||||
|
}
|
||||||
|
catch (IOException ioex)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Debug("Log has rolled: {0}", ioex.Message);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Error(ex);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!Stop)
|
||||||
|
syncHandle.Wait(TimeSpan.FromSeconds(_pollingIntervalInSeconds), CancelToken);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
Stop = true;
|
||||||
|
}
|
||||||
|
catch (Exception ex1)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Warn(ex1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Finished();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
|
||||||
@@ -18,7 +16,9 @@ namespace TimberWinR.Inputs
|
|||||||
private Thread _listenThreadV4;
|
private Thread _listenThreadV4;
|
||||||
private Thread _listenThreadV6;
|
private Thread _listenThreadV6;
|
||||||
private readonly int _port;
|
private readonly int _port;
|
||||||
|
private TimberWinR.Parser.TcpParameters _arguments;
|
||||||
private long _receivedMessages;
|
private long _receivedMessages;
|
||||||
|
private long _errorCount;
|
||||||
|
|
||||||
public override JObject ToJson()
|
public override JObject ToJson()
|
||||||
{
|
{
|
||||||
@@ -26,20 +26,25 @@ namespace TimberWinR.Inputs
|
|||||||
new JProperty("tcp",
|
new JProperty("tcp",
|
||||||
new JObject(
|
new JObject(
|
||||||
new JProperty("port", _port),
|
new JProperty("port", _port),
|
||||||
|
new JProperty("errors", _errorCount),
|
||||||
new JProperty("messages", _receivedMessages)
|
new JProperty("messages", _receivedMessages)
|
||||||
)));
|
)));
|
||||||
|
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TcpInputListener(CancellationToken cancelToken, int port = 5140)
|
public TcpInputListener(TimberWinR.Parser.TcpParameters arguments, CancellationToken cancelToken, int port = 5140)
|
||||||
: base(cancelToken, "Win32-Tcp")
|
: base(cancelToken, "Win32-Tcp")
|
||||||
{
|
{
|
||||||
_port = port;
|
_port = port;
|
||||||
|
_arguments = arguments;
|
||||||
|
|
||||||
LogManager.GetCurrentClassLogger().Info("Tcp Input(v4/v6) on Port {0} Ready", _port);
|
LogManager.GetCurrentClassLogger().Info("Tcp Input(v4/v6) on Port {0} Ready", _port);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(arguments.Type))
|
||||||
|
SetTypeName(arguments.Type);
|
||||||
|
|
||||||
|
_receivedMessages = 0;
|
||||||
|
|
||||||
_tcpListenerV6 = new System.Net.Sockets.TcpListener(IPAddress.IPv6Any, port);
|
_tcpListenerV6 = new System.Net.Sockets.TcpListener(IPAddress.IPv6Any, port);
|
||||||
_tcpListenerV4 = new System.Net.Sockets.TcpListener(IPAddress.Any, port);
|
_tcpListenerV4 = new System.Net.Sockets.TcpListener(IPAddress.Any, port);
|
||||||
|
|
||||||
@@ -53,6 +58,8 @@ namespace TimberWinR.Inputs
|
|||||||
|
|
||||||
public override void Shutdown()
|
public override void Shutdown()
|
||||||
{
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Info("{0}: Shutting Down {1}", Thread.CurrentThread.ManagedThreadId, InputType);
|
||||||
|
|
||||||
this._tcpListenerV4.Stop();
|
this._tcpListenerV4.Stop();
|
||||||
this._tcpListenerV6.Stop();
|
this._tcpListenerV6.Stop();
|
||||||
|
|
||||||
@@ -67,7 +74,6 @@ namespace TimberWinR.Inputs
|
|||||||
|
|
||||||
listener.Start();
|
listener.Start();
|
||||||
|
|
||||||
|
|
||||||
while (!CancelToken.IsCancellationRequested)
|
while (!CancelToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -78,7 +84,7 @@ namespace TimberWinR.Inputs
|
|||||||
// Wait for a client, spin up a thread.
|
// Wait for a client, spin up a thread.
|
||||||
var clientThread = new Thread(new ParameterizedThreadStart(HandleNewClient));
|
var clientThread = new Thread(new ParameterizedThreadStart(HandleNewClient));
|
||||||
clientThread.Start(client);
|
clientThread.Start(client);
|
||||||
}
|
}
|
||||||
catch (SocketException ex)
|
catch (SocketException ex)
|
||||||
{
|
{
|
||||||
if (ex.SocketErrorCode == SocketError.Interrupted)
|
if (ex.SocketErrorCode == SocketError.Interrupted)
|
||||||
@@ -89,40 +95,77 @@ namespace TimberWinR.Inputs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Renames, and AddFields
|
||||||
|
//
|
||||||
|
private void ApplyFilters(JObject json)
|
||||||
|
{
|
||||||
|
if (_arguments.Renames != null)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _arguments.Renames.Length; i += 2)
|
||||||
|
{
|
||||||
|
var oldName = ExpandField(_arguments.Renames[i], json);
|
||||||
|
var newName = ExpandField(_arguments.Renames[i + 1], json);
|
||||||
|
RenameProperty(json, oldName, newName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_arguments.AddFields != null)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _arguments.AddFields.Length; i += 2)
|
||||||
|
{
|
||||||
|
var fieldName = ExpandField(_arguments.AddFields[i], json);
|
||||||
|
var fieldValue = ExpandField(_arguments.AddFields[i + 1], json);
|
||||||
|
AddOrModify(json, fieldName, fieldValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void HandleNewClient(object client)
|
private void HandleNewClient(object client)
|
||||||
{
|
{
|
||||||
var tcpClient = (TcpClient)client;
|
var tcpClient = (TcpClient)client;
|
||||||
NetworkStream clientStream = null;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
clientStream = tcpClient.GetStream();
|
NetworkStream clientStream = tcpClient.GetStream();
|
||||||
var stream = new StreamReader(clientStream);
|
using (var stream = new StreamReader(clientStream))
|
||||||
string line;
|
|
||||||
while ((line = stream.ReadLine()) != null)
|
|
||||||
{
|
{
|
||||||
try
|
//assume a continuous stream of JSON objects
|
||||||
|
using (var reader = new JsonTextReader(stream) { SupportMultipleContent = true })
|
||||||
{
|
{
|
||||||
JObject json = JObject.Parse(line);
|
while (reader.Read())
|
||||||
ProcessJson(json);
|
{
|
||||||
_receivedMessages++;
|
if (CancelToken.IsCancellationRequested) break;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
JObject json = JObject.Load(reader);
|
||||||
|
ApplyFilters(json);
|
||||||
|
ProcessJson(json);
|
||||||
|
Interlocked.Increment(ref _receivedMessages);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
var jex1 = LogErrors.LogException("Bad Json", ex);
|
||||||
|
if (jex1 != null)
|
||||||
|
ProcessJson(jex1);
|
||||||
|
|
||||||
|
LogManager.GetCurrentClassLogger().Warn(ex);
|
||||||
|
Interlocked.Increment(ref _errorCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LogManager.GetCurrentClassLogger().Error(ex);
|
|
||||||
}
|
|
||||||
if (CancelToken.IsCancellationRequested)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch(OperationCanceledException)
|
||||||
|
{
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
LogManager.GetCurrentClassLogger().Error(ex);
|
LogManager.GetCurrentClassLogger().Error(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clientStream != null)
|
|
||||||
clientStream.Close();
|
|
||||||
|
|
||||||
tcpClient.Close();
|
tcpClient.Close();
|
||||||
Finished();
|
Finished();
|
||||||
}
|
}
|
||||||
|
|||||||
201
TimberWinR/Inputs/UdpInputListener.cs
Normal file
201
TimberWinR/Inputs/UdpInputListener.cs
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using NLog;
|
||||||
|
|
||||||
|
namespace TimberWinR.Inputs
|
||||||
|
{
|
||||||
|
public class UdpInputListener : InputListener
|
||||||
|
{
|
||||||
|
private UdpClient _udpListenerV4;
|
||||||
|
private IPEndPoint _udpEndpointV4;
|
||||||
|
private readonly BlockingCollection<byte[]> _unprocessedRawData;
|
||||||
|
private readonly Thread _rawDataProcessingThread;
|
||||||
|
private readonly int _port;
|
||||||
|
private long _receivedMessages;
|
||||||
|
private long _parseErrors;
|
||||||
|
private long _receiveErrors;
|
||||||
|
private long _parsedMessages;
|
||||||
|
private TimberWinR.Parser.UdpParameters _arguments;
|
||||||
|
|
||||||
|
public override JObject ToJson()
|
||||||
|
{
|
||||||
|
var json =
|
||||||
|
new JObject(new JProperty("udp",
|
||||||
|
new JObject(new JProperty("port", _port),
|
||||||
|
new JProperty("receive_errors", _receiveErrors),
|
||||||
|
new JProperty("parse_errors", _parseErrors),
|
||||||
|
new JProperty("messages", _receivedMessages),
|
||||||
|
new JProperty("parsed_messages", _parsedMessages),
|
||||||
|
new JProperty("unprocessed_messages", _unprocessedRawData.Count))));
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UdpInputListener(TimberWinR.Parser.UdpParameters arguments, CancellationToken cancelToken, int port = 5140) : base(cancelToken, "Win32-Udp")
|
||||||
|
{
|
||||||
|
_port = port;
|
||||||
|
_receivedMessages = 0;
|
||||||
|
_arguments = arguments;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(arguments.Type))
|
||||||
|
SetTypeName(arguments.Type);
|
||||||
|
|
||||||
|
// setup raw data processor
|
||||||
|
_unprocessedRawData = new BlockingCollection<byte[]>();
|
||||||
|
_rawDataProcessingThread = new Thread(ProcessDataLoop) { Name = "Win32-Udp-DataProcessor"};
|
||||||
|
_rawDataProcessingThread.Start();
|
||||||
|
|
||||||
|
// start listing to udp port
|
||||||
|
StartListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Shutdown()
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Shutting Down {0}", InputType);
|
||||||
|
|
||||||
|
// close UDP listeners, which will end the listener threads
|
||||||
|
_udpListenerV4.Close();
|
||||||
|
|
||||||
|
base.Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StartListener()
|
||||||
|
{
|
||||||
|
_udpEndpointV4 = new IPEndPoint(IPAddress.Any, _port);
|
||||||
|
|
||||||
|
// setup listener
|
||||||
|
_udpListenerV4 = new UdpClient(_port);
|
||||||
|
|
||||||
|
// start listening on UDP port
|
||||||
|
StartReceiving();
|
||||||
|
|
||||||
|
// all started; log details
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Udp Input on Port {0} Ready", _udpEndpointV4);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StartReceiving()
|
||||||
|
{
|
||||||
|
if (!CancelToken.IsCancellationRequested)
|
||||||
|
_udpListenerV4.BeginReceive(DataReceived, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DataReceived(IAsyncResult result)
|
||||||
|
{
|
||||||
|
if (CancelToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
_unprocessedRawData.CompleteAdding();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
byte[] bytes = _udpListenerV4.EndReceive(result, ref _udpEndpointV4);
|
||||||
|
Interlocked.Increment(ref _receivedMessages);
|
||||||
|
StartReceiving();
|
||||||
|
_unprocessedRawData.Add(bytes);
|
||||||
|
}
|
||||||
|
catch (SocketException)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Socked exception. Ending UDP Listener.");
|
||||||
|
_unprocessedRawData.CompleteAdding();
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Object disposed. Ending UDP Listener");
|
||||||
|
_unprocessedRawData.CompleteAdding();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Warn("Error while receiving data.", ex);
|
||||||
|
|
||||||
|
Interlocked.Increment(ref _receiveErrors);
|
||||||
|
StartReceiving();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessDataLoop()
|
||||||
|
{
|
||||||
|
while (!_unprocessedRawData.IsCompleted)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ProcessData(_unprocessedRawData.Take());
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
// we are shutting down.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException)
|
||||||
|
{
|
||||||
|
// when the collection is marked as completed
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().ErrorException("Error while processing data", ex);
|
||||||
|
Thread.Sleep(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Finished();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Renames, and AddFields
|
||||||
|
//
|
||||||
|
private void ApplyFilters(JObject json)
|
||||||
|
{
|
||||||
|
if (_arguments.Renames != null)
|
||||||
|
{
|
||||||
|
for (int i=0; i<_arguments.Renames.Length; i += 2)
|
||||||
|
{
|
||||||
|
var oldName = ExpandField(_arguments.Renames[i], json);
|
||||||
|
var newName = ExpandField(_arguments.Renames[i + 1], json);
|
||||||
|
RenameProperty(json, oldName, newName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_arguments.AddFields != null)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _arguments.AddFields.Length; i += 2)
|
||||||
|
{
|
||||||
|
var fieldName = ExpandField(_arguments.AddFields[i], json);
|
||||||
|
var fieldValue = ExpandField(_arguments.AddFields[i + 1], json);
|
||||||
|
AddOrModify(json, fieldName, fieldValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessData(byte[] bytes)
|
||||||
|
{
|
||||||
|
var data = Encoding.UTF8.GetString(bytes, 0, bytes.Length);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var json = JObject.Parse(data);
|
||||||
|
ApplyFilters(json);
|
||||||
|
ProcessJson(json);
|
||||||
|
_parsedMessages++;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
var jex1 = LogErrors.LogException(string.Format("Invalid JSON: {0}", data), ex);
|
||||||
|
if (jex1 != null)
|
||||||
|
{
|
||||||
|
ProcessJson(jex1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var msg = string.Format("Bad JSON: {0}", data);
|
||||||
|
LogManager.GetCurrentClassLogger().Warn(msg, ex);
|
||||||
|
|
||||||
|
_parseErrors++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
171
TimberWinR/Inputs/W3CInputListener.cs
Normal file
171
TimberWinR/Inputs/W3CInputListener.cs
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.AccessControl;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.IO;
|
||||||
|
using Interop.MSUtil;
|
||||||
|
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
using NLog;
|
||||||
|
using TimberWinR.Parser;
|
||||||
|
using LogQuery = Interop.MSUtil.LogQueryClassClass;
|
||||||
|
using W3CLogInputFormat = Interop.MSUtil.COMW3CInputContextClassClass;
|
||||||
|
using LogRecordSet = Interop.MSUtil.ILogRecordset;
|
||||||
|
|
||||||
|
|
||||||
|
namespace TimberWinR.Inputs
|
||||||
|
{
|
||||||
|
public class W3CInputListener : InputListener
|
||||||
|
{
|
||||||
|
private readonly int _pollingIntervalInSeconds;
|
||||||
|
private readonly TimberWinR.Parser.W3CLogParameters _arguments;
|
||||||
|
private long _receivedMessages;
|
||||||
|
public bool Stop { get; set; }
|
||||||
|
|
||||||
|
public W3CInputListener(TimberWinR.Parser.W3CLogParameters arguments, CancellationToken cancelToken, int pollingIntervalInSeconds = 5)
|
||||||
|
: base(cancelToken, "Win32-W3CLog")
|
||||||
|
{
|
||||||
|
_arguments = arguments;
|
||||||
|
_receivedMessages = 0;
|
||||||
|
_pollingIntervalInSeconds = pollingIntervalInSeconds;
|
||||||
|
foreach (string loc in _arguments.Location.Split(','))
|
||||||
|
{
|
||||||
|
string hive = loc.Trim();
|
||||||
|
Task.Factory.StartNew(() => IISW3CWatcher(loc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Shutdown()
|
||||||
|
{
|
||||||
|
Stop = true;
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Shutting Down {0}", InputType);
|
||||||
|
base.Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override JObject ToJson()
|
||||||
|
{
|
||||||
|
JObject json = new JObject(
|
||||||
|
new JProperty("iisw3c",
|
||||||
|
new JObject(
|
||||||
|
new JProperty("messages", _receivedMessages),
|
||||||
|
new JProperty("location", _arguments.Location),
|
||||||
|
new JProperty("codepage", _arguments.CodePage),
|
||||||
|
new JProperty("separator", _arguments.Separator),
|
||||||
|
new JProperty("dQuotes", _arguments.DoubleQuotes),
|
||||||
|
new JProperty("dtLines", _arguments.DtLines)
|
||||||
|
)));
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void IISW3CWatcher(string location)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Info("IISW3Listener Ready For {0}", location);
|
||||||
|
|
||||||
|
var oLogQuery = new LogQuery();
|
||||||
|
|
||||||
|
var iFmt = new W3CLogInputFormat()
|
||||||
|
{
|
||||||
|
codepage = _arguments.CodePage,
|
||||||
|
iCodepage = _arguments.CodePage,
|
||||||
|
doubleQuotedStrings = _arguments.DoubleQuotes,
|
||||||
|
detectTypesLines = _arguments.DtLines,
|
||||||
|
dQuotes = _arguments.DoubleQuotes,
|
||||||
|
separator = _arguments.Separator
|
||||||
|
};
|
||||||
|
|
||||||
|
Dictionary<string, Int64> logFileMaxRecords = new Dictionary<string, Int64>();
|
||||||
|
using (var syncHandle = new ManualResetEventSlim())
|
||||||
|
{
|
||||||
|
// Execute the query
|
||||||
|
while (!Stop)
|
||||||
|
{
|
||||||
|
// Execute the query
|
||||||
|
if (!CancelToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
oLogQuery = new LogQuery();
|
||||||
|
|
||||||
|
var qfiles = string.Format("SELECT Distinct [LogFilename] FROM {0}", location);
|
||||||
|
var rsfiles = oLogQuery.Execute(qfiles, iFmt);
|
||||||
|
for (; !rsfiles.atEnd(); rsfiles.moveNext())
|
||||||
|
{
|
||||||
|
var record = rsfiles.getRecord();
|
||||||
|
string fileName = record.getValue("LogFilename") as string;
|
||||||
|
if (!logFileMaxRecords.ContainsKey(fileName))
|
||||||
|
{
|
||||||
|
var qcount = string.Format("SELECT max(RowNumber) as MaxRecordNumber FROM {0}",
|
||||||
|
fileName);
|
||||||
|
var rcount = oLogQuery.Execute(qcount, iFmt);
|
||||||
|
var qr = rcount.getRecord();
|
||||||
|
var lrn = (Int64)qr.getValueEx("MaxRecordNumber");
|
||||||
|
logFileMaxRecords[fileName] = lrn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
foreach (string fileName in logFileMaxRecords.Keys.ToList())
|
||||||
|
{
|
||||||
|
var lastRecordNumber = logFileMaxRecords[fileName];
|
||||||
|
var query = string.Format(
|
||||||
|
"SELECT * FROM '{0}' Where RowNumber > {1} order by RowNumber", fileName,
|
||||||
|
lastRecordNumber);
|
||||||
|
var rs = oLogQuery.Execute(query, iFmt);
|
||||||
|
var colMap = new Dictionary<string, int>();
|
||||||
|
for (int col = 0; col < rs.getColumnCount(); col++)
|
||||||
|
{
|
||||||
|
string colName = rs.getColumnName(col);
|
||||||
|
colMap[colName] = col;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Browse the recordset
|
||||||
|
for (; !rs.atEnd(); rs.moveNext())
|
||||||
|
{
|
||||||
|
var record = rs.getRecord();
|
||||||
|
var json = new JObject();
|
||||||
|
foreach (var field in colMap.Keys)
|
||||||
|
{
|
||||||
|
object v = record.getValue(field);
|
||||||
|
if (field == "date" || field == "time")
|
||||||
|
{
|
||||||
|
DateTime dt = DateTime.Parse(v.ToString());
|
||||||
|
json.Add(new JProperty(field, dt));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
json.Add(new JProperty(field, v));
|
||||||
|
}
|
||||||
|
ProcessJson(json);
|
||||||
|
_receivedMessages++;
|
||||||
|
var lrn = (Int64)record.getValueEx("RowNumber");
|
||||||
|
logFileMaxRecords[fileName] = lrn;
|
||||||
|
record = null;
|
||||||
|
json = null;
|
||||||
|
}
|
||||||
|
// Close the recordset
|
||||||
|
rs.close();
|
||||||
|
}
|
||||||
|
if (!Stop)
|
||||||
|
syncHandle.Wait(TimeSpan.FromSeconds(_pollingIntervalInSeconds), CancelToken);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Error(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Finished();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.AccessControl;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.IO;
|
|
||||||
using Interop.MSUtil;
|
|
||||||
|
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Newtonsoft.Json.Serialization;
|
|
||||||
using NLog;
|
using NLog;
|
||||||
|
using TimberWinR.Parser;
|
||||||
using LogQuery = Interop.MSUtil.LogQueryClassClass;
|
using LogQuery = Interop.MSUtil.LogQueryClassClass;
|
||||||
using EventLogInputFormat = Interop.MSUtil.COMEventLogInputContextClassClass;
|
using EventLogInputFormat = Interop.MSUtil.COMEventLogInputContextClassClass;
|
||||||
using LogRecordSet = Interop.MSUtil.ILogRecordset;
|
using LogRecordSet = Interop.MSUtil.ILogRecordset;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace TimberWinR.Inputs
|
namespace TimberWinR.Inputs
|
||||||
{
|
{
|
||||||
@@ -22,21 +17,35 @@ namespace TimberWinR.Inputs
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class WindowsEvtInputListener : InputListener
|
public class WindowsEvtInputListener : InputListener
|
||||||
{
|
{
|
||||||
private int _pollingIntervalInSeconds = 1;
|
private readonly int _pollingIntervalInSeconds = 1;
|
||||||
private TimberWinR.Parser.WindowsEvent _arguments;
|
private readonly WindowsEvent _arguments;
|
||||||
private long _receivedMessages;
|
private long _receivedMessages;
|
||||||
|
private readonly List<Thread> _tasks;
|
||||||
|
public bool Stop { get; set; }
|
||||||
|
|
||||||
public WindowsEvtInputListener(TimberWinR.Parser.WindowsEvent arguments, CancellationToken cancelToken, int pollingIntervalInSeconds = 1)
|
public WindowsEvtInputListener(WindowsEvent arguments, CancellationToken cancelToken)
|
||||||
: base(cancelToken, "Win32-Eventlog")
|
: base(cancelToken, "Win32-Eventlog")
|
||||||
{
|
{
|
||||||
_arguments = arguments;
|
_arguments = arguments;
|
||||||
_pollingIntervalInSeconds = pollingIntervalInSeconds;
|
_pollingIntervalInSeconds = arguments.Interval;
|
||||||
var task = new Task(EventWatcher, cancelToken);
|
_tasks = new List<Thread>();
|
||||||
task.Start();
|
|
||||||
|
foreach (string eventHive in _arguments.Source.Split(','))
|
||||||
|
{
|
||||||
|
var thread = new Thread(EventWatcher) {Name = "Win32-Eventlog-" + eventHive};
|
||||||
|
_tasks.Add(thread);
|
||||||
|
thread.Start(eventHive);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Shutdown()
|
public override void Shutdown()
|
||||||
{
|
{
|
||||||
|
Stop = true;
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Shutting Down {0}", InputType);
|
||||||
|
foreach (var thread in _tasks)
|
||||||
|
{
|
||||||
|
thread.Join();
|
||||||
|
}
|
||||||
base.Shutdown();
|
base.Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,6 +57,7 @@ namespace TimberWinR.Inputs
|
|||||||
new JProperty("messages", _receivedMessages),
|
new JProperty("messages", _receivedMessages),
|
||||||
new JProperty("binaryFormat", _arguments.BinaryFormat.ToString()),
|
new JProperty("binaryFormat", _arguments.BinaryFormat.ToString()),
|
||||||
new JProperty("direction", _arguments.Direction.ToString()),
|
new JProperty("direction", _arguments.Direction.ToString()),
|
||||||
|
new JProperty("interval", _arguments.Interval),
|
||||||
new JProperty("formatMsg", _arguments.FormatMsg),
|
new JProperty("formatMsg", _arguments.FormatMsg),
|
||||||
new JProperty("fullEventCode", _arguments.FullEventCode),
|
new JProperty("fullEventCode", _arguments.FullEventCode),
|
||||||
new JProperty("fullText", _arguments.FullText),
|
new JProperty("fullText", _arguments.FullText),
|
||||||
@@ -59,9 +69,9 @@ namespace TimberWinR.Inputs
|
|||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EventWatcher()
|
private void EventWatcher(object ploc)
|
||||||
{
|
{
|
||||||
var oLogQuery = new LogQuery();
|
string location = ploc.ToString();
|
||||||
|
|
||||||
LogManager.GetCurrentClassLogger().Info("WindowsEvent Input Listener Ready");
|
LogManager.GetCurrentClassLogger().Info("WindowsEvent Input Listener Ready");
|
||||||
|
|
||||||
@@ -75,55 +85,109 @@ namespace TimberWinR.Inputs
|
|||||||
fullText = _arguments.FullText,
|
fullText = _arguments.FullText,
|
||||||
msgErrorMode = _arguments.MsgErrorMode.ToString(),
|
msgErrorMode = _arguments.MsgErrorMode.ToString(),
|
||||||
stringsSep = _arguments.StringsSep,
|
stringsSep = _arguments.StringsSep,
|
||||||
resolveSIDs = _arguments.ResolveSIDS,
|
resolveSIDs = _arguments.ResolveSIDS
|
||||||
iCheckpoint = CheckpointFileName,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var logFileMaxRecords = new Dictionary<string, Int64>();
|
||||||
|
|
||||||
// Create the query
|
using (var syncHandle = new ManualResetEventSlim())
|
||||||
var query = string.Format("SELECT * FROM {0}", _arguments.Source);
|
|
||||||
|
|
||||||
var firstQuery = true;
|
|
||||||
// Execute the query
|
|
||||||
while (!CancelToken.IsCancellationRequested)
|
|
||||||
{
|
{
|
||||||
try
|
// Execute the query
|
||||||
|
while (!Stop)
|
||||||
{
|
{
|
||||||
var rs = oLogQuery.Execute(query, iFmt);
|
// Execute the query
|
||||||
// Browse the recordset
|
if (!CancelToken.IsCancellationRequested)
|
||||||
for (; !rs.atEnd(); rs.moveNext())
|
|
||||||
{
|
{
|
||||||
// We want to "tail" the log, so skip the first query results.
|
var oLogQuery = new LogQuery();
|
||||||
if (!firstQuery)
|
try
|
||||||
{
|
{
|
||||||
var record = rs.getRecord();
|
|
||||||
var json = new JObject();
|
var qfiles = string.Format("SELECT Distinct [EventLog] FROM {0}", location);
|
||||||
foreach (var field in _arguments.Fields)
|
var rsfiles = oLogQuery.Execute(qfiles, iFmt);
|
||||||
|
|
||||||
|
for (; !rsfiles.atEnd(); rsfiles.moveNext())
|
||||||
{
|
{
|
||||||
object v = record.getValue(field.Name);
|
var record = rsfiles.getRecord();
|
||||||
if (field.Name == "Data")
|
string logName = record.getValue("EventLog") as string;
|
||||||
v = ToPrintable(v.ToString());
|
if (!logFileMaxRecords.ContainsKey(logName))
|
||||||
json.Add(new JProperty(field.Name, v));
|
{
|
||||||
|
var qcount = string.Format("SELECT max(RecordNumber) as MaxRecordNumber FROM {0}",
|
||||||
|
logName);
|
||||||
|
var rcount = oLogQuery.Execute(qcount, iFmt);
|
||||||
|
var qr = rcount.getRecord();
|
||||||
|
var lrn = (Int64) qr.getValueEx("MaxRecordNumber");
|
||||||
|
logFileMaxRecords[logName] = lrn;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessJson(json);
|
|
||||||
_receivedMessages++;
|
foreach (string fileName in logFileMaxRecords.Keys.ToList())
|
||||||
|
{
|
||||||
|
var lastRecordNumber = logFileMaxRecords[fileName];
|
||||||
|
var query = string.Format("SELECT * FROM {0} where RecordNumber > {1}", location,
|
||||||
|
lastRecordNumber);
|
||||||
|
|
||||||
|
var rs = oLogQuery.Execute(query, iFmt);
|
||||||
|
// Browse the recordset
|
||||||
|
for (; !rs.atEnd(); rs.moveNext())
|
||||||
|
{
|
||||||
|
|
||||||
|
var record = rs.getRecord();
|
||||||
|
var json = new JObject();
|
||||||
|
foreach (var field in _arguments.Fields)
|
||||||
|
{
|
||||||
|
object v = record.getValue(field.Name);
|
||||||
|
if (field.Name == "Data")
|
||||||
|
v = ToPrintable(v.ToString());
|
||||||
|
if ((field.Name == "TimeGenerated" || field.Name == "TimeWritten") &&
|
||||||
|
field.DataType == typeof (DateTime))
|
||||||
|
v = ((DateTime) v).ToUniversalTime();
|
||||||
|
json.Add(new JProperty(field.Name, v));
|
||||||
|
}
|
||||||
|
|
||||||
|
var lrn = (Int64) record.getValueEx("RecordNumber");
|
||||||
|
logFileMaxRecords[fileName] = lrn;
|
||||||
|
|
||||||
|
ProcessJson(json);
|
||||||
|
_receivedMessages++;
|
||||||
|
}
|
||||||
|
// Close the recordset
|
||||||
|
rs.close();
|
||||||
|
GC.Collect();
|
||||||
|
}
|
||||||
|
if (!Stop)
|
||||||
|
syncHandle.Wait(TimeSpan.FromSeconds(_pollingIntervalInSeconds), CancelToken);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Error(ex);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
oLogQuery = null;
|
||||||
|
// Sleep
|
||||||
|
if (!Stop)
|
||||||
|
syncHandle.Wait(TimeSpan.FromSeconds(_pollingIntervalInSeconds), CancelToken);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
catch (Exception ex1)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Warn(ex1);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Close the recordset
|
|
||||||
rs.close();
|
|
||||||
firstQuery = false;
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
Finished();
|
||||||
{
|
|
||||||
LogManager.GetCurrentClassLogger().Error(ex);
|
|
||||||
firstQuery = true;
|
|
||||||
oLogQuery = new LogQuery();
|
|
||||||
}
|
|
||||||
System.Threading.Thread.Sleep(_pollingIntervalInSeconds * 1000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Finished();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
48
TimberWinR/LogErrors.cs
Normal file
48
TimberWinR/LogErrors.cs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
using TimberWinR.Parser;
|
||||||
|
|
||||||
|
namespace TimberWinR
|
||||||
|
{
|
||||||
|
public class LogErrors
|
||||||
|
{
|
||||||
|
public static JObject LogException(Exception ex)
|
||||||
|
{
|
||||||
|
return LogException("Exception", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JObject LogException(string errorMessage, Exception ex)
|
||||||
|
{
|
||||||
|
JObject result = new JObject();
|
||||||
|
result["type"] = "TimberWinR-Error";
|
||||||
|
result["ErrorMessage"] = errorMessage;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
|
||||||
|
{
|
||||||
|
Formatting = Formatting.Indented,
|
||||||
|
NullValueHandling = NullValueHandling.Ignore
|
||||||
|
};
|
||||||
|
|
||||||
|
var exJson = JObject.Parse(JsonConvert.SerializeObject(ex));
|
||||||
|
|
||||||
|
result.Merge(exJson, new JsonMergeSettings
|
||||||
|
{
|
||||||
|
MergeArrayHandling = MergeArrayHandling.Replace
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception ex1)
|
||||||
|
{
|
||||||
|
result["ErrorMessage"] = ex1.ToString();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
|
using System.Reflection;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NLog.Config;
|
using NLog.Config;
|
||||||
using NLog.Targets;
|
using NLog.Targets;
|
||||||
@@ -10,6 +12,7 @@ using System.Text;
|
|||||||
using TimberWinR.Inputs;
|
using TimberWinR.Inputs;
|
||||||
using TimberWinR.Outputs;
|
using TimberWinR.Outputs;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace TimberWinR
|
namespace TimberWinR
|
||||||
{
|
{
|
||||||
@@ -19,14 +22,18 @@ namespace TimberWinR
|
|||||||
public class Manager
|
public class Manager
|
||||||
{
|
{
|
||||||
public Configuration Config { get; set; }
|
public Configuration Config { get; set; }
|
||||||
public List<OutputSender> Outputs { get; set; }
|
public List<OutputSender> Outputs { get; set; }
|
||||||
public List<TcpInputListener> Tcps { get; set; }
|
public List<InputListener> Listeners { get; set; }
|
||||||
public List<InputListener> Listeners { get; set; }
|
public bool LiveMonitor { get; set; }
|
||||||
|
|
||||||
|
public event Action<Configuration> OnConfigurationProcessed;
|
||||||
|
|
||||||
public DateTime StartedOn { get; set; }
|
public DateTime StartedOn { get; set; }
|
||||||
public string JsonConfig { get; set; }
|
public string JsonConfig { get; set; }
|
||||||
public string LogfileDir { get; set; }
|
public string LogfileDir { get; set; }
|
||||||
|
|
||||||
public int NumConnections {
|
public int NumConnections
|
||||||
|
{
|
||||||
get { return numConnections; }
|
get { return numConnections; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,34 +45,47 @@ namespace TimberWinR
|
|||||||
private static int numConnections;
|
private static int numConnections;
|
||||||
private static int numMessages;
|
private static int numMessages;
|
||||||
|
|
||||||
|
|
||||||
public void Shutdown()
|
public void Shutdown()
|
||||||
{
|
{
|
||||||
LogManager.GetCurrentClassLogger().Info("Shutting Down");
|
LogManager.GetCurrentClassLogger().Info("Shutting Down");
|
||||||
|
|
||||||
foreach (InputListener listener in Listeners)
|
foreach (InputListener listener in Listeners)
|
||||||
listener.Shutdown();
|
listener.Shutdown();
|
||||||
|
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Completed ShutDown");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void IncrementMessageCount(int count = 1)
|
public void IncrementMessageCount(int count = 1)
|
||||||
{
|
{
|
||||||
Interlocked.Add(ref numMessages, count);
|
Interlocked.Add(ref numMessages, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Manager(string jsonConfigFile, string logLevel, string logfileDir, CancellationToken cancelToken)
|
|
||||||
{
|
|
||||||
StartedOn = DateTime.UtcNow;
|
|
||||||
|
|
||||||
JsonConfig = jsonConfigFile;
|
public Manager()
|
||||||
|
{
|
||||||
|
LogsFileDatabase.Manager = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Manager(string jsonConfigFile, string logLevel, string logfileDir, bool liveMonitor, CancellationToken cancelToken, bool processConfiguration = true)
|
||||||
|
{
|
||||||
|
LogsFileDatabase.Manager = this;
|
||||||
|
|
||||||
|
StartedOn = DateTime.UtcNow;
|
||||||
|
LiveMonitor = liveMonitor;
|
||||||
|
|
||||||
|
var vfi = new FileInfo(jsonConfigFile);
|
||||||
|
|
||||||
|
JsonConfig = vfi.FullName;
|
||||||
LogfileDir = logfileDir;
|
LogfileDir = logfileDir;
|
||||||
|
|
||||||
|
|
||||||
numMessages = 0;
|
numMessages = 0;
|
||||||
numConnections = 0;
|
numConnections = 0;
|
||||||
|
|
||||||
Outputs = new List<OutputSender>();
|
Outputs = new List<OutputSender>();
|
||||||
Listeners = new List<InputListener>();
|
Listeners = new List<InputListener>();
|
||||||
|
|
||||||
var loggingConfiguration = new LoggingConfiguration();
|
var loggingConfiguration = new LoggingConfiguration();
|
||||||
|
|
||||||
// Create our default targets
|
// Create our default targets
|
||||||
@@ -80,107 +100,221 @@ namespace TimberWinR
|
|||||||
loggingConfiguration.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, coloredConsoleTarget));
|
loggingConfiguration.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, coloredConsoleTarget));
|
||||||
// LogLevel.Debug means has to be at least Debug to show up in logfile
|
// LogLevel.Debug means has to be at least Debug to show up in logfile
|
||||||
loggingConfiguration.LoggingRules.Add(new LoggingRule("*", LogLevel.Debug, fileTarget));
|
loggingConfiguration.LoggingRules.Add(new LoggingRule("*", LogLevel.Debug, fileTarget));
|
||||||
|
|
||||||
LogManager.Configuration = loggingConfiguration;
|
LogManager.Configuration = loggingConfiguration;
|
||||||
LogManager.EnableLogging();
|
LogManager.EnableLogging();
|
||||||
|
|
||||||
LogManager.GlobalThreshold = LogLevel.FromString(logLevel);
|
LogManager.GlobalThreshold = LogLevel.FromString(logLevel);
|
||||||
|
|
||||||
|
//LogManager.GetCurrentClassLogger()
|
||||||
|
// .Info("TimberWinR Version {0}", GetAssemblyByName("TimberWinR.ServiceHost").GetName().Version.ToString());
|
||||||
|
|
||||||
// Is it a directory?
|
LogManager.GetCurrentClassLogger()
|
||||||
if (Directory.Exists(jsonConfigFile))
|
.Info("TimberWinR Version {0}", Assembly.GetEntryAssembly().GetName().Version.ToString());
|
||||||
{
|
|
||||||
DirectoryInfo di = new DirectoryInfo(jsonConfigFile);
|
LogManager.GetCurrentClassLogger()
|
||||||
LogManager.GetCurrentClassLogger().Info("Initialized, Reading Configurations From {0}", di.FullName);
|
.Info("Database Filename: {0}", LogsFileDatabase.Instance.DatabaseFileName);
|
||||||
Config = Configuration.FromDirectory(jsonConfigFile);
|
|
||||||
}
|
try
|
||||||
else
|
|
||||||
{
|
{
|
||||||
var fi = new FileInfo(jsonConfigFile);
|
var fi = new FileInfo(jsonConfigFile);
|
||||||
|
if (fi.Exists)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Initialized, Reading Configurations From File: {0}", fi.FullName);
|
||||||
|
|
||||||
LogManager.GetCurrentClassLogger().Info("Initialized, Reading Configurations From File: {0}", fi.FullName);
|
if (!fi.Exists)
|
||||||
|
throw new FileNotFoundException("Missing config file", jsonConfigFile);
|
||||||
if (!fi.Exists)
|
|
||||||
throw new FileNotFoundException("Missing config file", jsonConfigFile);
|
|
||||||
|
|
||||||
LogManager.GetCurrentClassLogger().Info("Initialized, Reading Config: {0}", fi.FullName);
|
LogManager.GetCurrentClassLogger().Info("Initialized, Reading Config: {0}", fi.FullName);
|
||||||
Config = Configuration.FromFile(jsonConfigFile);
|
Config = Configuration.FromFile(jsonConfigFile);
|
||||||
|
}
|
||||||
|
else if (Directory.Exists(jsonConfigFile))
|
||||||
|
{
|
||||||
|
DirectoryInfo di = new DirectoryInfo(jsonConfigFile);
|
||||||
|
LogManager.GetCurrentClassLogger().Info("Initialized, Reading Configurations From {0}", di.FullName);
|
||||||
|
Config = Configuration.FromDirectory(jsonConfigFile, cancelToken, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (JsonSerializationException jse)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Error(jse);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Error(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
LogManager.GetCurrentClassLogger().Info("Log Directory {0}", logfileDir);
|
LogManager.GetCurrentClassLogger().Info("Log Directory {0}", logfileDir);
|
||||||
LogManager.GetCurrentClassLogger().Info("Logging Level: {0}", LogManager.GlobalThreshold);
|
LogManager.GetCurrentClassLogger().Info("Logging Level: {0}", LogManager.GlobalThreshold);
|
||||||
|
|
||||||
// Read the Configuration file
|
|
||||||
|
|
||||||
if (Config.RedisOutputs != null)
|
|
||||||
{
|
|
||||||
foreach (var ro in Config.RedisOutputs)
|
|
||||||
{
|
|
||||||
var redis = new RedisOutput(this, ro, cancelToken);
|
|
||||||
Outputs.Add(redis);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if (Config.ElasticsearchOutputs != null)
|
|
||||||
{
|
|
||||||
foreach (var ro in Config.ElasticsearchOutputs)
|
|
||||||
{
|
|
||||||
var els = new ElasticsearchOutput(this, ro, cancelToken);
|
|
||||||
Outputs.Add(els);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (Config.StdoutOutputs != null)
|
|
||||||
{
|
|
||||||
foreach (var ro in Config.StdoutOutputs)
|
|
||||||
{
|
|
||||||
var stdout = new StdoutOutput(this, ro, cancelToken);
|
|
||||||
Outputs.Add(stdout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (Parser.IISW3CLog iisw3cConfig in Config.IISW3C)
|
if (processConfiguration)
|
||||||
{
|
{
|
||||||
var elistner = new IISW3CInputListener(iisw3cConfig, cancelToken);
|
ProcessConfiguration(cancelToken, Config);
|
||||||
Listeners.Add(elistner);
|
}
|
||||||
foreach(var output in Outputs)
|
|
||||||
output.Connect(elistner);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (Parser.WindowsEvent eventConfig in Config.Events)
|
|
||||||
{
|
|
||||||
var elistner = new WindowsEvtInputListener(eventConfig, cancelToken);
|
|
||||||
Listeners.Add(elistner);
|
|
||||||
foreach (var output in Outputs)
|
|
||||||
output.Connect(elistner);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var logConfig in Config.Logs)
|
|
||||||
{
|
|
||||||
var elistner = new LogsListener(logConfig, cancelToken);
|
|
||||||
Listeners.Add(elistner);
|
|
||||||
foreach (var output in Outputs)
|
|
||||||
output.Connect(elistner);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var tcp in Config.Tcps)
|
|
||||||
{
|
|
||||||
var elistner = new TcpInputListener(cancelToken, tcp.Port);
|
|
||||||
Listeners.Add(elistner);
|
|
||||||
foreach (var output in Outputs)
|
|
||||||
output.Connect(elistner);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
foreach (var tcp in Config.Stdins)
|
|
||||||
{
|
|
||||||
var elistner = new StdinListener(cancelToken);
|
|
||||||
Listeners.Add(elistner);
|
|
||||||
foreach (var output in Outputs)
|
|
||||||
output.Connect(elistner);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Start(CancellationToken cancelToken)
|
||||||
|
{
|
||||||
|
ProcessConfiguration(cancelToken, Config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ProcessConfiguration(CancellationToken cancelToken, Configuration config)
|
||||||
|
{
|
||||||
|
// Read the Configuration file
|
||||||
|
if (config != null)
|
||||||
|
{
|
||||||
|
if (OnConfigurationProcessed != null)
|
||||||
|
OnConfigurationProcessed(config);
|
||||||
|
|
||||||
|
if (config.StatsDOutputs != null)
|
||||||
|
{
|
||||||
|
foreach (var ro in config.StatsDOutputs)
|
||||||
|
{
|
||||||
|
var output = new StatsDOutput(this, ro, cancelToken);
|
||||||
|
Outputs.Add(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.RedisOutputs != null)
|
||||||
|
{
|
||||||
|
foreach (var ro in config.RedisOutputs)
|
||||||
|
{
|
||||||
|
var redis = new RedisOutput(this, ro, cancelToken);
|
||||||
|
Outputs.Add(redis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (config.ElasticsearchOutputs != null)
|
||||||
|
{
|
||||||
|
foreach (var ro in config.ElasticsearchOutputs)
|
||||||
|
{
|
||||||
|
var els = new ElasticsearchOutput(this, ro, cancelToken);
|
||||||
|
Outputs.Add(els);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (config.StdoutOutputs != null)
|
||||||
|
{
|
||||||
|
foreach (var ro in config.StdoutOutputs)
|
||||||
|
{
|
||||||
|
var stdout = new StdoutOutput(this, ro, cancelToken);
|
||||||
|
Outputs.Add(stdout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.FileOutputs != null)
|
||||||
|
{
|
||||||
|
foreach (var ro in config.FileOutputs)
|
||||||
|
{
|
||||||
|
var output = new FileOutput(this, ro, cancelToken);
|
||||||
|
Outputs.Add(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (Parser.IISW3CLogParameters iisw3cConfig in config.IISW3C)
|
||||||
|
{
|
||||||
|
var elistner = new IISW3CInputListener(iisw3cConfig, cancelToken);
|
||||||
|
Listeners.Add(elistner);
|
||||||
|
foreach (var output in Outputs)
|
||||||
|
output.Connect(elistner);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (Parser.W3CLogParameters iisw3cConfig in config.W3C)
|
||||||
|
{
|
||||||
|
var elistner = new W3CInputListener(iisw3cConfig, cancelToken);
|
||||||
|
Listeners.Add(elistner);
|
||||||
|
foreach (var output in Outputs)
|
||||||
|
output.Connect(elistner);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (Parser.WindowsEvent eventConfig in config.Events)
|
||||||
|
{
|
||||||
|
var elistner = new WindowsEvtInputListener(eventConfig, cancelToken);
|
||||||
|
Listeners.Add(elistner);
|
||||||
|
foreach (var output in Outputs)
|
||||||
|
output.Connect(elistner);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var logConfig in config.Logs)
|
||||||
|
{
|
||||||
|
var elistner = new LogsListener(logConfig, cancelToken);
|
||||||
|
Listeners.Add(elistner);
|
||||||
|
foreach (var output in Outputs)
|
||||||
|
output.Connect(elistner);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var logConfig in config.TailFiles)
|
||||||
|
{
|
||||||
|
var elistner = new TailFileListener(logConfig, cancelToken);
|
||||||
|
Listeners.Add(elistner);
|
||||||
|
foreach (var output in Outputs)
|
||||||
|
output.Connect(elistner);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var tcp in config.Tcps)
|
||||||
|
{
|
||||||
|
var elistner = new TcpInputListener(tcp, cancelToken, tcp.Port);
|
||||||
|
Listeners.Add(elistner);
|
||||||
|
foreach (var output in Outputs)
|
||||||
|
output.Connect(elistner);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var udp in config.Udps)
|
||||||
|
{
|
||||||
|
var elistner = new UdpInputListener(udp, cancelToken, udp.Port);
|
||||||
|
Listeners.Add(elistner);
|
||||||
|
foreach (var output in Outputs)
|
||||||
|
output.Connect(elistner);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var stdin in config.Stdins)
|
||||||
|
{
|
||||||
|
var elistner = new StdinListener(stdin, cancelToken);
|
||||||
|
Listeners.Add(elistner);
|
||||||
|
foreach (var output in Outputs)
|
||||||
|
output.Connect(elistner);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var stdin in config.Generators)
|
||||||
|
{
|
||||||
|
var elistner = new GeneratorInput(stdin, cancelToken);
|
||||||
|
Listeners.Add(elistner);
|
||||||
|
foreach (var output in Outputs)
|
||||||
|
output.Connect(elistner);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var computerName = System.Environment.MachineName + "." +
|
||||||
|
Microsoft.Win32.Registry.LocalMachine.OpenSubKey(
|
||||||
|
@"SYSTEM\CurrentControlSet\services\Tcpip\Parameters")
|
||||||
|
.GetValue("Domain", "")
|
||||||
|
.ToString();
|
||||||
|
|
||||||
|
foreach (var output in Outputs)
|
||||||
|
{
|
||||||
|
var name = Assembly.GetExecutingAssembly().GetName();
|
||||||
|
JObject json = new JObject(
|
||||||
|
new JProperty("TimberWinR",
|
||||||
|
new JObject(
|
||||||
|
new JProperty("version",
|
||||||
|
Assembly.GetEntryAssembly().GetName().Version.ToString()),
|
||||||
|
//GetAssemblyByName("TimberWinR.ServiceHost").GetName().Version.ToString()),
|
||||||
|
new JProperty("host", computerName),
|
||||||
|
new JProperty("output", output.Name),
|
||||||
|
new JProperty("initialized", DateTime.UtcNow)
|
||||||
|
)));
|
||||||
|
json.Add(new JProperty("type", "Win32-TimberWinR"));
|
||||||
|
json.Add(new JProperty("host", computerName));
|
||||||
|
output.Startup(json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Assembly GetAssemblyByName(string name)
|
||||||
|
{
|
||||||
|
return AppDomain.CurrentDomain.GetAssemblies().
|
||||||
|
SingleOrDefault(assembly => assembly.GetName().Name == name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates the default <see cref="FileTarget"/>.
|
/// Creates the default <see cref="FileTarget"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -2,52 +2,102 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Elasticsearch.Net;
|
||||||
|
using Elasticsearch.Net.ConnectionPool;
|
||||||
|
using Nest;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
using RapidRegex.Core;
|
||||||
using RestSharp;
|
using RestSharp;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Elasticsearch.Net.Serialization;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace TimberWinR.Outputs
|
namespace TimberWinR.Outputs
|
||||||
{
|
{
|
||||||
|
public class Person
|
||||||
|
{
|
||||||
|
public string Firstname { get; set; }
|
||||||
|
public string Lastname { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public partial class ElasticsearchOutput : OutputSender
|
public partial class ElasticsearchOutput : OutputSender
|
||||||
{
|
{
|
||||||
private TimberWinR.Manager _manager;
|
private TimberWinR.Manager _manager;
|
||||||
private readonly int _port;
|
private readonly int _port;
|
||||||
private readonly int _interval;
|
private readonly int _interval;
|
||||||
private readonly string[] _host;
|
private readonly int _flushSize;
|
||||||
private readonly string _protocol;
|
private readonly int _idleFlushTimeSeconds;
|
||||||
private readonly string _index;
|
private readonly string[] _hosts;
|
||||||
private int _hostIndex;
|
private readonly string _protocol;
|
||||||
private readonly int _timeout;
|
private readonly int _timeout;
|
||||||
private readonly object _locker = new object();
|
private readonly object _locker = new object();
|
||||||
private readonly List<JObject> _jsonQueue;
|
private readonly List<JObject> _jsonQueue;
|
||||||
private readonly int _numThreads;
|
private readonly int _numThreads;
|
||||||
private long _sentMessages;
|
private long _sentMessages;
|
||||||
private long _errorCount;
|
private long _errorCount;
|
||||||
|
private readonly int _maxQueueSize;
|
||||||
|
private readonly bool _queueOverflowDiscardOldest;
|
||||||
|
private readonly bool _disablePing;
|
||||||
|
private readonly int _pingTimeout;
|
||||||
|
|
||||||
public ElasticsearchOutput(TimberWinR.Manager manager, Parser.ElasticsearchOutput eo, CancellationToken cancelToken)
|
private Parser.ElasticsearchOutputParameters _parameters;
|
||||||
: base(cancelToken)
|
public bool Stop { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the bulk connection pool of hosts
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
private ElasticClient getClient()
|
||||||
|
{
|
||||||
|
var nodes = new List<Uri>();
|
||||||
|
foreach (var host in _hosts)
|
||||||
|
{
|
||||||
|
var url = string.Format("http://{0}:{1}", host, _port);
|
||||||
|
nodes.Add(new Uri(url));
|
||||||
|
}
|
||||||
|
var pool = new StaticConnectionPool(nodes.ToArray());
|
||||||
|
var settings = new ConnectionSettings(pool)
|
||||||
|
.ExposeRawResponse();
|
||||||
|
|
||||||
|
if (_disablePing)
|
||||||
|
settings.DisablePing();
|
||||||
|
else if (_pingTimeout != 0)
|
||||||
|
settings.SetPingTimeout(_pingTimeout);
|
||||||
|
|
||||||
|
var client = new ElasticClient(settings);
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ElasticsearchOutput(TimberWinR.Manager manager, Parser.ElasticsearchOutputParameters parameters, CancellationToken cancelToken)
|
||||||
|
: base(cancelToken, "Elasticsearch")
|
||||||
{
|
{
|
||||||
_sentMessages = 0;
|
_sentMessages = 0;
|
||||||
_errorCount = 0;
|
_errorCount = 0;
|
||||||
|
|
||||||
_protocol = eo.Protocol;
|
_parameters = parameters;
|
||||||
_timeout = eo.Timeout;
|
_flushSize = parameters.FlushSize;
|
||||||
|
_idleFlushTimeSeconds = parameters.IdleFlushTimeInSeconds;
|
||||||
|
_protocol = parameters.Protocol;
|
||||||
|
_timeout = parameters.Timeout;
|
||||||
_manager = manager;
|
_manager = manager;
|
||||||
_port = eo.Port;
|
_port = parameters.Port;
|
||||||
_interval = eo.Interval;
|
_interval = parameters.Interval;
|
||||||
_host = eo.Host;
|
_hosts = parameters.Host;
|
||||||
_index = eo.Index;
|
|
||||||
_hostIndex = 0;
|
|
||||||
_jsonQueue = new List<JObject>();
|
_jsonQueue = new List<JObject>();
|
||||||
_numThreads = eo.NumThreads;
|
_numThreads = parameters.NumThreads;
|
||||||
|
_maxQueueSize = parameters.MaxQueueSize;
|
||||||
|
_queueOverflowDiscardOldest = parameters.QueueOverflowDiscardOldest;
|
||||||
|
_disablePing = !parameters.EnablePing;
|
||||||
|
_pingTimeout = parameters.PingTimeout;
|
||||||
|
|
||||||
for (int i = 0; i < eo.NumThreads; i++)
|
for (int i = 0; i < parameters.NumThreads; i++)
|
||||||
{
|
{
|
||||||
var elsThread = new Task(ElasticsearchSender, cancelToken);
|
Task.Factory.StartNew(ElasticsearchSender, cancelToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
|
||||||
elsThread.Start();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,16 +106,20 @@ namespace TimberWinR.Outputs
|
|||||||
JObject json = new JObject(
|
JObject json = new JObject(
|
||||||
new JProperty("elasticsearch",
|
new JProperty("elasticsearch",
|
||||||
new JObject(
|
new JObject(
|
||||||
new JProperty("host", string.Join(",", _host)),
|
new JProperty("host", string.Join(",", _hosts)),
|
||||||
new JProperty("errors", _errorCount),
|
new JProperty("errors", _errorCount),
|
||||||
new JProperty("sent_messages", _sentMessages),
|
new JProperty("messages", _sentMessages),
|
||||||
new JProperty("queued_messages", _jsonQueue.Count),
|
new JProperty("queuedMessageCount", _jsonQueue.Count),
|
||||||
new JProperty("port", _port),
|
new JProperty("port", _port),
|
||||||
|
new JProperty("flushSize", _flushSize),
|
||||||
|
new JProperty("idleFlushTime", _idleFlushTimeSeconds),
|
||||||
new JProperty("interval", _interval),
|
new JProperty("interval", _interval),
|
||||||
new JProperty("threads", _numThreads),
|
new JProperty("threads", _numThreads),
|
||||||
|
new JProperty("maxQueueSize", _maxQueueSize),
|
||||||
|
new JProperty("overflowDiscardOldest", _queueOverflowDiscardOldest),
|
||||||
new JProperty("hosts",
|
new JProperty("hosts",
|
||||||
new JArray(
|
new JArray(
|
||||||
from h in _host
|
from h in _hosts
|
||||||
select new JObject(
|
select new JObject(
|
||||||
new JProperty("host", h)))))));
|
new JProperty("host", h)))))));
|
||||||
return json;
|
return json;
|
||||||
@@ -75,118 +129,153 @@ namespace TimberWinR.Outputs
|
|||||||
//
|
//
|
||||||
private void ElasticsearchSender()
|
private void ElasticsearchSender()
|
||||||
{
|
{
|
||||||
while (!CancelToken.IsCancellationRequested)
|
// Force an inital flush
|
||||||
{
|
DateTime lastFlushTime = DateTime.MinValue;
|
||||||
JObject[] messages;
|
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
messages = _jsonQueue.Take(1).ToArray();
|
|
||||||
_jsonQueue.RemoveRange(0, messages.Length);
|
|
||||||
if (messages.Length > 0)
|
|
||||||
_manager.IncrementMessageCount(messages.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (messages.Length > 0)
|
LogManager.GetCurrentClassLogger()
|
||||||
{
|
.Info("{0}: Elasticsarch Output To {1} Ready", Thread.CurrentThread.ManagedThreadId, string.Join(",", _hosts));
|
||||||
int numHosts = _host.Length;
|
|
||||||
while (numHosts-- > 0)
|
|
||||||
|
using (var syncHandle = new ManualResetEventSlim())
|
||||||
|
{
|
||||||
|
// Execute the query
|
||||||
|
while (!Stop)
|
||||||
|
{
|
||||||
|
if (!CancelToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Get the next client
|
int messageCount = 0;
|
||||||
RestClient client = getClient();
|
List<JObject> messages = new List<JObject>();
|
||||||
if (client != null)
|
|
||||||
{
|
// Lets get whats in the queue
|
||||||
LogManager.GetCurrentClassLogger()
|
lock (_locker)
|
||||||
.Debug("Sending {0} Messages to {1}", messages.Length, client.BaseUrl);
|
{
|
||||||
|
messageCount = _jsonQueue.Count;
|
||||||
foreach (JObject json in messages)
|
|
||||||
|
// Time to flush?
|
||||||
|
if (messageCount >= _flushSize || (DateTime.UtcNow - lastFlushTime).Seconds >= _idleFlushTimeSeconds)
|
||||||
{
|
{
|
||||||
string typeName = "Win32-Elasticsearch";
|
messages = _jsonQueue.Take(messageCount).ToList();
|
||||||
if (json["type"] != null)
|
_jsonQueue.RemoveRange(0, messageCount);
|
||||||
typeName = json["type"].ToString();
|
if (messages.Count > 0)
|
||||||
string indexName = _index;
|
_manager.IncrementMessageCount(messages.Count);
|
||||||
if (string.IsNullOrEmpty(indexName))
|
|
||||||
{
|
|
||||||
DateTime now = DateTime.UtcNow;
|
|
||||||
indexName = string.Format("logstash-{0}", DateTime.UtcNow.ToString("yyyy.MM.dd"));
|
|
||||||
}
|
|
||||||
var req = new RestRequest(string.Format("/{0}/{1}/", indexName, typeName), Method.POST);
|
|
||||||
|
|
||||||
req.AddParameter("text/json", json.ToString(), ParameterType.RequestBody);
|
|
||||||
|
|
||||||
req.RequestFormat = DataFormat.Json;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
client.ExecuteAsync(req, response =>
|
|
||||||
{
|
|
||||||
if (response.StatusCode != HttpStatusCode.Created)
|
|
||||||
{
|
|
||||||
LogManager.GetCurrentClassLogger()
|
|
||||||
.Error("Failed to send: {0}", response.ErrorMessage);
|
|
||||||
Interlocked.Increment(ref _errorCount);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_sentMessages++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (Exception error)
|
|
||||||
{
|
|
||||||
LogManager.GetCurrentClassLogger().Error(error);
|
|
||||||
Interlocked.Increment(ref _errorCount);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
// We have some messages to work with
|
||||||
|
if (messages.Count > 0)
|
||||||
{
|
{
|
||||||
|
var client = getClient();
|
||||||
|
|
||||||
LogManager.GetCurrentClassLogger()
|
LogManager.GetCurrentClassLogger()
|
||||||
.Fatal("Unable to connect with any Elasticsearch hosts, {0}",
|
.Debug("Sending {0} Messages to {1}", messages.Count, string.Join(",", _hosts));
|
||||||
String.Join(",", _host));
|
// This loop will process all messages we've taken from the queue
|
||||||
Interlocked.Increment(ref _errorCount);
|
// that have the same index and type (an elasticsearch requirement)
|
||||||
|
do
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Grab all messages with same index and type (this is the whole point, group the same ones)
|
||||||
|
var bulkTypeName = this._parameters.GetTypeName(messages[0]);
|
||||||
|
var bulkIndexName = this._parameters.GetIndexName(messages[0]);
|
||||||
|
|
||||||
|
IEnumerable<JObject> bulkItems =
|
||||||
|
messages.TakeWhile(
|
||||||
|
message =>
|
||||||
|
String.Compare(bulkTypeName, _parameters.GetTypeName(message), false) == 0 &&
|
||||||
|
String.Compare(bulkIndexName, _parameters.GetIndexName(message), false) == 0);
|
||||||
|
|
||||||
|
// Send the message(s), if the are successfully sent, they
|
||||||
|
// are removed from the queue
|
||||||
|
lastFlushTime = transmitBulkData(bulkItems, bulkIndexName, bulkTypeName, client, lastFlushTime, messages);
|
||||||
|
|
||||||
|
GC.Collect();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Error(ex);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (messages.Count > 0);
|
||||||
}
|
}
|
||||||
|
GC.Collect();
|
||||||
|
if (!Stop)
|
||||||
|
{
|
||||||
|
syncHandle.Wait(TimeSpan.FromMilliseconds(_interval), CancelToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
LogManager.GetCurrentClassLogger().Error(ex);
|
LogManager.GetCurrentClassLogger().Error(ex);
|
||||||
Interlocked.Increment(ref _errorCount);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
System.Threading.Thread.Sleep(_interval);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LogManager.GetCurrentClassLogger()
|
||||||
|
.Info("{0}: Elasticsarch Output To {1} Terminated", Thread.CurrentThread.ManagedThreadId, string.Join(",", _hosts));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private RestClient getClient()
|
//
|
||||||
{
|
// Send the messages to Elasticsearch (bulk)
|
||||||
if (_hostIndex >= _host.Length)
|
//
|
||||||
_hostIndex = 0;
|
private DateTime transmitBulkData(IEnumerable<JObject> bulkItems, string bulkIndexName, string bulkTypeName,
|
||||||
|
ElasticClient client, DateTime lastFlushTime, List<JObject> messages)
|
||||||
int numTries = 0;
|
{
|
||||||
while (numTries < _host.Length)
|
var bulkRequest = new BulkRequest() {Refresh = true};
|
||||||
|
bulkRequest.Operations = new List<IBulkOperation>();
|
||||||
|
foreach (var json in bulkItems)
|
||||||
{
|
{
|
||||||
try
|
// ES requires a timestamp, add one if not present
|
||||||
{
|
var ts = json["@timestamp"];
|
||||||
string url = string.Format("{0}://{1}:{2}", _protocol.Replace(":",""), _host[_hostIndex], _port);
|
if (ts == null)
|
||||||
var client = new RestClient(url);
|
json["@timestamp"] = DateTime.UtcNow;
|
||||||
client.Timeout = _timeout;
|
var bi = new BulkIndexOperation<JObject>(json);
|
||||||
|
bi.Index = bulkIndexName;
|
||||||
_hostIndex++;
|
bi.Type = bulkTypeName;
|
||||||
if (_hostIndex >= _host.Length)
|
bulkRequest.Operations.Add(bi);
|
||||||
_hostIndex = 0;
|
|
||||||
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
numTries++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
// The total messages processed for this operation.
|
||||||
|
int numMessages = bulkItems.Count();
|
||||||
|
|
||||||
|
var response = client.Bulk(bulkRequest);
|
||||||
|
if (!response.IsValid)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Error("Failed to send: {0}", response);
|
||||||
|
Interlocked.Increment(ref _errorCount);
|
||||||
|
interlockedInsert(messages); // Put the messages back into the queue
|
||||||
|
}
|
||||||
|
else // Success!
|
||||||
|
{
|
||||||
|
lastFlushTime = DateTime.UtcNow;
|
||||||
|
LogManager.GetCurrentClassLogger()
|
||||||
|
.Info("Successfully sent {0} messages in a single bulk request", numMessages);
|
||||||
|
Interlocked.Add(ref _sentMessages, numMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove them from the working list
|
||||||
|
messages.RemoveRange(0, numMessages);
|
||||||
|
return lastFlushTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Places messages back into the queue (for a future attempt)
|
||||||
|
private void interlockedInsert(List<JObject> messages)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
_jsonQueue.InsertRange(0, messages);
|
||||||
|
if (_jsonQueue.Count > _maxQueueSize)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Warn("Exceeded maximum queue depth");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -196,10 +285,30 @@ namespace TimberWinR.Outputs
|
|||||||
ApplyFilters(jsonMessage);
|
ApplyFilters(jsonMessage);
|
||||||
|
|
||||||
var message = jsonMessage.ToString();
|
var message = jsonMessage.ToString();
|
||||||
LogManager.GetCurrentClassLogger().Debug(message);
|
LogManager.GetCurrentClassLogger().Trace(message);
|
||||||
|
|
||||||
lock (_locker)
|
lock (_locker)
|
||||||
{
|
{
|
||||||
|
if (_jsonQueue.Count >= _maxQueueSize)
|
||||||
|
{
|
||||||
|
// If we've exceeded our queue size, and we're supposed to throw out the oldest objects first,
|
||||||
|
// then remove as many as necessary to get us under our limit
|
||||||
|
if (_queueOverflowDiscardOldest)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger()
|
||||||
|
.Warn("Overflow discarding oldest {0} messages", _jsonQueue.Count - _maxQueueSize + 1);
|
||||||
|
|
||||||
|
_jsonQueue.RemoveRange(0, (_jsonQueue.Count - _maxQueueSize) + 1);
|
||||||
|
}
|
||||||
|
// Otherwise we're in a "discard newest" mode, and this is the newest message, so just ignore it
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger()
|
||||||
|
.Warn("Overflow discarding newest message: {0}", message);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
_jsonQueue.Add(jsonMessage);
|
_jsonQueue.Add(jsonMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
140
TimberWinR/Outputs/File.cs
Normal file
140
TimberWinR/Outputs/File.cs
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using NLog;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace TimberWinR.Outputs
|
||||||
|
{
|
||||||
|
public class FileOutput : OutputSender
|
||||||
|
{
|
||||||
|
private TimberWinR.Manager _manager;
|
||||||
|
private readonly int _interval;
|
||||||
|
private readonly object _locker = new object();
|
||||||
|
private readonly List<JObject> _jsonQueue;
|
||||||
|
private long _sentMessages;
|
||||||
|
private Parser.FileOutputParameters _arguments;
|
||||||
|
public bool Stop { get; set; }
|
||||||
|
|
||||||
|
public FileOutput(TimberWinR.Manager manager, Parser.FileOutputParameters arguments, CancellationToken cancelToken)
|
||||||
|
: base(cancelToken, "File")
|
||||||
|
{
|
||||||
|
_arguments = arguments;
|
||||||
|
_sentMessages = 0;
|
||||||
|
_manager = manager;
|
||||||
|
_interval = arguments.Interval;
|
||||||
|
_jsonQueue = new List<JObject>();
|
||||||
|
|
||||||
|
var elsThread = new Task(FileSender, cancelToken);
|
||||||
|
elsThread.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override JObject ToJson()
|
||||||
|
{
|
||||||
|
JObject json = new JObject(
|
||||||
|
new JProperty("file-output",
|
||||||
|
new JObject(
|
||||||
|
new JProperty("queuedMessageCount", _jsonQueue.Count),
|
||||||
|
new JProperty("sentMessageCount", _sentMessages))));
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Pull off messages from the Queue, batch them up and send them all across
|
||||||
|
//
|
||||||
|
private void FileSender()
|
||||||
|
{
|
||||||
|
|
||||||
|
using (var syncHandle = new ManualResetEventSlim())
|
||||||
|
{
|
||||||
|
var fi = new FileInfo(_arguments.FileName);
|
||||||
|
if (File.Exists(_arguments.FileName))
|
||||||
|
File.Delete(_arguments.FileName);
|
||||||
|
|
||||||
|
LogManager.GetCurrentClassLogger().Info("File Output Sending To: {0}", fi.FullName);
|
||||||
|
|
||||||
|
using (StreamWriter sw = File.AppendText(_arguments.FileName))
|
||||||
|
{
|
||||||
|
// Execute the query
|
||||||
|
while (!Stop)
|
||||||
|
{
|
||||||
|
if (!CancelToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
JObject[] messages;
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
messages = _jsonQueue.Take(_jsonQueue.Count).ToArray();
|
||||||
|
_jsonQueue.RemoveRange(0, messages.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messages.Length > 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (JObject obj in messages)
|
||||||
|
{
|
||||||
|
sw.WriteLine(obj.ToString(_arguments.ToFormat()));
|
||||||
|
_sentMessages++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Error(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!Stop)
|
||||||
|
syncHandle.Wait(TimeSpan.FromMilliseconds(_interval), CancelToken);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void MessageReceivedHandler(Newtonsoft.Json.Linq.JObject jsonMessage)
|
||||||
|
{
|
||||||
|
if (_manager.Config.Filters != null)
|
||||||
|
{
|
||||||
|
if (ApplyFilters(jsonMessage))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var message = jsonMessage.ToString();
|
||||||
|
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
_jsonQueue.Add(jsonMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ApplyFilters(JObject json)
|
||||||
|
{
|
||||||
|
bool drop = false;
|
||||||
|
|
||||||
|
foreach (var filter in _manager.Config.Filters)
|
||||||
|
{
|
||||||
|
if (!filter.Apply(json))
|
||||||
|
drop = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return drop;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -4,18 +4,21 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
using TimberWinR.Diagnostics;
|
||||||
using TimberWinR.Inputs;
|
using TimberWinR.Inputs;
|
||||||
|
|
||||||
namespace TimberWinR.Outputs
|
namespace TimberWinR.Outputs
|
||||||
{
|
{
|
||||||
public abstract class OutputSender
|
public abstract class OutputSender : IDiagnosable
|
||||||
{
|
{
|
||||||
public CancellationToken CancelToken { get; private set; }
|
public CancellationToken CancelToken { get; private set; }
|
||||||
private List<InputListener> _inputs;
|
private List<InputListener> _inputs;
|
||||||
|
public string Name { get; set; }
|
||||||
public OutputSender(CancellationToken cancelToken)
|
|
||||||
|
public OutputSender(CancellationToken cancelToken, string name)
|
||||||
{
|
{
|
||||||
CancelToken = cancelToken;
|
CancelToken = cancelToken;
|
||||||
|
Name = name;
|
||||||
_inputs = new List<InputListener>();
|
_inputs = new List<InputListener>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,6 +27,11 @@ namespace TimberWinR.Outputs
|
|||||||
listener.OnMessageRecieved += MessageReceivedHandler;
|
listener.OnMessageRecieved += MessageReceivedHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Startup(JObject json)
|
||||||
|
{
|
||||||
|
MessageReceivedHandler(json);
|
||||||
|
}
|
||||||
|
|
||||||
public abstract JObject ToJson();
|
public abstract JObject ToJson();
|
||||||
protected abstract void MessageReceivedHandler(JObject jsonMessage);
|
protected abstract void MessageReceivedHandler(JObject jsonMessage);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.Eventing.Reader;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using ctstone.Redis;
|
using CSRedis;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
@@ -13,30 +14,133 @@ using System.Threading.Tasks;
|
|||||||
using RapidRegex.Core;
|
using RapidRegex.Core;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using TimberWinR.Parser;
|
||||||
|
|
||||||
namespace TimberWinR.Outputs
|
namespace TimberWinR.Outputs
|
||||||
{
|
{
|
||||||
|
internal class BatchCounter
|
||||||
|
{
|
||||||
|
// Total number of times reached max batch count (indicates we are under pressure)
|
||||||
|
public int ReachedMaxBatchCountTimes { get; set; }
|
||||||
|
|
||||||
|
private readonly int[] _sampleQueueDepths;
|
||||||
|
private int _sampleCountIndex;
|
||||||
|
private const int QUEUE_SAMPLE_SIZE = 30; // 30 samples over 2.5 minutes (default)
|
||||||
|
private object _locker = new object();
|
||||||
|
private bool _warnedReachedMax;
|
||||||
|
|
||||||
|
private readonly int _maxBatchCount;
|
||||||
|
private readonly int _batchCount;
|
||||||
|
private int _totalSamples;
|
||||||
|
|
||||||
|
public int[] Samples()
|
||||||
|
{
|
||||||
|
return _sampleQueueDepths;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BatchCounter(int batchCount, int maxBatchCount)
|
||||||
|
{
|
||||||
|
_batchCount = batchCount;
|
||||||
|
_maxBatchCount = maxBatchCount;
|
||||||
|
_sampleQueueDepths = new int[QUEUE_SAMPLE_SIZE];
|
||||||
|
_sampleCountIndex = 0;
|
||||||
|
_totalSamples = 0;
|
||||||
|
ReachedMaxBatchCountTimes = 0;
|
||||||
|
}
|
||||||
|
public void SampleQueueDepth(int queueDepth)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
if (_totalSamples < QUEUE_SAMPLE_SIZE)
|
||||||
|
_totalSamples++;
|
||||||
|
|
||||||
|
// Take a sample of the queue depth
|
||||||
|
if (_sampleCountIndex >= QUEUE_SAMPLE_SIZE)
|
||||||
|
_sampleCountIndex = 0;
|
||||||
|
|
||||||
|
_sampleQueueDepths[_sampleCountIndex++] = queueDepth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int AverageQueueDepth()
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
if (_totalSamples > 0)
|
||||||
|
{
|
||||||
|
var samples = _sampleQueueDepths.Take(_totalSamples);
|
||||||
|
int avg = (int)samples.Average();
|
||||||
|
return avg;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sample the queue and adjust the batch count if needed (ramp up slowly)
|
||||||
|
public int UpdateCurrentBatchCount(int queueSize, int currentBatchCount)
|
||||||
|
{
|
||||||
|
var avgQueueDepth = AverageQueueDepth();
|
||||||
|
|
||||||
|
if (currentBatchCount < _maxBatchCount && currentBatchCount < queueSize && avgQueueDepth > currentBatchCount)
|
||||||
|
{
|
||||||
|
currentBatchCount += Math.Max(avgQueueDepth / _batchCount, _batchCount / 5);
|
||||||
|
if (currentBatchCount >= _maxBatchCount && !_warnedReachedMax)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Warn("Maximum Batch Count of {0} reached.", currentBatchCount);
|
||||||
|
_warnedReachedMax = true; // Only complain when it's reached (1 time, unless reset)
|
||||||
|
ReachedMaxBatchCountTimes++;
|
||||||
|
currentBatchCount = _maxBatchCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // Reset to default
|
||||||
|
{
|
||||||
|
currentBatchCount = _batchCount;
|
||||||
|
_warnedReachedMax = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentBatchCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public class RedisOutput : OutputSender
|
public class RedisOutput : OutputSender
|
||||||
{
|
{
|
||||||
|
public int QueueDepth
|
||||||
|
{
|
||||||
|
get { return _jsonQueue.Count; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public long SentMessages
|
||||||
|
{
|
||||||
|
get { return _sentMessages; }
|
||||||
|
}
|
||||||
|
|
||||||
private readonly string _logstashIndexName;
|
private readonly string _logstashIndexName;
|
||||||
private readonly int _port;
|
private readonly int _port;
|
||||||
private readonly int _timeout;
|
private readonly int _timeout;
|
||||||
private readonly object _locker = new object();
|
private readonly object _locker = new object();
|
||||||
private readonly List<string> _jsonQueue;
|
private readonly List<string> _jsonQueue;
|
||||||
// readonly Task _consumerTask;
|
|
||||||
private readonly string[] _redisHosts;
|
private readonly string[] _redisHosts;
|
||||||
private int _redisHostIndex;
|
private int _redisHostIndex;
|
||||||
private TimberWinR.Manager _manager;
|
private TimberWinR.Manager _manager;
|
||||||
private readonly int _batchCount;
|
private readonly int _batchCount;
|
||||||
|
private int _currentBatchCount;
|
||||||
|
private readonly int _maxBatchCount;
|
||||||
private readonly int _interval;
|
private readonly int _interval;
|
||||||
private readonly int _numThreads;
|
private readonly int _numThreads;
|
||||||
|
|
||||||
private long _sentMessages;
|
private long _sentMessages;
|
||||||
private long _errorCount;
|
private long _errorCount;
|
||||||
private long _redisDepth;
|
private long _redisDepth;
|
||||||
|
private DateTime? _lastErrorTimeUTC;
|
||||||
|
private DateTime? _lastSentTimeUTC;
|
||||||
|
private readonly int _maxQueueSize;
|
||||||
|
private readonly bool _queueOverflowDiscardOldest;
|
||||||
|
private BatchCounter _batchCounter;
|
||||||
|
|
||||||
|
public bool Stop { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the next client
|
/// Get the next client from the list of hosts.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private RedisClient getClient()
|
private RedisClient getClient()
|
||||||
@@ -49,16 +153,19 @@ namespace TimberWinR.Outputs
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
RedisClient client = new RedisClient(_redisHosts[_redisHostIndex], _port, _timeout);
|
RedisClient client = new RedisClient(_redisHosts[_redisHostIndex], _port);
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Error(ex);
|
||||||
|
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
_redisHostIndex++;
|
_redisHostIndex++;
|
||||||
if (_redisHostIndex >= _redisHosts.Length)
|
if (_redisHostIndex >= _redisHosts.Length)
|
||||||
_redisHostIndex = 0;
|
_redisHostIndex = 0;
|
||||||
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
}
|
}
|
||||||
numTries++;
|
numTries++;
|
||||||
}
|
}
|
||||||
@@ -73,13 +180,22 @@ namespace TimberWinR.Outputs
|
|||||||
new JObject(
|
new JObject(
|
||||||
new JProperty("host", string.Join(",", _redisHosts)),
|
new JProperty("host", string.Join(",", _redisHosts)),
|
||||||
new JProperty("errors", _errorCount),
|
new JProperty("errors", _errorCount),
|
||||||
new JProperty("redis_depth", _redisDepth),
|
new JProperty("lastErrorTimeUTC", _lastErrorTimeUTC),
|
||||||
new JProperty("sent_messages", _sentMessages),
|
new JProperty("lastSentTimeUTC", _lastSentTimeUTC),
|
||||||
new JProperty("queued_messages", _jsonQueue.Count),
|
new JProperty("redisQueueDepth", _redisDepth),
|
||||||
|
new JProperty("sentMessageCount", _sentMessages),
|
||||||
|
new JProperty("queuedMessageCount", _jsonQueue.Count),
|
||||||
new JProperty("port", _port),
|
new JProperty("port", _port),
|
||||||
|
new JProperty("maxQueueSize", _maxQueueSize),
|
||||||
|
new JProperty("overflowDiscardOldest", _queueOverflowDiscardOldest),
|
||||||
new JProperty("interval", _interval),
|
new JProperty("interval", _interval),
|
||||||
new JProperty("threads", _numThreads),
|
new JProperty("threads", _numThreads),
|
||||||
new JProperty("batchcount", _batchCount),
|
new JProperty("batchcount", _batchCount),
|
||||||
|
new JProperty("currentBatchCount", _currentBatchCount),
|
||||||
|
new JProperty("reachedMaxBatchCountTimes", _batchCounter.ReachedMaxBatchCountTimes),
|
||||||
|
new JProperty("maxBatchCount", _maxBatchCount),
|
||||||
|
new JProperty("averageQueueDepth", _batchCounter.AverageQueueDepth()),
|
||||||
|
new JProperty("queueSamples", new JArray(_batchCounter.Samples())),
|
||||||
new JProperty("index", _logstashIndexName),
|
new JProperty("index", _logstashIndexName),
|
||||||
new JProperty("hosts",
|
new JProperty("hosts",
|
||||||
new JArray(
|
new JArray(
|
||||||
@@ -89,23 +205,33 @@ namespace TimberWinR.Outputs
|
|||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RedisOutput(TimberWinR.Manager manager, Parser.RedisOutput ro, CancellationToken cancelToken)
|
public RedisOutput(TimberWinR.Manager manager, Parser.RedisOutputParameters parameters, CancellationToken cancelToken)
|
||||||
: base(cancelToken)
|
: base(cancelToken, "Redis")
|
||||||
{
|
{
|
||||||
_redisDepth = 0;
|
_redisDepth = 0;
|
||||||
_batchCount = ro.BatchCount;
|
_batchCount = parameters.BatchCount;
|
||||||
|
_maxBatchCount = parameters.MaxBatchCount;
|
||||||
|
// Make sure maxBatchCount is larger than batchCount
|
||||||
|
if (_maxBatchCount <= _batchCount)
|
||||||
|
_maxBatchCount = _batchCount * 10;
|
||||||
|
|
||||||
_manager = manager;
|
_manager = manager;
|
||||||
_redisHostIndex = 0;
|
_redisHostIndex = 0;
|
||||||
_redisHosts = ro.Host;
|
_redisHosts = parameters.Host;
|
||||||
_jsonQueue = new List<string>();
|
_jsonQueue = new List<string>();
|
||||||
_port = ro.Port;
|
_port = parameters.Port;
|
||||||
_timeout = ro.Timeout;
|
_timeout = parameters.Timeout;
|
||||||
_logstashIndexName = ro.Index;
|
_logstashIndexName = parameters.Index;
|
||||||
_interval = ro.Interval;
|
_interval = parameters.Interval;
|
||||||
_numThreads = ro.NumThreads;
|
_numThreads = parameters.NumThreads;
|
||||||
_errorCount = 0;
|
_errorCount = 0;
|
||||||
|
_lastErrorTimeUTC = null;
|
||||||
|
_maxQueueSize = parameters.MaxQueueSize;
|
||||||
|
_queueOverflowDiscardOldest = parameters.QueueOverflowDiscardOldest;
|
||||||
|
_batchCounter = new BatchCounter(_batchCount, _maxBatchCount);
|
||||||
|
_currentBatchCount = _batchCount;
|
||||||
|
|
||||||
for (int i = 0; i < ro.NumThreads; i++)
|
for (int i = 0; i < parameters.NumThreads; i++)
|
||||||
{
|
{
|
||||||
var redisThread = new Task(RedisSender, cancelToken);
|
var redisThread = new Task(RedisSender, cancelToken);
|
||||||
redisThread.Start();
|
redisThread.Start();
|
||||||
@@ -114,7 +240,7 @@ namespace TimberWinR.Outputs
|
|||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return string.Format("Redis Host: {0} Port: {1}, Threads: {2}, Interval: {3}, BatchCount: {4}", string.Join(",", _redisHosts) , _port, _numThreads, _interval, _batchCount);
|
return string.Format("Redis Host: {0} Port: {1}, Threads: {2}, Interval: {3}, BatchCount: {4}", string.Join(",", _redisHosts), _port, _numThreads, _interval, _batchCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -124,90 +250,185 @@ namespace TimberWinR.Outputs
|
|||||||
protected override void MessageReceivedHandler(JObject jsonMessage)
|
protected override void MessageReceivedHandler(JObject jsonMessage)
|
||||||
{
|
{
|
||||||
if (_manager.Config.Filters != null)
|
if (_manager.Config.Filters != null)
|
||||||
ApplyFilters(jsonMessage);
|
{
|
||||||
|
if (ApplyFilters(jsonMessage))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var message = jsonMessage.ToString();
|
var message = jsonMessage.ToString();
|
||||||
LogManager.GetCurrentClassLogger().Debug(message);
|
LogManager.GetCurrentClassLogger().Trace(message);
|
||||||
|
|
||||||
lock (_locker)
|
lock (_locker)
|
||||||
{
|
{
|
||||||
|
if (_jsonQueue.Count >= _maxQueueSize)
|
||||||
|
{
|
||||||
|
// If we've exceeded our queue size, and we're supposed to throw out the oldest objects first,
|
||||||
|
// then remove as many as necessary to get us under our limit
|
||||||
|
if (_queueOverflowDiscardOldest)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger()
|
||||||
|
.Warn("Overflow discarding oldest {0} messages", _jsonQueue.Count - _maxQueueSize + 1);
|
||||||
|
|
||||||
|
_jsonQueue.RemoveRange(0, (_jsonQueue.Count - _maxQueueSize) + 1);
|
||||||
|
}
|
||||||
|
// Otherwise we're in a "discard newest" mode, and this is the newest message, so just ignore it
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger()
|
||||||
|
.Warn("Overflow discarding newest message: {0}", message);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_jsonQueue.Add(message);
|
_jsonQueue.Add(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyFilters(JObject json)
|
private bool ApplyFilters(JObject json)
|
||||||
{
|
{
|
||||||
|
bool drop = false;
|
||||||
foreach (var filter in _manager.Config.Filters)
|
foreach (var filter in _manager.Config.Filters)
|
||||||
{
|
{
|
||||||
filter.Apply(json);
|
if (!filter.Apply(json))
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Debug("{0}: Dropping: {1}", Thread.CurrentThread.ManagedThreadId, json.ToString());
|
||||||
|
drop = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return drop;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Pull off messages from the Queue, batch them up and send them all across
|
// Pull off messages from the Queue, batch them up and send them all across
|
||||||
//
|
//
|
||||||
private void RedisSender()
|
private void RedisSender()
|
||||||
{
|
{
|
||||||
while (!CancelToken.IsCancellationRequested)
|
using (var syncHandle = new ManualResetEventSlim())
|
||||||
{
|
{
|
||||||
string[] messages;
|
// Execute the query
|
||||||
lock (_locker)
|
while (!Stop)
|
||||||
{
|
{
|
||||||
messages = _jsonQueue.Take(_batchCount).ToArray();
|
if (!CancelToken.IsCancellationRequested)
|
||||||
_jsonQueue.RemoveRange(0, messages.Length);
|
|
||||||
if (messages.Length > 0)
|
|
||||||
_manager.IncrementMessageCount(messages.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (messages.Length > 0)
|
|
||||||
{
|
|
||||||
int numHosts = _redisHosts.Length;
|
|
||||||
while (numHosts-- > 0)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Get the next client
|
string[] messages;
|
||||||
using (RedisClient client = getClient())
|
// Exclusively
|
||||||
|
lock (_locker)
|
||||||
{
|
{
|
||||||
if (client != null)
|
_batchCounter.SampleQueueDepth(_jsonQueue.Count);
|
||||||
{
|
// Re-compute current batch size
|
||||||
client.StartPipe();
|
|
||||||
LogManager.GetCurrentClassLogger()
|
|
||||||
.Debug("Sending {0} Messages to {1}", messages.Length, client.Host);
|
|
||||||
|
|
||||||
foreach (string jsonMessage in messages)
|
LogManager.GetCurrentClassLogger()
|
||||||
|
.Trace("{0}: Average Queue Depth: {1}, Current Length: {2}",
|
||||||
|
Thread.CurrentThread.ManagedThreadId, _batchCounter.AverageQueueDepth(),
|
||||||
|
_jsonQueue.Count);
|
||||||
|
|
||||||
|
_currentBatchCount = _batchCounter.UpdateCurrentBatchCount(_jsonQueue.Count,
|
||||||
|
_currentBatchCount);
|
||||||
|
|
||||||
|
messages = _jsonQueue.Take(_currentBatchCount).ToArray();
|
||||||
|
_jsonQueue.RemoveRange(0, messages.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messages.Length > 0)
|
||||||
|
{
|
||||||
|
int numHosts = _redisHosts.Length;
|
||||||
|
bool sentSuccessfully = false;
|
||||||
|
while (numHosts-- > 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
try
|
// Get the next client
|
||||||
|
using (RedisClient client = getClient())
|
||||||
{
|
{
|
||||||
_redisDepth = client.RPush(_logstashIndexName, jsonMessage);
|
if (client != null)
|
||||||
_sentMessages++;
|
{
|
||||||
}
|
client.StartPipe();
|
||||||
catch (SocketException ex)
|
LogManager.GetCurrentClassLogger()
|
||||||
{
|
.Debug("{0}: Sending {1} Messages to {2}",
|
||||||
LogManager.GetCurrentClassLogger().Warn(ex);
|
Thread.CurrentThread.ManagedThreadId, messages.Length,
|
||||||
Interlocked.Increment(ref _errorCount);
|
client.Host);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_redisDepth = client.RPush(_logstashIndexName, messages);
|
||||||
|
Interlocked.Add(ref _sentMessages, messages.Length);
|
||||||
|
client.EndPipe();
|
||||||
|
sentSuccessfully = true;
|
||||||
|
_lastSentTimeUTC = DateTime.UtcNow;
|
||||||
|
if (messages.Length > 0)
|
||||||
|
_manager.IncrementMessageCount(messages.Length);
|
||||||
|
}
|
||||||
|
catch (SocketException ex)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Warn(ex);
|
||||||
|
Interlocked.Increment(ref _errorCount);
|
||||||
|
_lastErrorTimeUTC = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Error(ex);
|
||||||
|
Interlocked.Increment(ref _errorCount);
|
||||||
|
_lastErrorTimeUTC = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref _errorCount);
|
||||||
|
LogManager.GetCurrentClassLogger()
|
||||||
|
.Fatal("Unable to connect with any Redis hosts, {0}",
|
||||||
|
String.Join(",", _redisHosts));
|
||||||
|
_lastErrorTimeUTC = DateTime.UtcNow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
client.EndPipe();
|
catch (Exception ex)
|
||||||
break;
|
{
|
||||||
}
|
LogManager.GetCurrentClassLogger().Error(ex);
|
||||||
else
|
Interlocked.Increment(ref _errorCount);
|
||||||
|
_lastErrorTimeUTC = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
} // No more hosts to try.
|
||||||
|
|
||||||
|
// Couldn't send, put it back into the queue.
|
||||||
|
if (!sentSuccessfully)
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref _errorCount);
|
lock (_locker)
|
||||||
LogManager.GetCurrentClassLogger()
|
{
|
||||||
.Fatal("Unable to connect with any Redis hosts, {0}",
|
_jsonQueue.InsertRange(0, messages);
|
||||||
String.Join(",", _redisHosts));
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (ThreadAbortException)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
LogManager.GetCurrentClassLogger().Error(ex);
|
_lastErrorTimeUTC = DateTime.UtcNow;
|
||||||
Interlocked.Increment(ref _errorCount);
|
Interlocked.Increment(ref _errorCount);
|
||||||
|
LogManager.GetCurrentClassLogger().Error(ex);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!Stop)
|
||||||
|
syncHandle.Wait(TimeSpan.FromMilliseconds(_interval), CancelToken);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
System.Threading.Thread.Sleep(_interval);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
368
TimberWinR/Outputs/StatsD.cs
Normal file
368
TimberWinR/Outputs/StatsD.cs
Normal file
@@ -0,0 +1,368 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.Eventing.Reader;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using CSRedis;
|
||||||
|
using Nest;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using NLog;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using RapidRegex.Core;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Globalization;
|
||||||
|
using StatsdClient;
|
||||||
|
using TimberWinR.Parser;
|
||||||
|
|
||||||
|
namespace TimberWinR.Outputs
|
||||||
|
{
|
||||||
|
public class StatsDOutput : OutputSender
|
||||||
|
{
|
||||||
|
public int QueueDepth
|
||||||
|
{
|
||||||
|
get { return _jsonQueue.Count; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public long SentMessages
|
||||||
|
{
|
||||||
|
get { return _sentMessages; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private readonly int _port;
|
||||||
|
public string _host { get; set; }
|
||||||
|
|
||||||
|
private readonly int _interval;
|
||||||
|
private readonly object _locker = new object();
|
||||||
|
private readonly List<JObject> _jsonQueue;
|
||||||
|
private TimberWinR.Manager _manager;
|
||||||
|
private long _sentMessages;
|
||||||
|
private long _errorCount;
|
||||||
|
private readonly int _maxQueueSize;
|
||||||
|
private readonly bool _queueOverflowDiscardOldest;
|
||||||
|
private readonly int _flushSize;
|
||||||
|
private readonly int _idleFlushTimeSeconds;
|
||||||
|
private readonly int _numThreads;
|
||||||
|
private Parser.StatsDOutputParameters _params;
|
||||||
|
|
||||||
|
public bool Stop { get; set; }
|
||||||
|
|
||||||
|
public override JObject ToJson()
|
||||||
|
{
|
||||||
|
var json = new JObject(
|
||||||
|
new JProperty("statsd",
|
||||||
|
new JObject(
|
||||||
|
new JProperty("errors", _errorCount),
|
||||||
|
new JProperty("sentMessageCount", _sentMessages),
|
||||||
|
new JProperty("queuedMessageCount", _jsonQueue.Count),
|
||||||
|
new JProperty("port", _port),
|
||||||
|
new JProperty("threads", _numThreads),
|
||||||
|
new JProperty("flushSize", _flushSize),
|
||||||
|
new JProperty("idleFlushTime", _idleFlushTimeSeconds),
|
||||||
|
new JProperty("maxQueueSize", _maxQueueSize),
|
||||||
|
new JProperty("overflowDiscardOldest", _queueOverflowDiscardOldest),
|
||||||
|
new JProperty("interval", _interval),
|
||||||
|
new JProperty("host", _host)
|
||||||
|
)));
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatsDOutput(TimberWinR.Manager manager, Parser.StatsDOutputParameters parameters, CancellationToken cancelToken)
|
||||||
|
: base(cancelToken, "StatsD")
|
||||||
|
{
|
||||||
|
_params = parameters;
|
||||||
|
_manager = manager;
|
||||||
|
_port = parameters.Port;
|
||||||
|
_host = parameters.Host;
|
||||||
|
_interval = parameters.Interval;
|
||||||
|
_flushSize = parameters.FlushSize;
|
||||||
|
_idleFlushTimeSeconds = parameters.IdleFlushTimeInSeconds;
|
||||||
|
_maxQueueSize = parameters.MaxQueueSize;
|
||||||
|
_queueOverflowDiscardOldest = parameters.QueueOverflowDiscardOldest;
|
||||||
|
_numThreads = parameters.NumThreads;
|
||||||
|
_jsonQueue = new List<JObject>();
|
||||||
|
|
||||||
|
var metricsConfig = new MetricsConfig
|
||||||
|
{
|
||||||
|
StatsdServerName = _host,
|
||||||
|
Prefix = parameters.Namespace,
|
||||||
|
};
|
||||||
|
|
||||||
|
StatsdClient.Metrics.Configure(metricsConfig);
|
||||||
|
|
||||||
|
for (int i = 0; i < _numThreads; i++)
|
||||||
|
{
|
||||||
|
Task.Factory.StartNew(StatsDSender, cancelToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format("StatsD Host: {0} Port: {1}", _host, _port);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Forward on Json message to Redis Logstash queue
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="jsonMessage"></param>
|
||||||
|
protected override void MessageReceivedHandler(JObject jsonMessage)
|
||||||
|
{
|
||||||
|
if (_manager.Config.Filters != null)
|
||||||
|
{
|
||||||
|
if (ApplyFilters(jsonMessage))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var message = jsonMessage.ToString();
|
||||||
|
LogManager.GetCurrentClassLogger().Trace(message);
|
||||||
|
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
if (_jsonQueue.Count >= _maxQueueSize)
|
||||||
|
{
|
||||||
|
// If we've exceeded our queue size, and we're supposed to throw out the oldest objects first,
|
||||||
|
// then remove as many as necessary to get us under our limit
|
||||||
|
if (_queueOverflowDiscardOldest)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger()
|
||||||
|
.Warn("Overflow discarding oldest {0} messages", _jsonQueue.Count - _maxQueueSize + 1);
|
||||||
|
|
||||||
|
_jsonQueue.RemoveRange(0, (_jsonQueue.Count - _maxQueueSize) + 1);
|
||||||
|
}
|
||||||
|
// Otherwise we're in a "discard newest" mode, and this is the newest message, so just ignore it
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger()
|
||||||
|
.Warn("Overflow discarding newest message: {0}", message);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_jsonQueue.Add(jsonMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ApplyFilters(JObject json)
|
||||||
|
{
|
||||||
|
bool drop = false;
|
||||||
|
foreach (var filter in _manager.Config.Filters)
|
||||||
|
{
|
||||||
|
if (!filter.Apply(json))
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Debug("{0}: Dropping: {1}", Thread.CurrentThread.ManagedThreadId, json.ToString());
|
||||||
|
drop = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check for matching type (if defined).
|
||||||
|
if (!drop && !string.IsNullOrEmpty(_params.InputType) && json["type"] != null)
|
||||||
|
{
|
||||||
|
string msgType = json["type"].ToString();
|
||||||
|
if (!string.IsNullOrEmpty(msgType) && msgType != _params.InputType)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return drop;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Places messages back into the queue (for a future attempt)
|
||||||
|
private void interlockedInsert(List<JObject> messages)
|
||||||
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref _errorCount);
|
||||||
|
_jsonQueue.InsertRange(0, messages);
|
||||||
|
if (_jsonQueue.Count > _maxQueueSize)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Warn("Exceeded maximum queue depth");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Pull off messages from the Queue, batch them up and send them all across
|
||||||
|
//
|
||||||
|
private void StatsDSender()
|
||||||
|
{
|
||||||
|
DateTime lastFlushTime = DateTime.MinValue;
|
||||||
|
|
||||||
|
using (var syncHandle = new ManualResetEventSlim())
|
||||||
|
{
|
||||||
|
// Execute the query
|
||||||
|
while (!Stop)
|
||||||
|
{
|
||||||
|
if (!CancelToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int messageCount = 0;
|
||||||
|
List<JObject> messages = new List<JObject>();
|
||||||
|
|
||||||
|
// Lets get whats in the queue
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
messageCount = _jsonQueue.Count;
|
||||||
|
|
||||||
|
// Time to flush?
|
||||||
|
if (messageCount >= _flushSize || (DateTime.UtcNow - lastFlushTime).Seconds >= _idleFlushTimeSeconds)
|
||||||
|
{
|
||||||
|
messages = _jsonQueue.Take(messageCount).ToList();
|
||||||
|
_jsonQueue.RemoveRange(0, messageCount);
|
||||||
|
if (messages.Count > 0)
|
||||||
|
_manager.IncrementMessageCount(messages.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TransmitStats(messages);
|
||||||
|
|
||||||
|
if (!Stop)
|
||||||
|
syncHandle.Wait(TimeSpan.FromMilliseconds(_interval), CancelToken);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (ThreadAbortException)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref _errorCount);
|
||||||
|
LogManager.GetCurrentClassLogger().Error(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected string ExpandField(string fieldName, JObject json)
|
||||||
|
{
|
||||||
|
foreach (var token in json.Children())
|
||||||
|
{
|
||||||
|
string replaceString = "%{" + token.Path + "}";
|
||||||
|
fieldName = fieldName.Replace(replaceString, json[token.Path].ToString());
|
||||||
|
}
|
||||||
|
return fieldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string BuildMetricPath(string metric, JObject json)
|
||||||
|
{
|
||||||
|
return string.Format("{0}.{1}", ExpandField(_params.Sender, json), ExpandField(metric, json));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TransmitStats(List<JObject> messages)
|
||||||
|
{
|
||||||
|
// We've got some to send.
|
||||||
|
if (messages.Count > 0)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int numMessages = messages.Count;
|
||||||
|
foreach (var m in messages)
|
||||||
|
{
|
||||||
|
SendMetrics(m);
|
||||||
|
}
|
||||||
|
messages.RemoveRange(0, numMessages);
|
||||||
|
Interlocked.Add(ref _sentMessages, numMessages);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Error(ex);
|
||||||
|
|
||||||
|
interlockedInsert(messages); // Put the messages back into the queue
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (messages.Count > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process all the metrics for this json
|
||||||
|
private void SendMetrics(JObject m)
|
||||||
|
{
|
||||||
|
if (_params.Gauges != null && _params.Gauges.Length > 0)
|
||||||
|
DoGauges(m);
|
||||||
|
if (_params.Counts != null && _params.Counts.Length > 0)
|
||||||
|
DoCounts(m);
|
||||||
|
if (_params.Timings != null && _params.Timings.Length > 0)
|
||||||
|
DoTimings(m);
|
||||||
|
if (_params.Increments != null && _params.Increments.Length > 0)
|
||||||
|
DoIncrements(m);
|
||||||
|
if (_params.Decrements != null && _params.Decrements.Length > 0)
|
||||||
|
DoDecrements(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the Gauges
|
||||||
|
private void DoGauges(JObject json)
|
||||||
|
{
|
||||||
|
for (int i=0; i<_params.Gauges.Length; i += 2)
|
||||||
|
{
|
||||||
|
string metricPath = BuildMetricPath(_params.Gauges[i], json);
|
||||||
|
string gaugeName = ExpandField(_params.Gauges[i + 1], json);
|
||||||
|
int value;
|
||||||
|
if (int.TryParse(gaugeName, out value))
|
||||||
|
{
|
||||||
|
Metrics.Gauge(metricPath, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the Gauges
|
||||||
|
private void DoTimings(JObject json)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _params.Timings.Length; i += 2)
|
||||||
|
{
|
||||||
|
string metricPath = BuildMetricPath(_params.Timings[i], json);
|
||||||
|
string timingName = ExpandField(_params.Timings[i + 1], json);
|
||||||
|
int value;
|
||||||
|
if (int.TryParse(timingName, out value))
|
||||||
|
{
|
||||||
|
Metrics.Timer(metricPath, value, _params.SampleRate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the Counts
|
||||||
|
private void DoCounts(JObject json)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _params.Counts.Length; i += 2)
|
||||||
|
{
|
||||||
|
string metricPath = BuildMetricPath(_params.Counts[i], json);
|
||||||
|
string countName = ExpandField(_params.Counts[i + 1], json);
|
||||||
|
int value;
|
||||||
|
if (int.TryParse(countName, out value))
|
||||||
|
{
|
||||||
|
Metrics.Counter(metricPath, value, _params.SampleRate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Process the Increments
|
||||||
|
private void DoIncrements(JObject json)
|
||||||
|
{
|
||||||
|
foreach (var metric in _params.Increments)
|
||||||
|
{
|
||||||
|
string metricPath = BuildMetricPath(metric, json);
|
||||||
|
Metrics.Counter(metricPath, 1,_params.SampleRate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the Increments
|
||||||
|
private void DoDecrements(JObject json)
|
||||||
|
{
|
||||||
|
foreach (var metric in _params.Increments)
|
||||||
|
{
|
||||||
|
string metricPath = BuildMetricPath(metric, json);
|
||||||
|
Metrics.Counter(metricPath, -1, _params.SampleRate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,9 +15,10 @@ namespace TimberWinR.Outputs
|
|||||||
private readonly object _locker = new object();
|
private readonly object _locker = new object();
|
||||||
private readonly List<JObject> _jsonQueue;
|
private readonly List<JObject> _jsonQueue;
|
||||||
private long _sentMessages;
|
private long _sentMessages;
|
||||||
|
public bool Stop { get; set; }
|
||||||
|
|
||||||
public StdoutOutput(TimberWinR.Manager manager, Parser.StdoutOutput eo, CancellationToken cancelToken)
|
public StdoutOutput(TimberWinR.Manager manager, Parser.StdoutOutputParameters eo, CancellationToken cancelToken)
|
||||||
: base(cancelToken)
|
: base(cancelToken, "Stdout")
|
||||||
{
|
{
|
||||||
_sentMessages = 0;
|
_sentMessages = 0;
|
||||||
_manager = manager;
|
_manager = manager;
|
||||||
@@ -33,8 +34,9 @@ namespace TimberWinR.Outputs
|
|||||||
JObject json = new JObject(
|
JObject json = new JObject(
|
||||||
new JProperty("stdout",
|
new JProperty("stdout",
|
||||||
new JObject(
|
new JObject(
|
||||||
new JProperty("sent_messages", _sentMessages))));
|
new JProperty("queuedMessageCount", _jsonQueue.Count),
|
||||||
|
new JProperty("sentMessageCount", _sentMessages))));
|
||||||
|
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,38 +45,59 @@ namespace TimberWinR.Outputs
|
|||||||
//
|
//
|
||||||
private void StdoutSender()
|
private void StdoutSender()
|
||||||
{
|
{
|
||||||
while (!CancelToken.IsCancellationRequested)
|
using (var syncHandle = new ManualResetEventSlim())
|
||||||
{
|
{
|
||||||
JObject[] messages;
|
// Execute the query
|
||||||
lock (_locker)
|
while (!Stop)
|
||||||
{
|
{
|
||||||
messages = _jsonQueue.Take(1).ToArray();
|
if (!CancelToken.IsCancellationRequested)
|
||||||
_jsonQueue.RemoveRange(0, messages.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (messages.Length > 0)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
foreach (JObject obj in messages)
|
try
|
||||||
|
{
|
||||||
|
JObject[] messages;
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
messages = _jsonQueue.Take(_jsonQueue.Count).ToArray();
|
||||||
|
_jsonQueue.RemoveRange(0, messages.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messages.Length > 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (JObject obj in messages)
|
||||||
|
{
|
||||||
|
Console.WriteLine(obj.ToString());
|
||||||
|
Interlocked.Increment(ref _sentMessages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogManager.GetCurrentClassLogger().Error(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!Stop)
|
||||||
|
syncHandle.Wait(TimeSpan.FromMilliseconds(_interval), CancelToken);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
{
|
{
|
||||||
Console.WriteLine(obj.ToString());
|
|
||||||
_sentMessages++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
LogManager.GetCurrentClassLogger().Error(ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
System.Threading.Thread.Sleep(_interval);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void MessageReceivedHandler(Newtonsoft.Json.Linq.JObject jsonMessage)
|
protected override void MessageReceivedHandler(Newtonsoft.Json.Linq.JObject jsonMessage)
|
||||||
{
|
{
|
||||||
if (_manager.Config.Filters != null)
|
if (_manager.Config.Filters != null)
|
||||||
ApplyFilters(jsonMessage);
|
{
|
||||||
|
if (ApplyFilters(jsonMessage))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var message = jsonMessage.ToString();
|
var message = jsonMessage.ToString();
|
||||||
LogManager.GetCurrentClassLogger().Debug(message);
|
LogManager.GetCurrentClassLogger().Debug(message);
|
||||||
@@ -85,12 +108,17 @@ namespace TimberWinR.Outputs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyFilters(JObject json)
|
private bool ApplyFilters(JObject json)
|
||||||
{
|
{
|
||||||
|
bool drop = false;
|
||||||
|
|
||||||
foreach (var filter in _manager.Config.Filters)
|
foreach (var filter in _manager.Config.Filters)
|
||||||
{
|
{
|
||||||
filter.Apply(json);
|
if (!filter.Apply(json))
|
||||||
|
drop = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return drop;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
|||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion("1.0.0.0")]
|
[assembly: AssemblyVersion("1.2.0.0")]
|
||||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
[assembly: AssemblyFileVersion("1.2.0.0")]
|
||||||
|
|||||||
115
TimberWinR/ReleaseNotes.md
Normal file
115
TimberWinR/ReleaseNotes.md
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
TimberWinR Release Notes
|
||||||
|
==================================
|
||||||
|
A Native Windows to Redis/Elasticsearch Logstash Agent which runs as a service.
|
||||||
|
|
||||||
|
Version / Date
|
||||||
|
### 1.3.26.0 - 2015-05-15
|
||||||
|
1. Added StatsD outputter
|
||||||
|
2. Fixed shutdown hang if shutdown was received before service was fully started up.
|
||||||
|
3. Closed issue [#36](https://github.com/Cimpress-MCP/TimberWinR/issues/36)
|
||||||
|
|
||||||
|
### 1.3.25.0 - 2015-04-30
|
||||||
|
1. Fixed Issue [#49](https://github.com/Cimpress-MCP/TimberWinR/issues/49)
|
||||||
|
2. Fixed potential non-thread safe when renaming properties
|
||||||
|
3. Added add_field, rename support to Udp/Tcp Input Listeners
|
||||||
|
4. Fixed issue with multiple renames (was previously only renaming the first one)
|
||||||
|
5. Added File outputter for testing.
|
||||||
|
|
||||||
|
### 1.3.24.0 - 2015-04-29
|
||||||
|
1. Fixed potential bug in TailFiles when tailing log files which are partially flushed
|
||||||
|
to disk, it now will not process the line until the \r\n has been seen.
|
||||||
|
2. Added Generator input.
|
||||||
|
|
||||||
|
### 1.3.23.0 - 2015-04-23
|
||||||
|
1. Fixed bug with parsing a single json config file, rather than reading
|
||||||
|
JSON files from a directory.
|
||||||
|
2. Diabled elasticsearch outputter ping by default and parameterized the ping capability.
|
||||||
|
|
||||||
|
### 1.3.22.0 - 2015-04-14
|
||||||
|
1. Fixed minor bug with TailFiles and service re-starts not picking up
|
||||||
|
rolled files right away.
|
||||||
|
|
||||||
|
### 1.3.21.0 - 2015-04-13
|
||||||
|
1. Rolled Udp listener support to V4 only, too many issues with dual mode sockets
|
||||||
|
and hosts file. If we want to add this back, I will add a Udpv6 input.
|
||||||
|
|
||||||
|
### 1.3.20.0 - 2015-04-03
|
||||||
|
|
||||||
|
1. A re-factoring of Logs and TailLogs to be more efficient and detect log rolling correctly,
|
||||||
|
this requires http://support.microsoft.com/en-us/kb/172190 which will be detected and
|
||||||
|
set by TimberWinR, however, requires a reboot.
|
||||||
|
2. Fixed issue [#38](https://github.com/Cimpress-MCP/TimberWinR/issues/38) diagnostic output not showing drop flag for Grok filter.
|
||||||
|
3. Created TimberWinR.TestGenerator for complete testing of TimberWinR
|
||||||
|
4. Fixed ipv4/ipv6 thread-safe issue with UdpInputListener which might lead to corrupted input data.
|
||||||
|
|
||||||
|
### 1.3.19.1 - 2015-03-03
|
||||||
|
|
||||||
|
1. Added new Redis parameter _max\_batch\_count_ which increases the _batch\_count_ dynamically over time
|
||||||
|
to handle input flooding. Default is _batch\_count_ * 10
|
||||||
|
|
||||||
|
### 1.3.19.0 - 2015-02-26
|
||||||
|
|
||||||
|
1. Added support for Multiline codecs for Stdin and Logs listeners, closes issue [#23](https://github.com/Cimpress-MCP/TimberWinR/issues/23)
|
||||||
|
2. Added new TailFiles input type which uses a native implementation (more-efficient) than using LogParser's Log
|
||||||
|
3. Updated Udp input listner to use UTF8 Encoding rather than ASCII
|
||||||
|
4. Reduced noisy complaint about missing log files for Logs listener
|
||||||
|
5. Fixed bug when tailing non-existent log files which resulted in high cpu-usage.
|
||||||
|
6. Added feature to watch the configuration directory
|
||||||
|
|
||||||
|
### 1.3.18.0 - 2014-12-22
|
||||||
|
|
||||||
|
1. Fixed bug introduced in 1.3.17.0 which changed the meaning of the delay for Elasticsearch, Redis and Stdout
|
||||||
|
intervals to be interpreted as seconds instead of milliseconds. 1.3.17.0 should not be used.
|
||||||
|
2. Removed ability for installer to downgrade which was leading to leaving previous versions laying around (i.e. reverts 1.3.13.0 change)
|
||||||
|
|
||||||
|
### 1.3.17.0 - 2014-12-19
|
||||||
|
|
||||||
|
1. Continued work improving shutdown time by using syncHandle.Wait instead of Thread.Sleep
|
||||||
|
|
||||||
|
### 1.3.16.0 - 2014-12-19
|
||||||
|
|
||||||
|
1. Added logSource property to the Log input to facility the steering of log messages to different indices.
|
||||||
|
|
||||||
|
### 1.3.15.0 - 2014-12-12
|
||||||
|
|
||||||
|
1. Fixed bug whereby if the Udp or Tcp inputs receive an impropery formatted Json it caused the thread to terminate, and ignore
|
||||||
|
future messages.
|
||||||
|
|
||||||
|
### 1.3.14.0 - 2014-12-11
|
||||||
|
|
||||||
|
1. Fixed bug with the Grok filter to match properly the value of the Text field against non-blank entries.
|
||||||
|
|
||||||
|
### 1.3.13.0 - 2014-12-02
|
||||||
|
|
||||||
|
1. Fixed MSI installer to allow downgrades.
|
||||||
|
|
||||||
|
### 1.3.12.0 - 2014-11-25
|
||||||
|
|
||||||
|
1. Fixed all remaining memory leaks due to the COM Weak Surrogate which requires an explicit GC.Collect
|
||||||
|
|
||||||
|
### 1.3.11.0 - 2014-11-21
|
||||||
|
|
||||||
|
1. Re-worked WindowsEvent listener to enable shutting down in a quicker fashion.
|
||||||
|
|
||||||
|
### 1.3.10.0 - 2014-11-18
|
||||||
|
|
||||||
|
1. Refactored Conditions handler to use non-leaking evaluator.
|
||||||
|
|
||||||
|
### 1.3.9.0 - 2014-11-11
|
||||||
|
|
||||||
|
1. Merged in pull request #9
|
||||||
|
2. Updated chocolately uninstall to preserve GUID
|
||||||
|
|
||||||
|
### 1.3.8.0 - 2014-11-06
|
||||||
|
|
||||||
|
1. Added interval parameter to WindowsEvent input listener
|
||||||
|
2. Increased default value for interval to 60 seconds for polling WindowsEvents
|
||||||
|
|
||||||
|
### 1.3.7.0 - 2014-10-21
|
||||||
|
|
||||||
|
1. Added additional information for diagnostics port
|
||||||
|
2. Completed minor handling of Log rolling detection
|
||||||
|
|
||||||
|
### 1.3.6.0 - 2014-10-16
|
||||||
|
|
||||||
|
1. Handle rolling of logs whereby the logfile remains the same, but the content resets back to 0 bytes.
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user