Created
August 15, 2013 17:50
-
-
Save thefringeninja/6242976 to your computer and use it in GitHub Desktop.
Profiling RavenDB with Nancy
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
public class RavenProfilerModule : NancyModule | |
{ | |
private readonly IEnumerable<IDocumentStore> documentStores; | |
public RavenProfilerModule(IEnumerable<IDocumentStore> documentStores) | |
: base("/_raven-profiler") | |
{ | |
this.documentStores = documentStores; | |
Get["/"] = p => | |
{ | |
var sessions = this.Bind<RequestedSessions>(); | |
var viewModel = GetProfilingInformation(sessions.SessionId); | |
return Negotiate.WithModel(viewModel); | |
}; | |
} | |
private RavenProfiledSessionViewModel GetProfilingInformation(IEnumerable<Guid> sessionIds) | |
{ | |
var profilingInformations = from documentStore in documentStores.OfType<DocumentStore>() | |
from sessionId in sessionIds | |
select documentStore.GetProfilingInformationFor(sessionId); | |
return new RavenProfiledSessionViewModel(profilingInformations); | |
} | |
} | |
public class RequestedSessions | |
{ | |
public Guid[] SessionId { get; set; } | |
} |
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
public static class RavenProfiling | |
{ | |
private const string ContextualSessionStorageKey = "###RavenProfilerSessions###"; | |
private static readonly | |
ConcurrentDictionary | |
<NancyContext, Tuple<Action<InMemoryDocumentSessionOperations>, Action<ProfilingInformation>>> | |
Operations = | |
new ConcurrentDictionary | |
<NancyContext, Tuple<Action<InMemoryDocumentSessionOperations>, Action<ProfilingInformation>>>(); | |
public static void InitializeFor(IDocumentStore documentStore, IPipelines pipelines) | |
{ | |
var store = documentStore as DocumentStore; | |
if (store == null) | |
throw new ArgumentException("Must be a Raven.Client.DocumentStore", "documentStore"); | |
InitializeFor(store, pipelines); | |
} | |
private static void InitializeFor(DocumentStore documentStore, IPipelines pipelines) | |
{ | |
documentStore.Conventions.DisableProfiling = false; | |
documentStore.InitializeProfiling(); | |
pipelines.BeforeRequest.AddItemToStartOfPipeline( | |
context => | |
{ | |
if (documentStore.WasDisposed) | |
return null; | |
Action<InMemoryDocumentSessionOperations> onSessionCreated = | |
operation => SessionCreated(operation, context); | |
Action<ProfilingInformation> onInformationCreated = | |
information => InformationCreated(information, context); | |
if (false == Operations.TryAdd(context, Tuple.Create(onSessionCreated, onInformationCreated))) | |
return null; | |
documentStore.SessionCreatedInternal += onSessionCreated; | |
return null; | |
}); | |
pipelines.AfterRequest.AddItemToEndOfPipeline( | |
context => | |
{ | |
if (documentStore.WasDisposed) | |
return; | |
//do something here to get the session ids into the rendered view. | |
// I use cassette so I will add an inline script with a hook. | |
//var bundesHelper = context.Items[BundlesHelper.BUNDLES_HELPER] as IBundlesHelper; | |
// if (bundesHelper != null) | |
// TrackSessionsInBundles(bundesHelper, context); | |
Tuple<Action<InMemoryDocumentSessionOperations>, Action<ProfilingInformation>> operation; | |
if (false == Operations.TryGetValue(context, out operation)) | |
return; | |
documentStore.SessionCreatedInternal -= operation.Item1; | |
ProfilingInformation.OnContextCreated -= operation.Item2; | |
}); | |
} | |
private static void InformationCreated(ProfilingInformation information, NancyContext context) | |
{ | |
// add additional context | |
} | |
private static void SessionCreated(InMemoryDocumentSessionOperations operations, NancyContext context) | |
{ | |
GetContextualSessionStorage(context).Add(operations.Id); | |
} | |
private static List<Guid> GetContextualSessionStorage(NancyContext context) | |
{ | |
object trackedSessionsThing; | |
List<Guid> trackedSessions; | |
if (context.Items.TryGetValue(ContextualSessionStorageKey, out trackedSessionsThing) | |
&& (trackedSessions = trackedSessionsThing as List<Guid>) != null) | |
return trackedSessions; | |
trackedSessions = new List<Guid>(); | |
context.Items[ContextualSessionStorageKey] = trackedSessions; | |
return trackedSessions; | |
} | |
/* | |
public static void TrackSessionsInBundles(IBundlesHelper bundles, NancyContext context) | |
{ | |
const string script = @" | |
(function(document, window){{ | |
var profiler = new window.RavenProfiler('{0}', document, window); | |
profiler.forSession({1}); | |
}})(document, window); | |
"; | |
var trackedSessions = GetContextualSessionStorage(context); | |
if (false == trackedSessions.Any()) | |
return; | |
var sessionIdsInJson = "[" + String.Join( | |
",", | |
trackedSessions.Select(sessionId => "'" + sessionId + "'")) + "]"; | |
bundles.AddInlineScript( | |
String.Format(script, context.ToFullPath("~/_raven-profiler"), sessionIdsInJson), | |
references: new[] {"~/scripts/features/raven-profiler"}); | |
}*/ | |
} |
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
public class RavenRemoteCallViewModel | |
{ | |
private readonly string method; | |
private readonly Uri url; | |
public RavenRemoteCallViewModel(RequestResultArgs model) | |
{ | |
At = model.At; | |
Duration = model.DurationMilliseconds + " ms"; | |
method = model.Method; | |
Status = model.HttpResult + " " + (((HttpStatusCode) model.HttpResult).ToString().Titleize()); | |
url = new Uri("http://ravendb" + model.Url); | |
} | |
public string Status { get; private set; } | |
public string Request | |
{ | |
get { return method + " " + Endpoint; } | |
} | |
public string Endpoint | |
{ | |
get | |
{ | |
// should get rid of /databases/YourDatabaseName | |
return "/" + String.Join("/", url.AbsolutePath.Split('/').Skip(3)); | |
} | |
} | |
public IEnumerable<string> Query | |
{ | |
get | |
{ | |
return url.Query.Length < 1 | |
? new string[0] | |
: url.Query.Substring(1).Split('&') | |
.Select(HttpUtility.UrlDecode) | |
.Select(HttpUtility.UrlDecode); | |
} | |
} | |
public DateTime At { get; private set; } | |
public string Duration { get; private set; } | |
} | |
public class RavenProfiledSessionViewModel : IEnumerable<RavenRemoteCallViewModel> | |
{ | |
private readonly IEnumerable<RavenRemoteCallViewModel> items; | |
public RavenProfiledSessionViewModel(IEnumerable<ProfilingInformation> profilingInformations) | |
{ | |
items = from profilingInformation in profilingInformations | |
where profilingInformation != null | |
from result in profilingInformation.Requests | |
select new RavenRemoteCallViewModel(result); | |
} | |
#region IEnumerable<RavenRemoteCallViewModel> Members | |
public IEnumerator<RavenRemoteCallViewModel> GetEnumerator() | |
{ | |
return items.GetEnumerator(); | |
} | |
IEnumerator IEnumerable.GetEnumerator() | |
{ | |
return GetEnumerator(); | |
} | |
#endregion | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment