1: using System;
2: using System.Collections.Generic;
3: using System.Data.Services.Client;
4: using System.Text;
5: using System.Diagnostics;
6: using Microsoft.WindowsAzure.Storage;
7: using Microsoft.WindowsAzure.Storage.Table;
8:
9: namespace ToDoCommon
10: {
11: public class TableTraceListener : TraceListener
12: {
13: #region Fields
14: readonly string _connectionString;
15: readonly string _diagnosticsTable = "CustomLogTable";
16:
17: [ThreadStatic]
18: static StringBuilder _messageBuffer;
19:
20: readonly object _initializationSection = new object();
21: bool _isInitialized;
22:
23: CloudTableClient _tableStorage;
24: readonly object _traceLogAccess = new object();
25: readonly List<LogEntry> _traceLog = new List<LogEntry>();
26: #endregion
27:
28: #region Constructors
29:
30: public TableTraceListener()
31: : this("Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString")
32: {
33: }
34:
35: public TableTraceListener(string connectionString)
36: : base("TableTraceListener")
37: {
38: _connectionString = connectionString;
39: }
40:
41: public TableTraceListener(string connectionString, string tableName)
42: : base("TableTraceListener")
43: {
44: _connectionString = connectionString;
45: _diagnosticsTable = tableName;
46: }
47: #endregion
48:
49: #region Methods
50:
51: /// <summary>
52: /// Flushes the entries to the storage table
53: /// </summary>
54: public override void Flush()
55: {
56: if (!_isInitialized)
57: {
58: lock (_initializationSection)
59: {
60: if (!_isInitialized)
61: {
62: Initialize();
63: }
64: }
65: }
66:
67: var context = _tableStorage.GetTableServiceContext();
68: context.MergeOption = MergeOption.AppendOnly;
69: lock (_traceLogAccess)
70: {
71: _traceLog.ForEach(entry => context.AddObject(_diagnosticsTable, entry));
72: _traceLog.Clear();
73: }
74:
75: if (context.Entities.Count > 0)
76: {
77: context.BeginSaveChangesWithRetries(SaveChangesOptions.None,
78: (ar) => context.EndSaveChangesWithRetries(ar), null);
79: }
80: }
81: /// <summary>
82: /// Creates the storage table object
83: /// </summary>
84: private void Initialize()
85: {
86: var account = CloudStorageAccount.Parse(_connectionString);
87: _tableStorage = account.CreateCloudTableClient();
88: _tableStorage.GetTableReference(_diagnosticsTable).CreateIfNotExists();
89: _isInitialized = true;
90: }
91:
92: public override bool IsThreadSafe
93: {
94: get
95: {
96: return true;
97: }
98: }
99:
100: #region Trace and Write Methods
101: /// <summary>
102: /// Writes the message to a string buffer
103: /// </summary>
104: /// <param name="message">the Message</param>
105: public override void Write(string message)
106: {
107: if (_messageBuffer == null)
108: _messageBuffer = new StringBuilder();
109:
110: _messageBuffer.Append(message);
111: }
112:
113: /// <summary>
114: /// Writes the message with a line breaker to a string buffer
115: /// </summary>
116: /// <param name="message"></param>
117: public override void WriteLine(string message)
118: {
119: if (_messageBuffer == null)
120: _messageBuffer = new StringBuilder();
121:
122: _messageBuffer.AppendLine(message);
123: }
124: /// <summary>
125: /// Appends the trace information and message
126: /// </summary>
127: /// <param name="eventCache">the Event Cache</param>
128: /// <param name="source">the Source</param>
129: /// <param name="eventType">the Event Type</param>
130: /// <param name="id">the Id</param>
131: /// <param name="message">the Message</param>
132: public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string message)
133: {
134: base.TraceEvent(eventCache, source, eventType, id, message);
135: AppendEntry(id, eventType, eventCache);
136: }
137:
138: /// <summary>
139: /// Adds the trace information to a collection of LogEntry objects
140: /// </summary>
141: /// <param name="id">the Id</param>
142: /// <param name="eventType">the Event Type</param>
143: /// <param name="eventCache">the EventCache</param>
144: private void AppendEntry(int id, TraceEventType eventType, TraceEventCache eventCache)
145: {
146: if (_messageBuffer == null)
147: _messageBuffer = new StringBuilder();
148:
149: var message = _messageBuffer.ToString();
150: _messageBuffer.Length = 0;
151:
152: if (message.EndsWith(Environment.NewLine))
153: message = message.Substring(0, message.Length - Environment.NewLine.Length);
154:
155: if (message.Length == 0)
156: return;
157:
158: var entry = new LogEntry()
159: {
160: PartitionKey = string.Format("{0:D10}", eventCache.Timestamp >> 30),
161: RowKey = string.Format("{0:D19}", eventCache.Timestamp),
162: EventTickCount = eventCache.Timestamp,
163: Level = (int)eventType,
164: EventId = id,
165: Pid = eventCache.ProcessId,
166: Tid = eventCache.ThreadId,
167: Message = message
168: };
169:
170: lock (_traceLogAccess)
171: _traceLog.Add(entry);
172: }
173:
174: #endregion
175:
176: #endregion
177: }
178: }