├── .editorconfig ├── .gitignore ├── .nuget └── packages.config ├── Build.bat ├── Build.ps1 ├── Build.psake.ps1 ├── Building.txt ├── EventSourceProxy.Example ├── EventSourceProxy.Example.csproj ├── IExampleLogSource.cs └── Program.cs ├── EventSourceProxy.Tests ├── BaseLoggingTest.cs ├── EventActivityScopeTests.cs ├── EventAttributeProviderTests.cs ├── EventDataTypesTests.cs ├── EventSourceImplementerTests.cs ├── EventSourceProxy.Tests.csproj ├── FluentInterfaceTests.cs ├── ParameterProviderTests.cs ├── Properties │ ├── Resources.Designer.cs │ └── Resources.resx ├── SerializationTests.cs ├── TestEventListener.cs ├── TraceContextProviderTests.cs └── TracingProxyTests.cs ├── EventSourceProxy.nuspec ├── EventSourceProxy.sln ├── EventSourceProxy.snk ├── EventSourceProxy ├── Any.cs ├── CodeAnalysisRules.ruleset ├── CustomDictionary.xml ├── EventActivityScope.cs ├── EventAttributeHelper.cs ├── EventAttributeProvider.cs ├── EventAttributeProviderAttribute.cs ├── EventExceptionAttribute.cs ├── EventSourceAttributeHelper.cs ├── EventSourceImplementationAttribute.cs ├── EventSourceImplementer.cs ├── EventSourceManifest.cs ├── EventSourceProxy.csproj ├── GlobalSuppressions.cs ├── InvocationContext.cs ├── InvocationContextTypes.cs ├── JsonObjectSerializer.cs ├── NullObjectSerializer.cs ├── ParameterBuilder.cs ├── ParameterBuilderValue.cs ├── ParameterConverter.cs ├── ParameterDefinition.cs ├── ParameterMapping.cs ├── Properties │ └── AssemblyInfo.cs ├── ProviderManager.cs ├── ProxyHelper.cs ├── Settings.StyleCop ├── StaticFieldStorage.cs ├── ToStringObjectSerializer.cs ├── TraceAsAttribute.cs ├── TraceAsDataAttribute.cs ├── TraceContext.cs ├── TraceContextAttribute.cs ├── TraceContextProvider.cs ├── TraceContextProviderAttribute.cs ├── TraceIgnoreAttribute.cs ├── TraceMemberAttribute.cs ├── TraceParameterProvider.cs ├── TraceParameterProviderAttribute.cs ├── TraceProviderAttribute.cs ├── TraceSerializationAttribute.cs ├── TraceSerializationContext.cs ├── TraceSerializationProvider.cs ├── TraceSerializationProviderAttribute.cs ├── TraceTransformAttribute.cs ├── TracingProxy.cs ├── TracingProxyImplementer.cs └── TypeImplementer.cs ├── GenerateProxyManifest ├── GenerateProxyManifest.csproj └── Program.cs ├── LICENSE └── README.md /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | end_of_line = crlf 3 | insert_final_newline = true 4 | indent_style = tab 5 | indent_size = 4 6 | trim_tailing_whitespace = true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | obj 3 | *.user 4 | *.suo 5 | *.docstates 6 | *.nupkg 7 | *.orig 8 | *.rej 9 | StyleCop.Cache 10 | Output 11 | TestResult.xml 12 | packages 13 | PerfView.exe -------------------------------------------------------------------------------- /.nuget/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /Build.bat: -------------------------------------------------------------------------------- 1 | PowerShell .\build.ps1 %1 -------------------------------------------------------------------------------- /Build.ps1: -------------------------------------------------------------------------------- 1 | param ( 2 | [string] $target = "default" 3 | ) 4 | 5 | Invoke-psake .\Build.psake.ps1 -taskList $target 6 | -------------------------------------------------------------------------------- /Build.psake.ps1: -------------------------------------------------------------------------------- 1 | $psake.use_exit_on_error = $true 2 | 3 | ######################################### 4 | # to build a new version 5 | # 1. git tag 1.0.x 6 | # 2. build package 7 | ######################################### 8 | 9 | properties { 10 | $version = git describe --abbrev=0 --tags 11 | } 12 | 13 | Task default -depends Build 14 | 15 | Task Build { 16 | Write-Output "Building Version $version" 17 | 18 | Exec { 19 | dotnet build --configuration Release /p:Version=$version 20 | } 21 | } 22 | 23 | Task Test { 24 | dotnet test --configuration Release 25 | } 26 | 27 | Task Package -depends Test { 28 | nuget pack .\EventSourceProxy.nuspec -version $version 29 | } 30 | 31 | Task Push { 32 | cmd /c "for %p in (*.nupkg) do nuget push %p -source https://www.nuget.org/api/v2/package" 33 | } -------------------------------------------------------------------------------- /Building.txt: -------------------------------------------------------------------------------- 1 | Notes for Building EventSourceProxy 2 | =================================== 3 | 4 | * Building requires Psake - you can get it from http://nuget.org/packages/psake/ 5 | -------------------------------------------------------------------------------- /EventSourceProxy.Example/EventSourceProxy.Example.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Exe 9 | netcoreapp2.0 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /EventSourceProxy.Example/IExampleLogSource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.Tracing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace EventSourceProxy.Example 9 | { 10 | public interface IExampleLogSource 11 | { 12 | [Event(1, Message="Starting")] 13 | void Starting(); 14 | void AnEvent(string data); 15 | [Event(2, Message = "Stopping")] 16 | void Stopping(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /EventSourceProxy.Example/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Diagnostics.Tracing; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Runtime.InteropServices; 8 | using System.Security; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using EventSourceProxy; 12 | 13 | namespace EventSourceProxy.Example 14 | { 15 | public class Foo 16 | { 17 | public virtual void Bar() {} 18 | public virtual int Bar2() { return 1; } 19 | } 20 | 21 | public class TestListener : EventListener 22 | { 23 | protected override void OnEventWritten(EventWrittenEventArgs eventData) 24 | { 25 | Console.Write("Activity: {0} ", EventActivityScope.CurrentActivityId); 26 | Console.WriteLine(eventData.Message, eventData.Payload.ToArray()); 27 | } 28 | } 29 | 30 | class Program 31 | { 32 | static void Main(string[] args) 33 | { 34 | // create the log 35 | var log = EventSourceImplementer.GetEventSourceAs(); 36 | EventSource es = (EventSource)log; 37 | Console.WriteLine("Provider GUID = {0}", es.Guid); 38 | 39 | // create a listener 40 | var listener = new TestListener(); 41 | listener.EnableEvents(es, EventLevel.LogAlways); 42 | 43 | using (new EventActivityScope()) 44 | { 45 | log.Starting(); 46 | for (int i = 0; i < 10; i++) 47 | { 48 | using (new EventActivityScope()) 49 | { 50 | log.AnEvent(String.Format("i = {0}", i)); 51 | } 52 | } 53 | log.Stopping(); 54 | } 55 | 56 | TracingProxy.Create(new Foo()).Bar(); 57 | TracingProxy.Create(new Foo()).Bar2(); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /EventSourceProxy.Tests/BaseLoggingTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using NUnit.Framework; 7 | using System.Diagnostics.Tracing; 8 | 9 | namespace EventSourceProxy.Tests 10 | { 11 | public class BaseLoggingTest 12 | { 13 | #region Setup and TearDown 14 | internal TestEventListener _listener; 15 | 16 | [SetUp] 17 | public void SetUp() 18 | { 19 | _listener = new TestEventListener(); 20 | } 21 | #endregion 22 | 23 | protected void EnableLogging() where TLog : class 24 | { 25 | // create the logger and make sure it is serializing the parameters properly 26 | var logger = EventSourceImplementer.GetEventSource(); 27 | _listener.EnableEvents(logger, EventLevel.LogAlways); 28 | } 29 | 30 | protected void EnableLogging(object proxy) 31 | { 32 | // create the logger and make sure it is serializing the parameters properly 33 | _listener.EnableEvents((EventSource)proxy, EventLevel.LogAlways); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /EventSourceProxy.Tests/EventActivityScopeTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using NUnit.Framework; 7 | 8 | namespace EventSourceProxy.Tests 9 | { 10 | [TestFixture] 11 | public class EventActivityScopeTests 12 | { 13 | [Test] 14 | public void GetActivityIdShouldReturnEmptyGuid() 15 | { 16 | using (EventActivityScope scope1 = new EventActivityScope()) 17 | { 18 | Assert.AreEqual(Guid.Empty, scope1.PreviousActivityId); 19 | } 20 | } 21 | 22 | [Test] 23 | public void NewScopeShouldGenerateNewActivity() 24 | { 25 | using (EventActivityScope scope1 = new EventActivityScope()) 26 | { 27 | // make sure we don't have an outer activity, but do have an inner activity 28 | Assert.AreEqual(Guid.Empty, scope1.PreviousActivityId); 29 | Assert.AreNotEqual(Guid.Empty, scope1.ActivityId); 30 | 31 | using (EventActivityScope scope2 = new EventActivityScope()) 32 | { 33 | Assert.AreEqual(scope1.ActivityId, scope2.PreviousActivityId); 34 | Assert.AreNotEqual(scope1.ActivityId, scope2.ActivityId); 35 | } 36 | } 37 | } 38 | 39 | [Test] 40 | public void ReuseScopeShouldReuseActivityId() 41 | { 42 | using (EventActivityScope scope1 = new EventActivityScope()) 43 | { 44 | // make sure we don't have an outer activity, but do have an inner activity 45 | Assert.AreEqual(Guid.Empty, scope1.PreviousActivityId); 46 | Assert.AreNotEqual(Guid.Empty, scope1.ActivityId); 47 | 48 | using (EventActivityScope scope2 = new EventActivityScope(true)) 49 | { 50 | Assert.AreEqual(scope1.ActivityId, scope2.PreviousActivityId); 51 | Assert.AreEqual(scope1.ActivityId, scope2.ActivityId); 52 | } 53 | } 54 | } 55 | 56 | [Test] 57 | public void NewScopeWithExistingActivityIdShouldUseCorrectActivityId() 58 | { 59 | Guid externalGuid = Guid.Parse("c2cbe3e9-53ee-440c-b16b-2dec89df7202"); 60 | using (EventActivityScope scope1 = new EventActivityScope(externalGuid)) 61 | { 62 | Assert.AreEqual(scope1.ActivityId, externalGuid); 63 | } 64 | } 65 | 66 | [Test] 67 | public void ReuseScopeShouldReuseExternalActivityId() 68 | { 69 | Guid externalGuid = Guid.Parse("b423a74f-f5c7-4707-8555-552567ec446a"); 70 | using (EventActivityScope scope1 = new EventActivityScope(externalGuid)) 71 | { 72 | Assert.AreEqual(scope1.ActivityId, externalGuid); 73 | // make sure we don't have an outer activity, but do have an inner activity 74 | Assert.AreEqual(Guid.Empty, scope1.PreviousActivityId); 75 | Assert.AreNotEqual(Guid.Empty, scope1.ActivityId); 76 | 77 | using (EventActivityScope scope2 = new EventActivityScope(true)) 78 | { 79 | Assert.AreEqual(scope1.ActivityId, scope2.PreviousActivityId); 80 | Assert.AreEqual(scope1.ActivityId, scope2.ActivityId); 81 | } 82 | } 83 | } 84 | 85 | [Test] 86 | public void NewScopeShouldGenerateNewActivityIdWhenParentHasExternalActivityId() 87 | { 88 | Guid externalGuid = Guid.Parse("adc174e5-6f7b-4280-a4a4-8d8550ab4f89"); 89 | using (EventActivityScope scope1 = new EventActivityScope(externalGuid)) 90 | { 91 | Assert.AreEqual(scope1.ActivityId, externalGuid); 92 | // make sure we don't have an outer activity, but do have an inner activity 93 | Assert.AreEqual(Guid.Empty, scope1.PreviousActivityId); 94 | Assert.AreNotEqual(Guid.Empty, scope1.ActivityId); 95 | 96 | using (EventActivityScope scope2 = new EventActivityScope()) 97 | { 98 | Assert.AreEqual(scope1.ActivityId, scope2.PreviousActivityId); 99 | Assert.AreNotEqual(scope1.ActivityId, scope2.ActivityId); 100 | } 101 | } 102 | } 103 | 104 | [Test] 105 | public void NewScopeWithExternalActivityIdShouldUseCorrectActivityId() 106 | { 107 | Guid externalGuid = Guid.Parse("64a49a09-c775-4a91-b4ec-91a9a6e3caeb"); 108 | using (EventActivityScope scope1 = new EventActivityScope()) 109 | { 110 | // make sure we don't have an outer activity, but do have an inner activity 111 | Assert.AreEqual(Guid.Empty, scope1.PreviousActivityId); 112 | Assert.AreNotEqual(Guid.Empty, scope1.ActivityId); 113 | 114 | using (EventActivityScope scope2 = new EventActivityScope(externalGuid)) 115 | { 116 | Assert.AreEqual(scope2.ActivityId, externalGuid); 117 | Assert.AreEqual(scope1.ActivityId, scope2.PreviousActivityId); 118 | Assert.AreNotEqual(scope1.ActivityId, scope2.ActivityId); 119 | } 120 | } 121 | } 122 | 123 | #region Async Tests 124 | public interface ILogForAsync 125 | { 126 | void Log(); 127 | } 128 | 129 | public async Task TestActivityIDAsync() 130 | { 131 | var log = EventSourceImplementer.GetEventSourceAs(); 132 | 133 | using (var scope = new EventSourceProxy.EventActivityScope()) 134 | { 135 | var before = EventActivityScope.CurrentActivityId; 136 | 137 | log.Log(); 138 | 139 | await Task.Factory.StartNew(() => {}); 140 | 141 | Assert.AreEqual(before, EventActivityScope.CurrentActivityId); 142 | Assert.AreEqual(before, scope.ActivityId); 143 | } 144 | } 145 | 146 | [Test] 147 | public void ActivityIDShouldRestoreAfterAwait() 148 | { 149 | TestActivityIDAsync().Wait(); 150 | } 151 | #endregion 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /EventSourceProxy.Tests/EventAttributeProviderTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.Tracing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using NUnit.Framework; 8 | 9 | namespace EventSourceProxy.Tests 10 | { 11 | [TestFixture] 12 | public class EventAttributeProviderTests : BaseLoggingTest 13 | { 14 | #region Provider Attribute Tests 15 | [EventAttributeProvider(typeof(MyEventAttributeProvider))] 16 | public interface ILogInterfaceWithAttribute 17 | { 18 | void DoSomething(); 19 | } 20 | 21 | public class LogInterfaceWithAttribute : ILogInterfaceWithAttribute 22 | { 23 | public void DoSomething() { throw new ApplicationException(); } 24 | } 25 | 26 | public class MyEventAttributeProvider : EventAttributeProvider 27 | { 28 | public MyEventAttributeProvider() : base(EventLevel.Warning, EventLevel.Critical) 29 | { 30 | } 31 | } 32 | 33 | [Test] 34 | public void AttributeShouldDetermineProvider() 35 | { 36 | var logger = EventSourceImplementer.GetEventSourceAs(); 37 | _listener.EnableEvents((EventSource)logger, EventLevel.LogAlways); 38 | 39 | logger.DoSomething(); 40 | 41 | // look at the events 42 | var events = _listener.Events.ToArray(); 43 | Assert.AreEqual(1, events.Length); 44 | Assert.AreEqual(EventLevel.Warning, events[0].Level); 45 | 46 | _listener.Reset(); 47 | var proxy = TracingProxy.Create(new LogInterfaceWithAttribute()); 48 | try { proxy.DoSomething(); } 49 | catch { } 50 | 51 | events = _listener.Events.ToArray(); 52 | Assert.AreEqual(2, events.Length); 53 | Assert.AreEqual(EventLevel.Critical, events[1].Level); 54 | } 55 | #endregion 56 | 57 | #region Exception Attribute Tests 58 | [EventException(EventLevel.Critical)] 59 | public interface ILogInterfaceWithExceptionAttribute 60 | { 61 | void DoSomething(); 62 | } 63 | 64 | public class LogInterfaceWithExceptionAttribute : ILogInterfaceWithExceptionAttribute 65 | { 66 | public void DoSomething() { throw new ApplicationException(); } 67 | } 68 | 69 | public interface ILogInterfaceWithExceptionMethodAttribute 70 | { 71 | [EventException(EventLevel.Critical)] 72 | void DoSomething(); 73 | } 74 | 75 | public class LogInterfaceWithExceptionMethodAttribute : ILogInterfaceWithExceptionMethodAttribute 76 | { 77 | public void DoSomething() { throw new ApplicationException(); } 78 | } 79 | 80 | [Test] 81 | public void ExceptionAttributeShouldDetermineLevel() 82 | { 83 | Assert.AreEqual(EventLevel.Error, new EventAttributeProvider().ExceptionEventLevel); 84 | 85 | var logger = EventSourceImplementer.GetEventSourceAs(); 86 | _listener.EnableEvents((EventSource)logger, EventLevel.LogAlways); 87 | 88 | logger.DoSomething(); 89 | 90 | // look at the events 91 | var events = _listener.Events.ToArray(); 92 | Assert.AreEqual(1, events.Length); 93 | Assert.AreEqual(EventLevel.Informational, events[0].Level); 94 | 95 | _listener.Reset(); 96 | var proxy = TracingProxy.Create(new LogInterfaceWithExceptionAttribute()); 97 | try { proxy.DoSomething(); } 98 | catch { } 99 | 100 | events = _listener.Events.ToArray(); 101 | Assert.AreEqual(2, events.Length); 102 | Assert.AreEqual(EventLevel.Critical, events[1].Level); 103 | } 104 | 105 | [Test] 106 | public void ExceptionAttributeOnMethodShouldDetermineLevel() 107 | { 108 | Assert.AreEqual(EventLevel.Error, new EventAttributeProvider().ExceptionEventLevel); 109 | 110 | var logger = EventSourceImplementer.GetEventSourceAs(); 111 | _listener.EnableEvents((EventSource)logger, EventLevel.LogAlways); 112 | 113 | logger.DoSomething(); 114 | 115 | // look at the events 116 | var events = _listener.Events.ToArray(); 117 | Assert.AreEqual(1, events.Length); 118 | Assert.AreEqual(EventLevel.Informational, events[0].Level); 119 | 120 | _listener.Reset(); 121 | var proxy = TracingProxy.Create(new LogInterfaceWithExceptionMethodAttribute()); 122 | try { proxy.DoSomething(); } 123 | catch { } 124 | 125 | events = _listener.Events.ToArray(); 126 | Assert.AreEqual(2, events.Length); 127 | Assert.AreEqual(EventLevel.Critical, events[1].Level); 128 | } 129 | #endregion 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /EventSourceProxy.Tests/EventDataTypesTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.Tracing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using NUnit.Framework; 8 | 9 | namespace EventSourceProxy.Tests 10 | { 11 | [TestFixture] 12 | public class EventDataTypesTests 13 | { 14 | #region Tests for Built-in Types 15 | public enum FooEnum 16 | { 17 | Foo, 18 | Bar 19 | } 20 | 21 | public interface ITypeLog 22 | { 23 | void Log(T t); 24 | } 25 | 26 | public static class TypeLogTester 27 | { 28 | public static void Test(T t) 29 | { 30 | var testLog = (EventSource)EventSourceImplementer.GetEventSourceAs>(); 31 | 32 | using (var listener = new TestEventListener()) 33 | { 34 | listener.EnableEvents(testLog, EventLevel.LogAlways); 35 | 36 | ITypeLog tLog = (ITypeLog)testLog; 37 | tLog.Log(t); 38 | 39 | object value = listener.Events.Last().Payload[0]; 40 | if (TypeIsSupportedByEventSource(typeof(T))) 41 | Assert.AreEqual(t, value); 42 | else 43 | Assert.AreEqual(t.ToString(), value); 44 | 45 | listener.DisableEvents(testLog); 46 | } 47 | } 48 | 49 | internal static bool TypeIsSupportedByEventSource(Type type) 50 | { 51 | if (type == typeof(string)) return true; 52 | if (type == typeof(int)) return true; 53 | if (type == typeof(long)) return true; 54 | if (type == typeof(ulong)) return true; 55 | if (type == typeof(byte)) return true; 56 | if (type == typeof(sbyte)) return true; 57 | if (type == typeof(short)) return true; 58 | if (type == typeof(ushort)) return true; 59 | if (type == typeof(float)) return true; 60 | if (type == typeof(double)) return true; 61 | if (type == typeof(bool)) return true; 62 | if (type == typeof(Guid)) return true; 63 | if (type.IsEnum) return true; 64 | 65 | return false; 66 | } 67 | } 68 | 69 | [Test] 70 | public void BuiltInTypesCanBeLogged() 71 | { 72 | TypeLogTester.Test("string"); 73 | TypeLogTester.Test(5); 74 | TypeLogTester.Test(0x800000000); 75 | TypeLogTester.Test(0x1800000000); 76 | TypeLogTester.Test(0x78); 77 | TypeLogTester.Test(0x20); 78 | TypeLogTester.Test(0x1001); 79 | TypeLogTester.Test(0x8010); 80 | TypeLogTester.Test(1.234f); 81 | TypeLogTester.Test(2.3456); 82 | TypeLogTester.Test(true); 83 | TypeLogTester.Test(Guid.NewGuid()); 84 | TypeLogTester.Test(FooEnum.Bar); 85 | TypeLogTester.Test(new IntPtr(1234)); 86 | TypeLogTester.Test('c'); 87 | TypeLogTester.Test(3.456m); 88 | 89 | TypeLogTester.Test(5); 90 | TypeLogTester.Test(0x800000000); 91 | TypeLogTester.Test(0x1800000000); 92 | TypeLogTester.Test(0x78); 93 | TypeLogTester.Test(0x20); 94 | TypeLogTester.Test(0x1001); 95 | TypeLogTester.Test(0x8010); 96 | TypeLogTester.Test(1.234f); 97 | TypeLogTester.Test(2.3456); 98 | TypeLogTester.Test(true); 99 | TypeLogTester.Test(Guid.NewGuid()); 100 | TypeLogTester.Test(FooEnum.Bar); 101 | TypeLogTester.Test(new IntPtr(1234)); 102 | TypeLogTester.Test('c'); 103 | TypeLogTester.Test(3.456m); 104 | 105 | TypeLogTester.Test(null); 106 | TypeLogTester.Test(null); 107 | TypeLogTester.Test(null); 108 | TypeLogTester.Test(null); 109 | TypeLogTester.Test(null); 110 | TypeLogTester.Test(null); 111 | TypeLogTester.Test(null); 112 | TypeLogTester.Test(null); 113 | TypeLogTester.Test(null); 114 | TypeLogTester.Test(null); 115 | TypeLogTester.Test(null); 116 | TypeLogTester.Test(null); 117 | TypeLogTester.Test(null); 118 | TypeLogTester.Test(null); 119 | TypeLogTester.Test(null); 120 | } 121 | #endregion 122 | 123 | #region Serialized Types in Abstract Methods Tests 124 | public abstract class TypeLogWithSerializedTypesInAbstractMethod : EventSource 125 | { 126 | public abstract void LogIntPtr(IntPtr p); 127 | public abstract void LogChar(char c); 128 | public abstract void LogDecimal(decimal d); 129 | } 130 | 131 | [Test] 132 | public void BuiltInSerializedTypesCanBeLoggedInAbstractMethods() 133 | { 134 | var listener = new TestEventListener(); 135 | var testLog = EventSourceImplementer.GetEventSourceAs(); 136 | listener.EnableEvents(testLog, EventLevel.LogAlways); 137 | 138 | testLog.LogIntPtr(new IntPtr(1234)); Assert.AreEqual("1234", listener.Events.Last().Payload[0].ToString()); 139 | testLog.LogChar('c'); Assert.AreEqual("c", listener.Events.Last().Payload[0].ToString()); 140 | testLog.LogDecimal(3.456m); Assert.AreEqual("3.456", listener.Events.Last().Payload[0].ToString()); 141 | } 142 | #endregion 143 | 144 | #region Serialized Types in Direct Methods Tests 145 | public class TypeLogWithSerializedTypesInDirectMethod : EventSource 146 | { 147 | public void LogIntPtr(IntPtr p) { WriteEvent(1, p); } 148 | public void LogChar(char c) { WriteEvent(2, c); } 149 | public void LogDecimal(decimal d) { WriteEvent(3, d); } 150 | } 151 | 152 | [Test] 153 | public void BuiltInSerializedTypesCanBeLoggedInDirectMethods() 154 | { 155 | var listener = new TestEventListener(); 156 | var testLog = EventSourceImplementer.GetEventSourceAs(); 157 | listener.EnableEvents(testLog, EventLevel.LogAlways); 158 | 159 | testLog.LogIntPtr(new IntPtr(1234)); Assert.AreEqual("1234", listener.Events.Last().Payload[0].ToString()); 160 | testLog.LogChar('c'); Assert.AreEqual("c", listener.Events.Last().Payload[0].ToString()); 161 | testLog.LogDecimal(3.456m); Assert.AreEqual("3.456", listener.Events.Last().Payload[0].ToString()); 162 | } 163 | #endregion 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /EventSourceProxy.Tests/EventSourceProxy.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0;net46 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /EventSourceProxy.Tests/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.34014 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace EventSourceProxy.Tests.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("EventSourceProxy.Tests.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized string similar to 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901 [rest of string was truncated]";. 65 | /// 66 | internal static string TooBig { 67 | get { 68 | return ResourceManager.GetString("TooBig", resourceCulture); 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /EventSourceProxy.Tests/SerializationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.Tracing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Newtonsoft.Json; 8 | using NUnit.Framework; 9 | 10 | namespace EventSourceProxy.Tests 11 | { 12 | [TestFixture] 13 | public class SerializationTests : BaseLoggingTest 14 | { 15 | #region Test Classes 16 | public class ClassData 17 | { 18 | public string Name { get; set; } 19 | public int Age { get; set; } 20 | 21 | public static ClassData Test = new ClassData() { Name = "Fred", Age = 38 }; 22 | public static string TestJson = JsonConvert.SerializeObject(Test); 23 | } 24 | 25 | public struct StructData 26 | { 27 | public string Name { get; set; } 28 | public int Age { get; set; } 29 | 30 | public static StructData Test = new StructData() { Name = "Fred", Age = 38 }; 31 | public static string TestJson = JsonConvert.SerializeObject(Test); 32 | } 33 | 34 | public interface ILogInterfaceWithClassData 35 | { 36 | void SendData(ClassData data); 37 | } 38 | 39 | public interface ILogInterfaceWithStructData 40 | { 41 | void SendData(StructData data); 42 | } 43 | 44 | public abstract class LogClassWithClassData : EventSource 45 | { 46 | public abstract void SendData(ClassData data); 47 | } 48 | 49 | public abstract class LogClassWithStructData : EventSource 50 | { 51 | public abstract void SendData(StructData data); 52 | } 53 | 54 | public interface ILogInterfaceWithClassData2 55 | { 56 | void SendData(ClassData data); 57 | } 58 | 59 | public class ILogClassWithClassData : ILogInterfaceWithClassData2 60 | { 61 | public void SendData(ClassData data) 62 | { 63 | } 64 | } 65 | #endregion 66 | 67 | #region Test Cases 68 | [Test] 69 | public void InterfaceWithClassShouldSerializeAsJson() 70 | { 71 | EventSourceImplementer.RegisterProvider(new JsonObjectSerializer()); 72 | var logger = EventSourceImplementer.GetEventSourceAs(); 73 | _listener.EnableEvents((EventSource)logger, EventLevel.LogAlways); 74 | 75 | logger.SendData(ClassData.Test); 76 | 77 | // look at the events 78 | var events = _listener.Events.ToArray(); 79 | Assert.AreEqual(1, events.Length); 80 | Assert.AreEqual(1, events[0].EventId); 81 | Assert.AreEqual(ClassData.TestJson, events[0].Payload[0]); 82 | } 83 | 84 | [Test] 85 | public void InterfaceWithStructShouldSerializeAsJson() 86 | { 87 | EventSourceImplementer.RegisterProvider(new JsonObjectSerializer()); 88 | var logger = EventSourceImplementer.GetEventSourceAs(); 89 | _listener.EnableEvents((EventSource)logger, EventLevel.LogAlways); 90 | 91 | logger.SendData(StructData.Test); 92 | 93 | // look at the events 94 | var events = _listener.Events.ToArray(); 95 | Assert.AreEqual(1, events.Length); 96 | Assert.AreEqual(1, events[0].EventId); 97 | Assert.AreEqual(StructData.TestJson, events[0].Payload[0]); 98 | } 99 | 100 | [Test] 101 | public void ClassWithClassShouldSerializeAsJson() 102 | { 103 | EventSourceImplementer.RegisterProvider(new JsonObjectSerializer()); 104 | var logger = EventSourceImplementer.GetEventSourceAs(); 105 | _listener.EnableEvents((EventSource)logger, EventLevel.LogAlways); 106 | 107 | logger.SendData(ClassData.Test); 108 | 109 | // look at the events 110 | var events = _listener.Events.ToArray(); 111 | Assert.AreEqual(1, events.Length); 112 | Assert.AreEqual(1, events[0].EventId); 113 | Assert.AreEqual(ClassData.TestJson, events[0].Payload[0]); 114 | } 115 | 116 | [Test] 117 | public void ClassWithStructShouldSerializeAsJson() 118 | { 119 | EventSourceImplementer.RegisterProvider(new JsonObjectSerializer()); 120 | var logger = EventSourceImplementer.GetEventSourceAs(); 121 | _listener.EnableEvents((EventSource)logger, EventLevel.LogAlways); 122 | 123 | logger.SendData(StructData.Test); 124 | 125 | // look at the events 126 | var events = _listener.Events.ToArray(); 127 | Assert.AreEqual(1, events.Length); 128 | Assert.AreEqual(1, events[0].EventId); 129 | Assert.AreEqual(StructData.TestJson, events[0].Payload[0]); 130 | } 131 | 132 | [Test] 133 | public void ClassImplementingAnInterfaceShouldSerializeData() 134 | { 135 | EventSourceImplementer.RegisterProvider(new JsonObjectSerializer()); 136 | var logger = EventSourceImplementer.GetEventSourceAs(); 137 | _listener.EnableEvents((EventSource)logger, EventLevel.LogAlways); 138 | 139 | var proxy = TracingProxy.Create(new ILogClassWithClassData()); 140 | 141 | proxy.SendData(ClassData.Test); 142 | 143 | // look at the events 144 | var events = _listener.Events.ToArray(); 145 | Assert.AreEqual(2, events.Length); 146 | Assert.AreEqual(1, events[0].EventId); 147 | Assert.AreEqual(ClassData.TestJson, events[0].Payload[0]); 148 | } 149 | #endregion 150 | 151 | #region ToStringSerializer Tests 152 | public interface ILogInterfaceWithClassDataToString 153 | { 154 | void SendData(ClassData data); 155 | } 156 | 157 | [Test] 158 | public void InterfaceWithClassShouldSerializeToString() 159 | { 160 | // register the provider 161 | EventSourceImplementer.RegisterProvider(new ToStringObjectSerializer()); 162 | 163 | var logger = EventSourceImplementer.GetEventSourceAs(); 164 | _listener.EnableEvents((EventSource)logger, EventLevel.LogAlways); 165 | 166 | logger.SendData(ClassData.Test); 167 | 168 | // look at the events 169 | var events = _listener.Events.ToArray(); 170 | Assert.AreEqual(1, events.Length); 171 | Assert.AreEqual(1, events[0].EventId); 172 | Assert.AreEqual(ClassData.Test.ToString(), events[0].Payload[0]); 173 | } 174 | #endregion 175 | 176 | #region NullSerializer Tests 177 | public interface ILogInterfaceWithClassDataToNull 178 | { 179 | void SendData(ClassData data); 180 | } 181 | 182 | [Test] 183 | public void InterfaceWithClassShouldSerializeAsNull() 184 | { 185 | // register the provider 186 | EventSourceImplementer.RegisterProvider(new NullObjectSerializer()); 187 | 188 | var logger = EventSourceImplementer.GetEventSourceAs(); 189 | _listener.EnableEvents((EventSource)logger, EventLevel.LogAlways); 190 | 191 | logger.SendData(ClassData.Test); 192 | 193 | // look at the events 194 | var events = _listener.Events.ToArray(); 195 | Assert.AreEqual(1, events.Length); 196 | Assert.AreEqual(1, events[0].EventId); 197 | Assert.AreEqual(String.Empty, events[0].Payload[0]); 198 | } 199 | #endregion 200 | 201 | #region CustomSerializer Tests 202 | public interface ILogInterfaceWithClassDataToCustom 203 | { 204 | void SendData(ClassData data); 205 | } 206 | 207 | class CustomSerializer : TraceSerializationProvider 208 | { 209 | public override string SerializeObject(object value, TraceSerializationContext context) 210 | { 211 | return "custom"; 212 | } 213 | 214 | public override EventLevel? GetEventLevelForContext(TraceSerializationContext context) 215 | { 216 | return EventLevel.LogAlways; 217 | } 218 | 219 | public override bool ShouldSerialize(TraceSerializationContext context) 220 | { 221 | return true; 222 | } 223 | } 224 | 225 | [Test] 226 | public void InterfaceWithClassShouldSerializeToCustom() 227 | { 228 | // register the provider 229 | EventSourceImplementer.RegisterProvider(new CustomSerializer()); 230 | 231 | var logger = EventSourceImplementer.GetEventSourceAs(); 232 | _listener.EnableEvents((EventSource)logger, EventLevel.LogAlways); 233 | 234 | logger.SendData(ClassData.Test); 235 | 236 | // look at the events 237 | var events = _listener.Events.ToArray(); 238 | Assert.AreEqual(1, events.Length); 239 | Assert.AreEqual(1, events[0].EventId); 240 | Assert.AreEqual("custom", events[0].Payload[0]); 241 | } 242 | #endregion 243 | 244 | #region Provider Attribute Tests 245 | [TraceSerializationProvider(typeof(FakeSerializer))] 246 | public interface ILogInterfaceWithSerializationAttribute 247 | { 248 | void SendData(ClassData data); 249 | } 250 | 251 | [TraceSerializationProvider(typeof(FakeSerializer))] 252 | public interface ILogInterfaceWithAttribute2 253 | { 254 | void SendData(ClassData data); 255 | } 256 | 257 | public class FakeSerializer : TraceSerializationProvider 258 | { 259 | public override string SerializeObject(object value, TraceSerializationContext context) 260 | { 261 | return "nope"; 262 | } 263 | } 264 | 265 | [Test] 266 | public void AttributeShouldDetermineSerializer() 267 | { 268 | var logger = EventSourceImplementer.GetEventSourceAs(); 269 | _listener.EnableEvents((EventSource)logger, EventLevel.LogAlways); 270 | 271 | logger.SendData(ClassData.Test); 272 | 273 | // look at the events 274 | var events = _listener.Events.ToArray(); 275 | Assert.AreEqual(1, events.Length); 276 | Assert.AreEqual(1, events[0].EventId); 277 | Assert.AreEqual("nope", events[0].Payload[0]); 278 | } 279 | 280 | [Test] 281 | public void RegisterProviderShouldOverrideAttribute() 282 | { 283 | EventSourceImplementer.RegisterProvider(new JsonObjectSerializer(EventLevel.Verbose)); 284 | var logger = EventSourceImplementer.GetEventSourceAs(); 285 | 286 | _listener.EnableEvents((EventSource)logger, EventLevel.Informational); 287 | logger.SendData(ClassData.Test); 288 | 289 | // look at the events 290 | var events = _listener.Events.ToArray(); 291 | Assert.AreEqual(1, events.Length); 292 | Assert.AreEqual(1, events[0].EventId); 293 | Assert.AreEqual(String.Empty, events[0].Payload[0]); 294 | 295 | _listener.Reset(); 296 | _listener.EnableEvents((EventSource)logger, EventLevel.Verbose); 297 | logger.SendData(ClassData.Test); 298 | 299 | // look at the events 300 | events = _listener.Events.ToArray(); 301 | Assert.AreEqual(1, events.Length); 302 | Assert.AreEqual(1, events[0].EventId); 303 | Assert.AreEqual(ClassData.TestJson, events[0].Payload[0]); 304 | } 305 | #endregion 306 | 307 | #region Level Attribute Tests 308 | public interface ISerializeNormally 309 | { 310 | void SendData(ClassData data); 311 | } 312 | 313 | [TraceSerialization(EventLevel.Informational)] 314 | public interface ISerializeVerbosely 315 | { 316 | void SendData(ClassData data); 317 | } 318 | 319 | public interface ISerializeVerboselyByMethod 320 | { 321 | [TraceSerialization(EventLevel.Informational)] 322 | void SendData(ClassData data); 323 | } 324 | 325 | public interface ISerializeVerboselyByParameter 326 | { 327 | void SendData([TraceSerialization(EventLevel.Informational)]ClassData data); 328 | } 329 | 330 | [TraceSerialization(EventLevel.Informational)] 331 | public class ClassData2 : ClassData 332 | { 333 | } 334 | 335 | public interface ISerializeVerboselyByParameterClass 336 | { 337 | void SendData(ClassData2 data); 338 | } 339 | 340 | [Test] 341 | public void ShouldNormallyBeDisabledAtInfoAndEnabledAtVerbose() 342 | { 343 | var logger = EventSourceImplementer.GetEventSourceAs(); 344 | _listener.EnableEvents((EventSource)logger, EventLevel.Informational); 345 | 346 | logger.SendData(ClassData.Test); 347 | 348 | // look at the events 349 | var events = _listener.Events.ToArray(); 350 | Assert.AreEqual(1, events.Length); 351 | Assert.AreEqual(1, events[0].EventId); 352 | Assert.AreEqual(String.Empty, events[0].Payload[0]); 353 | 354 | _listener.Reset(); 355 | _listener.EnableEvents((EventSource)logger, EventLevel.Verbose); 356 | logger.SendData(ClassData.Test); 357 | events = _listener.Events.ToArray(); 358 | Assert.AreEqual(1, events.Length); 359 | Assert.AreEqual(1, events[0].EventId); 360 | Assert.IsNotNull(events[0].Payload[0]); 361 | } 362 | 363 | [Test] 364 | public void AttributeCanChangeLevelAtClassLevel() 365 | { 366 | var logger = EventSourceImplementer.GetEventSourceAs(); 367 | _listener.EnableEvents((EventSource)logger, EventLevel.Informational); 368 | 369 | logger.SendData(ClassData.Test); 370 | 371 | // look at the events 372 | var events = _listener.Events.ToArray(); 373 | Assert.AreEqual(1, events.Length); 374 | Assert.AreEqual(1, events[0].EventId); 375 | Assert.IsNotNull(events[0].Payload[0]); 376 | } 377 | 378 | [Test] 379 | public void AttributeCanChangeLevelAtMethodLevel() 380 | { 381 | var logger = EventSourceImplementer.GetEventSourceAs(); 382 | _listener.EnableEvents((EventSource)logger, EventLevel.Informational); 383 | 384 | logger.SendData(ClassData.Test); 385 | 386 | // look at the events 387 | var events = _listener.Events.ToArray(); 388 | Assert.AreEqual(1, events.Length); 389 | Assert.AreEqual(1, events[0].EventId); 390 | Assert.IsNotNull(events[0].Payload[0]); 391 | } 392 | 393 | [Test] 394 | public void AttributeCanChangeLevelAtParameterLevel() 395 | { 396 | var logger = EventSourceImplementer.GetEventSourceAs(); 397 | _listener.EnableEvents((EventSource)logger, EventLevel.Informational); 398 | 399 | logger.SendData(ClassData.Test); 400 | 401 | // look at the events 402 | var events = _listener.Events.ToArray(); 403 | Assert.AreEqual(1, events.Length); 404 | Assert.AreEqual(1, events[0].EventId); 405 | Assert.IsNotNull(events[0].Payload[0]); 406 | } 407 | 408 | [Test] 409 | public void AttributeCanChangeLevelAtParameterTypeLevel() 410 | { 411 | var logger = EventSourceImplementer.GetEventSourceAs(); 412 | _listener.EnableEvents((EventSource)logger, EventLevel.Informational); 413 | 414 | logger.SendData(new ClassData2() { Name = "Fred", Age = 38 }); 415 | 416 | // look at the events 417 | var events = _listener.Events.ToArray(); 418 | Assert.AreEqual(1, events.Length); 419 | Assert.AreEqual(1, events[0].EventId); 420 | Assert.IsNotNull(events[0].Payload[0]); 421 | } 422 | #endregion 423 | } 424 | } 425 | -------------------------------------------------------------------------------- /EventSourceProxy.Tests/TestEventListener.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Diagnostics.Tracing; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace EventSourceProxy.Tests 10 | { 11 | /// 12 | /// Listens to events and records them for testing. 13 | /// 14 | class TestEventListener : EventListener 15 | { 16 | public IReadOnlyCollection Events { get { return new ReadOnlyCollection(_events); } } 17 | private List _events = new List(); 18 | 19 | public void Reset() 20 | { 21 | _events.Clear(); 22 | } 23 | 24 | protected override void OnEventWritten(EventWrittenEventArgs eventData) 25 | { 26 | _events.Add(eventData); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /EventSourceProxy.Tests/TraceContextProviderTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.Tracing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using NUnit.Framework; 8 | 9 | namespace EventSourceProxy.Tests 10 | { 11 | [TestFixture] 12 | public class TraceContextProviderTests : BaseLoggingTest 13 | { 14 | #region Test Classes 15 | public interface ILog 16 | { 17 | void DoSomething(); 18 | } 19 | 20 | public interface ILog2 { } 21 | public interface ILog3 { } 22 | public interface ILog4 23 | { 24 | void DoSomething(); 25 | } 26 | 27 | class MyTraceContextProvider : TraceContextProvider 28 | { 29 | public bool WasCalled = false; 30 | public string Method = null; 31 | 32 | public override string ProvideContext(InvocationContext context) 33 | { 34 | Method = context.MethodInfo.Name; 35 | WasCalled = true; 36 | return "context"; 37 | } 38 | } 39 | #endregion 40 | 41 | #region Base Test Cases 42 | [Test] 43 | public void ProviderShouldBeCalledOnLog() 44 | { 45 | var contextProvider = new MyTraceContextProvider(); 46 | EventSourceImplementer.RegisterProvider(contextProvider); 47 | 48 | var testLog = EventSourceImplementer.GetEventSourceAs(); 49 | _listener.EnableEvents((EventSource)testLog, EventLevel.LogAlways); 50 | 51 | testLog.DoSomething(); 52 | 53 | Assert.IsTrue(contextProvider.WasCalled); 54 | Assert.AreEqual("DoSomething", contextProvider.Method); 55 | 56 | // look at the events 57 | var events = _listener.Events.ToArray(); 58 | Assert.AreEqual(1, events.Length); 59 | Assert.AreEqual("context", events[0].Payload[0]); 60 | } 61 | 62 | [Test] 63 | public void ProviderShouldNotBeCalledWhenLogIsDisabled() 64 | { 65 | var contextProvider = new MyTraceContextProvider(); 66 | EventSourceImplementer.RegisterProvider(contextProvider); 67 | 68 | var testLog = EventSourceImplementer.GetEventSourceAs(); 69 | testLog.DoSomething(); 70 | 71 | Assert.IsFalse(contextProvider.WasCalled); 72 | Assert.IsNull(contextProvider.Method); 73 | 74 | // look at the events 75 | var events = _listener.Events.ToArray(); 76 | Assert.AreEqual(0, events.Length); 77 | } 78 | 79 | [Test] 80 | public void RegisterProviderTwiceShouldFail() 81 | { 82 | var contextProvider = new MyTraceContextProvider(); 83 | EventSourceImplementer.RegisterProvider(contextProvider); 84 | Assert.Throws(() => EventSourceImplementer.RegisterProvider(contextProvider)); 85 | } 86 | 87 | [Test] 88 | public void RegisterProviderAfterSourceCreationShouldFail() 89 | { 90 | var log = EventSourceImplementer.GetEventSource(); 91 | 92 | var contextProvider = new MyTraceContextProvider(); 93 | Assert.Throws(() => EventSourceImplementer.RegisterProvider(contextProvider)); 94 | } 95 | #endregion 96 | 97 | #region Attribute Tests 98 | [TraceContextProvider(typeof(MyTraceContextProvider))] 99 | public interface ILogWithProviderAttribute 100 | { 101 | void DoSomething(); 102 | } 103 | 104 | [TraceContextProvider(typeof(MyTraceContextProvider))] 105 | [TraceContext(false)] 106 | public interface ILogWithAttributeDisabled 107 | { 108 | void DoSomething(); 109 | } 110 | 111 | [TraceContextProvider(typeof(MyTraceContextProvider))] 112 | [TraceContext(false)] 113 | public interface ILogWithAttributeDisabledAndMethodEnabled 114 | { 115 | [TraceContext(true)] 116 | void DoSomething(); 117 | } 118 | 119 | [Test] 120 | public void ProviderCanBeSpecifiedByAttribute() 121 | { 122 | var testLog = EventSourceImplementer.GetEventSourceAs(); 123 | _listener.EnableEvents((EventSource)testLog, EventLevel.LogAlways); 124 | 125 | testLog.DoSomething(); 126 | 127 | // look at the events 128 | var events = _listener.Events.ToArray(); 129 | Assert.AreEqual(1, events.Length); 130 | Assert.AreEqual("context", events[0].Payload[0]); 131 | } 132 | 133 | [Test] 134 | public void ContextCanBeControlledByClassAttribute() 135 | { 136 | var testLog = EventSourceImplementer.GetEventSourceAs(); 137 | _listener.EnableEvents((EventSource)testLog, EventLevel.LogAlways); 138 | 139 | testLog.DoSomething(); 140 | 141 | // look at the events 142 | var events = _listener.Events.ToArray(); 143 | Assert.AreEqual(1, events.Length); 144 | Assert.AreEqual(0, events[0].Payload.Count); 145 | } 146 | 147 | [Test] 148 | public void ContextCanBeControlledByMethodAttribute() 149 | { 150 | var testLog = EventSourceImplementer.GetEventSourceAs(); 151 | _listener.EnableEvents((EventSource)testLog, EventLevel.LogAlways); 152 | 153 | testLog.DoSomething(); 154 | 155 | // look at the events 156 | var events = _listener.Events.ToArray(); 157 | Assert.AreEqual(1, events.Length); 158 | Assert.AreEqual(1, events[0].Payload.Count); 159 | } 160 | #endregion 161 | 162 | #region Rich Context Tests 163 | public interface IHaveRichContext 164 | { 165 | void Log(string message); 166 | } 167 | 168 | [Test] 169 | public void TestAddingContextToEachMethod() 170 | { 171 | TraceParameterProvider.Default.For() 172 | .AddContextData("Site.Id") 173 | .AddContextData("Site.Name") 174 | .AddContextData("PID"); 175 | 176 | var proxy = EventSourceImplementer.GetEventSourceAs(); 177 | EnableLogging(proxy); 178 | 179 | using (var context = TraceContext.Begin()) 180 | { 181 | context["PID"] = 1234; 182 | 183 | using (var context2 = TraceContext.Begin()) 184 | { 185 | context2["Site.Id"] = "Ts1"; 186 | context2["Site.Name"] = "TestSite1"; 187 | 188 | proxy.Log("message"); 189 | } 190 | } 191 | 192 | // look at the events 193 | var events = _listener.Events.ToArray(); 194 | Assert.AreEqual(1, events.Length); 195 | Assert.AreEqual(4, events[0].Payload.Count); 196 | Assert.AreEqual("message", events[0].Payload[0]); 197 | Assert.AreEqual("Ts1", events[0].Payload[1]); 198 | Assert.AreEqual("TestSite1", events[0].Payload[2]); 199 | Assert.AreEqual(1234, events[0].Payload[3]); 200 | } 201 | #endregion 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /EventSourceProxy.nuspec: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | EventSourceProxy 5 | 1.0.0 6 | EventSourceProxy 7 | Jon Wagner 8 | 9 | Automatically implements the tedious part of the .NET EventSource classes. 10 | Also automatically converts any interface to an ETW trace point. 11 | Wrap your interfaces and get free ETW tracing. 12 | ** Now with .NET Standard 2.0 support! ** 13 | 14 | en-US 15 | http://github.com/jonwagner/EventSourceProxy 16 | MS-PL OR MIT 17 | EventSource log semantic structured strongly-typed logging ETW tracing event listener block lab LOB slab 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /EventSourceProxy.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.30110.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventSourceProxy", "EventSourceProxy\EventSourceProxy.csproj", "{40DABAAD-795A-4420-982F-08BE1978FE02}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventSourceProxy.Tests", "EventSourceProxy.Tests\EventSourceProxy.Tests.csproj", "{33A95AA5-4F87-47CF-B37A-2023CE9E0400}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventSourceProxy.Example", "EventSourceProxy.Example\EventSourceProxy.Example.csproj", "{581BDCB4-6413-4272-B85F-4D39F6C131B8}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GenerateProxyManifest", "GenerateProxyManifest\GenerateProxyManifest.csproj", "{A01CE647-4B33-43E0-8DD4-F45F732D4C77}" 13 | EndProject 14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{773540B5-F7A1-4E68-8343-C738E2183769}" 15 | ProjectSection(SolutionItems) = preProject 16 | Build.bat = Build.bat 17 | Build.ps1 = Build.ps1 18 | Build.psake.ps1 = Build.psake.ps1 19 | Building.txt = Building.txt 20 | EventSourceProxy.nuspec = EventSourceProxy.nuspec 21 | EventSourceProxy.snk = EventSourceProxy.snk 22 | README.md = README.md 23 | EndProjectSection 24 | EndProject 25 | Global 26 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 27 | Debug|Any CPU = Debug|Any CPU 28 | Release|Any CPU = Release|Any CPU 29 | EndGlobalSection 30 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 31 | {40DABAAD-795A-4420-982F-08BE1978FE02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {40DABAAD-795A-4420-982F-08BE1978FE02}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {40DABAAD-795A-4420-982F-08BE1978FE02}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {40DABAAD-795A-4420-982F-08BE1978FE02}.Release|Any CPU.Build.0 = Release|Any CPU 35 | {33A95AA5-4F87-47CF-B37A-2023CE9E0400}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 36 | {33A95AA5-4F87-47CF-B37A-2023CE9E0400}.Debug|Any CPU.Build.0 = Debug|Any CPU 37 | {33A95AA5-4F87-47CF-B37A-2023CE9E0400}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {33A95AA5-4F87-47CF-B37A-2023CE9E0400}.Release|Any CPU.Build.0 = Release|Any CPU 39 | {581BDCB4-6413-4272-B85F-4D39F6C131B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 40 | {581BDCB4-6413-4272-B85F-4D39F6C131B8}.Debug|Any CPU.Build.0 = Debug|Any CPU 41 | {581BDCB4-6413-4272-B85F-4D39F6C131B8}.Release|Any CPU.ActiveCfg = Release|Any CPU 42 | {581BDCB4-6413-4272-B85F-4D39F6C131B8}.Release|Any CPU.Build.0 = Release|Any CPU 43 | {A01CE647-4B33-43E0-8DD4-F45F732D4C77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 44 | {A01CE647-4B33-43E0-8DD4-F45F732D4C77}.Debug|Any CPU.Build.0 = Debug|Any CPU 45 | {A01CE647-4B33-43E0-8DD4-F45F732D4C77}.Release|Any CPU.ActiveCfg = Release|Any CPU 46 | {A01CE647-4B33-43E0-8DD4-F45F732D4C77}.Release|Any CPU.Build.0 = Release|Any CPU 47 | {B151A261-B9A0-4EE1-A61C-C04FFE94C373}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 48 | {B151A261-B9A0-4EE1-A61C-C04FFE94C373}.Debug|Any CPU.Build.0 = Debug|Any CPU 49 | {B151A261-B9A0-4EE1-A61C-C04FFE94C373}.Release|Any CPU.ActiveCfg = Release|Any CPU 50 | {B151A261-B9A0-4EE1-A61C-C04FFE94C373}.Release|Any CPU.Build.0 = Release|Any CPU 51 | {FDB8844B-BB34-45E6-BDE1-BA657663A868}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 52 | {FDB8844B-BB34-45E6-BDE1-BA657663A868}.Debug|Any CPU.Build.0 = Debug|Any CPU 53 | {FDB8844B-BB34-45E6-BDE1-BA657663A868}.Release|Any CPU.ActiveCfg = Release|Any CPU 54 | {FDB8844B-BB34-45E6-BDE1-BA657663A868}.Release|Any CPU.Build.0 = Release|Any CPU 55 | {0BF86E34-2AFC-4620-A5C6-E084448347A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 56 | {0BF86E34-2AFC-4620-A5C6-E084448347A2}.Debug|Any CPU.Build.0 = Debug|Any CPU 57 | {0BF86E34-2AFC-4620-A5C6-E084448347A2}.Release|Any CPU.ActiveCfg = Release|Any CPU 58 | {0BF86E34-2AFC-4620-A5C6-E084448347A2}.Release|Any CPU.Build.0 = Release|Any CPU 59 | EndGlobalSection 60 | GlobalSection(SolutionProperties) = preSolution 61 | HideSolutionNode = FALSE 62 | EndGlobalSection 63 | EndGlobal 64 | -------------------------------------------------------------------------------- /EventSourceProxy.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonwagner/EventSourceProxy/2d672e26afb8eb57e0e42daddd2f954d468a90b5/EventSourceProxy.snk -------------------------------------------------------------------------------- /EventSourceProxy/Any.cs: -------------------------------------------------------------------------------- 1 | namespace EventSourceProxy 2 | { 3 | /// 4 | /// Encapsulates a placeholder value that can be passed to a method. 5 | /// 6 | /// The type of the placeholder. 7 | public static class Any 8 | { 9 | /// 10 | /// Gets the default value for the Any type. 11 | /// 12 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")] 13 | public static TType Value 14 | { 15 | get { return default(TType); } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /EventSourceProxy/CodeAnalysisRules.ruleset: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /EventSourceProxy/CustomDictionary.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | GetEventSource 6 | EventSource 7 | EventActivityIdControl 8 | 9 | 10 | EventSource 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /EventSourceProxy/EventActivityScope.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Diagnostics.CodeAnalysis; 5 | using System.Globalization; 6 | using System.Linq; 7 | using System.Runtime.InteropServices; 8 | using System.Security; 9 | using System.Text; 10 | using System.Threading; 11 | using System.Threading.Tasks; 12 | 13 | namespace EventSourceProxy 14 | { 15 | /// 16 | /// Manages the lifetime of an ETW Activity ID. 17 | /// 18 | public sealed class EventActivityScope : IDisposable 19 | { 20 | #region Private Members 21 | /// 22 | /// The name of the call context slot we use for the activity ID. 23 | /// 24 | private static AsyncLocal _storage = new AsyncLocal(); 25 | 26 | /// 27 | /// The Activity ID outside of this scope. It is restored on the disposal of the scope. 28 | /// 29 | private Guid _previousActivityId; 30 | 31 | /// 32 | /// The Activity ID of this scope. 33 | /// 34 | private Guid _activityId; 35 | #endregion 36 | 37 | #region Constructors 38 | /// 39 | /// Initializes a new instance of the EventActivityScope class. 40 | /// A new Activity ID is generated and assigned to the current thread. 41 | /// 42 | public EventActivityScope() 43 | : this(false) 44 | { 45 | } 46 | 47 | /// 48 | /// Initializes a new instance of the EventActivityScope class. 49 | /// A new Activity ID is generated and assigned to the current thread. 50 | /// 51 | /// 52 | /// True to reuse an existing Activity ID if one is already in use. 53 | /// Since EventSource currently does not support Activity ID transfer, you 54 | /// may want to call this constructor with 'true' so you are sure to have an Activity ID, 55 | /// but to keep the current activity if one has already been established. 56 | /// 57 | public EventActivityScope(bool reuseExistingActivityId) 58 | { 59 | _previousActivityId = GetActivityId(); 60 | 61 | if (!reuseExistingActivityId || _previousActivityId == Guid.Empty) 62 | { 63 | _activityId = Guid.NewGuid(); 64 | SetActivityId(_activityId); 65 | } 66 | else 67 | _activityId = _previousActivityId; 68 | } 69 | 70 | /// 71 | /// Initializes a new instance of the EventActivityScope class with a given Activity ID. 72 | /// 73 | /// 74 | /// The existing Activity ID to use. 75 | /// 76 | public EventActivityScope(Guid externalActivityId) 77 | { 78 | _previousActivityId = GetActivityId(); 79 | _activityId = externalActivityId; 80 | SetActivityId(_activityId); 81 | } 82 | #endregion 83 | 84 | #region Properties 85 | /// 86 | /// Gets the current Activity Id. 87 | /// 88 | public static Guid CurrentActivityId 89 | { 90 | get 91 | { 92 | return GetActivityId(); 93 | } 94 | } 95 | 96 | /// 97 | /// Gets the Activity ID of the enclosing scope. 98 | /// 99 | public Guid PreviousActivityId { get { return _previousActivityId; } } 100 | 101 | /// 102 | /// Gets a value indicating whether the Activity ID of the enclosing scope. 103 | /// 104 | public bool IsNewScope { get { return _previousActivityId == Guid.Empty; } } 105 | 106 | /// 107 | /// Gets the Activity ID of this scope. 108 | /// 109 | public Guid ActivityId { get { return _activityId; } } 110 | #endregion 111 | 112 | /// 113 | /// Perform an action within an activity scope. 114 | /// This method ensures that an activity scope exists. 115 | /// If an activity scope exists, it is reused. 116 | /// 117 | /// The action to perform. 118 | public static void DoInScope(Action action) 119 | { 120 | Do(action, newScope: false); 121 | } 122 | 123 | /// 124 | /// Perform an action within a new activity scope. 125 | /// 126 | /// The action to perform. 127 | public static void DoInNewScope(Action action) 128 | { 129 | Do(action, newScope: true); 130 | } 131 | 132 | /// 133 | /// Disposes the current Activity Scope by restoring the previous scope. 134 | /// 135 | public void Dispose() 136 | { 137 | SetActivityId(_previousActivityId); 138 | _activityId = _previousActivityId; 139 | GC.SuppressFinalize(this); 140 | } 141 | 142 | #region Helper Methods 143 | /// 144 | /// Performs an action in an activity scope. 145 | /// 146 | /// The action to perform. 147 | /// True to always create a new scope. 148 | private static void Do(Action action, bool newScope) 149 | { 150 | Guid previousActivityId = GetActivityId(); 151 | if (newScope || previousActivityId == Guid.Empty) 152 | { 153 | Guid activityID = Guid.NewGuid(); 154 | try 155 | { 156 | SetActivityId(activityID); 157 | action(); 158 | } 159 | finally 160 | { 161 | SetActivityId(previousActivityId); 162 | } 163 | } 164 | } 165 | 166 | /// 167 | /// Get the current Activity Id. 168 | /// 169 | /// The current activity Id. 170 | private static Guid GetActivityId() 171 | { 172 | return Trace.CorrelationManager.ActivityId; 173 | } 174 | 175 | /// 176 | /// Sets the current Activity Id. 177 | /// 178 | /// The activity Id to set. 179 | private static void SetActivityId(Guid activityId) 180 | { 181 | Trace.CorrelationManager.ActivityId = activityId; 182 | } 183 | #endregion 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /EventSourceProxy/EventAttributeHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.Tracing; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Reflection.Emit; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace EventSourceProxy 11 | { 12 | /// 13 | /// Helps emit attribute data to IL. 14 | /// 15 | static class EventAttributeHelper 16 | { 17 | #region EventAttribute Members 18 | /// 19 | /// The constructor for EventAttribute. 20 | /// 21 | private static ConstructorInfo _eventAttributeConstructor = typeof(EventAttribute).GetConstructor(new[] { typeof(int) }); 22 | 23 | /// 24 | /// The array of properties used to serialize the custom attribute values. 25 | /// 26 | private static PropertyInfo[] _eventAttributePropertyInfo = new PropertyInfo[] 27 | { 28 | typeof(EventAttribute).GetProperty("Keywords"), 29 | typeof(EventAttribute).GetProperty("Level"), 30 | typeof(EventAttribute).GetProperty("Message"), 31 | typeof(EventAttribute).GetProperty("Opcode"), 32 | typeof(EventAttribute).GetProperty("Task"), 33 | typeof(EventAttribute).GetProperty("Version"), 34 | typeof(EventAttribute).GetProperty("Channel"), 35 | }; 36 | 37 | /// 38 | /// A set of empty parameters that can be sent to a method call. 39 | /// 40 | private static object[] _emptyParameters = new object[0]; 41 | #endregion 42 | 43 | /// 44 | /// Converts an EventAttribute to a CustomAttributeBuilder so it can be assigned to a method. 45 | /// 46 | /// The attribute to copy. 47 | /// A CustomAttributeBuilder that can be assigned to a method. 48 | internal static CustomAttributeBuilder ConvertEventAttributeToAttributeBuilder(EventAttribute attribute) 49 | { 50 | var propertyValues = new object[] 51 | { 52 | attribute.Keywords, 53 | attribute.Level, 54 | attribute.Message, 55 | attribute.Opcode, 56 | attribute.Task, 57 | attribute.Version, 58 | attribute.Channel 59 | }; 60 | 61 | CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder( 62 | _eventAttributeConstructor, 63 | new object[] { attribute.EventId }, 64 | _eventAttributePropertyInfo, 65 | propertyValues); 66 | 67 | return attributeBuilder; 68 | } 69 | 70 | /// 71 | /// Creates an empty NonEventAttribute. 72 | /// 73 | /// A CustomAttributeBuilder that can be added to a method. 74 | internal static CustomAttributeBuilder CreateNonEventAttribute() 75 | { 76 | return new CustomAttributeBuilder(typeof(NonEventAttribute).GetConstructor(Type.EmptyTypes), _emptyParameters); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /EventSourceProxy/EventAttributeProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.Tracing; 4 | using System.Globalization; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace EventSourceProxy 11 | { 12 | /// 13 | /// Provides the Event attributes when generating proxy classes. 14 | /// The default implementation uses EventAttribute and EventExceptionAttribute to generate the values. 15 | /// 16 | public class EventAttributeProvider 17 | { 18 | /// 19 | /// Initializes a new instance of the EventAttributeProvider class. 20 | /// 21 | public EventAttributeProvider() : this(EventLevel.Informational, EventLevel.Error) 22 | { 23 | } 24 | 25 | /// 26 | /// Initializes a new instance of the EventAttributeProvider class. 27 | /// 28 | /// The default EventLevel for events if not specified by EventAttributes. 29 | /// The default exception events if not specified by EventExceptionAttributes. 30 | public EventAttributeProvider(EventLevel eventLevel, EventLevel exceptionEventLevel) 31 | { 32 | EventLevel = eventLevel; 33 | ExceptionEventLevel = exceptionEventLevel; 34 | } 35 | 36 | /// 37 | /// Gets the default EventLevel for methods. 38 | /// 39 | public EventLevel EventLevel { get; private set; } 40 | 41 | /// 42 | /// Gets the default EventLevel for exceptions generated by methods. 43 | /// 44 | public EventLevel ExceptionEventLevel { get; private set; } 45 | 46 | /// 47 | /// Returns an EventAttribute for the given call context. 48 | /// 49 | /// The context of the call. 50 | /// The next event ID to use if not specified by some other mechanism. 51 | /// The parameter mapping for the method, or null if not a method call. 52 | /// The EventAttribute for the call context. 53 | public virtual EventAttribute GetEventAttribute(InvocationContext context, int nextEventId, IReadOnlyCollection parameterMapping) 54 | { 55 | if (context == null) throw new ArgumentNullException("context"); 56 | 57 | EventAttribute eventAttribute = context.MethodInfo.GetCustomAttribute(); 58 | if (eventAttribute != null) 59 | return eventAttribute; 60 | 61 | return new EventAttribute(nextEventId) 62 | { 63 | Level = GetEventLevelForContext(context, null), 64 | Message = GetEventMessage(context, parameterMapping) 65 | }; 66 | } 67 | 68 | /// 69 | /// Returns an EventAttribute for the Completed or Faulted events for a call context. 70 | /// 71 | /// The EventAttribute for the method call that should be copied. 72 | /// The context of the call. 73 | /// The next event ID to use if not specified by some other mechanism. 74 | /// The EventAttribute for the call context. 75 | public virtual EventAttribute CopyEventAttribute(EventAttribute baseAttribute, InvocationContext context, int nextEventId) 76 | { 77 | if (baseAttribute == null) throw new ArgumentNullException("baseAttribute"); 78 | if (context == null) throw new ArgumentNullException("context"); 79 | 80 | return new EventAttribute(nextEventId) 81 | { 82 | Keywords = baseAttribute.Keywords, 83 | Level = GetEventLevelForContext(context, baseAttribute), 84 | Message = GetEventMessage(context, null), 85 | Opcode = baseAttribute.Opcode, 86 | Task = baseAttribute.Task, 87 | Version = baseAttribute.Version 88 | }; 89 | } 90 | 91 | /// 92 | /// Gets the appropriate EventLevel for the call context. 93 | /// 94 | /// The context of the call. 95 | /// The base attribute to copy if there are no additional attributes. 96 | /// The EventLevel for the call context. 97 | protected virtual EventLevel GetEventLevelForContext(InvocationContext context, EventAttribute baseAttribute) 98 | { 99 | if (context == null) throw new ArgumentNullException("context"); 100 | 101 | // for faulted methods, allow the EventExceptionAttribute to override the event level 102 | if (context.ContextType == InvocationContextTypes.MethodFaulted) 103 | { 104 | var attribute = context.MethodInfo.GetCustomAttribute(); 105 | if (attribute != null) 106 | return attribute.Level; 107 | 108 | attribute = context.MethodInfo.DeclaringType.GetCustomAttribute(); 109 | if (attribute != null) 110 | return attribute.Level; 111 | 112 | return ExceptionEventLevel; 113 | } 114 | 115 | // check for an attribute on the type 116 | var implementationAttribute = context.MethodInfo.DeclaringType.GetCustomAttribute(); 117 | if (implementationAttribute != null && implementationAttribute.DefaultLevel.HasValue) 118 | return implementationAttribute.DefaultLevel.Value; 119 | 120 | if (baseAttribute != null) 121 | return baseAttribute.Level; 122 | 123 | return EventLevel; 124 | } 125 | 126 | /// 127 | /// Gets the message for an event. 128 | /// 129 | /// The context of the call. 130 | /// The parameter mapping for the method, or null if not a method call. 131 | /// The message for the event. 132 | protected virtual string GetEventMessage(InvocationContext context, IReadOnlyCollection parameterMapping) 133 | { 134 | if (context == null) throw new ArgumentNullException("context"); 135 | 136 | switch (context.ContextType) 137 | { 138 | case InvocationContextTypes.MethodCall: 139 | if (parameterMapping == null) throw new ArgumentNullException("parameterMapping"); 140 | return String.Join(" ", Enumerable.Range(0, parameterMapping.Count).Select(i => String.Format(CultureInfo.InvariantCulture, "{{{0}}}", i))); 141 | 142 | case InvocationContextTypes.MethodFaulted: 143 | return "{0}"; 144 | 145 | case InvocationContextTypes.MethodCompletion: 146 | if (context.MethodInfo.ReturnType != typeof(void)) 147 | return "{0}"; 148 | else 149 | return String.Empty; 150 | } 151 | 152 | return String.Empty; 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /EventSourceProxy/EventAttributeProviderAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace EventSourceProxy 8 | { 9 | /// 10 | /// Specifies the TraceSerializationProvider to use for a class or interface. 11 | /// 12 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)] 13 | public sealed class EventAttributeProviderAttribute : TraceProviderAttribute 14 | { 15 | /// 16 | /// Initializes a new instance of the EventAttributeProviderAttribute class. 17 | /// 18 | /// The type of the provider to assign to this class or interface. 19 | public EventAttributeProviderAttribute(Type providerType) 20 | : base(providerType) 21 | { 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /EventSourceProxy/EventExceptionAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Diagnostics.Tracing; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace EventSourceProxy 10 | { 11 | /// 12 | /// Specifies the EventLevel for an exception generated by a method. 13 | /// 14 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Method, AllowMultiple = false)] 15 | public sealed class EventExceptionAttribute : Attribute 16 | { 17 | /// 18 | /// Initializes a new instance of the EventExceptionAttribute class. 19 | /// 20 | /// The EventLevel to use for exceptions generated by a method. 21 | public EventExceptionAttribute(EventLevel level) 22 | { 23 | Level = level; 24 | } 25 | 26 | /// 27 | /// Gets the EventLevel to use for exceptions generated by a method. 28 | /// 29 | public EventLevel Level { get; private set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /EventSourceProxy/EventSourceAttributeHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Diagnostics.Tracing; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Reflection.Emit; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace EventSourceProxy 12 | { 13 | /// 14 | /// Helps emit attribute data to IL. 15 | /// 16 | static class EventSourceAttributeHelper 17 | { 18 | #region EventSourceAttribute Members 19 | /// 20 | /// The constructor for EventSourceAttribute. 21 | /// 22 | private static ConstructorInfo _eventSourceAttributeConstructor = typeof(EventSourceAttribute).GetConstructor(Type.EmptyTypes); 23 | 24 | /// 25 | /// The array of properties used to serialize the custom attribute values. 26 | /// 27 | private static PropertyInfo[] _eventSourceAttributePropertyInfo = new PropertyInfo[] 28 | { 29 | typeof(EventSourceAttribute).GetProperty("Name"), 30 | typeof(EventSourceAttribute).GetProperty("Guid"), 31 | typeof(EventSourceAttribute).GetProperty("LocalizationResources"), 32 | }; 33 | 34 | /// 35 | /// Manages a list of the types that have been implemented so far. 36 | /// 37 | private static List _typesImplemented = new List(); 38 | 39 | /// 40 | /// An empty parameter list. 41 | /// 42 | private static object[] _emptyParameters = new object[0]; 43 | #endregion 44 | 45 | #region Helper Methods 46 | /// 47 | /// Copies the EventSourceAttribute from the interfaceType to a CustomAttributeBuilder. 48 | /// 49 | /// The interfaceType to copy. 50 | /// A CustomAttributeBuilder that can be assigned to a type. 51 | internal static CustomAttributeBuilder GetEventSourceAttributeBuilder(Type type) 52 | { 53 | var attribute = type.GetCustomAttribute() ?? new EventSourceAttribute(); 54 | var implementation = EventSourceImplementationAttribute.GetAttributeFor(type); 55 | 56 | // by default, we will use a null guid, which will tell EventSource to generate the guid from the name 57 | // but if we have already generated this type, we will have to generate a new one 58 | string guid = implementation.Guid ?? attribute.Guid ?? null; 59 | if (guid == null) 60 | { 61 | lock (_typesImplemented) 62 | { 63 | if (_typesImplemented.Contains(type)) 64 | guid = Guid.NewGuid().ToString(); 65 | else 66 | _typesImplemented.Add(type); 67 | } 68 | } 69 | 70 | var propertyValues = new object[] 71 | { 72 | implementation.Name ?? attribute.Name ?? (type.IsGenericType ? type.FullName : type.Name), 73 | guid, 74 | implementation.LocalizationResources ?? attribute.LocalizationResources ?? null 75 | }; 76 | 77 | CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder( 78 | _eventSourceAttributeConstructor, 79 | _emptyParameters, 80 | _eventSourceAttributePropertyInfo, 81 | propertyValues); 82 | 83 | return attributeBuilder; 84 | } 85 | #endregion 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /EventSourceProxy/EventSourceImplementationAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using System.Collections.Concurrent; 4 | using System.Collections.Generic; 5 | using System.Diagnostics.Tracing; 6 | using System.Reflection; 7 | 8 | namespace EventSourceProxy 9 | { 10 | /// 11 | /// Specifies the classes to use for the Keywords, Tasks, and Opcodes enumerations for an EventSource. 12 | /// 13 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)] 14 | public class EventSourceImplementationAttribute : Attribute 15 | { 16 | #region Static Fields 17 | /// 18 | /// The overridden attributes 19 | /// 20 | private static ConcurrentDictionary _attributes = new ConcurrentDictionary(); 21 | #endregion 22 | 23 | /// 24 | /// Specifies whether complement methods should be emitted. 25 | /// 26 | private bool _implementComplementMethods = true; 27 | 28 | /// 29 | /// Gets or sets the name of the EventSource. This overrides any EventSource attribute. 30 | /// 31 | public string Name { get; set; } 32 | 33 | /// 34 | /// Gets or sets a value indicating whether event write errors should throw. Used when constructing the EventSource. This specifies whether to throw an exception when an error occurs in the underlying Windows code. Default is false. 35 | /// 36 | public bool ThrowOnEventWriteErrors { get; set; } 37 | 38 | /// 39 | /// Gets or sets a value indicating whether the guid of the EventSource. This overrides any EventSource attribute. 40 | /// 41 | public string Guid { get; set; } 42 | 43 | /// 44 | /// Gets or sets the LocalizationResources of the EventSource. This overrides any EventSource attribute. 45 | /// 46 | public string LocalizationResources { get; set; } 47 | 48 | /// 49 | /// Gets or sets a value indicating whether the EventSource should auto-generate keywords. 50 | /// 51 | public bool AutoKeywords { get; set; } 52 | 53 | /// 54 | /// Gets or sets the type that contains the Keywords enumeration for the EventSource. 55 | /// 56 | public Type Keywords { get; set; } 57 | 58 | /// 59 | /// Gets or sets the type that contains the Tasks enumeration for the EventSource. 60 | /// 61 | public Type Tasks { get; set; } 62 | 63 | /// 64 | /// Gets or sets the type that contains the Opcodes enumeration for the EventSource. 65 | /// 66 | public Type OpCodes { get; set; } 67 | 68 | /// 69 | /// Gets or sets the default event level for the EventSource. Defaults to Verbose. 70 | /// 71 | public EventLevel Level { get; set; } 72 | internal EventLevel? DefaultLevel { get; set; } 73 | 74 | /// 75 | /// Gets or sets a value indicating whether the _Completed and _Faulted methods should be implemented 76 | /// on the EventSource. The default (null) indicates that complement methods are implemented on all classes 77 | /// that do not derive from EventSource. 78 | /// 79 | public bool ImplementComplementMethods 80 | { 81 | get { return _implementComplementMethods; } 82 | set { _implementComplementMethods = value; } 83 | } 84 | 85 | /// 86 | /// Overrides the EventSourceImplementationAttribute for a type. Allows you to define logging for other people's interfaces. 87 | /// 88 | /// The type of interface we are overriding. 89 | /// The new EventSourceImplementationAttribute for the type. 90 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")] 91 | public static void For(EventSourceImplementationAttribute attribute) 92 | { 93 | if (attribute == null) throw new ArgumentNullException("attribute"); 94 | 95 | _attributes.AddOrUpdate(typeof(T), attribute, (t, a) => a); 96 | } 97 | 98 | /// 99 | /// Get the EventSourceImplementationAttribute for a type. 100 | /// 101 | /// The type. 102 | /// The attribute. 103 | internal static EventSourceImplementationAttribute GetAttributeFor(Type type) 104 | { 105 | EventSourceImplementationAttribute attribute; 106 | if (_attributes.TryGetValue(type, out attribute)) 107 | return attribute; 108 | 109 | return type.GetCustomAttribute() ?? new EventSourceImplementationAttribute(); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /EventSourceProxy/EventSourceImplementer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Diagnostics.Tracing; 5 | using System.Globalization; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Reflection.Emit; 9 | using System.Security.Cryptography; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | 13 | namespace EventSourceProxy 14 | { 15 | /// 16 | /// Completes the implementation of an EventSource by generating a class from an interface or from an abstract class. 17 | /// 18 | public static class EventSourceImplementer 19 | { 20 | #region Private Members 21 | /// 22 | /// The cache of constructors. 23 | /// 24 | private static ConcurrentDictionary _eventSources = new ConcurrentDictionary(); 25 | #endregion 26 | 27 | #region Public Members 28 | /// 29 | /// Gets or sets a value indicating whether EventSources should always have auto-keywords. 30 | /// Set this to true if you were using ESP before v3.0 and need auto-keywords to be on. 31 | /// 32 | public static bool ForceAutoKeywords { get; set; } 33 | 34 | /// 35 | /// Implements an EventSource that matches the virtual or abstract methods of a type. 36 | /// If the type is an interface, this creates a type derived from EventSource that implements the interface. 37 | /// If the type is a class derived from EventSource, this derives from the type and implements any abstract methods. 38 | /// If the type is a class not derived from EventSource, this method fails while casting to T. Use GetEventSource instead. 39 | /// 40 | /// An type to implement as an EventSource. 41 | /// An EventSource that is compatible with the given type. 42 | public static T GetEventSourceAs() where T : class 43 | { 44 | // if it's not an interface or a subclass of EventSource 45 | // then we have to make an entirely new class that isn't derived from T 46 | // and the cast below will fail. Give the programmer a hint. 47 | if (!typeof(T).IsInterface && !typeof(T).IsSubclassOf(typeof(EventSource))) 48 | throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "{0} must be derived from EventSource. Use GetEventSource instead.", typeof(T).FullName)); 49 | 50 | return (T)(object)GetEventSource(typeof(T)); 51 | } 52 | 53 | /// 54 | /// Implements an EventSource that matches the virtual or abstract methods of a type. 55 | /// If the type is an interface, this creates a type derived from EventSource that implements the interface. 56 | /// If the type is a class derived from EventSource, this derives from the type and implements any abstract methods. 57 | /// If the type is a class not derived from EventSource, this creates a type derived from EventSource that implements 58 | /// method that match the virtual methods of the target type. 59 | /// 60 | /// An type to implement as an EventSource. 61 | /// An EventSource that is compatible with the given type. 62 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")] 63 | public static EventSource GetEventSource() where T : class 64 | { 65 | return GetEventSource(typeof(T)); 66 | } 67 | 68 | /// 69 | /// Implements an EventSource that matches the virtual or abstract methods of a type. 70 | /// If the type is an interface, this creates a type derived from EventSource that implements the interface. 71 | /// If the type is a class derived from EventSource, this derives from the type and implements any abstract methods. 72 | /// If the type is a class not derived from EventSource, this creates a type derived from EventSource that implements 73 | /// method that match the virtual methods of the target type. 74 | /// 75 | /// An type to implement as an EventSource. 76 | /// An EventSource that is compatible with the given type. 77 | public static EventSource GetEventSource(Type type) 78 | { 79 | lock (_eventSources) 80 | { 81 | return (EventSource)_eventSources.GetOrAdd(type, t => new TypeImplementer(t).EventSource); 82 | } 83 | } 84 | 85 | /// 86 | /// Registers a Context Provider for a given event source. 87 | /// 88 | /// The type of event source to register with. 89 | /// The provider to register. 90 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")] 91 | public static void RegisterProvider(TraceContextProvider provider) 92 | { 93 | RegisterProvider(typeof(TLog), typeof(TraceContextProvider), provider); 94 | } 95 | 96 | /// 97 | /// Registers a Context Provider for a given event source. 98 | /// 99 | /// The type of event source to register with. 100 | /// The provider to register. 101 | public static void RegisterProvider(Type type, TraceContextProvider provider) 102 | { 103 | RegisterProvider(type, typeof(TraceContextProvider), provider); 104 | } 105 | 106 | /// 107 | /// Registers a default TraceContextProvider for all event sources. 108 | /// 109 | /// The provider to register. 110 | public static void RegisterDefaultProvider(TraceContextProvider provider) 111 | { 112 | RegisterProvider(null, provider); 113 | } 114 | 115 | /// 116 | /// Registers a Serialization Provider for a given event source. 117 | /// 118 | /// The type of event source to register with. 119 | /// The provider to register. 120 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")] 121 | public static void RegisterProvider(TraceSerializationProvider provider) 122 | { 123 | RegisterProvider(typeof(TLog), typeof(TraceSerializationProvider), provider); 124 | } 125 | 126 | /// 127 | /// Registers a Serialization Provider for a given event source. 128 | /// 129 | /// The type of event source to register with. 130 | /// The provider to register. 131 | public static void RegisterProvider(Type type, TraceSerializationProvider provider) 132 | { 133 | RegisterProvider(type, typeof(TraceSerializationProvider), provider); 134 | } 135 | 136 | /// 137 | /// Registers a default TraceSerializationProvider for all event sources. 138 | /// 139 | /// The provider to register. 140 | public static void RegisterDefaultProvider(TraceSerializationProvider provider) 141 | { 142 | RegisterProvider(null, provider); 143 | } 144 | 145 | /// 146 | /// Registers an EventAttributeProvider for a given event source. 147 | /// 148 | /// The type of event source to register with. 149 | /// The provider to register. 150 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")] 151 | public static void RegisterProvider(EventAttributeProvider provider) 152 | { 153 | RegisterProvider(typeof(TLog), typeof(EventAttributeProvider), provider); 154 | } 155 | 156 | /// 157 | /// Registers a EventAttributeProvider for a given event source. 158 | /// 159 | /// The type of event source to register with. 160 | /// The provider to register. 161 | public static void RegisterProvider(Type type, EventAttributeProvider provider) 162 | { 163 | RegisterProvider(type, typeof(EventAttributeProvider), provider); 164 | } 165 | 166 | /// 167 | /// Registers a default EventAttributeProvider for all event sources. 168 | /// 169 | /// The provider to register. 170 | public static void RegisterDefaultProvider(EventAttributeProvider provider) 171 | { 172 | RegisterProvider(null, provider); 173 | } 174 | 175 | /// 176 | /// Registers an TraceParameterProvider for a given event source. 177 | /// 178 | /// The type of event source to register with. 179 | /// The provider to register. 180 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")] 181 | public static void RegisterProvider(TraceParameterProvider provider) 182 | { 183 | RegisterProvider(typeof(TLog), typeof(TraceParameterProvider), provider); 184 | } 185 | 186 | /// 187 | /// Registers a TraceParameterProvider for a given event source. 188 | /// 189 | /// The type of event source to register with. 190 | /// The provider to register. 191 | public static void RegisterProvider(Type type, TraceParameterProvider provider) 192 | { 193 | RegisterProvider(type, typeof(TraceParameterProvider), provider); 194 | } 195 | 196 | /// 197 | /// Registers a default TraceParameterProvider for all event sources. 198 | /// 199 | /// The provider to register. 200 | public static void RegisterDefaultProvider(TraceParameterProvider provider) 201 | { 202 | RegisterProvider(null, provider); 203 | } 204 | 205 | /// 206 | /// Gets the keyword value for a method on a type. 207 | /// 208 | /// The type of the EventSource. 209 | /// The name of the method. 210 | /// The keyword value. 211 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")] 212 | public static EventKeywords GetKeywordValue(string methodName) where T : class 213 | { 214 | if (methodName == null) throw new ArgumentNullException("methodName"); 215 | 216 | var logType = GetEventSourceAs().GetType(); 217 | 218 | var keywordType = logType.GetNestedType("Keywords"); 219 | if (keywordType == null) 220 | throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Keywords have not been defined for {0}", typeof(T))); 221 | 222 | var keyword = keywordType.GetField(methodName); 223 | if (keyword == null) 224 | throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Keyword has not been defined for {1} on {0}", typeof(T), methodName)); 225 | 226 | return (EventKeywords)keyword.GetValue(null); 227 | } 228 | #endregion 229 | 230 | #region Internal Members 231 | /// 232 | /// Registers a Provider for a given event source. 233 | /// 234 | /// The type of event source to register with. If null, then the default provider is overridden. 235 | /// The type of provider being provided. 236 | /// The provider to register. 237 | private static void RegisterProvider(Type logType, Type providerType, object provider) 238 | { 239 | if (providerType == null) throw new ArgumentNullException("providerType"); 240 | 241 | lock (_eventSources) 242 | { 243 | // if the eventsource already exists, then fail 244 | if (logType != null && _eventSources.ContainsKey(logType)) 245 | throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Cannot register {0} after creating a log for type {1}", providerType.Name, logType.Name)); 246 | 247 | ProviderManager.RegisterProvider(logType, providerType, provider); 248 | } 249 | } 250 | #endregion 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /EventSourceProxy/EventSourceManifest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.Tracing; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Security.Cryptography; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace EventSourceProxy 11 | { 12 | /// 13 | /// Exposes ETW manifest information for provider types. 14 | /// 15 | public static class EventSourceManifest 16 | { 17 | #region Metadata Members 18 | /// 19 | /// Return the GUID of a provider name. 20 | /// 21 | /// The name of the provider. 22 | /// The GUID representing the name. 23 | public static Guid GetGuidFromProviderName(string providerName) 24 | { 25 | if (providerName == null) throw new ArgumentNullException("providerName"); 26 | 27 | string name = providerName.ToUpperInvariant(); 28 | byte[] buffer = new byte[(name.Length * 2) + 0x10]; 29 | uint num = 0x482c2db2; 30 | uint num2 = 0xc39047c8; 31 | uint num3 = 0x87f81a15; 32 | uint num4 = 0xbfc130fb; 33 | for (int i = 3; 0 <= i; i--) 34 | { 35 | buffer[i] = (byte)num; 36 | num = num >> 8; 37 | buffer[i + 4] = (byte)num2; 38 | num2 = num2 >> 8; 39 | buffer[i + 8] = (byte)num3; 40 | num3 = num3 >> 8; 41 | buffer[i + 12] = (byte)num4; 42 | num4 = num4 >> 8; 43 | } 44 | 45 | for (int j = 0; j < name.Length; j++) 46 | { 47 | buffer[((2 * j) + 0x10) + 1] = (byte)name[j]; 48 | buffer[(2 * j) + 0x10] = (byte)(name[j] >> 8); 49 | } 50 | 51 | using (SHA1 sha = SHA1.Create()) 52 | { 53 | byte[] buffer2 = sha.ComputeHash(buffer); 54 | int a = (((((buffer2[3] << 8) + buffer2[2]) << 8) + buffer2[1]) << 8) + buffer2[0]; 55 | short b = (short)((buffer2[5] << 8) + buffer2[4]); 56 | short num9 = (short)((buffer2[7] << 8) + buffer2[6]); 57 | return new System.Guid(a, b, (short)((num9 & 0xfff) | 0x5000), buffer2[8], buffer2[9], buffer2[10], buffer2[11], buffer2[12], buffer2[13], buffer2[14], buffer2[15]); 58 | } 59 | } 60 | 61 | /// 62 | /// Return the GUID of a provider. 63 | /// 64 | /// The provider type. 65 | /// The GUID representing the name. 66 | public static Guid GetGuid(Type type) 67 | { 68 | EventSource eventSource = EventSourceImplementer.GetEventSource(type); 69 | return eventSource.Guid; 70 | } 71 | 72 | /// 73 | /// Return the manifest of a provider for the given type. 74 | /// 75 | /// The provider type. 76 | /// The XML manifest content. 77 | public static string GenerateManifest(Type type) 78 | { 79 | EventSource eventSource = EventSourceImplementer.GetEventSource(type); 80 | return EventSource.GenerateManifest(eventSource.GetType(), Assembly.GetAssembly(type).Location); 81 | } 82 | #endregion 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /EventSourceProxy/EventSourceProxy.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0;net46 5 | true 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /EventSourceProxy/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. 3 | // Project-level suppressions either have no target or are given 4 | // a specific target and scoped to a namespace, type, member, etc. 5 | // 6 | // To add a suppression to this file, right-click the message in the 7 | // Code Analysis results, point to "Suppress Message", and click 8 | // "In Suppression File". 9 | // You do not need to add suppressions to this file manually. 10 | 11 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA2210:AssembliesShouldHaveValidStrongNames")] 12 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "With", Scope = "member", Target = "EventSourceProxy.IParameterBuilder.#With`1()")] 13 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "And", Scope = "member", Target = "EventSourceProxy.IParameterBuilderWithParameter.#And(System.String[])")] 14 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "With", Scope = "member", Target = "EventSourceProxy.IParameterBuilder.#With`1(System.String)")] 15 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "And", Scope = "member", Target = "EventSourceProxy.IParameterBuilderWithParameter.#And`2(System.Linq.Expressions.Expression`1>[])")] 16 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Alias", Scope = "member", Target = "EventSourceProxy.IParameterBuilderWithParameter.#TogetherAs(System.String)")] 17 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "With", Scope = "member", Target = "EventSourceProxy.IParameterBuilderWithType`1.#With`1()")] 18 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "With", Scope = "member", Target = "EventSourceProxy.IParameterBuilderWithType`1.#With`1(System.String)")] 19 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "And", Scope = "member", Target = "EventSourceProxy.IParameterBuilderWithTypeAndParameter`1.#And(System.String[])")] 20 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "And", Scope = "member", Target = "EventSourceProxy.IParameterBuilderWithTypeAndParameter`1.#And`1(System.Linq.Expressions.Expression`1>[])")] 21 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "As", Scope = "member", Target = "EventSourceProxy.IParameterBuilderWithTypeAndParameter`1.#As(System.String[])")] 22 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Alias", Scope = "member", Target = "EventSourceProxy.IParameterBuilderWithTypeAndParameter`1.#TogetherAs(System.String)")] 23 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "With", Scope = "member", Target = "EventSourceProxy.IParameterBuilderWithTypeAndParameter`1.#With`1()")] 24 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "With", Scope = "member", Target = "EventSourceProxy.IParameterBuilderWithTypeAndParameter`1.#With`1(System.String)")] 25 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Scope = "member", Target = "EventSourceProxy.IParameterBuilder.#Ignore`1()")] 26 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "As", Scope = "member", Target = "EventSourceProxy.IParameterBuilderWithParameter.#As(System.String[])")] 27 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "For", Scope = "member", Target = "EventSourceProxy.IParameterBuilderBase.#For`1()")] 28 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "For", Scope = "member", Target = "EventSourceProxy.IParameterBuilderBase.#For`1(System.Linq.Expressions.Expression`1>)")] 29 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "With", Scope = "member", Target = "EventSourceProxy.IParameterBuilderBase.#With`1()")] 30 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "With", Scope = "member", Target = "EventSourceProxy.IParameterBuilderBase.#With`1(System.String)")] 31 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "EventSourceProxy.ParameterMapping.#AddSource(System.Reflection.ParameterInfo,System.String,System.Linq.Expressions.LambdaExpression)")] 32 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Alias", Scope = "member", Target = "EventSourceProxy.IParameterBuilder.#WithContext`1(System.String,System.Linq.Expressions.Expression`1>)")] 33 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "EventSourceProxy.IParameterBuilder.#WithContext`1(System.String,System.Linq.Expressions.Expression`1>)")] 34 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Alias", Scope = "member", Target = "EventSourceProxy.IParameterBuilder.#AddContext`1(System.String,System.Linq.Expressions.Expression`1>)")] 35 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "EventSourceProxy.IParameterBuilder.#AddContext`1(System.String,System.Linq.Expressions.Expression`1>)")] 36 | -------------------------------------------------------------------------------- /EventSourceProxy/InvocationContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.Tracing; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Reflection.Emit; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace EventSourceProxy 11 | { 12 | /// 13 | /// Specifies the invocation of a method and the type of the invocation (MethodCall, MethodCompletion, or MethodFaulted). 14 | /// 15 | public class InvocationContext 16 | { 17 | /// 18 | /// Initializes a new instance of the InvocationContext class. 19 | /// 20 | /// The handle of the method being invoked. 21 | /// The context type for this invocation. 22 | internal InvocationContext(MethodInfo methodInfo, InvocationContextTypes contextType) 23 | { 24 | MethodInfo = methodInfo; 25 | ContextType = contextType; 26 | } 27 | 28 | /// 29 | /// Gets the EventSource associated with the InvocationContext. 30 | /// 31 | public EventSource EventSource { get; internal set; } 32 | 33 | /// 34 | /// Gets the method being invoked. 35 | /// 36 | public MethodInfo MethodInfo { get; private set; } 37 | 38 | /// 39 | /// Gets the type of the invocation. 40 | /// 41 | public InvocationContextTypes ContextType { get; private set; } 42 | 43 | /// 44 | /// Creates a clone of this InvocationContext, changing the type of the context. 45 | /// 46 | /// The new InvocationContextType. 47 | /// A clone of this InvocationContext with a new context type. 48 | internal InvocationContext SpecifyType(InvocationContextTypes contextType) 49 | { 50 | InvocationContext context = (InvocationContext)this.MemberwiseClone(); 51 | context.ContextType = contextType; 52 | return context; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /EventSourceProxy/InvocationContextTypes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace EventSourceProxy 8 | { 9 | /// 10 | /// Specifies the type of invocation of the method. 11 | /// 12 | [Flags] 13 | public enum InvocationContextTypes 14 | { 15 | /// 16 | /// The invocation is the method call. 17 | /// 18 | MethodCall = 1 << 0, 19 | 20 | /// 21 | /// The invocation is the completion of the method. 22 | /// The parameter is the return value, if any. 23 | /// 24 | MethodCompletion = 1 << 1, 25 | 26 | /// 27 | /// The invocation is the exception event. 28 | /// The parameter is the exception. 29 | /// 30 | MethodFaulted = 1 << 2, 31 | 32 | /// 33 | /// No types of method invocations. 34 | /// 35 | None = 0, 36 | 37 | /// 38 | /// The invocation is to bundle a set of parameters into a single parameter. 39 | /// The parameter is a string-to-string map. 40 | /// 41 | BundleParameters = 1 << 4, 42 | 43 | /// 44 | /// All types of method invocations. 45 | /// 46 | All = MethodCall | MethodCompletion | MethodFaulted | BundleParameters 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /EventSourceProxy/JsonObjectSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Diagnostics.Tracing; 5 | using System.Globalization; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using Newtonsoft.Json; 10 | 11 | namespace EventSourceProxy 12 | { 13 | /// 14 | /// Used internally to serialize a string. By default, it uses Newtonsoft.Json to JSON serialize the object. 15 | /// 16 | public class JsonObjectSerializer : TraceSerializationProvider 17 | { 18 | #region Constructors 19 | /// 20 | /// Initializes a new instance of the JsonObjectSerializer class. 21 | /// The default is to serialize objects only in Verbose tracing. 22 | /// 23 | public JsonObjectSerializer() : base(EventLevel.Verbose) 24 | { 25 | } 26 | 27 | /// 28 | /// Initializes a new instance of the JsonObjectSerializer class. 29 | /// 30 | /// 31 | /// The default EventLevel to allow object serialization. 32 | /// The default is to serialize objects whenever tracing occurs, but this can be used to allow serialization 33 | /// only when logging is at a particular level of verbosity. 34 | /// 35 | public JsonObjectSerializer(EventLevel defaultEventLevel) : base(defaultEventLevel) 36 | { 37 | } 38 | #endregion 39 | 40 | /// 41 | /// Serializes an object to a string. 42 | /// 43 | /// The object to serialize. 44 | /// The context of the serialization. 45 | /// The serialized representation of the object. 46 | [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Logging should not affect program behavior.")] 47 | public override string SerializeObject(object value, TraceSerializationContext context) 48 | { 49 | try 50 | { 51 | // if we have a task, don't attempt to serialize the task if it's not completed 52 | Task t = value as Task; 53 | if (t != null && !t.IsCompleted) 54 | { 55 | return JsonConvert.SerializeObject(new { TaskId = t.Id }); 56 | } 57 | 58 | return JsonConvert.SerializeObject(value); 59 | } 60 | catch (Exception e) 61 | { 62 | // don't let serialization exceptions blow up processing 63 | return String.Format(CultureInfo.InvariantCulture, "{{ Exception: '{0}' }}", e.Message.Replace("'", "\\'")); 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /EventSourceProxy/NullObjectSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.Tracing; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Newtonsoft.Json; 9 | 10 | namespace EventSourceProxy 11 | { 12 | /// 13 | /// Serializes an object by returning null. This is effectively a NoOp. 14 | /// 15 | public class NullObjectSerializer : TraceSerializationProvider 16 | { 17 | #region Constructors 18 | /// 19 | /// Initializes a new instance of the NullObjectSerializer class. 20 | /// The Null serializer serializes everything as null. 21 | /// 22 | public NullObjectSerializer() 23 | { 24 | } 25 | #endregion 26 | 27 | /// 28 | /// Serializes an object to a string. 29 | /// 30 | /// The object to serialize. 31 | /// The context of the serialization. 32 | /// The serialized representation of the object. 33 | public override string SerializeObject(object value, TraceSerializationContext context) 34 | { 35 | return String.Empty; 36 | } 37 | 38 | /// 39 | /// Returns if the should the given parameter be serialized. 40 | /// 41 | /// The context of the serialization. 42 | /// True if the value should be serialized, false otherwise. 43 | public override bool ShouldSerialize(TraceSerializationContext context) 44 | { 45 | return true; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /EventSourceProxy/ParameterBuilderValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | 7 | namespace EventSourceProxy 8 | { 9 | /// 10 | /// Represents a value to be extracted from a parameter list. 11 | /// 12 | class ParameterBuilderValue 13 | { 14 | /// 15 | /// Initializes a new instance of the ParameterBuilderValue class. 16 | /// 17 | /// The name of the parameter to bind to, or null to bind to parameters of any name. 18 | /// The name to use to trace the parameter. 19 | /// The type of parameter to filter on. 20 | /// An expression used to convert the parameter to another value, or null to use the parameter as it is. 21 | public ParameterBuilderValue(string parameterName, string alias, Type parameterType, LambdaExpression converter) 22 | { 23 | Alias = alias; 24 | ParameterName = parameterName; 25 | 26 | Converter = converter; 27 | 28 | if (converter != null && converter.Parameters.Count > 0) 29 | ParameterType = parameterType ?? converter.Parameters[0].Type; 30 | else 31 | ParameterType = parameterType; 32 | } 33 | 34 | /// 35 | /// Gets or sets the name to use to log the value. 36 | /// 37 | public string Alias { get; set; } 38 | 39 | /// 40 | /// Gets the name of the parameter to bind to, or null, representing binding to a parameter of any name. 41 | /// 42 | public string ParameterName { get; private set; } 43 | 44 | /// 45 | /// Gets the type of parameter to bind to. 46 | /// 47 | public Type ParameterType { get; private set; } 48 | 49 | /// 50 | /// Gets the expression to use to convert the value. If set, the value will only bind to parameters matching the given type. 51 | /// 52 | public LambdaExpression Converter { get; private set; } 53 | 54 | /// 55 | /// Gets or sets a value indicating whether the parameter should be ignored. 56 | /// 57 | public bool Ignore { get; set; } 58 | 59 | /// 60 | /// Returns all of the parameters that match this value's name and converter. 61 | /// 62 | /// The parameter to evaluate. 63 | /// An enumeration of the matching parameters. 64 | public bool Matches(ParameterInfo parameter) 65 | { 66 | // if both name and type are not specified, then this only matches the null parameter 67 | // otherwise we will match every parameter 68 | if (ParameterName == null && ParameterType == null) 69 | return parameter == null; 70 | if (parameter == null) 71 | return false; 72 | 73 | // filter by name and/or parameter type 74 | return 75 | (ParameterName == null || String.Compare(parameter.Name, ParameterName, StringComparison.OrdinalIgnoreCase) == 0) && 76 | (ParameterType == null || parameter.ParameterType == ParameterType || parameter.ParameterType.IsSubclassOf(ParameterType)); 77 | } 78 | 79 | /// 80 | /// Returns all of the parameters that match this value's name and converter. 81 | /// 82 | /// The parameters to evaluate. 83 | /// An enumeration of the matching parameters. 84 | public IEnumerable Matches(IEnumerable parameters) 85 | { 86 | // filter by name and/or parameter type 87 | return parameters.Where(p => Matches(p)); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /EventSourceProxy/ParameterConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using System.Reflection.Emit; 4 | 5 | namespace EventSourceProxy 6 | { 7 | /// 8 | /// Handles custom conversion of logging data by invoking LambdaExpressions. 9 | /// 10 | 11 | class ParameterConverter 12 | { 13 | /// 14 | /// Gets the type of the input parameter, or null if there are no parameters. 15 | /// 16 | public Type InputType { get; private set; } 17 | 18 | /// 19 | /// Gets the type of the return value. 20 | /// 21 | public Type ReturnType { get; private set; } 22 | 23 | private Func _generator; 24 | 25 | /// 26 | /// Initializes a new instance of the ParameterConverter class. 27 | /// 28 | /// The type of the input parameter. 29 | /// The type returned from the converter. 30 | /// A function that emits the conversion code and returns the return type. 31 | public ParameterConverter(Type inputType, Type returnType, Func generator) 32 | { 33 | InputType = inputType; 34 | ReturnType = returnType; 35 | _generator = generator; 36 | } 37 | 38 | /// 39 | /// Initializes a new instance of the ParameterConverter class. 40 | /// 41 | /// A LambdaExpression that converts the value. 42 | public ParameterConverter(LambdaExpression expression) 43 | { 44 | var hasParameters = (expression.Parameters.Count > 0); 45 | 46 | InputType = hasParameters ? expression.Parameters[0].Type : null; 47 | ReturnType = expression.ReturnType; 48 | 49 | _generator = (il => 50 | { 51 | var del = expression.Compile(); 52 | 53 | if (hasParameters) 54 | { 55 | var local = il.DeclareLocal(InputType); 56 | il.Emit(OpCodes.Stloc, local.LocalIndex); 57 | StaticFieldStorage.EmitLoad(il, del); 58 | il.Emit(OpCodes.Ldloc, local.LocalIndex); 59 | il.Emit(OpCodes.Call, del.GetType().GetMethod("Invoke")); 60 | } 61 | else 62 | { 63 | StaticFieldStorage.EmitLoad(il, del); 64 | il.Emit(OpCodes.Call, del.GetType().GetMethod("Invoke")); 65 | } 66 | 67 | return expression.ReturnType; 68 | }); 69 | } 70 | 71 | /// 72 | /// Emits the IL to perform the conversion. 73 | /// 74 | /// The return type of the conversion. 75 | public Type Emit(ILGenerator il) 76 | { 77 | return _generator(il); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /EventSourceProxy/ParameterDefinition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace EventSourceProxy 10 | { 11 | /// 12 | /// Represents the definition of a parameter. 13 | /// 14 | class ParameterDefinition 15 | { 16 | /// 17 | /// Initializes a new instance of the ParameterDefinition class from a parameter. 18 | /// 19 | /// The name of the parameter. 20 | /// The position of the parameter on the stack. 21 | /// The type of the parameter. 22 | public ParameterDefinition(string alias, int position, Type sourceType) 23 | { 24 | if (sourceType == null) throw new ArgumentNullException("sourceType"); 25 | if (alias == null) throw new ArgumentNullException("alias"); 26 | if (position < 0) throw new ArgumentOutOfRangeException("position", "position must not be negative"); 27 | 28 | Position = position; 29 | SourceType = sourceType; 30 | Alias = alias; 31 | } 32 | 33 | /// 34 | /// Initializes a new instance of the ParameterDefinition class from parameter. 35 | /// 36 | /// The name of the parameter. 37 | /// The parameter to bind to. 38 | /// An optional converter that converts the parameter to a desired result. 39 | public ParameterDefinition(string alias, ParameterInfo parameterInfo, ParameterConverter converter = null) 40 | { 41 | if (alias == null) throw new ArgumentNullException("alias"); 42 | 43 | Alias = alias; 44 | Converter = converter; 45 | 46 | if (parameterInfo != null) 47 | { 48 | Position = parameterInfo.Position; 49 | SourceType = parameterInfo.ParameterType; 50 | 51 | if (converter != null) 52 | { 53 | if (SourceType != converter.InputType && !SourceType.IsSubclassOf(converter.InputType)) 54 | throw new ArgumentException("The conversion expression must match the type of the parameter.", "converter"); 55 | } 56 | } 57 | else 58 | { 59 | if (converter == null) 60 | throw new ArgumentException("A conversion expression must be specified.", "converter"); 61 | if (converter.InputType != null) 62 | throw new ArgumentException("The conversion expression must take no parameters.", "converter"); 63 | 64 | Position = -1; 65 | SourceType = converter.ReturnType; 66 | } 67 | } 68 | 69 | /// 70 | /// Gets the position of the parameter. 71 | /// 72 | /// If less than zero, then there is no source of the parameter. 73 | public int Position { get; private set; } 74 | 75 | /// 76 | /// Gets the type of the parameter. 77 | /// 78 | /// If null, then there is no source of the parameter. 79 | public Type SourceType { get; private set; } 80 | 81 | /// 82 | /// Gets the name to use to log the parameter. 83 | /// 84 | public string Alias { get; private set; } 85 | 86 | /// 87 | /// Gets an expression that converts the parameter to the intended logged value. 88 | /// 89 | public ParameterConverter Converter { get; private set; } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /EventSourceProxy/ParameterMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace EventSourceProxy 10 | { 11 | /// 12 | /// Represents the mapping between the caller's parameters and the parameters for the underlying method. 13 | /// 14 | public class ParameterMapping 15 | { 16 | /// 17 | /// The sources of the parameter. 18 | /// 19 | private List _sources = new List(); 20 | 21 | /// 22 | /// Initializes a new instance of the ParameterMapping class with an empty source list. 23 | /// 24 | /// The name of the parameter. 25 | public ParameterMapping(string name) 26 | { 27 | Name = name; 28 | } 29 | 30 | /// 31 | /// Gets the name of the parameter. 32 | /// 33 | public string Name { get; private set; } 34 | 35 | /// 36 | /// Gets the sources of the parameter. 37 | /// 38 | internal IEnumerable Sources { get { return _sources; } } 39 | 40 | /// 41 | /// Gets the target type of the parameter. 42 | /// 43 | internal Type TargetType 44 | { 45 | get 46 | { 47 | if (_sources.Count == 1) 48 | { 49 | // if there is one source, then we use the type of the individual source 50 | var source = _sources[0]; 51 | if (source.Converter != null) 52 | return source.Converter.ReturnType; 53 | 54 | return source.SourceType; 55 | } 56 | 57 | // otherwise we have multiple sources, or a context, so we target a string 58 | return typeof(string); 59 | } 60 | } 61 | 62 | /// 63 | /// Gets the target type of the parameter, compatible with ETW. 64 | /// 65 | internal Type CleanTargetType { get { return TypeImplementer.GetTypeSupportedByEventSource(TargetType); } } 66 | 67 | /// 68 | /// Gets a value indicating whether this mapping has any sources. 69 | /// 70 | internal bool HasSource { get { return _sources.Any(); } } 71 | 72 | /// 73 | /// Gets the source type of the parameter. 74 | /// 75 | internal Type SourceType 76 | { 77 | get 78 | { 79 | // if there is one source, then use its type, otherwise we are reading from an object 80 | if (_sources.Count == 1) 81 | return _sources[0].SourceType; 82 | else 83 | return typeof(object); 84 | } 85 | } 86 | 87 | /// 88 | /// Adds a parameter source to this mapping. 89 | /// 90 | /// The parameter to add. 91 | /// The alias to use to log the parameter. 92 | /// A converter that converts the parameter to a desired value. 93 | public void AddSource(ParameterInfo pi, string alias = null, LambdaExpression converter = null) 94 | { 95 | if (alias == null) 96 | { 97 | if (pi == null) 98 | throw new ArgumentNullException("pi"); 99 | else 100 | alias = pi.Name; 101 | } 102 | 103 | var pc = (converter != null) ? new ParameterConverter(converter) : null; 104 | 105 | _sources.Add(new ParameterDefinition(alias, pi, pc)); 106 | } 107 | 108 | /// 109 | /// Adds a parameter source to this mapping. 110 | /// 111 | /// The input type of the converter. 112 | /// The output type of the converter. 113 | /// The parameter to add. 114 | /// The alias to use to log the parameter. 115 | /// A converter that converts the parameter to a desired value. 116 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures"), 117 | System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "This lets the compiler know to generate expressions")] 118 | public void AddSource(ParameterInfo pi, string alias, Expression> converter) 119 | { 120 | if (pi == null) throw new ArgumentNullException("pi"); 121 | 122 | AddSource(pi, alias, (LambdaExpression)converter); 123 | } 124 | 125 | /// 126 | /// Adds a parameter source to this mapping. 127 | /// 128 | /// The input type of the converter. 129 | /// The output type of the converter. 130 | /// The parameter to add. 131 | /// A converter that converts the parameter to a desired value. 132 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures"), 133 | System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "This lets the compiler know to generate expressions")] 134 | public void AddSource(ParameterInfo pi, Expression> converter) 135 | { 136 | if (pi == null) throw new ArgumentNullException("pi"); 137 | 138 | AddSource(pi, pi.Name, (LambdaExpression)converter); 139 | } 140 | 141 | /// 142 | /// Adds a context method to this mapping. 143 | /// 144 | /// The output type of the context expression 145 | /// The alias to use to log the context. 146 | /// An expression that can generate context. 147 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures"), 148 | System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "This lets the compiler know to generate expressions")] 149 | public void AddContext(string alias, Expression> contextExpression) 150 | { 151 | AddSource(null, alias, (LambdaExpression)contextExpression); 152 | } 153 | 154 | /// 155 | /// Adds a parameter source to this mapping. 156 | /// 157 | /// The parameter to add. 158 | internal void AddSource(ParameterDefinition source) 159 | { 160 | if (source == null) throw new ArgumentNullException("source"); 161 | 162 | _sources.Add(source); 163 | } 164 | 165 | /// 166 | /// Adds a parameter source to this mapping. 167 | /// 168 | /// The parameter to add. 169 | /// The alias to use to log the parameter. 170 | /// A converter that converts the parameter to a desired value. 171 | internal void AddSource(ParameterInfo pi, string alias, ParameterConverter converter) 172 | { 173 | if (alias == null) 174 | { 175 | if (pi == null) 176 | throw new ArgumentNullException("pi"); 177 | else 178 | alias = pi.Name; 179 | } 180 | 181 | _sources.Add(new ParameterDefinition(alias, pi, converter)); 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /EventSourceProxy/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using System; 3 | 4 | [assembly: InternalsVisibleTo("EventSourceProxy.Tests")] 5 | -------------------------------------------------------------------------------- /EventSourceProxy/ProviderManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Globalization; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace EventSourceProxy 10 | { 11 | /// 12 | /// Manages the providers for the Event Sources. 13 | /// 14 | class ProviderManager 15 | { 16 | /// 17 | /// The list of registered providers. 18 | /// 19 | private static ConcurrentDictionary, object> _providers = new ConcurrentDictionary, object>(); 20 | 21 | /// 22 | /// Registers a Provider for a given event source. 23 | /// 24 | /// The type of event source to register with. 25 | /// The type of provider being provided. 26 | /// The provider to register. 27 | internal static void RegisterProvider(Type logType, Type providerType, object provider) 28 | { 29 | var key = Tuple.Create(logType, providerType); 30 | 31 | // if the provider is null, then remove the provider 32 | if (provider == null) 33 | { 34 | _providers.TryRemove(key, out provider); 35 | return; 36 | } 37 | 38 | if (!providerType.IsInstanceOfType(provider)) 39 | throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Provider must be an instance of {0}", providerType.Name)); 40 | 41 | // save the provider for future construction 42 | // if the provider already exists, then fail 43 | if (!_providers.TryAdd(key, provider)) 44 | throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "{0} already exists for type {1}", providerType.Name, logType.Name)); 45 | } 46 | 47 | /// 48 | /// Gets the given type of provider for the given type of log. 49 | /// 50 | /// The type of provider being provided. 51 | /// The type of event source to register with. 52 | /// The type of the ProviderAttribute that can specify the provider. 53 | /// The constructor to use to create the provider if it does not exist. 54 | /// The provider for a given type, or null if there is no provider. 55 | internal static T GetProvider(Type logType, Type attributeType, Func defaultConstructor) 56 | { 57 | if (defaultConstructor == null) 58 | defaultConstructor = () => default(T); 59 | 60 | return (T)GetProvider(logType, typeof(T), attributeType, () => defaultConstructor()); 61 | } 62 | 63 | /// 64 | /// Gets the given type of provider for the given type of log. 65 | /// 66 | /// The type of event source to register with. 67 | /// The type of provider being provided. 68 | /// The type of the ProviderAttribute that can specify the provider. 69 | /// The constructor to use to create the provider if it does not exist. 70 | /// The provider for a given type, or null if there is no provider. 71 | private static object GetProvider(Type logType, Type providerType, Type attributeType, Func defaultConstructor) 72 | { 73 | var key = Tuple.Create(logType, providerType); 74 | 75 | return _providers.GetOrAdd( 76 | key, 77 | _ => 78 | { 79 | // if there is a provider attribute on the class or interface, 80 | // then instantiate the given type 81 | if (attributeType != null) 82 | { 83 | var providerAttribute = (TraceProviderAttribute)logType.GetCustomAttributes(attributeType, true).FirstOrDefault(); 84 | if (providerAttribute != null) 85 | return providerAttribute.ProviderType.GetConstructor(Type.EmptyTypes).Invoke(null); 86 | } 87 | 88 | // no provider attribute, check for a default provider 89 | if (logType != null) 90 | { 91 | object provider; 92 | var defaultKey = Tuple.Create((Type)null, providerType); 93 | if (_providers.TryGetValue(defaultKey, out provider)) 94 | return provider; 95 | } 96 | 97 | return defaultConstructor(); 98 | }); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /EventSourceProxy/ProxyHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.Tracing; 4 | using System.Globalization; 5 | using System.Linq; 6 | using System.Linq.Expressions; 7 | using System.Reflection; 8 | using System.Reflection.Emit; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace EventSourceProxy 13 | { 14 | /// 15 | /// Methods to help with building proxies. 16 | /// 17 | static class ProxyHelper 18 | { 19 | /// 20 | /// The name of the generated dynamic assembly. 21 | /// 22 | internal const string AssemblyName = "EventSourceImplementation"; 23 | 24 | /// 25 | /// Copy the generic attributes of a method. 26 | /// 27 | /// The source method. 28 | /// The target method. 29 | internal static void CopyGenericSignature(MethodInfo sourceMethod, MethodBuilder targetMethod) 30 | { 31 | if (sourceMethod.IsGenericMethod) 32 | { 33 | // get the interface's generic types and make our own 34 | var oldTypes = sourceMethod.GetGenericArguments(); 35 | var newTypes = targetMethod.DefineGenericParameters(oldTypes.Select(t => t.Name).ToArray()); 36 | for (int i = 0; i < newTypes.Length; i++) 37 | { 38 | var oldType = oldTypes[i]; 39 | var newType = newTypes[i]; 40 | 41 | newType.SetGenericParameterAttributes(oldType.GenericParameterAttributes); 42 | newType.SetInterfaceConstraints(oldType.GetGenericParameterConstraints()); 43 | } 44 | } 45 | } 46 | 47 | /// 48 | /// Copies the method signature from one method to another. 49 | /// This includes generic parameters, constraints and parameters. 50 | /// 51 | /// The source method. 52 | /// The target method. 53 | internal static void CopyMethodSignature(MethodInfo sourceMethod, MethodBuilder targetMethod) 54 | { 55 | CopyGenericSignature(sourceMethod, targetMethod); 56 | 57 | targetMethod.SetReturnType(sourceMethod.ReturnType); 58 | 59 | // copy the parameters and attributes 60 | // it seems that we can use the source parameters directly because the target method is derived 61 | // from the source method 62 | var parameters = sourceMethod.GetParameters(); 63 | targetMethod.SetParameters(parameters.Select(p => p.ParameterType).ToArray()); 64 | 65 | for (int i = 0; i < parameters.Length; i++) 66 | { 67 | var parameter = parameters[i]; 68 | targetMethod.DefineParameter(i + 1, parameter.Attributes, parameter.Name); 69 | } 70 | } 71 | 72 | /// 73 | /// Emits the code needed to properly push an object on the stack, 74 | /// serializing the value if necessary. 75 | /// 76 | /// The TypeBuilder for the method being built. 77 | /// The method currently being built. 78 | /// The invocation context for this call. 79 | /// A list of invocation contexts that will be appended to. 80 | /// The static field containing the array of invocation contexts at runtime. 81 | /// The mapping of source parameters to destination parameters. 82 | /// The serialization provider for the current interface. 83 | /// 84 | /// The field on the current object that contains the serialization provider at runtime. 85 | /// This method assume the current object is stored in arg.0. 86 | /// 87 | internal static void EmitSerializeValue( 88 | TypeBuilder typeBuilder, 89 | MethodBuilder methodBuilder, 90 | InvocationContext invocationContext, 91 | List invocationContexts, 92 | FieldBuilder invocationContextsField, 93 | ParameterMapping parameterMapping, 94 | TraceSerializationProvider serializationProvider, 95 | FieldBuilder serializationProviderField) 96 | { 97 | var sourceCount = parameterMapping.Sources.Count(); 98 | if (sourceCount == 0) 99 | return; 100 | 101 | if (sourceCount == 1) 102 | { 103 | var parameter = parameterMapping.Sources.First(); 104 | 105 | EmitSerializeValue( 106 | typeBuilder, 107 | methodBuilder, 108 | invocationContext, 109 | invocationContexts, 110 | invocationContextsField, 111 | parameter.Position, 112 | parameter.SourceType, 113 | parameterMapping.CleanTargetType, 114 | parameter.Converter, 115 | serializationProvider, 116 | serializationProviderField); 117 | return; 118 | } 119 | 120 | var il = methodBuilder.GetILGenerator(); 121 | 122 | // use the serializer to serialize the objects 123 | var context = new TraceSerializationContext(invocationContext.SpecifyType(InvocationContextTypes.BundleParameters), -1); 124 | context.EventLevel = serializationProvider.GetEventLevelForContext(context); 125 | 126 | if (context.EventLevel != null) 127 | { 128 | // get the object serializer from the this pointer 129 | il.Emit(OpCodes.Ldsfld, serializationProviderField); 130 | 131 | // create a new dictionary strings and values 132 | il.Emit(OpCodes.Newobj, typeof(Dictionary).GetConstructor(Type.EmptyTypes)); 133 | 134 | foreach (var parameter in parameterMapping.Sources) 135 | { 136 | il.Emit(OpCodes.Dup); 137 | il.Emit(OpCodes.Ldstr, parameter.Alias); 138 | 139 | EmitSerializeValue( 140 | typeBuilder, 141 | methodBuilder, 142 | invocationContext, 143 | invocationContexts, 144 | invocationContextsField, 145 | parameter.Position, 146 | parameter.SourceType, 147 | parameterMapping.CleanTargetType, 148 | parameter.Converter, 149 | serializationProvider, 150 | serializationProviderField); 151 | 152 | var method = typeof(Dictionary).GetMethod("Add"); 153 | il.Emit(OpCodes.Call, method); 154 | } 155 | 156 | // get the invocation context from the array on the provider 157 | il.Emit(OpCodes.Ldsfld, invocationContextsField); 158 | il.Emit(OpCodes.Ldc_I4, invocationContexts.Count); 159 | il.Emit(OpCodes.Ldelem, typeof(TraceSerializationContext)); 160 | invocationContexts.Add(context); 161 | 162 | il.Emit(OpCodes.Callvirt, typeof(TraceSerializationProvider).GetMethod("ProvideSerialization", BindingFlags.Instance | BindingFlags.Public)); 163 | } 164 | else 165 | il.Emit(OpCodes.Ldnull); 166 | } 167 | 168 | /// 169 | /// Emits the code needed to properly push an object on the stack, 170 | /// serializing the value if necessary. 171 | /// 172 | /// The TypeBuilder for the method being built. 173 | /// The method currently being built. 174 | /// The invocation context for this call. 175 | /// A list of invocation contexts that will be appended to. 176 | /// The static field containing the array of invocation contexts at runtime. 177 | /// The index of the current parameter being pushed. 178 | /// The type that the parameter is being converted from. 179 | /// The type that the parameter is being converted to. 180 | /// An optional converter to apply to the source type. 181 | /// The serialization provider for the current interface. 182 | /// 183 | /// The field on the current object that contains the serialization provider at runtime. 184 | /// This method assume the current object is stored in arg.0. 185 | /// 186 | internal static void EmitSerializeValue( 187 | TypeBuilder typeBuilder, 188 | MethodBuilder methodBuilder, 189 | InvocationContext invocationContext, 190 | List invocationContexts, 191 | FieldBuilder invocationContextsField, 192 | int i, 193 | Type sourceType, 194 | Type targetType, 195 | ParameterConverter converter, 196 | TraceSerializationProvider serializationProvider, 197 | FieldBuilder serializationProviderField) 198 | { 199 | ILGenerator mIL = methodBuilder.GetILGenerator(); 200 | 201 | // load the parameter onto the stack unless it's the context parameter 202 | if (i >= 0) 203 | mIL.Emit(OpCodes.Ldarg, i + 1); 204 | 205 | // if a converter is passed in, then define a static method and use it to convert 206 | if (converter != null) 207 | sourceType = converter.Emit(mIL); 208 | 209 | // if the source type is a reference to the target type, we have to dereference it 210 | if (sourceType.IsByRef && sourceType.GetElementType() == targetType) 211 | { 212 | sourceType = sourceType.GetElementType(); 213 | mIL.Emit(OpCodes.Ldobj, sourceType); 214 | return; 215 | } 216 | 217 | // if the types match, just put the argument on the stack 218 | if (sourceType == targetType) 219 | return; 220 | 221 | // this is not a match, so convert using the serializer. 222 | // verify that the target type is a string 223 | if (targetType != typeof(string)) 224 | throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Cannot convert type {0} to a type compatible with EventSource", targetType.FullName)); 225 | 226 | // for fundamental types, just convert them with ToString and be done with it 227 | var underlyingType = Nullable.GetUnderlyingType(sourceType) ?? sourceType; 228 | if (!sourceType.IsGenericParameter && (underlyingType.IsEnum || (underlyingType.IsValueType && underlyingType.Assembly == typeof(string).Assembly))) 229 | { 230 | // convert the argument to a string with ToString 231 | LocalBuilder lb = mIL.DeclareLocal(sourceType); 232 | mIL.Emit(OpCodes.Stloc, lb.LocalIndex); 233 | mIL.Emit(OpCodes.Ldloca, lb.LocalIndex); 234 | mIL.Emit(OpCodes.Call, sourceType.GetMethod("ToString", Type.EmptyTypes)); 235 | return; 236 | } 237 | 238 | // non-fundamental types use the object serializer 239 | var context = new TraceSerializationContext(invocationContext, i); 240 | context.EventLevel = serializationProvider.GetEventLevelForContext(context); 241 | if (context.EventLevel != null) 242 | { 243 | LocalBuilder lb = mIL.DeclareLocal(sourceType); 244 | mIL.Emit(OpCodes.Stloc, lb.LocalIndex); 245 | 246 | // get the object serializer from the this pointer 247 | mIL.Emit(OpCodes.Ldsfld, serializationProviderField); 248 | mIL.Emit(OpCodes.Ldloc, lb.LocalIndex); 249 | 250 | // if the source type is a reference to the target type, we have to dereference it 251 | if (sourceType.IsByRef) 252 | { 253 | sourceType = sourceType.GetElementType(); 254 | mIL.Emit(OpCodes.Ldobj, sourceType); 255 | } 256 | 257 | // if it's a value type, we have to box it to log it 258 | if (sourceType.IsGenericParameter || sourceType.IsValueType) 259 | mIL.Emit(OpCodes.Box, sourceType); 260 | 261 | // get the invocation context from the array on the provider 262 | mIL.Emit(OpCodes.Ldsfld, invocationContextsField); 263 | mIL.Emit(OpCodes.Ldc_I4, invocationContexts.Count); 264 | mIL.Emit(OpCodes.Ldelem, typeof(TraceSerializationContext)); 265 | invocationContexts.Add(context); 266 | 267 | mIL.Emit(OpCodes.Callvirt, typeof(TraceSerializationProvider).GetMethod("ProvideSerialization", BindingFlags.Instance | BindingFlags.Public)); 268 | } 269 | else 270 | { 271 | mIL.Emit(OpCodes.Pop); 272 | mIL.Emit(OpCodes.Ldnull); 273 | } 274 | } 275 | 276 | /// 277 | /// Determine if the parameters of a method match a list of parameter types. 278 | /// 279 | /// The method to test. 280 | /// The list of parameter types. 281 | /// True if the types of parameters match. 282 | internal static bool ParametersMatch(MethodInfo m, Type[] targetTypes) 283 | { 284 | var sourceTypes = m.GetParameters().Select(p => p.ParameterType).ToArray(); 285 | 286 | // need to have the same number of types 287 | if (sourceTypes.Length != targetTypes.Length) 288 | return false; 289 | 290 | for (int i = 0; i < sourceTypes.Length; i++) 291 | { 292 | var sourceType = sourceTypes[i]; 293 | var targetType = targetTypes[i]; 294 | 295 | // if they match exactly, then go to the next parameter 296 | if (sourceType == targetType) 297 | continue; 298 | 299 | // if both are generics of the same type, then they match 300 | if (sourceType.IsGenericType && 301 | targetType.IsGenericType && 302 | sourceType.GetGenericTypeDefinition() == targetType.GetGenericTypeDefinition()) 303 | continue; 304 | 305 | // if both are generic parameters and they match by name, then we can use this 306 | // NOTE: this only works because we copy generic parameters and keep their names 307 | if (sourceType.IsGenericParameter && 308 | targetType.IsGenericParameter && 309 | sourceType.Name == targetType.Name) 310 | continue; 311 | 312 | return false; 313 | } 314 | 315 | return true; 316 | } 317 | 318 | /// 319 | /// Discovers the methods that need to be implemented for a type. 320 | /// 321 | /// The type to implement. 322 | /// The virtual and abstract methods that need to be implemented. 323 | internal static List DiscoverMethods(Type type) 324 | { 325 | List methods = new List(); 326 | 327 | // for interfaces, we need to look at all of the methods that are in the base interfaces 328 | foreach (Type baseInterface in type.GetInterfaces()) 329 | if (baseInterface != typeof(IDisposable)) 330 | methods.AddRange(DiscoverMethods(baseInterface)); 331 | 332 | // add in all of the methods on the type 333 | BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.DeclaredOnly; 334 | for (; type != null && type != typeof(object) && type != typeof(EventSource); type = type.BaseType) 335 | methods.AddRange(type.GetMethods(bindingFlags)); 336 | 337 | return methods.Distinct().ToList(); 338 | } 339 | 340 | /// 341 | /// Emits the code to load the default value of a type onto the stack. 342 | /// 343 | /// The ILGenerator to emit to. 344 | /// The type of object to emit. 345 | internal static void EmitDefaultValue(ILGenerator mIL, Type type) 346 | { 347 | // if there is no type, then we don't need a value 348 | if (type == null || type == typeof(void)) 349 | return; 350 | 351 | // for generics and values, init a local object with a blank object 352 | if (type.IsGenericParameter || type.IsValueType) 353 | { 354 | var returnValue = mIL.DeclareLocal(type); 355 | mIL.Emit(OpCodes.Ldloca_S, returnValue); 356 | mIL.Emit(OpCodes.Initobj, type); 357 | mIL.Emit(OpCodes.Ldloc, returnValue); 358 | } 359 | else 360 | mIL.Emit(OpCodes.Ldnull); 361 | } 362 | } 363 | } 364 | -------------------------------------------------------------------------------- /EventSourceProxy/Settings.StyleCop: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | arg 5 | enum 6 | guid 7 | json 8 | Newtonsoft 9 | opcodes 10 | param 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | False 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | False 29 | 30 | 31 | 32 | 33 | False 34 | 35 | 36 | 37 | 38 | False 39 | 40 | 41 | 42 | 43 | False 44 | 45 | 46 | 47 | 48 | False 49 | 50 | 51 | 52 | 53 | False 54 | 55 | 56 | 57 | 58 | False 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | False 69 | 70 | 71 | 72 | 73 | False 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | False 84 | 85 | 86 | 87 | 88 | False 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | False 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | False 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | False 119 | 120 | 121 | 122 | 123 | False 124 | 125 | 126 | 127 | 128 | False 129 | 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /EventSourceProxy/StaticFieldStorage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Reflection.Emit; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace EventSourceProxy 12 | { 13 | /// 14 | /// Allows storage of a static variable in a way that can be easily emitted into IL. 15 | /// 16 | /// 17 | /// We store the information in the static fields of a class because it is easy to access them 18 | /// in the IL of a DynamicMethod. 19 | /// 20 | public static class StaticFieldStorage 21 | { 22 | /// 23 | /// Stores the static objects to return later. 24 | /// 25 | static List _values = new List(); 26 | 27 | /// 28 | /// Returns a value from the static field cache. 29 | /// 30 | /// The index into the cache. 31 | /// The value from the cache. 32 | public static object GetValue(int index) 33 | { 34 | return _values[index]; 35 | } 36 | 37 | /// 38 | /// Adds a value to the cache. 39 | /// 40 | /// The value to add to the cache. 41 | /// The index into the cache. 42 | internal static int CacheValue(object value) 43 | { 44 | lock (_values) 45 | { 46 | _values.Add(value); 47 | return _values.Count - 1; 48 | } 49 | } 50 | 51 | /// 52 | /// Emits the value stored in static storage. 53 | /// 54 | /// The ILGenerator to emit to. 55 | /// The value to emit. 56 | internal static void EmitLoad(ILGenerator il, object value) 57 | { 58 | if (value == null) 59 | { 60 | il.Emit(OpCodes.Ldnull); 61 | } 62 | else 63 | { 64 | il.Emit(OpCodes.Ldc_I4, CacheValue(value)); 65 | il.Emit(OpCodes.Call, typeof(StaticFieldStorage).GetMethod("GetValue")); 66 | il.Emit(OpCodes.Castclass, value.GetType()); 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /EventSourceProxy/ToStringObjectSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.Tracing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Newtonsoft.Json; 8 | 9 | namespace EventSourceProxy 10 | { 11 | /// 12 | /// Serializes objects by calling ToString on them. 13 | /// 14 | public class ToStringObjectSerializer : TraceSerializationProvider 15 | { 16 | #region Constructors 17 | /// 18 | /// Initializes a new instance of the ToStringObjectSerializer class. 19 | /// The default is to allow serialization whenever tracing occurs. 20 | /// 21 | public ToStringObjectSerializer() 22 | { 23 | } 24 | 25 | /// 26 | /// Initializes a new instance of the ToStringObjectSerializer class. 27 | /// 28 | /// 29 | /// The default EventLevel to allow object serialization. 30 | /// The default is to serialize objects whenever tracing occurs, but this can be used to allow serialization 31 | /// only when logging is at a particular level of verbosity. 32 | /// 33 | public ToStringObjectSerializer(EventLevel defaultEventLevel) : base(defaultEventLevel) 34 | { 35 | } 36 | #endregion 37 | 38 | /// 39 | /// Serializes an object to a string. 40 | /// 41 | /// The object to serialize. 42 | /// The context of the serialization. 43 | /// The serialized representation of the object. 44 | public override string SerializeObject(object value, TraceSerializationContext context) 45 | { 46 | if (value == null) 47 | return null; 48 | 49 | return value.ToString(); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /EventSourceProxy/TraceAsAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace EventSourceProxy 8 | { 9 | /// 10 | /// Specifies the name to use to trace the given parameter. 11 | /// 12 | /// 13 | /// If multiple parameters are traced into the same name, then they are traced as a string-to-string map, 14 | /// and serialized into a string by the TraceSerializationProvider. 15 | /// If TraceAsAttribute is applied to a method, then all parameters of the method are traced into the specified name 16 | /// unless other TraceAsAttributes are applied. 17 | /// 18 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes")] 19 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] 20 | public class TraceAsAttribute : Attribute 21 | { 22 | /// 23 | /// Initializes a new instance of the TraceAsAttribute class, providing a name for the given parameter. 24 | /// 25 | public TraceAsAttribute() 26 | { 27 | } 28 | 29 | /// 30 | /// Initializes a new instance of the TraceAsAttribute class, providing a name for the given parameter. 31 | /// 32 | /// The name to trace the parameter as. 33 | public TraceAsAttribute(string name) 34 | { 35 | Name = name; 36 | } 37 | 38 | /// 39 | /// Gets the name to use when tracing the parameter. 40 | /// 41 | public string Name { get; private set; } 42 | 43 | /// 44 | /// Gets or sets the String.Format to use when tracing the parameter. 45 | /// 46 | public string Format { get; set; } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /EventSourceProxy/TraceAsDataAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace EventSourceProxy 8 | { 9 | /// 10 | /// Specifies that the parameter should be traced in an element called "data". 11 | /// 12 | /// 13 | /// If multiple parameters are traced into the same name, then they are traced as a string-to-string map, 14 | /// and serialized into a string by the TraceSerializationProvider. 15 | /// If TraceAsDataAttribute is applied to a method, then all parameters of the method are traced into the specified name 16 | /// unless other TraceAsAttributes are applied. 17 | /// 18 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method, AllowMultiple = false)] 19 | public sealed class TraceAsDataAttribute : TraceAsAttribute 20 | { 21 | /// 22 | /// Initializes a new instance of the TraceAsDataAttribute class, providing a name for the given parameter. 23 | /// 24 | public TraceAsDataAttribute() : base("data") 25 | { 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /EventSourceProxy/TraceContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace EventSourceProxy 9 | { 10 | /// 11 | /// A thread-static bag of data that can be dropped into any trace. 12 | /// 13 | public sealed class TraceContext : IDisposable 14 | { 15 | #region Properties 16 | /// 17 | /// The slot we use to store our data. 18 | /// 19 | private static AsyncLocal _storage = new AsyncLocal(); 20 | 21 | /// 22 | /// An outer context. 23 | /// 24 | private TraceContext _baseContext; 25 | 26 | /// 27 | /// The dictionary containing the data. 28 | /// 29 | private Dictionary _data = new Dictionary(); 30 | #endregion 31 | 32 | /// 33 | /// Initializes a new instance of the TraceContext class. 34 | /// 35 | /// The base context. 36 | private TraceContext(TraceContext baseContext) 37 | { 38 | _baseContext = baseContext; 39 | } 40 | 41 | /// 42 | /// Gets or sets logging values in this scope. 43 | /// 44 | /// The key in the data dictionary. 45 | /// The value associated with the key. 46 | public object this[string key] 47 | { 48 | get 49 | { 50 | object value = null; 51 | if (_data.TryGetValue(key, out value)) 52 | return value; 53 | 54 | if (_baseContext != null) 55 | return _baseContext[key]; 56 | 57 | return null; 58 | } 59 | 60 | set 61 | { 62 | _data[key] = value; 63 | } 64 | } 65 | 66 | /// 67 | /// Starts a new TraceContext scope. 68 | /// 69 | /// The new TraceContext that can be filled in. 70 | public static TraceContext Begin() 71 | { 72 | return _storage.Value = new TraceContext(_storage.Value); 73 | } 74 | 75 | /// 76 | /// Gets a value associated with the given key in the current scope. 77 | /// 78 | /// The key to look up. 79 | /// The value associated with the key, or null of the value was not set. 80 | public static object GetValue(string key) 81 | { 82 | return _storage.Value?[key]; 83 | } 84 | 85 | /// 86 | public void Dispose() 87 | { 88 | End(); 89 | GC.SuppressFinalize(this); 90 | } 91 | 92 | /// 93 | /// Ends the TraceContext scope. 94 | /// 95 | private void End() 96 | { 97 | if (_storage.Value != this) 98 | throw new ApplicationException("TraceContext chain has been broken."); 99 | 100 | _storage.Value = _baseContext; 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /EventSourceProxy/TraceContextAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace EventSourceProxy 9 | { 10 | /// 11 | /// Allows TraceContext to be enabled or disabled at a class or method level. 12 | /// 13 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Method, AllowMultiple = false)] 14 | [SuppressMessage("Microsoft.Design", "CA1019:DefineAccessorsForAttributeArguments", Justification = "This is a convenience method for enabling/disabling all invocations")] 15 | public sealed class TraceContextAttribute : Attribute 16 | { 17 | /// 18 | /// Initializes a new instance of the TraceContextAttribute class. 19 | /// Tracing will be enabled with this constructor. 20 | /// 21 | public TraceContextAttribute() 22 | { 23 | EnabledFor = InvocationContextTypes.All; 24 | } 25 | 26 | /// 27 | /// Initializes a new instance of the TraceContextAttribute class. 28 | /// 29 | /// True to enable context logging for all invocations, false to disable context logging for all invocations. 30 | public TraceContextAttribute(bool enabled) 31 | { 32 | EnabledFor = enabled ? InvocationContextTypes.All : InvocationContextTypes.None; 33 | } 34 | 35 | /// 36 | /// Initializes a new instance of the TraceContextAttribute class. 37 | /// 38 | /// The types of invocations to enable context logging. 39 | public TraceContextAttribute(InvocationContextTypes enabledFor) 40 | { 41 | EnabledFor = enabledFor; 42 | } 43 | 44 | /// 45 | /// Gets a value indicating whether the context provider should generate context for the method. 46 | /// 47 | public InvocationContextTypes EnabledFor { get; private set; } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /EventSourceProxy/TraceContextProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace EventSourceProxy 9 | { 10 | /// 11 | /// The base class for a TraceContextProvider. TraceContextProvider properly determines whether 12 | /// context is needed for a given method invocation. 13 | /// 14 | public abstract class TraceContextProvider 15 | { 16 | /// 17 | /// Provides context information, such as security context, for a trace session. 18 | /// 19 | /// The context of the current invocation. 20 | /// A string representing the current context. 21 | public abstract string ProvideContext(InvocationContext context); 22 | 23 | /// 24 | /// Determines whether the given invocation context requires context to be provided. 25 | /// 26 | /// The context of the invocation. 27 | /// True if EventSourceProxy should ask for context, false to skip context generation. 28 | public virtual bool ShouldProvideContext(InvocationContext context) 29 | { 30 | if (context == null) throw new ArgumentNullException("context"); 31 | 32 | // NOTE: this method is called at proxy generation time and is not called at runtime 33 | // so we don't need to cache anything here 34 | // if this returns false, then ProvideContext will never be called for the given context 35 | 36 | // check the method first 37 | var attribute = context.MethodInfo.GetCustomAttribute(); 38 | if (attribute != null) 39 | return attribute.EnabledFor.HasFlag(context.ContextType); 40 | 41 | // now check the class 42 | attribute = context.MethodInfo.DeclaringType.GetCustomAttribute(); 43 | if (attribute != null) 44 | return attribute.EnabledFor.HasFlag(context.ContextType); 45 | 46 | // it's enabled by default 47 | return true; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /EventSourceProxy/TraceContextProviderAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace EventSourceProxy 8 | { 9 | /// 10 | /// Specifies the TraceContextProvider to use for a class or interface. 11 | /// 12 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)] 13 | public sealed class TraceContextProviderAttribute : TraceProviderAttribute 14 | { 15 | /// 16 | /// Initializes a new instance of the TraceContextProviderAttribute class. 17 | /// 18 | /// The type of the provider to assign to this class or interface. 19 | public TraceContextProviderAttribute(Type providerType) : base(providerType) 20 | { 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /EventSourceProxy/TraceIgnoreAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace EventSourceProxy 8 | { 9 | /// 10 | /// Specifies that a given parameter should not be traced. 11 | /// 12 | [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)] 13 | public sealed class TraceIgnoreAttribute : Attribute 14 | { 15 | /// 16 | /// Initializes a new instance of the TraceIgnoreAttribute class. 17 | /// 18 | public TraceIgnoreAttribute() 19 | { 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /EventSourceProxy/TraceMemberAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace EventSourceProxy 8 | { 9 | /// 10 | /// Specifies that a member of the given parameter should be traced as a separate parameter. 11 | /// 12 | [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true)] 13 | public sealed class TraceMemberAttribute : TraceAsAttribute 14 | { 15 | /// 16 | /// Initializes a new instance of the TraceMemberAttribute class. 17 | /// 18 | /// The name of the member to trace. 19 | public TraceMemberAttribute(string member) 20 | : base(member) 21 | { 22 | Member = member; 23 | } 24 | 25 | /// 26 | /// Initializes a new instance of the TraceMemberAttribute class. 27 | /// 28 | /// The name of the member to trace. 29 | /// The name to use to trace the member. 30 | public TraceMemberAttribute(string member, string name) 31 | : base(name) 32 | { 33 | Member = member; 34 | } 35 | 36 | /// 37 | /// Gets the name of the member that will be traced. 38 | /// 39 | public string Member { get; private set; } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /EventSourceProxy/TraceParameterProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | using System.Reflection.Emit; 7 | 8 | namespace EventSourceProxy 9 | { 10 | /// 11 | /// Implements a provider that can bundle the parameters of an interface. 12 | /// 13 | public class TraceParameterProvider 14 | { 15 | #region Fields and Properties 16 | /// 17 | /// The default parameter provider. 18 | /// 19 | private static TraceParameterProvider _defaultProvider = new TraceParameterProvider(); 20 | 21 | /// 22 | /// The list of parameter builders. 23 | /// 24 | private List _builders = new List(); 25 | 26 | /// 27 | /// Gets the default TraceParameterProvider. 28 | /// 29 | public static TraceParameterProvider Default { get { return _defaultProvider; } } 30 | 31 | /// 32 | /// Gets the list of TraceBuilders that have been defined for this provider. 33 | /// 34 | internal IEnumerable Builders { get { return _builders; } } 35 | #endregion 36 | 37 | #region Fluent Configuration Methods 38 | /// 39 | /// Configures a parameter rule that matches all types and methods. 40 | /// 41 | /// A configuration rule that can be extended. 42 | public IParameterBuilder ForAnything() 43 | { 44 | return new ParameterBuilder(this); 45 | } 46 | 47 | /// 48 | /// Configures a parameter rule that matches the given type. 49 | /// 50 | /// The type to be matched. 51 | /// A configuration rule that can be extended. 52 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")] 53 | public IParameterBuilder For() 54 | { 55 | return new ParameterBuilder(this); 56 | } 57 | 58 | /// 59 | /// Configures a parameter rule that matches the given type. 60 | /// 61 | /// The type to be matched. 62 | /// A lambda expression that contains a method call to match on. 63 | /// A configuration rule that can be extended. 64 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] 65 | public IParameterBuilder For(Expression> methodExpression) 66 | { 67 | return new ParameterBuilder(this, methodExpression); 68 | } 69 | #endregion 70 | 71 | /// 72 | /// Returns the parameter mapping for the given method. 73 | /// 74 | /// The method to analyze. 75 | /// A list of ParameterMapping representing the desired bundling of parameters. 76 | public virtual IReadOnlyCollection ProvideParameterMapping(MethodInfo methodInfo) 77 | { 78 | if (methodInfo == null) throw new ArgumentNullException("methodInfo"); 79 | 80 | return EvaluateBuilders(methodInfo); 81 | } 82 | 83 | /// 84 | /// Returns the parameter provider for a given type. 85 | /// 86 | /// The type to analyze. 87 | /// The parameter provider for the type. 88 | internal static TraceParameterProvider GetParameterProvider(Type interfaceType) 89 | { 90 | return ProviderManager.GetProvider(interfaceType, typeof(TraceParameterProviderAttribute), () => _defaultProvider); 91 | } 92 | 93 | /// 94 | /// Adds a TraceBuilder rule to this provider. 95 | /// 96 | /// The ParameterBuilder to add. 97 | internal void Add(ParameterBuilder builder) 98 | { 99 | if (!_builders.Contains(builder)) 100 | _builders.Add(builder); 101 | } 102 | 103 | #region Internal Methods for Building 104 | /// 105 | /// Adds a mapping to the list of mappings 106 | /// 107 | /// The list of parameter mappings. 108 | /// The parameter being evaluated. 109 | /// The name of the parameter. 110 | /// The alias to use to output the parameter. 111 | /// An optional expression to use to convert the parameter. 112 | private static void AddMapping(List mappings, ParameterInfo parameterInfo, string parameterName, string alias, ParameterConverter converter) 113 | { 114 | // find the mapping that matches the name or create a new mapping 115 | var mapping = mappings.FirstOrDefault(p => String.Compare(p.Name, parameterName, StringComparison.OrdinalIgnoreCase) == 0); 116 | if (mapping == null) 117 | { 118 | mapping = new ParameterMapping(parameterName); 119 | mappings.Add(mapping); 120 | } 121 | 122 | mapping.AddSource(parameterInfo, alias, converter); 123 | } 124 | 125 | /// 126 | /// Uses the TraceBuilder rules on the provider to generate the parameter bindings. 127 | /// 128 | /// The method to analyze. 129 | /// A list of ParameterMapping representing the desired bundling of parameters. 130 | private IReadOnlyCollection EvaluateBuilders(MethodInfo methodInfo) 131 | { 132 | var mappings = new List(); 133 | var parameters = methodInfo.GetParameters().ToList(); 134 | 135 | // give a context rule the opportunity to bind 136 | parameters.Add(null); 137 | 138 | // get the default name to use for tracing (null if none specified) 139 | var traceAsDefault = methodInfo.GetCustomAttribute(); 140 | 141 | foreach (var parameter in parameters) 142 | { 143 | bool hasRule = false; 144 | 145 | // go through the rules 146 | foreach (var builder in _builders.Where(b => b.Matches(methodInfo))) 147 | { 148 | foreach (var value in builder.Values.Where(v => v.Matches(parameter))) 149 | { 150 | hasRule = true; 151 | 152 | // if the value is an ignore rule, skip it 153 | if (value.Ignore) 154 | continue; 155 | 156 | var converter = (value.Converter != null) ? new ParameterConverter(value.Converter) : null; 157 | var alias = value.Alias ?? builder.Alias ?? "data"; 158 | 159 | AddMapping(mappings, parameter, builder.Alias ?? String.Format("{0}.{1}", parameter.Name, alias), alias, converter); 160 | 161 | // add the parameter value 162 | // var alias = value.Alias ?? builder.Alias ?? "data"; 163 | // if (parameter != null && parameter.Name != alias) 164 | // alias = String.Format("{0}.{1}", parameter.Name, alias); 165 | // AddMapping(mappings, parameter, alias, alias, converter); 166 | // AddMapping(mappings, parameter, parameter?.Name ?? alias, alias, converter); 167 | } 168 | } 169 | 170 | // if a rule has been applied, then don't add the base value 171 | if (hasRule) 172 | continue; 173 | 174 | if (parameter == null) 175 | continue; 176 | 177 | // if there is a TraceIgnore attribute, then skip this parameter 178 | if (parameter.GetCustomAttribute() != null) 179 | continue; 180 | 181 | // we need one parameter per attribute, and at least one per parameter 182 | var attributes = parameter.GetCustomAttributes(); 183 | if (!attributes.Any()) 184 | attributes = new TraceAsAttribute[1] { traceAsDefault ?? new TraceAsAttribute(parameter.Name) }; 185 | 186 | foreach (var attribute in attributes) 187 | { 188 | var traceName = attribute.Name ?? parameter.Name; 189 | 190 | var traceMember = attribute as TraceMemberAttribute; 191 | var traceTransform = attribute as TraceTransformAttribute; 192 | var hasFormat = !String.IsNullOrWhiteSpace(attribute.Format); 193 | var needsConverter = (traceMember != null || traceTransform != null || hasFormat); 194 | 195 | Func ilGenerator = (ILGenerator il) => 196 | { 197 | var returnType = parameter.ParameterType; 198 | 199 | // if the attribute is a TraceMember, then extract the member 200 | if (traceMember != null) 201 | { 202 | var memberInfo = parameter.ParameterType.GetMember(traceMember.Member).First(); 203 | var propInfo = memberInfo as PropertyInfo; 204 | var fieldInfo = memberInfo as FieldInfo; 205 | 206 | if (propInfo != null) 207 | { 208 | il.Emit(OpCodes.Call, propInfo.GetGetMethod()); 209 | returnType = propInfo.PropertyType; 210 | } 211 | else 212 | if (fieldInfo != null) 213 | { 214 | il.Emit(OpCodes.Ldfld, fieldInfo); 215 | returnType = fieldInfo.FieldType; 216 | } 217 | } 218 | 219 | // if the attribute is a TraceTransform then validate the usage and use the provided method 220 | // to build an expression to apply to the trace value 221 | if (traceTransform != null) 222 | { 223 | var method = traceTransform.GetTransformMethod(returnType); 224 | if (method == null) 225 | { 226 | var message = String.Format("{0}.GetTransformMethod() returned null.", attribute.GetType().Name); 227 | throw new ArgumentNullException("Method", message); 228 | } 229 | 230 | var methodParams = method.GetParameters(); 231 | if (methodParams.Length != 1 || method.ReturnType == typeof(void) || !method.IsStatic) 232 | { 233 | var message = String.Format("{0}.GetTransformMethod() should return MethodInfo for a static method with one input parameter and a non-void response type.", attribute.GetType().Name); 234 | throw new ArgumentException(message); 235 | } 236 | 237 | if (methodParams[0].ParameterType != returnType) 238 | { 239 | var message = String.Format( 240 | "{0}.GetTransformMethod() returned MethodInfo for a static method which expects an input type of '{1}' but was applied to a trace parameter of type '{2}'. (trace method name: {3}, parameter name: {4})", 241 | attribute.GetType().Name, 242 | methodParams[0].ParameterType, 243 | returnType, 244 | methodInfo.Name, 245 | traceName); 246 | throw new ArgumentException(message); 247 | } 248 | 249 | il.Emit(OpCodes.Call, method); 250 | } 251 | 252 | // if a string format was specified, wrap the expression in a method call 253 | if (hasFormat) 254 | { 255 | // value is on the stack, need to reorder it 256 | var local = il.DeclareLocal(returnType); 257 | il.Emit(OpCodes.Stloc, local.LocalIndex); 258 | il.Emit(OpCodes.Ldstr, attribute.Format); 259 | il.Emit(OpCodes.Ldloc, local.LocalIndex); 260 | if (returnType.GetTypeInfo().IsValueType) 261 | il.Emit(OpCodes.Box, returnType); 262 | il.Emit(OpCodes.Call, (typeof(String).GetMethod("Format", new Type[] { typeof(string), typeof(object) }))); 263 | 264 | returnType = typeof(String); 265 | } 266 | 267 | // at this point, the value should be converted to the desired type 268 | 269 | return returnType; 270 | }; 271 | 272 | ParameterConverter converter = needsConverter ? new ParameterConverter(parameter.ParameterType, typeof(Object), ilGenerator) : null; 273 | 274 | AddMapping(mappings, parameter, traceName, parameter.Name, converter); 275 | } 276 | } 277 | 278 | return mappings; 279 | } 280 | #endregion 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /EventSourceProxy/TraceParameterProviderAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace EventSourceProxy 8 | { 9 | /// 10 | /// Specifies the TraceParameterProvider to use for a given interface. 11 | /// 12 | [AttributeUsage(AttributeTargets.Interface, AllowMultiple = false)] 13 | public sealed class TraceParameterProviderAttribute : TraceProviderAttribute 14 | { 15 | /// 16 | /// Initializes a new instance of the TraceParameterProviderAttribute class. 17 | /// 18 | /// The type of provider to use for the given interface. 19 | public TraceParameterProviderAttribute(Type providerType) 20 | : base(providerType) 21 | { 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /EventSourceProxy/TraceProviderAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace EventSourceProxy 9 | { 10 | /// 11 | /// Specifies a TraceProvider for a class or interface. 12 | /// 13 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)] 14 | [SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAtttributes", Justification = "Other attributes derive from this class")] 15 | public class TraceProviderAttribute : Attribute 16 | { 17 | /// 18 | /// Initializes a new instance of the TraceProviderAttribute class. 19 | /// 20 | /// The type of the provider to assign to this class or interface. 21 | public TraceProviderAttribute(Type providerType) 22 | { 23 | ProviderType = providerType; 24 | } 25 | 26 | /// 27 | /// Gets the type of the provider to assign to the class or interface. 28 | /// 29 | public Type ProviderType { get; private set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /EventSourceProxy/TraceSerializationAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Diagnostics.Tracing; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace EventSourceProxy 10 | { 11 | /// 12 | /// Allows the TraceSerialization level to be adjusted at the class, method, interface, or parameter level. 13 | /// 14 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.ReturnValue, AllowMultiple = false)] 15 | public sealed class TraceSerializationAttribute : Attribute 16 | { 17 | /// 18 | /// Initializes a new instance of the TraceSerializationAttribute class. 19 | /// 20 | /// The minimum EventLevel required to enable serialization. 21 | public TraceSerializationAttribute(EventLevel level) 22 | { 23 | Level = level; 24 | } 25 | 26 | /// 27 | /// Gets the minimum EventLevel required to enable serialization. 28 | /// 29 | public EventLevel Level { get; private set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /EventSourceProxy/TraceSerializationContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.Tracing; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Reflection.Emit; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace EventSourceProxy 11 | { 12 | /// 13 | /// Describes the context in which an object is being serialized. 14 | /// 15 | public class TraceSerializationContext : InvocationContext 16 | { 17 | /// 18 | /// Initializes a new instance of the TraceSerializationContext class. 19 | /// 20 | /// The InvocationContext this is based on. 21 | /// The index of the parameter being serialized. 22 | internal TraceSerializationContext(InvocationContext invocationContext, int parameterIndex) : 23 | base(invocationContext.MethodInfo, invocationContext.ContextType) 24 | { 25 | ParameterIndex = parameterIndex; 26 | } 27 | 28 | /// 29 | /// Gets the index of the parameter being serialized. 30 | /// 31 | public int ParameterIndex { get; private set; } 32 | 33 | /// 34 | /// Gets the EventLevel required to serialize this object. 35 | /// 36 | public EventLevel? EventLevel { get; internal set; } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /EventSourceProxy/TraceSerializationProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.Tracing; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace EventSourceProxy 10 | { 11 | /// 12 | /// Defines a provider that serializes objects to the ETW log when the log cannot handle the type natively. 13 | /// 14 | public abstract class TraceSerializationProvider 15 | { 16 | #region Private Fields 17 | /// 18 | /// The default EventLevel at which to allow serialization. 19 | /// 20 | private EventLevel _defaultEventLevel; 21 | #endregion 22 | 23 | #region Constructors 24 | /// 25 | /// Initializes a new instance of the TraceSerializationProvider class. 26 | /// The default is to only allow serialization in Verbose mode. 27 | /// 28 | protected TraceSerializationProvider() : this(EventLevel.Verbose) 29 | { 30 | } 31 | 32 | /// 33 | /// Initializes a new instance of the TraceSerializationProvider class. 34 | /// 35 | /// 36 | /// The default EventLevel to allow object serialization. 37 | /// The default is to serialize objects whenever tracing occurs, but this can be used to allow serialization 38 | /// only when logging is at a particular level of verbosity. 39 | /// 40 | protected TraceSerializationProvider(EventLevel defaultEventLevel) 41 | { 42 | _defaultEventLevel = defaultEventLevel; 43 | } 44 | #endregion 45 | 46 | /// 47 | /// Returns the EventLevel at which to enable serialization for the given context. 48 | /// This method looks at the TraceSerializationAttributes on the parameter, method, or class. 49 | /// 50 | /// The serialization context to evaluate. 51 | /// The EventLevel at which to enable serialization for the given context. 52 | public virtual EventLevel? GetEventLevelForContext(TraceSerializationContext context) 53 | { 54 | if (context == null) throw new ArgumentNullException("context"); 55 | 56 | TraceSerializationAttribute attribute = null; 57 | 58 | // look on the parameter first 59 | ParameterInfo parameterInfo = null; 60 | switch (context.ContextType) 61 | { 62 | case InvocationContextTypes.MethodCall: 63 | // added context data is added by default 64 | if (context.ParameterIndex < 0) 65 | return _defaultEventLevel; 66 | 67 | parameterInfo = context.MethodInfo.GetParameters()[context.ParameterIndex]; 68 | break; 69 | case InvocationContextTypes.MethodCompletion: 70 | parameterInfo = context.MethodInfo.ReturnParameter; 71 | break; 72 | } 73 | 74 | if (parameterInfo != null) 75 | { 76 | // look at the attribute on the parameter 77 | attribute = parameterInfo.GetCustomAttribute(); 78 | if (attribute != null) 79 | return attribute.Level; 80 | 81 | // look at the attribute on the parameter's type 82 | attribute = parameterInfo.ParameterType.GetCustomAttribute(); 83 | if (attribute != null) 84 | return attribute.Level; 85 | } 86 | 87 | // now look on the method 88 | attribute = context.MethodInfo.GetCustomAttribute(); 89 | if (attribute != null) 90 | return attribute.Level; 91 | 92 | // now look at the type 93 | attribute = context.MethodInfo.DeclaringType.GetCustomAttribute(); 94 | if (attribute != null) 95 | return attribute.Level; 96 | 97 | return _defaultEventLevel; 98 | } 99 | 100 | /// 101 | /// Called by EventSourceProxy to serialize an object. This method should call ShouldSerialize 102 | /// then SerializeObject if serialization is enabled. 103 | /// 104 | /// The value to serialize. 105 | /// The serialization context. 106 | /// The serialized value. 107 | public virtual string ProvideSerialization(object value, TraceSerializationContext context) 108 | { 109 | if (!ShouldSerialize(context)) 110 | return null; 111 | 112 | return SerializeObject(value, context); 113 | } 114 | 115 | /// 116 | /// Returns if the should the given parameter be serialized. 117 | /// 118 | /// The context of the serialization. 119 | /// True if the value should be serialized, false otherwise. 120 | public virtual bool ShouldSerialize(TraceSerializationContext context) 121 | { 122 | if (context == null) throw new ArgumentNullException("context"); 123 | 124 | if (context.EventLevel == null) 125 | return false; 126 | 127 | var eventLevel = context.EventLevel.Value; 128 | if (eventLevel == EventLevel.LogAlways) 129 | return true; 130 | 131 | return context.EventSource.IsEnabled(eventLevel, (EventKeywords)(-1)); 132 | } 133 | 134 | /// 135 | /// Serializes an object to a string. 136 | /// 137 | /// The object to serialize. 138 | /// The context of the serialization. 139 | /// The serialized representation of the object. 140 | public abstract string SerializeObject(object value, TraceSerializationContext context); 141 | 142 | /// 143 | /// Gets the serialization provider for a given type. 144 | /// 145 | /// The type to serialize. 146 | /// The serialization provider or the default JSON provider. 147 | internal static TraceSerializationProvider GetSerializationProvider(Type type) 148 | { 149 | return ProviderManager.GetProvider(type, typeof(TraceSerializationProviderAttribute), () => new JsonObjectSerializer()); 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /EventSourceProxy/TraceSerializationProviderAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace EventSourceProxy 8 | { 9 | /// 10 | /// Specifies the TraceSerializationProvider to use for a class or interface. 11 | /// 12 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)] 13 | public sealed class TraceSerializationProviderAttribute : TraceProviderAttribute 14 | { 15 | /// 16 | /// Initializes a new instance of the TraceSerializationProviderAttribute class. 17 | /// 18 | /// The type of the provider to assign to this class or interface. 19 | public TraceSerializationProviderAttribute(Type providerType) 20 | : base(providerType) 21 | { 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /EventSourceProxy/TraceTransformAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace EventSourceProxy 10 | { 11 | /// 12 | /// Provides a transformation method which will be used to modify the input value before tracing. 13 | /// 14 | /// Consumers can derive from this base to create their own methods to run against values being traced. 15 | /// This is particularly useful as a means of masking or filtering data. 16 | /// 17 | [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true)] 18 | public abstract class TraceTransformAttribute : TraceAsAttribute 19 | { 20 | /// 21 | /// Retrieves the MethodInfo for a static method to use for transforming the trace value. 22 | /// 23 | /// The MethodInfo should correspond to a static method which can handle the supplied input Type. 24 | /// The method's response will be used as the trace value. 25 | /// The type of object to bind to. 26 | /// MethodInfo for the method to use. 27 | public abstract MethodInfo GetTransformMethod(Type inputType); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /EventSourceProxy/TracingProxy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Diagnostics.Tracing; 5 | using System.Globalization; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Reflection.Emit; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace EventSourceProxy 13 | { 14 | /// 15 | /// Constructs a proxy out of a logger and an object to give you automatic logging of an interface. 16 | /// 17 | public static class TracingProxy 18 | { 19 | #region Private Members 20 | /// 21 | /// A cache of the constructors for the proxies. 22 | /// 23 | private static ConcurrentDictionary, Func> _constructors = 24 | new ConcurrentDictionary, Func>(); 25 | #endregion 26 | 27 | #region Public Members 28 | /// 29 | /// Creates a tracing proxy around the T interface or class of the given object. 30 | /// Events will log to the EventSource defined for type T. 31 | /// The proxy will trace any virtual or interface methods of type T. 32 | /// 33 | /// The interface or class to proxy and log. 34 | /// The instance of the object to log. 35 | /// A proxy object of type T that traces calls. 36 | public static T CreateWithActivityScope(object instance) 37 | where T : class 38 | { 39 | return (T)CreateInternal(instance, typeof(T), typeof(T), callWithActivityScope: true); 40 | } 41 | 42 | /// 43 | /// Creates a tracing proxy around the T interface or class of the given object. 44 | /// Events will log to the EventSource defined for type T. 45 | /// The proxy will trace any virtual or interface methods of type T. 46 | /// The proxy will not create an Activity Scope. You should use CreateWithActivityScope unless you know that 47 | /// your context will have an Activity Scope and you want to optimize performance a little. 48 | /// 49 | /// The interface or class to proxy and log. 50 | /// The instance of the object to log. 51 | /// A proxy object of type T that traces calls. 52 | public static T Create(object instance) 53 | where T : class 54 | { 55 | return (T)CreateInternal(instance, typeof(T), typeof(T), callWithActivityScope: false); 56 | } 57 | 58 | /// 59 | /// Creates a tracing proxy around the T interface or class of the given object 60 | /// and attempts to log to an alternate EventSource defined by TEventSource. 61 | /// Events will log to the EventSource defined for type TEventSource. 62 | /// The proxy will trace any methods that match the signatures of methods on TEventSource. 63 | /// 64 | /// The interface or class to proxy and log. 65 | /// The matching interface to log to. 66 | /// The instance of the object to log. 67 | /// A proxy object of type T that traces calls. 68 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")] 69 | public static T CreateWithActivityScope(T instance) 70 | where T : class 71 | where TEventSource : class 72 | { 73 | return (T)CreateInternal(instance, typeof(T), typeof(TEventSource), callWithActivityScope: true); 74 | } 75 | 76 | /// 77 | /// Creates a tracing proxy around the T interface or class of the given object 78 | /// and attempts to log to an alternate EventSource defined by TEventSource. 79 | /// Events will log to the EventSource defined for type TEventSource. 80 | /// The proxy will trace any methods that match the signatures of methods on TEventSource. 81 | /// The proxy will not create an Activity Scope. You should use CreateWithActivityScope unless you know that 82 | /// your context will have an Activity Scope and you want to optimize performance a little. 83 | /// 84 | /// The interface or class to proxy and log. 85 | /// The matching interface to log to. 86 | /// The instance of the object to log. 87 | /// A proxy object of type T that traces calls. 88 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")] 89 | public static T Create(T instance) 90 | where T : class 91 | where TEventSource : class 92 | { 93 | return (T)CreateInternal(instance, typeof(T), typeof(TEventSource), callWithActivityScope: false); 94 | } 95 | 96 | /// 97 | /// Creates a tracing proxy around the T interface or class of the given object. 98 | /// Events will log to the EventSource defined for type T. 99 | /// The proxy will trace any virtual or interface methods of type T. 100 | /// 101 | /// The instance of the object to log. 102 | /// The type of interface to log on. 103 | /// A proxy object of type interfaceType that traces calls. 104 | public static object CreateWithActivityScope(object instance, Type interfaceType) 105 | { 106 | return CreateInternal(instance, interfaceType, interfaceType, callWithActivityScope: true); 107 | } 108 | 109 | /// 110 | /// Creates a tracing proxy around the T interface or class of the given object. 111 | /// Events will log to the EventSource defined for type T. 112 | /// The proxy will trace any virtual or interface methods of type T. 113 | /// The proxy will not create an Activity Scope. You should use CreateWithActivityScope unless you know that 114 | /// your context will have an Activity Scope and you want to optimize performance a little. 115 | /// 116 | /// The instance of the object to log. 117 | /// The type of interface to log on. 118 | /// A proxy object of type interfaceType that traces calls. 119 | public static object Create(object instance, Type interfaceType) 120 | { 121 | return CreateInternal(instance, interfaceType, interfaceType, callWithActivityScope: false); 122 | } 123 | #endregion 124 | 125 | #region Implementation 126 | /// 127 | /// Creates a proxy out of a logger and an object to give you automatic logging of an instance. 128 | /// The logger and object must implement the same interface. 129 | /// 130 | /// The instance of the object that executes the interface. 131 | /// The type of the execute object. 132 | /// The type on the log object that should be mapped to the execute object. 133 | /// True to create a proxy that guarantees there is an activity scope around each call. 134 | /// A proxy object of type T that logs to the log object and executes on the execute object. 135 | private static object CreateInternal(object execute, Type executeType, Type logType, bool callWithActivityScope) 136 | { 137 | if (!executeType.IsInstanceOfType(execute)) 138 | throw new ArgumentException("execute", String.Format(CultureInfo.InvariantCulture, "Object must implement {0} in order to proxy it.", executeType.FullName)); 139 | 140 | // cache constructors based on tuple of types, including logoverride 141 | var tuple = Tuple.Create(executeType, logType, callWithActivityScope); 142 | 143 | // get the constructor 144 | var creator = _constructors.GetOrAdd( 145 | tuple, 146 | t => (Func)new TracingProxyImplementer(t.Item1, t.Item2, t.Item3).CreateMethod.CreateDelegate(typeof(Func))); 147 | 148 | return creator(execute); 149 | } 150 | #endregion 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /GenerateProxyManifest/GenerateProxyManifest.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Exe 9 | netcoreapp2.0 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /GenerateProxyManifest/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.Tracing; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using EventSourceProxy; 9 | 10 | namespace GenerateProxyManifest 11 | { 12 | class Program 13 | { 14 | static bool _showHelp = false; 15 | static bool _outputGuid = false; 16 | static string _assemblyPath = null; 17 | static string _typeName = null; 18 | static string _name = null; 19 | 20 | static void Main(string[] args) 21 | { 22 | for (int i = 0; i < args.Length; i++) 23 | { 24 | switch (args[i].ToLowerInvariant()) 25 | { 26 | case "-h": 27 | case "--help": 28 | case "-?": 29 | case "/?": 30 | _showHelp = true; 31 | break; 32 | 33 | case "-n": 34 | case "-name": 35 | _name = args[++i]; 36 | _outputGuid = true; 37 | break; 38 | 39 | case "-g": 40 | case "-guid": 41 | _outputGuid = true; 42 | break; 43 | 44 | case "-a": 45 | case "-assembly": 46 | _assemblyPath = args[++i]; 47 | break; 48 | 49 | case "-t": 50 | case "-type": 51 | _typeName = args[++i]; 52 | break; 53 | 54 | default: 55 | if (args[i][0] == '-') 56 | throw new ApplicationException(String.Format("Unknown option {0}", args[i])); 57 | else if (_assemblyPath == null) 58 | _assemblyPath = args[i]; 59 | else if (_typeName == null) 60 | _typeName = args[i]; 61 | else 62 | throw new ApplicationException("Too many parameters"); 63 | break; 64 | } 65 | } 66 | 67 | if (_showHelp) 68 | Console.WriteLine(@" 69 | GenerateProxyManifest - outputs the ETW information for a class. 70 | 71 | Parameters: 72 | -h 73 | -help 74 | -? 75 | /? 76 | Shows Help 77 | 78 | -a [assemblypath] 79 | -assembly [assemblypath] 80 | The path to the assembly .exe or .dll 81 | 82 | -t [type full name] 83 | -type [type full name] 84 | The full name of the type. 85 | 86 | -g 87 | -guid 88 | To output the ETW provider GUID rather than the manifest. 89 | 90 | -n [provider name] 91 | -name [provider name] 92 | Outputs the ETW provider guid given the provider name. 93 | "); 94 | else if (_name != null) 95 | Console.WriteLine(EventSourceManifest.GetGuidFromProviderName(_name)); 96 | else if (_outputGuid) 97 | Console.WriteLine(GetGuid(_assemblyPath, _typeName)); 98 | else 99 | Console.WriteLine(GenerateManifest(_assemblyPath, _typeName)); 100 | } 101 | 102 | /// 103 | /// Return the GUID of a provider from the assembly and type. 104 | /// 105 | /// The path to the assembly containing the type. 106 | /// The full name of the type. 107 | /// The GUID representing the name. 108 | private static Guid GetGuid(string assemblyPath, string typeName) 109 | { 110 | Assembly assembly = Assembly.LoadFrom(assemblyPath); 111 | Type type = assembly.GetType(typeName); 112 | 113 | return EventSourceManifest.GetGuid(type); 114 | } 115 | 116 | /// 117 | /// Return the manifest of a provider from the assembly and type. 118 | /// 119 | /// The path to the assembly containing the type. 120 | /// The full name of the type. 121 | /// The XML manifest content. 122 | private static string GenerateManifest(string assemblyPath, string typeName) 123 | { 124 | Assembly assembly = Assembly.LoadFrom(assemblyPath); 125 | Type type = assembly.GetType(typeName); 126 | 127 | return EventSourceManifest.GenerateManifest(type); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | EventSourceProxy 2 | Copyright (C) 2013, Jon Wagner (jonwagner@hotmail.com), and others 3 | 4 | =============================================================== 5 | Microsoft Public License (MS-PL) 6 | 7 | This license governs use of the accompanying software. If you use the software, you 8 | accept this license. If you do not accept the license, do not use the software. 9 | 10 | 1. Definitions 11 | The terms "reproduce," "reproduction," "derivative works," and "distribution" have the 12 | same meaning here as under U.S. copyright law. 13 | A "contribution" is the original software, or any additions or changes to the software. 14 | A "contributor" is any person that distributes its contribution under this license. 15 | "Licensed patents" are a contributor's patent claims that read directly on its contribution. 16 | 17 | 2. Grant of Rights 18 | (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. 19 | (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. 20 | 21 | 3. Conditions and Limitations 22 | (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. 23 | (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. 24 | (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. 25 | (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. 26 | (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. 27 | 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EventSourceProxy # 2 | 3 | **EventSourceProxy** (ESP) is the easiest way to add scalable Event Tracing for Windows (ETW) logging to your .NET program. 4 | 5 | **Now in NuGet!** 6 | 7 | There are now two versions of ESP: 8 | 9 | * EventSourceProxy - works with the .NET Framework - System.Diagnostics.Tracing 10 | * EventSourceProxy.NuGet - works with the EventSource library in NuGet - Microsoft.Diagnostics.Tracing 11 | 12 | [Get EventSourceProxy (ESP) from NuGet](http://nuget.org/packages/EventSourceProxy) 13 | 14 | ## Why You Want This ## 15 | 16 | - You really should be logging more than you do now. 17 | - ETW is the best way to log in Windows. 18 | - It's about zero effort to add logging to new code. 19 | - It's zero effort to add logging to existing interfaces. 20 | - Generated IL keeps overhead low, and it's almost nothing if tracing is off. 21 | 22 | Here is ESP implementing a logging interface for you automatically: 23 | 24 | public interface ILog 25 | { 26 | void SomethingIsStarting(string message); 27 | void SomethingIsFinishing(string message); 28 | } 29 | 30 | // yeah, this is it 31 | var log = EventSourceImplementer.GetEventSourceAs(); 32 | 33 | log.SomethingIsStarting("hello"); 34 | log.SomethingIsFinishing("goodbye"); 35 | 36 | Here is ESP doing the hard work of implementing an EventSource if you really want to do that: 37 | 38 | public abstract MyEventSource : EventSource 39 | { 40 | public abstract void SomethingIsStarting(string message); 41 | public abstract void SomethingIsFinishing(string message); 42 | } 43 | 44 | // ESP does the rest 45 | var log = EventSourceImplementer.GetEventSourceAs(); 46 | 47 | Here is ESP wrapping an existing interface for tracing: 48 | 49 | public interface ICalculator 50 | { 51 | void Clear(); 52 | int Add(int x, int y); 53 | int Multiple(int x, int y); 54 | } 55 | 56 | public class Calculator : ICalculator 57 | { 58 | // blah blah 59 | } 60 | 61 | Calculator calculator = new Calculator(); 62 | 63 | // again, that's it 64 | ICalculator proxy = TracingProxy.CreateWithActivityScope(calculator); 65 | 66 | // all calls are automatically logged when the ETW source is enabled 67 | int total = proxy.Add(1, 2); 68 | 69 | And let's say that your interface doesn't look at all like what you want logged. You can add rules to clean all that up: 70 | 71 | Say you have the following interface: 72 | 73 | interface IEmailer 74 | { 75 | void Send(Email email, DateTime when); 76 | void Receive(Email email); 77 | void Cancel(string from, string to, DateTime earliest, DateTime latest); 78 | } 79 | 80 | class Email 81 | { 82 | public string From; 83 | public string To; 84 | public string Subject; 85 | public string Body; 86 | public IEnumerable Attachments; 87 | } 88 | 89 | Set up rules on how ESP should trace the data to ETW: 90 | 91 | TraceParameterProvider.Default 92 | .For() 93 | .With() 94 | .Trace(e => e.From).As("Sender") 95 | .Trace(e => e.To).As("Recipient") 96 | .Trace(e => e.Subject).As("s") 97 | .And(e => e.Body).As("b") 98 | .TogetherAs("message") 99 | .Trace(e => String.Join("/", e.Attachments.Select(Convert.ToBase64String).ToArray())) 100 | .As("attachments") 101 | .For(m => m.Send(Any.Value, Any.Value) 102 | .Ignore("when"); 103 | .ForAnything() 104 | .AddContext("user", () => SomeMethodThatChecksIdentity()); 105 | 106 | And now the Send method will log: 107 | 108 | * Sender : From 109 | * Recipient : To 110 | * Message : { "s":subject, "b":body } 111 | * Attachments : [ base64, base64 ] 112 | * User : current user 113 | 114 | So, this is great for adding logging to any interface in your application. 115 | 116 | # Features # 117 | 118 | * Automatically implement logging for any interface, class derived from EventSource. 119 | * Takes all of the boilerplate code out of implementing an EventSource. 120 | * Supports EventSource and Event attributes for controlling the generated events. 121 | * Supports reusing Keyword, Opcode, and Task enums in multiple log sources. 122 | * Automatically proxy any interface and create a logging source. 123 | * Add rules to transform parameters and objects from your interface to your log. 124 | * Proxies also implement _Completed and _Faulted events. 125 | * Automatically convert complex types to JSON strings for logging. 126 | * Optionally override the object serializer. 127 | * Optionally provide a logging context across an entire logging interface. 128 | * Easily manage Activity IDs with EventActivityScope. 129 | 130 | # New in v2.0 - Logging Transforms # 131 | 132 | * Use attributes and configuration rules to transform your interface calls to entirely different logging calls. See [Controlling Logged Data](https://github.com/jonwagner/EventSourceProxy/wiki/Controlling-Logged-Data) and [Adding Additional Logging Context](https://github.com/jonwagner/EventSourceProxy/wiki/Adding-Additional-Logging-Context). 133 | 134 | # Documentation # 135 | 136 | **Full documentation is available on the [wiki](https://github.com/jonwagner/EventSourceProxy/wiki)!** 137 | 138 | # Good References # 139 | 140 | * Want to get the data out of ETW? Use the [Microsoft Enterprise Library Semantic Logging Application Block](http://nuget.org/packages/EnterpriseLibrary.SemanticLogging/). It has ETW listeners to log to the console, rolling flat file, and databases, so you can integrate ETW with your existing log destinations. 141 | --------------------------------------------------------------------------------