diff --git a/OKLogger.sln b/OKLogger.sln index 233c9bd..20b4f98 100644 --- a/OKLogger.sln +++ b/OKLogger.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.27004.2005 +VisualStudioVersion = 15.0.27004.2009 MinimumVisualStudioVersion = 15.0.26124.0 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B5000E86-781F-480F-B687-6E50201A471C}" EndProject @@ -20,9 +20,6 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OKLogger.PerformanceTestsHarness", "src\OKLogger.PerformanceTestsHarness\OKLogger.PerformanceTestsHarness.csproj", "{16965038-71F0-4A9E-B76E-8E46BF54CC05}" EndProject Global - GlobalSection(Performance) = preSolution - HasPerformanceSessions = true - EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 @@ -109,4 +106,7 @@ Global GlobalSection(Performance) = preSolution HasPerformanceSessions = true EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection EndGlobal diff --git a/src/OKLogger.TestHanress/OKLogger.TestHarness.csproj b/src/OKLogger.TestHanress/OKLogger.TestHarness.csproj index 3f733f6..557d4ee 100644 --- a/src/OKLogger.TestHanress/OKLogger.TestHarness.csproj +++ b/src/OKLogger.TestHanress/OKLogger.TestHarness.csproj @@ -7,6 +7,7 @@ + diff --git a/src/OKLogger.Tests/FormattingTests.cs b/src/OKLogger.Tests/FormattingTests.cs index eaa72a4..54a04d3 100644 --- a/src/OKLogger.Tests/FormattingTests.cs +++ b/src/OKLogger.Tests/FormattingTests.cs @@ -45,7 +45,7 @@ public void ObjectFormatter_SimpleList() var result = objectFormatter.Format(objectToFormat, 0); Assert.Single(result); - Assert.Equal(result["Alpha"], "1,2,3,4,5"); + Assert.Equal("1,2,3,4,5", result["Alpha"]); } @@ -66,9 +66,9 @@ public void DictionaryFormatter() var result = objectFormatter.Format(objectToFormat, 0); Assert.Equal(3, result.Count); - Assert.Equal(result["A"], "1"); - Assert.Equal(result["B"], "2"); - Assert.Equal(result["C"], "3"); + Assert.Equal("1", result["A"]); + Assert.Equal("2", result["B"]); + Assert.Equal("3", result["C"]); } @@ -104,13 +104,13 @@ public void ObjectFormatter_Nested() var duration = stopwatch.ElapsedMilliseconds; Assert.Equal(7, result.Count); - Assert.Equal(result["Alpha"], "a"); - Assert.Equal(result["Beta"], "1"); - Assert.Equal(result["EnumProp"], "SecondValue"); - Assert.Equal(result["Receiver_Name"], "bkennedy"); - Assert.Equal(result["Receiver_User_Id"], "2"); - Assert.Equal(result["Sender_Name"], "admin"); - Assert.Equal(result["Sender_User_Id"], "1"); + Assert.Equal("a",result["Alpha"]); + Assert.Equal("1",result["Beta"]); + Assert.Equal("SecondValue",result["EnumProp"]); + Assert.Equal("bkennedy",result["Receiver_Name"]); + Assert.Equal("2",result["Receiver_User_Id"]); + Assert.Equal("admin", result["Sender_Name"]); + Assert.Equal("1",result["Sender_User_Id"]); } @@ -126,7 +126,7 @@ public void ArrayFormatter_Int() Assert.Single(result); Assert.True(result.ContainsKey(string.Empty), "Should have a single, empty key"); - Assert.Equal(result[string.Empty], "1,2,3,4"); + Assert.Equal("1,2,3,4", result[string.Empty]); @@ -142,7 +142,7 @@ public void ArrayFormatter_Double() Assert.Single(result); Assert.True(result.ContainsKey(string.Empty), "Should have a single, empty key"); - Assert.Equal(result[string.Empty], "1.1,2.2,3.3,4.4"); + Assert.Equal("1.1,2.2,3.3,4.4", result[string.Empty]); } @@ -156,7 +156,7 @@ public void ArrayFormatter_String() Assert.Single(result); Assert.True(result.ContainsKey(string.Empty), "Should have a single, empty key"); - Assert.Equal(result[string.Empty], "alpha,beta,gamma"); + Assert.Equal("alpha,beta,gamma", result[string.Empty]); } [Fact] @@ -169,7 +169,7 @@ public void GuidFormatter() Assert.Single(result); Assert.True(result.ContainsKey(string.Empty), "Should have a single, empty key"); - Assert.Equal(result[string.Empty], "df0187e1-e1eb-4a9f-a528-4afebfecf4a5"); + Assert.Equal("df0187e1-e1eb-4a9f-a528-4afebfecf4a5",result[string.Empty]); } [Fact] @@ -182,7 +182,7 @@ public void EnumFormatter() Assert.Single(result); Assert.True(result.ContainsKey(string.Empty), "Should have a single, empty key"); - Assert.Equal(result[string.Empty], "SecondValue"); + Assert.Equal("SecondValue", result[string.Empty]); } @@ -196,7 +196,7 @@ public void EnumFormatter_Nullable_WithValue() Assert.Single(result); Assert.True(result.ContainsKey(string.Empty), "Should have a single, empty key"); - Assert.Equal(result[string.Empty], "SecondValue"); + Assert.Equal("SecondValue", result[string.Empty]); } @@ -223,7 +223,7 @@ public void Nullable_Integer_With_Value() Assert.Single(result); Assert.True(result.ContainsKey(string.Empty), "Should have a single, empty key"); - Assert.Equal(result[string.Empty], "3"); + Assert.Equal("3", result[string.Empty]); } [Fact] @@ -268,7 +268,7 @@ public void PropParser_Escape() Assert.Equal(1, result.Count); Assert.True(result.ContainsKey("A"), "Should have a single, empty key"); - Assert.Equal(result["A"], "test_S_tring"); + Assert.Equal("test_S_tring", result["A"]); } [Fact] diff --git a/src/OKLogger.Tests/JTokenFormatterTests.cs b/src/OKLogger.Tests/JTokenFormatterTests.cs new file mode 100644 index 0000000..81a698f --- /dev/null +++ b/src/OKLogger.Tests/JTokenFormatterTests.cs @@ -0,0 +1,462 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Xunit; +using Moq; +using OKLogger.Parsing; +using Newtonsoft.Json.Linq; +using System.Diagnostics; +using OKLogger.Parsing.JObjects; + +namespace OKLogger.Tests +{ + + public class JTokenFormatterTests + { + public IValueEscaper Scrubber = new CharEscaper('"'); + + public JValue SampleStringJToken = new JValue("This is a test string"); + + [Fact] + [Trait("Category", "JToken")] + public void JToken_StringFormatter_Formatting() + { + + var testString = "This is a test string"; + var testval = new JValue(testString); + + var formatter = new StringJTokenFormatter(Scrubber); + + var result = formatter.Format(testval, 0); + + Assert.Single(result); + Assert.True(result.ContainsKey(string.Empty), "Should have a single, empty key"); + Assert.Equal(testString, result[string.Empty]); + } + + [Fact] + [Trait("Category", "JToken")] + public void JToken_StringFormatter_Handles() + { + var formatter = new StringJTokenFormatter(Scrubber); + + + var result = formatter.Handles(SampleTokens.String.Token.Type); + Assert.True(result, "JToken string formatter should handle token string"); + + result = formatter.Handles(SampleTokens.Guid.Token.Type); + Assert.False(result, "JToken string formatter should not handle guid types"); + + result = formatter.Handles(SampleTokens.Int.Token.Type); + Assert.False(result, "JToken string formatter should not handle integer types"); + + result = formatter.Handles(SampleTokens.TrueBool.Token.Type); + Assert.False(result, "JToken string formatter should not handle boolean types"); + + result = formatter.Handles(SampleTokens.Double.Token.Type); + Assert.False(result, "JToken string formatter should not handle float types"); + + result = formatter.Handles(SampleTokens.TestObject.Token.Type); + Assert.False(result, "JToken string formatter should not handle object types"); + } + + + [Fact] + [Trait("Category", "JToken")] + public void JToken_BooleanFormatter_Formatting() + { + + + var formatter = new BooleanJTokenFormatter(); + + var result = formatter.Format(SampleTokens.TrueBool.Token, 0); + + Assert.Single(result); + Assert.True(result.ContainsKey(string.Empty), "Should have a single, empty key"); + Assert.Equal("true", result[string.Empty]); + + result = formatter.Format(SampleTokens.FalseBool.Token, 0); + + Assert.Single(result); + Assert.True(result.ContainsKey(string.Empty), "Should have a single, empty key"); + Assert.Equal("false", result[string.Empty]); + } + + [Fact] + [Trait("Category", "JToken")] + public void JToken_BooleanFormatter_Handles() + { + var formatter = new BooleanJTokenFormatter(); + + var result = formatter.Handles(SampleTokens.TrueBool.Token.Type); + Assert.True(result, "JToken boolean formatter should handle token bool"); + + result = formatter.Handles(SampleTokens.FalseBool.Token.Type); + Assert.True(result, "JToken boolean formatter should handle token bool"); + + result = formatter.Handles(SampleTokens.String.Token.Type); + Assert.False(result, "JToken boolean formatter should not handle string types"); + + result = formatter.Handles(SampleTokens.Guid.Token.Type); + Assert.False(result, "JToken boolean formatter should not handle guid types"); + + result = formatter.Handles(SampleTokens.Int.Token.Type); + Assert.False(result, "JToken boolean formatter should not handle integer types"); + + result = formatter.Handles(SampleTokens.Double.Token.Type); + Assert.False(result, "JToken boolean formatter should not handle float types"); + + result = formatter.Handles(SampleTokens.TestObject.Token.Type); + Assert.False(result, "JToken boolean formatter should not handle object types"); + } + + [Fact] + [Trait("Category", "JToken")] + public void JToken_FloatFormatter_Formatting() + { + + + var formatter = new FloatJTokenFormatter(); + + var result = formatter.Format(SampleTokens.Double.Token, 0); + + Assert.Single(result); + Assert.True(result.ContainsKey(string.Empty), "Should have a single, empty key"); + Assert.Equal("3.14", result[string.Empty]); + + } + + [Fact] + [Trait("Category", "JToken")] + public void JToken_FloatFormatter_Handles() + { + var formatter = new FloatJTokenFormatter(); + + var result = formatter.Handles(SampleTokens.Double.Token.Type); + Assert.True(result, "JToken float formatter should handle token double"); + + result = formatter.Handles(SampleTokens.FalseBool.Token.Type); + Assert.False(result, "JToken float formatter should not handle token bool"); + + result = formatter.Handles(SampleTokens.String.Token.Type); + Assert.False(result, "JToken float formatter should not handle string types"); + + result = formatter.Handles(SampleTokens.Guid.Token.Type); + Assert.False(result, "JToken float formatter should not handle guid types"); + + result = formatter.Handles(SampleTokens.Int.Token.Type); + Assert.False(result, "JToken float formatter should not handle integer types"); + + result = formatter.Handles(SampleTokens.TestObject.Token.Type); + Assert.False(result, "JToken float formatter should not handle object types"); + } + + + [Fact] + [Trait("Category", "JToken")] + public void JToken_IntegerFormatter_Formatting() + { + + + var formatter = new IntegerJTokenFormatter(); + + var result = formatter.Format(SampleTokens.Int.Token, 0); + + Assert.Single(result); + Assert.True(result.ContainsKey(string.Empty), "Should have a single, empty key"); + Assert.Equal(SampleTokens.Int.Value.ToString(), result[string.Empty]); + + } + + [Fact] + [Trait("Category", "JToken")] + public void JToken_IntegerFormatter_Handles() + { + var formatter = new IntegerJTokenFormatter(); + + var result = formatter.Handles(SampleTokens.Double.Token.Type); + Assert.False(result, "JToken int formatter should not handle token double"); + + result = formatter.Handles(SampleTokens.FalseBool.Token.Type); + Assert.False(result, "JToken int formatter should not handle token bool"); + + result = formatter.Handles(SampleTokens.String.Token.Type); + Assert.False(result, "JToken int formatter should not handle string types"); + + result = formatter.Handles(SampleTokens.Guid.Token.Type); + Assert.False(result, "JToken int formatter should not handle guid types"); + + result = formatter.Handles(SampleTokens.Int.Token.Type); + Assert.True(result, "JToken int formatter not handle integer types"); + + result = formatter.Handles(SampleTokens.TestObject.Token.Type); + Assert.False(result, "JToken int formatter should not handle object types"); + } + + + [Fact] + [Trait("Category", "JToken")] + public void JToken_GuidFormatter_Formatting() + { + + + var formatter = new GuidFormatter(); + + var result = formatter.Format(SampleTokens.Guid.Token, 0); + + Assert.Single(result); + Assert.True(result.ContainsKey(string.Empty), "Should have a single, empty key"); + Assert.Equal(SampleTokens.Guid.Value.ToString(), result[string.Empty]); + + } + + [Fact] + [Trait("Category", "JToken")] + public void JToken_GuidFormatter_Handles() + { + var formatter = new GuidJTokenFormatter(); + + var result = formatter.Handles(SampleTokens.Double.Token.Type); + Assert.False(result, "JToken float formatter should not handle token double"); + + result = formatter.Handles(SampleTokens.FalseBool.Token.Type); + Assert.False(result, "JToken float formatter should not handle token bool"); + + result = formatter.Handles(SampleTokens.String.Token.Type); + Assert.False(result, "JToken float formatter should not handle string types"); + + result = formatter.Handles(SampleTokens.Guid.Token.Type); + Assert.True(result, "JToken float formatter should handle guid types"); + + result = formatter.Handles(SampleTokens.Int.Token.Type); + Assert.False(result, "JToken float formatter should not handle integer types"); + + result = formatter.Handles(SampleTokens.TestObject.Token.Type); + Assert.False(result, "JToken float formatter should not handle float types"); + } + + [Fact] + [Trait("Category", "JToken")] + public void JToken_JObjectFormatter_Handles() + { + var formatter = new JObjectFormatter(new DefaultJTokenFormatters(), ",", 3); + + var result = formatter.Handles(SampleTokens.Double.Token.Type); + Assert.False(result, "JToken float formatter should not handle token double"); + + result = formatter.Handles(SampleTokens.FalseBool.Token.Type); + Assert.False(result, "JToken float formatter should not handle token bool"); + + result = formatter.Handles(SampleTokens.String.Token.Type); + Assert.False(result, "JToken float formatter should not handle string types"); + + result = formatter.Handles(SampleTokens.Guid.Token.Type); + Assert.False(result, "JToken float formatter should handle guid types"); + + result = formatter.Handles(SampleTokens.Int.Token.Type); + Assert.False(result, "JToken float formatter should not handle integer types"); + + result = formatter.Handles(SampleTokens.TestObject.Token.Type); + Assert.True(result, "JToken float formatter should handle object types"); + } + + [Fact] + [Trait("Category", "JToken")] + public void JToken_JObjectFormatter_NestedObject() + { + var testValue = JObject.FromObject(new + { + Alpha = "a", + Beta = 1.0, + Receiver = new + { + Name = "bkennedy", + User_Id = 2 + }, + Sender = new + { + Name = "admin", + User_Id = 1 + } + + }); + + var formatter = new JObjectFormatter(new DefaultJTokenFormatters(), ",", 3); + var result = formatter.Format(testValue, 0); + + Assert.Equal(6, result.Count); + Assert.Equal("a", result["Alpha"]); + Assert.Equal("1", result["Beta"]); + Assert.Equal("bkennedy", result["Receiver_Name"]); + Assert.Equal("2", result["Receiver_User_Id"]); + Assert.Equal("admin", result["Sender_Name"]); + Assert.Equal("1", result["Sender_User_Id"]); + + } + + [Fact] + [Trait("Category", "JToken")] + public void JToken_JObjectFormatter_Formatting() + { + var testValue = JObject.FromObject(new + { + Alpha = "a", + Beta = 1.0, + Receiver = new + { + Name = "bkennedy", + User_Id = 2 + }, + Sender = new + { + Name = "admin", + User_Id = 1 + } + + }); + + var formatter = new JObjectFormatter(new DefaultJTokenFormatters(), ",", 3); + var result = formatter.Format(testValue, 0); + + Assert.Equal(6, result.Count); + Assert.Equal("a", result["Alpha"]); + Assert.Equal("1", result["Beta"]); + Assert.Equal("bkennedy", result["Receiver_Name"]); + Assert.Equal("2", result["Receiver_User_Id"]); + Assert.Equal("admin", result["Sender_Name"]); + Assert.Equal("1", result["Sender_User_Id"]); + + } + + + [Fact] + [Trait("Category", "JToken")] + public void JToken_JArrayFormatter_Handles() + { + var formatter = new ArrayJTokenFormatter(new DefaultJTokenFormatters(), ",", 3); + + var result = formatter.Handles(SampleTokens.Double.Token.Type); + Assert.False(result, "JToken array formatter should not handle token double"); + + result = formatter.Handles(SampleTokens.FalseBool.Token.Type); + Assert.False(result, "JToken array formatter should not handle token bool"); + + result = formatter.Handles(SampleTokens.String.Token.Type); + Assert.False(result, "JToken array formatter should not handle string types"); + + result = formatter.Handles(SampleTokens.Guid.Token.Type); + Assert.False(result, "JToken array formatter should handle guid types"); + + result = formatter.Handles(SampleTokens.Int.Token.Type); + Assert.False(result, "JToken array formatter should not handle integer types"); + + result = formatter.Handles(SampleTokens.TestObject.Token.Type); + Assert.False(result, "JToken array formatter should not handle object types"); + + var testArray = JArray.FromObject(new SampleObject[] { + new SampleObject { Alpha = "first", Beta = 1 }, + new SampleObject { Alpha = "second", Beta = 2 } + }); + + result = formatter.Handles(testArray.Type); + Assert.True(result, "JToken array formatter should handle array types"); + } + + [Fact] + [Trait("Category", "JToken")] + public void JToken_JArrayFormatter_Formatting_ObjectArray() + { + var formatter = new ArrayJTokenFormatter(new DefaultJTokenFormatters(), ",", 3); + + var testArray = JArray.FromObject( new SampleObject[] { + new SampleObject { Alpha = "first", Beta = 1 }, + new SampleObject { Alpha = "second", Beta = 2 } + }); + + var stopwatch = new Stopwatch(); + stopwatch.Start(); + var result = formatter.Format(testArray, 0); + stopwatch.Stop(); + + + Assert.Equal(4, result.Count); + Assert.Equal("first", result["_0_Alpha"]); + Assert.Equal("second", result["_1_Alpha"]); + Assert.Equal("1", result["_0_Beta"]); + Assert.Equal("2", result["_1_Beta"]); + + } + + [Fact] + [Trait("Category", "JToken")] + public void JToken_JArrayFormatter_Formatting_ValueArray() + { + var formatter = new ArrayJTokenFormatter(new DefaultJTokenFormatters(), ",", 3); + + var testArray = JArray.FromObject(new int[] { 1, 1, 2, 3, 5, 8, 13}); + + var result = formatter.Format(testArray, 0); + + Assert.Single(result); + Assert.Equal("1,1,2,3,5,8,13", result[string.Empty]); + + + } + /// + /// This tests the entity formatter that forwards to the jtoken forwarder + /// + [Fact] + [Trait("Category", "JToken")] + public void JToken_JTokenFormatter_Formatting_Object() + { + var testValue = JObject.FromObject(new + { + Alpha = "a", + Beta = 1.0, + Receiver = new + { + Name = "bkennedy", + User_Id = 2 + }, + Sender = new + { + Name = "admin", + User_Id = 1 + } + + }); + + var formatter = new JTokenFormatter(new DefaultJTokenFormatters(), ",", 3); + var result = formatter.Format(testValue, 0); + + Assert.Equal(6, result.Count); + Assert.Equal("a", result["Alpha"]); + Assert.Equal("1", result["Beta"]); + Assert.Equal("bkennedy", result["Receiver_Name"]); + Assert.Equal("2", result["Receiver_User_Id"]); + Assert.Equal("admin", result["Sender_Name"]); + Assert.Equal("1", result["Sender_User_Id"]); + + } + + /// + /// This tests the entity formatter that forwards to the jtoken forwarder + /// + [Fact] + [Trait("Category", "JToken")] + public void JToken_JTokenFormatter_Formatting() + { + var testArray = JArray.FromObject(new int[] { 1, 1, 2, 3, 5, 8, 13 }); + + + var formatter = new JTokenFormatter(new DefaultJTokenFormatters(), ",", 3); + var result = formatter.Format(testArray, 0); + + Assert.Single(result); + Assert.Equal("1,1,2,3,5,8,13", result[string.Empty]); + + } + + } +} + diff --git a/src/OKLogger.Tests/OKLogger.Tests.csproj b/src/OKLogger.Tests/OKLogger.Tests.csproj index af34a3c..022187a 100644 --- a/src/OKLogger.Tests/OKLogger.Tests.csproj +++ b/src/OKLogger.Tests/OKLogger.Tests.csproj @@ -8,8 +8,8 @@ - + diff --git a/src/OKLogger.Tests/SampleToken.cs b/src/OKLogger.Tests/SampleToken.cs new file mode 100644 index 0000000..e687856 --- /dev/null +++ b/src/OKLogger.Tests/SampleToken.cs @@ -0,0 +1,74 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Text; + +namespace OKLogger.Tests +{ + public class SampleToken + { + public JToken Token { get; set; } + public V Value { get; set; } + + public SampleToken() + { + + } + + public SampleToken(JToken t, V v) + { + Token = t; + Value = v; + } + + public SampleToken(V v) + { + Value = v; + Token = new JValue(v); + } + } + + + + public class SampleTokens + { + public static SampleToken String = new SampleToken("This is a test string"); + + public static SampleToken Int = new SampleToken(42); + public static SampleToken Double = new SampleToken(3.14); + + public static SampleToken Guid = new SampleToken(new Guid("203a9f71-7d62-44aa-8147-caf8680f17ec")); + + public static SampleToken TrueBool = new SampleToken(true); + public static SampleToken FalseBool = new SampleToken(false); + + public static SampleToken TestObject = new SampleToken() + { + Value = new SampleObject + { + Alpha = "a", + Beta = 2 + + }, + Token = JObject.FromObject( + new SampleObject + { + Alpha = "a", + Beta = 2 + + } + ) + + + }; + + + } + + public class SampleObject + { + public string Alpha { get; set; } + public int Beta { get; set; } + + } +} diff --git a/src/OKLogger/OKLogger.csproj b/src/OKLogger/OKLogger.csproj index 0f1dc84..6aa707e 100644 --- a/src/OKLogger/OKLogger.csproj +++ b/src/OKLogger/OKLogger.csproj @@ -7,6 +7,7 @@ + diff --git a/src/OKLogger/Parsing/DefaultFormatters.cs b/src/OKLogger/Parsing/DefaultFormatters.cs index 164fdb5..f331385 100644 --- a/src/OKLogger/Parsing/DefaultFormatters.cs +++ b/src/OKLogger/Parsing/DefaultFormatters.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using OKLogger.Parsing.JObjects; namespace OKLogger.Parsing { @@ -26,6 +27,7 @@ public DefaultFormatters() Add(101, new GenericListFormatter(",", Scrubber)); Add(102, new DictionaryFormatter(",", Scrubber)); + Add(500, new JTokenFormatter(new DefaultJTokenFormatters(), ",", MaxDepth)); Add(Int16.MaxValue, new ObjectFormatter(this, ",", MaxDepth)); diff --git a/src/OKLogger/Parsing/FormatterFactory.cs b/src/OKLogger/Parsing/FormatterFactory.cs index 8cb6b47..1c1bcff 100644 --- a/src/OKLogger/Parsing/FormatterFactory.cs +++ b/src/OKLogger/Parsing/FormatterFactory.cs @@ -27,7 +27,7 @@ public void AddCustomFormatter( IEntityFormatter formatter) Add(CurrentCustomFormatterPosition++, formatter); } - public IEntityFormatter GetParser(Type t) + public IEntityFormatter GetFormatter(Type t) { foreach (var formatter in Formatters) { diff --git a/src/OKLogger/Parsing/IFormatterFactory.cs b/src/OKLogger/Parsing/IFormatterFactory.cs index c92e67c..60628af 100644 --- a/src/OKLogger/Parsing/IFormatterFactory.cs +++ b/src/OKLogger/Parsing/IFormatterFactory.cs @@ -8,7 +8,7 @@ namespace OKLogger.Parsing { public interface IFormatterFactory { - IEntityFormatter GetParser(Type t); + IEntityFormatter GetFormatter(Type t); void AddCustomFormatter(IEntityFormatter formatter); } diff --git a/src/OKLogger/Parsing/JObjects/ArrayJTokenFormatter.cs b/src/OKLogger/Parsing/JObjects/ArrayJTokenFormatter.cs new file mode 100644 index 0000000..9e0ecd8 --- /dev/null +++ b/src/OKLogger/Parsing/JObjects/ArrayJTokenFormatter.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Newtonsoft.Json.Linq; +using System.Linq; + +namespace OKLogger.Parsing.JObjects +{ + public class ArrayJTokenFormatter : IJTokenFormatter + { + private IJTokenFormatterFactory Formatters { get; set; } + private string FieldContentDelimiter { get; set; } + private int MaxDepth { get; set; } + + private ResultMerger ResultMerger = new ResultMerger(); + + public ArrayJTokenFormatter(IJTokenFormatterFactory formatters, string fieldContentDelimiter, int maxDepth) + { + Formatters = formatters; + FieldContentDelimiter = fieldContentDelimiter; + MaxDepth = maxDepth; + } + + public bool Handles(JTokenType t) + { + return t == JTokenType.Array; + } + + public Dictionary Format(JToken item, int depth) + { + if (depth > MaxDepth || item == null) return new Dictionary(); + + var arr = item as JArray; + + if (arr == null || arr.Count < 1) + { + return new Dictionary() { { string.Empty, string.Empty } }; + } + + var result = new Dictionary(); + + + var elementValues = new List>(); + + for (int i = 0; i < arr.Count; i++) + { + + JToken valAtIndex = arr[i]; + + try + { + var val = arr[i]; + if (val == null) continue; + + var formatter = Formatters.GetFormatter(val.Type); + if (formatter == null) continue; + + var propValue = formatter.Format(val, depth + 1); + elementValues.Add(propValue); + + + } + catch { } // swallow exception + } + + // if all elements are single values we just concatenate them in a delimited list + // otherwise, make the separate keys + var isValueArray = elementValues.All(val => val.Count == 1); + if(isValueArray) + { + result[string.Empty] = string.Join(FieldContentDelimiter, elementValues.Select(x => x[string.Empty])); + return result; + } + else + { + for(var i = 0; i < elementValues.Count; i++) + { + ResultMerger.Merge(result, $"_{i}", elementValues[i]); + } + } + + + return result; + } + + + } +} diff --git a/src/OKLogger/Parsing/JObjects/BooleanJTokenFormatter.cs b/src/OKLogger/Parsing/JObjects/BooleanJTokenFormatter.cs new file mode 100644 index 0000000..9a32b6e --- /dev/null +++ b/src/OKLogger/Parsing/JObjects/BooleanJTokenFormatter.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace OKLogger.Parsing.JObjects +{ + public class BooleanJTokenFormatter : IJTokenFormatter + { + + public bool Handles(JTokenType t) + { + return t == JTokenType.Boolean; + } + + public Dictionary Format(JToken item, int depth) + { + var val = item as JValue; + if(val == null) + { + return new Dictionary() { { string.Empty, string.Empty } }; + } + + var valAsBoolean = val.Value(); + + if (valAsBoolean) + { + return new Dictionary() + { + { string.Empty, "true" } + }; + } + else + { + return new Dictionary() + { + { string.Empty, "false" } + }; + } + } + } +} diff --git a/src/OKLogger/Parsing/JObjects/DateJTokenFormatter.cs b/src/OKLogger/Parsing/JObjects/DateJTokenFormatter.cs new file mode 100644 index 0000000..60307d0 --- /dev/null +++ b/src/OKLogger/Parsing/JObjects/DateJTokenFormatter.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Newtonsoft.Json.Linq; + +namespace OKLogger.Parsing.JObjects +{ + public class DateJTokenFormatter : IJTokenFormatter + { + private string DatePattern { get; set; } + + private IValueEscaper Scrub { get; set; } + + public DateJTokenFormatter(IValueEscaper scrub, string datePattern = "u") + { + DatePattern = datePattern; + Scrub = scrub; + } + + public Dictionary Format(JToken item, int depth) + { + var val = item as JValue; + if (val == null) + { + return new Dictionary() { { string.Empty, string.Empty } }; + } + + DateTime? valAsDatetime = (DateTime)val; + + if(!valAsDatetime.HasValue) + { + return new Dictionary() { { string.Empty, string.Empty } }; + } + + return new Dictionary() + { + { string.Empty, Scrub.Escape(valAsDatetime.Value.ToString(DatePattern)) } + }; + + } + + public bool Handles(JTokenType t) + { + return t == JTokenType.Date; + } + } +} diff --git a/src/OKLogger/Parsing/JObjects/DefaultJTokenFormatters.cs b/src/OKLogger/Parsing/JObjects/DefaultJTokenFormatters.cs new file mode 100644 index 0000000..2c55843 --- /dev/null +++ b/src/OKLogger/Parsing/JObjects/DefaultJTokenFormatters.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace OKLogger.Parsing.JObjects +{ + public class DefaultJTokenFormatters : JTokenFormatterFactory + { + public const int MaxDepth = 3; + public IValueEscaper Scrubber = new CharEscaper('"'); + + public DefaultJTokenFormatters() + : base() + { + Add(1, new StringJTokenFormatter(Scrubber)); + Add(2, new FloatJTokenFormatter()); + Add(3, new DateJTokenFormatter(Scrubber)); + Add(4, new GuidJTokenFormatter()); + Add(5, new BooleanJTokenFormatter()); + Add(6, new IntegerJTokenFormatter()); + + + + Add(1001, new ArrayJTokenFormatter(this, ",", MaxDepth)); + Add(1002, new JObjectFormatter(this, ",", MaxDepth)); + } + } +} diff --git a/src/OKLogger/Parsing/JObjects/FloatJTokenFormatter.cs b/src/OKLogger/Parsing/JObjects/FloatJTokenFormatter.cs new file mode 100644 index 0000000..7f25cbf --- /dev/null +++ b/src/OKLogger/Parsing/JObjects/FloatJTokenFormatter.cs @@ -0,0 +1,25 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Text; + +namespace OKLogger.Parsing.JObjects +{ + public class FloatJTokenFormatter : IJTokenFormatter + { + public Dictionary Format(JToken item, int depth) + { + if (item == null) return new Dictionary() { { string.Empty, string.Empty } }; + + return new Dictionary() + { + { string.Empty, item.ToString() } + }; + } + + public bool Handles(JTokenType t) + { + return t == JTokenType.Float; + } + } +} diff --git a/src/OKLogger/Parsing/JObjects/GuidJTokenFormatter.cs b/src/OKLogger/Parsing/JObjects/GuidJTokenFormatter.cs new file mode 100644 index 0000000..0a28a40 --- /dev/null +++ b/src/OKLogger/Parsing/JObjects/GuidJTokenFormatter.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Newtonsoft.Json.Linq; + +namespace OKLogger.Parsing.JObjects +{ + public class GuidJTokenFormatter : IJTokenFormatter + { + public Dictionary Format(JToken item, int depth) + { + if (item == null) return new Dictionary() { { string.Empty, string.Empty } }; + + return new Dictionary() + { + { string.Empty, item.ToString() } + }; + } + + public bool Handles(JTokenType t) + { + return t == JTokenType.Guid; + } + } +} diff --git a/src/OKLogger/Parsing/JObjects/IJTokenFormatter.cs b/src/OKLogger/Parsing/JObjects/IJTokenFormatter.cs new file mode 100644 index 0000000..cc10a70 --- /dev/null +++ b/src/OKLogger/Parsing/JObjects/IJTokenFormatter.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Text; + +namespace OKLogger.Parsing.JObjects +{ + public interface IJTokenFormatter + { + bool Handles(JTokenType t); + Dictionary Format(JToken item, int depth); + } +} diff --git a/src/OKLogger/Parsing/JObjects/IJTokenFormatterFactory.cs b/src/OKLogger/Parsing/JObjects/IJTokenFormatterFactory.cs new file mode 100644 index 0000000..b6cb0d6 --- /dev/null +++ b/src/OKLogger/Parsing/JObjects/IJTokenFormatterFactory.cs @@ -0,0 +1,14 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Text; + +namespace OKLogger.Parsing.JObjects +{ + public interface IJTokenFormatterFactory + { + IJTokenFormatter GetFormatter(JTokenType t); + + void AddCustomFormatter(IJTokenFormatter formatter); + } +} diff --git a/src/OKLogger/Parsing/JObjects/IntegerJTokenFormatter.cs b/src/OKLogger/Parsing/JObjects/IntegerJTokenFormatter.cs new file mode 100644 index 0000000..3560371 --- /dev/null +++ b/src/OKLogger/Parsing/JObjects/IntegerJTokenFormatter.cs @@ -0,0 +1,25 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Text; + +namespace OKLogger.Parsing.JObjects +{ + public class IntegerJTokenFormatter : IJTokenFormatter + { + public Dictionary Format(JToken item, int depth) + { + if (item == null) return new Dictionary() { { string.Empty, string.Empty } }; + + return new Dictionary() + { + { string.Empty, item.ToString() } + }; + } + + public bool Handles(JTokenType t) + { + return t == JTokenType.Integer; + } + } +} diff --git a/src/OKLogger/Parsing/JObjects/JObjectFormatter.cs b/src/OKLogger/Parsing/JObjects/JObjectFormatter.cs new file mode 100644 index 0000000..955e66b --- /dev/null +++ b/src/OKLogger/Parsing/JObjects/JObjectFormatter.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Newtonsoft.Json.Linq; + +namespace OKLogger.Parsing.JObjects +{ + public class JObjectFormatter : IJTokenFormatter + { + private IJTokenFormatterFactory Formatters { get; set; } + private string FieldContentDelimiter { get; set; } + private int MaxDepth { get; set; } + + private ResultMerger ResultMerger = new ResultMerger(); + + public JObjectFormatter(IJTokenFormatterFactory formatters, string fieldContentDelimiter, int maxDepth) + { + Formatters = formatters; + FieldContentDelimiter = fieldContentDelimiter; + MaxDepth = maxDepth; + } + + + public Dictionary Format(JToken item, int depth) + { + if (depth > MaxDepth || item == null) return new Dictionary(); + + var obj = item as JObject; + + if(obj == null) + { + return new Dictionary() { { string.Empty, string.Empty } }; + } + + var result = new Dictionary(); + + foreach(var prop in obj.Properties()) + { + + try + { + var val = prop.Value; + if (val == null) continue; + + var formatter = Formatters.GetFormatter(val.Type); + if (formatter == null) continue; + + var propValue = formatter.Format(val, depth + 1); + + ResultMerger.Merge(result, prop.Name, propValue); + + } + catch { } // swallow exception + } + + return result; + } + + public bool Handles(JTokenType t) + { + return t == JTokenType.Object; + } + } +} diff --git a/src/OKLogger/Parsing/JObjects/JTokenFormatter.cs b/src/OKLogger/Parsing/JObjects/JTokenFormatter.cs new file mode 100644 index 0000000..c335f2e --- /dev/null +++ b/src/OKLogger/Parsing/JObjects/JTokenFormatter.cs @@ -0,0 +1,52 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Text; + +namespace OKLogger.Parsing.JObjects +{ + /// + /// Entity formatter for JTokens + /// + public class JTokenFormatter : IEntityFormatter + { + private IJTokenFormatterFactory Formatters { get; set; } + private string FieldContentDelimiter { get; set; } + + private int MaxDepth { get; set; } + + public JTokenFormatter(IJTokenFormatterFactory formatters, string fieldContentDelimiter, int maxDepth) + { + Formatters = formatters; + FieldContentDelimiter = fieldContentDelimiter; + MaxDepth = maxDepth; + } + + public Dictionary Format(object item, int depth) + { + var val = item as JToken; + if(val == null) + { + return new Dictionary(); + } + + var formatter = Formatters.GetFormatter(val.Type); + if(formatter == null) + { + return new Dictionary(); + } + + return formatter.Format(val, depth); + + } + + + public bool Handles(Type t) + { + return t == typeof(JToken); + } + + + + } +} diff --git a/src/OKLogger/Parsing/JObjects/JTokenFormatterFactory.cs b/src/OKLogger/Parsing/JObjects/JTokenFormatterFactory.cs new file mode 100644 index 0000000..bee0569 --- /dev/null +++ b/src/OKLogger/Parsing/JObjects/JTokenFormatterFactory.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Newtonsoft.Json.Linq; + +namespace OKLogger.Parsing.JObjects +{ + public class JTokenFormatterFactory : IJTokenFormatterFactory + { + protected SortedList Formatters { get; set; } + + protected int CurrentCustomFormatterPosition = 10000; + + public JTokenFormatterFactory() + { + Formatters = new SortedList(); + + } + + public void Add(int priority, IJTokenFormatter formatter) + { + Formatters[priority] = formatter; + } + + public void AddCustomFormatter(IJTokenFormatter formatter) + { + Add(CurrentCustomFormatterPosition++, formatter); + } + + + public IJTokenFormatter GetFormatter(JTokenType t) + { + foreach (var formatter in Formatters) + { + if (formatter.Value.Handles(t)) + return formatter.Value; + } + return null; + } + } +} diff --git a/src/OKLogger/Parsing/JObjects/StringJTokenFormatter.cs b/src/OKLogger/Parsing/JObjects/StringJTokenFormatter.cs new file mode 100644 index 0000000..086ed38 --- /dev/null +++ b/src/OKLogger/Parsing/JObjects/StringJTokenFormatter.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Newtonsoft.Json.Linq; + +namespace OKLogger.Parsing.JObjects +{ + public class StringJTokenFormatter : IJTokenFormatter + { + private IValueEscaper Scrub { get; set; } + + public StringJTokenFormatter(IValueEscaper scrub) + { + Scrub = scrub; + } + + public Dictionary Format(JToken item, int depth) + { + var valueAsString = item.Value(); + + return new Dictionary() + { + { string.Empty, Scrub.Escape(valueAsString.ToString()) } + }; + } + + public bool Handles(JTokenType t) + { + return t == JTokenType.String; + } + } +} diff --git a/src/OKLogger/Parsing/ObjectFormatter.cs b/src/OKLogger/Parsing/ObjectFormatter.cs index 451415a..17cc56d 100644 --- a/src/OKLogger/Parsing/ObjectFormatter.cs +++ b/src/OKLogger/Parsing/ObjectFormatter.cs @@ -36,7 +36,7 @@ public Dictionary Format(object item, int depth) foreach (var prop in props) { // see if we have a parser for this property - var parser = Formatters.GetParser(prop.PropertyType); + var parser = Formatters.GetFormatter(prop.PropertyType); if (parser == null) continue; try diff --git a/src/OKLogger/Parsing/PropertyParser.cs b/src/OKLogger/Parsing/PropertyParser.cs index 344215d..7e12483 100644 --- a/src/OKLogger/Parsing/PropertyParser.cs +++ b/src/OKLogger/Parsing/PropertyParser.cs @@ -32,7 +32,7 @@ public IDictionary Parse(object o) var results = new Dictionary(); if (o == null) return results; - var formatter = Formatters.GetParser(o.GetType()); + var formatter = Formatters.GetFormatter(o.GetType()); return formatter.Format(o,0); } diff --git a/src/OKLogger/ResultMerger.cs b/src/OKLogger/ResultMerger.cs new file mode 100644 index 0000000..2656eb6 --- /dev/null +++ b/src/OKLogger/ResultMerger.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace OKLogger +{ + public class ResultMerger + { + + public void Merge(Dictionary root, string rootName, Dictionary child) + { + foreach (var kv in child) + { + var suffix = string.IsNullOrWhiteSpace(kv.Key) ? string.Empty : "_" + kv.Key; + string key = rootName + suffix; + string value; + if (key.Length > 4000) + { + key = kv.Key.Substring(0, 4000); //short circuit strings larger than 8kb + } + if (!string.IsNullOrEmpty(kv.Value) && kv.Value.Length > 4000) + { + value = kv.Value.Substring(0, 4000); + } + else + { + value = kv.Value; + } + root[key] = value; + } + + } + } +}