1
0
mirror of https://git.teknik.io/Teknikode/Teknik.git synced 2023-08-02 14:16:22 +02:00

Added Status page for viewing realtime stats of server resources.

Currently setup with CPU, Memory, and Network
This commit is contained in:
Uncled1023 2017-02-17 16:54:27 -08:00
parent 8608efbe54
commit 2c972f1a83
14 changed files with 553 additions and 2 deletions

View File

@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Web;
using System.Web.Mvc;
using Teknik.Areas.Status.ViewModels;
using Teknik.Attributes;
using Teknik.Controllers;
using Teknik.Filters;
using Teknik.Models;
using Teknik.Utilities;
namespace Teknik.Areas.Status.Controllers
{
[TeknikAuthorize]
public class StatusController : DefaultController
{
private TeknikEntities db = new TeknikEntities();
[TrackPageView]
[AllowAnonymous]
public ActionResult Index()
{
ViewBag.Title = "Status Information - " + Config.Title;
ViewBag.Description = "Current status information for the server and resources.";
StatusViewModel model = new StatusViewModel();
// Load initial status info
return View(model);
}
[HttpGet]
[AllowAnonymous]
public ActionResult GetUsage()
{
try
{
float totalCPUValue = 0;
float webCPUValue = 0;
float dbCPUValue = 0;
float totalMem = 0;
float totalAvailMemValue = 0;
float totalUsedMemValue = 0;
float webMemValue = 0;
float dbMemValue = 0;
float bytesSent = 0;
float bytesReceived = 0;
// CPU
using (PerformanceCounter totalCPU = new PerformanceCounter("Processor", "% Processor Time", "_Total", true))
using (PerformanceCounter webCPU = new PerformanceCounter("Process", "% Processor Time", Process.GetCurrentProcess().ProcessName, true))
using (PerformanceCounter dbCPU = new PerformanceCounter("Process", "% Processor Time", Config.StatusConfig.DatabaseProcessName, true))
// Memory
using (PerformanceCounter totalAvailMem = new PerformanceCounter("Memory", "Available Bytes", true))
using (PerformanceCounter webMem = new PerformanceCounter("Process", "Private Bytes", Process.GetCurrentProcess().ProcessName, true))
using (PerformanceCounter dbMem = new PerformanceCounter("Process", "Private Bytes", Config.StatusConfig.DatabaseProcessName, true))
// Network
using (PerformanceCounter sentPerf = new PerformanceCounter("Network Interface", "Bytes Sent/sec", Config.StatusConfig.NetworkInterface, true))
using (PerformanceCounter receivedPerf = new PerformanceCounter("Network Interface", "Bytes Received/sec", Config.StatusConfig.NetworkInterface, true))
{
// CPU Sample
totalCPU.NextValue();
if (Config.StatusConfig.ShowWebStatus)
{
webCPU.NextValue();
}
if (Config.StatusConfig.ShowDatabaseStatus)
{
dbCPU.NextValue();
}
// Network Sample
sentPerf.NextValue();
receivedPerf.NextValue();
// Wait the sample time
Thread.Sleep(1000);
// CPU Values
totalCPUValue = totalCPU.NextValue();
if (Config.StatusConfig.ShowWebStatus)
{
webCPUValue = webCPU.NextValue();
}
if (Config.StatusConfig.ShowDatabaseStatus)
{
dbCPUValue = dbCPU.NextValue();
}
// Memory Values
totalMem = Config.StatusConfig.TotalMemory;
totalAvailMemValue = totalAvailMem.NextValue();
totalUsedMemValue = totalMem - totalAvailMemValue;
if (Config.StatusConfig.ShowWebStatus)
{
webMemValue = webMem.NextValue();
}
if (Config.StatusConfig.ShowDatabaseStatus)
{
dbMemValue = dbMem.NextValue();
}
// Network Values
bytesSent = sentPerf.NextValue();
bytesReceived = receivedPerf.NextValue();
// Return usage info
return Json(new { result = new {
cpu = new { total = totalCPUValue, web = webCPUValue, db = dbCPUValue },
memory = new { total = totalMem, totalAvail = totalAvailMemValue, totalUsed = totalUsedMemValue, webUsed = webMemValue, dbUsed = dbMemValue },
network = new { sent = bytesSent, received = bytesReceived }
} }, JsonRequestBehavior.AllowGet);
}
}
catch (Exception ex)
{
return Json(new { error = new { message = ex.GetFullMessage(true) } }, JsonRequestBehavior.AllowGet);
}
}
}
}

View File

@ -0,0 +1,216 @@
var cpuUsageChart;
var memUsageChart;
var networkUsageChart;
$(document).ready(function () {
cpuUsageChart = new Highcharts.chart({
chart: {
useUTC: false,
renderTo: 'cpu-usage-chart',
type: 'line',
marginRight: 10
},
title: {
text: 'CPU Usage'
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150
},
yAxis: {
title: {
text: 'Percentage %'
},
max: 100,
min: 0,
format: '{value}',
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
tooltip: {
shared: true,
crosshairs: true,
pointFormat: '<span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.y:.2f}%</b><br/>'
},
series: [
{
name: 'Total',
data: []
},
{
name: 'Website',
data: []
},
{
name: 'Database',
data: []
}
]
});
memUsageChart = new Highcharts.chart({
chart: {
useUTC: false,
renderTo: 'mem-usage-chart',
type: 'line',
marginRight: 10
},
title: {
text: 'Memory Usage'
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150
},
yAxis: {
title: {
text: 'Memory'
},
min: 0,
max: totalMemory,
tickInterval: tickInterval,
labels: {
formatter: function () {
return filesize(this.value);
}
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
tooltip: {
shared: true,
crosshairs: true,
pointFormatter: function () {
var yVal = filesize(this.y);
return '<span style="color:' + this.color + '">\u25CF</span> ' + this.series.name + ': <b>' + yVal + '</b><br/>';
}
},
series: [
{
name: 'Total',
data: []
},
{
name: 'Website',
data: []
},
{
name: 'Database',
data: []
}
]
});
networkUsageChart = new Highcharts.chart({
chart: {
useUTC: false,
renderTo: 'network-usage-chart',
type: 'line',
marginRight: 10
},
title: {
text: 'Network Usage'
},
xAxis: {
type: 'datetime',
tickPixelInterval: 150
},
yAxis: {
title: {
text: 'Speed'
},
min: 0,
labels: {
formatter: function () {
return getReadableBandwidthString(this.value);
}
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
tooltip: {
shared: true,
crosshairs: true,
pointFormatter: function () {
var yVal = getReadableBandwidthString(this.y);
return '<span style="color:' + this.color + '">\u25CF</span> ' + this.series.name + ': <b>' + yVal + '</b><br/>';
}
},
series: [
{
name: 'Sent',
data: []
},
{
name: 'Received',
data: []
}
]
});
// Fire Off the request data
requestData();
});
function requestData() {
$.ajax({
type: "GET",
url: getUsageURL,
success: function (response) {
if (response.result) {
// Tick Time
var x = (new Date()).getTime();
// CPU Usage
cpuUsageChart.series[0].addPoint([x, response.result.cpu.total], false, cpuUsageChart.series[0].data.length > 20);
if (showWebCPU) {
cpuUsageChart.series[1].addPoint([x, response.result.cpu.web], false, cpuUsageChart.series[1].data.length > 20);
}
if (showDatabaseCPU) {
cpuUsageChart.series[2].addPoint([x, response.result.cpu.db], false, cpuUsageChart.series[2].data.length > 20);
}
// Database Usage
memUsageChart.series[0].addPoint([x, response.result.memory.totalUsed], false, memUsageChart.series[0].data.length > 20);
if (showWebCPU) {
memUsageChart.series[1].addPoint([x, response.result.memory.webUsed], false, memUsageChart.series[1].data.length > 20);
}
if (showDatabaseCPU) {
memUsageChart.series[2].addPoint([x, response.result.memory.dbUsed], false, memUsageChart.series[2].data.length > 20);
}
// Network Usage
networkUsageChart.series[0].addPoint([x, response.result.network.sent], false, networkUsageChart.series[0].data.length > 20);
networkUsageChart.series[1].addPoint([x, response.result.network.received], false, networkUsageChart.series[1].data.length > 20);
// Redraw the charts
cpuUsageChart.redraw();
memUsageChart.redraw();
networkUsageChart.redraw();
// call it again right away
setTimeout(requestData, 100);
}
else {
var err = response;
if (response.error) {
err = response.error.message;
}
$("#top_msg").css('display', 'inline', 'important');
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + err + '</div>');
}
},
cache: false
});
}

View File

@ -0,0 +1,46 @@
using System.Collections.Generic;
using System.Web.Mvc;
using System.Web.Optimization;
using Teknik.Configuration;
using Teknik.Utilities;
namespace Teknik.Areas.Status
{
public class StatusAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Status";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
Config config = Config.Load();
context.MapSubdomainRoute(
"Status.Index",
new List<string>() { "status" }, // Subdomains
new List<string>() { config.Host }, // domains
"",
new { controller = "Status", action = "Index" },
new[] { typeof(Controllers.StatusController).Namespace }
);
context.MapSubdomainRoute(
"Status.Action",
new List<string>() { "status" }, // Subdomains
new List<string>() { config.Host }, // domains
"Action/{controller}/{action}",
new { controller = "Status", action = "Index" },
new[] { typeof(Controllers.StatusController).Namespace }
);
// Register Script Bundle
BundleTable.Bundles.Add(new CdnScriptBundle("~/bundles/status", config.CdnHost).Include(
"~/Scripts/Highcharts/highcharts.js",
"~/Scripts/FileSize/filesize.min.js",
"~/Areas/Status/Scripts/Status.js"));
}
}
}

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Teknik.ViewModels;
namespace Teknik.Areas.Status.ViewModels
{
public class StatusViewModel : ViewModelBase
{
}
}

View File

@ -0,0 +1,50 @@
@model Teknik.Areas.Status.ViewModels.StatusViewModel
@using Teknik.Utilities
@Scripts.Render("~/bundles/status")
<script type="text/javascript">
var showWebCPU = @Model.Config.StatusConfig.ShowWebStatus.ToString().ToLower();
var showDatabaseCPU = @Model.Config.StatusConfig.ShowDatabaseStatus.ToString().ToLower();
var showNetworkUsage = @Model.Config.StatusConfig.ShowNetworkStatus.ToString().ToLower();
var totalMemory = @Model.Config.StatusConfig.TotalMemory;
var tickInterval = totalMemory / 5;
var getUsageURL = '@Url.SubRouteUrl("status", "Status.Action", new { action = "GetUsage" })';
</script>
<div class="container">
@if (Model.Config.StatusConfig.Enabled)
{
<div class="row">
<div class="col-sm-10 col-sm-offset-1">
<h2 class="text-center"><b>Server Status</b></h2>
<hr>
<div class="row">
<div class="col-sm-12">
<div id="cpu-usage-chart"></div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div id="mem-usage-chart"></div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div id="network-usage-chart"></div>
</div>
</div>
</div>
</div>
}
else
{
<div class="row">
<div class="col-sm-8 col-sm-offset-2 text-center">
<h3>Status Information has been disabled</h3>
</div>
</div>
}
</div>

View File

@ -0,0 +1,3 @@
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}

View File

@ -0,0 +1,36 @@
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
</sectionGroup>
</configSections>
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
<add namespace="System.Web.Optimization" />
<add namespace="Teknik" />
</namespaces>
</pages>
</system.web.webPages.razor>
<appSettings>
<add key="webpages:Enabled" value="false" />
</appSettings>
<system.webServer>
<handlers>
<remove name="BlockViewHandler"/>
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
</handlers>
</system.webServer>
</configuration>

View File

@ -7,6 +7,7 @@ using System.Web.Security;
using Teknik.Utilities;
using Teknik.Models;
using Teknik.ViewModels;
using System.Web.Mvc;
namespace Teknik.Areas.Users.ViewModels
{
@ -21,6 +22,7 @@ namespace Teknik.Areas.Users.ViewModels
[Required]
[Display(Name = "Password")]
[DataType(DataType.Password)]
[AllowHtml]
public string Password { get; set; }
[Display(Name = "Remember Me")]

View File

@ -0,0 +1,6 @@
/*
2017
@version 3.5.4
*/
"use strict";!function(a){function b(a){var b=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},f=[],g=0,h=void 0,i=void 0,j=void 0,k=void 0,l=void 0,m=void 0,n=void 0,o=void 0,p=void 0,q=void 0,r=void 0,s=void 0,t=void 0,u=void 0;if(isNaN(a))throw new Error("Invalid arguments");return j=b.bits===!0,r=b.unix===!0,i=b.base||2,q=void 0!==b.round?b.round:r?1:2,s=void 0!==b.spacer?b.spacer:r?"":" ",u=b.symbols||b.suffixes||{},t=2===i?b.standard||"jedec":"jedec",p=b.output||"string",l=b.fullform===!0,m=b.fullforms instanceof Array?b.fullforms:[],h=void 0!==b.exponent?b.exponent:-1,o=Number(a),n=o<0,k=i>2?1e3:1024,n&&(o=-o),0===o?(h=0,f[0]=0,f[1]=r?"":j?"b":"B"):((h===-1||isNaN(h))&&(h=Math.floor(Math.log(o)/Math.log(k)),h<0&&(h=0)),h>8&&(h=8),g=o/(2===i?Math.pow(2,10*h):Math.pow(1e3,h)),j&&(g*=8,g>=k&&h<8&&(g/=k,h++)),f[0]=Number(g.toFixed(h>0?q:0)),f[1]=10===i&&1===h?j?"kb":"kB":d[t][j?"bits":"bytes"][h],r&&(f[1]="jedec"===t?f[1].charAt(0):h>0?f[1].replace(/B$/,""):f[1],c.test(f[1])&&(f[0]=Math.floor(f[0]),f[1]=""))),n&&(f[0]=-f[0]),f[1]=u[f[1]]||f[1],"array"===p?f:"exponent"===p?h:"object"===p?{value:f[0],suffix:f[1],symbol:f[1]}:(l&&(f[1]=m[h]?m[h]:e[t][h]+(j?"bit":"byte")+(1===f[0]?"":"s")),f.join(s))}var c=/^(b|B)$/,d={iec:{bits:["b","Kib","Mib","Gib","Tib","Pib","Eib","Zib","Yib"],bytes:["B","KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"]},jedec:{bits:["b","Kb","Mb","Gb","Tb","Pb","Eb","Zb","Yb"],bytes:["B","KB","MB","GB","TB","PB","EB","ZB","YB"]}},e={iec:["","kibi","mebi","gibi","tebi","pebi","exbi","zebi","yobi"],jedec:["","kilo","mega","giga","tera","peta","exa","zetta","yotta"]};b.partial=function(a){return function(c){return b(c,a)}},"undefined"!=typeof exports?module.exports=b:"function"==typeof define&&define.amd?define(function(){return b}):a.filesize=b}("undefined"!=typeof window?window:global);
//# sourceMappingURL=filesize.min.js.map

Binary file not shown.

View File

@ -78,6 +78,7 @@
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.VisualBasic" />
<Reference Include="MySql.Data, Version=6.9.9.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d, processorArchitecture=MSIL">
<HintPath>..\packages\MySql.Data.6.9.9\lib\net45\MySql.Data.dll</HintPath>
<Private>True</Private>
@ -233,6 +234,9 @@
<Compile Include="Areas\Privacy\Controllers\PrivacyController.cs" />
<Compile Include="Areas\Privacy\PrivacyAreaRegistration.cs" />
<Compile Include="Areas\Privacy\ViewModels\PrivacyViewModel.cs" />
<Compile Include="Areas\Status\Controllers\StatusController.cs" />
<Compile Include="Areas\Status\StatusAreaRegistration.cs" />
<Compile Include="Areas\Status\ViewModels\StatusViewModel.cs" />
<Compile Include="Areas\User\Controllers\UserController.cs" />
<Compile Include="Areas\User\Models\BlogSettings.cs" />
<Compile Include="Areas\User\Models\ResetPasswordVerification.cs" />
@ -338,6 +342,7 @@
<Content Include="Areas\Paste\Scripts\Paste.js" />
<Content Include="Areas\Podcast\Content\Podcast.css" />
<Content Include="Areas\Podcast\Scripts\Podcast.js" />
<Content Include="Areas\Status\Scripts\Status.js" />
<Content Include="Areas\Transparency\Scripts\Transparency.js" />
<Content Include="Areas\Upload\Content\Upload.css" />
<Content Include="Areas\User\Scripts\CheckAuthCode.js" />
@ -584,6 +589,9 @@
<Content Include="Areas\Vault\Views\Vault\ModifyVaultItem.cshtml" />
<Content Include="Areas\Vault\Views\Vault\PasteItem.cshtml" />
<Content Include="Areas\Vault\Views\Vault\UploadItem.cshtml" />
<Content Include="Areas\Status\Views\web.config" />
<Content Include="Areas\Status\Views\_ViewStart.cshtml" />
<Content Include="Areas\Status\Views\Shared\Index.cshtml" />
<None Include="Properties\PublishProfiles\Teknik Dev.pubxml" />
<None Include="Properties\PublishProfiles\Teknik Production.pubxml" />
<None Include="Scripts\jquery-2.1.4.intellisense.js" />
@ -597,6 +605,7 @@
<Content Include="Scripts\Crypto-js\pad-nopadding.js" />
<Content Include="Scripts\Dropzone\dropzone.js" />
<Content Include="Scripts\FileSaver.js" />
<Content Include="Scripts\FileSize\filesize.min.js" />
<Content Include="Scripts\Highcharts\highcharts-3d.js" />
<Content Include="Scripts\Highcharts\highcharts-more.js" />
<Content Include="Scripts\Highcharts\highcharts.js" />
@ -679,6 +688,8 @@
<Folder Include="Areas\Privacy\Models\" />
<Folder Include="Areas\Privacy\Views\Shared\" />
<Folder Include="Areas\Shortener\Views\Shared\" />
<Folder Include="Areas\Status\Models\" />
<Folder Include="Areas\Status\Views\Status\" />
<Folder Include="Areas\Stream\Views\Shared\" />
<Folder Include="Areas\TOS\Models\" />
<Folder Include="Areas\TOS\Views\Shared\" />

View File

@ -41,6 +41,7 @@ namespace Teknik.Configuration
private ShortenerConfig _ShortenerConfig;
private VaultConfig _VaultConfig;
private TransparencyConfig _TransparencyConfig;
private StatusConfig _StatusConfig;
private DatabaseConfig _DatabaseConfig;
private LoggingConfig _LoggingConfig;
private PiwikConfig _PiwikConfig;
@ -94,12 +95,15 @@ namespace Teknik.Configuration
// Shortener Configuration
public ShortenerConfig ShortenerConfig { get { return _ShortenerConfig; } set { _ShortenerConfig = value; } }
// Shortener Configuration
// Vault Configuration
public VaultConfig VaultConfig { get { return _VaultConfig; } set { _VaultConfig = value; } }
// Shortener Configuration
// Transparency Configuration
public TransparencyConfig TransparencyConfig { get { return _TransparencyConfig; } set { _TransparencyConfig = value; } }
// Status Configuration
public StatusConfig StatusConfig { get { return _StatusConfig; } set { _StatusConfig = value; } }
// Database Configuration
public DatabaseConfig DatabaseConfig { get { return _DatabaseConfig; } set { _DatabaseConfig = value; } }
@ -147,6 +151,7 @@ namespace Teknik.Configuration
ShortenerConfig = new ShortenerConfig();
VaultConfig = new VaultConfig();
TransparencyConfig = new TransparencyConfig();
StatusConfig = new StatusConfig();
DatabaseConfig = new DatabaseConfig();
LoggingConfig = new LoggingConfig();
PiwikConfig = new PiwikConfig();

View File

@ -59,6 +59,7 @@
<Compile Include="PiwikConfig.cs" />
<Compile Include="PodcastConfig.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="StatusConfig.cs" />
<Compile Include="VaultConfig.cs" />
<Compile Include="ShortenerConfig.cs" />
<Compile Include="StreamConfig.cs" />

View File

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Teknik.Configuration
{
public class StatusConfig
{
public bool Enabled { get; set; }
public bool ShowWebStatus { get; set; }
public bool ShowDatabaseStatus { get; set; }
public bool ShowNetworkStatus { get; set; }
public string DatabaseProcessName { get; set; }
public string NetworkInterface { get; set; }
public long TotalMemory { get; set; }
public StatusConfig()
{
Enabled = false;
ShowWebStatus = false;
ShowDatabaseStatus = false;
ShowNetworkStatus = false;
DatabaseProcessName = string.Empty;
NetworkInterface = string.Empty;
TotalMemory = 0;
}
}
}