Add documentation

This commit is contained in:
Thorsten Sommer committed 2015-06-17 17:44:52 +02:00
1 parent 69d0bdabf4
commit 6dab89a1d4
144 files changed
+1107 -549

No files matched your search

+1
View File
@@ -4,6 +4,7 @@ import (
"github.com/SommerEngineering/Ocean/Log/Meta"
)
// The interface for every logging device.
type Device interface {
Log(logEntries []Meta.Entry)
Flush()
@@ -4,6 +4,7 @@ import (
"github.com/SommerEngineering/Ocean/Log"
)
// Function with the setup of the logging device.
func ActivateLoggingDevice() {
Log.AddLoggingDevice(Console{})
}
+2
View File
@@ -5,9 +5,11 @@ import (
"github.com/SommerEngineering/Ocean/Log/Meta"
)
// The logging device.
type Console struct {
}
// This function is the interface between the logging system and the console logger.
func (dev Console) Log(entries []Meta.Entry) {
for _, entry := range entries {
fmt.Println(entry.Format())
@@ -4,6 +4,7 @@ import (
"github.com/SommerEngineering/Ocean/Log"
)
// Function with the setup of the logging device.
func ActivateLoggingDevice() {
Log.AddLoggingDevice(Database{})
}
+18
View File
@@ -0,0 +1,18 @@
package DeviceDatabase
// Function to check if the cache is full. If so, write all events to the database.
func cacheFull() {
mutexCacheFull.Lock()
defer mutexCacheFull.Unlock()
// Is the cache full?
if len(cache) < cacheSizeNumberOfEvents {
// Case: Cache is not full.
return
}
// Case: The cache is full. Write all events to the database.
for counter := 0; counter < cacheSizeNumberOfEvents; counter++ {
write2Database(<-cache)
}
}
@@ -0,0 +1,37 @@
package DeviceDatabase
import (
"github.com/SommerEngineering/Ocean/Log"
LM "github.com/SommerEngineering/Ocean/Log/Meta"
"github.com/SommerEngineering/Ocean/Shutdown"
"time"
)
// Function for the thread which maintain the message name cache.
func cacheRefreshMessageNames() {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `The message names' refresh thread is now running.`)
// Create an own thread:
go func() {
// Endless loop:
for true {
// Read the message names rom the DB:
data := readMessageNamesFromDB()
mutexCacheMessageNames.Lock()
// Overwrite the cache:
cacheMessageNames = data
mutexCacheMessageNames.Unlock()
// Sleep some time:
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelTALKATIVE, LM.MessageNameEXECUTE, `The message names' cache was refreshed.`)
time.Sleep(time.Duration(nameCachesRefreshTimeSeconds) * time.Second)
// Case: The server goes down now.
if Shutdown.IsDown() {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityLow, LM.ImpactLow, LM.MessageNameSHUTDOWN, `The message name's refresh thread is now shutting down.`)
return
}
}
}()
}
@@ -0,0 +1,38 @@
package DeviceDatabase
import (
"github.com/SommerEngineering/Ocean/Log"
LM "github.com/SommerEngineering/Ocean/Log/Meta"
"github.com/SommerEngineering/Ocean/Shutdown"
"time"
)
// Function for the thread which maintain the sender name cache.
func cacheRefreshSenderNames() {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `The sender names' refresh thread is now running.`)
// Use an extra thread:
go func() {
// Endless lopp:
for true {
// Read the sender names from the DB:
data := readSenderNamesFromDB()
mutexCacheSenderNames.Lock()
// Overwrite the cache:
cacheSenderNames = data
mutexCacheSenderNames.Unlock()
// Sleep some time:
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelTALKATIVE, LM.MessageNameEXECUTE, `The sender names' cache was refreshed.`)
time.Sleep(time.Duration(nameCachesRefreshTimeSeconds) * time.Second)
// Case: The server is going down now.
if Shutdown.IsDown() {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityLow, LM.ImpactLow, LM.MessageNameSHUTDOWN, `The sender name's refresh thread is now shutting down.`)
return
}
}
}()
}
+2
View File
@@ -1,5 +1,6 @@
package DeviceDatabase
// Flush the cache and write all messages to the database.
func (dev Database) Flush() {
mutexCacheFull.Lock()
defer mutexCacheFull.Unlock()
@@ -9,6 +10,7 @@ func (dev Database) Flush() {
write2Database(<-cache)
}
// Shutdown the database connection:
logDB.Logout()
logDBSession.Close()
}
+3 -1
View File
@@ -4,8 +4,9 @@ import (
"github.com/SommerEngineering/Ocean/Log/Meta"
)
// Function to format a logging database entry as string.
func (entry LogDBEntry) Format() (result string) {
// First, we convert the logging db entry to the common logging type:
converted := Meta.Entry{}
converted.Time = entry.TimeUTC
converted.Project = entry.Project
@@ -18,6 +19,7 @@ func (entry LogDBEntry) Format() (result string) {
converted.MessageDescription = entry.MessageDescription
converted.Parameters = entry.Parameters
// Second, we can use then the format operation of these type:
result = converted.Format()
return
}
+15 -1
View File
@@ -8,12 +8,19 @@ import (
"strconv"
)
// The init function for this package.
func init() {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `Starting now the database logging.`)
defer Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `Starting the database logging done.`)
// Init the database first:
initDatabase()
//
// Read all configuration values:
//
if value, err := strconv.Atoi(ConfigurationDB.Read(`LogDBCacheSizeNumberOfEvents`)); err != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityHigh, LM.ImpactUnknown, LM.MessageNameCONFIGURATION, `It was not possible to read the LogDBCacheSizeNumberOfEvents configuration.`, `The default value will be used.`, fmt.Sprintf(`Default value is %d.`, cacheSizeNumberOfEvents))
} else {
@@ -35,8 +42,15 @@ func init() {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `Configuration LogDBWebInterfaceNameCacheRefreshTimeSeconds was loaded.`, fmt.Sprintf(`The value is %d.`, nameCachesRefreshTimeSeconds))
}
// Create the cache:
cache = make(chan LogDBEntry, cacheSizeNumberOfEvents)
initTimeout()
// Starts a thread to write events based on time-outs:
startTimeout()
// Starts a thread to refresh the sender name cache:
cacheRefreshSenderNames()
// Starts a thread to refresh the message name cache:
cacheRefreshMessageNames()
}
+10
View File
@@ -12,19 +12,26 @@ import (
"time"
)
// Init the database for the logging.
func initDatabase() {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Checking and init the logging database collection.`)
defer Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Checking and init the logging database collection done.`)
// Read the configuration values for the logging database:
databaseHost := ConfigurationDB.Read(`LogDBHost`)
databaseDB := ConfigurationDB.Read(`LogDBDatabase`)
databaseUsername := ConfigurationDB.Read(`LogDBUsername`)
databasePassword := ConfigurationDB.Read(`LogDBPassword`)
// Should the logging events at the database expire?
expire := strings.ToLower(ConfigurationDB.Read(`LogDBEventsExpire`)) == `true`
// The default values for the TTL (time to live):
expireAfterDays := 21900 // 60 years ~ maximum of MongoDB
expireValue4DisabledFunction := 21900 // 60 years ~ maximum of MongoDB
// Try to read the configured value for the TTL:
if value, errValue := strconv.Atoi(ConfigurationDB.Read(`LogDBEventsExpireAfterDays`)); errValue != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityMiddle, LM.ImpactUnknown, LM.MessageNameCONFIGURATION, `It was not possible to read the configuration for the expire time of logging events. Log events will not expire any more.`, errValue.Error())
expire = false
@@ -86,6 +93,9 @@ func initDatabase() {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNameDATABASE, fmt.Sprintf(`Update the expire policy for the logging database done.`))
}
//
// Ensure that all necessary indexes are existing:
//
indexProject := mgo.Index{}
indexProject.Key = []string{`Project`}
logDBCollection.EnsureIndex(indexProject)
+7 -1
View File
@@ -7,21 +7,27 @@ import (
func ReadCustom(timeRange, logLevel, logCategory, logImpact, logSeverity, logMessageName, logSender, logPage string) (events []LogDBEntry) {
//
// TODO => Is currently stub
// TODO => Is currently a stub
//
// Define the query:
query := logDBCollection.Find(bson.D{}).Sort(`-TimeUTC`).Limit(26)
count := 26
// Execute the query and count the results:
if n, err := query.Count(); err == nil {
count = n
}
// The iterator for the results:
iter := query.Iter()
entry := LogDBEntry{}
pos := 0
// Reserve the memory for the results:
events = make([]LogDBEntry, count)
// Loop over all entries and store it:
for iter.Next(&entry) {
events[pos] = entry
pos++
+7 -1
View File
@@ -4,20 +4,26 @@ import (
"gopkg.in/mgo.v2/bson"
)
// Read the latest logging events from the database.
func ReadLatest() (events []LogDBEntry) {
// Define the query:
query := logDBCollection.Find(bson.D{}).Sort(`-TimeUTC`).Limit(26)
count := 26
// Execute the query and count the results:
if n, err := query.Count(); err == nil {
count = n
}
// The iterator for the results:
iter := query.Iter()
entry := LogDBEntry{}
pos := 0
// Reserve the memory for the results:
events = make([]LogDBEntry, count)
// Loop over all entries and store it:
for iter.Next(&entry) {
events[pos] = entry
pos++
+1 -43
View File
@@ -1,51 +1,9 @@
package DeviceDatabase
import (
"github.com/SommerEngineering/Ocean/Log"
LM "github.com/SommerEngineering/Ocean/Log/Meta"
"github.com/SommerEngineering/Ocean/Shutdown"
"gopkg.in/mgo.v2/bson"
"sort"
"time"
)
// Read the message names out of the cache.
func ReadMessageNames() (messageNames []string) {
mutexCacheMessageNames.RLock()
defer mutexCacheMessageNames.RUnlock()
messageNames = cacheMessageNames
return
}
func cacheRefreshMessageNames() {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `The message names' refresh thread is now running.`)
go func() {
for true {
data := readMessageNamesFromDB()
mutexCacheMessageNames.Lock()
cacheMessageNames = data
mutexCacheMessageNames.Unlock()
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelTALKATIVE, LM.MessageNameEXECUTE, `The message names' cache was refreshed.`)
time.Sleep(time.Duration(nameCachesRefreshTimeSeconds) * time.Second)
if Shutdown.IsDown() {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityLow, LM.ImpactLow, LM.MessageNameSHUTDOWN, `The message name's refresh thread is now shutting down.`)
return
}
}
}()
}
func readMessageNamesFromDB() (result []string) {
var nextMessageNames []string
if err := logDBCollection.Find(bson.D{}).Distinct(`MessageName`, &nextMessageNames); err != nil {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.MessageNameDATABASE, `Was not able to read the message names from the database.`, err.Error())
return
}
sort.Strings(nextMessageNames)
result = nextMessageNames
return
}
@@ -0,0 +1,23 @@
package DeviceDatabase
import (
"github.com/SommerEngineering/Ocean/Log"
LM "github.com/SommerEngineering/Ocean/Log/Meta"
"gopkg.in/mgo.v2/bson"
"sort"
)
// Read the message names from the database without any cache.
func readMessageNamesFromDB() (result []string) {
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:
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.MessageNameDATABASE, `Was not able to read the message names from the database.`, err.Error())
return
}
// Sort the sender names:
sort.Strings(nextMessageNames)
result = nextMessageNames
return
}
+1 -43
View File
@@ -1,51 +1,9 @@
package DeviceDatabase
import (
"github.com/SommerEngineering/Ocean/Log"
LM "github.com/SommerEngineering/Ocean/Log/Meta"
"github.com/SommerEngineering/Ocean/Shutdown"
"gopkg.in/mgo.v2/bson"
"sort"
"time"
)
// Read the sender names out of the cache.
func ReadSenderNames() (senderNames []string) {
mutexCacheSenderNames.RLock()
defer mutexCacheSenderNames.RUnlock()
senderNames = cacheSenderNames
return
}
func cacheRefreshSenderNames() {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `The sender names' refresh thread is now running.`)
go func() {
for true {
data := readSenderNamesFromDB()
mutexCacheSenderNames.Lock()
cacheSenderNames = data
mutexCacheSenderNames.Unlock()
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelTALKATIVE, LM.MessageNameEXECUTE, `The sender names' cache was refreshed.`)
time.Sleep(time.Duration(nameCachesRefreshTimeSeconds) * time.Second)
if Shutdown.IsDown() {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityLow, LM.ImpactLow, LM.MessageNameSHUTDOWN, `The sender name's refresh thread is now shutting down.`)
return
}
}
}()
}
func readSenderNamesFromDB() (result []string) {
var nextSenderNames []string
if err := logDBCollection.Find(bson.D{}).Distinct(`Sender`, &nextSenderNames); err != nil {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.MessageNameDATABASE, `Was not able to read the sender names from the database.`, err.Error())
return
}
sort.Strings(nextSenderNames)
result = nextSenderNames
return
}
@@ -0,0 +1,23 @@
package DeviceDatabase
import (
"github.com/SommerEngineering/Ocean/Log"
LM "github.com/SommerEngineering/Ocean/Log/Meta"
"gopkg.in/mgo.v2/bson"
"sort"
)
// Reads the sender names from the database without any caching.
func readSenderNamesFromDB() (result []string) {
var nextSenderNames []string
if err := logDBCollection.Find(bson.D{}).Distinct(`Sender`, &nextSenderNames); err != nil {
// Case: Was not possible to write to the database.
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.MessageNameDATABASE, `Was not able to read the sender names from the database.`, err.Error())
return
}
// Sort the sender names:
sort.Strings(nextSenderNames)
result = nextSenderNames
return
}
+2 -3
View File
@@ -4,14 +4,13 @@ import (
"github.com/SommerEngineering/Ocean/Log/Meta"
)
type Database struct {
}
// This function is the interface between the logging system and the database logger.
func (dev Database) Log(entries []Meta.Entry) {
//
// Cannot log here to prevent endless loop (consumer is also producer)
//
// Write every incoming batch to the cache:
write2Cache(entries)
}
+6
View File
@@ -4,6 +4,7 @@ import (
"time"
)
// The type for the database logging.
type LogDBEntry struct {
TimeUTC time.Time `bson:"TimeUTC"`
Project string `bson:"Project"`
@@ -17,8 +18,13 @@ type LogDBEntry struct {
Parameters []string `bson:"Parameters"`
}
// A type for the TTL (time to live) for the index.
type TTLUpdateResult struct {
ExpireAfterSeconds_old int32 `bson:"expireAfterSeconds_old"`
ExpireAfterSeconds_new int32 `bson:"expireAfterSeconds_new"`
Ok int32 `bson:"ok"`
}
// The logging device.
type Database struct {
}
@@ -1,41 +1,32 @@
package DeviceDatabase
import (
"github.com/SommerEngineering/Ocean/Shutdown"
"time"
)
// Case: The cache is full
func cacheFull() {
mutexCacheFull.Lock()
defer mutexCacheFull.Unlock()
if len(cache) < cacheSizeNumberOfEvents {
return
}
for counter := 0; counter < cacheSizeNumberOfEvents; counter++ {
write2Database(<-cache)
}
}
// Case: Time out
func initTimeout() {
go func() {
for {
if Shutdown.IsDown() {
return
}
time.Sleep(time.Duration(cacheSizeTime2FlushSeconds) * time.Second)
mutexCacheFull.Lock()
amount := len(cache)
for counter := 0; counter < amount; counter++ {
write2Database(<-cache)
}
mutexCacheFull.Unlock()
}
}()
}
package DeviceDatabase
import (
"github.com/SommerEngineering/Ocean/Shutdown"
"time"
)
// The timeout function writes the logging events to the database afer some time.
func startTimeout() {
// Starts a new thread:
go func() {
for {
// Case: The system goes down now.
if Shutdown.IsDown() {
return
}
// Wait for the right time:
time.Sleep(time.Duration(cacheSizeTime2FlushSeconds) * time.Second)
// Write the events to the database:
mutexCacheFull.Lock()
amount := len(cache)
for counter := 0; counter < amount; counter++ {
write2Database(<-cache)
}
mutexCacheFull.Unlock()
}
}()
}
+1
View File
@@ -7,6 +7,7 @@ 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{}
+7
View File
@@ -4,12 +4,17 @@ import (
"github.com/SommerEngineering/Ocean/Log/Meta"
)
// This function writes a batch of log entries to the cache.
func write2Cache(entries []Meta.Entry) {
// Loop over each entry:
for _, entry := range entries {
// If the cache is full, execute it:
if len(cache) == cacheSizeNumberOfEvents {
// Execute the cache with a new thread:
go cacheFull()
}
// Convert the log entry to the database format:
logDBentry := LogDBEntry{}
logDBentry.Category = entry.Category.Format()
logDBentry.Impact = entry.Impact.Format()
@@ -21,6 +26,8 @@ func write2Cache(entries []Meta.Entry) {
logDBentry.Sender = string(entry.Sender)
logDBentry.Severity = entry.Severity.Format()
logDBentry.TimeUTC = entry.Time
// Write it to the cache:
cache <- logDBentry
}
}
+9 -1
View File
@@ -1,7 +1,15 @@
package DeviceDatabase
import (
"fmt"
)
// Function to write a logging event to the database.
func write2Database(entry LogDBEntry) {
// Try to write the event to the database:
if err := logDBCollection.Insert(entry); err != nil {
// Can not log here to prevent endless loop (consumer is also producer)
// Case: Error!
// Cannot log here to prevent endless loop (consumer is also producer)
fmt.Printf("Was not able to write a logging event to the database: '%s'. The log entry was: '%s'.\n", err.Error(), entry.Format())
}
}
+16 -3
View File
@@ -5,18 +5,18 @@ import (
"github.com/SommerEngineering/Ocean/Log/Meta"
)
// Queue a log event before delivery to the devices.
func deviceDelay(newEntry Meta.Entry) {
defer checkDeviceDelaySize()
// Insert the new entry at the correct position (time)!
// Insert the new entry at the correct position (time).
// To ensure that the causality is guaranteed.
for logEvent := deviceDelayBuffer.Front(); logEvent != nil; logEvent = logEvent.Next() {
currentEvent := logEvent.Value.(Meta.Entry)
if newEntry.Time.Before(currentEvent.Time) {
mutexDeviceDelays.Lock()
deviceDelayBuffer.InsertBefore(newEntry, logEvent)
mutexDeviceDelays.Unlock()
return
}
}
@@ -27,19 +27,32 @@ func deviceDelay(newEntry Meta.Entry) {
mutexDeviceDelays.Unlock()
}
// Check if the size of the buffer is huge enough to deliver entries.
func checkDeviceDelaySize() {
// Get exklusive access:
mutexDeviceDelays.Lock()
// Is the size huge enough?
if deviceDelayBuffer.Len() >= logDeviceDelayNumberEvents {
// Read all entries:
dataArray := logEntryListToArray(deviceDelayBuffer)
// Re-init the buffer:
deviceDelayBuffer.Init()
// Loop over all devices:
mutexDevices.RLock()
for entry := devices.Front(); entry != nil; entry = entry.Next() {
dev := entry.Value.(Device.Device)
// Deliver the data with a new thread:
go dev.Log(dataArray)
}
mutexDevices.RUnlock()
}
// Release the lock:
mutexDeviceDelays.Unlock()
}
+6 -3
View File
@@ -4,22 +4,25 @@ import (
"github.com/SommerEngineering/Ocean/Log/Device"
)
/*
Please do not call this function your self! This function allows Ocean to flush the logging at the shutting down case.
*/
// Function to force all buffers to flush the events.
func Flush() {
// Close the entry buffer:
mutexChannel.Lock()
channelReady = false
close(entriesBuffer)
mutexChannel.Unlock()
// Wait that the scheduler is done:
<-schedulerExitSignal
// Get all log entries from the device delay buffer:
mutexDeviceDelays.Lock()
dataArray := logEntryListToArray(deviceDelayBuffer)
deviceDelayBuffer.Init()
mutexDeviceDelays.Unlock()
// Deliver all the events to all devices:
mutexDevices.RLock()
for entry := devices.Front(); entry != nil; entry = entry.Next() {
dev := entry.Value.(Device.Device)
+5 -2
View File
@@ -7,12 +7,12 @@ import (
"time"
)
// Writes a log message to the channel.
func writeToChannel(logEntry Meta.Entry) {
select {
case entriesBuffer <- logEntry:
case <-time.After(time.Duration(int64(logBufferTimeoutSeconds)) * time.Second):
// Warn: Can not log here to prevent endless loop and memory leak!
// Warn: Cannot log here to prevent endless loop and memory leak!
fmt.Println(`Warning: Was not able to write to the logging buffer! Message=` + logEntry.Format())
}
}
@@ -110,6 +110,7 @@ func LogShort(sender Meta.Sender, category Meta.Category, level Meta.Level, mess
TakeEntry(entry)
}
// Removes white spaces from the message.
func clearEntry(entry Meta.Entry) (result Meta.Entry) {
entry.MessageDescription = removeWhitespaces(entry.MessageDescription)
entry.Parameters = clearParameters(entry.Parameters)
@@ -117,6 +118,7 @@ func clearEntry(entry Meta.Entry) (result Meta.Entry) {
return
}
// Remove white spaces from the parameters.
func clearParameters(oldParameters []string) (result []string) {
for n := 0; n < len(oldParameters); n++ {
oldParameters[n] = removeWhitespaces(oldParameters[n])
@@ -126,6 +128,7 @@ func clearParameters(oldParameters []string) (result []string) {
return
}
// Removes white spaces from a string.
func removeWhitespaces(text string) (result string) {
text = strings.Replace(text, "\n", ` `, -1)
text = strings.Replace(text, "\t", ` `, -1)
+14 -27
View File
@@ -3,44 +3,29 @@ package Log
import (
"container/list"
"github.com/SommerEngineering/Ocean/Log/Meta"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
)
func readProjectName() {
if currentDir, dirError := os.Getwd(); dirError != nil {
panic(`Can not read the current working directory and therefore can not read the project name!`)
} else {
filename := filepath.Join(currentDir, `project.name`)
if _, errFile := os.Stat(filename); errFile != nil {
if os.IsNotExist(errFile) {
panic(`Can not read the project name file 'project.name': File not found!`)
} else {
panic(`Can not read the project name file 'project.name': ` + errFile.Error())
}
}
if projectNameBytes, errRead := ioutil.ReadFile(filename); errRead != nil {
panic(`Can not read the project name file 'project.name': ` + errRead.Error())
} else {
projectName = string(projectNameBytes)
projectName = strings.TrimSpace(projectName)
}
}
}
// Init the logging package.
func init() {
// Read the project name:
readProjectName()
// Create the mutexe:
mutexDeviceDelays = sync.Mutex{}
mutexPreChannelBuffer = sync.Mutex{}
mutexChannel = sync.RWMutex{}
// Create buffers:
preChannelBuffer = list.New()
deviceDelayBuffer = list.New()
// Create the device list:
devices = list.New()
// Channel to exit the scheduler:
schedulerExitSignal = make(chan bool)
initTimer()
@@ -48,8 +33,10 @@ func init() {
}
func initCode() {
// Creates the buffer for logging entries:
entriesBuffer = make(chan Meta.Entry, logBufferSize)
LogShort(senderName, Meta.CategorySYSTEM, Meta.LevelINFO, `Starting`, `The logger is now starting.`, `logBufferSize=`+strconv.Itoa(int(logBufferSize)), `logBufferTimeoutSeconds=`+strconv.Itoa(int(logBufferTimeoutSeconds)))
// Start the scheduler as new thread:
go scheduler(entriesBuffer)
}
+19 -18
View File
@@ -1,18 +1,19 @@
package Log
import (
"container/list"
"github.com/SommerEngineering/Ocean/Log/Meta"
)
func logEntryListToArray(data *list.List) (result []Meta.Entry) {
count := data.Len()
result = make([]Meta.Entry, count, count)
position := 0
for entry := data.Front(); entry != nil; entry = entry.Next() {
result[position] = entry.Value.(Meta.Entry)
position++
}
return
}
package Log
import (
"container/list"
"github.com/SommerEngineering/Ocean/Log/Meta"
)
// Converts a list with logging events to an array.
func logEntryListToArray(data *list.List) (result []Meta.Entry) {
count := data.Len()
result = make([]Meta.Entry, count, count)
position := 0
for entry := data.Front(); entry != nil; entry = entry.Next() {
result[position] = entry.Value.(Meta.Entry)
position++
}
return
}
+2 -1
View File
@@ -1,7 +1,8 @@
package Log
/*
This function is used just internal by Ocean. Please do not call this function by your self!
A function to change the state of the logging after the database is
accessible.
*/
func LoggingIsReady() {
channelReady = true
+6 -4
View File
@@ -3,12 +3,13 @@ package Meta
type Category byte
const (
CategoryBUSINESS = Category(iota)
CategorySYSTEM = Category(iota)
CategoryAPP = Category(iota)
CategoryUSER = Category(iota)
CategoryBUSINESS = Category(iota) // Business category
CategorySYSTEM = Category(iota) // System category
CategoryAPP = Category(iota) // Application category
CategoryUSER = Category(iota) // User category
)
// Formats a category as string.
func (cat Category) Format() (result string) {
switch cat {
case CategoryBUSINESS:
@@ -26,6 +27,7 @@ func (cat Category) Format() (result string) {
return
}
// Parse a category from a string.
func ParseCategory(cat string) (value Category) {
switch cat {
case `C:BUSINESS`:
+1
View File
@@ -4,6 +4,7 @@ import (
"time"
)
// Type for a log entry.
type Entry struct {
Project string
Time time.Time
+8 -2
View File
@@ -7,18 +7,22 @@ import (
"time"
)
// Formats a log entry as string.
func (entry Entry) Format() (result string) {
// Force the maximum length for a sender:
lenSender := len(entry.Sender)
if lenSender > 40 {
lenSender = 40
}
// Force the maximum length for the message name:
lenMessageName := len(entry.MessageName)
if lenMessageName > 26 {
lenMessageName = 26
}
// Force the maximum length for the project name:
lenProject := len(entry.Project)
if lenProject > 10 {
lenProject = 10
@@ -29,21 +33,23 @@ func (entry Entry) Format() (result string) {
messageDescription = strings.Replace(messageDescription, "\t", ` `, -1)
messageDescription = strings.Replace(messageDescription, "\r", ` `, -1)
// Format the basic fields of the log message:
result = fmt.Sprintf(` [■] P:%10s [■] %s [■] %10s [■] %11s [■] %10s [■] %10s [■] sender: %-40s [■] name: %-26s [■] %s [■]`, entry.Project[:lenProject], formatTime(entry.Time), entry.Category.Format(), entry.Level.Format(), entry.Severity.Format(), entry.Impact.Format(), entry.Sender[:lenSender], entry.MessageName[:lenMessageName], messageDescription)
// Formats the parameters:
for _, param := range entry.Parameters {
paramText := param
paramText = strings.Replace(paramText, "\n", ` `, -1)
paramText = strings.Replace(paramText, "\t", ` `, -1)
paramText = strings.Replace(paramText, "\r", ` `, -1)
result = fmt.Sprintf(`%s %s [■]`, result, paramText)
}
return
}
// Formats the given time as YYYYMMdd HHmmss.fff! This function is necessary
// to prevent a import cycle.
func formatTime(t1 time.Time) (result string) {
var year int = t1.Year()
var month int = int(t1.Month())
+8 -6
View File
@@ -3,14 +3,15 @@ package Meta
type Impact byte
const (
ImpactNone = Impact(iota)
ImpactLow = Impact(iota)
ImpactMiddle = Impact(iota)
ImpactHigh = Impact(iota)
ImpactCritical = Impact(iota)
ImpactUnknown = Impact(iota)
ImpactNone = Impact(iota) // None impact
ImpactLow = Impact(iota) // Low impact
ImpactMiddle = Impact(iota) // Middle impact
ImpactHigh = Impact(iota) // High impact
ImpactCritical = Impact(iota) // Critical impact
ImpactUnknown = Impact(iota) // Unknown impact
)
// Formats a impact as string.
func (pri Impact) Format() (result string) {
switch pri {
case ImpactCritical:
@@ -32,6 +33,7 @@ func (pri Impact) Format() (result string) {
return
}
// Parse a impact from a string.
func ParseImpact(pri string) (value Impact) {
switch pri {
case `I:CRITICAL`:
+8 -6
View File
@@ -3,14 +3,15 @@ package Meta
type Level byte
const (
LevelWARN = Level(iota)
LevelDEBUG = Level(iota)
LevelERROR = Level(iota)
LevelINFO = Level(iota)
LevelTALKATIVE = Level(iota)
LevelSECURITY = Level(iota)
LevelWARN = Level(iota) // Level: Warning
LevelDEBUG = Level(iota) // Level: Debug
LevelERROR = Level(iota) // Level: Error
LevelINFO = Level(iota) // Level: Information
LevelTALKATIVE = Level(iota) // Level: Talkative (even more events as debug)
LevelSECURITY = Level(iota) // Level: Security
)
// Formats a level as string.
func (lvl Level) Format() (result string) {
switch lvl {
case LevelDEBUG:
@@ -32,6 +33,7 @@ func (lvl Level) Format() (result string) {
return
}
// Parse a level from a string.
func ParseLevel(lvl string) (value Level) {
switch lvl {
case `L:DEBUG`:
+33 -32
View File
@@ -2,37 +2,38 @@ package Meta
type MessageName string
// Some pre-defined message names:
const (
MessageNameSTARTUP = `Startup`
MessageNameINIT = `Init`
MessageNameSHUTDOWN = `Shutdown`
MessageNameEXECUTE = `Execute`
MessageNameDATABASE = `Database`
MessageNameNETWORK = `Network`
MessageNameLOGIN = `Login`
MessageNameLOGOUT = `Logout`
MessageNameSESSION = `Session`
MessageNameTIMEOUT = `Timeout`
MessageNameFILESYSTEM = `Filesystem`
MessageNameCOMMUNICATION = `Communication`
MessageNameWRITE = `Write`
MessageNameREAD = `Read`
MessageNameALGORITHM = `Algorithm`
MessageNameCONFIGURATION = `Configuration`
MessageNameTIMER = `Timer`
MessageNameINPUT = `Input`
MessageNameOUTPUT = `Output`
MessageNameBROWSER = `Browser`
MessageNameSECURITY = `Security`
MessageNameNOTFOUND = `NotFound`
MessageNameANALYSIS = `Analysis`
MessageNameSTATE = `State`
MessageNameGENERATOR = `Generator`
MessageNamePRODUCER = `Producer`
MessageNameCONSUMER = `Consumer`
MessageNamePASSWORD = `Password`
MessageNamePARSE = `Parse`
MessageNameUSER = `User`
MessageNameREQUEST = `Request`
MessageNameRESPONSE = `Response`
MessageNameSTARTUP = `Startup` // e.g. the server startup
MessageNameINIT = `Init` // some kind of init event
MessageNameSHUTDOWN = `Shutdown` // some kind of shutdown event
MessageNameEXECUTE = `Execute` // some kind of execution context
MessageNameDATABASE = `Database` // events which are related to database issues
MessageNameNETWORK = `Network` // events which are related to network issues
MessageNameLOGIN = `Login` // some kind of login event
MessageNameLOGOUT = `Logout` // some kind of logout event
MessageNameSESSION = `Session` // some kind of session event
MessageNameTIMEOUT = `Timeout` // some kind of timeout event
MessageNameFILESYSTEM = `Filesystem` // events which are related to the filesystem
MessageNameCOMMUNICATION = `Communication` // events which are related to communication issues
MessageNameWRITE = `Write` // some kind of write event
MessageNameREAD = `Read` // some kind of read event
MessageNameALGORITHM = `Algorithm` // some kind of algorithm event
MessageNameCONFIGURATION = `Configuration` // some kind of configuration event
MessageNameTIMER = `Timer` // some kind of timer event
MessageNameINPUT = `Input` // some kind of input event
MessageNameOUTPUT = `Output` // some kind of output event
MessageNameBROWSER = `Browser` // some kind of browser event
MessageNameSECURITY = `Security` // some kind of security event
MessageNameNOTFOUND = `NotFound` // something was not found
MessageNameANALYSIS = `Analysis` // some kind of analysis event
MessageNameSTATE = `State` // some kind of state-related event
MessageNameGENERATOR = `Generator` // some kind of generator event
MessageNamePRODUCER = `Producer` // some kind of producer event
MessageNameCONSUMER = `Consumer` // some kind of consumer event
MessageNamePASSWORD = `Password` // some kind of password event
MessageNamePARSE = `Parse` // some kind of parser-related event
MessageNameUSER = `User` // some kind of user event
MessageNameREQUEST = `Request` // some kind of request-related event
MessageNameRESPONSE = `Response` // some kind of response-related event
)
+1
View File
@@ -1,3 +1,4 @@
package Meta
// Type for the sender name
type Sender string
+8 -6
View File
@@ -3,14 +3,15 @@ package Meta
type Severity byte
const (
SeverityNone = Severity(iota)
SeverityLow = Severity(iota)
SeverityMiddle = Severity(iota)
SeverityHigh = Severity(iota)
SeverityCritical = Severity(iota)
SeverityUnknown = Severity(iota)
SeverityNone = Severity(iota) // None severity
SeverityLow = Severity(iota) // Low severity
SeverityMiddle = Severity(iota) // Middle severity
SeverityHigh = Severity(iota) // High severity
SeverityCritical = Severity(iota) // Critical severity
SeverityUnknown = Severity(iota) // Unknown severity
)
// Format the severity as string.
func (pri Severity) Format() (result string) {
switch pri {
case SeverityCritical:
@@ -32,6 +33,7 @@ func (pri Severity) Format() (result string) {
return
}
// Parse the severity from a string.
func ParseSeverity(pri string) (value Severity) {
switch pri {
case `S:CRITICAL`:
+38
View File
@@ -0,0 +1,38 @@
package Log
import (
"io/ioutil"
"os"
"path/filepath"
"strings"
)
// Read the project name out of the local configuration file "project.name".
func readProjectName() {
// Try to get access to the working directory:
if currentDir, dirError := os.Getwd(); dirError != nil {
// Case: Error! Stop the server.
panic(`Cannot read the current working directory and therefore cannot read the project name!`)
} else {
// Try to get access to the file:
filename := filepath.Join(currentDir, `project.name`)
if _, errFile := os.Stat(filename); errFile != nil {
// Cases: Error.
if os.IsNotExist(errFile) {
panic(`Cannot read the project name file 'project.name': File not found!`)
} else {
panic(`Cannot read the project name file 'project.name': ` + errFile.Error())
}
}
// Try to read the file:
if projectNameBytes, errRead := ioutil.ReadFile(filename); errRead != nil {
// Case: Error.
panic(`Cannot read the project name file 'project.name': ` + errRead.Error())
} else {
// Store the project name:
projectName = string(projectNameBytes)
projectName = strings.TrimSpace(projectName)
}
}
}
+13 -2
View File
@@ -5,21 +5,30 @@ import (
"time"
)
// Note: The scheduler is the consumer for the logging channel!
/*
The scheduler function which runs at a own thread.
Pleae note: The scheduler is the consumer for the logging channel.
*/
func scheduler(logBuffer chan Meta.Entry) {
LogShort(senderName, Meta.CategorySYSTEM, Meta.LevelINFO, Meta.MessageNameSTARTUP, `The scheduler runs now.`)
var stopNextTime = false
// Endless loop:
for {
// Enable the loop to stop:
if stopNextTime {
break
}
// Read one entry from the buffer (channel):
nextEntry, ok := <-logBuffer
// Case: The channel was closed.
if !ok {
// The channel was closed!
// Create a log message for this event.
stopNextTime = true
nextEntry = Meta.Entry{}
nextEntry.Project = projectName
@@ -33,9 +42,11 @@ func scheduler(logBuffer chan Meta.Entry) {
nextEntry.MessageDescription = `The logging channel was closed!`
}
// Queue the log event for the delivery to the devices:
deviceDelay(nextEntry)
}
// Exit the scheduler. Send the signal.
LogFull(senderName, Meta.CategorySYSTEM, Meta.LevelWARN, Meta.SeverityCritical, Meta.ImpactNone, Meta.MessageNameSHUTDOWN, `The scheduler is down now.`)
schedulerExitSignal <- true
}
+19 -1
View File
@@ -9,6 +9,7 @@ import (
func initTimer() {
// Ensure that the timer runs only once:
if timerIsRunning == true {
LogFull(senderName, Meta.CategorySYSTEM, Meta.LevelWARN, Meta.SeverityHigh, Meta.ImpactNone, Meta.MessageNameSTARTUP, `The logging timer is already running.`)
return
@@ -17,23 +18,40 @@ func initTimer() {
timerIsRunning = true
LogShort(senderName, Meta.CategorySYSTEM, Meta.LevelINFO, Meta.MessageNameSTARTUP, `Create the logging timer now.`, fmt.Sprintf(`Timeout=%d seconds`, logDeviceDelayTimeoutSeconds))
// Start the timer at a own thread:
go func() {
// An endless loop:
for {
// Wait for the next run time:
time.Sleep(time.Duration(logDeviceDelayTimeoutSeconds) * time.Second)
// Get exklusive access to the buffer:
mutexDeviceDelays.Lock()
// Read all the data from the device delay buffer:
dataArray := logEntryListToArray(deviceDelayBuffer)
// Re-init the buffer:
deviceDelayBuffer.Init()
// Release the lock to the buffer:
mutexDeviceDelays.Unlock()
// Read-lock to read the devices list:
mutexDevices.RLock()
// For each logging device:
for entry := devices.Front(); entry != nil; entry = entry.Next() {
dev := entry.Value.(Device.Device)
// Deliver the current logging events with an extra thread:
go dev.Log(dataArray)
}
mutexDevices.RUnlock()
// Release the read-lock:
mutexDevices.RUnlock()
}
}()
}
+18 -18
View File
@@ -7,22 +7,22 @@ import (
)
var (
entriesBuffer chan Meta.Entry = nil
schedulerExitSignal chan bool = nil
logBufferSize int = 500
logBufferTimeoutSeconds int = 4
logDeviceDelayNumberEvents int = 600
logDeviceDelayTimeoutSeconds int = 5
channelReady bool = false
preChannelBufferUsed bool = false
preChannelBuffer *list.List = nil
deviceDelayBuffer *list.List = nil
devices *list.List = nil
mutexDeviceDelays sync.Mutex = sync.Mutex{}
mutexPreChannelBuffer sync.Mutex = sync.Mutex{}
mutexChannel sync.RWMutex = sync.RWMutex{}
mutexDevices sync.RWMutex = sync.RWMutex{}
timerIsRunning bool = false
projectName string = `not set`
senderName Meta.Sender = `System::Log`
entriesBuffer chan Meta.Entry = nil // The channel / buffer for new log entries
schedulerExitSignal chan bool = nil // Exit signal for the scheduler
logBufferSize int = 500 // Buffer size for the logging
logBufferTimeoutSeconds int = 4 // Timeout for the logging
logDeviceDelayNumberEvents int = 600 // Delay of # of events for the devices
logDeviceDelayTimeoutSeconds int = 5 // Timeout for the logging devices
channelReady bool = false // State of the channel
preChannelBufferUsed bool = false // State of the logging (pre or ready?)
preChannelBuffer *list.List = nil // Extra buffer for the pre logging phase
deviceDelayBuffer *list.List = nil // Buffer for the batch write to the devices
devices *list.List = nil // List of all devices
mutexDeviceDelays sync.Mutex = sync.Mutex{} // Mutex for buffer
mutexPreChannelBuffer sync.Mutex = sync.Mutex{} // Mutex for buffer
mutexChannel sync.RWMutex = sync.RWMutex{} // Mutex for the main channel
mutexDevices sync.RWMutex = sync.RWMutex{} // Mutex for the devices
timerIsRunning bool = false // Status of timer
projectName string = `not set` // The project name for the logging
senderName Meta.Sender = `System::Log` // This is the name for logging event from this package
)
+7
View File
@@ -8,6 +8,7 @@ import (
"net/http"
)
// Handler for some CSS data for the web logger.
func HandlerCSSNormalize(response http.ResponseWriter, request *http.Request) {
if Shutdown.IsDown() {
@@ -19,6 +20,7 @@ func HandlerCSSNormalize(response http.ResponseWriter, request *http.Request) {
fmt.Fprint(response, Assets.CSSNormalize)
}
// Handler for some CSS data for the web logger.
func HandlerCSSWebflow(response http.ResponseWriter, request *http.Request) {
if Shutdown.IsDown() {
@@ -30,6 +32,7 @@ func HandlerCSSWebflow(response http.ResponseWriter, request *http.Request) {
fmt.Fprint(response, Assets.CSSWebflow)
}
// Handler for some CSS data for the web logger.
func HandlerCSSLog(response http.ResponseWriter, request *http.Request) {
if Shutdown.IsDown() {
@@ -41,6 +44,7 @@ func HandlerCSSLog(response http.ResponseWriter, request *http.Request) {
fmt.Fprint(response, Assets.CSSLog)
}
// Handler for some JS for the web logger.
func HandlerJSModernizr(response http.ResponseWriter, request *http.Request) {
if Shutdown.IsDown() {
@@ -52,6 +56,7 @@ func HandlerJSModernizr(response http.ResponseWriter, request *http.Request) {
fmt.Fprint(response, Assets.JSModernizr)
}
// Handler for some JS for the web logger.
func HandlerJSWebflow(response http.ResponseWriter, request *http.Request) {
if Shutdown.IsDown() {
@@ -63,6 +68,7 @@ func HandlerJSWebflow(response http.ResponseWriter, request *http.Request) {
fmt.Fprint(response, Assets.JSWebflow)
}
// Handler for some JS for the web logger.
func HandlerJSjQuery(response http.ResponseWriter, request *http.Request) {
if Shutdown.IsDown() {
@@ -74,6 +80,7 @@ func HandlerJSjQuery(response http.ResponseWriter, request *http.Request) {
fmt.Fprint(response, Assets.JSjQuery)
}
// Handler for some JS for the web logger.
func HandlerJSjQueryMap(response http.ResponseWriter, request *http.Request) {
if Shutdown.IsDown() {
+8 -4
View File
@@ -11,30 +11,32 @@ import (
"strings"
)
// Handler for accessing the web logging.
func HandlerWebLog(response http.ResponseWriter, request *http.Request) {
// Case: The system goes down now.
if Shutdown.IsDown() {
http.NotFound(response, request)
return
}
// Execute the HTTP form:
request.ParseForm()
countParameters := len(request.Form)
// Setup the data for the HTML template:
data := Scheme.Viewer{}
data.Title = `Web Log Viewer`
data.Sender = DeviceDatabase.ReadSenderNames()
data.MessageNames = DeviceDatabase.ReadMessageNames()
// To less parameters?
if countParameters < 9 {
// Initial view => refresh & first page (latest logs)
data.Events = readLatest()
data.SetLiveView = true
} else {
// Custom view
// Case: Custom view
currentLevel := request.FormValue(`Level`)
currentTimeRange := request.FormValue(`timeRange`)
currentCategory := request.FormValue(`Category`)
@@ -45,6 +47,7 @@ func HandlerWebLog(response http.ResponseWriter, request *http.Request) {
currentPage := request.FormValue(`CurrentPage`)
currentLiveView := request.FormValue(`LiveView`)
// Store the events for the template:
data.Events = readCustom(currentTimeRange, currentLevel, currentCategory, currentImpact, currentSeverity, currentMessageName, currentSender, currentPage)
if strings.ToLower(currentLiveView) == `true` {
@@ -94,6 +97,7 @@ func HandlerWebLog(response http.ResponseWriter, request *http.Request) {
}
}
// Write the MIME type and execute the template:
MimeTypes.Write2HTTP(response, MimeTypes.TypeWebHTML)
if executeError := templates.ExecuteTemplate(response, `WebLog`, data); executeError != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameEXECUTE, `Was not able to execute the web log viewer template.`, executeError.Error())
+2
View File
@@ -7,11 +7,13 @@ import (
"html/template"
)
// The init function for this package.
func init() {
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Init the web log.`)
defer Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Init the web log done.`)
// Create the cache of all web logging templates:
templates = template.New(`root`)
if _, err := templates.Parse(WebTemp.Viewer); err != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNamePARSE, `Was not able to parse the template for the web log viewer.`, err.Error())
+6
View File
@@ -7,18 +7,24 @@ import (
"strings"
)
// Read a custom event range from the database.
func readCustom(timeRange, logLevel, logCategory, logImpact, logSeverity, logMessageName, logSender, logPage string) (events []Scheme.LogEvent) {
// Get the custom events:
eventsFromDB := DeviceDatabase.ReadCustom(timeRange, logLevel, logCategory, logImpact, logSeverity, logMessageName, logSender, logPage)
count := len(eventsFromDB)
// Array with all events, prepared for the website:
events = make([]Scheme.LogEvent, count)
// Copy each event to the right format:
for n := 0; n < count; n++ {
eventFromDB := eventsFromDB[n]
events[n] = Scheme.LogEvent{}
events[n].LogLine = eventFromDB.Format()
events[n].LogLevel = fmt.Sprintf("log%s", strings.ToLower(eventFromDB.Level[2:]))
// Vary the color of each line:
if n%2 == 0 {
events[n].AB = Scheme.B
} else {
+6 -1
View File
@@ -7,18 +7,23 @@ import (
"strings"
)
// Read the latest log events from the database
func readLatest() (events []Scheme.LogEvent) {
// Get the latest events from the database
eventsFromDB := DeviceDatabase.ReadLatest()
count := len(eventsFromDB)
// Array for the log events, prepared for the website:
events = make([]Scheme.LogEvent, count)
// Copy each event to the right format for the website:
for n := 0; n < count; n++ {
eventFromDB := eventsFromDB[n]
events[n] = Scheme.LogEvent{}
events[n].LogLine = eventFromDB.Format()
events[n].LogLevel = fmt.Sprintf("log%s", strings.ToLower(eventFromDB.Level[2:]))
// Vary the color of each line:
if n%2 == 0 {
events[n].AB = Scheme.B
} else {
+2 -2
View File
@@ -1,6 +1,6 @@
package Scheme
const (
A string = `loga`
B string = `logb`
A string = `loga` // Web log line, kind A (different color for each line)
B string = `logb` // Web log line, kind B (different color for each line)
)
+2 -6
View File
@@ -1,5 +1,6 @@
package Scheme
// The type for the web logger viewer template
type Viewer struct {
Title string
SetLiveView bool
@@ -16,12 +17,7 @@ type Viewer struct {
Events []LogEvent
}
// <li class="logline loga logwarn">
// <div>....</div>
// </li>
// <li class="logline logb logwarn">
// <div>....</div>
// </li>
// Type for a log event
type LogEvent struct {
LogLine string
LogLevel string // logwarn || logdebug || logerror || loginfo || logtalkative || logsecurity
+1
View File
@@ -1,5 +1,6 @@
package Templates
// The template for the web log viewer:
var Viewer string = `
{{define "WebLog"}}
<!DOCTYPE html>
+2 -2
View File
@@ -6,6 +6,6 @@ import (
)
var (
templates *template.Template = nil
senderName LM.Sender = `System::WebLog`
templates *template.Template = nil // The web logging templates
senderName LM.Sender = `System::WebLog` // This is the name for logging event from this package
)