Skip to content

Commit

Permalink
Added 2 components :
Browse files Browse the repository at this point in the history
- SuperMassive.Logging : A logging library
- SuperMassive.Logging.AzureTable : Logging library for Azure table

Signed-off-by: PulsarBlow <pulsarblow@gmail.com>
  • Loading branch information
PulsarBlow committed Mar 2, 2015
1 parent 46d4697 commit bf668cf
Show file tree
Hide file tree
Showing 32 changed files with 5,303 additions and 1 deletion.
66 changes: 66 additions & 0 deletions Source/SuperMassive.Logging.AzureTable/ApplicationLog.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using Microsoft.WindowsAzure.Storage.Table;

namespace SuperMassive.Logging.AzureTable
{
public class ApplicationLogEntity : TableEntity
{
#region Properties
/// <summary>
/// ApplicationName
/// </summary>
public string ApplicationName { get; set; }
/// <summary>
/// EventId
/// </summary>
public int EventId { get; set; }
/// <summary>
/// Priority
/// </summary>
public int Priority { get; set; }
/// <summary>
/// Severity
/// </summary>
public string Severity { get; set; }
/// <summary>
/// Category
/// </summary>
public string Category { get; set; }
/// <summary>
/// Title
/// </summary>
public string Title { get; set; }
/// <summary>
/// MachineName
/// </summary>
public string MachineName { get; set; }
/// <summary>
/// AppDomainName
/// </summary>
public string AppDomainName { get; set; }
/// <summary>
/// ProcessId
/// </summary>
public string ProcessId { get; set; }
/// <summary>
/// ProcessName
/// </summary>
public string ProcessName { get; set; }
/// <summary>
/// ThreadName
/// </summary>
public string ThreadName { get; set; }
/// <summary>
/// Win32ThreadId
/// </summary>
public string Win32ThreadId { get; set; }
/// <summary>
/// Message
/// </summary>
public string Message { get; set; }
/// <summary>
/// FormattedMessage
/// </summary>
public string FormattedMessage { get; set; }
#endregion
}
}
122 changes: 122 additions & 0 deletions Source/SuperMassive.Logging.AzureTable/ApplicationLogManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
using SuperMassive.Logging.Formatters;
using System;
using System.Diagnostics;
using System.Globalization;

namespace SuperMassive.Logging.AzureTable
{
/// <summary>
/// This class is responsible for managing <see cref="ApplicationLogEntity"/> in the system.
/// </summary>
public static class ApplicationLogEntityManager
{
/// <summary>
/// Returns a formatted row key
/// </summary>
/// <param name="sortOrder"></param>
/// <returns></returns>
public static string CreateRowKey(SortOrder sortOrder = SortOrder.Descending)
{
return CreateRowKey(Guid.NewGuid(), DateTime.UtcNow, sortOrder);
}

/// <summary>
/// Returns a formatted row key
/// </summary>
/// <param name="guid"></param>
/// <returns></returns>
public static string CreateRowKey(Guid guid, SortOrder sortOrder = SortOrder.Descending)
{
return CreateRowKey(guid, DateTime.UtcNow, sortOrder);
}

/// <summary>
/// Returns a formatted row key
/// </summary>
/// <param name="guid"></param>
/// <param name="date"></param>
/// <param name="sortOrder"></param>
/// <returns></returns>
public static string CreateRowKey(Guid guid, DateTime date, SortOrder sortOrder)
{
long ticks = date.Ticks;
if (sortOrder == SortOrder.Descending)
ticks = DateTime.MaxValue.Ticks - date.Ticks;

return String.Format(CultureInfo.InvariantCulture, "{0:D19}_{1}", ticks, guid.ToString());
}

/// <summary>
/// Returns a formatted partition key
/// </summary>
/// <param name="applicationName"></param>
/// <param name="timeStamp"></param>
/// <returns></returns>
public static string CreatePartitionKey(string applicationName, DateTimeOffset timeStamp)
{
Guard.ArgumentNotNullOrWhiteSpace(applicationName, "applicationName");

return String.Format(CultureInfo.InvariantCulture, "{0}_{1}",
applicationName.Trim(),
timeStamp.ToString("yyyyMM"));
}

/// <summary>
/// Creates a full populated new <see cref="ApplicationLogEntity"/> from a given <see cref="LogEntry"/>
/// </summary>
/// <param name="logEntry"></param>
/// <param name="formatter"></param>
/// <returns></returns>
public static ApplicationLogEntity CreateFromLogEntry(LogEntry logEntry, ILogFormatter formatter)
{
Guard.ArgumentNotNull(logEntry, "logEntry");
ApplicationLogEntity entity = new ApplicationLogEntity
{
AppDomainName = logEntry.AppDomainName,
ApplicationName = logEntry.ApplicationName,
Category = logEntry.Categories.Join(","),
EventId = logEntry.EventId,
MachineName = Environment.MachineName,
Message = logEntry.Message,
Priority = logEntry.Priority,
ProcessId = logEntry.ProcessId,
ProcessName = logEntry.ProcessName,
Severity = logEntry.Severity.ToString(),
ThreadName = logEntry.ManagedThreadName,
Title = logEntry.Title,
Win32ThreadId = logEntry.Win32ThreadId
};

entity.Timestamp = new DateTimeOffset(logEntry.TimeStamp, TimeSpan.Zero);
entity.PartitionKey = CreatePartitionKey(entity.ApplicationName, entity.Timestamp);
entity.RowKey = ApplicationLogEntityManager.CreateRowKey(SortOrder.Descending);
entity.FormattedMessage = formatter != null ? formatter.Format(logEntry) : logEntry.Message;

return entity;
}

public static ApplicationLogEntity Create(string title, string rawMessage, string formattedMessage, int eventId, string applicationName = "Default", TraceEventType severity = TraceEventType.Error, string category = "General", int priority = 1, string processId = null, string processName = null, string threadName = null, string wind32ThreadId = null)
{
return new ApplicationLogEntity
{
AppDomainName = "Unknown AppDomain",
ApplicationName = applicationName,
Category = category,
EventId = eventId,
FormattedMessage = formattedMessage,
MachineName = Environment.MachineName,
Message = rawMessage,
PartitionKey = CreatePartitionKey(applicationName, DateTimeOffset.UtcNow),
Priority = priority,
ProcessId = processId,
ProcessName = processName,
RowKey = ApplicationLogEntityManager.CreateRowKey(SortOrder.Descending),
Severity = severity.ToString(),
ThreadName = threadName,
Timestamp = DateTimeOffset.UtcNow,
Title = title,
Win32ThreadId = wind32ThreadId
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Table;
using SuperMassive.Logging.Formatters;
using SuperMassive.Logging.TraceListeners;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SuperMassive.Logging.AzureTable
{
/// <summary>
/// A <see cref="System.Diagnostics.TraceListener"/> that writes to a Cloud Storage,
/// formatting the output with an <see cref="ILogFormatter"/>.
/// </summary>
public class FormattedAzureTableTraceListener : FormattedTraceListenerBase
{
/// <summary>
/// The name of the Azure Table where the log will be saved
/// </summary>
public const string DefaultAzureTableName = "ApplicationLogs";

/// <summary>
/// The cloud storage connection string where to save the log to.
/// </summary>
protected string _cloudStorageConnectionString;

/// <summary>
/// The azure table storage name where to store traces
/// </summary>
protected string _azureTableName;

/// <summary>
/// Creates a new instance of the <see cref="FormattedAzureTableTraceListener"/>
/// </summary>
/// <param name="cloudStorageConnectionString"></param>
public FormattedAzureTableTraceListener(string cloudStorageConnectionString)
: this(cloudStorageConnectionString, DefaultAzureTableName, null)
{ }

/// <summary>
/// Creates a new instance of the <see cref="FormattedAzureTableTraceListener"/>
/// </summary>
/// <param name="cloudStorageConnectionString"></param>
/// <param name="azureTableName"></param>
public FormattedAzureTableTraceListener(string cloudStorageConnectionString, string azureTableName)
: this(cloudStorageConnectionString, azureTableName, null)
{ }

/// <summary>
/// Creates a new instance of the <see cref="FormattedAzureTableTraceListener"/>
/// </summary>
/// <param name="cloudStorageConnectionString"></param>
/// <param name="azureTaleName"></param>
/// <param name="formatter"></param>
public FormattedAzureTableTraceListener(string cloudStorageConnectionString, string azureTaleName, ILogFormatter formatter)
: base(formatter)
{
Guard.ArgumentNotNullOrWhiteSpace(cloudStorageConnectionString, "cloudStorageConnectionString");
Guard.ArgumentNotNullOrWhiteSpace(azureTaleName, "azureTableName");
_cloudStorageConnectionString = cloudStorageConnectionString;
_azureTableName = azureTaleName;
}

/// <summary>
/// Write a message
/// </summary>
/// <param name="message"></param>
public override void Write(string message)
{
LogEntry logEntry = CreateDefaultLogEntry(message);
ExecuteWriteLog(logEntry);
}

/// <summary>
/// Asynchronously write a message
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public async Task WriteAsync(string message)
{
LogEntry logEntry = CreateDefaultLogEntry(message);
await ExecuteWriteLogAsync(logEntry);
}

/// <summary>
/// WriteLine
/// </summary>
/// <param name="message"></param>
public override void WriteLine(string message)
{
Write(message);
}

/// <summary>
/// Asynchronous WriteLine
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public async Task WriteLineAsync(string message)
{
await WriteAsync(message);
}

/// <summary>
/// Trace data
/// </summary>
/// <param name="eventCache"></param>
/// <param name="source"></param>
/// <param name="eventType"></param>
/// <param name="id"></param>
/// <param name="data">Data to log. Can be a <see cref="String"/> or <see cref="LogEntry"/></param>
public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data)
{
if (this.Filter == null || this.Filter.ShouldTrace(eventCache, source, eventType, id, null, null, data, null))
{
if (data is LogEntry)
{
LogEntry logEntry = data as LogEntry;
ExecuteWriteLog(logEntry);
}
else if (data is String)
{
Write(data as String);
}
else
{
base.TraceData(eventCache, source, eventType, id, data);
}
}
}

/// <summary>
/// CloudStorage write implementation
/// </summary>
/// <param name="logEntry"></param>
protected void ExecuteWriteLog(LogEntry logEntry)
{
ApplicationLogEntity entity = ApplicationLogEntityManager.CreateFromLogEntry(logEntry, this.Formatter);
if (entity == null)
return;

CloudTable table = GetTableReference();
table.CreateIfNotExists();
TableOperation insertOperation = TableOperation.Insert(entity);
table.Execute(insertOperation);
}

/// <summary>
/// CloudStorage asynchronous write implementation
/// </summary>
/// <param name="logEntry"></param>
/// <returns></returns>
protected async Task ExecuteWriteLogAsync(LogEntry logEntry)
{
ApplicationLogEntity entity = ApplicationLogEntityManager.CreateFromLogEntry(logEntry, this.Formatter);
if (entity == null)
return;

CloudTable table = GetTableReference();
await table.CreateIfNotExistsAsync();
TableOperation insertOperation = TableOperation.Insert(entity);
await table.ExecuteAsync(insertOperation);
}

/// <summary>
/// Creates a default log entry
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
protected virtual LogEntry CreateDefaultLogEntry(string message)
{
return new LogEntry
{
Severity = System.Diagnostics.TraceEventType.Information,
TimeStamp = DateTime.UtcNow,
Message = message,
ApplicationName = "Undefined"
};
}

private CloudTable GetTableReference()
{
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(_cloudStorageConnectionString);
CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
return tableClient.GetTableReference(DefaultAzureTableName);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Reflection;
using System.Runtime.InteropServices;


[assembly: AssemblyTitle("SuperMassive.Logging.AzureTable")]
[assembly: AssemblyDescription("Logging to Azure Table Storage")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("7cc4c6d3-f6b2-418b-87eb-2f9c8c33e83c")]
Loading

0 comments on commit bf668cf

Please sign in to comment.