Last active
August 29, 2015 14:21
-
-
Save wgv-zbonham/f8d607164f535505cf69 to your computer and use it in GitHub Desktop.
Example Logging Using System.Diagnostics
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8" ?> | |
<configuration> | |
<startup> | |
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> | |
</startup> | |
<system.diagnostics> | |
<sources> | |
<source name="WatchGuardLogging" | |
switchName="sourceSwitch" | |
switchType="System.Diagnostics.SourceSwitch"> | |
<listeners> | |
<add name="WatchGuard"/> | |
<add name="EventLog"/> | |
</listeners> | |
</source> | |
<source name="WatchGuard.PackageLoader" | |
switchName="sourceSwitch" | |
switchType="System.Diagnostics.SourceSwitch"> | |
<listeners> | |
<add name="WatchGuard"/> | |
</listeners> | |
</source> | |
</sources> | |
<sharedListeners> | |
<add name="WatchGuard" type="TickTickBoom.AzureStorageTraceListener, TickTickBoom" /> | |
<add name="EventLog" type="System.Diagnostics.EventLogTraceListener" initializeData="Application"/> | |
</sharedListeners> | |
<switches> | |
<!-- this knob controls the logging level --> | |
<add name="sourceSwitch" value="Verbose"/> | |
</switches> | |
</system.diagnostics> | |
</configuration> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Diagnostics; | |
using System.Text; | |
namespace TickTickBoom | |
{ | |
public class AzureStorageTraceListener : TraceListener | |
{ | |
public AzureStorageTraceListener() | |
{ | |
// TODO - this needs to come from Role configuration | |
StorageConnectionString = "UseDevelopmentStorage=true"; | |
} | |
public string StorageConnectionString { get; private set; } | |
public override void Write(string message) | |
{ | |
Debug.Write(message); | |
} | |
public override void WriteLine(string message) | |
{ | |
Debug.WriteLine(message); | |
} | |
/// <summary> | |
/// </summary> | |
/// <param name="eventCache"></param> | |
/// <param name="source"></param> | |
/// <param name="eventType"></param> | |
/// <param name="eventId"></param> | |
/// <param name="data"></param> | |
public override void TraceData( | |
TraceEventCache eventCache, | |
string source, | |
TraceEventType eventType, | |
int eventId, | |
object data) | |
{ | |
var appEvent = data as AppEvent; | |
if (appEvent == null) return; | |
// TODO: write to Table Storage | |
// we are going to partition logs on daily boundary | |
var partitionKey = DateTime.Today.ToUniversalTime().Ticks.ToString(); | |
// may want a better row key, or include more. | |
var rowKey = string.Format("{0}_{1}", partitionKey, Guid.NewGuid().ToString("N")); | |
var eventTick = DateTime.UtcNow.Ticks; | |
// TODO: get this from RoleEnvironment | |
var deploymentId = string.Empty; | |
var role = string.Empty; | |
var roleInstance = string.Empty; | |
Console.WriteLine(appEvent.Message); | |
} | |
} | |
public static class TraceSourceExtensions | |
{ | |
public static void Debug(this TraceSource traceSource, string message, int eventId = 0, string detail = "") | |
{ | |
WriteEvent(traceSource, TraceEventType.Verbose, message, eventId, detail); | |
} | |
public static void Info(this TraceSource traceSource, string message, int eventId = 0, string detail = "") | |
{ | |
WriteEvent(traceSource, TraceEventType.Information, message, eventId, detail); | |
} | |
public static void Warn(this TraceSource traceSource, string message, int eventId = 0, string detail = "") | |
{ | |
WriteEvent(traceSource, TraceEventType.Warning, message, eventId, detail); | |
} | |
public static void Error(this TraceSource traceSource, string message, int eventId = 0) | |
{ | |
Error(traceSource, message, eventId, null); | |
} | |
public static void Error(this TraceSource traceSource, Exception eax) | |
{ | |
Error(traceSource, eax.Message, 0, eax); | |
} | |
public static void Error(this TraceSource traceSource, string message, Exception eax) | |
{ | |
Error(traceSource, message, 0, eax); | |
} | |
public static void Error(this TraceSource traceSource, string message, int eventId, Exception eax) | |
{ | |
var detail = eax == null ? string.Empty : eax.ToString(); | |
WriteEvent(traceSource, TraceEventType.Error, message, eventId, detail); | |
} | |
private static void WriteEvent(TraceSource traceSource, TraceEventType traceEventType, string message, | |
int eventId, string detail) | |
{ | |
var appEvent = new AppEvent | |
{ | |
Source = traceSource.Name, | |
EventId = eventId, | |
Level = (int) traceEventType, | |
Message = message, | |
Detail = detail | |
}; | |
if (traceSource.Switch.ShouldTrace(traceEventType)) | |
traceSource.TraceData(traceEventType, appEvent.EventId, appEvent); | |
} | |
} | |
public class AppEvent | |
{ | |
/// <summary> | |
/// Unique instance id | |
/// </summary> | |
public Guid Id { get; set; } | |
/// <summary> | |
/// Integer representation of Error (3), Warning (3), Info (4), Verbose/Debug (16) | |
/// </summary> | |
public int Level { get; set; } | |
/// <summary> | |
/// EventId can be used to give specific event types an integer to pivot on | |
/// for monitoring and alerting decisions instead of having to read the message, or detail, to figure it out. | |
/// </summary> | |
public int EventId { get; set; } | |
/// <summary> | |
/// On premise, this is likely the Proces Name. On Azure, it will likely be Role Name. | |
/// </summary> | |
public string Source { get; set; } | |
/// <summary> | |
/// Something meaningful. | |
/// </summary> | |
public string Message { get; set; } | |
/// <summary> | |
/// Additional detail - could be a call stack for error, or message body if logging a request. | |
/// </summary> | |
public string Detail { get; set; } | |
/// <summary> | |
/// When did this event occur? | |
/// </summary> | |
public DateTime Timestamp { get; set; } | |
public override string ToString() | |
{ | |
var sb = new StringBuilder(); | |
sb.AppendLine(string.Format("Id: {0}", Id)); | |
sb.AppendLine(string.Format("Source: {0}", Source)); | |
sb.AppendLine(string.Format("Message: {0}", Message)); | |
sb.AppendLine(string.Format("Detail: {0}", Detail)); | |
return sb.ToString(); | |
} | |
} | |
internal class Program | |
{ | |
private static void Main(string[] args) | |
{ | |
// Generally, a feature area will use the *same* SourceName. e.g. Mvc, WebApi, Worker, etc. | |
// You *can* have specific sources to specific activities but then we need to manage where they are stored | |
// so we can publish to operations | |
// | |
var logger = new TraceSource("WatchGuardLogging"); | |
var packageSource = new TraceSource("WatchGuard.PackageLoader"); | |
logger.Debug("This is a debug event"); | |
logger.Info("This is an informational event"); | |
logger.Warn("this is a warning event"); | |
logger.Error("This is an error", 1000 ); | |
logger.Error(new Exception("something wicked this way comes")); | |
var tenantId = Guid.NewGuid(); | |
logger.Error(string.Format("Failed to connect to AD tenant {0}", tenantId), | |
new Exception("THis is really some specific connection/AD exception")); | |
packageSource.Debug("This is from package loader"); | |
Console.ReadKey(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment