diff --git a/Implab.Playground/Program.cs b/Implab.Playground/Program.cs --- a/Implab.Playground/Program.cs +++ b/Implab.Playground/Program.cs @@ -12,141 +12,24 @@ using Unity.Injection; using Unity.Registration; namespace Implab.Playground { - - public class Foo { - - public class Bar { - - } - - public string Name { get; set; } - - public int IntValue { get; set; } - - public string StringValue { get; set; } - - public void AddRange(Foo[] items) { - Console.WriteLine($"AddRange: Foo[]"); - } + using static Trace; + + class Foo { } - public class FooFactory : IFactory, IFactory { - - public bool UseSsl { get; set; } - - public string Connection { get; set; } - - public Foo Create() { - return new Foo() { - Name = "AutoFac" - }; - } - - Foo.Bar IFactory.Create() { - return new Foo.Bar(); - } - } - - public interface IContainer { - T Instance { get; set; } - } - - public class Container : IContainer { - public class Bar { - - } + class Bar : Foo { - public class Bar { - public class Baz { - - } - - } - - public Container() { - - } - - public Container(T instance) { - Instance = instance; - } - - public T Instance { get; set; } - - public void SetInstance(T value) { - Instance = value; - } - - public void AddRange(List items) { - Console.WriteLine($"AddRange: {typeof(List)}"); - } - - public void AddRange(T[] items) { - Console.WriteLine($"AddRange: T[] ofType {typeof(T[])}"); - } } - public class Program { static void Main(string[] args) { - var u1 = new Uri("/some/one"); - - dynamic obj = new ExpandoObject(); - - obj.Name = "Dynamo"; - - obj.Hello = new Func(() => { return "Hello"; }); - - Console.WriteLine($"{obj.Hello()}"); - - } - - static void Main2(string[] args) { - var listener = new SimpleTraceListener(Console.Out); - var source = Trace.TraceSource; - source.Switch.Level = SourceLevels.All; - source.Listeners.Add(listener); - - var stopwatch = new Stopwatch(); - stopwatch.Start(); - - var container = new UnityContainer(); - - Console.WriteLine($"Created: {stopwatch.ElapsedMilliseconds}"); - stopwatch.Restart(); - container.LoadXmlConfiguration("data/sample.xml"); - - Console.WriteLine($"Loaded: {stopwatch.ElapsedMilliseconds}"); - - stopwatch.Restart(); - var instace1 = container.Resolve>(); - Console.WriteLine($"Resolved1: {stopwatch.ElapsedMilliseconds}"); - - stopwatch.Restart(); - var instace2 = container.Resolve>(); - Console.WriteLine($"Resolved2: {stopwatch.ElapsedMilliseconds}"); - - DisplayContainerRegistrations(container); - } - - static void DisplayContainerRegistrations(IUnityContainer theContainer) { - string regName, regType, mapTo, lifetime; - Console.WriteLine("Container has {0} Registrations:", - theContainer.Registrations.Count()); - foreach (ContainerRegistration item in theContainer.Registrations) { - regType = item.RegisteredType.FullName; - mapTo = item.MappedToType.FullName; - regName = item.Name ?? "[default]"; - lifetime = item.LifetimeManager.LifetimeType.Name; - if (mapTo != regType) { - mapTo = " -> " + mapTo; - } else { - mapTo = string.Empty; - } - lifetime = lifetime.Substring(0, lifetime.Length - "LifetimeManager".Length); - Console.WriteLine("+ {0}{1} '{2}' {3}", regType, mapTo, regName, lifetime); + using(TraceRegistry.Global.Subscribe(ch => { + Console.WriteLine($"{ch.Id}: {ch.Source.Name}"); + }, true)) { + Trace.Log("Hi!"); + Log("Respect!"); } } diff --git a/Implab.ServiceHost/Unity/ContainerBuilder.cs b/Implab.ServiceHost/Unity/ContainerBuilder.cs --- a/Implab.ServiceHost/Unity/ContainerBuilder.cs +++ b/Implab.ServiceHost/Unity/ContainerBuilder.cs @@ -5,7 +5,7 @@ using Implab.Diagnostics; using Unity; namespace Implab.ServiceHost.Unity { - using static Trace; + using Log = Trace; public class ContainerBuilder { @@ -33,7 +33,9 @@ namespace Implab.ServiceHost.Unity { } public Type ResolveType(string typeReference) { - return string.IsNullOrEmpty(typeReference) ? null : m_resolver.Resolve(typeReference, true); + var resolved = string.IsNullOrEmpty(typeReference) ? null : m_resolver.Resolve(typeReference, true); + Log.Debug("ResolveType('{0}'): {1}", typeReference, resolved?.FullName); + return resolved; } public void Visit(ITypeRegistration registration) { diff --git a/Implab/Diagnostics/LogicalOperation.cs b/Implab/Diagnostics/LogicalOperation.cs --- a/Implab/Diagnostics/LogicalOperation.cs +++ b/Implab/Diagnostics/LogicalOperation.cs @@ -1,15 +1,25 @@ using System; -using System.Diagnostics; +using Stopwatch = System.Diagnostics.Stopwatch; namespace Implab.Diagnostics { public class LogicalOperation { - public Stopwatch OperationStopwatch { get; private set; } + readonly Stopwatch m_stopwatch; public string Name { get; private set; } internal LogicalOperation(string name) { Name = string.IsNullOrEmpty(name) ? "" : name; - OperationStopwatch = Stopwatch.StartNew(); + m_stopwatch = Stopwatch.StartNew(); + } + + public TimeSpan Elapsed { + get { + return m_stopwatch.Elapsed; + } + } + + public void End() { + m_stopwatch.Stop(); } public override string ToString() => Name; diff --git a/Implab/Diagnostics/LogicalOperationScope.cs b/Implab/Diagnostics/LogicalOperationScope.cs --- a/Implab/Diagnostics/LogicalOperationScope.cs +++ b/Implab/Diagnostics/LogicalOperationScope.cs @@ -13,7 +13,7 @@ namespace Implab.Diagnostics { } public void Dispose() { - m_operation.OperationStopwatch.Stop(); + m_operation.End(); Trace.CorrelationManager.StopLogicalOperation(); m_source.TraceData(TraceEventType.Information, TraceEventCodes.StopLogicalOperation, m_operation); } diff --git a/Implab/Diagnostics/SimpleTraceListener.cs b/Implab/Diagnostics/SimpleTraceListener.cs --- a/Implab/Diagnostics/SimpleTraceListener.cs +++ b/Implab/Diagnostics/SimpleTraceListener.cs @@ -41,7 +41,7 @@ namespace Implab.Diagnostics { string FormatStopLogicalOperation(object data) { if (data is LogicalOperation op) { - return string.Format("-{0} ({1})", op, FormatTimespan(op.OperationStopwatch.Elapsed)); + return string.Format("-{0} ({1})", op, FormatTimespan(op.Elapsed)); } else { return data?.ToString(); } @@ -90,7 +90,7 @@ namespace Implab.Diagnostics { operation = eventCache.LogicalOperationStack.Peek() as LogicalOperation; if (operation != null) { - base.TraceData(eventCache, source, eventType, id, FormatTimespan(operation.OperationStopwatch.Elapsed) + ": " + message); + base.TraceData(eventCache, source, eventType, id, FormatTimespan(operation.Elapsed) + ": " + message); } else { base.TraceData(eventCache, source, eventType, id, message); } diff --git a/Implab/Diagnostics/Trace.cs b/Implab/Diagnostics/Trace.cs --- a/Implab/Diagnostics/Trace.cs +++ b/Implab/Diagnostics/Trace.cs @@ -10,35 +10,50 @@ using System.Threading; using System.Threading.Tasks; namespace Implab.Diagnostics { + /// + /// Static class which creates an individual for + /// the type specified in the parameter . + /// + /// The type for which tracing is demanded. public static class Trace { - static Lazy _traceSource = new Lazy(CreateChannel, LazyThreadSafetyMode.ExecutionAndPublication); - - static int _nextId; + readonly static Lazy _trace = new Lazy(() => TraceSourceChannel.Default.Source); - static TraceSource CreateChannel() { - var id = Interlocked.Increment(ref _nextId); - var trs = new TraceSource(typeof(T).Name); - - TraceChannelRegistry.AllCannels.NotifyChannelCreated(new ChannelInfo(typeof(T), trs)); - - return trs; - } - - public static TraceSource TraceSource { get { return _traceSource.Value; } } + public static TraceSource TraceSource { get { return _trace.Value; } } #if NETFX_TRACE_BUG readonly static AsyncLocal m_currentOperation = new AsyncLocal(); #endif /// - /// Starts the logical operation nested to the current operation nested to the current one. + /// If this property is set then will produce output. + /// + public static bool TraceVerbose { + get { + return TraceSource.Switch.ShouldTrace(TraceEventType.Verbose); + } + } + + /// + /// If this property is set then will produce output. /// - public static void StartLogicalOperation() { - Trace.CorrelationManager.StartLogicalOperation(); + public static bool TraceInformation { + get { + return TraceSource.Switch.ShouldTrace(TraceEventType.Information); + } + } + /// + /// If this property is set then will produce output. + /// + public static bool TraceWarnings { + get { + return TraceSource.Switch.ShouldTrace(TraceEventType.Warning); + } } + + /// /// Starts the logical operation with the specified name, this name is usefull in logs. /// @@ -48,10 +63,27 @@ namespace Implab.Diagnostics { m_currentOperation.Value = name; Trace.CorrelationManager.StartLogicalOperation(name); } + + /// + /// Starts the logical operation nested to the current operation nested to the current one. + /// + public static void StartLogicalOperation() { + m_currentOperation.Value = new object(); + Trace.CorrelationManager.StartLogicalOperation(); + + } #else public static void StartLogicalOperation(object name) { Trace.CorrelationManager.StartLogicalOperation(name); } + + /// + /// Starts the logical operation nested to the current operation nested to the current one. + /// + public static void StartLogicalOperation() { + Trace.CorrelationManager.StartLogicalOperation(); + + } #endif /// @@ -68,7 +100,7 @@ namespace Implab.Diagnostics { /// Arguments. [Conditional("DEBUG")] public static void Debug(string format, params object[] arguments) { - + TraceSource.TraceEvent(TraceEventType.Verbose, 0, format, arguments); } /// @@ -135,7 +167,7 @@ namespace Implab.Diagnostics { Trace.CorrelationManager.ActivityId = Guid.NewGuid(); var prev = Trace.CorrelationManager.ActivityId; - + TraceSource.TraceEvent(TraceEventType.Start, 0, activityName); return new ActivityScope(TraceSource, prev, 0, activityName); } diff --git a/Implab/Diagnostics/TraceChannel.cs b/Implab/Diagnostics/TraceChannel.cs new file mode 100644 --- /dev/null +++ b/Implab/Diagnostics/TraceChannel.cs @@ -0,0 +1,17 @@ +namespace Implab.Diagnostics +{ + public abstract class TraceChannel { + readonly object m_id; + + public object Id { + get { + return m_id; + } + } + + protected TraceChannel(object id) { + m_id = id ?? new object(); + } + + } +} \ No newline at end of file diff --git a/Implab/Diagnostics/TraceRegistry.cs b/Implab/Diagnostics/TraceRegistry.cs new file mode 100644 --- /dev/null +++ b/Implab/Diagnostics/TraceRegistry.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Implab.Parallels; + +namespace Implab.Diagnostics { + public class TraceRegistry { + + class Subscription : IDisposable { + readonly WeakReference m_registry; + readonly Action m_unsubscribe; + + public Subscription(TraceRegistry registry) { + m_registry = new WeakReference(registry); + } + + public void Dispose() { + TraceRegistry t; + if (m_registry.TryGetTarget(out t)) + t.RemoveSubscription(this); + } + } + + public static TraceRegistry Global { get; } = new TraceRegistry(); + + readonly object m_lock = new object(); + + readonly Dictionary> m_subscriptions = new Dictionary>(); + readonly SimpleAsyncQueue m_channels = new SimpleAsyncQueue(); + + internal void Register(TraceChannel channel) { + // notifications can run in parallel + Action[] handlers = null; + + lock(m_lock) { + m_channels.Enqueue(channel); + if (m_subscriptions.Count > 0) { + handlers = new Action[m_subscriptions.Count]; + m_subscriptions.Values.CopyTo(handlers, 0); + } + } + + if (handlers != null) + foreach(var h in handlers) + h(channel); + } + + /// + /// Subscribes the specified handler to notifications about new trace + /// channels + /// + /// + /// + public IDisposable Subscribe(Action handler, bool existing) { + Safe.ArgumentNotNull(handler, nameof(handler)); + + var cookie = new Subscription(this); + + IEnumerable snap; + + // lock to ensure that no new channels will be added + // while the subscription is added + lock(m_lock) { + m_subscriptions.Add(cookie, handler); + snap = m_channels.Snapshot(); + } + + // announce previously declared channels if required + if (existing) { + foreach(var c in snap) + handler(c); + } + + // return the subscription + return cookie; + } + + void RemoveSubscription(object cookie) { + lock(m_lock) + m_subscriptions.Remove(cookie); + } + } +} \ No newline at end of file diff --git a/Implab/Diagnostics/TraceSourceAttribute.cs b/Implab/Diagnostics/TraceSourceAttribute.cs --- a/Implab/Diagnostics/TraceSourceAttribute.cs +++ b/Implab/Diagnostics/TraceSourceAttribute.cs @@ -5,6 +5,7 @@ namespace Implab.Diagnostics { /// Used to mark class which uses class to trace it's events /// [AttributeUsage(AttributeTargets.Class)] + [Obsolete("Use TraceRegistry to monitor trace sources")] public class TraceSourceAttribute : Attribute { } } diff --git a/Implab/Diagnostics/TraceSourceChannel.cs b/Implab/Diagnostics/TraceSourceChannel.cs new file mode 100644 --- /dev/null +++ b/Implab/Diagnostics/TraceSourceChannel.cs @@ -0,0 +1,27 @@ +using TraceSource = System.Diagnostics.TraceSource; + +namespace Implab.Diagnostics { + + /// + /// Trace channel which incapsulates instance. + /// + public class TraceSourceChannel : TraceChannel { + readonly TraceSource m_trace; + + public TraceSourceChannel() : base(new object()) { + } + + public TraceSourceChannel(object id) : base(id) { + } + + public TraceSourceChannel(object id, string name) : base(id) { + m_trace = new TraceSource(name); + } + + public TraceSource Source { + get { + return m_trace; + } + } + } +} \ No newline at end of file diff --git a/Implab/Diagnostics/TraceSourceChannel`1.cs b/Implab/Diagnostics/TraceSourceChannel`1.cs new file mode 100644 --- /dev/null +++ b/Implab/Diagnostics/TraceSourceChannel`1.cs @@ -0,0 +1,33 @@ +using System; +using System.Threading; +using TraceSource = System.Diagnostics.TraceSource; + +namespace Implab.Diagnostics { + /// + /// This class is used to provide a single + /// instance for the specified class in parameter. + /// + /// + /// The class for which is required. + /// + /// + /// The instance will be created on demand + /// and automatically registered in . + /// + public static class TraceSourceChannel { + static Lazy _traceSource = new Lazy(CreateChannel, LazyThreadSafetyMode.ExecutionAndPublication); + + /// + /// The default instance. + /// + public static TraceSourceChannel Default { get { return _traceSource.Value; } } + + static TraceSourceChannel CreateChannel() { + var channel = new TraceSourceChannel(typeof(T), typeof(T).Name); + + TraceRegistry.Global.Register(channel); + + return channel; + } + } +} \ No newline at end of file