diff --git a/Its.Log.Monitoring.UnitTests/MonitoringTestExecutionTests.cs b/Its.Log.Monitoring.UnitTests/MonitoringTestExecutionTests.cs index 581a125..e89cf7c 100644 --- a/Its.Log.Monitoring.UnitTests/MonitoringTestExecutionTests.cs +++ b/Its.Log.Monitoring.UnitTests/MonitoringTestExecutionTests.cs @@ -20,7 +20,7 @@ public class MonitoringTestExecutionTests public MonitoringTestExecutionTests() { - Formatter.Register(b => new + LogFormatter.Register(b => new { b.HasContent, HashCode = b.GetHashCode() diff --git a/Its.Log.Monitoring/AssertionExtensions.cs b/Its.Log.Monitoring/AssertionExtensions.cs index c83b971..b093586 100644 --- a/Its.Log.Monitoring/AssertionExtensions.cs +++ b/Its.Log.Monitoring/AssertionExtensions.cs @@ -18,19 +18,19 @@ public static class AssertionExtensions { static AssertionExtensions() { - Formatter.RegisterForAllMembers(); - Formatter.Register(c => new + LogFormatter.RegisterForAllMembers(); + LogFormatter.Register(c => new { c.ReadAsStringAsync().Result, c.Headers }.ToLogString()); - Formatter.Register(c => new + LogFormatter.Register(c => new { c.ReadAsStringAsync().Result, c.Headers }.ToLogString()); - Formatter.RegisterForAllMembers(); - Formatter.RegisterForAllMembers(); + LogFormatter.RegisterForAllMembers(); + LogFormatter.RegisterForAllMembers(); } public static HttpResponseMessage ShouldSucceed( diff --git a/Its.Log.Monitoring/TestTarget.cs b/Its.Log.Monitoring/TestTarget.cs index 82c7570..7923459 100644 --- a/Its.Log.Monitoring/TestTarget.cs +++ b/Its.Log.Monitoring/TestTarget.cs @@ -10,7 +10,7 @@ public class TestTarget { static TestTarget() { - Formatter.RegisterForAllMembers(); + LogFormatter.RegisterForAllMembers(); } internal TestTarget() diff --git a/Its.Log.UnitTests/Demo.cs b/Its.Log.UnitTests/Demo.cs index 3a369db..0211e9e 100644 --- a/Its.Log.UnitTests/Demo.cs +++ b/Its.Log.UnitTests/Demo.cs @@ -59,8 +59,8 @@ public void Basics3_You_control_where_entries_get_written() public void Formatting1_Format_the_log_output_so_that_it_is_more_descriptive() { Log.EntryPosted += (sender, e) => Console.WriteLine(e.LogEntry.ToLogString()); - Formatter.RegisterForAllMembers(); - Formatter.RegisterForAllMembers(); + LogFormatter.RegisterForAllMembers(); + LogFormatter.RegisterForAllMembers(); Log.Write(() => Movie.StarWars); } @@ -71,10 +71,10 @@ public void Formatting2_Format_the_log_output_so_that_it_not_tooooo_descriptive( Log.EntryPosted += (sender, e) => Console.WriteLine(e.LogEntry.ToLogString()); // only certain properties... - Formatter.RegisterForMembers(m => m.Title, m => m.Stars); + LogFormatter.RegisterForMembers(m => m.Title, m => m.Stars); // or specify your own function... - Formatter.Register(p => string.Format("{0} (born: {1})", p.Name, p.DateOfBirth)); + LogFormatter.Register(p => string.Format("{0} (born: {1})", p.Name, p.DateOfBirth)); Log.Write(() => Movie.StarWars); } @@ -104,10 +104,10 @@ public void Formatting4_Every_exception_gets_an_unique_id_which_is_shared_among_ Log.EntryPosted += (sender, e) => Console.WriteLine(e.LogEntry.ToLogString()); // scope down the formatting to show just the relevant bits - Formatter.RegisterForMembers(e => e.ExceptionId, e => e.EventType, e => e.Subject); - Formatter.RegisterForMembers(e => e.Data, e => e.InnerException); - Formatter.RegisterForMembers(e => e.Data, e => e.InnerException); - Formatter.RegisterForMembers(e => e.Data, e => e.InnerException); + LogFormatter.RegisterForMembers(e => e.ExceptionId, e => e.EventType, e => e.Subject); + LogFormatter.RegisterForMembers(e => e.Data, e => e.InnerException); + LogFormatter.RegisterForMembers(e => e.Data, e => e.InnerException); + LogFormatter.RegisterForMembers(e => e.Data, e => e.InnerException); ; var dataException = new DataException("oops!"); @@ -129,8 +129,8 @@ public void Formatting4_Every_exception_gets_an_unique_id_which_is_shared_among_ public void Formatting5_Logging_several_objects() { Log.EntryPosted += (sender, e) => Console.WriteLine(e.LogEntry.ToLogString()); - Formatter.RegisterForAllMembers(); - Formatter.RegisterForAllMembers(); + LogFormatter.RegisterForAllMembers(); + LogFormatter.RegisterForAllMembers(); var carrie = Person.CarrieFisher; Log.Write(() => new { carrie, Person.GeorgeLucas }); @@ -140,13 +140,13 @@ public void Formatting5_Logging_several_objects() public void Formatting6_What_about_long_lists() { Log.EntryPosted += (sender, e) => Console.WriteLine(e.LogEntry.ToLogString()); - Formatter.RegisterForAllMembers(); - Formatter.RegisterForAllMembers(); + LogFormatter.RegisterForAllMembers(); + LogFormatter.RegisterForAllMembers(); - Formatter.ListExpansionLimit = 5; + LogFormatter.ListExpansionLimit = 5; Log.Write(() => Movie.StarWars); - Formatter.ListExpansionLimit = 10; + LogFormatter.ListExpansionLimit = 10; Log.Write(() => Movie.StarWars); } @@ -154,16 +154,16 @@ public void Formatting6_What_about_long_lists() public void Formatting8_What_about_recursion() { Log.EntryPosted += (sender, e) => Console.WriteLine(e.LogEntry.ToLogString()); - Formatter.RegisterForAllMembers(); - Formatter.RegisterForAllMembers(); + LogFormatter.RegisterForAllMembers(); + LogFormatter.RegisterForAllMembers(); // set up a recursive relationship that the above properties formatters will traverse Person.GeorgeLucas.Movies = new[] { Movie.StarWars }; - Formatter.RecursionLimit = 3; + LogFormatter.RecursionLimit = 3; Log.Write(() => Person.GeorgeLucas); - Formatter.RecursionLimit = 10; + LogFormatter.RecursionLimit = 10; Log.Write(() => Person.GeorgeLucas); } @@ -183,7 +183,7 @@ public void LogEntryObject2_Has_information_about_the_call_source() Log.EntryPosted += (sender, e) => Console.WriteLine(e.LogEntry.ToLogString()); // this is already in the default output, but it's also in the LogEntry object, so we'll create a formatter that exposes it directly - Formatter.RegisterForMembers( + LogFormatter.RegisterForMembers( e => e.CallingType, e => e.CallingMethod); @@ -195,7 +195,7 @@ public void LogEntryObject3_Has_a_time_stamp() { Log.EntryPosted += (sender, e) => Console.WriteLine(e.LogEntry.ToLogString()); - Formatter.RegisterForMembers(e => e.TimeStamp); + LogFormatter.RegisterForMembers(e => e.TimeStamp); Log.Write(() => new { Person.GeorgeLucas }); } @@ -205,7 +205,7 @@ public void LogEntryObject4_Has_a_unique_exception_id_if_the_subject_is_an_excep { Log.EntryPosted += (sender, e) => Console.WriteLine(e.LogEntry.ToLogString()); - Formatter.RegisterForMembers(e => e.ExceptionId); + LogFormatter.RegisterForMembers(e => e.ExceptionId); Log.Write(() => new { Person.GeorgeLucas }); Log.Write(() => new Exception("oops")); @@ -217,7 +217,7 @@ public void MethodBoundaries1_Logging_method_boundaries() Log.EntryPosted += (sender, e) => Console.WriteLine(e.LogEntry.ToLogString()); // again, isolating just the parts that are specific to boundary logging: - Formatter.RegisterForMembers( + LogFormatter.RegisterForMembers( e => e.Message, e => e.Params, e => e.ElapsedMilliseconds); @@ -366,7 +366,7 @@ public void Reactive4_Disable_logging_from_specific_classes() public void SetUp() { Log.UnsubscribeAllFromEntryPosted(); - Formatter.ResetToDefault(); + LogFormatter.ResetToDefault(); Extension.EnableAll(); } } diff --git a/Its.Log.UnitTests/ExtensionTests.cs b/Its.Log.UnitTests/ExtensionTests.cs index 341301b..1908a36 100644 --- a/Its.Log.UnitTests/ExtensionTests.cs +++ b/Its.Log.UnitTests/ExtensionTests.cs @@ -276,11 +276,11 @@ public void Params_usage_of_formatter_for_ToString_does_not_cause_stack_overflow var count = 0; for (var i = 1; i <= 20; i++) { - Formatter.RecursionLimit = i; + LogFormatter.RecursionLimit = i; using (TestHelper.LogToConsole()) using (Log.Events().Subscribe(e => count++)) { - Log.WithParams(() => new { Formatter.RecursionLimit }).Write("testing..."); + Log.WithParams(() => new { LogFormatter.RecursionLimit }).Write("testing..."); } } diff --git a/Its.Log.UnitTests/FormatterTests.cs b/Its.Log.UnitTests/FormatterTests.cs index 5e29816..862569c 100644 --- a/Its.Log.UnitTests/FormatterTests.cs +++ b/Its.Log.UnitTests/FormatterTests.cs @@ -28,14 +28,14 @@ public class FormatterTests [SetUp] public void SetUp() { - Formatter.ResetToDefault(); + LogFormatter.ResetToDefault(); Extension.EnableAll(); } [Test] public virtual void Generate_creates_a_function_that_emits_the_property_names_and_values_for_a_specific_type() { - var write = Formatter.GenerateForAllMembers(); + var write = LogFormatter.GenerateForAllMembers(); var writer = new StringWriter(); write(new Widget { Name = "Bob" }, writer); @@ -48,7 +48,7 @@ public virtual void Generate_creates_a_function_that_emits_the_property_names_an [Test] public virtual void GenerateFor_creates_a_function_that_emits_the_specified_property_names_and_values_for_a_specific_type() { - Formatter.RegisterForMembers( + LogFormatter.RegisterForMembers( o => o.DateProperty, o => o.StringProperty); @@ -68,7 +68,7 @@ public virtual void GenerateFor_creates_a_function_that_emits_the_specified_prop [Test] public void GenerateForMembers_throws_when_an_expression_is_not_a_MemberExpression() { - var ex = Assert.Throws(() => Formatter.GenerateForMembers( + var ex = Assert.Throws(() => LogFormatter.GenerateForMembers( o => o.DateProperty.ToShortDateString(), o => o.StringProperty)); @@ -82,11 +82,11 @@ public void GenerateForMembers_compiles_expressions_that_are_not_MemberExpressio { var node = new Node { Id = "1", Nodes = new[] { new Node { Id = "1.1" }, new Node { Id = "1.2" } } }; - Formatter.RegisterForAllMembers(); + LogFormatter.RegisterForAllMembers(); Console.WriteLine(node.ToLogString()); - Formatter.GenerateForMembers( + LogFormatter.GenerateForMembers( di => di.Id, di => di.Nodes.Select(n => n.Id).ToLogString()); @@ -104,8 +104,8 @@ public void Recursive_formatter_calls_do_not_cause_exceptions() var widget = new Widget(); widget.Parts = new List { new Part { Widget = widget } }; - Formatter.RegisterForMembers(); - Formatter.RegisterForMembers(); + LogFormatter.RegisterForMembers(); + LogFormatter.RegisterForMembers(); // this should not throw var s = widget.ToLogString(); @@ -128,7 +128,7 @@ public void Formatter_expands_properties_of_ExpandoObjects() dynamic expando = new ExpandoObject(); expando.Name = "socks"; expando.Parts = null; - Formatter.RegisterForMembers(); + LogFormatter.RegisterForMembers(); dynamic expandoString = Log.ToLogString(expando); @@ -142,8 +142,8 @@ public void Formatter_does_not_expand_string() var widget = new Widget(); widget.Parts = new List { new Part { Widget = widget } }; - Formatter.RegisterForMembers(); - Formatter.RegisterForMembers(); + LogFormatter.RegisterForMembers(); + LogFormatter.RegisterForMembers(); // this should not throw var s = widget.ToLogString(); @@ -200,7 +200,7 @@ public void Default_formatter_for_Type_displays_generic_parameter_names_for_open [Test] public void Custom_formatter_for_Type_can_be_registered() { - Formatter.Register(t => t.GUID.ToString()); + LogFormatter.Register(t => t.GUID.ToString()); Assert.That(GetType().ToLogString(), Is.EqualTo(GetType().GUID.ToString())); @@ -226,7 +226,7 @@ public virtual void Formatter_recursively_formats_types_within_IEnumerable() new Widget { Name = "widget z" } }; - Formatter.Register( + LogFormatter.Register( w => w.Name + ", Parts: " + (w.Parts == null ? "0" : w.Parts.Count.ToString())); var formatted = list.ToLogString(); @@ -244,7 +244,7 @@ public virtual void Formatter_truncates_expansion_of_long_IEnumerable() { list.Add("number " + i); } - Formatter.ListExpansionLimit = 4; + LogFormatter.ListExpansionLimit = 4; var formatted = list.ToLogString(); @@ -265,7 +265,7 @@ public virtual void Formatter_iterates_IEnumerable_property_when_its_actual_type [Test] public virtual void Formatter_iterates_IEnumerable_property_when_its_actual_type_is_an_array_of_objects() { - Formatter.RegisterForMembers(); + LogFormatter.RegisterForMembers(); var node = new Node { @@ -289,7 +289,7 @@ public virtual void Formatter_iterates_IEnumerable_property_when_its_actual_type [Test] public virtual void Formatter_iterates_IEnumerable_property_when_its_reflected_type_is_array() { - Formatter.RegisterForMembers(); + LogFormatter.RegisterForMembers(); var node = new Node { @@ -314,7 +314,7 @@ public virtual void Formatter_iterates_IEnumerable_property_when_its_reflected_t public virtual void Default_LogEntry_formatter_can_be_set() { var log = ""; - Formatter.Register(e => string.Format("Here's a log entry!")); + LogFormatter.Register(e => string.Format("Here's a log entry!")); using (Log.Events().Subscribe(e => log += e.ToLogString())) { @@ -328,7 +328,7 @@ public virtual void Default_LogEntry_formatter_can_be_set() public virtual void When_default_LogEntry_formatter_is_changed_it_controls_formatting_of_newly_registered_LogEntry_generic_types() { var log = ""; - Formatter.Register(e => "Here's a log entry!"); + LogFormatter.Register(e => "Here's a log entry!"); using (Log.Events().Subscribe(e => log += e.ToLogString())) { @@ -347,7 +347,7 @@ public void Previously_registered_formatters_for_generic_LogEntry_types_use_upda using (Log.Events().Subscribe(e => log += e.ToLogString())) { Log.Write(() => new { msg = "one" }); - Formatter.Register(e => "Here's a log entry!"); + LogFormatter.Register(e => "Here's a log entry!"); Log.Write(() => new { msg = "two" }); } @@ -359,7 +359,7 @@ public void Previously_registered_formatters_for_generic_LogEntry_types_use_upda [Test] public virtual void GenerateForAllMembers_expands_properties_of_structs() { - var write = Formatter.GenerateForAllMembers(); + var write = LogFormatter.GenerateForAllMembers(); var id = new EntityId("the typename", "the id"); var writer = new StringWriter(); @@ -375,7 +375,7 @@ public virtual void GenerateForAllMembers_expands_properties_of_structs() [Test] public void Static_fields_are_not_written() { - Formatter.RegisterForMembers(); + LogFormatter.RegisterForMembers(); Assert.That(new Widget().ToLogString(), !Contains.Substring("StaticField")); @@ -384,7 +384,7 @@ public void Static_fields_are_not_written() [Test] public void Static_properties_are_not_written() { - Formatter.RegisterForMembers(); + LogFormatter.RegisterForMembers(); Assert.That(new Widget().ToLogString(), !Contains.Substring("StaticProperty")); @@ -393,7 +393,7 @@ public void Static_properties_are_not_written() [Test] public virtual void GenerateForAllMembers_expands_fields_of_objects() { - var write = Formatter.GenerateForAllMembers(); + var write = LogFormatter.GenerateForAllMembers(); var today = DateTime.Today; var tomorrow = DateTime.Today.AddDays(1); var id = new SomeStruct @@ -507,7 +507,7 @@ public void Exception_InnerExceptions_are_included_by_default() public void When_a_property_throws_it_does_not_prevent_other_properties_from_being_written() { var log = new StringBuilder(); - Formatter.RegisterForMembers(); + LogFormatter.RegisterForMembers(); using (Log.Events().Subscribe(e => log.AppendLine(e.ToLogString()))) using (TestHelper.LogToConsole()) @@ -527,7 +527,7 @@ public void Clearing_formatters_reregisters_Params_formatters() using (Log.Events().Subscribe(e => log += e.ToLogString())) { Log.WithParams(() => new { ints = new[] { 1, 2, 3 } }).Write("1"); - Formatter.ResetToDefault(); + LogFormatter.ResetToDefault(); Log.WithParams(() => new { ints = new[] { 4, 5, 6 } }).Write("1"); } @@ -542,7 +542,7 @@ public void Clearing_formatters_reregisters_LogEntry_formatters() using (Log.Events().Subscribe(e => log += e.ToLogString())) { Log.Write(() => new { ints = new[] { 1, 2, 3 } }); - Formatter.ResetToDefault(); + LogFormatter.ResetToDefault(); Log.Write(() => new { ints = new[] { 4, 5, 6 } }); } @@ -553,7 +553,7 @@ public void Clearing_formatters_reregisters_LogEntry_formatters() [Test] public void GenerateForAllMembers_can_include_internal_fields() { - var write = Formatter.GenerateForAllMembers(true); + var write = LogFormatter.GenerateForAllMembers(true); var writer = new StringWriter(); write(new Node { Id = "5" }, writer); @@ -564,7 +564,7 @@ public void GenerateForAllMembers_can_include_internal_fields() [Test] public void GenerateForAllMembers_does_not_include_autoproperty_backing_fields() { - var formatter = Formatter.GenerateForAllMembers(true); + var formatter = LogFormatter.GenerateForAllMembers(true); var writer = new StringWriter(); formatter(new Node(), writer); @@ -577,7 +577,7 @@ public void GenerateForAllMembers_does_not_include_autoproperty_backing_fields() [Test] public void GenerateForAllMembers_can_include_internal_properties() { - var formatter = Formatter.GenerateForAllMembers(true); + var formatter = LogFormatter.GenerateForAllMembers(true); var writer = new StringWriter(); formatter(new Node { Id = "6" }, writer); @@ -592,12 +592,12 @@ public void When_ResetToDefault_is_called_then_default_formatters_are_immediatel var before = logEntry.ToLogString(); - Formatter.Register(e => "hello!"); + LogFormatter.Register(e => "hello!"); Assert.That(logEntry.ToLogString(), Is.Not.EqualTo(before)); - Formatter.ResetToDefault(); + LogFormatter.ResetToDefault(); Assert.That(logEntry.ToLogString(), Is.EqualTo(before)); @@ -620,12 +620,12 @@ public void ToLogString_uses_actual_type_formatter_and_not_compiled_type() bool widgetFormatterCalled = false; bool inheritedWidgetFormatterCalled = false; - Formatter.Register(w => + LogFormatter.Register(w => { widgetFormatterCalled = true; return ""; }); - Formatter.Register(w => + LogFormatter.Register(w => { inheritedWidgetFormatterCalled = true; return ""; @@ -704,16 +704,16 @@ public void RecursionCounter_does_not_share_state_across_threads() [Test] public void Params_formatters_are_not_subject_recursion_limits() { - Formatter.RecursionLimit = 2; + LogFormatter.RecursionLimit = 2; - using (Formatter.RecursionCounter.Enter()) - using (Formatter.RecursionCounter.Enter()) - using (Formatter.RecursionCounter.Enter()) + using (LogFormatter.RecursionCounter.Enter()) + using (LogFormatter.RecursionCounter.Enter()) + using (LogFormatter.RecursionCounter.Enter()) { var p = new Params(); p.SetAccessor(() => new Widget()); var writer = new StringWriter(); - Formatter>.Format(p, writer); + LogFormatter>.Format(p, writer); Assert.That(writer.ToString(), !Contains.Substring(typeof (Widget).ToString())); } } @@ -721,7 +721,7 @@ public void Params_formatters_are_not_subject_recursion_limits() [Test] public void Custom_formatters_can_be_registered_for_types_not_known_until_runtime() { - Formatter.Register( + LogFormatter.Register( type: typeof (FileInfo), formatter: (filInfo, writer) => writer.Write("hello")); @@ -740,11 +740,11 @@ public void Generated_formatters_can_be_registered_for_types_not_known_until_run StringProperty = "oh hai", UriProperty = new Uri("http://blammo.com") }; - var reference = Formatter.GenerateForAllMembers(); + var reference = LogFormatter.GenerateForAllMembers(); var writer = new StringWriter(); reference(obj, writer); - Formatter.RegisterForAllMembers(typeof (SomethingWithLotsOfProperties)); + LogFormatter.RegisterForAllMembers(typeof (SomethingWithLotsOfProperties)); Assert.That(obj.ToLogString(), Is.EqualTo(writer.ToString())); @@ -782,8 +782,8 @@ public void When_JArray_is_formatted_it_outputs_its_string_representation() [Test] public void ListExpansionLimit_can_be_specified_per_type() { - Formatter>.ListExpansionLimit = 1000; - Formatter.ListExpansionLimit = 4; + LogFormatter>.ListExpansionLimit = 1000; + LogFormatter.ListExpansionLimit = 4; var dictionary = new Dictionary { { "zero", 0 }, @@ -809,7 +809,7 @@ public void ListExpansionLimit_can_be_specified_per_type() [Test] public void FormatAllTypes_allows_formatters_to_be_registered_on_fly_for_all_types() { - Formatter.AutoGenerateForType = t => true; + LogFormatter.AutoGenerateForType = t => true; Assert.That(new FileInfo(@"c:\temp\foo.txt").ToLogString(), Is.StringContaining(@"DirectoryName = c:\temp")); @@ -825,7 +825,7 @@ public void FormatAllTypes_allows_formatters_to_be_registered_on_fly_for_all_typ public void FormatAllTypes_does_not_reregister_formatters_for_types_having_special_default_formatters() { var log = ""; - Formatter.AutoGenerateForType = t => true; + LogFormatter.AutoGenerateForType = t => true; using (Log.Events().Subscribe(e => log += e.ToLogString())) { Log.Write(() => "hello"); diff --git a/Its.Log.UnitTests/JsonSerializationTests.cs b/Its.Log.UnitTests/JsonSerializationTests.cs index 582a45a..061e609 100644 --- a/Its.Log.UnitTests/JsonSerializationTests.cs +++ b/Its.Log.UnitTests/JsonSerializationTests.cs @@ -20,7 +20,7 @@ public class JsonSerializationTests [TearDown] public void TearDown() { - Formatter.ResetToDefault(); + LogFormatter.ResetToDefault(); } [Ignore("Scenario under development")] @@ -35,7 +35,7 @@ public void All_Log_entries_can_be_formatted_to_JSON_by_registering_a_formatter_ var log = ""; var thereWereErrors = false; - Formatter.Register((entry, writer) => + LogFormatter.Register((entry, writer) => { serializer.Serialize(writer, entry); writer.WriteLine(); @@ -65,7 +65,7 @@ public void All_Log_entries_can_be_formatted_to_JSON_by_setting_Formatter_Defaul var log = ""; var thereWereErrors = false; - Formatter.Default = (obj, writer) => + LogFormatter.Default = (obj, writer) => { serializer.Serialize(writer, obj); writer.WriteLine(); @@ -100,7 +100,7 @@ public void ToLogString_can_be_made_to_output_JSON() ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; - Formatter.Default = (o, writer) => serializer.Serialize(writer, o); + LogFormatter.Default = (o, writer) => serializer.Serialize(writer, o); Assert.That(new FileInfo(@"c:\temp\1.log").ToLogString(), Is.EqualTo("{\"$type\":\"System.IO.FileInfo, mscorlib\",\"OriginalPath\":\"c:\\\\temp\\\\1.log\",\"FullPath\":\"c:\\\\temp\\\\1.log\"}")); @@ -138,7 +138,7 @@ public void Perf_benchmarks_for_JSON_versus_formatter() TypeNameHandling = TypeNameHandling.All, ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; - Formatter.Register((entry, writer) => + LogFormatter.Register((entry, writer) => { serializer.Serialize(writer, entry); writer.WriteLine(); diff --git a/Its.Log.UnitTests/LogActivityTests.cs b/Its.Log.UnitTests/LogActivityTests.cs index 1da0b9f..86ecb0c 100644 --- a/Its.Log.UnitTests/LogActivityTests.cs +++ b/Its.Log.UnitTests/LogActivityTests.cs @@ -539,7 +539,7 @@ public void When_Confirm_is_called_with_no_args_and_not_required_then_it_is_indi public void When_there_are_a_large_number_of_Confirm_calls_they_are_not_truncated_in_the_log_output() { var log = new List(); - Formatter.ListExpansionLimit = 4; + LogFormatter.ListExpansionLimit = 4; using (Log.Events().Subscribe(log.Add)) using (var activity = Log.Enter(() => { })) @@ -577,7 +577,7 @@ public async Task When_Confirm_delegate_throws_then_the_exception_is_handled() public void Activity_Confirm_outputs_include_timings() { var log = new List(); - Formatter.ListExpansionLimit = 4; + LogFormatter.ListExpansionLimit = 4; using (Log.Events().Subscribe(log.Add)) using (var activity = Log.Enter(() => { })) diff --git a/Its.Log.UnitTests/LogEntryTests.cs b/Its.Log.UnitTests/LogEntryTests.cs index d106117..bede151 100644 --- a/Its.Log.UnitTests/LogEntryTests.cs +++ b/Its.Log.UnitTests/LogEntryTests.cs @@ -22,7 +22,7 @@ public class LogEntryTests [SetUp] public void SetUp() { - Formatter.ResetToDefault(); + LogFormatter.ResetToDefault(); } [Test] @@ -144,7 +144,7 @@ public void Write_passing_func_that_returns_anonymous_type_describes_subject_pro [Test] public virtual void Write_passing_func_that_returns_anonymous_type_describes_subject_properties_using_formatter() { - Formatter.RegisterForAllMembers(); + LogFormatter.RegisterForAllMembers(); var log = ""; var widget = new Widget { Name = "Hula Hoop" }; diff --git a/Its.Log.UnitTests/MultiLineTextFormatterTests.cs b/Its.Log.UnitTests/MultiLineTextFormatterTests.cs index a8892c5..d20be8c 100644 --- a/Its.Log.UnitTests/MultiLineTextFormatterTests.cs +++ b/Its.Log.UnitTests/MultiLineTextFormatterTests.cs @@ -19,9 +19,9 @@ public class MultiLineTextFormatterTests public void SetUp() { Log.UnsubscribeAllFromEntryPosted(); - Formatter.ResetToDefault(); + LogFormatter.ResetToDefault(); Extension.EnableAll(); - Formatter.TextFormatter = new MultiLineTextFormatter(); + LogFormatter.TextFormatter = new MultiLineTextFormatter(); MultiLineTextFormatter.DebugMode = false; log = ""; @@ -32,7 +32,7 @@ public void SetUp() public void TearDown() { Console.WriteLine(log); - Formatter.TextFormatter = new SingleLineTextFormatter(); + LogFormatter.TextFormatter = new SingleLineTextFormatter(); subscription.Dispose(); } diff --git a/Its.Log.UnitTests/TextFormatterTests.cs b/Its.Log.UnitTests/TextFormatterTests.cs index ff5ca20..3949fb7 100644 --- a/Its.Log.UnitTests/TextFormatterTests.cs +++ b/Its.Log.UnitTests/TextFormatterTests.cs @@ -18,14 +18,14 @@ public class TextFormatterTests public void SetUp() { Log.UnsubscribeAllFromEntryPosted(); - Formatter.ResetToDefault(); + LogFormatter.ResetToDefault(); Extension.EnableAll(); } [TearDown] public void TearDown() { - Formatter.TextFormatter = new SingleLineTextFormatter(); + LogFormatter.TextFormatter = new SingleLineTextFormatter(); } private void WriteSomething() @@ -33,7 +33,7 @@ private void WriteSomething() Log.EntryPosted += (sender, e) => Console.WriteLine(e.LogEntry.ToLogString()); // again, isolating just the parts that are specific to boundary logging: - Formatter.RegisterForMembers( + LogFormatter.RegisterForMembers( e => e.Message, e => e.Params, e => e.ElapsedMilliseconds); @@ -50,7 +50,7 @@ private void WriteSomething() [Test] public void MultiLine() { - Formatter.TextFormatter = new MultiLineTextFormatter(); + LogFormatter.TextFormatter = new MultiLineTextFormatter(); WriteSomething(); // TODO (SingleLineTextFormatter) write test Assert.Fail("Test not written yet."); diff --git a/Its.Log/ExceptionExtensions.cs b/Its.Log/ExceptionExtensions.cs index b93d9f8..29b3f24 100644 --- a/Its.Log/ExceptionExtensions.cs +++ b/Its.Log/ExceptionExtensions.cs @@ -112,7 +112,7 @@ public static TException WithData(this TException exception, object key = ExceptionDataKey + index++; } - exception.Data[key] = Formatter.Format(obj); + exception.Data[key] = LogFormatter.Format(obj); return exception; } diff --git a/Its.Log/Extensions/EventLogInfo.cs b/Its.Log/Extensions/EventLogInfo.cs index d5d953f..cee9798 100644 --- a/Its.Log/Extensions/EventLogInfo.cs +++ b/Its.Log/Extensions/EventLogInfo.cs @@ -13,8 +13,8 @@ public class EventLogInfo { static EventLogInfo() { - Formatter.Clearing += (o, e) => Formatter.RegisterForAllMembers(); - Formatter.RegisterForAllMembers(); + LogFormatter.Clearing += (o, e) => LogFormatter.RegisterForAllMembers(); + LogFormatter.RegisterForAllMembers(); } /// diff --git a/Its.Log/Extensions/Params{T}.cs b/Its.Log/Extensions/Params{T}.cs index 28fe2e5..74c4e38 100644 --- a/Its.Log/Extensions/Params{T}.cs +++ b/Its.Log/Extensions/Params{T}.cs @@ -17,7 +17,7 @@ public class Params : Params static Params() { - Formatter.Clearing += (o, e) => RegisterFormatters(); + LogFormatter.Clearing += (o, e) => RegisterFormatters(); RegisterFormatters(); } @@ -25,15 +25,15 @@ private static void RegisterFormatters() { if (ShouldGenerateFormatter()) { - Formatter.RegisterForMembers(); + LogFormatter.RegisterForMembers(); } formatter = (p, writer) => writer.Write(p.paramsAccessor().ToLogString()); - Formatter>.Default = formatter; + LogFormatter>.Default = formatter; } private static bool ShouldGenerateFormatter() { - if (Formatter.IsCustom) + if (LogFormatter.IsCustom) { return false; } diff --git a/Its.Log/Extensions/Telemetry.cs b/Its.Log/Extensions/Telemetry.cs index 085564b..0a7028a 100644 --- a/Its.Log/Extensions/Telemetry.cs +++ b/Its.Log/Extensions/Telemetry.cs @@ -20,7 +20,7 @@ public sealed class Telemetry : IApplyOnExit /// static Telemetry() { - Formatter.RegisterForAllMembers(); + LogFormatter.RegisterForAllMembers(); } /// diff --git a/Its.Log/FormatterSet.cs b/Its.Log/FormatterSet.cs index afb34b7..b0478e6 100644 --- a/Its.Log/FormatterSet.cs +++ b/Its.Log/FormatterSet.cs @@ -23,11 +23,11 @@ public int ListExpansionLimit { get { - return Formatter.ListExpansionLimit; + return LogFormatter.ListExpansionLimit; } set { - Formatter.ListExpansionLimit = value; + LogFormatter.ListExpansionLimit = value; } } @@ -41,11 +41,11 @@ public string NullString { get { - return Formatter.NullString; + return LogFormatter.NullString; } set { - Formatter.NullString = value ?? ""; + LogFormatter.NullString = value ?? ""; } } @@ -59,18 +59,18 @@ public int RecursionLimit { get { - return Formatter.RecursionLimit; + return LogFormatter.RecursionLimit; } set { - Formatter.RecursionLimit = value; + LogFormatter.RecursionLimit = value; } } /// /// Clears all formatters in the . /// - public void Clear() => Formatter.ResetToDefault(); + public void Clear() => LogFormatter.ResetToDefault(); /// /// Dynamically generates a formatter function that will output the specified properties of objects of type as a string. @@ -82,10 +82,10 @@ public int RecursionLimit /// public Func CreateFormatterFor(params Expression>[] members) { - var write = Formatter.GenerateForMembers(members); + var write = LogFormatter.GenerateForMembers(members); return t => { - var writer = Formatter.CreateWriter(); + var writer = LogFormatter.CreateWriter(); write(t, writer); return writer.ToString(); }; @@ -98,10 +98,10 @@ public Func CreateFormatterFor(params Expression>[ /// A formatter function. public Func CreatePropertiesFormatter(bool includeInternals = false) { - var write = Formatter.GenerateForAllMembers(includeInternals); + var write = LogFormatter.GenerateForAllMembers(includeInternals); return t => { - var writer = Formatter.CreateWriter(); + var writer = LogFormatter.CreateWriter(); write(t, writer); return writer.ToString(); }; @@ -121,7 +121,7 @@ public Func CreatePropertiesFormatter(bool includeInternals = fals /// A function that returs a string representation of instances of . public FormatterSet RegisterFormatter(Func format) { - Formatter.Register(format); + LogFormatter.Register(format); return this; } diff --git a/Its.Log/Its.Log.csproj b/Its.Log/Its.Log.csproj index 8ad01ca..a5621e9 100644 --- a/Its.Log/Its.Log.csproj +++ b/Its.Log/Its.Log.csproj @@ -101,9 +101,9 @@ - + - + diff --git a/Its.Log/Log.cs b/Its.Log/Log.cs index da5c8fd..9257ecf 100644 --- a/Its.Log/Log.cs +++ b/Its.Log/Log.cs @@ -183,7 +183,7 @@ public IDisposable Subscribe(IObserver observer) /// Formats an object to a string based on the framework configuration for its . /// /// The object to be formatted. - public static string ToLogString(this T obj) => Formatter.Format(obj); + public static string ToLogString(this T obj) => LogFormatter.Format(obj); /// /// Gets an observable sequence of telemetry events. diff --git a/Its.Log/LogActivity.cs b/Its.Log/LogActivity.cs index e6157b7..c5456c4 100644 --- a/Its.Log/LogActivity.cs +++ b/Its.Log/LogActivity.cs @@ -243,7 +243,7 @@ private class ConfirmationList : IEnumerable static ConfirmationList() { - Formatter.ListExpansionLimit = 100; + LogFormatter.ListExpansionLimit = 100; } public void Add(Confirmation confirmation) diff --git a/Its.Log/LogEntry.cs b/Its.Log/LogEntry.cs index 9d5e319..269e3d4 100644 --- a/Its.Log/LogEntry.cs +++ b/Its.Log/LogEntry.cs @@ -286,7 +286,7 @@ Subject is T /// /// A that represents the current . /// - public override string ToString() => Formatter.Format(this); + public override string ToString() => LogFormatter.Format(this); /// /// Adds info to the . diff --git a/Its.Log/LogEntry{T}.cs b/Its.Log/LogEntry{T}.cs index 46d8aec..06481fa 100644 --- a/Its.Log/LogEntry{T}.cs +++ b/Its.Log/LogEntry{T}.cs @@ -16,7 +16,7 @@ public class LogEntry : LogEntry static LogEntry() { - Formatter.Clearing += (o, e) => RegisterFormatters(); + LogFormatter.Clearing += (o, e) => RegisterFormatters(); RegisterFormatters(); } @@ -24,11 +24,11 @@ internal static void RegisterFormatters() { if (isAnonymous) { - Formatter.RegisterForMembers(); + LogFormatter.RegisterForMembers(); } // register the default formatter for this LogEntry type, which should be the non-generic LogEntry formatter - Formatter>.Register((e, writer) => Formatter.Format(e, writer)); + LogFormatter>.Register((e, writer) => LogFormatter.Format(e, writer)); } /// @@ -65,7 +65,7 @@ public LogEntry(Func subjectAccessor) : base(subjectAccessor.GetAnonym /// public override string ToString() { - return Formatter>.Format(this); + return LogFormatter>.Format(this); } /// diff --git a/Its.Log/Formatter.cs b/Its.Log/LogFormatter.cs similarity index 84% rename from Its.Log/Formatter.cs rename to Its.Log/LogFormatter.cs index 79461f0..12f5640 100644 --- a/Its.Log/Formatter.cs +++ b/Its.Log/LogFormatter.cs @@ -1,411 +1,411 @@ -// Copyright (c) Microsoft. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System; -using System.Collections; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Dynamic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using Its.Log.Instrumentation.Extensions; - -namespace Its.Log.Instrumentation -{ - /// - /// Provides methods for formatting objects into log strings. - /// - public static class Formatter - { - private static Func autoGenerateForType = t => false; - private static int defaultListExpansionLimit; - private static int recursionLimit; - internal static readonly RecursionCounter RecursionCounter = new RecursionCounter(); - - private static readonly ConcurrentDictionary> genericFormatters = new ConcurrentDictionary>(); - - /// - /// Initializes the class. - /// - static Formatter() - { - ResetToDefault(); - } - - /// - /// A factory function called to get a TextWriter for writing out log-formatted objects. - /// - public static Func CreateWriter = () => new StringWriter(CultureInfo.InvariantCulture); - - internal static ILogTextFormatter TextFormatter = new SingleLineTextFormatter(); - - internal static Action Default { get; set; } - - /// - /// Gets or sets the limit to the number of items that will be written out in detail from an IEnumerable sequence. - /// - /// - /// The list expansion limit. - /// - public static int ListExpansionLimit - { - get - { - return defaultListExpansionLimit; - } - set - { - if (value < 0) - { - throw new ArgumentException("ListExpansionLimit must be at least 0."); - } - defaultListExpansionLimit = value; - } - } - - /// - /// Gets or sets the string that will be written out for null items. - /// - /// - /// The null string. - /// - public static string NullString; - - /// - /// Gets or sets the limit to how many levels the formatter will recurse into an object graph. - /// - /// - /// The recursion limit. - /// - public static int RecursionLimit - { - get - { - return recursionLimit; - } - set - { - if (value < 0) - { - throw new ArgumentException("RecursionLimit must be at least 0."); - } - recursionLimit = value; - } - } - - internal static event EventHandler Clearing; - - /// - /// Resets all formatters and formatter settings to their default values. - /// - public static void ResetToDefault() - { - Clearing?.Invoke(null, EventArgs.Empty); - - AutoGenerateForType = t => false; - ListExpansionLimit = 10; - RecursionLimit = 6; - NullString = "[null]"; - - RegisterDefaults(); - Default = null; - } - - /// - /// Gets or sets a delegate that is checked when a type is being formatted that not previously been formatted and has no custom formatting rules set. If this delegate returns true, then is called for that type. - /// - /// - /// The type being formatted. - /// - /// value - public static Func AutoGenerateForType - { - get - { - return autoGenerateForType; - } - set - { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - autoGenerateForType = value; - } - } - - /// - /// Formats the specified object using a registered formatter function if available. - /// - /// The object to be formatted. - /// A string representation of the object. - public static string Format(object obj) - { - var writer = CreateWriter(); - FormatTo(obj, writer); - return writer.ToString(); - } - - /// - /// Writes a formatted representation of the object to the specified writer. - /// - /// The type of the object being written. - /// The object to write. - /// The writer. - public static void FormatTo(this T obj, TextWriter writer) - { - var custom = Default; - if (custom != null) - { - custom(obj, writer); - return; - } - - if (obj != null) - { - var actualType = obj.GetType(); - if (typeof (T) != actualType) - { - // in some cases the generic parameter is Object but the object is of a more specific type, in which case get or add a cached accessor to the more specific Formatter.Format method - var genericFormatter = - genericFormatters.GetOrAdd(actualType, - GetGenericFormatterMethod); - genericFormatter(obj, writer); - return; - } - } - - Formatter.Format(obj, writer); - } - - internal static Action GetGenericFormatterMethod(this Type type) - { - var methodInfo = typeof (Formatter<>) - .MakeGenericType(type) - .GetMethod("Format", new[] { type, typeof (TextWriter) }); - - var targetParam = Expression.Parameter(typeof (object), "target"); - var writerParam = Expression.Parameter(typeof (TextWriter), "target"); - - var methodCallExpr = Expression.Call(null, methodInfo, - Expression.Convert(targetParam, type), - writerParam); - - return Expression.Lambda>(methodCallExpr, targetParam, writerParam).Compile(); - } - - // TODO: (Formatter) make Join methods public and expose an override for iteration limit - - internal static void Join( - IEnumerable list, - TextWriter writer, - int? listExpansionLimit = null) => - Join(list.Cast(), writer, listExpansionLimit); - - - internal static void Join(IEnumerable list, - TextWriter writer, - int? listExpansionLimit = null) - { - if (list == null) - { - writer.Write(NullString); - return; - } - - var i = 0; - - TextFormatter.WriteStartSequence(writer); - - listExpansionLimit = listExpansionLimit ?? Formatter.ListExpansionLimit; - - using (var enumerator = list.GetEnumerator()) - { - while (enumerator.MoveNext()) - { - if (i < listExpansionLimit) - { - // write out another item in the list - if (i > 0) - { - TextFormatter.WriteSequenceDelimiter(writer); - } - - i++; - - TextFormatter.WriteStartSequenceItem(writer); - - enumerator.Current.FormatTo(writer); - } - else - { - // write out just a count of the remaining items in the list - var difference = list.Count() - i; - if (difference > 0) - { - writer.Write(" ... ("); - writer.Write(difference); - writer.Write(" more)"); - } - break; - } - } - } - - TextFormatter.WriteEndSequence(writer); - } - - /// - /// Registers a formatter to be used when formatting instances of a specified type. - /// - public static void Register(Type type, Action formatter) - { - var genericRegisterMethod = typeof (Formatter<>) - .MakeGenericType(type) - .GetMethod("Register", new[] { typeof (Action<,>).MakeGenericType(type, typeof (TextWriter)) }); - - genericRegisterMethod.Invoke(null, new object[] { formatter }); - } - - /// - /// Registers a formatter to be used when formatting instances of a specified type. - /// - public static void RegisterForAllMembers(Type type, bool includeInternals = false) - { - var genericRegisterMethod = typeof (Formatter<>) - .MakeGenericType(type) - .GetMethod("RegisterForAllMembers"); - - genericRegisterMethod.Invoke(null, new object[] { includeInternals }); - } - - private static void RegisterDefaults() - { - RegisterDefaultLogEntryFormatters(); - - // common primitive types - Formatter.Default = (value, writer) => writer.Write(value); - Formatter.Default = (value, writer) => writer.Write(value); - Formatter.Default = (value, writer) => writer.Write(value); - Formatter.Default = (value, writer) => writer.Write(value); - Formatter.Default = (value, writer) => writer.Write(value); - Formatter.Default = (value, writer) => writer.Write(value); - Formatter.Default = (value, writer) => writer.Write(value); - Formatter.Default = (value, writer) => writer.Write(value); - Formatter.Default = (value, writer) => writer.Write(value); - - Formatter.Default = (value, writer) => writer.Write(value.ToString("u")); - Formatter.Default = (value, writer) => writer.Write(value.ToString("u")); - - // common complex types - Formatter>.Default = (pair, writer) => - { - writer.Write(pair.Key); - TextFormatter.WriteNameValueDelimiter(writer); - pair.Value.FormatTo(writer); - }; - - Formatter.Default = (pair, writer) => - { - writer.Write(pair.Key); - TextFormatter.WriteNameValueDelimiter(writer); - pair.Value.FormatTo(writer); - }; - - Formatter.Default = (expando, writer) => - { - TextFormatter.WriteStartObject(writer); - KeyValuePair[] pairs = expando.ToArray(); - int length = pairs.Length; - for (var i = 0; i < length; i++) - { - KeyValuePair pair = pairs[i]; - writer.Write(pair.Key); - TextFormatter.WriteNameValueDelimiter(writer); - pair.Value.FormatTo(writer); - if (i < length - 1) - { - TextFormatter.WritePropertyDelimiter(writer); - } - } - TextFormatter.WriteEndObject(writer); - }; - - Formatter.Default = (type, writer) => - { - var typeName = type.Name; - if (typeName.Contains("`") && !type.IsAnonymous()) - { - writer.Write(typeName.Remove(typeName.IndexOf('`'))); - writer.Write("<"); - var genericArguments = type.GetGenericArguments(); - - for (var i = 0; i < genericArguments.Length; i++) - { - Formatter.Default(genericArguments[i], writer); - if (i < genericArguments.Length - 1) - { - writer.Write(","); - } - } - writer.Write(">"); - } - else - { - writer.Write(typeName); - } - }; - - // an additional formatter is needed since typeof(Type) == System.RuntimeType, which is not public - // ReSharper disable once PossibleMistakenCallToGetType.2 - Register(typeof (Type).GetType(), - (obj, writer) => Formatter.Default((Type) obj, writer)); - - // supply a formatter for String so that it will not be iterated - Formatter.Default = (s, writer) => writer.Write(s); - - // extensions - Formatter.Default = Formatter.GenerateForAllMembers(); - Formatter.Default = Formatter.GenerateForAllMembers(); - - // Newtonsoft.Json types -- these implement IEnumerable and their default output is not useful, so use their default ToString - TryRegisterDefault("Newtonsoft.Json.Linq.JArray, Newtonsoft.Json", (obj, writer) => writer.Write(obj)); - TryRegisterDefault("Newtonsoft.Json.Linq.JObject, Newtonsoft.Json", (obj, writer) => writer.Write(obj)); - } - - private static void TryRegisterDefault(string typeName, Action write) - { - try - { - var type = Type.GetType(typeName); - if (type != null) - { - Register(type, write); - } - } - catch (Exception exception) - { - if (exception.ShouldThrow()) - { - throw; - } - Log.Write(() => exception, $"An exception occurred while trying to register a formatter for type '{typeName}'."); - } - } - - internal static void RegisterDefaultLogEntryFormatters() => - Formatter.RegisterForMembers( - e => e.CallingType, - e => e.CallingMethod, - e => e.ElapsedMilliseconds, - e => e.Category, - e => e.ExceptionId, - e => e.Message, - e => e.Subject, - e => e.TimeStamp, - e => e.Params); - } +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Dynamic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using Its.Log.Instrumentation.Extensions; + +namespace Its.Log.Instrumentation +{ + /// + /// Provides methods for formatting objects into log strings. + /// + public static class LogFormatter + { + private static Func autoGenerateForType = t => false; + private static int defaultListExpansionLimit; + private static int recursionLimit; + internal static readonly RecursionCounter RecursionCounter = new RecursionCounter(); + + private static readonly ConcurrentDictionary> genericFormatters = new ConcurrentDictionary>(); + + /// + /// Initializes the class. + /// + static LogFormatter() + { + ResetToDefault(); + } + + /// + /// A factory function called to get a TextWriter for writing out log-formatted objects. + /// + public static Func CreateWriter = () => new StringWriter(CultureInfo.InvariantCulture); + + internal static ILogTextFormatter TextFormatter = new SingleLineTextFormatter(); + + internal static Action Default { get; set; } + + /// + /// Gets or sets the limit to the number of items that will be written out in detail from an IEnumerable sequence. + /// + /// + /// The list expansion limit. + /// + public static int ListExpansionLimit + { + get + { + return defaultListExpansionLimit; + } + set + { + if (value < 0) + { + throw new ArgumentException("ListExpansionLimit must be at least 0."); + } + defaultListExpansionLimit = value; + } + } + + /// + /// Gets or sets the string that will be written out for null items. + /// + /// + /// The null string. + /// + public static string NullString; + + /// + /// Gets or sets the limit to how many levels the formatter will recurse into an object graph. + /// + /// + /// The recursion limit. + /// + public static int RecursionLimit + { + get + { + return recursionLimit; + } + set + { + if (value < 0) + { + throw new ArgumentException("RecursionLimit must be at least 0."); + } + recursionLimit = value; + } + } + + internal static event EventHandler Clearing; + + /// + /// Resets all formatters and formatter settings to their default values. + /// + public static void ResetToDefault() + { + Clearing?.Invoke(null, EventArgs.Empty); + + AutoGenerateForType = t => false; + ListExpansionLimit = 10; + RecursionLimit = 6; + NullString = "[null]"; + + RegisterDefaults(); + Default = null; + } + + /// + /// Gets or sets a delegate that is checked when a type is being formatted that not previously been formatted and has no custom formatting rules set. If this delegate returns true, then is called for that type. + /// + /// + /// The type being formatted. + /// + /// value + public static Func AutoGenerateForType + { + get + { + return autoGenerateForType; + } + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + autoGenerateForType = value; + } + } + + /// + /// Formats the specified object using a registered formatter function if available. + /// + /// The object to be formatted. + /// A string representation of the object. + public static string Format(object obj) + { + var writer = CreateWriter(); + FormatTo(obj, writer); + return writer.ToString(); + } + + /// + /// Writes a formatted representation of the object to the specified writer. + /// + /// The type of the object being written. + /// The object to write. + /// The writer. + public static void FormatTo(this T obj, TextWriter writer) + { + var custom = Default; + if (custom != null) + { + custom(obj, writer); + return; + } + + if (obj != null) + { + var actualType = obj.GetType(); + if (typeof (T) != actualType) + { + // in some cases the generic parameter is Object but the object is of a more specific type, in which case get or add a cached accessor to the more specific Formatter.Format method + var genericFormatter = + genericFormatters.GetOrAdd(actualType, + GetGenericFormatterMethod); + genericFormatter(obj, writer); + return; + } + } + + LogFormatter.Format(obj, writer); + } + + internal static Action GetGenericFormatterMethod(this Type type) + { + var methodInfo = typeof (LogFormatter<>) + .MakeGenericType(type) + .GetMethod("Format", new[] { type, typeof (TextWriter) }); + + var targetParam = Expression.Parameter(typeof (object), "target"); + var writerParam = Expression.Parameter(typeof (TextWriter), "target"); + + var methodCallExpr = Expression.Call(null, methodInfo, + Expression.Convert(targetParam, type), + writerParam); + + return Expression.Lambda>(methodCallExpr, targetParam, writerParam).Compile(); + } + + // TODO: (Formatter) make Join methods public and expose an override for iteration limit + + internal static void Join( + IEnumerable list, + TextWriter writer, + int? listExpansionLimit = null) => + Join(list.Cast(), writer, listExpansionLimit); + + + internal static void Join(IEnumerable list, + TextWriter writer, + int? listExpansionLimit = null) + { + if (list == null) + { + writer.Write(NullString); + return; + } + + var i = 0; + + TextFormatter.WriteStartSequence(writer); + + listExpansionLimit = listExpansionLimit ?? LogFormatter.ListExpansionLimit; + + using (var enumerator = list.GetEnumerator()) + { + while (enumerator.MoveNext()) + { + if (i < listExpansionLimit) + { + // write out another item in the list + if (i > 0) + { + TextFormatter.WriteSequenceDelimiter(writer); + } + + i++; + + TextFormatter.WriteStartSequenceItem(writer); + + enumerator.Current.FormatTo(writer); + } + else + { + // write out just a count of the remaining items in the list + var difference = list.Count() - i; + if (difference > 0) + { + writer.Write(" ... ("); + writer.Write(difference); + writer.Write(" more)"); + } + break; + } + } + } + + TextFormatter.WriteEndSequence(writer); + } + + /// + /// Registers a formatter to be used when formatting instances of a specified type. + /// + public static void Register(Type type, Action formatter) + { + var genericRegisterMethod = typeof (LogFormatter<>) + .MakeGenericType(type) + .GetMethod("Register", new[] { typeof (Action<,>).MakeGenericType(type, typeof (TextWriter)) }); + + genericRegisterMethod.Invoke(null, new object[] { formatter }); + } + + /// + /// Registers a formatter to be used when formatting instances of a specified type. + /// + public static void RegisterForAllMembers(Type type, bool includeInternals = false) + { + var genericRegisterMethod = typeof (LogFormatter<>) + .MakeGenericType(type) + .GetMethod("RegisterForAllMembers"); + + genericRegisterMethod.Invoke(null, new object[] { includeInternals }); + } + + private static void RegisterDefaults() + { + RegisterDefaultLogEntryFormatters(); + + // common primitive types + LogFormatter.Default = (value, writer) => writer.Write(value); + LogFormatter.Default = (value, writer) => writer.Write(value); + LogFormatter.Default = (value, writer) => writer.Write(value); + LogFormatter.Default = (value, writer) => writer.Write(value); + LogFormatter.Default = (value, writer) => writer.Write(value); + LogFormatter.Default = (value, writer) => writer.Write(value); + LogFormatter.Default = (value, writer) => writer.Write(value); + LogFormatter.Default = (value, writer) => writer.Write(value); + LogFormatter.Default = (value, writer) => writer.Write(value); + + LogFormatter.Default = (value, writer) => writer.Write(value.ToString("u")); + LogFormatter.Default = (value, writer) => writer.Write(value.ToString("u")); + + // common complex types + LogFormatter>.Default = (pair, writer) => + { + writer.Write(pair.Key); + TextFormatter.WriteNameValueDelimiter(writer); + pair.Value.FormatTo(writer); + }; + + LogFormatter.Default = (pair, writer) => + { + writer.Write(pair.Key); + TextFormatter.WriteNameValueDelimiter(writer); + pair.Value.FormatTo(writer); + }; + + LogFormatter.Default = (expando, writer) => + { + TextFormatter.WriteStartObject(writer); + KeyValuePair[] pairs = expando.ToArray(); + int length = pairs.Length; + for (var i = 0; i < length; i++) + { + KeyValuePair pair = pairs[i]; + writer.Write(pair.Key); + TextFormatter.WriteNameValueDelimiter(writer); + pair.Value.FormatTo(writer); + if (i < length - 1) + { + TextFormatter.WritePropertyDelimiter(writer); + } + } + TextFormatter.WriteEndObject(writer); + }; + + LogFormatter.Default = (type, writer) => + { + var typeName = type.Name; + if (typeName.Contains("`") && !type.IsAnonymous()) + { + writer.Write(typeName.Remove(typeName.IndexOf('`'))); + writer.Write("<"); + var genericArguments = type.GetGenericArguments(); + + for (var i = 0; i < genericArguments.Length; i++) + { + LogFormatter.Default(genericArguments[i], writer); + if (i < genericArguments.Length - 1) + { + writer.Write(","); + } + } + writer.Write(">"); + } + else + { + writer.Write(typeName); + } + }; + + // an additional formatter is needed since typeof(Type) == System.RuntimeType, which is not public + // ReSharper disable once PossibleMistakenCallToGetType.2 + Register(typeof (Type).GetType(), + (obj, writer) => LogFormatter.Default((Type) obj, writer)); + + // supply a formatter for String so that it will not be iterated + LogFormatter.Default = (s, writer) => writer.Write(s); + + // extensions + LogFormatter.Default = LogFormatter.GenerateForAllMembers(); + LogFormatter.Default = LogFormatter.GenerateForAllMembers(); + + // Newtonsoft.Json types -- these implement IEnumerable and their default output is not useful, so use their default ToString + TryRegisterDefault("Newtonsoft.Json.Linq.JArray, Newtonsoft.Json", (obj, writer) => writer.Write(obj)); + TryRegisterDefault("Newtonsoft.Json.Linq.JObject, Newtonsoft.Json", (obj, writer) => writer.Write(obj)); + } + + private static void TryRegisterDefault(string typeName, Action write) + { + try + { + var type = Type.GetType(typeName); + if (type != null) + { + Register(type, write); + } + } + catch (Exception exception) + { + if (exception.ShouldThrow()) + { + throw; + } + Log.Write(() => exception, $"An exception occurred while trying to register a formatter for type '{typeName}'."); + } + } + + internal static void RegisterDefaultLogEntryFormatters() => + LogFormatter.RegisterForMembers( + e => e.CallingType, + e => e.CallingMethod, + e => e.ElapsedMilliseconds, + e => e.Category, + e => e.ExceptionId, + e => e.Message, + e => e.Subject, + e => e.TimeStamp, + e => e.Params); + } } \ No newline at end of file diff --git a/Its.Log/Formatter{T}.cs b/Its.Log/LogFormatter{T}.cs similarity index 85% rename from Its.Log/Formatter{T}.cs rename to Its.Log/LogFormatter{T}.cs index 8968e8a..36a4d32 100644 --- a/Its.Log/Formatter{T}.cs +++ b/Its.Log/LogFormatter{T}.cs @@ -1,298 +1,298 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections; -using System.IO; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using Its.Log.Instrumentation.Extensions; - -namespace Its.Log.Instrumentation -{ - /// - /// Provides formatting functionality for a specific type. - /// - /// The type for which formatting is provided. - public static class Formatter - { - private static readonly bool isAnonymous = typeof (T).IsAnonymous(); - private static Action Custom; - private static readonly bool isException = typeof (Exception).IsAssignableFrom(typeof (T)); - private static readonly bool writeHeader = !isAnonymous && typeof (T).BaseType != typeof (Params); - private static readonly bool isLogEntry = typeof (LogEntry).IsAssignableFrom(typeof (T)); - private static int? listExpansionLimit; - - /// - /// Initializes the class. - /// - static Formatter() - { - Formatter.Clearing += (o, e) => Custom = null; - } - - /// - /// Gets or sets the default formatter for type . - /// - public static Action Default { get; set; } = WriteDefault; - - /// - /// Generates a formatter action that will write out all properties and fields from instances of type . - /// - /// if set to true include internal and private members. - public static Action GenerateForAllMembers(bool includeInternals = false) => - CreateCustom(typeof (T).GetAllMembers(includeInternals).ToArray()); - - /// - /// Generates a formatter action that will write out all properties and fields from instances of type . - /// - /// Expressions specifying the members to include in formatting. - /// - public static Action GenerateForMembers(params Expression>[] members) => - CreateCustom(typeof (T).GetMembers(members).ToArray()); - - /// - /// Registers a formatter to be used when formatting instances of type . - /// - public static void Register(Action formatter) - { - if (formatter == null) - { - throw new ArgumentNullException(nameof(formatter)); - } - - if (typeof (T) == typeof (Type)) - { - // special treatment is needed since typeof(Type) == System.RuntimeType, which is not public - // ReSharper disable once PossibleMistakenCallToGetType.2 - Formatter.Register(typeof (Type).GetType(), (o, writer) => formatter((T) o, writer)); - } - - Custom = formatter; - } - - /// - /// Registers a formatter to be used when formatting instances of type . - /// - public static void Register(Func formatter) => - Register((obj, writer) => writer.Write(formatter(obj))); - - /// - /// Registers a formatter to be used when formatting instances of type . - /// - public static void RegisterForAllMembers(bool includeInternals = false) => - Register(GenerateForAllMembers(includeInternals)); - - /// - /// Registers a formatter to be used when formatting instances of type . - /// - public static void RegisterForMembers(params Expression>[] members) - { - if (members == null || !members.Any()) - { - Register(GenerateForAllMembers()); - } - else - { - Register(GenerateForMembers(members)); - } - } - - internal static string Format(T obj) - { - var writer = Formatter.CreateWriter(); - Format(obj, writer); - return writer.ToString(); - } - - /// - /// Formats an object and writes it to a writer. - /// - /// The obj. - /// The writer. - public static void Format(T obj, TextWriter writer) - { - // TODO: (Format) is it worth caching the result of this for LogEntry objects? it could be done directly in the default formatter. - if (obj == null) - { - writer.Write(Formatter.NullString); - return; - } - - // find a formatter for the object type, and possibly register one on the fly - using (Formatter.RecursionCounter.Enter()) - { - if (Formatter.RecursionCounter.Depth <= Formatter.RecursionLimit) - { - if (Custom == null) - { - if (isAnonymous || isException) - { - Custom = GenerateForAllMembers(); - } - else if (isLogEntry) - { - // TODO: (Format) this shouldn't happen, but it occurs after formatter have been cleared. find a better way to do this. this really only occurs in testing, usually, so not highest priority. - typeof (T).GetMethod("RegisterFormatters", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, null); - } - else if (Default == WriteDefault) - { - if (Formatter.AutoGenerateForType(typeof (T))) - { - Custom = GenerateForAllMembers(); - } - else - { - // short circuit for future checks - Custom = (o, w) => Default(o, w); - } - } - } - (Custom ?? Default)(obj, writer); - } - else - { - Default(obj, writer); - } - } - } - - /// - /// Creates a custom formatter for the specified members. - /// - private static Action CreateCustom(MemberInfo[] forMembers) - { - var accessors = forMembers.GetMemberAccessors(); - - if (isException) - { - // filter out internal values from the Data dictionary, since they're intended to be surfaced in other ways - var dataAccessor = accessors.SingleOrDefault(a => a.MemberName == "Data"); - if (dataAccessor != null) - { - var originalGetData = dataAccessor.GetValue; - dataAccessor.GetValue = e => ((IDictionary) originalGetData(e)) - .Cast() - .Where(de => !de.Key.ToString().StartsWith(ExceptionExtensions.ExceptionDataPrefix)) - .ToDictionary(de => de.Key, de => de.Value); - } - - // replace the default stack trace with the full stack trace when present - var stackTraceAccessor = accessors.SingleOrDefault(a => a.MemberName == "StackTrace"); - if (stackTraceAccessor != null) - { - stackTraceAccessor.GetValue = e => - { - var ex = e as Exception; - if (ex.Data.Contains(ExceptionExtensions.FullStackTraceKey)) - { - return ex.Data[ExceptionExtensions.FullStackTraceKey]; - } - return ex.StackTrace; - }; - } - } - - return (target, writer) => - { - Formatter.TextFormatter.WriteStartObject(writer); - - if (writeHeader) - { - var entry = target as LogEntry; - - if (entry != null) - { - Formatter.TextFormatter.WriteLogEntryHeader(entry, writer); - } - else - { - Formatter.Format(typeof (T), writer); - } - - Formatter.TextFormatter.WriteEndHeader(writer); - } - - for (var i = 0; i < accessors.Length; i++) - { - try - { - var accessor = accessors[i]; - - if (accessor.Ignore) - { - continue; - } - - var value = accessor.GetValue(target); - - if (accessor.SkipOnNull && value == null) - { - continue; - } - - Formatter.TextFormatter.WriteStartProperty(writer); - writer.Write(accessor.MemberName); - Formatter.TextFormatter.WriteNameValueDelimiter(writer); - value.FormatTo(writer); - Formatter.TextFormatter.WriteEndProperty(writer); - - if (i < accessors.Length - 1) - { - Formatter.TextFormatter.WritePropertyDelimiter(writer); - } - } - catch (Exception ex) - { - ex.RaiseErrorEvent(); - if (ex.ShouldThrow()) - { - throw; - } - } - } - - Formatter.TextFormatter.WriteEndObject(writer); - }; - } - - /// - /// Gets or sets the limit to the number of items that will be written out in detail from an IEnumerable sequence of . - /// - /// The list expansion limit. - internal static int ListExpansionLimit - { - get - { - return listExpansionLimit ?? Formatter.ListExpansionLimit; - } - set - { - listExpansionLimit = value; - } - } - - internal static bool IsCustom => - Custom != null || Default != WriteDefault; - - private static void WriteDefault(T obj, TextWriter writer) - { - if (obj is string) - { - writer.Write(obj); - return; - } - - var enumerable = obj as IEnumerable; - if (enumerable != null) - { - Formatter.Join(enumerable, writer, listExpansionLimit); - } - else - { - writer.Write(obj.ToString()); - } - } - } +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using Its.Log.Instrumentation.Extensions; + +namespace Its.Log.Instrumentation +{ + /// + /// Provides formatting functionality for a specific type. + /// + /// The type for which formatting is provided. + public static class LogFormatter + { + private static readonly bool isAnonymous = typeof (T).IsAnonymous(); + private static Action Custom; + private static readonly bool isException = typeof (Exception).IsAssignableFrom(typeof (T)); + private static readonly bool writeHeader = !isAnonymous && typeof (T).BaseType != typeof (Params); + private static readonly bool isLogEntry = typeof (LogEntry).IsAssignableFrom(typeof (T)); + private static int? listExpansionLimit; + + /// + /// Initializes the class. + /// + static LogFormatter() + { + LogFormatter.Clearing += (o, e) => Custom = null; + } + + /// + /// Gets or sets the default formatter for type . + /// + public static Action Default { get; set; } = WriteDefault; + + /// + /// Generates a formatter action that will write out all properties and fields from instances of type . + /// + /// if set to true include internal and private members. + public static Action GenerateForAllMembers(bool includeInternals = false) => + CreateCustom(typeof (T).GetAllMembers(includeInternals).ToArray()); + + /// + /// Generates a formatter action that will write out all properties and fields from instances of type . + /// + /// Expressions specifying the members to include in formatting. + /// + public static Action GenerateForMembers(params Expression>[] members) => + CreateCustom(typeof (T).GetMembers(members).ToArray()); + + /// + /// Registers a formatter to be used when formatting instances of type . + /// + public static void Register(Action formatter) + { + if (formatter == null) + { + throw new ArgumentNullException(nameof(formatter)); + } + + if (typeof (T) == typeof (Type)) + { + // special treatment is needed since typeof(Type) == System.RuntimeType, which is not public + // ReSharper disable once PossibleMistakenCallToGetType.2 + LogFormatter.Register(typeof (Type).GetType(), (o, writer) => formatter((T) o, writer)); + } + + Custom = formatter; + } + + /// + /// Registers a formatter to be used when formatting instances of type . + /// + public static void Register(Func formatter) => + Register((obj, writer) => writer.Write(formatter(obj))); + + /// + /// Registers a formatter to be used when formatting instances of type . + /// + public static void RegisterForAllMembers(bool includeInternals = false) => + Register(GenerateForAllMembers(includeInternals)); + + /// + /// Registers a formatter to be used when formatting instances of type . + /// + public static void RegisterForMembers(params Expression>[] members) + { + if (members == null || !members.Any()) + { + Register(GenerateForAllMembers()); + } + else + { + Register(GenerateForMembers(members)); + } + } + + internal static string Format(T obj) + { + var writer = LogFormatter.CreateWriter(); + Format(obj, writer); + return writer.ToString(); + } + + /// + /// Formats an object and writes it to a writer. + /// + /// The obj. + /// The writer. + public static void Format(T obj, TextWriter writer) + { + // TODO: (Format) is it worth caching the result of this for LogEntry objects? it could be done directly in the default formatter. + if (obj == null) + { + writer.Write(LogFormatter.NullString); + return; + } + + // find a formatter for the object type, and possibly register one on the fly + using (LogFormatter.RecursionCounter.Enter()) + { + if (LogFormatter.RecursionCounter.Depth <= LogFormatter.RecursionLimit) + { + if (Custom == null) + { + if (isAnonymous || isException) + { + Custom = GenerateForAllMembers(); + } + else if (isLogEntry) + { + // TODO: (Format) this shouldn't happen, but it occurs after formatter have been cleared. find a better way to do this. this really only occurs in testing, usually, so not highest priority. + typeof (T).GetMethod("RegisterFormatters", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, null); + } + else if (Default == WriteDefault) + { + if (LogFormatter.AutoGenerateForType(typeof (T))) + { + Custom = GenerateForAllMembers(); + } + else + { + // short circuit for future checks + Custom = (o, w) => Default(o, w); + } + } + } + (Custom ?? Default)(obj, writer); + } + else + { + Default(obj, writer); + } + } + } + + /// + /// Creates a custom formatter for the specified members. + /// + private static Action CreateCustom(MemberInfo[] forMembers) + { + var accessors = forMembers.GetMemberAccessors(); + + if (isException) + { + // filter out internal values from the Data dictionary, since they're intended to be surfaced in other ways + var dataAccessor = accessors.SingleOrDefault(a => a.MemberName == "Data"); + if (dataAccessor != null) + { + var originalGetData = dataAccessor.GetValue; + dataAccessor.GetValue = e => ((IDictionary) originalGetData(e)) + .Cast() + .Where(de => !de.Key.ToString().StartsWith(ExceptionExtensions.ExceptionDataPrefix)) + .ToDictionary(de => de.Key, de => de.Value); + } + + // replace the default stack trace with the full stack trace when present + var stackTraceAccessor = accessors.SingleOrDefault(a => a.MemberName == "StackTrace"); + if (stackTraceAccessor != null) + { + stackTraceAccessor.GetValue = e => + { + var ex = e as Exception; + if (ex.Data.Contains(ExceptionExtensions.FullStackTraceKey)) + { + return ex.Data[ExceptionExtensions.FullStackTraceKey]; + } + return ex.StackTrace; + }; + } + } + + return (target, writer) => + { + LogFormatter.TextFormatter.WriteStartObject(writer); + + if (writeHeader) + { + var entry = target as LogEntry; + + if (entry != null) + { + LogFormatter.TextFormatter.WriteLogEntryHeader(entry, writer); + } + else + { + LogFormatter.Format(typeof (T), writer); + } + + LogFormatter.TextFormatter.WriteEndHeader(writer); + } + + for (var i = 0; i < accessors.Length; i++) + { + try + { + var accessor = accessors[i]; + + if (accessor.Ignore) + { + continue; + } + + var value = accessor.GetValue(target); + + if (accessor.SkipOnNull && value == null) + { + continue; + } + + LogFormatter.TextFormatter.WriteStartProperty(writer); + writer.Write(accessor.MemberName); + LogFormatter.TextFormatter.WriteNameValueDelimiter(writer); + value.FormatTo(writer); + LogFormatter.TextFormatter.WriteEndProperty(writer); + + if (i < accessors.Length - 1) + { + LogFormatter.TextFormatter.WritePropertyDelimiter(writer); + } + } + catch (Exception ex) + { + ex.RaiseErrorEvent(); + if (ex.ShouldThrow()) + { + throw; + } + } + } + + LogFormatter.TextFormatter.WriteEndObject(writer); + }; + } + + /// + /// Gets or sets the limit to the number of items that will be written out in detail from an IEnumerable sequence of . + /// + /// The list expansion limit. + internal static int ListExpansionLimit + { + get + { + return listExpansionLimit ?? LogFormatter.ListExpansionLimit; + } + set + { + listExpansionLimit = value; + } + } + + internal static bool IsCustom => + Custom != null || Default != WriteDefault; + + private static void WriteDefault(T obj, TextWriter writer) + { + if (obj is string) + { + writer.Write(obj); + return; + } + + var enumerable = obj as IEnumerable; + if (enumerable != null) + { + LogFormatter.Join(enumerable, writer, listExpansionLimit); + } + else + { + writer.Write(obj.ToString()); + } + } + } } \ No newline at end of file