Add documentation
This commit is contained in:
1 parent
69d0bdabf4
commit
6dab89a1d4
144 files changed
+1107
-549
No files matched your search
@@ -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{})
|
||||
}
|
||||
@@ -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{})
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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,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++
|
||||
|
||||
@@ -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,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,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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -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{}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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`:
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Type for a log entry.
|
||||
type Entry struct {
|
||||
Project string
|
||||
Time time.Time
|
||||
|
||||
+8
-2
@@ -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
@@ -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
@@ -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
@@ -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,3 +1,4 @@
|
||||
package Meta
|
||||
|
||||
// Type for the sender name
|
||||
type Sender string
|
||||
@@ -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`:
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
)
|
||||
@@ -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() {
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
@@ -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,5 +1,6 @@
|
||||
package Templates
|
||||
|
||||
// The template for the web log viewer:
|
||||
var Viewer string = `
|
||||
{{define "WebLog"}}
|
||||
<!DOCTYPE html>
|
||||
|
||||
@@ -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
|
||||
)
|
||||
Reference in new issue
Block a user