Release 1.3.25.0 candidate

This commit is contained in:
Eric Fontana
2015-04-30 10:09:07 -04:00
parent 6600bb2b85
commit 3669acbb26
25 changed files with 387 additions and 89 deletions

View File

@@ -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.24.0")] [assembly: AssemblyVersion("1.3.25.0")]
[assembly: AssemblyFileVersion("1.3.24.0")] [assembly: AssemblyFileVersion("1.3.25.0")]

View File

@@ -117,6 +117,15 @@
<Content Include="test4.json"> <Content Include="test4.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </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>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\TimberWinR\TimberWinR.csproj"> <ProjectReference Include="..\TimberWinR\TimberWinR.csproj">

View File

@@ -24,7 +24,7 @@ namespace TimberWinR.TestGenerator
NumMessages = 100; NumMessages = 100;
Port = 6379; Port = 6379;
Host = "localhost"; Host = "localhost";
SleepTimeMilliseconds = 10; SleepTimeMilliseconds = 1;
} }
} }
@@ -48,16 +48,23 @@ namespace TimberWinR.TestGenerator
{ {
JObject o = new JObject JObject o = new JObject
{ {
{"Application", "udp-generator"}, {"Application", "udp-generator"},
{"Host", hostName}, {"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"},
{"Host", hostName},
{"UtcTimestamp", DateTime.UtcNow.ToString("o")}, {"UtcTimestamp", DateTime.UtcNow.ToString("o")},
{"Type", "udp"}, {"Type", "VP.Fulfillment.Direct.Initialization.LogWrapper"},
{"Message", "Testgenerator udp message " + DateTime.UtcNow.ToString("o")}, {"Message", "Testgenerator udp message " + DateTime.UtcNow.ToString("o")},
{"Index", "logstash"} {"Index", "logstash"}
}; };
byte[] sendbuf = Encoding.UTF8.GetBytes(o.ToString()); byte[] sendbuf = Encoding.UTF8.GetBytes(o.ToString());
IPEndPoint ep = new IPEndPoint(broadcast, parms.Port); IPEndPoint ep = new IPEndPoint(broadcast, parms.Port);
s.SendTo(sendbuf, ep); s.SendTo(sendbuf, ep);
if (i % 1000 == 0)
LogManager.GetCurrentClassLogger().Info("Sent {0} of {1} messages", i, parms.NumMessages);
Thread.Sleep(parms.SleepTimeMilliseconds); Thread.Sleep(parms.SleepTimeMilliseconds);
} }

View File

@@ -0,0 +1,13 @@
{
"Results": {
"Inputs": [
{
"udp": {
"test1: message sent count": "[messages] == 10000",
"test2: average cpu": "[avgCpuUsage] <= 30",
"test3: maximum memory": "[maxMemUsage] <= 30"
}
}
]
}
}

View File

@@ -39,7 +39,7 @@
"_comment": "Change the host to your Redis instance", "_comment": "Change the host to your Redis instance",
"port": 6379, "port": 6379,
"batch_count": 500, "batch_count": 500,
"threads": 2, "threads": 1,
"host": [ "host": [
"tstlexiceapp006.vistaprint.svc" "tstlexiceapp006.vistaprint.svc"
] ]

View File

@@ -0,0 +1,42 @@
{
"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": {
"Redis": [
{
"_comment": "Change the host to your Redis instance",
"port": 6379,
"batch_count": 500,
"interval": 1000,
"threads": 4,
"host": [
"tstlexiceapp006.vistaprint.svc"
]
}
]
}
}
}

View File

@@ -0,0 +1,15 @@
{
"test": "Test 5",
"arguments": {
"--start": "",
"--testFile": "test5.json",
"--testDir": "test5",
"--timberWinRConfig": "test5-twconfig.json",
"--numMessages": 20000,
"--logLevel": "debug",
"--udp-host": "localhost",
"--udp": "5140",
"--udp-rate": 5,
"--resultsFile": "results5.json"
}
}

View File

@@ -77,5 +77,56 @@ namespace TimberWinR.UnitTests
Assert.IsNull(nostuff); Assert.IsNull(nostuff);
Assert.AreEqual(6, jsonInputLine2.Count); 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());
}
} }
} }

View File

@@ -116,4 +116,7 @@ Global
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
EndGlobal EndGlobal

View File

@@ -30,8 +30,6 @@ namespace TimberWinR.Parser
} }
} }
public partial class Grok : LogstashFilter public partial class Grok : LogstashFilter
{ {
public override JObject ToJson() public override JObject ToJson()
@@ -92,35 +90,37 @@ namespace TimberWinR.Parser
return true; return true;
} }
// Test for any true matching condition(s)
private bool Matches(Newtonsoft.Json.Linq.JObject json) private bool Matches(Newtonsoft.Json.Linq.JObject json)
{ {
string field = Match[0]; for (int i = 0; i < Match.Length; i += 2)
string expr = Match[1];
JToken token = null;
if (json.TryGetValue(field, out token))
{ {
string text = token.ToString(); string field = Match[i];
if (!string.IsNullOrEmpty(text)) string expr = Match[i + 1];
JToken token = null;
if (json.TryGetValue(field, out token))
{ {
var resolver = new RegexGrokResolver(); string text = token.ToString();
var pattern = resolver.ResolveToRegex(expr); if (!string.IsNullOrEmpty(text))
var match = Regex.Match(text, pattern);
if (match.Success)
{ {
var regex = new Regex(pattern); var resolver = new RegexGrokResolver();
var namedCaptures = regex.MatchNamedCaptures(text); var pattern = resolver.ResolveToRegex(expr);
foreach (string fieldName in namedCaptures.Keys) var match = Regex.Match(text, pattern);
if (match.Success)
{ {
AddOrModify(json, fieldName, namedCaptures[fieldName]); var regex = new Regex(pattern);
var namedCaptures = regex.MatchNamedCaptures(text);
foreach (string fieldName in namedCaptures.Keys)
{
AddOrModify(json, fieldName, namedCaptures[fieldName]);
}
return true; // Yes!
} }
return true; // Yes!
} }
if (string.IsNullOrEmpty(expr))
return true; // Empty field is no match
} }
if (string.IsNullOrEmpty(expr))
return true; // Empty field is no match
else
return false;
} }
return false; // Not specified is failure return false; // Not specified is failure
} }
@@ -136,7 +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)
{ {

View File

@@ -92,9 +92,12 @@ namespace TimberWinR.Parser
if (Rename != null && Rename.Length > 0) if (Rename != null && Rename.Length > 0)
{ {
string oldName = ExpandField(Rename[0], json); for (int i = 0; i < Rename.Length; i += 2)
string newName = ExpandField(Rename[1], json); {
RenameProperty(json, oldName, newName); string oldName = ExpandField(Rename[i], json);
string newName = ExpandField(Rename[i+1], json);
RenameProperty(json, oldName, newName);
}
} }
if (RemoveSource) if (RemoveSource)

View File

@@ -101,6 +101,34 @@ 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 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() protected void EnsureRollingCaught()
{ {
@@ -128,6 +156,7 @@ namespace TimberWinR.Inputs
} }
} }
public virtual void AddDefaultFields(JObject json) public virtual void AddDefaultFields(JObject json)
{ {
if (json["type"] == null) if (json["type"] == null)

View File

@@ -16,7 +16,7 @@ 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; private long _errorCount;
@@ -32,15 +32,19 @@ namespace TimberWinR.Inputs
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);
_receivedMessages = 0; 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);
@@ -91,6 +95,33 @@ 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;
@@ -109,6 +140,7 @@ namespace TimberWinR.Inputs
try try
{ {
JObject json = JObject.Load(reader); JObject json = JObject.Load(reader);
ApplyFilters(json);
ProcessJson(json); ProcessJson(json);
Interlocked.Increment(ref _receivedMessages); Interlocked.Increment(ref _receivedMessages);
} }

View File

@@ -20,6 +20,7 @@ namespace TimberWinR.Inputs
private long _parseErrors; private long _parseErrors;
private long _receiveErrors; private long _receiveErrors;
private long _parsedMessages; private long _parsedMessages;
private TimberWinR.Parser.UdpParameters _arguments;
public override JObject ToJson() public override JObject ToJson()
{ {
@@ -35,10 +36,14 @@ namespace TimberWinR.Inputs
return json; return json;
} }
public UdpInputListener(CancellationToken cancelToken, int port = 5140) : base(cancelToken, "Win32-Udp") public UdpInputListener(TimberWinR.Parser.UdpParameters arguments, CancellationToken cancelToken, int port = 5140) : base(cancelToken, "Win32-Udp")
{ {
_port = port; _port = port;
_receivedMessages = 0; _receivedMessages = 0;
_arguments = arguments;
if (!string.IsNullOrEmpty(arguments.Type))
SetTypeName(arguments.Type);
// setup raw data processor // setup raw data processor
_unprocessedRawData = new BlockingCollection<byte[]>(); _unprocessedRawData = new BlockingCollection<byte[]>();
@@ -141,6 +146,32 @@ namespace TimberWinR.Inputs
Finished(); 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) private void ProcessData(byte[] bytes)
{ {
var data = Encoding.UTF8.GetString(bytes, 0, bytes.Length); var data = Encoding.UTF8.GetString(bytes, 0, bytes.Length);
@@ -148,8 +179,8 @@ namespace TimberWinR.Inputs
try try
{ {
var json = JObject.Parse(data); var json = JObject.Parse(data);
ApplyFilters(json);
ProcessJson(json); ProcessJson(json);
_parsedMessages++; _parsedMessages++;
} }
catch (Exception ex) catch (Exception ex)

View File

@@ -232,7 +232,7 @@ namespace TimberWinR
foreach (var tcp in config.Tcps) foreach (var tcp in config.Tcps)
{ {
var elistner = new TcpInputListener(cancelToken, tcp.Port); var elistner = new TcpInputListener(tcp, cancelToken, tcp.Port);
Listeners.Add(elistner); Listeners.Add(elistner);
foreach (var output in Outputs) foreach (var output in Outputs)
output.Connect(elistner); output.Connect(elistner);
@@ -240,7 +240,7 @@ namespace TimberWinR
foreach (var udp in config.Udps) foreach (var udp in config.Udps)
{ {
var elistner = new UdpInputListener(cancelToken, udp.Port); var elistner = new UdpInputListener(udp, cancelToken, udp.Port);
Listeners.Add(elistner); Listeners.Add(elistner);
foreach (var output in Outputs) foreach (var output in Outputs)
output.Connect(elistner); output.Connect(elistner);

View File

@@ -79,9 +79,11 @@ namespace TimberWinR.Outputs
// Sample the queue and adjust the batch count if needed (ramp up slowly) // Sample the queue and adjust the batch count if needed (ramp up slowly)
public int UpdateCurrentBatchCount(int queueSize, int currentBatchCount) public int UpdateCurrentBatchCount(int queueSize, int currentBatchCount)
{ {
if (currentBatchCount < _maxBatchCount && currentBatchCount < queueSize && AverageQueueDepth() > currentBatchCount) var avgQueueDepth = AverageQueueDepth();
if (currentBatchCount < _maxBatchCount && currentBatchCount < queueSize && avgQueueDepth > currentBatchCount)
{ {
currentBatchCount += Math.Max(_maxBatchCount / _batchCount, 1); currentBatchCount += Math.Max(avgQueueDepth / _batchCount, _batchCount / 5);
if (currentBatchCount >= _maxBatchCount && !_warnedReachedMax) if (currentBatchCount >= _maxBatchCount && !_warnedReachedMax)
{ {
LogManager.GetCurrentClassLogger().Warn("Maximum Batch Count of {0} reached.", currentBatchCount); LogManager.GetCurrentClassLogger().Warn("Maximum Batch Count of {0} reached.", currentBatchCount);
@@ -314,6 +316,9 @@ namespace TimberWinR.Outputs
{ {
_batchCounter.SampleQueueDepth(_jsonQueue.Count); _batchCounter.SampleQueueDepth(_jsonQueue.Count);
// Re-compute current batch size // Re-compute current batch size
LogManager.GetCurrentClassLogger().Trace("{0}: Average Queue Depth: {1}, Current Length: {2}", Thread.CurrentThread.ManagedThreadId, _batchCounter.AverageQueueDepth(), _jsonQueue.Count);
_currentBatchCount = _batchCounter.UpdateCurrentBatchCount(_jsonQueue.Count, _currentBatchCount); _currentBatchCount = _batchCounter.UpdateCurrentBatchCount(_jsonQueue.Count, _currentBatchCount);
messages = _jsonQueue.Take(_currentBatchCount).ToArray(); messages = _jsonQueue.Take(_currentBatchCount).ToArray();

View File

@@ -12,6 +12,7 @@ using Microsoft.SqlServer.Server;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using NLog; using NLog;
using NLog.Config;
using TimberWinR.Outputs; using TimberWinR.Outputs;
using System.CodeDom.Compiler; using System.CodeDom.Compiler;
@@ -42,11 +43,9 @@ namespace TimberWinR.Parser
{ {
JToken token = json[oldName]; JToken token = json[oldName];
if (token != null) if (token != null)
{ {
json.Add(newName, token.DeepClone());
json.Remove(oldName); json.Remove(oldName);
JToken newToken = json[newName];
if (newToken == null)
json.Add(newName, token);
} }
} }
@@ -269,7 +268,7 @@ namespace TimberWinR.Parser
public string Type { get; set; } public string Type { get; set; }
[JsonProperty(PropertyName = "codec")] [JsonProperty(PropertyName = "codec")]
public CodecArguments CodecArguments { get; set; } public CodecArguments CodecArguments { get; set; }
[JsonProperty(PropertyName = "message")] [JsonProperty(PropertyName = "message")]
public string Message { get; set; } public string Message { get; set; }
@@ -281,7 +280,7 @@ namespace TimberWinR.Parser
public int Rate { get; set; } public int Rate { get; set; }
public void Validate() public void Validate()
{ {
} }
public GeneratorParameters() public GeneratorParameters()
@@ -336,9 +335,9 @@ namespace TimberWinR.Parser
[JsonProperty(PropertyName = "type")] [JsonProperty(PropertyName = "type")]
public string Type { get; set; } public string Type { get; set; }
[JsonProperty(PropertyName = "location")] [JsonProperty(PropertyName = "location")]
public string Location { get; set; } public string Location { get; set; }
[JsonProperty(PropertyName = "recurse")] [JsonProperty(PropertyName = "recurse")]
public int Recurse { get; set; } public int Recurse { get; set; }
[JsonProperty(PropertyName = "fields")] [JsonProperty(PropertyName = "fields")]
public List<Field> Fields { get; set; } public List<Field> Fields { get; set; }
[JsonProperty(PropertyName = "interval")] [JsonProperty(PropertyName = "interval")]
@@ -403,15 +402,21 @@ namespace TimberWinR.Parser
{ {
[JsonProperty(PropertyName = "port")] [JsonProperty(PropertyName = "port")]
public int Port { get; set; } public int Port { get; set; }
[JsonProperty(PropertyName = "type")]
public string Type { get; set; }
[JsonProperty("add_field")]
public string[] AddFields { get; set; }
[JsonProperty("rename")]
public string[] Renames { get; set; }
public TcpParameters() public TcpParameters()
{ {
Port = 5140; Port = 5140;
Type = "Win32-Tcp";
} }
public void Validate() public void Validate()
{ {
} }
} }
@@ -420,15 +425,21 @@ namespace TimberWinR.Parser
{ {
[JsonProperty(PropertyName = "port")] [JsonProperty(PropertyName = "port")]
public int Port { get; set; } public int Port { get; set; }
[JsonProperty(PropertyName = "type")]
public string Type { get; set; }
[JsonProperty("add_field")]
public string[] AddFields { get; set; }
[JsonProperty("rename")]
public string[] Renames { get; set; }
public UdpParameters() public UdpParameters()
{ {
Port = 5142; Port = 5142;
Type = "Win32-Udp";
} }
public void Validate() public void Validate()
{ {
} }
} }
public class W3CLogParameters : IValidateSchema public class W3CLogParameters : IValidateSchema
@@ -557,9 +568,9 @@ namespace TimberWinR.Parser
[JsonProperty(PropertyName = "max_queue_size")] [JsonProperty(PropertyName = "max_queue_size")]
public int MaxQueueSize { get; set; } public int MaxQueueSize { get; set; }
[JsonProperty(PropertyName = "queue_overflow_discard_oldest")] [JsonProperty(PropertyName = "queue_overflow_discard_oldest")]
public bool QueueOverflowDiscardOldest { get; set; } public bool QueueOverflowDiscardOldest { get; set; }
[JsonProperty(PropertyName = "enable_ping")] [JsonProperty(PropertyName = "enable_ping")]
public bool EnablePing { get; set; } public bool EnablePing { get; set; }
[JsonProperty(PropertyName = "ping_timeout")] [JsonProperty(PropertyName = "ping_timeout")]
public int PingTimeout { get; set; } public int PingTimeout { get; set; }
@@ -650,7 +661,6 @@ namespace TimberWinR.Parser
Host = new string[] { "localhost" }; Host = new string[] { "localhost" };
Timeout = 10000; Timeout = 10000;
BatchCount = 200; BatchCount = 200;
MaxBatchCount = BatchCount*10;
NumThreads = 1; NumThreads = 1;
Interval = 5000; Interval = 5000;
QueueOverflowDiscardOldest = true; QueueOverflowDiscardOldest = true;
@@ -755,7 +765,7 @@ namespace TimberWinR.Parser
public override void Validate() public override void Validate()
{ {
if (Match == null || Match.Length != 2) if (Match == null || Match.Length % 2 != 0)
throw new GrokFilterException(); throw new GrokFilterException();
if (AddTag != null && AddTag.Length % 2 != 0) if (AddTag != null && AddTag.Length % 2 != 0)
@@ -896,7 +906,7 @@ namespace TimberWinR.Parser
} }
} }
public partial class Json : LogstashFilter public partial class Json : LogstashFilter
{ {
public class JsonMissingSourceException : Exception public class JsonMissingSourceException : Exception
@@ -980,7 +990,7 @@ namespace TimberWinR.Parser
[JsonProperty("grokFilters")] [JsonProperty("grokFilters")]
public Grok[] Groks { get; set; } public Grok[] Groks { get; set; }
[JsonProperty("mutateFilters")] [JsonProperty("mutateFilters")]
public Mutate[] Mutates { get; set; } public Mutate[] Mutates { get; set; }
@@ -991,7 +1001,7 @@ namespace TimberWinR.Parser
public Json[] Jsons { get; set; } public Json[] Jsons { get; set; }
[JsonProperty("geoipFilters")] [JsonProperty("geoipFilters")]
public GeoIP[] GeoIPs { get; set; } public GeoIP[] GeoIPs { get; set; }
} }
public class TimberWinR public class TimberWinR

View File

@@ -3,6 +3,12 @@
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.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 previousy only renaming the first one)
### 1.3.24.0 - 2015-04-29 ### 1.3.24.0 - 2015-04-29
1. Fixed potential bug in TailFiles when tailing log files which are partially flushed 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. to disk, it now will not process the line until the \r\n has been seen.

View File

@@ -26,14 +26,14 @@ The following operations are allowed when mutating a field.
| Operation | Type | Description | Operation | Type | Description
| :---------------|:----------------|:-----------------------------------------------------------------------| | :---------------|:----------------|:-----------------------------------------------------------------------|
| *type* | property:string |Type to which this filter applyes, if empty, applies to all types.
| *condition* | property:string |C# expression
| *rename* | property:array |Rename one or more fields
| *match* | property:string |Required field must match before any subsequent grok operations are executed.
| *add_field* | property:array |If the filter is successful, add an arbitrary field to this event. Field names can be dynamic and include parts of the event using the %{field} syntax. This property must be specified in pairs. | *add_field* | property:array |If the filter is successful, add an arbitrary field to this event. Field names can be dynamic and include parts of the event using the %{field} syntax. This property must be specified in pairs.
| *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.
| *add_tag* | property:array |If the filter is successful, add an arbitrary tag to this event. Tag names can be dynamic and include parts of the event using the %{field} syntax. | *add_tag* | property:array |If the filter is successful, add an arbitrary tag to this event. Tag names can be dynamic and include parts of the event using the %{field} syntax.
| *condition* | property:string |C# expression
| *match* | property:array |Required field must match (any) before any subsequent grok operations are executed.
| *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
| *type* | property:string |Type to which this filter applyes, if empty, applies to all types.
## Operation Details ## Operation Details
### match ### match
@@ -67,6 +67,28 @@ Given this configuration
} }
] ]
``` ```
Given this configuration
```json
"Filters": [
{
"grok": {
"matches": [
"message",
"%{IP:client} %{WORD:method} %{URIPATHPARAM:request} %{NUMBER:bytes} %{NUMBER:duration}"
],
"add_tag": [
"http_log"
],
"add_field": [
"verb", "%{method}"
]
}
}
]
```
And if the message matches, then 6 fields would be added to the event: And if the message matches, then 6 fields would be added to the event:
1. client=55.3.244.1 1. client=55.3.244.1
2. method=GET 2. method=GET

View File

@@ -7,25 +7,27 @@ The following operations are allowed when mutating a field.
| Operation | Type | Description | Operation | Type | Description
| :---------------|:----------------|:-----------------------------------------------------------------------| | :---------------|:----------------|:-----------------------------------------------------------------------|
| *type* | property:string |Type to which this filter applies, if empty, applies to all types.
| *condition* | property:string |C# expression, if the expression is true, continue, otherwise, ignore
| *remove_source* | property:bool |If true, the source property is removed, default: true
| *source* | property:string |Required field indicates which field contains the Json to be parsed
| *promote* | property:string |If supplied any properties named *promote* will be promoted to top-level
| *target* | property:string |If suppled, the parsed json will be contained underneath a propery named *target*
| *add_field* | property:array |If the filter is successful, add an arbitrary field to this event. Field names can be dynamic and include parts of the event using the %{field} syntax. This property must be specified in pairs. | *add_field* | property:array |If the filter is successful, add an arbitrary field to this event. Field names can be dynamic and include parts of the event using the %{field} syntax. This property must be specified in pairs.
| *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.
| *add_tag* | property:array |If the filter is successful, add an arbitrary tag to this event. Tag names can be dynamic and include parts of the event using the %{field} syntax. | *add_tag* | property:array |If the filter is successful, add an arbitrary tag to this event. Tag names can be dynamic and include parts of the event using the %{field} syntax.
| *condition* | property:string |C# expression, if the expression is true, continue, otherwise, ignore
| *promote* | property:string |If supplied any properties named *promote* will be promoted to top-level
| *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.
| *remove_source* | property:bool |If true, the source property is removed, default: true
| *rename* | property:array |Rename one or more fields
| *source* | property:string |Required field indicates which field contains the Json to be parsed
| *target* | property:string |If suppled, the parsed json will be contained underneath a propery named *target*
| *type* | property:string |Type to which this filter applies, if empty, applies to all types.
## Operation Details ## Operation Details
### source ### source
The match field is required, the first argument is the field to inspect, and compare to the expression specified by the second The source field is required, and indicates what Field contains the target Json, In the
argument. In the below example, the message is spected to be something like this from a fictional sample log: below example, the [Logs](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/Logs.md) input produces a Text field,
which contains a line to be parsed as Json.
Given this input configuration: Given this input configuration:
Lets assume that a newline such as the following is appended to foo.jlog: Lets assume that a new line such as the following is appended to foo.jlog:, this would end up being the Text field
``` ```
{"Email":"james@example.com","Active":true,"CreatedDate":"2013-01-20T00:00:00Z","Roles":["User","Admin"]} {"Email":"james@example.com","Active":true,"CreatedDate":"2013-01-20T00:00:00Z","Roles":["User","Admin"]}
``` ```
@@ -54,7 +56,7 @@ Lets assume that a newline such as the following is appended to foo.jlog:
} }
``` ```
In the above example, the file foo.jlog is being tailed, and when a newline is appended, it is assumed In the above example, the file foo.jlog is being tailed, and when a new line is appended, it is assumed
to be Json and is parsed from the Text field, the parsed Json is then inserted underneath a property *stuff* to be Json and is parsed from the Text field, the parsed Json is then inserted underneath a property *stuff*
The resulting output would be: The resulting output would be:
@@ -84,8 +86,8 @@ The fields must be in pairs with oldname first and newname second.
"target": "stuff", "target": "stuff",
"source": "Text", "source": "Text",
"rename": [ "rename": [
"Text", "level",
"Data" "Level"
] ]
} }
} }
@@ -96,7 +98,9 @@ The fields must be in pairs with fieldName first and value second.
```json ```json
"Filters": [ "Filters": [
{ {
"json": { "json": {
"type": "Win32-FileLog",
"source": "Text",
"add_field": [ "add_field": [
"ComputerName", "Host", "ComputerName", "Host",
"Username", "%{SID}" "Username", "%{SID}"
@@ -112,7 +116,9 @@ Remove the fields. More than one field can be specified at a time.
"Filters": [ "Filters": [
{ {
"json": { "json": {
"remove_tag": [ "type": "Win32-FileLog",
"source": "Text",
"remove_field": [
"static_tag1", "static_tag1",
"Computer_%{Host}" "Computer_%{Host}"
] ]
@@ -128,6 +134,8 @@ Adds the tag(s) to the tag array.
"Filters": [ "Filters": [
{ {
"json": { "json": {
"type": "Win32-FileLog",
"source": "Text",
"add_tag": [ "add_tag": [
"foo_%{Host}", "foo_%{Host}",
"static_tag1" "static_tag1"
@@ -143,6 +151,8 @@ Remove the tag(s) to the tag array. More than one tag can be specified at a tim
"Filters": [ "Filters": [
{ {
"json": { "json": {
"type": "Win32-FileLog",
"source": "Text",
"remove_tag": [ "remove_tag": [
"static_tag1", "static_tag1",
"Username" "Username"

View File

@@ -7,11 +7,12 @@ The following parameters are allowed when configuring WindowsEvents.
| Parameter | Type | Description | Details | Default | | Parameter | Type | Description | Details | Default |
| :---------------- |:---------------| :----------------------------------------------------------------------- | :--------------------------- | :-- | | :---------------- |:---------------| :----------------------------------------------------------------------- | :--------------------------- | :-- |
| *iCodepage* | integer |Codepage of the text file. | 0 is the system codepage, -1 is UNICODE. | 0 |
| *location* | string |Location of file(s) to monitor | Path to text file(s) including wildcards. | | | *location* | string |Location of file(s) to monitor | Path to text file(s) including wildcards. | |
| *logSource* | string |Source name | Used for conditions | | | *logSource* | string |Source name | Used for conditions | |
| *recurse* | integer |Max subdirectory recursion level. | 0 disables subdirectory recursion; -1 enables unlimited recursion. | 0 | | *recurse* | integer |Max subdirectory recursion level. | 0 disables subdirectory recursion; -1 enables unlimited recursion. | 0 |
| *splitLongLines* | boolean |Behavior when event messages or event category names cannot be resolved. |When a text line is longer than 128K characters, the format truncates the line and either discards the remaining of the line (when this parameter is set to "false"), or processes the remainder of the line as a new line (when this parameter is set to "true").| false | | *splitLongLines* | boolean |Behavior when event messages or event category names cannot be resolved. |When a text line is longer than 128K characters, the format truncates the line and either discards the remaining of the line (when this parameter is set to "false"), or processes the remainder of the line as a new line (when this parameter is set to "true").| false |
| *iCodepage* | integer |Codepage of the text file. | 0 is the system codepage, -1 is UNICODE. | 0 | | *type* | string |Typename for this Input | | Win32-FileLog |
| [codec](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/Codec.md) | object | Codec to use | | [codec](https://github.com/Cimpress-MCP/TimberWinR/blob/master/TimberWinR/mdocs/Codec.md) | object | Codec to use |
Example Input: Monitors all files (recursively) located at C:\Logs1\ matching *.log as a pattern. I.e. C:\Logs1\foo.log, C:\Logs1\Subdir\Log2.log, etc. Example Input: Monitors all files (recursively) located at C:\Logs1\ matching *.log as a pattern. I.e. C:\Logs1\foo.log, C:\Logs1\Subdir\Log2.log, etc.
@@ -39,3 +40,4 @@ After a successful parse of an event, the following fields are added:
| LogFilename | STRING |Full path of the file containing this line | | LogFilename | STRING |Full path of the file containing this line |
| Index | INTEGER | Line number | | Index | INTEGER | Line number |
| Text | STRING | Text line content | | Text | STRING | Text line content |
| type | STRING | Win32-FileLog |

View File

@@ -7,15 +7,15 @@ The following parameters are allowed when configuring the Redis output.
| Parameter | Type | Description | Details | Default | | Parameter | Type | Description | Details | Default |
| :-------------|:---------|:------------------------------------------------------------| :--------------------------- | :-- | | :-------------|:---------|:------------------------------------------------------------| :--------------------------- | :-- |
| *threads* | string | Location of log files(s) to monitor | Number of worker theads to send messages | 1 |
| *batch_count* | integer | Sent as a single message | Number of messages to aggregate | 200 | | *batch_count* | integer | Sent as a single message | Number of messages to aggregate | 200 |
| *max_batch_count* | integer | Dynamically adjusted count maximum | Increases over time | batch_count*10 | | *host* | string | The hostname(s) of your Redis server(s) | IP or DNS name | |
| *interval* | integer | Interval in milliseconds to sleep during batch sends | Interval | 5000 |
| *index* | string | The name of the redis list | logstash index name | logstash | | *index* | string | The name of the redis list | logstash index name | logstash |
| *host* | [string] | The hostname(s) of your Redis server(s) | IP or DNS name | | | *interval* | integer | Interval in milliseconds to sleep during batch sends | Interval | 5000 |
| *port* | integer | Redis port number | This port must be open | 6379 | | *max_batch_count* | integer | Dynamically adjusted count maximum | Increases over time | batch_count * 10 |
| *max_queue_size* | integer | Maximum redis queue depth | | 50000 | | *max_queue_size* | integer | Maximum redis queue depth | | 50000 |
| *port* | integer | Redis port number | This port must be open | 6379 |
| *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 | Location of log files(s) to monitor | Number of worker theads to send messages | 1 |
Example Input: Example Input:
```json ```json

View File

@@ -8,6 +8,7 @@ The following parameters are allowed when configuring WindowsEvents.
| Parameter | Type | Description | Details | Default | | Parameter | Type | Description | Details | Default |
| :---------------- |:---------------| :----------------------------------------------------------------------- | :--------------------------- | :-- | | :---------------- |:---------------| :----------------------------------------------------------------------- | :--------------------------- | :-- |
| *type* | string |Typename for this Input | | Win32-TailLog |
| *location* | string |Location of file(s) to monitor | Path to text file(s) including wildcards. | | | *location* | string |Location of file(s) to monitor | Path to text file(s) including wildcards. | |
| *logSource* | string |Source name | Used for conditions | | | *logSource* | string |Source name | Used for conditions | |
| *recurse* | integer |Max subdirectory recursion level. | 0 disables subdirectory recursion; -1 enables unlimited recursion. | 0 | | *recurse* | integer |Max subdirectory recursion level. | 0 disables subdirectory recursion; -1 enables unlimited recursion. | 0 |
@@ -39,3 +40,4 @@ After a successful parse of an event, the following fields are added:
| LogFilename | STRING |Full path of the file containing this line | | LogFilename | STRING |Full path of the file containing this line |
| Index | INTEGER | Line number | | Index | INTEGER | Line number |
| Text | STRING | Text line content | | Text | STRING | Text line content |
| type | STRING | Win32-TailLog |

View File

@@ -5,9 +5,12 @@ The Tcp input will open a port and listen for properly formatted JSON and will f
## Parameters ## Parameters
The following parameters are allowed when configuring the Tcp input. The following parameters are allowed when configuring the Tcp input.
| Parameter | Type | Description | Details | Default | | Parameter | Type | Description | Details | Default |
| :---------------- |:---------------| :----------------------------------------------------------------------- | :--------------------------- | :-- | | :---------------- |:-----------------| :----------------------------------------------------------------------- | :--------------------------- | :-- |
| *port* | integer |Port number to open | Must be an available port | | | *add_field* | property:array |Add field(s) to this event. Field names can be dynamic and include parts of the event using the %{field} syntax. This property must be specified in pairs. | |
| *port* | integer |Port number to open | Must be an available port | |
| *rename* | property:array |Rename one or more fields | | |
| *type* | string |Typename for this Input | | Win32-Tcp |
Example Input: Listen on Port 5140 Example Input: Listen on Port 5140

View File

@@ -7,7 +7,10 @@ The following parameters are allowed when configuring the Udp input.
| Parameter | Type | Description | Details | Default | | Parameter | Type | Description | Details | Default |
| :---------------- |:---------------| :----------------------------------------------------------------------- | :--------------------------- | :-- | | :---------------- |:---------------| :----------------------------------------------------------------------- | :--------------------------- | :-- |
| *port* | integer |Port number to open | Must be an available port | | | *add_field* | property:array |Add field(s) to this event. Field names can be dynamic and include parts of the event using the %{field} syntax. This property must be specified in pairs. | |
| *port* | integer |Port number to open | Must be an available port | |
| *rename* | property:array |Rename one or more fields | | |
| *type* | string |Typename for this Input | | Win32-Udp |
Example Input: Listen on Port 5142 Example Input: Listen on Port 5142