Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5165224c65 | ||
|
|
fd9306e24e | ||
|
|
050ae7cf84 | ||
|
|
a99b04e1b1 | ||
|
|
dddbf6ae0d | ||
|
|
147e243478 | ||
|
|
7cf1230d7a | ||
|
|
4fe5cec9e2 | ||
|
|
9308f91316 | ||
|
|
28d20199ab | ||
|
|
5692d2ec42 | ||
|
|
4dbb926698 | ||
|
|
92f23c1117 | ||
|
|
cd31cf47f5 |
@@ -2,6 +2,6 @@
|
|||||||
<packages>
|
<packages>
|
||||||
<package id="Newtonsoft.Json" version="6.0.4" targetFramework="net40" />
|
<package id="Newtonsoft.Json" version="6.0.4" targetFramework="net40" />
|
||||||
<package id="NUnit.Runners" version="2.6.4" />
|
<package id="NUnit.Runners" version="2.6.4" />
|
||||||
<package id="RapidRegex.Core" version="1.0.0.2" targetFramework="net40" />
|
<package id="RapidRegex.Core" version="1.0.0.4" targetFramework="net40" />
|
||||||
<package id="System.Linq.Dynamic" version="1.0.4" targetFramework="net40" />
|
<package id="System.Linq.Dynamic" version="1.0.4" targetFramework="net40" />
|
||||||
</packages>
|
</packages>
|
||||||
51
README.md
51
README.md
@@ -2,6 +2,14 @@ TimberWinR
|
|||||||
==========
|
==========
|
||||||
A Native Windows to Redis/Elasticsearch Logstash Agent which runs as a service.
|
A Native Windows to Redis/Elasticsearch Logstash Agent which runs as a service.
|
||||||
|
|
||||||
|
## Development and Roadmap
|
||||||
|
|
||||||
|
TimberWinR's development has been and in the short term will continue to be on a hiatus. The project's ownership has been recently transferred, and thus the project will be slow to pick up steam again. Currently a roadmap is being established, and a vision for the project is currently being formulated.
|
||||||
|
|
||||||
|
#### Getting Involved
|
||||||
|
|
||||||
|
If you wish to be involved with TimberWinR in an on-going basis file an issue.
|
||||||
|
|
||||||
## Why have TimberWinR?
|
## Why have TimberWinR?
|
||||||
TimberWinR is a native .NET implementation utilizing Microsoft's [LogParser](http://technet.microsoft.com/en-us/scriptcenter/dd919274.aspx). This means
|
TimberWinR is a native .NET implementation utilizing Microsoft's [LogParser](http://technet.microsoft.com/en-us/scriptcenter/dd919274.aspx). This means
|
||||||
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
|
||||||
@@ -9,14 +17,14 @@ the data from LogParser and ships it to Logstash via Redis (or can ship direcly
|
|||||||
|
|
||||||
## Release Notes
|
## Release Notes
|
||||||
|
|
||||||
[View Version History](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/ReleaseNotes.md)
|
[View Version History](https://github.com/tparnell8/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 (Redis, Elasticsearch or Stdout)
|
3. Outputs (e.g. Redis, Elasticsearch, Stdout, StatsD)
|
||||||
|
|
||||||
### Support ###
|
### Support ###
|
||||||
Please use the TimberWinR Google Group for discussion and support:
|
Please use the TimberWinR Google Group for discussion and support:
|
||||||
@@ -30,27 +38,27 @@ Latest Build:
|
|||||||
|
|
||||||
## Inputs
|
## Inputs
|
||||||
The current supported Input format sources are:
|
The current supported Input format sources are:
|
||||||
1. [Logs](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/Logs.md) (Files, a.k.a Tailing a file)
|
1. [Logs](https://github.com/tparnell8/TimberWinR/blob/master/TimberWinR/mdocs/Logs.md) (Files, a.k.a Tailing a file)
|
||||||
2. [Tcp](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/TcpInput.md) (listens on TCP port for JSON messages)
|
2. [Tcp](https://github.com/tparnell8/TimberWinR/blob/master/TimberWinR/mdocs/TcpInput.md) (listens on TCP port for JSON messages)
|
||||||
3. [IISW3C](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/IISW3CInput.md)(Internet Information Services W3C Format)
|
3. [IISW3C](https://github.com/tparnell8/TimberWinR/blob/master/TimberWinR/mdocs/IISW3CInput.md)(Internet Information Services W3C Format)
|
||||||
4. [WindowsEvents](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/WindowsEvents.md) (Windows Event Viewer)
|
4. [WindowsEvents](https://github.com/tparnell8/TimberWinR/blob/master/TimberWinR/mdocs/WindowsEvents.md) (Windows Event Viewer)
|
||||||
5. [Stdin](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/StdinInput.md) (Standard Input for Debugging)
|
5. [Stdin](https://github.com/tparnell8/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)
|
6. [W3C](https://github.com/tparnell8/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)
|
7. [Udp](https://github.com/tparnell8/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. [TailFiles](https://github.com/tparnell8/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*)
|
8. [Generator](https://github.com/tparnell8/TimberWinR/blob/master/TimberWinR/mdocs/Generator.md) (Generate logs for testing *New*)
|
||||||
|
|
||||||
## Codecs
|
## Codecs
|
||||||
The current list of supported codecs are:
|
The current list of supported codecs are:
|
||||||
1. [Multiline](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/Codec.md)
|
1. [Multiline](https://github.com/tparnell8/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/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/GrokFilter.md)
|
1. [Grok](https://github.com/tparnell8/TimberWinR/blob/master/TimberWinR/mdocs/GrokFilter.md)
|
||||||
2. [Mutate](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/MutateFilter.md)
|
2. [Mutate](https://github.com/tparnell8/TimberWinR/blob/master/TimberWinR/mdocs/MutateFilter.md)
|
||||||
3. [Date](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/DateFilter.md)
|
3. [Date](https://github.com/tparnell8/TimberWinR/blob/master/TimberWinR/mdocs/DateFilter.md)
|
||||||
4. [Json](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/JsonFilter.md)
|
4. [Json](https://github.com/tparnell8/TimberWinR/blob/master/TimberWinR/mdocs/JsonFilter.md)
|
||||||
5. [GeoIP](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/GeoIPFilter.md)
|
5. [GeoIP](https://github.com/tparnell8/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
|
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:
|
type then you must use the array tag instead of the singular tag. i.e:
|
||||||
@@ -104,10 +112,11 @@ Since TimberWinR only ships to Redis and Elasticsearch, the format generated by
|
|||||||
represented as a JSON Property or Array.
|
represented as a JSON Property or Array.
|
||||||
|
|
||||||
## Outputs
|
## Outputs
|
||||||
1. [Redis](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/RedisOutput.md)
|
1. [Redis](https://github.com/tparnell8/TimberWinR/blob/master/TimberWinR/mdocs/RedisOutput.md)
|
||||||
2. [Elasticsearch](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/ElasticsearchOutput.md)
|
2. [Elasticsearch](https://github.com/tparnell8/TimberWinR/blob/master/TimberWinR/mdocs/ElasticsearchOutput.md)
|
||||||
3. [Stdout](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/StdoutOutput.md)
|
3. [Stdout](https://github.com/tparnell8/TimberWinR/blob/master/TimberWinR/mdocs/StdoutOutput.md)
|
||||||
4. [File](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/FileOutput.md)
|
4. [File](https://github.com/tparnell8/TimberWinR/blob/master/TimberWinR/mdocs/FileOutput.md)
|
||||||
|
5. [StatsD](https://github.com/tparnell8/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:
|
||||||
|
|||||||
@@ -131,6 +131,8 @@ 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)
|
||||||
{
|
{
|
||||||
@@ -147,6 +149,8 @@ namespace TimberWinR.ServiceHost
|
|||||||
|
|
||||||
public void Stop()
|
public void Stop()
|
||||||
{
|
{
|
||||||
|
WaitForStartupToComplete();
|
||||||
|
|
||||||
_cancellationTokenSource.Cancel();
|
_cancellationTokenSource.Cancel();
|
||||||
if (_diags != null)
|
if (_diags != null)
|
||||||
_diags.Shutdown();
|
_diags.Shutdown();
|
||||||
@@ -155,14 +159,31 @@ namespace TimberWinR.ServiceHost
|
|||||||
_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()
|
||||||
{
|
{
|
||||||
|
StartingUp = true;
|
||||||
_manager = new TimberWinR.Manager(_args.ConfigFile, _args.LogLevel, _args.LogfileDir, _args.LiveMonitor, _cancellationToken);
|
_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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.3.25.0")]
|
[assembly: AssemblyVersion("1.3.26.0")]
|
||||||
[assembly: AssemblyFileVersion("1.3.25.0")]
|
[assembly: AssemblyFileVersion("1.3.26.0")]
|
||||||
|
|||||||
@@ -38,9 +38,16 @@
|
|||||||
<ApplicationIcon>timberwinr.ico</ApplicationIcon>
|
<ApplicationIcon>timberwinr.ico</ApplicationIcon>
|
||||||
</PropertyGroup>
|
</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" />
|
||||||
|
|||||||
@@ -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="StatsdClient" version="1.0.0.19" targetFramework="net40" />
|
||||||
<package id="Topshelf" version="3.1.4" targetFramework="net40" />
|
<package id="Topshelf" version="3.1.4" targetFramework="net40" />
|
||||||
</packages>
|
</packages>
|
||||||
@@ -27,6 +27,9 @@ namespace TimberWinR.TestGenerator
|
|||||||
[Option("resultsFile", HelpText = "Expected results Results json file")]
|
[Option("resultsFile", HelpText = "Expected results Results json file")]
|
||||||
public string ExpectedResultsFile { get; set; }
|
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)")]
|
[Option('n', "numMessages", DefaultValue = 1000, HelpText = "The number of messages to send to the output(s)")]
|
||||||
public int NumMessages { get; set; }
|
public int NumMessages { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -350,7 +350,7 @@ namespace TimberWinR.TestGenerator
|
|||||||
var mbc = outputToken["queuedMessageCount"].Value<int>();
|
var mbc = outputToken["queuedMessageCount"].Value<int>();
|
||||||
var smc = outputToken["sentMessageCount"].Value<int>();
|
var smc = outputToken["sentMessageCount"].Value<int>();
|
||||||
|
|
||||||
// LogManager.GetCurrentClassLogger().Info("Queued: {0}, Sent: {1}", mbc, smc);
|
//LogManager.GetCurrentClassLogger().Info("Output: {2} Queued: {0}, Sent: {1}", mbc, smc, outputToken.ToString());
|
||||||
|
|
||||||
completed = mbc == 0 && smc >= _totalMessagesToSend;
|
completed = mbc == 0 && smc >= _totalMessagesToSend;
|
||||||
}
|
}
|
||||||
@@ -504,6 +504,8 @@ namespace TimberWinR.TestGenerator
|
|||||||
|
|
||||||
static Task[] RunGenerators(CommandLineOptions options)
|
static Task[] RunGenerators(CommandLineOptions options)
|
||||||
{
|
{
|
||||||
|
_totalMessagesToSend = options.TotalMessages;
|
||||||
|
|
||||||
_monitorTask = Task.Factory.StartNew(() =>
|
_monitorTask = Task.Factory.StartNew(() =>
|
||||||
{
|
{
|
||||||
using (var syncHandle = new ManualResetEventSlim())
|
using (var syncHandle = new ManualResetEventSlim())
|
||||||
|
|||||||
@@ -126,6 +126,18 @@
|
|||||||
<Content Include="results5.json">
|
<Content Include="results5.json">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</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>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\TimberWinR\TimberWinR.csproj">
|
<ProjectReference Include="..\TimberWinR\TimberWinR.csproj">
|
||||||
|
|||||||
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
@@ -1,11 +1,12 @@
|
|||||||
{
|
{
|
||||||
"test": "Test 2",
|
"test": "Test 2",
|
||||||
"arguments": {
|
"arguments": {
|
||||||
|
"--start": "",
|
||||||
"--testFile": "test2.json",
|
"--testFile": "test2.json",
|
||||||
"--testDir": "test2",
|
"--testDir": "test2",
|
||||||
"--timberWinRConfig": "test2-tw.json",
|
"--timberWinRConfig": "test2-tw.json",
|
||||||
"--numMessages": 1234,
|
"--numMessages": 1234,
|
||||||
"--logLevel": "trace",
|
"--logLevel": "debug",
|
||||||
"--udp": "5140",
|
"--udp": "5140",
|
||||||
"--jroll": ["r1.jlog", "r2.jlog"],
|
"--jroll": ["r1.jlog", "r2.jlog"],
|
||||||
"--json": ["1.jlog", "2.jlog", "3.jlog", "4.jlog"],
|
"--json": ["1.jlog", "2.jlog", "3.jlog", "4.jlog"],
|
||||||
|
|||||||
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace TimberWinR.UnitTests.Parser
|
using TimberWinR.Outputs;
|
||||||
|
|
||||||
|
namespace TimberWinR.UnitTests.Parser
|
||||||
{
|
{
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
@@ -51,5 +53,51 @@
|
|||||||
|
|
||||||
Assert.AreEqual("someindex-" + DateTime.UtcNow.ToString("yyyy.MM.dd"), result);
|
Assert.AreEqual("someindex-" + DateTime.UtcNow.ToString("yyyy.MM.dd"), result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Given_no_ssl_then_validate_does_not_throw()
|
||||||
|
{
|
||||||
|
parser.Ssl = false;
|
||||||
|
Assert.That(() => parser.Validate(), Throws.Nothing);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Given_ssl_and_no_username_then_validate_throws()
|
||||||
|
{
|
||||||
|
parser.Ssl = true;
|
||||||
|
parser.Password = "pass";
|
||||||
|
|
||||||
|
Assert.That(() => parser.Validate(), Throws.Exception.InstanceOf<ElasticsearchOutputParameters.ElasticsearchBasicAuthException>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Given_ssl_and_no_password_then_validate_throws()
|
||||||
|
{
|
||||||
|
parser.Ssl = true;
|
||||||
|
parser.Username = "user";
|
||||||
|
|
||||||
|
Assert.That(() => parser.Validate(), Throws.Exception.InstanceOf<ElasticsearchOutputParameters.ElasticsearchBasicAuthException>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Given_ssl_and_username_and_password_then_validate_does_not_throw()
|
||||||
|
{
|
||||||
|
parser.Ssl = true;
|
||||||
|
parser.Username = "user";
|
||||||
|
parser.Password = "pass";
|
||||||
|
|
||||||
|
Assert.That(() => parser.Validate(), Throws.Nothing);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[TestCase("host", 1234, false, null, null, "http://host:1234/")]
|
||||||
|
[TestCase("host", 1234, true, "user", "pass", "https://user:pass@host:1234/")]
|
||||||
|
[TestCase("host", 1234, true, "user:", "pass@", "https://user%3A:pass%40@host:1234/")]
|
||||||
|
public void ComposeUri_Matches_Expected(string host, int port, bool ssl, string username, string password, string expectedUri)
|
||||||
|
{
|
||||||
|
var uri = ElasticsearchOutput.ComposeUri(host, port, ssl, username, password);
|
||||||
|
|
||||||
|
Assert.That(uri.ToString(), Is.EqualTo(expectedUri));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,12 @@ namespace TimberWinR
|
|||||||
get { return _events; }
|
get { return _events; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<StatsDOutputParameters> _statsdOutputs = new List<StatsDOutputParameters>();
|
||||||
|
public IEnumerable<StatsDOutputParameters> StatsDOutputs
|
||||||
|
{
|
||||||
|
get { return _statsdOutputs; }
|
||||||
|
}
|
||||||
|
|
||||||
private List<RedisOutputParameters> _redisOutputs = new List<RedisOutputParameters>();
|
private List<RedisOutputParameters> _redisOutputs = new List<RedisOutputParameters>();
|
||||||
public IEnumerable<RedisOutputParameters> RedisOutputs
|
public IEnumerable<RedisOutputParameters> RedisOutputs
|
||||||
{
|
{
|
||||||
@@ -265,6 +271,8 @@ namespace TimberWinR
|
|||||||
|
|
||||||
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.AddRange(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)
|
||||||
@@ -307,6 +315,7 @@ namespace TimberWinR
|
|||||||
_events = new List<WindowsEvent>();
|
_events = new List<WindowsEvent>();
|
||||||
_iisw3clogs = new List<IISW3CLogParameters>();
|
_iisw3clogs = new List<IISW3CLogParameters>();
|
||||||
_logs = new List<LogParameters>();
|
_logs = new List<LogParameters>();
|
||||||
|
_statsdOutputs = new List<StatsDOutputParameters>();
|
||||||
_redisOutputs = new List<RedisOutputParameters>();
|
_redisOutputs = new List<RedisOutputParameters>();
|
||||||
_elasticsearchOutputs = new List<ElasticsearchOutputParameters>();
|
_elasticsearchOutputs = new List<ElasticsearchOutputParameters>();
|
||||||
_stdoutOutputs = new List<StdoutOutputParameters>();
|
_stdoutOutputs = new List<StdoutOutputParameters>();
|
||||||
|
|||||||
@@ -165,6 +165,15 @@ namespace TimberWinR
|
|||||||
if (OnConfigurationProcessed != null)
|
if (OnConfigurationProcessed != null)
|
||||||
OnConfigurationProcessed(config);
|
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)
|
if (config.RedisOutputs != null)
|
||||||
{
|
{
|
||||||
foreach (var ro in config.RedisOutputs)
|
foreach (var ro in config.RedisOutputs)
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ namespace TimberWinR.Outputs
|
|||||||
{
|
{
|
||||||
private TimberWinR.Manager _manager;
|
private TimberWinR.Manager _manager;
|
||||||
private readonly int _port;
|
private readonly int _port;
|
||||||
|
private readonly bool _ssl;
|
||||||
|
private readonly string _username;
|
||||||
|
private readonly string _password;
|
||||||
private readonly int _interval;
|
private readonly int _interval;
|
||||||
private readonly int _flushSize;
|
private readonly int _flushSize;
|
||||||
private readonly int _idleFlushTimeSeconds;
|
private readonly int _idleFlushTimeSeconds;
|
||||||
@@ -57,8 +60,8 @@ namespace TimberWinR.Outputs
|
|||||||
var nodes = new List<Uri>();
|
var nodes = new List<Uri>();
|
||||||
foreach (var host in _hosts)
|
foreach (var host in _hosts)
|
||||||
{
|
{
|
||||||
var url = string.Format("http://{0}:{1}", host, _port);
|
var uri = ComposeUri(host, _port, _ssl, _username, _password);
|
||||||
nodes.Add(new Uri(url));
|
nodes.Add(uri);
|
||||||
}
|
}
|
||||||
var pool = new StaticConnectionPool(nodes.ToArray());
|
var pool = new StaticConnectionPool(nodes.ToArray());
|
||||||
var settings = new ConnectionSettings(pool)
|
var settings = new ConnectionSettings(pool)
|
||||||
@@ -73,6 +76,13 @@ namespace TimberWinR.Outputs
|
|||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Uri ComposeUri(string host, int port, bool ssl, string username, string password)
|
||||||
|
{
|
||||||
|
return ssl
|
||||||
|
? new Uri(string.Format("https://{0}:{1}@{2}:{3}", Uri.EscapeDataString(username), Uri.EscapeDataString(password), host, port))
|
||||||
|
: new Uri(string.Format("http://{0}:{1}", host, port));
|
||||||
|
}
|
||||||
|
|
||||||
public ElasticsearchOutput(TimberWinR.Manager manager, Parser.ElasticsearchOutputParameters parameters, CancellationToken cancelToken)
|
public ElasticsearchOutput(TimberWinR.Manager manager, Parser.ElasticsearchOutputParameters parameters, CancellationToken cancelToken)
|
||||||
: base(cancelToken, "Elasticsearch")
|
: base(cancelToken, "Elasticsearch")
|
||||||
{
|
{
|
||||||
@@ -86,6 +96,9 @@ namespace TimberWinR.Outputs
|
|||||||
_timeout = parameters.Timeout;
|
_timeout = parameters.Timeout;
|
||||||
_manager = manager;
|
_manager = manager;
|
||||||
_port = parameters.Port;
|
_port = parameters.Port;
|
||||||
|
_ssl = parameters.Ssl;
|
||||||
|
_username = parameters.Username;
|
||||||
|
_password = parameters.Password;
|
||||||
_interval = parameters.Interval;
|
_interval = parameters.Interval;
|
||||||
_hosts = parameters.Host;
|
_hosts = parameters.Host;
|
||||||
_jsonQueue = new List<JObject>();
|
_jsonQueue = new List<JObject>();
|
||||||
@@ -111,6 +124,9 @@ namespace TimberWinR.Outputs
|
|||||||
new JProperty("messages", _sentMessages),
|
new JProperty("messages", _sentMessages),
|
||||||
new JProperty("queuedMessageCount", _jsonQueue.Count),
|
new JProperty("queuedMessageCount", _jsonQueue.Count),
|
||||||
new JProperty("port", _port),
|
new JProperty("port", _port),
|
||||||
|
new JProperty("ssl", _ssl),
|
||||||
|
new JProperty("username", _username),
|
||||||
|
new JProperty("password", _password),
|
||||||
new JProperty("flushSize", _flushSize),
|
new JProperty("flushSize", _flushSize),
|
||||||
new JProperty("idleFlushTime", _idleFlushTimeSeconds),
|
new JProperty("idleFlushTime", _idleFlushTimeSeconds),
|
||||||
new JProperty("interval", _interval),
|
new JProperty("interval", _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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,6 +34,7 @@ namespace TimberWinR.Outputs
|
|||||||
JObject json = new JObject(
|
JObject json = new JObject(
|
||||||
new JProperty("stdout",
|
new JProperty("stdout",
|
||||||
new JObject(
|
new JObject(
|
||||||
|
new JProperty("queuedMessageCount", _jsonQueue.Count),
|
||||||
new JProperty("sentMessageCount", _sentMessages))));
|
new JProperty("sentMessageCount", _sentMessages))));
|
||||||
|
|
||||||
return json;
|
return json;
|
||||||
@@ -67,7 +68,7 @@ namespace TimberWinR.Outputs
|
|||||||
foreach (JObject obj in messages)
|
foreach (JObject obj in messages)
|
||||||
{
|
{
|
||||||
Console.WriteLine(obj.ToString());
|
Console.WriteLine(obj.ToString());
|
||||||
_sentMessages++;
|
Interlocked.Increment(ref _sentMessages);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|||||||
@@ -543,8 +543,98 @@ namespace TimberWinR.Parser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ElasticsearchOutputParameters
|
public class StatsDOutputParameters : IValidateSchema
|
||||||
{
|
{
|
||||||
|
public class StatsDGaugeHashException : Exception
|
||||||
|
{
|
||||||
|
public StatsDGaugeHashException()
|
||||||
|
: base("StatsD output 'gauge' must be an array of pairs.")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class StatsDCountHashException : Exception
|
||||||
|
{
|
||||||
|
public StatsDCountHashException()
|
||||||
|
: base("StatsD output 'count' must be an array of pairs.")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[JsonProperty(PropertyName = "type")]
|
||||||
|
public string InputType { get; set; }
|
||||||
|
[JsonProperty(PropertyName = "sender")]
|
||||||
|
public string Sender { get; set; }
|
||||||
|
[JsonProperty(PropertyName = "namespace")]
|
||||||
|
public string Namespace { get; set; }
|
||||||
|
[JsonProperty(PropertyName = "host")]
|
||||||
|
public string Host { get; set; }
|
||||||
|
[JsonProperty(PropertyName = "port")]
|
||||||
|
public int Port { get; set; }
|
||||||
|
[JsonProperty(PropertyName = "interval")]
|
||||||
|
public int Interval { get; set; }
|
||||||
|
[JsonProperty(PropertyName = "flush_size")]
|
||||||
|
public int FlushSize { get; set; }
|
||||||
|
[JsonProperty(PropertyName = "idle_flush_time")]
|
||||||
|
public int IdleFlushTimeInSeconds { get; set; }
|
||||||
|
[JsonProperty(PropertyName = "max_queue_size")]
|
||||||
|
public int MaxQueueSize { get; set; }
|
||||||
|
[JsonProperty(PropertyName = "queue_overflow_discard_oldest")]
|
||||||
|
public bool QueueOverflowDiscardOldest { get; set; }
|
||||||
|
[JsonProperty(PropertyName = "threads")]
|
||||||
|
public int NumThreads { get; set; }
|
||||||
|
[JsonProperty(PropertyName = "sample_rate")]
|
||||||
|
public double SampleRate { get; set; }
|
||||||
|
[JsonProperty(PropertyName = "increment")] // Array: metric names
|
||||||
|
public string[] Increments { get; set; }
|
||||||
|
[JsonProperty(PropertyName = "decrement")] // Array: metric names
|
||||||
|
public string[] Decrements { get; set; }
|
||||||
|
[JsonProperty(PropertyName = "gauge")] // Hash: metric_name => gauge
|
||||||
|
public string[] Gauges { get; set; }
|
||||||
|
[JsonProperty(PropertyName = "count")] // Hash: metric_name => count
|
||||||
|
public string[] Counts { get; set; }
|
||||||
|
[JsonProperty(PropertyName = "timing")] // Hash: metric_name => count
|
||||||
|
public string[] Timings { get; set; }
|
||||||
|
|
||||||
|
public StatsDOutputParameters()
|
||||||
|
{
|
||||||
|
SampleRate = 1;
|
||||||
|
Port = 8125;
|
||||||
|
Host = "localhost";
|
||||||
|
Interval = 5000;
|
||||||
|
FlushSize = 5000;
|
||||||
|
IdleFlushTimeInSeconds = 10;
|
||||||
|
QueueOverflowDiscardOldest = true;
|
||||||
|
MaxQueueSize = 50000;
|
||||||
|
NumThreads = 1;
|
||||||
|
Namespace = "timberwinr";
|
||||||
|
Sender = System.Environment.MachineName.ToLower() + "." +
|
||||||
|
Microsoft.Win32.Registry.LocalMachine.OpenSubKey(
|
||||||
|
@"SYSTEM\CurrentControlSet\services\Tcpip\Parameters")
|
||||||
|
.GetValue("Domain", "")
|
||||||
|
.ToString().ToLower();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Validate()
|
||||||
|
{
|
||||||
|
if (Gauges != null && Gauges.Length % 2 != 0)
|
||||||
|
throw new StatsDGaugeHashException();
|
||||||
|
|
||||||
|
if (Counts != null && Counts.Length % 2 != 0)
|
||||||
|
throw new StatsDCountHashException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class ElasticsearchOutputParameters : IValidateSchema
|
||||||
|
{
|
||||||
|
public class ElasticsearchBasicAuthException : Exception
|
||||||
|
{
|
||||||
|
public ElasticsearchBasicAuthException()
|
||||||
|
: base("Elasticsearch 'username' and 'password' properties must be set when SSL is enabled.")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const string IndexDatePattern = "(%\\{(?<format>[^\\}]+)\\})";
|
const string IndexDatePattern = "(%\\{(?<format>[^\\}]+)\\})";
|
||||||
|
|
||||||
[JsonProperty(PropertyName = "host")]
|
[JsonProperty(PropertyName = "host")]
|
||||||
@@ -553,6 +643,12 @@ namespace TimberWinR.Parser
|
|||||||
public string Index { get; set; }
|
public string Index { get; set; }
|
||||||
[JsonProperty(PropertyName = "port")]
|
[JsonProperty(PropertyName = "port")]
|
||||||
public int Port { get; set; }
|
public int Port { get; set; }
|
||||||
|
[JsonProperty(PropertyName = "ssl")]
|
||||||
|
public bool Ssl { get; set; }
|
||||||
|
[JsonProperty(PropertyName = "username")]
|
||||||
|
public string Username { get; set; }
|
||||||
|
[JsonProperty(PropertyName = "password")]
|
||||||
|
public string Password { get; set; }
|
||||||
[JsonProperty(PropertyName = "timeout")]
|
[JsonProperty(PropertyName = "timeout")]
|
||||||
public int Timeout { get; set; }
|
public int Timeout { get; set; }
|
||||||
[JsonProperty(PropertyName = "threads")]
|
[JsonProperty(PropertyName = "threads")]
|
||||||
@@ -580,6 +676,9 @@ namespace TimberWinR.Parser
|
|||||||
IdleFlushTimeInSeconds = 10;
|
IdleFlushTimeInSeconds = 10;
|
||||||
Protocol = "http";
|
Protocol = "http";
|
||||||
Port = 9200;
|
Port = 9200;
|
||||||
|
Ssl = false;
|
||||||
|
Username = string.Empty;
|
||||||
|
Password = string.Empty;
|
||||||
Index = "";
|
Index = "";
|
||||||
Host = new string[] { "localhost" };
|
Host = new string[] { "localhost" };
|
||||||
Timeout = 10000;
|
Timeout = 10000;
|
||||||
@@ -629,6 +728,11 @@ namespace TimberWinR.Parser
|
|||||||
return typeName;
|
return typeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Validate()
|
||||||
|
{
|
||||||
|
if (Ssl && (string.IsNullOrWhiteSpace(Username) || string.IsNullOrWhiteSpace(Password)))
|
||||||
|
throw new ElasticsearchBasicAuthException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RedisOutputParameters
|
public class RedisOutputParameters
|
||||||
@@ -668,6 +772,8 @@ namespace TimberWinR.Parser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class StdoutOutputParameters
|
public class StdoutOutputParameters
|
||||||
{
|
{
|
||||||
[JsonProperty(PropertyName = "interval")]
|
[JsonProperty(PropertyName = "interval")]
|
||||||
@@ -729,6 +835,9 @@ namespace TimberWinR.Parser
|
|||||||
|
|
||||||
[JsonProperty("File")]
|
[JsonProperty("File")]
|
||||||
public FileOutputParameters[] File { get; set; }
|
public FileOutputParameters[] File { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("StatsD")]
|
||||||
|
public StatsDOutputParameters[] StatsD { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class InputSources
|
public class InputSources
|
||||||
|
|||||||
@@ -3,6 +3,11 @@
|
|||||||
A Native Windows to Redis/Elasticsearch Logstash Agent which runs as a service.
|
A Native Windows to Redis/Elasticsearch Logstash Agent which runs as a service.
|
||||||
|
|
||||||
Version / Date
|
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.3.25.0 - 2015-04-30
|
||||||
1. Fixed Issue [#49](https://github.com/Cimpress-MCP/TimberWinR/issues/49)
|
1. Fixed Issue [#49](https://github.com/Cimpress-MCP/TimberWinR/issues/49)
|
||||||
2. Fixed potential non-thread safe when renaming properties
|
2. Fixed potential non-thread safe when renaming properties
|
||||||
|
|||||||
@@ -60,13 +60,21 @@
|
|||||||
<Reference Include="NLog">
|
<Reference Include="NLog">
|
||||||
<HintPath>..\packages\NLog.3.2.0.0\lib\net40\NLog.dll</HintPath>
|
<HintPath>..\packages\NLog.3.2.0.0\lib\net40\NLog.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="RapidRegex.Core">
|
<Reference Include="RapidRegex.Core, Version=1.0.0.4, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\RapidRegex.Core.1.0.0.2\lib\net40\RapidRegex.Core.dll</HintPath>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\packages\RapidRegex.Core.1.0.0.4\lib\net40\RapidRegex.Core.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="RestSharp">
|
<Reference Include="RestSharp">
|
||||||
<HintPath>..\packages\RestSharp.105.0.0\lib\net4\RestSharp.dll</HintPath>
|
<HintPath>..\packages\RestSharp.105.0.0\lib\net4\RestSharp.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="StatsdClient">
|
||||||
|
<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 Include="System" />
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.configuration" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Linq.Dynamic">
|
<Reference Include="System.Linq.Dynamic">
|
||||||
<HintPath>..\packages\System.Linq.Dynamic.1.0.4\lib\net40\System.Linq.Dynamic.dll</HintPath>
|
<HintPath>..\packages\System.Linq.Dynamic.1.0.4\lib\net40\System.Linq.Dynamic.dll</HintPath>
|
||||||
@@ -114,6 +122,7 @@
|
|||||||
<Compile Include="Manager.cs" />
|
<Compile Include="Manager.cs" />
|
||||||
<Compile Include="Outputs\Elasticsearch.cs" />
|
<Compile Include="Outputs\Elasticsearch.cs" />
|
||||||
<Compile Include="Outputs\OutputSender.cs" />
|
<Compile Include="Outputs\OutputSender.cs" />
|
||||||
|
<Compile Include="Outputs\StatsD.cs" />
|
||||||
<Compile Include="Outputs\Redis.cs" />
|
<Compile Include="Outputs\Redis.cs" />
|
||||||
<Compile Include="Outputs\File.cs" />
|
<Compile Include="Outputs\File.cs" />
|
||||||
<Compile Include="Outputs\Stdout.cs" />
|
<Compile Include="Outputs\Stdout.cs" />
|
||||||
@@ -139,6 +148,7 @@
|
|||||||
</Content>
|
</Content>
|
||||||
<None Include="mdocs\Codec.md" />
|
<None Include="mdocs\Codec.md" />
|
||||||
<None Include="mdocs\DateFilter.md" />
|
<None Include="mdocs\DateFilter.md" />
|
||||||
|
<None Include="mdocs\StatsD.md" />
|
||||||
<None Include="mdocs\Filters.md" />
|
<None Include="mdocs\Filters.md" />
|
||||||
<None Include="mdocs\GeoIPFilter.md" />
|
<None Include="mdocs\GeoIPFilter.md" />
|
||||||
<None Include="mdocs\Generator.md" />
|
<None Include="mdocs\Generator.md" />
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
The Elasticsearch output passes on data directly to Elasticsearch.
|
The Elasticsearch output passes on data directly to Elasticsearch.
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
The following parameters are allowed when configuring the Redis output.
|
The following parameters are allowed when configuring the Elasticsearch output.
|
||||||
|
|
||||||
| Parameter | Type | Description | Details | Default |
|
| Parameter | Type | Description | Details | Default |
|
||||||
| :-------------|:---------|:------------------------------------------------------------| :--------------------------- | :-- |
|
| :-------------|:---------|:------------------------------------------------------------| :--------------------------- | :-- |
|
||||||
@@ -14,6 +14,9 @@ The following parameters are allowed when configuring the Redis output.
|
|||||||
| *interval* | integer | Interval in milliseconds to sleep during batch sends | Interval | 5000 |
|
| *interval* | integer | Interval in milliseconds to sleep during batch sends | Interval | 5000 |
|
||||||
| *max_queue_size* | integer | Maximum Elasticsearch queue depth | | 50000 |
|
| *max_queue_size* | integer | Maximum Elasticsearch queue depth | | 50000 |
|
||||||
| *port* | integer | Elasticsearch port number | This port must be open | 9200 |
|
| *port* | integer | Elasticsearch port number | This port must be open | 9200 |
|
||||||
|
| *ssl* | bool | If true, use an HTTPS connection to Elasticsearch. See [this page] (https://www.elastic.co/guide/en/found/current/elk-and-found.html#_using_logstash) for a configuration example. | *username* and *password* are also required for HTTPS connections. | false |
|
||||||
|
| *username* | string | Username for Elasticsearch credentials. | Required for HTTPS connection. | |
|
||||||
|
| *password* | string | Password for Elasticsearch credentials. | Required for HTTPS connection. | |
|
||||||
| *queue_overflow_discard_oldest* | bool | If true, discard oldest messages when max_queue_size reached otherwise discard newest | | true |
|
| *queue_overflow_discard_oldest* | bool | If true, discard oldest messages when max_queue_size reached otherwise discard newest | | true |
|
||||||
| *threads* | [string] | Number of Threads | Number of worker threads processing messages | 1 |
|
| *threads* | [string] | Number of Threads | Number of worker threads processing messages | 1 |
|
||||||
| *enable_ping* | bool | If true, pings the server to test for keep alive | | false |
|
| *enable_ping* | bool | If true, pings the server to test for keep alive | | false |
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ The following operations are allowed when mutating a field.
|
|||||||
| *remove_field* | property:array |If the filter is successful, remove arbitrary fields from this event. Field names can be dynamic and include parts of the event using the %{field} syntax.
|
| *remove_field* | property:array |If the filter is successful, remove arbitrary fields from this event. Field names can be dynamic and include parts of the event using the %{field} syntax.
|
||||||
| *remove_tag* | property:array |If the filter is successful, remove arbitrary tags from this event. Field names can be dynamic and include parts of the event using the %{field} syntax.
|
| *remove_tag* | property:array |If the filter is successful, remove arbitrary tags from this event. Field names can be dynamic and include parts of the event using the %{field} syntax.
|
||||||
| *rename* | property:array |Rename one or more fields
|
| *rename* | property:array |Rename one or more fields
|
||||||
| *type* | property:string |Type to which this filter applyes, if empty, applies to all types.
|
| *type* | property:string |Type to which this filter applies, if empty, applies to all types.
|
||||||
|
|
||||||
## Operation Details
|
## Operation Details
|
||||||
### match
|
### match
|
||||||
|
|||||||
94
TimberWinR/mdocs/StatsD.md
Normal file
94
TimberWinR/mdocs/StatsD.md
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
# Output: StatsD
|
||||||
|
|
||||||
|
The StatsD output passes on data directly to StatsD. (https://github.com/etsy/statsd)
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
The following parameters are allowed when configuring the StatsD output.
|
||||||
|
|
||||||
|
| Parameter | Type | Description | Details | Default |
|
||||||
|
| :-------------|:---------|:------------------------------------------------------------| :--------------------------- | :-- |
|
||||||
|
| *count* | string | Array of (metric_name, gauge name) pairs counted | Must come in pairs | |
|
||||||
|
| *decrement* | string | Array of metrics to be decremented | | |
|
||||||
|
| *flush_size* | integer | Maximum number of messages before flushing | | 50000 |
|
||||||
|
| *gauge* | string | Array of (metric_name, gauge name) pairs gauged | Must come in pairs | |
|
||||||
|
| *host* | string | Hostname or IP of StatsD server | localhost | |
|
||||||
|
| *idle_flush_time* | integer | Maximum number of seconds elapsed before triggering a flush | | 10 |
|
||||||
|
| *increment* | string | Array of metrics to be incremented | | |
|
||||||
|
| *interval* | integer | Interval in milliseconds to sleep between sends | Interval | 5000 |
|
||||||
|
| *max_queue_size* | integer | Maximum StatsD queue depth | | 50000 |
|
||||||
|
| *namespace* | string | Namespace for stats | timberwinr | |
|
||||||
|
| *port* | integer | StatsD port number | This port must be open | 8125 |
|
||||||
|
| *queue_overflow_discard_oldest* | bool | If true, discard oldest messages when max_queue_size reached otherwise discard newest | | true |
|
||||||
|
| *sample_rate* | integer | StatsD sample rate | | 1 |
|
||||||
|
| *sender* | string | Sender name | FQDN | |
|
||||||
|
| *threads* | string | Number of Threads processing messages | | 1 |
|
||||||
|
| *timing* | string | Array of (metric_name, timing_name) pairs timed | Must come in pairs | |
|
||||||
|
| *type* | string |Type to which this filter applies, if empty, applies to all types.
|
||||||
|
|
||||||
|
### Example Usage
|
||||||
|
Example Input: Tail an apache log file, and record counts for bytes and increments for response codes.
|
||||||
|
|
||||||
|
sample-apache.log (snip)
|
||||||
|
```
|
||||||
|
180.76.5.25 - - [13/May/2015:17:02:26 -0700] "GET /frameset.htm HTTP/1.1" 404 89 "-" "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)" "www.redlug.com"
|
||||||
|
208.115.113.94 - - [13/May/2015:17:03:55 -0700] "GET /robots.txt HTTP/1.1" 200 37 "-" "Mozilla/5.0 (compatible; DotBot/1.1; http://www.opensiteexplorer.org/dotbot, help@moz.com)" "redlug.com"
|
||||||
|
208.115.113.94 - - [13/May/2015:17:03:55 -0700] "GET /robots.txt HTTP/1.1" 200 37 "-" "Mozilla/5.0 (compatible; DotBot/1.1; http://www.opensiteexplorer.org/dotbot, help@moz.com)" "www.redlug.com"
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: [COMBINEDAPACHELOG](https://github.com/elastic/logstash/blob/v1.4.2/patterns/grok-patterns) is a standard
|
||||||
|
Grok Pattern.
|
||||||
|
|
||||||
|
TimberWinR configuration
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"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",
|
||||||
|
"port": 8125,
|
||||||
|
"host": "stats.mycompany.svc",
|
||||||
|
"increment": ["apache.response.%{response}"],
|
||||||
|
"count": ["apache.bytes", "%{bytes}"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Assuming your FQDN is something like mymachine.mycompany.com, you should see the following in Graphite:
|
||||||
|
|
||||||
|
```
|
||||||
|
stats.counters.timberwinr.mymachine.mycompany.com.apache.bytes.count
|
||||||
|
stats.counters.timberwinr.mymachine.mycompany.com.apache.bytes.rate
|
||||||
|
stats.counters.timberwinr.mymachine.mycompany.com.apache.response.200.count
|
||||||
|
stats.counters.timberwinr.mymachine.mycompany.com.apache.response.200.rate
|
||||||
|
stats.counters.timberwinr.mymachine.mycompany.com.apache.response.404.count
|
||||||
|
stats.counters.timberwinr.mymachine.mycompany.com.apache.response.404.rate
|
||||||
|
...
|
||||||
|
...
|
||||||
|
```
|
||||||
@@ -65,3 +65,5 @@ After a successful parse of an event, the following fields are added:
|
|||||||
| Message | STRING | The full event message |
|
| Message | STRING | The full event message |
|
||||||
| Data | STRING | The binary data associated with the event |
|
| Data | STRING | The binary data associated with the event |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,9 @@
|
|||||||
<package id="NEST" version="1.3.1" targetFramework="net40" />
|
<package id="NEST" version="1.3.1" targetFramework="net40" />
|
||||||
<package id="Newtonsoft.Json" version="6.0.5" targetFramework="net40" />
|
<package id="Newtonsoft.Json" version="6.0.5" targetFramework="net40" />
|
||||||
<package id="NLog" version="3.2.0.0" targetFramework="net40" />
|
<package id="NLog" version="3.2.0.0" targetFramework="net40" />
|
||||||
<package id="RapidRegex.Core" version="1.0.0.2" targetFramework="net40" />
|
<package id="RapidRegex.Core" version="1.0.0.4" targetFramework="net40" />
|
||||||
<package id="RestSharp" version="105.0.0" targetFramework="net40" />
|
<package id="RestSharp" version="105.0.0" targetFramework="net40" />
|
||||||
|
<package id="StatsdClient" version="1.0.0.19" targetFramework="net40" />
|
||||||
<package id="System.Linq.Dynamic" version="1.0.4" targetFramework="net40" />
|
<package id="System.Linq.Dynamic" version="1.0.4" targetFramework="net40" />
|
||||||
<package id="Topshelf" version="3.1.4" targetFramework="net40" />
|
<package id="Topshelf" version="3.1.4" targetFramework="net40" />
|
||||||
</packages>
|
</packages>
|
||||||
@@ -54,6 +54,8 @@
|
|||||||
<File Id="default.json" Source="$(var.TimberWinR.ServiceHost.TargetDir)\default.json" />
|
<File Id="default.json" Source="$(var.TimberWinR.ServiceHost.TargetDir)\default.json" />
|
||||||
<File Id="Newtonsoft.Json.dll" Source="$(var.TimberWinR.ServiceHost.TargetDir)\Newtonsoft.Json.dll" />
|
<File Id="Newtonsoft.Json.dll" Source="$(var.TimberWinR.ServiceHost.TargetDir)\Newtonsoft.Json.dll" />
|
||||||
<File Id="Nlog.dll" Source="$(var.TimberWinR.ServiceHost.TargetDir)\Nlog.dll" />
|
<File Id="Nlog.dll" Source="$(var.TimberWinR.ServiceHost.TargetDir)\Nlog.dll" />
|
||||||
|
<File Id="StatsdClient.dll" Source="$(var.TimberWinR.ServiceHost.TargetDir)\StatsdClient.dll" />
|
||||||
|
<File Id="StatsdClient.Configuration.dll" Source="$(var.TimberWinR.ServiceHost.TargetDir)\StatsdClient.Configuration.dll" />
|
||||||
<File Id="RestSharp.dll" Source="$(var.TimberWinR.ServiceHost.TargetDir)\RestSharp.dll" />
|
<File Id="RestSharp.dll" Source="$(var.TimberWinR.ServiceHost.TargetDir)\RestSharp.dll" />
|
||||||
<File Id="RapidRegex.Core.dll" Source="$(var.TimberWinR.ServiceHost.TargetDir)\RapidRegex.Core.dll" />
|
<File Id="RapidRegex.Core.dll" Source="$(var.TimberWinR.ServiceHost.TargetDir)\RapidRegex.Core.dll" />
|
||||||
<File Id="TimberWinR.dll" Source="$(var.TimberWinR.ServiceHost.TargetDir)\TimberWinR.dll" />
|
<File Id="TimberWinR.dll" Source="$(var.TimberWinR.ServiceHost.TargetDir)\TimberWinR.dll" />
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ $packageName = 'TimberWinR-${version}' # arbitrary name for the package, used in
|
|||||||
$installerType = 'msi' #only one of these: exe, msi, msu
|
$installerType = 'msi' #only one of these: exe, msi, msu
|
||||||
$scriptPath = $(Split-Path $MyInvocation.MyCommand.Path)
|
$scriptPath = $(Split-Path $MyInvocation.MyCommand.Path)
|
||||||
$fileFullPath = Join-Path $scriptPath 'TimberWinR-${version}.0.msi'
|
$fileFullPath = Join-Path $scriptPath 'TimberWinR-${version}.0.msi'
|
||||||
$silentArgs = '{CC4DF908-07C4-4BD8-A9FA-6E6AC315E30B} /quiet'
|
$silentArgs = '{E001D138-669B-4604-88C5-02C756461C15} /quiet'
|
||||||
$validExitCodes = @(0) #please insert other valid exit codes here, exit codes for ms http://msdn.microsoft.com/en-us/library/aa368542(VS.85).aspx
|
$validExitCodes = @(0) #please insert other valid exit codes here, exit codes for ms http://msdn.microsoft.com/en-us/library/aa368542(VS.85).aspx
|
||||||
UnInstall-ChocolateyPackage "$packageName" "$installerType" "$silentArgs" "fileFullPath" -validExitCodes $validExitCodes
|
UnInstall-ChocolateyPackage "$packageName" "$installerType" "$silentArgs" "fileFullPath" -validExitCodes $validExitCodes
|
||||||
|
|||||||
BIN
timberwinr.jpg
Normal file
BIN
timberwinr.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.0 KiB |
Reference in New Issue
Block a user