Skip to content

Instantly share code, notes, and snippets.

@timiles
Last active June 7, 2019 19:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save timiles/5373483 to your computer and use it in GitHub Desktop.
Save timiles/5373483 to your computer and use it in GitHub Desktop.
RequestLogger
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Text;
using System.Web;
using Microsoft.WindowsAzure.ServiceRuntime;
public static class RequestLogger
{
private static string _logsConnectionString;
public static void Init(string logsConnectionString)
{
_logsConnectionString = logsConnectionString;
using (var connection = new SqlConnection(_logsConnectionString))
using (var comm = connection.CreateCommand())
{
comm.CommandText = Sql.CreateIfNotExists;
comm.CommandType = CommandType.Text;
connection.Open();
comm.ExecuteNonQuery();
}
}
static readonly ConcurrentDictionary<string, DateTimeOffset> RequestStartTimes = new ConcurrentDictionary<string, DateTimeOffset>();
static readonly ConcurrentDictionary<string, List<string>> RequestComments = new ConcurrentDictionary<string, List<string>>();
static readonly ConcurrentDictionary<string, string> RequestBodies = new ConcurrentDictionary<string, string>();
public static void BeginRequest()
{
try
{
var httpContext = HttpContext.Current;
var requestId = CreateRequestId(httpContext);
RequestStartTimes.TryAdd(requestId, DateTimeOffset.UtcNow);
var body = new StreamReader(httpContext.Request.InputStream).ReadToEnd();
httpContext.Request.InputStream.Position = 0;
RequestBodies.TryAdd(requestId, body);
}
catch (Exception)
{
// TODO: log
}
}
private static string CreateRequestId(HttpContext httpContext)
{
var requestId = Guid.NewGuid().ToString();
httpContext.Items["RequestLogger.RequestId"] = requestId;
return requestId;
}
public static void LogComment(string comment)
{
try
{
var httpContext = HttpContext.Current;
var requestId = (string) httpContext.Items["RequestLogger.RequestId"] ?? CreateRequestId(httpContext);
if (!RequestComments.ContainsKey(requestId))
{
RequestComments.TryAdd(requestId, new List<string>());
}
RequestComments[requestId].Add(comment);
}
catch (Exception)
{
// TODO: log
}
}
public static void EndRequest()
{
try
{
var httpContext = HttpContext.Current;
var request = httpContext.Request;
var response = httpContext.Response;
var endedAt = DateTimeOffset.UtcNow;
var requestId = (string)httpContext.Items["RequestLogger.RequestId"];
DateTimeOffset startedAt = DateTimeOffset.MinValue;
string body = null;
string comments = null;
if (requestId != null)
{
if (RequestStartTimes.ContainsKey(requestId))
{
RequestStartTimes.TryRemove(requestId, out startedAt);
}
if (RequestBodies.ContainsKey(requestId))
{
RequestBodies.TryRemove(requestId, out body);
}
if (RequestComments.ContainsKey(requestId))
{
List<string> commentsList;
RequestComments.TryRemove(requestId, out commentsList);
comments = string.Join(";", commentsList);
}
}
string authorizationHeader = null;
var headers = new StringBuilder();
foreach (string header in request.Headers)
{
if (header == "Authorization")
{
authorizationHeader = request.Headers[header];
}
else
{
headers.AppendFormat("{0}: {1}", header, request.Headers[header]);
headers.AppendLine();
}
}
InsertLog(request.HttpMethod, request.Url.ToString(), authorizationHeader, headers.ToString(), body,
startedAt, endedAt, response.StatusCode, comments);
}
catch (Exception)
{
// TODO: log
}
}
private static void InsertLog(string httpMethod, string url, string authorizationHeader, string otherHeaders, string body,
DateTimeOffset? startedAt, DateTimeOffset endedAt, int responseCode, string comments)
{
using (var connection = new SqlConnection(_logsConnectionString))
using (var comm = connection.CreateCommand())
{
comm.CommandText = Sql.InsertLog;
comm.CommandType = CommandType.Text;
comm.Parameters.Add("@InstanceId", SqlDbType.NVarChar).Value = RoleEnvironment.IsAvailable ? RoleEnvironment.CurrentRoleInstance.Id.Truncate(100) : "(local)";
comm.Parameters.Add("@Method", SqlDbType.NVarChar).Value = httpMethod.Truncate(10);
comm.Parameters.Add("@Url", SqlDbType.NVarChar).Value = url;
comm.Parameters.Add("@AuthorizationHeader", SqlDbType.NVarChar).Value =
(object)authorizationHeader ?? DBNull.Value;
comm.Parameters.Add("@OtherHeaders", SqlDbType.NVarChar).Value = otherHeaders;
comm.Parameters.Add("@Body", SqlDbType.NVarChar).Value = (object)body ?? DBNull.Value;
comm.Parameters.Add("@StartedAt", SqlDbType.DateTimeOffset).Value = (object)startedAt ?? DBNull.Value;
comm.Parameters.Add("@EndedAt", SqlDbType.DateTimeOffset).Value = endedAt;
comm.Parameters.Add("@DurationMs", SqlDbType.Int).Value =
startedAt.HasValue ? (object) (endedAt - startedAt.Value).TotalMilliseconds : DBNull.Value;
comm.Parameters.Add("@ResponseCode", SqlDbType.Int).Value = responseCode;
comm.Parameters.Add("@Comments", SqlDbType.NVarChar).Value = (object)comments ?? DBNull.Value;
connection.Open();
comm.ExecuteNonQuery();
}
}
public static class Sql
{
public const string CreateIfNotExists = @"
if not exists (select * from sys.tables where name = 'RequestLogs2')
begin
create table RequestLogs2 (
Id bigint not null identity(1,1) primary key clustered,
InstanceId nvarchar(100) not null,
Method nvarchar(10) null,
Url nvarchar(max) null,
AuthorizationHeader nvarchar(max) null,
OtherHeaders nvarchar(max) null,
Body nvarchar(max) null,
StartedAt datetime null,
EndedAt datetime null,
DurationMs int null,
ResponseCode int null,
Comments nvarchar(max) null
)
end";
public const string InsertLog = @"
insert into RequestLogs2 (InstanceId, Method, Url, AuthorizationHeader, OtherHeaders, Body, StartedAt, EndedAt, DurationMs, ResponseCode, Comments)
values (@InstanceId, @Method, @Url, @AuthorizationHeader, @OtherHeaders, @Body, @StartedAt, @EndedAt, @DurationMs, @ResponseCode, @Comments)";
}
public static string Truncate(this string s, int maxLength)
{
if (s == null)
return null;
if (s.Length < maxLength)
return s;
return s.Substring(0, maxLength);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment