1
0
mirror of https://github.com/bitwarden/server.git synced 2025-02-17 02:01:53 +01:00

admin logs

This commit is contained in:
Kyle Spearrin 2018-03-29 23:30:56 -04:00
parent f2ecea0a17
commit d4b4a2b014
6 changed files with 208 additions and 0 deletions

View File

@ -0,0 +1,70 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Bit.Admin.Models;
using Bit.Core;
using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.Documents.Linq;
namespace Bit.Admin.Controllers
{
[Authorize]
[SelfHosted(NotSelfHostedOnly = true)]
public class LogsController : Controller
{
private const string Database = "Diagnostics";
private const string Collection = "Logs";
private readonly GlobalSettings _globalSettings;
public LogsController(GlobalSettings globalSettings)
{
_globalSettings = globalSettings;
}
public async Task<IActionResult> Index(string cursor = null, int count = 25)
{
var collectionLink = UriFactory.CreateDocumentCollectionUri(Database, Collection);
using(var client = new DocumentClient(new Uri(_globalSettings.DocumentDb.Uri),
_globalSettings.DocumentDb.Key))
{
var options = new FeedOptions
{
MaxItemCount = count,
RequestContinuation = cursor
};
var query = client.CreateDocumentQuery(collectionLink, options)
.OrderByDescending(l => l.Timestamp).AsDocumentQuery();
var response = await query.ExecuteNextAsync<LogModel>();
return View(new CursorPagedModel<LogModel>
{
Items = response.ToList(),
Count = count,
Cursor = cursor,
NextCursor = response.ResponseContinuation
});
}
}
public async Task<IActionResult> View(string id)
{
using(var client = new DocumentClient(new Uri(_globalSettings.DocumentDb.Uri),
_globalSettings.DocumentDb.Key))
{
var uri = UriFactory.CreateDocumentUri(Database, Collection, id);
var response = await client.ReadDocumentAsync<LogModel>(uri);
if(response?.Document == null)
{
return RedirectToAction("Index");
}
return View(response.Document);
}
}
}
}

View File

@ -0,0 +1,12 @@
using System.Collections.Generic;
namespace Bit.Admin.Models
{
public class CursorPagedModel<T>
{
public List<T> Items { get; set; }
public int Count { get; set; }
public string Cursor { get; set; }
public string NextCursor { get; set; }
}
}

View File

@ -0,0 +1,28 @@
using System;
using System.Runtime.Serialization;
using Microsoft.Azure.Documents;
using Newtonsoft.Json;
using Serilog.Events;
namespace Bit.Admin.Models
{
public class LogModel : Resource
{
public long EventIdHash { get; set; }
public LogEventLevel Level { get; set; }
public string Message { get; set; }
public string MessageTruncated => Message.Length > 200 ? $"{Message.Substring(0, 200)}..." : Message;
public string MessageTemplate { get; set; }
public Error Exception { get; set; }
[JsonObject(MemberSerialization.OptIn)]
public class Error : Exception, ISerializable
{
[JsonProperty(PropertyName = "error")]
public string ErrorMessage { get; set; }
[JsonConstructor]
public Error() { }
}
}
}

View File

@ -0,0 +1,61 @@
@model CursorPagedModel<LogModel>
@{
ViewData["Title"] = "Logs";
}
<h1>Logs</h1>
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead>
<tr>
<th style="width: 50px;">&nbsp;</th>
<th style="width: 200px;">Timestamp</th>
<th style="width: 100px;">Level</th>
<th>Message</th>
</tr>
</thead>
<tbody>
@if(!Model.Items.Any())
{
<tr>
<td colspan="4">No results to list.</td>
</tr>
}
else
{
@foreach(var log in Model.Items)
{
<tr>
<td>
<a asp-action="View" asp-route-id="@log.Id" title="View">
<i class="fa fa-file-text-o fa-lg"></i>
</a>
</td>
<td>@log.Timestamp.ToString()</td>
<td>@log.Level</td>
<td>@log.MessageTruncated</td>
</tr>
}
}
</tbody>
</table>
</div>
<nav>
<ul class="pagination">
@if(string.IsNullOrWhiteSpace(Model.NextCursor))
{
<li class="page-item disabled">
<a class="page-link" href="#" tabindex="-1">Next</a>
</li>
}
else
{
<li class="page-item">
<a class="page-link" asp-action="Index" asp-route-cursor="@Model.NextCursor"
asp-route-count="@Model.Count">Next</a>
</li>
}
</ul>
</nav>

View File

@ -0,0 +1,30 @@
@model LogModel
@{
ViewData["Title"] = "Log: " + Model.Id;
}
<h1>Log <small>@Model.Id</small></h1>
<h2>Information</h2>
<dl class="row">
<dt class="col-sm-4 col-lg-3">Id</dt>
<dd class="col-sm-8 col-lg-9"><code>@Model.Id</code></dd>
<dt class="col-sm-4 col-lg-3">Event Id Hash</dt>
<dd class="col-sm-8 col-lg-9">@Model.EventIdHash</dd>
<dt class="col-sm-4 col-lg-3">Timestamp</dt>
<dd class="col-sm-8 col-lg-9">@Model.Timestamp.ToString()</dd>
<dt class="col-sm-4 col-lg-3">Level</dt>
<dd class="col-sm-8 col-lg-9">@Model.Level</dd>
</dl>
<h2>Message</h2>
<pre style="max-height: 500px;">@Model.Message</pre>
@if(Model.Exception != null)
{
<h2>Exception</h2>
<pre style="max-height: 500px;">@Model.Exception.ToString()</pre>
}

View File

@ -1,4 +1,5 @@
@inject SignInManager<IdentityUser> SignInManager
@inject Bit.Core.GlobalSettings GlobalSettings
<!DOCTYPE html>
<html>
<head>
@ -36,6 +37,12 @@
<li class="nav-item" active-controller="Organizations">
<a class="nav-link" asp-controller="Organizations" asp-action="Index">Organizations</a>
</li>
@if(!GlobalSettings.SelfHosted)
{
<li class="nav-item" active-controller="Logs">
<a class="nav-link" asp-controller="Logs" asp-action="Index">Logs</a>
</li>
}
}
<li class="nav-item">
<a class="nav-link" href="https://help.bitwarden.com/hosting/" target="_blank">Docs</a>