Skip to content

Commit 3466714

Browse files
committed
Added trace listener.
1 parent 6d31ac6 commit 3466714

5 files changed

Lines changed: 317 additions & 7 deletions

File tree

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Author: Ivan Andrew Pointer ([email protected])
3+
* Date: 11/20/2014
4+
* License: MIT (https://raw.githubusercontent.com/ivanpointer/NuLog/master/LICENSE)
5+
* Project Home: http://www.nulog.info
6+
* GitHub: https://github.com/ivanpointer/NuLog
7+
*/
8+
9+
using Newtonsoft.Json.Linq;
10+
using NuLog.Extenders;
11+
using System.Linq;
12+
13+
namespace NuLog.Configuration.Extenders
14+
{
15+
/// <summary>
16+
/// Defines the configuration for the TraceListenerExtender
17+
/// </summary>
18+
public class TraceListenerConfig : ExtenderConfig
19+
{
20+
21+
#region Constants/Members
22+
23+
// The name of the tags configuration token in the JSON config
24+
public const string TagsTokenName = "tags";
25+
26+
// Defaults for this configuration
27+
public static readonly string DefaultType = typeof(TraceListenerExtender).FullName.ToString();
28+
public static readonly string[] DefaultTags = new string[] { "tracelistener" };
29+
30+
/// <summary>
31+
/// The list of tags to include in log events sent from trace
32+
/// </summary>
33+
public string[] TraceTags { get; set; }
34+
35+
#endregion
36+
37+
/// <summary>
38+
/// The default constructor setting this up with a "tracelistener" tag
39+
/// </summary>
40+
public TraceListenerConfig()
41+
: base()
42+
{
43+
Type = DefaultType;
44+
TraceTags = DefaultTags;
45+
}
46+
47+
/// <summary>
48+
/// Builds this TraceListenerConfig from the provided JSON token
49+
/// </summary>
50+
/// <param name="config">The JSON token from which to build this configuration</param>
51+
public TraceListenerConfig(JToken config)
52+
: base(config)
53+
{
54+
// Set our defaults
55+
Type = DefaultType;
56+
TraceTags = DefaultTags;
57+
58+
// Check to see if we have been given something parsable to
59+
// configure with. If so, look for the tags in the config
60+
// and parse those out. This supports a configuration of
61+
// a single tag as a string, or a string array of tags.
62+
if (config != null && config.Type == JTokenType.Object)
63+
{
64+
var tagsRaw = config[TagsTokenName];
65+
if (tagsRaw != null)
66+
{
67+
if (tagsRaw.Type == JTokenType.Array)
68+
{
69+
TraceTags = tagsRaw.Values<string>().ToArray<string>();
70+
}
71+
else if (tagsRaw.Type == JTokenType.String)
72+
{
73+
TraceTags = new string[] { tagsRaw.Value<string>() };
74+
}
75+
}
76+
}
77+
}
78+
79+
}
80+
}

Source/NuLog/Configuration/LoggingConfigBuilder.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* GitHub: https://github.com/ivanpointer/NuLog
77
*/
88

9+
using NuLog.Configuration.Extenders;
910
using NuLog.Configuration.Targets;
1011
using System;
1112
using System.Collections.Generic;
@@ -21,6 +22,9 @@ public class LoggingConfigBuilder : ILoggingConfigBuilder
2122
private IList<TargetConfig> Targets { get; set; }
2223
private IList<RuleConfig> Rules { get; set; }
2324
private IList<TagGroupConfig> TagGroups { get; set; }
25+
private IList<string> ConfigExtenders { get; set; }
26+
private IList<string> StaticMetaDataProviders { get; set; }
27+
private IList<ExtenderConfig> Extenders { get; set; }
2428
public Action<Exception, string> ExceptionHandler { get; set; }
2529
public bool Debug { get; set; }
2630

@@ -32,6 +36,9 @@ private LoggingConfigBuilder()
3236
Targets = new List<TargetConfig>();
3337
Rules = new List<RuleConfig>();
3438
TagGroups = new List<TagGroupConfig>();
39+
ConfigExtenders = new List<string>();
40+
StaticMetaDataProviders = new List<string>();
41+
Extenders = new List<ExtenderConfig>();
3542
ExceptionHandler = null;
3643
}
3744

@@ -93,6 +100,50 @@ public LoggingConfigBuilder AddTagGroup(string tag, params string[] childTags)
93100
return this;
94101
}
95102

103+
/// <summary>
104+
/// Adds the provided config extender to the configuration
105+
/// </summary>
106+
/// <param name="type">The config extender to add to the configuration</param>
107+
/// <returns>This LoggingConfigBuilder</returns>
108+
public LoggingConfigBuilder AddConfigExtender(string type)
109+
{
110+
ConfigExtenders.Add(type);
111+
return this;
112+
}
113+
114+
/// <summary>
115+
/// Adds the provided static meta dat aprovider to the configuration
116+
/// </summary>
117+
/// <param name="type">The static meta data provider to add to the configuration</param>
118+
/// <returns>This LoggingConfigBuilder</returns>
119+
public LoggingConfigBuilder AddStaticMetaDataProvider(string type)
120+
{
121+
StaticMetaDataProviders.Add(type);
122+
return this;
123+
}
124+
125+
/// <summary>
126+
/// Adds the provided extender config to the configuration
127+
/// </summary>
128+
/// <param name="extender">The extender config to add</param>
129+
/// <returns>This LoggingConfigBuilder</returns>
130+
public LoggingConfigBuilder AddExtender(ExtenderConfig extender)
131+
{
132+
Extenders.Add(extender);
133+
return this;
134+
}
135+
136+
/// <summary>
137+
/// Adds the provided extender to the configuration
138+
/// </summary>
139+
/// <param name="type">The type of the extender to add</param>
140+
/// <returns>This LoggingConfigBuilder</returns>
141+
public LoggingConfigBuilder AddExtender(string type)
142+
{
143+
Extenders.Add(new ExtenderConfig(type));
144+
return this;
145+
}
146+
96147
/// <summary>
97148
/// Sets the exception handler to the logging configuration
98149
/// </summary>

Source/NuLog/Configuration/Targets/ConsoleColorRule.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ public ConsoleColorRule(string backgroundColor, string foregroundColor, params s
6464
? tags.ToList()
6565
: new List<string>();
6666

67-
BackgroundColor = (ConsoleColor)Enum.Parse(ConsoleColorType, backgroundColor); ;
68-
ForegroundColor = (ConsoleColor)Enum.Parse(ConsoleColorType, foregroundColor); ;
67+
BackgroundColor = (ConsoleColor)Enum.Parse(ConsoleColorType, backgroundColor);
68+
ForegroundColor = (ConsoleColor)Enum.Parse(ConsoleColorType, foregroundColor);
6969
}
7070

7171
/// <summary>

Source/NuLog/Extenders/TraceListenerExtender.cs

Lines changed: 183 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,197 @@
66
* GitHub: https://github.com/ivanpointer/NuLog
77
*/
88

9+
using NuLog.Configuration;
10+
using NuLog.Configuration.Extenders;
11+
using NuLog.Dispatch;
912
using System;
10-
using System.Collections.Generic;
11-
using System.Linq;
13+
using System.Diagnostics;
1214
using System.Text;
13-
using System.Threading.Tasks;
1415

1516
namespace NuLog.Extenders
1617
{
18+
/// <summary>
19+
/// An extender that listens to trace/debug messages and sends them through
20+
/// the framework as log events.
21+
/// </summary>
1722
public class TraceListenerExtender : ExtenderBase
1823
{
19-
public override void Startup(Dispatch.LogEventDispatcher dispatcher)
24+
/// <summary>
25+
/// This is an internal class that extends the basic TraceListener
26+
/// This is what will actually do the listening for trace
27+
/// </summary>
28+
internal class InternalTraceListener : TraceListener
2029
{
21-
throw new NotImplementedException();
30+
#region Constants/Members/Messages
31+
32+
// Our constants, including a write lock to help keep our writing clean
33+
private static readonly object WriteLock = new object();
34+
private static readonly char[] Split = Environment.NewLine.ToCharArray();
35+
36+
// Our members, including a logger
37+
private static readonly LoggerBase _logger = LoggerFactory.GetLogger();
38+
private string[] _tags;
39+
private StringBuilder _stringBuilder;
40+
41+
#endregion
42+
43+
/// <summary>
44+
/// The only constructor. Configures this trace listener based on
45+
/// the configuration in the provided TraceListenerExtender
46+
/// </summary>
47+
/// <param name="extender">The TraceListenerExtender that this TraceListener belongs to, and from which to load the configuration</param>
48+
public InternalTraceListener(TraceListenerExtender extender)
49+
: base()
50+
{
51+
// Prepare the String Builder
52+
_stringBuilder = new StringBuilder();
53+
54+
// We want to get the tags from the config
55+
if (extender != null && extender.TraceListenerConfig != null)
56+
_tags = extender.TraceListenerConfig.TraceTags;
57+
}
58+
59+
/// <summary>
60+
/// Writes a single message. If it has no newline characters, the contents are stored
61+
/// in a string buffer until a WriteLine is called, or a Write with a message that
62+
/// contains newlines, at which point, the message will be flushed to log
63+
/// </summary>
64+
/// <param name="message">The message to flush to log</param>
65+
public override void Write(string message)
66+
{
67+
// Make sure that we have a message
68+
if (String.IsNullOrEmpty(message) == false)
69+
lock (WriteLock)
70+
{
71+
// If the message has no newline, just queue it up
72+
if (!message.Contains(Environment.NewLine))
73+
{
74+
_stringBuilder.Append(message);
75+
}
76+
// The message contains at least one newline, split it up
77+
else
78+
{
79+
// Setup
80+
bool endsNewline = message.EndsWith(Environment.NewLine);
81+
var parts = message.Split(Split);
82+
83+
// Append, and write the first part
84+
// unless it is the only part and we have no newline at the end
85+
_stringBuilder.Append(parts[0]);
86+
if (parts.Length > 1 || endsNewline)
87+
{
88+
WriteLine(_stringBuilder.ToString());
89+
_stringBuilder.Clear();
90+
}
91+
92+
// Write out each of the child parts if there are more, queuing the last bit if
93+
// it isn't followed by a newline
94+
if (parts.Length > 1)
95+
{
96+
for (int index = 1; index < (endsNewline ? parts.Length : parts.Length - 1); index++)
97+
WriteLine(parts[index]);
98+
99+
if (endsNewline == false)
100+
_stringBuilder.Append(parts[parts.Length - 1]);
101+
}
102+
}
103+
}
104+
}
105+
106+
/// <summary>
107+
/// Flushes the buffer and writes the message to log
108+
/// </summary>
109+
/// <param name="message">The message to write to log</param>
110+
public override void WriteLine(string message)
111+
{
112+
// Ignore this request if the message is empty
113+
if (String.IsNullOrEmpty(message) == false)
114+
lock (WriteLock)
115+
{
116+
// Write the contents of the buffer out, followed by the message
117+
_logger.Log(new LogEvent
118+
{
119+
Message = String.Format("{0}{1}", _stringBuilder.ToString(), message),
120+
Tags = _tags,
121+
// Help prevent feedback loops:
122+
Silent = true
123+
});
124+
_stringBuilder.Clear();
125+
}
126+
}
127+
128+
/// <summary>
129+
/// Flushes the contents of the buffer out to log
130+
/// </summary>
131+
public override void Flush()
132+
{
133+
lock (WriteLock)
134+
{
135+
// Write out the contents of the buffer
136+
WriteLine(_stringBuilder.ToString());
137+
_stringBuilder.Clear();
138+
}
139+
}
140+
}
141+
142+
// This TraceListenerExtender's TraceListener
143+
private TraceListener _traceListener;
144+
145+
/// <summary>
146+
/// The configuration for this TraceListenerExtender
147+
/// </summary>
148+
public TraceListenerConfig TraceListenerConfig { get; set; }
149+
150+
/// <summary>
151+
/// Initializes this extender with the provided configuration
152+
/// </summary>
153+
/// <param name="extenderConfig">The configuration for this specific extender</param>
154+
/// <param name="loggingConfig">The whole configuration for the framework</param>
155+
public override void Initialize(ExtenderConfig extenderConfig, LoggingConfig loggingConfig)
156+
{
157+
// Let the base configure itself out
158+
base.Initialize(extenderConfig, loggingConfig);
159+
160+
// Configure this extender, using the TraceListenerConfig
161+
if (extenderConfig != null)
162+
{
163+
TraceListenerConfig = extenderConfig is TraceListenerConfig
164+
? (TraceListenerConfig)extenderConfig
165+
: new TraceListenerConfig(extenderConfig.Config);
166+
}
167+
}
168+
169+
/// <summary>
170+
/// Starts this trace listener extender. Starts a trace listener that will
171+
/// route trace messages through the framework as log events
172+
/// </summary>
173+
/// <param name="dispatcher">The dispatcher that has been initialized for the framework. This TraceListenerExtender doesn't use the dispatcher.</param>
174+
public override void Startup(LogEventDispatcher dispatcher)
175+
{
176+
_traceListener = new InternalTraceListener(this);
177+
Trace.Listeners.Add(_traceListener);
178+
}
179+
180+
/// <summary>
181+
/// Shuts down this instance. Will flush the buffer of the trace listener and remove it from the trace listeners list.
182+
/// </summary>
183+
/// <param name="timeout">The amount of time for this to shutdown. Ignored.</param>
184+
/// <param name="stopwatch">A stopwatch. Ignored.</param>
185+
/// <returns>A boolean indicating whether or not this shutdown successfully. This always returns true.</returns>
186+
public override bool Shutdown(int timeout = DefaultShutdownTimeout, Stopwatch stopwatch = null)
187+
{
188+
// If we have a configured trace listener, flush it and remove it from the list
189+
// of listeners.
190+
if (_traceListener != null)
191+
{
192+
_traceListener.Flush();
193+
_traceListener.Close();
194+
_traceListener.Dispose();
195+
Trace.Listeners.Remove(_traceListener);
196+
_traceListener = null;
197+
}
198+
return base.Shutdown();
22199
}
200+
23201
}
24202
}

Source/NuLog/NuLog.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
<Compile Include="Dispatch\TargetKeeper.cs" />
7575
<Compile Include="Extenders\ExtenderBase.cs" />
7676
<Compile Include="Extenders\IExtender.cs" />
77+
<Compile Include="Configuration\Extenders\TraceListenerConfig.cs" />
7778
<Compile Include="Extenders\TraceListenerExtender.cs" />
7879
<Compile Include="Extensions\Console\ConsoleLoggerExt.cs" />
7980
<Compile Include="Extensions\Email\EmailLogEventBuilder.cs" />

0 commit comments

Comments
 (0)