Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Local object cache viewer #36

Merged
merged 5 commits into from
Mar 14, 2019
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions DeveloperTools/Controllers/LocalObjectCacheController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Web.Mvc;
using System.Web.Routing;
using DeveloperTools.Core;
using DeveloperTools.Models;
using EPiServer.Core;
using EPiServer.Framework.Cache;

namespace DeveloperTools.Controllers
{
public class LocalObjectCacheController : DeveloperToolsController
{
private readonly ISynchronizedObjectInstanceCache _cache;

public LocalObjectCacheController(ISynchronizedObjectInstanceCache cache)
{
_cache = cache;
}

public ActionResult Index(string FilteredBy, bool os = false)
{
return View(PrepareViewModel(FilteredBy, os));
}

[HttpParamAction]
public ActionResult RemoveLocalCache(string[] cacheKeys, bool os)
{
if(cacheKeys != null)
{
foreach (string key in cacheKeys)
{
_cache.RemoveLocal(key);
}
}

return RedirectToAction("Index", new RouteValueDictionary(new { os }));
}

[HttpParamAction]
public ActionResult RemoveLocalRemoteCache(string[] cacheKeys, bool os)
{
if(cacheKeys != null)
{
foreach (string key in cacheKeys)
{
_cache.RemoveLocal(key);
_cache.RemoveRemote(key);
}
}

return RedirectToAction("Index", new RouteValueDictionary(new { os }));
}

[HttpParamAction]
public ActionResult ViewObjectSize()
{
return RedirectToAction("Index", new RouteValueDictionary(new { os = true }));
}

private LocalObjectCache PrepareViewModel(string FilteredBy, bool viewObjectSize)
{
var model = new LocalObjectCache();

var cachedEntries = HttpContext.Cache.Cast<DictionaryEntry>();

switch (FilteredBy)
{
case "pages":
model.CachedItems = ConvertToListItem(cachedEntries.Where(item => item.Value is PageData), viewObjectSize);
break;
case "content":
model.CachedItems = ConvertToListItem(cachedEntries.Where(item => item.Value is IContent), viewObjectSize);
break;
default:
model.CachedItems = ConvertToListItem(cachedEntries, viewObjectSize);
break;
}

model.FilteredBy = FilteredBy;
model.Choices = new[]
{
new SelectListItem { Text = "All Cached Objects", Value = "all" },
new SelectListItem { Text = "Any Content", Value = "content" },
new SelectListItem { Text = "Pages Only", Value = "pages" }
};

model.ViewObjectSize = viewObjectSize;
return model;
}

private IEnumerable<LocalObjectCacheItem> ConvertToListItem(IEnumerable<DictionaryEntry> cachedEntries, bool viewObjectSize) =>
cachedEntries.Select(e => new LocalObjectCacheItem
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think just adding a Take(10,000) here would protect against issues with really large caches until server side paging is in place. This code otherwise have the potential of running a server out of memory.

{
Key = e.Key.ToString(),
Value = e.Value,
Size = viewObjectSize ? GetObjectSize(e.Value) : 0
}).ToList();

private static long GetObjectSize(object obj)
{
if(obj == null)
return 0;

try
{
using (Stream s = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(s, obj);

return s.Length;
}
}
catch (Exception)
{
return -1;
}
}
}
}
7 changes: 6 additions & 1 deletion DeveloperTools/Core/DeveloperMenuProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ public class DeveloperMenuProvider : IMenuProvider
const string ModuleDependenciesTitle = "Module Dependencies";
const string ModuleDependenciesPath = "global/DeveloperTools/ModuleDependencies";

const string LocalObjectCacheTitle = "Local Object Cache";
const string LocalObjectCachePath = "global/DeveloperTools/LocalObjectCache";

public IEnumerable<MenuItem> GetMenuItems()
{
// Create the top menu section
Expand All @@ -69,6 +72,7 @@ public IEnumerable<MenuItem> GetMenuItems()
var routes = CreateUrlMenuItem(RoutesTitle, RoutesPath, Paths.ToResource(ModuleName, "Routes"));
var viewLocations = CreateUrlMenuItem(ViewLocationsTitle, ViewLocationsPath, Paths.ToResource(ModuleName, "ViewEngineLocations"));
var moduleDependencies = CreateUrlMenuItem(ModuleDependenciesTitle, ModuleDependenciesPath, Paths.ToResource(ModuleName, "ModuleDependencies"));
var localobjectcache = CreateUrlMenuItem(LocalObjectCacheTitle, LocalObjectCachePath, Paths.ToResource(ModuleName, "LocalObjectCache"));

return new MenuItem[]
{
Expand All @@ -84,7 +88,8 @@ public IEnumerable<MenuItem> GetMenuItems()
remoteEventViewer,
routes,
viewLocations,
moduleDependencies
moduleDependencies,
localobjectcache
};
}

Expand Down
21 changes: 21 additions & 0 deletions DeveloperTools/Core/HttpParamActionAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;
using System.Reflection;
using System.Web.Mvc;

namespace DeveloperTools.Core
{
public class HttpParamActionAttribute : ActionNameSelectorAttribute
{
public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
{
if(actionName.Equals(methodInfo.Name, StringComparison.InvariantCultureIgnoreCase))
return true;

if(!actionName.Equals("Action", StringComparison.InvariantCultureIgnoreCase))
return false;

var request = controllerContext.RequestContext.HttpContext.Request;
return request[methodInfo.Name] != null;
}
}
}
4 changes: 4 additions & 0 deletions DeveloperTools/DeveloperTools.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Controllers\ContentTypeAnalyzerController.cs" />
<Compile Include="Controllers\LocalObjectCacheController.cs" />
<Compile Include="Controllers\RoutesController.cs" />
<Compile Include="Controllers\RemoteEventController.cs" />
<Compile Include="Controllers\MemoryDumpController.cs" />
Expand All @@ -219,9 +220,11 @@
<Compile Include="Controllers\ViewEngineLocationsController.cs" />
<Compile Include="Core\DeveloperMenuProvider.cs" />
<Compile Include="Core\EnumerableExtensions.cs" />
<Compile Include="Core\HttpParamActionAttribute.cs" />
<Compile Include="Models\AssembliesModel.cs" />
<Compile Include="Models\ContentTypeAnalyzerModel.cs" />
<Compile Include="Models\ExtractedModuleInfo.cs" />
<Compile Include="Models\LocalObjectCache.cs" />
<Compile Include="Models\ModuleDependency.cs" />
<Compile Include="Models\ModuleDependencyViewModel.cs" />
<Compile Include="Models\ModuleInfo.cs" />
Expand All @@ -242,6 +245,7 @@
<None Include="modules\_protected\EPiServer.DeveloperTools\Views\ContentTypeAnalyzer\Index.aspx">
<SubType>ASPXCodeBehind</SubType>
</None>
<None Include="modules\_protected\EPiServer.DeveloperTools\Views\LocalObjectCache\Index.aspx" />
<None Include="modules\_protected\EPiServer.DeveloperTools\Views\Routes\Index.aspx" />
<None Include="modules\_protected\EPiServer.DeveloperTools\Views\RemoteEvent\Index.aspx">
<SubType>ASPXCodeBehind</SubType>
Expand Down
Binary file modified DeveloperTools/EPiServer.DeveloperTools.zip
Binary file not shown.
25 changes: 25 additions & 0 deletions DeveloperTools/Models/LocalObjectCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Collections.Generic;
using System.Web.Mvc;

namespace DeveloperTools.Models
{
public class LocalObjectCache
{
public IEnumerable<LocalObjectCacheItem> CachedItems { get; set; }

public string FilteredBy { get; set; }

public IEnumerable<SelectListItem> Choices { get; set; }

public bool ViewObjectSize { get; set; }
}

public class LocalObjectCacheItem
{
public string Key { get; set; }

public object Value { get; set; }

public long Size { get; set; }
}
}
6 changes: 3 additions & 3 deletions DeveloperTools/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("15a30f61-0a54-4a91-b98f-5b30eb22b6f5")]
[assembly: AssemblyVersion("3.3.3.0")]
[assembly: AssemblyFileVersion("3.3.3.0")]
[assembly: AssemblyInformationalVersion("3.3.3")]
[assembly: AssemblyVersion("3.4.0.0")]
[assembly: AssemblyFileVersion("3.4.0.0")]
[assembly: AssemblyInformationalVersion("3.4.0")]
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<DeveloperTools.Models.LocalObjectCache>" MasterPageFile="../Shared/DeveloperTools.Master" %>

<asp:Content ID="Styles" runat="server" ContentPlaceHolderID="HeaderStyles">
<style type="text/css">
.table-column-width {
width: 30%;
}
.stripe tbody tr:nth-child(even) {
background-color: #f0f2f2;
}
</style>
</asp:Content>

<asp:Content ID="Content" runat="server" ContentPlaceHolderID="MainRegion">
<link rel="stylesheet" type="text/css" href="//ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.0/css/jquery.dataTables.css" />
<script type="text/javascript" language="javascript" src="//ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.min.js"></script>
<script src="//ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.0/jquery.dataTables.min.js" type="text/javascript"></script>

<div class="epi-contentContainer epi-padding">
<div class="epi-contentArea">
<h1>Local Object Cache</h1>
<p class="EP-systemInfo">This tool shows all of the current items in the local object cache, and allows the deletion of one or more cached items.</p>
</div>

<div class="epi-contentArea epi-formArea">
<%using (Html.BeginForm("Index", "LocalObjectCache", FormMethod.Post))
{ %>
<%= Html.AntiForgeryToken() %>

<table class="table">
<tr>
<td>Filter By</td>
<td><%= Html.DropDownListFor(m => m.FilteredBy, Model.Choices) %></td>
<td>
<span class="epi-cmsButton">
<input class="epi-cmsButton-text epi-cmsButton-tools epi-cmsButton-Refresh" type="submit" name="filter" id="filter" value="Filter" onmouseover="EPi.ToolButton.MouseDownHandler(this)" onmouseout="EPi.ToolButton.ResetMouseDownHandler(this)" />
</span>
</td>
</tr>
</table>
<% } %>

<% using (Html.BeginForm("Action", "LocalObjectCache", FormMethod.Post))
{ %>
<div class="epi-contentArea">
<p class="EP-systemInfo">Total count of cached items: <%= Model.CachedItems.Count() %></p>
</div>
<div class="epi-buttonDefault">
<span class="epi-cmsButton">
<input class="epi-cmsButton-text epi-cmsButton-tools epi-cmsButton-Delete" type="submit" name="RemoveLocalCache" id="RemoveLocalCache" value="Remove Local Cache Items" onmouseover="EPi.ToolButton.MouseDownHandler(this)" onmouseout="EPi.ToolButton.ResetMouseDownHandler(this)" />
</span>
<span class="epi-cmsButton">
<input class="epi-cmsButton-text epi-cmsButton-tools epi-cmsButton-Delete" type="submit" name="removeLocalRemoteCache" id="removeLocalRemoteCache" value="Remove Local and Remote Cache Items" onmouseover="EPi.ToolButton.MouseDownHandler(this)" onmouseout="EPi.ToolButton.ResetMouseDownHandler(this)" />
</span>
<span class="epi-cmsButton">
<input class="epi-cmsButton-text epi-cmsButton-tools epi-cmsButton-ViewMode" type="submit" name="ViewObjectSize" id="ViewObjectSize" value="View Object Size" onmouseover="EPi.ToolButton.MouseDownHandler(this)" onmouseout="EPi.ToolButton.ResetMouseDownHandler(this)" />
</span>
</div>

<table class="table table-condensed table-bordered table-condensed stripe">
<thead>
<tr>
<th><input type="checkbox" id="clearAll" name="clearAll" onClick="toggle(this)" value="true" /></th>
<th class="table-column-width">Key</th>
<th class="table-column-width">Type</th>
<th>Size</th>
</tr>
</thead>
<tbody>
<% foreach (var item in Model.CachedItems)
{ %>
<tr>
<td class="center"><input type="checkbox" id="<%= item.Key %>" name="cacheKeys" value="<%= item.Key %>" /></td>
<td><%= item.Key %></td>
<td><%= item.Value.GetType() %></td>
<td><%= item.Size %></td>
</tr>
<% } %>
</tbody>
</table>

<div class="epi-buttonDefault">
<span class="epi-cmsButton">
<input class="epi-cmsButton-text epi-cmsButton-tools epi-cmsButton-Delete" type="submit" name="RemoveLocalCache" id="RemoveLocalCacheBottom" value="Remove Local Cache Items" onmouseover="EPi.ToolButton.MouseDownHandler(this)" onmouseout="EPi.ToolButton.ResetMouseDownHandler(this)" />
</span>
<span class="epi-cmsButton">
<input class="epi-cmsButton-text epi-cmsButton-tools epi-cmsButton-Delete" type="submit" name="removeLocalRemoteCache" id="removeLocalRemoteCacheBottom" value="Remove Local and Remote Cache Items" onmouseover="EPi.ToolButton.MouseDownHandler(this)" onmouseout="EPi.ToolButton.ResetMouseDownHandler(this)" />
</span>
<span class="epi-cmsButton">
<input class="epi-cmsButton-text epi-cmsButton-tools epi-cmsButton-ViewMode" type="submit" name="ViewObjectSize" id="ViewObjectSize" value="View Object Size" onmouseover="EPi.ToolButton.MouseDownHandler(this)" onmouseout="EPi.ToolButton.ResetMouseDownHandler(this)" />
</span>
</div>

<input type="hidden" id="os" name="os" value="<%= Model.ViewObjectSize %>"/>
<% } %>
</div>
</div>

<script language="JavaScript">
function toggle(source) {
checkboxes = document.getElementsByName('cacheKeys');
for (var i = 0, n = checkboxes.length; i < n; i++) {
checkboxes[i].checked = source.checked;
}
}
</script>
</asp:Content>