Implemented the logging viewer

This commit is contained in:
Thorsten Sommer 2015-06-22 17:38:55 +02:00
parent d925e85653
commit 71aa99a5e3
11 changed files with 250 additions and 120 deletions

View File

@ -259,6 +259,14 @@ p {
.adminitemlog {
padding-top: 14px;
}
.columns {
padding-right: 10px;
padding-left: 10px;
text-align: left;
}
.filterformcontainer {
text-align: center;
}
@media (max-width: 991px) {
.icons.oneback {
margin-left: 113px;

8
Admin/Scheme/LogEvent.go Normal file
View File

@ -0,0 +1,8 @@
package Scheme
// Type for a log event
type LogEvent struct {
LogLine string
LogLevel string // logwarn || logdebug || logerror || loginfo || logtalkative || logsecurity
AB string // loga || logb
}

View File

@ -0,0 +1,73 @@
package Scheme
import (
"html/template"
)
// The type for the web logger viewer template
type LoggingViewer struct {
Title string
SetLiveView bool
CurrentLevel string
CurrentTimeRange string
CurrentCategory string
CurrentImpact string
CurrentSeverity string
CurrentMessageName string
CurrentSender string
CurrentPage string
MessageNames []MessageNames
Sender []Sender
Events []LogEvent
}
// The type for the message names is necessary to be able to define a function on it.
type MessageNames string
// The type for the senders is necessary to be able to define a function on it.
type Sender string
// This function is used from the template to mark selected values. This is for the type MessageNames.
func (lv MessageNames) IsSelected(field MessageNames, currentValue string) string {
if string(field) == currentValue {
return ` selected`
} else {
return ``
}
}
// This function is necessary to mark the HTML attribute as safe. Only then it is possible
// to change plain HTML code.
func (lv MessageNames) Safe(element string) template.HTMLAttr {
return template.HTMLAttr(element)
}
// This function is used from the template to mark selected values. This is for the type Sender.
func (lv Sender) IsSelected(field Sender, currentValue string) string {
if string(field) == currentValue {
return ` selected`
} else {
return ``
}
}
// This function is necessary to mark the HTML attribute as safe. Only then it is possible
// to change plain HTML code.
func (lv Sender) Safe(element string) template.HTMLAttr {
return template.HTMLAttr(element)
}
// This function is used from the template to mark selected values.
func (lv LoggingViewer) IsSelected(field, currentValue string) string {
if field == currentValue {
return ` selected`
} else {
return ``
}
}
// This function is necessary to mark the HTML attribute as safe. Only then it is possible
// to change plain HTML code.
func (lv LoggingViewer) Safe(element string) template.HTMLAttr {
return template.HTMLAttr(element)
}

View File

@ -1,25 +0,0 @@
package Scheme
// The type for the web logger viewer template
type Viewer struct {
Title string
SetLiveView bool
CurrentLevel string
CurrentTimeRange string
CurrentCategory string
CurrentImpact string
CurrentSeverity string
CurrentMessageName string
CurrentSender string
CurrentPage string
MessageNames []string
Sender []string
Events []LogEvent
}
// Type for a log event
type LogEvent struct {
LogLine string
LogLevel string // logwarn || logdebug || logerror || loginfo || logtalkative || logsecurity
AB string // loga || logb
}

View File

@ -11,7 +11,7 @@ var LoggingViewer string = `
<meta charset="utf-8">
<title>{{.Title}}</title>
{{if .SetLiveView}}
<meta http-equiv="refresh" content="30; URL=/log?LiveView={{.SetLiveView}}">
<meta http-equiv="refresh" content="30; URL=/log?Level={{.CurrentLevel}}&TimeRange={{.CurrentTimeRange}}&Category={{.CurrentCategory}}&Impact={{.CurrentImpact}}&Severity={{.CurrentSeverity}}&MSGName={{.CurrentMessageName}}&Sender={{.CurrentSender}}&CurrentPage={{.CurrentPage}}&LiveView={{.SetLiveView}}">
{{end}}
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="generator" content="Webflow">
@ -24,91 +24,117 @@ var LoggingViewer string = `
<div class="headercontainer">
<h1>Logging Viewer</h1>
</div>
<div class="controlsection">
<div class="w-section controlsection">
<div class="w-row">
<div class="w-col w-col-6">
<h2 class="headercontrol">Options</h2>
<div class="options">
<a class="button optionbuttons" href="#">
{{if .SetLiveView}}
Disable live view
{{else}}
Enable live view
{{end}}
</a>
{{if .SetLiveView}}
<a class="button optionbuttons currentoption" href="/log?Level={{.CurrentLevel}}&TimeRange={{.CurrentTimeRange}}&Category={{.CurrentCategory}}&Impact={{.CurrentImpact}}&Severity={{.CurrentSeverity}}&MSGName={{.CurrentMessageName}}&Sender={{.CurrentSender}}&CurrentPage={{.CurrentPage}}&LiveView=false">
{{else}}
<a class="button optionbuttons" href="/log?Level={{.CurrentLevel}}&TimeRange={{.CurrentTimeRange}}&Category={{.CurrentCategory}}&Impact={{.CurrentImpact}}&Severity={{.CurrentSeverity}}&MSGName={{.CurrentMessageName}}&Sender={{.CurrentSender}}&CurrentPage={{.CurrentPage}}&LiveView=true">
{{end}}
{{if .SetLiveView}}
Disable live view
{{else}}
Enable live view
{{end}}
</a>
</div>
</div>
<div class="w-col w-col-6">
<h2 class="headercontrol">Filtering</h2>
<div class="filters">
<div class="w-row">
<div class="w-col w-col-6">
<h3 class="subheader">By Time Range</h3><a class="button optionbuttons" href="#">Last 5 minutes</a><a class="button optionbuttons" href="#">Last 30 minutes</a><a class="button optionbuttons" href="#">Last 60 minutes</a><a class="button optionbuttons" href="#">Last 24 hours</a><a class="button optionbuttons" href="#">Last 7 days</a><a class="button optionbuttons" href="#">Last month</a><a class="button optionbuttons" href="#">All times</a>
</div>
<div class="w-col w-col-6">
<h3 class="subheader">By Filter</h3>
<div class="w-form">
<form id="wf-form-Filters" name="wf-form-Filters" data-name="Filters">
<label for="Level">Level:</label>
<select class="w-select" id="Level" name="Level" data-name="Level">
<option value="*">Any</option>
<option value="INFO">Information</option>
<option value="WARN">Warning</option>
<option value="SECURITY">Security</option>
<option value="ERROR">Error</option>
<option value="DEBUG">Debug</option>
<option value="TALKATIVE">Talkative</option>
</select>
<label for="Category">Category:</label>
<select class="w-select" id="Category" name="Category" data-name="Category">
<option value="*">Any</option>
<option value="BUSINESS">Business</option>
<option value="APP">Application</option>
<option value="USER">User</option>
<option value="SYSTEM">System</option>
</select>
<label for="Impact">Impact:</label>
<select class="w-select" id="Impact" name="Impact" data-name="Impact">
<option value="*">Any</option>
<option value="LOW">Low</option>
<option value="MIDDLE">Middle</option>
<option value="HIGH">High</option>
<option value="CRITICAL">Critical</option>
<option value="UNKNOWN">Unknown</option>
</select>
<label for="Severity">Severity:</label>
<select class="w-select" id="Severity" name="Severity" data-name="Severity">
<option value="*">Any</option>
<option value="LOW">Low</option>
<option value="MIDDLE">Middle</option>
<option value="HIGH">High</option>
<option value="CRITICAL">Critical</option>
<option value="UNKNOWN">Unknown</option>
</select>
<label for="MSGName">Message Names:</label>
<select class="w-select" id="MSGName" name="MSGName" data-name="MSGName">
<option value="*">Any</option>
{{range .MessageNames}}
<option value="{{.}}">{{.}}</option>
{{end}}
</select>
<label for="Sender">Sender:</label>
<select class="w-select" id="Sender" name="Sender" data-name="Sender">
<option value="*">Any</option>
{{range .Sender}}
<option value="{{.}}">{{.}}</option>
{{end}}
</select>
<input class="w-button button optionbuttons applyfilters" type="submit" value="Apply filters" data-wait="Please wait...">
</form>
<div class="w-form">
<form class="filterformcontainer" id="wf-form-Filters" name="wf-form-Filters" data-name="Filters">
<div class="w-row">
<div class="w-col w-col-6">
<div class="columns">
<h3 class="subheader">By Time Range</h3>
<label for="TimeRange">Time range:</label>
<select class="w-select" id="TimeRange" name="TimeRange" data-name="TimeRange">
<option value="*"{{.IsSelected "*" .CurrentTimeRange | .Safe}}>Whole time range</option>
<option value="last5min"{{.IsSelected "last5min" .CurrentTimeRange | .Safe}}>Last 5 minutes</option>
<option value="last30min"{{.IsSelected "last30min" .CurrentTimeRange | .Safe}}>Last 30 minutes</option>
<option value="last60min"{{.IsSelected "last60min" .CurrentTimeRange | .Safe}}>Last 60 minutes</option>
<option value="last24h"{{.IsSelected "last24h" .CurrentTimeRange | .Safe}}>Last 24 hours</option>
<option value="last7d"{{.IsSelected "last7d" .CurrentTimeRange | .Safe}}>Last 7 days</option>
<option value="lastMonth"{{.IsSelected "lastMonth" .CurrentTimeRange | .Safe}}>Last month</option>
</select>
</div>
</div>
<div class="w-col w-col-6">
<div class="columns">
<h3 class="subheader">By Filter</h3>
<label for="Level">Level:</label>
<select class="w-select" id="Level" name="Level" data-name="Level">
<option value="*"{{.IsSelected "*" .CurrentLevel | .Safe}}>Any</option>
<option value="INFO"{{.IsSelected "INFO" .CurrentLevel | .Safe}}>Information</option>
<option value="WARN"{{.IsSelected "WARN" .CurrentLevel | .Safe}}>Warning</option>
<option value="SECURITY"{{.IsSelected "SECURITY" .CurrentLevel | .Safe}}>Security</option>
<option value="ERROR"{{.IsSelected "ERROR" .CurrentLevel | .Safe}}>Error</option>
<option value="DEBUG"{{.IsSelected "DEBUG" .CurrentLevel | .Safe}}>Debug</option>
<option value="TALKATIVE"{{.IsSelected "TALKATIVE" .CurrentLevel | .Safe}}>Talkative</option>
</select>
<label for="Category">Category:</label>
<select class="w-select" id="Category" name="Category" data-name="Category">
<option value="*"{{.IsSelected "*" .CurrentCategory | .Safe}}>Any</option>
<option value="BUSINESS"{{.IsSelected "BUSINESS" .CurrentCategory | .Safe}}>Business</option>
<option value="APP"{{.IsSelected "APP" .CurrentCategory | .Safe}}>Application</option>
<option value="USER"{{.IsSelected "USER" .CurrentCategory | .Safe}}>User</option>
<option value="SYSTEM"{{.IsSelected "SYSTEM" .CurrentCategory | .Safe}}>System</option>
</select>
<label for="Impact">Impact:</label>
<select class="w-select" id="Impact" name="Impact" data-name="Impact">
<option value="*"{{.IsSelected "*" .CurrentImpact | .Safe}}>Any</option>
<option value="LOW"{{.IsSelected "LOW" .CurrentImpact | .Safe}}>Low</option>
<option value="MIDDLE"{{.IsSelected "MIDDLE" .CurrentImpact | .Safe}}>Middle</option>
<option value="HIGH"{{.IsSelected "HIGH" .CurrentImpact | .Safe}}>High</option>
<option value="CRITICAL"{{.IsSelected "CRITICAL" .CurrentImpact | .Safe}}>Critical</option>
<option value="UNKNOWN"{{.IsSelected "UNKNOWN" .CurrentImpact | .Safe}}>Unknown</option>
</select>
<label for="Severity">Severity:</label>
<select class="w-select" id="Severity" name="Severity" data-name="Severity">
<option value="*"{{.IsSelected "*" .CurrentSeverity | .Safe}}>Any</option>
<option value="LOW"{{.IsSelected "LOW" .CurrentSeverity | .Safe}}>Low</option>
<option value="MIDDLE"{{.IsSelected "MIDDLE" .CurrentSeverity | .Safe}}>Middle</option>
<option value="HIGH"{{.IsSelected "HIGH" .CurrentSeverity | .Safe}}>High</option>
<option value="CRITICAL"{{.IsSelected "CRITICAL" .CurrentSeverity | .Safe}}>Critical</option>
<option value="UNKNOWN"{{.IsSelected "UNKNOWN" .CurrentSeverity | .Safe}}>Unknown</option>
</select>
<label for="MSGName">Message Names:</label>
<select class="w-select" id="MSGName" name="MSGName" data-name="MSGName">
<option value="*"{{.IsSelected "*" .CurrentMessageName | .Safe}}>Any</option>
{{$currentMessageName := .CurrentMessageName}}
{{range .MessageNames}}
<option value="{{.}}"{{.IsSelected . $currentMessageName | .Safe}}>{{.}}</option>
{{end}}
</select>
<label for="Sender">Sender:</label>
<select class="w-select" id="Sender" name="Sender" data-name="Sender">
<option value="*"{{.IsSelected "*" .CurrentSender | .Safe}}>Any</option>
{{$currentSender := .CurrentSender}}
{{range .Sender}}
<option value="{{.}}"{{.IsSelected . $currentSender | .Safe}}>{{.}}</option>
{{end}}
</select>
</div>
</div>
</div>
</div>
<input class="w-button button optionbuttons applyfilters" type="submit" value="Apply filters" data-wait="Please wait...">
</form>
</div>
<div class="w-form-done">
<p>Thank you! Your submission has been received!</p>
</div>
<div class="w-form-fail">
<p>Oops! Something went wrong while submitting the form :(</p>
</div>
</div>
</div>
</div>
</div>
<div>
<div class="w-section">
<h2 class="headeroutput">Output</h2>
<ul class="loglist">
{{range .Events}}
@ -128,6 +154,12 @@ var LoggingViewer string = `
<div class="w-hidden-main w-hidden-medium w-hidden-small newlineblock"></div>
<input class="w-button button changepagebutton pagechangesubmit" type="submit" value="Change page" data-wait="Please wait...">
</form>
<div class="w-form-done">
<p>Thank you! Your submission has been received!</p>
</div>
<div class="w-form-fail">
<p>Oops! Something went wrong while submitting the form :(</p>
</div>
</div>
<div class="w-hidden-main w-hidden-medium newlineblock"></div><a class="button changepagebutton" href="#">+1</a><a class="button changepagebutton" href="#">Last</a>
</div>

View File

@ -1,9 +1,17 @@
package DeviceDatabase
import (
"github.com/SommerEngineering/Ocean/Admin/Scheme"
)
// Read the message names out of the cache.
func ReadMessageNames() (messageNames []string) {
func ReadMessageNames() (messageNames []Scheme.MessageNames) {
mutexCacheMessageNames.RLock()
defer mutexCacheMessageNames.RUnlock()
messageNames = cacheMessageNames
// Transform the values to the right format:
for _, entry := range cacheMessageNames {
messageNames = append(messageNames, Scheme.MessageNames(entry))
}
return
}

View File

@ -1,6 +1,7 @@
package DeviceDatabase
import (
"github.com/SommerEngineering/Ocean/Admin/Scheme"
"github.com/SommerEngineering/Ocean/Log"
LM "github.com/SommerEngineering/Ocean/Log/Meta"
"gopkg.in/mgo.v2/bson"
@ -8,7 +9,7 @@ import (
)
// Read the message names from the database without any cache.
func readMessageNamesFromDB() (result []string) {
func readMessageNamesFromDB() (result []Scheme.MessageNames) {
var nextMessageNames []string
if err := logDBCollection.Find(bson.D{}).Distinct(`MessageName`, &nextMessageNames); err != nil {
// Case: Error, was not able to write the event to the database:
@ -18,6 +19,11 @@ func readMessageNamesFromDB() (result []string) {
// Sort the sender names:
sort.Strings(nextMessageNames)
result = nextMessageNames
// Transform the values to the right format:
for _, entry := range nextMessageNames {
result = append(result, Scheme.MessageNames(entry))
}
return
}

View File

@ -1,9 +1,17 @@
package DeviceDatabase
import (
"github.com/SommerEngineering/Ocean/Admin/Scheme"
)
// Read the sender names out of the cache.
func ReadSenderNames() (senderNames []string) {
func ReadSenderNames() (senderNames []Scheme.Sender) {
mutexCacheSenderNames.RLock()
defer mutexCacheSenderNames.RUnlock()
senderNames = cacheSenderNames
// Transform the values to the right format:
for _, entry := range cacheSenderNames {
senderNames = append(senderNames, Scheme.Sender(entry))
}
return
}

View File

@ -1,6 +1,7 @@
package DeviceDatabase
import (
"github.com/SommerEngineering/Ocean/Admin/Scheme"
"github.com/SommerEngineering/Ocean/Log"
LM "github.com/SommerEngineering/Ocean/Log/Meta"
"gopkg.in/mgo.v2/bson"
@ -8,7 +9,7 @@ import (
)
// Reads the sender names from the database without any caching.
func readSenderNamesFromDB() (result []string) {
func readSenderNamesFromDB() (result []Scheme.Sender) {
var nextSenderNames []string
if err := logDBCollection.Find(bson.D{}).Distinct(`Sender`, &nextSenderNames); err != nil {
// Case: Was not possible to write to the database.
@ -18,6 +19,10 @@ func readSenderNamesFromDB() (result []string) {
// Sort the sender names:
sort.Strings(nextSenderNames)
result = nextSenderNames
// Transform the values to the right format:
for _, entry := range nextSenderNames {
result = append(result, Scheme.Sender(entry))
}
return
}

View File

@ -1,6 +1,7 @@
package DeviceDatabase
import (
"github.com/SommerEngineering/Ocean/Admin/Scheme"
LM "github.com/SommerEngineering/Ocean/Log/Meta"
"gopkg.in/mgo.v2"
"sync"
@ -8,17 +9,17 @@ import (
var (
// This is the name for logging event from this package:
senderName LM.Sender = `System::Logger::Database`
mutexCacheFull sync.Mutex = sync.Mutex{}
mutexCacheSenderNames sync.RWMutex = sync.RWMutex{}
mutexCacheMessageNames sync.RWMutex = sync.RWMutex{}
cache chan LogDBEntry = nil
cacheSizeNumberOfEvents int = 50
cacheSizeTime2FlushSeconds int = 6
nameCachesRefreshTimeSeconds int = 300
cacheSenderNames []string = nil
cacheMessageNames []string = nil
logDB *mgo.Database = nil
logDBSession *mgo.Session = nil
logDBCollection *mgo.Collection = nil
senderName LM.Sender = `System::Logger::Database`
mutexCacheFull sync.Mutex = sync.Mutex{}
mutexCacheSenderNames sync.RWMutex = sync.RWMutex{}
mutexCacheMessageNames sync.RWMutex = sync.RWMutex{}
cache chan LogDBEntry = nil
cacheSizeNumberOfEvents int = 50
cacheSizeTime2FlushSeconds int = 6
nameCachesRefreshTimeSeconds int = 300
cacheSenderNames []Scheme.Sender = nil
cacheMessageNames []Scheme.MessageNames = nil
logDB *mgo.Database = nil
logDBSession *mgo.Session = nil
logDBCollection *mgo.Collection = nil
)

View File

@ -26,20 +26,20 @@ func HandlerWebLog(response http.ResponseWriter, request *http.Request) {
countParameters := len(request.Form)
// Setup the data for the HTML template:
data := Scheme.Viewer{}
data := Scheme.LoggingViewer{}
data.Title = `Logging Viewer`
data.Sender = DeviceDatabase.ReadSenderNames()
data.MessageNames = DeviceDatabase.ReadMessageNames()
// To less parameters?
if countParameters < 9 {
// Initial view => refresh & first page (latest logs)
// Initial view => first page (latest logs)
data.Events = readLatest()
data.SetLiveView = true
} else {
// Case: Custom view
currentLevel := request.FormValue(`Level`)
currentTimeRange := request.FormValue(`timeRange`)
currentTimeRange := request.FormValue(`TimeRange`)
currentCategory := request.FormValue(`Category`)
currentImpact := request.FormValue(`Impact`)
currentSeverity := request.FormValue(`Severity`)
@ -96,6 +96,12 @@ func HandlerWebLog(response http.ResponseWriter, request *http.Request) {
} else {
data.CurrentSender = `*`
}
if currentPage != `` {
data.CurrentPage = currentPage
} else {
data.CurrentPage = `*`
}
}
// Write the MIME type and execute the template: