Initial commit of Ocean's local development

This commit is contained in:
Thorsten Sommer committed 2014-04-26 11:18:56 +02:00
1 parent 9944a9e1df
commit 86451938ec
116 files changed
+3320

No files matched your search

+19
View File
@@ -0,0 +1,19 @@
package Log
import "github.com/SommerEngineering/Ocean/Log/Device"
/*
Registering the logging devices. Normally, it is not necessary to call this function. To enable or disable a logging device,
please use the configuration database instead. But if you create your own logging device, let say a e-mail logger, then you
are able to use this function to activate your own logging device. It is save to use this function at any time and it is
thread-save ;-)
*/
func AddLoggingDevice(device Device.Device) {
newDevice := device
go func() {
mutexDevices.Lock()
devices.PushBack(newDevice)
mutexDevices.Unlock()
}()
}
+16
View File
@@ -0,0 +1,16 @@
package Log
import "container/list"
import "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
}
+8
View File
@@ -0,0 +1,8 @@
package Device
import "github.com/SommerEngineering/Ocean/Log/Meta"
type Device interface {
Log(logEntries []Meta.Entry)
Flush()
}
+4
View File
@@ -0,0 +1,4 @@
/*
This is the logging device interface you have to fulfill to be a logging device :)
*/
package Device
@@ -0,0 +1,7 @@
package DeviceConsole
import "github.com/SommerEngineering/Ocean/Log"
func ActivateLoggingDevice() {
Log.AddLoggingDevice(Console{})
}
+17
View File
@@ -0,0 +1,17 @@
package DeviceConsole
import "fmt"
import "github.com/SommerEngineering/Ocean/Log/Meta"
type Console struct {
}
func (dev Console) Log(entries []Meta.Entry) {
for _, entry := range entries {
fmt.Println(entry.Format())
}
}
func (dev Console) Flush() {
// This is not necessary for a console logger
}
+4
View File
@@ -0,0 +1,4 @@
/*
This is the console logging device. Any received entry is logged to the console.
*/
package DeviceConsole
@@ -0,0 +1,7 @@
package DeviceDatabase
import "github.com/SommerEngineering/Ocean/Log"
func ActivateLoggingDevice() {
Log.AddLoggingDevice(Database{})
}
+5
View File
@@ -0,0 +1,5 @@
/*
This is the database logging device. The logging events will be cached and after a time span or a maximum amount of
events, the logging entries are written to the database.
*/
package DeviceDatabase
+14
View File
@@ -0,0 +1,14 @@
package DeviceDatabase
func (dev Database) Flush() {
mutexCacheFull.Lock()
defer mutexCacheFull.Unlock()
amount := len(cache)
for counter := 0; counter < amount; counter++ {
write2Database(<-cache)
}
logDB.Logout()
logDBSession.Close()
}
+31
View File
@@ -0,0 +1,31 @@
package DeviceDatabase
import "strconv"
import "fmt"
import "github.com/SommerEngineering/Ocean/ConfigurationDB"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
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.`)
initDatabase()
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 {
cacheSizeNumberOfEvents = value
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `Configuration LogDBCacheSizeNumberOfEvents was loaded.`, fmt.Sprintf(`The value is %d.`, cacheSizeNumberOfEvents))
}
if value, err := strconv.Atoi(ConfigurationDB.Read(`LogDBCacheSizeTime2FlushSeconds`)); err != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityHigh, LM.ImpactUnknown, LM.MessageNameCONFIGURATION, `It was not possible to read the LogDBCacheSizeTime2FlushSeconds configuration.`, `The default value will be used.`, fmt.Sprintf(`Default value is %d.`, cacheSizeTime2FlushSeconds))
} else {
cacheSizeTime2FlushSeconds = value
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `Configuration LogDBCacheSizeTime2FlushSeconds was loaded.`, fmt.Sprintf(`The value is %d.`, cacheSizeTime2FlushSeconds))
}
cache = make(chan LogDBEntry, cacheSizeNumberOfEvents)
initTimeout()
}
+165
View File
@@ -0,0 +1,165 @@
package DeviceDatabase
import "labix.org/v2/mgo"
import "github.com/SommerEngineering/Ocean/ConfigurationDB"
import "github.com/SommerEngineering/Ocean/Log"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
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.`)
databaseHost := ConfigurationDB.Read(`LogDBHost`)
databaseDB := ConfigurationDB.Read(`LogDBDatabase`)
databaseUsername := ConfigurationDB.Read(`LogDBUsername`)
databasePassword := ConfigurationDB.Read(`LogDBPassword`)
// Connect to MongoDB:
if newSession, errDial := mgo.Dial(databaseHost); errDial != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNameDATABASE, `It was not possible to connect to the MongoDB host `+databaseHost, errDial.Error())
return
} else {
logDBSession = newSession
}
// Use the correct database:
logDB = logDBSession.DB(databaseDB)
// Login:
if errLogin := logDB.Login(databaseUsername, databasePassword); errLogin != nil {
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelSECURITY, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNameDATABASE, `It was not possible to login the user `+databaseUsername, errLogin.Error())
return
}
// Get the collection:
logDBCollection = logDB.C(`Logbook`)
//
// Take care about all the indexes:
//
indexTimeUTC := mgo.Index{}
indexTimeUTC.Key = []string{`TimeUTC`}
logDBCollection.EnsureIndex(indexTimeUTC)
indexProject := mgo.Index{}
indexProject.Key = []string{`Project`}
logDBCollection.EnsureIndex(indexProject)
indexSender := mgo.Index{}
indexSender.Key = []string{`Sender`}
logDBCollection.EnsureIndex(indexSender)
indexCategory := mgo.Index{}
indexCategory.Key = []string{`Category`}
logDBCollection.EnsureIndex(indexCategory)
indexLevel := mgo.Index{}
indexLevel.Key = []string{`Level`}
logDBCollection.EnsureIndex(indexLevel)
indexSeverity := mgo.Index{}
indexSeverity.Key = []string{`Severity`}
logDBCollection.EnsureIndex(indexSeverity)
indexImpact := mgo.Index{}
indexImpact.Key = []string{`Impact`}
logDBCollection.EnsureIndex(indexImpact)
indexMessageName := mgo.Index{}
indexMessageName.Key = []string{`MessageName`}
logDBCollection.EnsureIndex(indexMessageName)
indexMessageDescription := mgo.Index{}
indexMessageDescription.Key = []string{`MessageDescription`}
logDBCollection.EnsureIndex(indexMessageDescription)
indexProjectTimeUTC := mgo.Index{}
indexProjectTimeUTC.Key = []string{`Project`, `TimeUTC`}
logDBCollection.EnsureIndex(indexProjectTimeUTC)
indexProjectSender := mgo.Index{}
indexProjectSender.Key = []string{`Project`, `Sender`}
logDBCollection.EnsureIndex(indexProjectSender)
indexProjectCategory := mgo.Index{}
indexProjectCategory.Key = []string{`Project`, `Category`}
logDBCollection.EnsureIndex(indexProjectCategory)
indexProjectLevel := mgo.Index{}
indexProjectLevel.Key = []string{`Project`, `Level`}
logDBCollection.EnsureIndex(indexProjectLevel)
indexProjectSeverity := mgo.Index{}
indexProjectSeverity.Key = []string{`Project`, `Severity`}
logDBCollection.EnsureIndex(indexProjectSeverity)
indexProjectImpact := mgo.Index{}
indexProjectImpact.Key = []string{`Project`, `Impact`}
logDBCollection.EnsureIndex(indexProjectImpact)
indexProjectMessageName := mgo.Index{}
indexProjectMessageName.Key = []string{`Project`, `MessageName`}
logDBCollection.EnsureIndex(indexProjectMessageName)
indexProjectMessageDescription := mgo.Index{}
indexProjectMessageDescription.Key = []string{`Project`, `MessageDescription`}
logDBCollection.EnsureIndex(indexProjectMessageDescription)
indexProjectTimeUTCSender := mgo.Index{}
indexProjectTimeUTCSender.Key = []string{`Project`, `TimeUTC`, `Sender`}
logDBCollection.EnsureIndex(indexProjectTimeUTCSender)
indexProjectTimeUTCCategory := mgo.Index{}
indexProjectTimeUTCCategory.Key = []string{`Project`, `TimeUTC`, `Category`}
logDBCollection.EnsureIndex(indexProjectTimeUTCCategory)
indexProjectTimeUTCLevel := mgo.Index{}
indexProjectTimeUTCLevel.Key = []string{`Project`, `TimeUTC`, `Level`}
logDBCollection.EnsureIndex(indexProjectTimeUTCLevel)
indexProjectTimeUTCSeverity := mgo.Index{}
indexProjectTimeUTCSeverity.Key = []string{`Project`, `TimeUTC`, `Severity`}
logDBCollection.EnsureIndex(indexProjectTimeUTCSeverity)
indexProjectTimeUTCImpact := mgo.Index{}
indexProjectTimeUTCImpact.Key = []string{`Project`, `TimeUTC`, `Impact`}
logDBCollection.EnsureIndex(indexProjectTimeUTCImpact)
indexProjectTimeUTCMessageName := mgo.Index{}
indexProjectTimeUTCMessageName.Key = []string{`Project`, `TimeUTC`, `MessageName`}
logDBCollection.EnsureIndex(indexProjectTimeUTCMessageName)
indexProjectTimeUTCMessageDescription := mgo.Index{}
indexProjectTimeUTCMessageDescription.Key = []string{`Project`, `TimeUTC`, `MessageDescription`}
logDBCollection.EnsureIndex(indexProjectTimeUTCMessageDescription)
indexTimeUTCSender := mgo.Index{}
indexTimeUTCSender.Key = []string{`TimeUTC`, `Sender`}
logDBCollection.EnsureIndex(indexTimeUTCSender)
indexTimeUTCCategory := mgo.Index{}
indexTimeUTCCategory.Key = []string{`TimeUTC`, `Category`}
logDBCollection.EnsureIndex(indexTimeUTCCategory)
indexTimeUTCLevel := mgo.Index{}
indexTimeUTCLevel.Key = []string{`TimeUTC`, `Level`}
logDBCollection.EnsureIndex(indexTimeUTCLevel)
indexTimeUTCSeverity := mgo.Index{}
indexTimeUTCSeverity.Key = []string{`TimeUTC`, `Severity`}
logDBCollection.EnsureIndex(indexTimeUTCSeverity)
indexTimeUTCImpact := mgo.Index{}
indexTimeUTCImpact.Key = []string{`TimeUTC`, `Impact`}
logDBCollection.EnsureIndex(indexTimeUTCImpact)
indexTimeUTCMessageName := mgo.Index{}
indexTimeUTCMessageName.Key = []string{`TimeUTC`, `MessageName`}
logDBCollection.EnsureIndex(indexTimeUTCMessageName)
indexTimeUTCMessageDescription := mgo.Index{}
indexProjectTimeUTCMessageDescription.Key = []string{`TimeUTC`, `MessageDescription`}
logDBCollection.EnsureIndex(indexTimeUTCMessageDescription)
}
+39
View File
@@ -0,0 +1,39 @@
package DeviceDatabase
import "time"
import "github.com/SommerEngineering/Ocean/Shutdown"
// 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()
}
}()
}
+15
View File
@@ -0,0 +1,15 @@
package DeviceDatabase
import "github.com/SommerEngineering/Ocean/Log/Meta"
type Database struct {
}
func (dev Database) Log(entries []Meta.Entry) {
//
// Can not log here to prevent endless loop (consumer is also producer)
//
write2Cache(entries)
}
+16
View File
@@ -0,0 +1,16 @@
package DeviceDatabase
import "time"
type LogDBEntry struct {
TimeUTC time.Time `bson:"TimeUTC"`
Project string `bson:"Project"`
Sender string `bson:"Sender"`
Category string `bson:"Category"`
Level string `bson:"Level"`
Severity string `bson:"Severity"`
Impact string `bson:"Impact"`
MessageName string `bson:"MessageName"`
MessageDescription string `bson:"MessageDescription"`
Parameters []string `bson:"Parameters"`
}
+16
View File
@@ -0,0 +1,16 @@
package DeviceDatabase
import "sync"
import "labix.org/v2/mgo"
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
var (
senderName LM.Sender = `System::Logger::Database`
mutexCacheFull sync.Mutex = sync.Mutex{}
cache chan LogDBEntry = nil
cacheSizeNumberOfEvents int = 50
cacheSizeTime2FlushSeconds int = 6
logDB *mgo.Database = nil
logDBSession *mgo.Session = nil
logDBCollection *mgo.Collection = nil
)
+24
View File
@@ -0,0 +1,24 @@
package DeviceDatabase
import "github.com/SommerEngineering/Ocean/Log/Meta"
func write2Cache(entries []Meta.Entry) {
for _, entry := range entries {
if len(cache) == cacheSizeNumberOfEvents {
go cacheFull()
}
logDBentry := LogDBEntry{}
logDBentry.Category = Meta.FormatCategory(entry.Category)
logDBentry.Impact = Meta.FormatImpact(entry.Impact)
logDBentry.Level = Meta.FormatLevel(entry.Level)
logDBentry.MessageDescription = entry.MessageDescription
logDBentry.MessageName = string(entry.MessageName)
logDBentry.Parameters = entry.Parameters
logDBentry.Project = entry.Project
logDBentry.Sender = string(entry.Sender)
logDBentry.Severity = Meta.FormatSeverity(entry.Severity)
logDBentry.TimeUTC = entry.Time
cache <- logDBentry
}
}
+7
View File
@@ -0,0 +1,7 @@
package DeviceDatabase
func write2Database(entry LogDBEntry) {
if err := logDBCollection.Insert(entry); err != nil {
// Can not log here to prevent endless loop (consumer is also producer)
}
}
+43
View File
@@ -0,0 +1,43 @@
package Log
import "github.com/SommerEngineering/Ocean/Log/Meta"
import "github.com/SommerEngineering/Ocean/Log/Device"
func deviceDelay(newEntry Meta.Entry) {
defer checkDeviceDelaySize()
// Insert the new entry at the correct position (time)!
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
}
}
// Default: Insert at the back!
mutexDeviceDelays.Lock()
deviceDelayBuffer.PushBack(newEntry)
mutexDeviceDelays.Unlock()
}
func checkDeviceDelaySize() {
mutexDeviceDelays.Lock()
if deviceDelayBuffer.Len() >= logDeviceDelayNumberEvents {
dataArray := logEntryListToArray(deviceDelayBuffer)
deviceDelayBuffer.Init()
mutexDevices.RLock()
for entry := devices.Front(); entry != nil; entry = entry.Next() {
dev := entry.Value.(Device.Device)
go dev.Log(dataArray)
}
mutexDevices.RUnlock()
}
mutexDeviceDelays.Unlock()
}
+33
View File
@@ -0,0 +1,33 @@
package Log
import "time"
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.
*/
func Flush() {
mutexChannel.Lock()
channelReady = false
close(entriesBuffer)
mutexChannel.Unlock()
// This is a bad design, but the scheduler need some time to write the last messages.
time.Sleep(15 * time.Second)
mutexDeviceDelays.Lock()
dataArray := logEntryListToArray(deviceDelayBuffer)
deviceDelayBuffer.Init()
mutexDeviceDelays.Unlock()
mutexDevices.RLock()
for entry := devices.Front(); entry != nil; entry = entry.Next() {
dev := entry.Value.(Device.Device)
dev.Log(dataArray) // Want to wait to complete, therefore no new thread here
go dev.Flush()
}
mutexDevices.RUnlock()
// This is a bad design, but the devices need (may) some time to write the last messages:
time.Sleep(15 * time.Second)
}
+134
View File
@@ -0,0 +1,134 @@
package Log
import "time"
import "fmt"
import "strings"
import "github.com/SommerEngineering/Ocean/Log/Meta"
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!
fmt.Println(`Warning: Was not able to write to the logging buffer! Message=` + logEntry.Format())
}
}
/*
If, for any reason, you want to deliver a whole log entry to the logging system, then use this function to do so :) Normally,
just use the LogFull() and LogShort() functions instead ;) There is one reason to use this: If you have to deliver old log
events you have may imported or you have received a batch of log events of an remote system, etc. Because the LogShort() and
LogFull() methods are setting the time to the current UTC time. Therefore, TakeEntry() is the only way to provide the time!
*/
func TakeEntry(logEntry Meta.Entry) {
logEntry = clearEntry(logEntry)
mutexChannel.RLock()
defer mutexChannel.RUnlock()
if !channelReady {
mutexPreChannelBuffer.Lock()
defer mutexPreChannelBuffer.Unlock()
preChannelBuffer.PushBack(logEntry)
return
}
if !preChannelBufferUsed {
preChannelBufferUsed = true
mutexPreChannelBuffer.Lock()
for entry := preChannelBuffer.Front(); entry != nil; entry = entry.Next() {
writeToChannel(entry.Value.(Meta.Entry))
}
preChannelBuffer.Init()
mutexPreChannelBuffer.Unlock()
}
writeToChannel(logEntry)
}
/*
Create and deliver a full log message with all fields and with the current UTC time as logging time.
Sender The sender of this message (name of your component, like e.g. "APP::LOGIC::PAYPAL" etc.)
Category Use "CategoryBUSINESS" for business events (new payments, etc.), use "CategoryUSER" for log events
regarding users (e.g. new user registered, etc.) or "CategoryAPP" (for anything else).
Level Choose a logging level.
Severity Choose a degree of severity.
Impact Choose a degree of impact.
MessageName Choose a message name. This is like a common category for this event!
Description The logging message you want to deliver.
Parameters Provide as many additional parameters (type string) as you need.
*/
func LogFull(sender Meta.Sender, category Meta.Category, level Meta.Level, severity Meta.Severity, impact Meta.Impact, messageName Meta.MessageName, messageDescription string, parameters ...string) {
entry := Meta.Entry{}
entry.Project = projectName
entry.Time = time.Now().UTC()
entry.Category = category
entry.Level = level
entry.MessageDescription = messageDescription
entry.MessageName = messageName
entry.Parameters = parameters
entry.Severity = severity
entry.Impact = impact
entry.Sender = sender
TakeEntry(entry)
}
/*
Create and deliver a short log message with the current UTC time as logging time. The fields severity and impact
are both set to "none" value. Therefore, this short logging function does not fit for logging problems, errors etc.
Sender The sender of this message (name of your component, like e.g. "APP::LOGIC::PAYPAL" etc.)
Category Use "CategoryBUSINESS" for business events (new payments, etc.), use "CategoryUSER" for log events
regarding users (e.g. new user registered, etc.) or "CategoryAPP" (for anything else).
Level Choose a logging level.
MessageName Choose a message name. This is like a common category for this event!
Description The logging message you want to deliver.
Parameters Provide as many additional parameters (type string) as you need.
*/
func LogShort(sender Meta.Sender, category Meta.Category, level Meta.Level, messageName Meta.MessageName, messageDescription string, parameters ...string) {
entry := Meta.Entry{}
entry.Project = projectName
entry.Time = time.Now().UTC()
entry.Category = category
entry.Level = level
entry.MessageDescription = messageDescription
entry.MessageName = messageName
entry.Parameters = parameters
entry.Severity = Meta.SeverityNone
entry.Impact = Meta.ImpactNone
entry.Sender = sender
TakeEntry(entry)
}
func clearEntry(entry Meta.Entry) (result Meta.Entry) {
entry.MessageDescription = removeWhitespaces(entry.MessageDescription)
entry.Parameters = clearParameters(entry.Parameters)
result = entry
return
}
func clearParameters(oldParameters []string) (result []string) {
for n := 0; n < len(oldParameters); n++ {
oldParameters[n] = removeWhitespaces(oldParameters[n])
}
result = oldParameters
return
}
func removeWhitespaces(text string) (result string) {
text = strings.Replace(text, "\n", ` `, -1)
text = strings.Replace(text, "\t", ` `, -1)
text = strings.Replace(text, "\r", ``, -1)
result = text
return
}
+52
View File
@@ -0,0 +1,52 @@
package Log
import "github.com/SommerEngineering/Ocean/Log/Meta"
import "strconv"
import "container/list"
import "sync"
import "path/filepath"
import "os"
import "io/ioutil"
import "strings"
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)
}
}
}
func init() {
readProjectName()
mutexDeviceDelays = sync.Mutex{}
mutexPreChannelBuffer = sync.Mutex{}
mutexChannel = sync.RWMutex{}
preChannelBuffer = list.New()
deviceDelayBuffer = list.New()
devices = list.New()
initTimer()
initCode()
}
func initCode() {
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)))
go scheduler(entriesBuffer)
}
+9
View File
@@ -0,0 +1,9 @@
package Log
/*
This function is used just internal by Ocean. Please do not call this function by your self!
*/
func LoggingIsReady() {
channelReady = true
preChannelBufferUsed = false
}
+27
View File
@@ -0,0 +1,27 @@
package Meta
type Category byte
const (
CategoryBUSINESS = Category(iota)
CategorySYSTEM = Category(iota)
CategoryAPP = Category(iota)
CategoryUSER = Category(iota)
)
func FormatCategory(cat Category) (result string) {
switch cat {
case CategoryBUSINESS:
result = `C:BUSINESS`
case CategoryAPP:
result = `C:APP`
case CategorySYSTEM:
result = `C:SYSTEM`
case CategoryUSER:
result = `C:USER`
default:
result = `C:N/A`
}
return
}
+16
View File
@@ -0,0 +1,16 @@
package Meta
import "time"
type Entry struct {
Project string
Time time.Time
Sender Sender
Category Category
Level Level
Severity Severity
Impact Impact
MessageName MessageName
MessageDescription string
Parameters []string
}
+96
View File
@@ -0,0 +1,96 @@
package Meta
import "fmt"
import "time"
import "strconv"
import "strings"
func (entry Entry) Format() (result string) {
lenSender := len(entry.Sender)
if lenSender > 40 {
lenSender = 40
}
lenMessageName := len(entry.MessageName)
if lenMessageName > 26 {
lenMessageName = 26
}
lenProject := len(entry.Project)
if lenProject > 10 {
lenProject = 10
}
messageDescription := entry.MessageDescription
messageDescription = strings.Replace(messageDescription, `\n`, ` `, -1)
messageDescription = strings.Replace(messageDescription, `\t`, ` `, -1)
messageDescription = strings.Replace(messageDescription, `\r`, ` `, -1)
result = fmt.Sprintf(` [■] P:%10s [■] %s [■] %10s [■] %11s [■] %10s [■] %10s [■] sender: %-40s [■] name: %-26s [■] %s [■]`, entry.Project[:lenProject], formatTime(entry.Time), FormatCategory(entry.Category), FormatLevel(entry.Level), FormatSeverity(entry.Severity), FormatImpact(entry.Impact), entry.Sender[:lenSender], entry.MessageName[:lenMessageName], messageDescription)
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
}
func formatTime(t1 time.Time) (result string) {
var year int = t1.Year()
var month int = int(t1.Month())
var day int = int(t1.Day())
var minutes int = int(t1.Minute())
var hours int = int(t1.Hour())
var seconds int = int(t1.Second())
var milliseconds int = int(float64(t1.Nanosecond()) / 1000000.0)
var monthText, dayText, minutesText, hoursText, secondsText, millisecondsText string
if month >= 1 && month <= 9 {
monthText = fmt.Sprintf(`0%d`, month)
} else {
monthText = strconv.Itoa(month)
}
if day >= 1 && day <= 9 {
dayText = fmt.Sprintf(`0%d`, day)
} else {
dayText = strconv.Itoa(day)
}
if minutes >= 0 && minutes <= 9 {
minutesText = fmt.Sprintf(`0%d`, minutes)
} else {
minutesText = strconv.Itoa(minutes)
}
if hours >= 0 && hours <= 9 {
hoursText = fmt.Sprintf(`0%d`, hours)
} else {
hoursText = strconv.Itoa(hours)
}
if seconds >= 0 && seconds <= 9 {
secondsText = fmt.Sprintf(`0%d`, seconds)
} else {
secondsText = strconv.Itoa(seconds)
}
if milliseconds >= 0 && milliseconds <= 9 {
millisecondsText = fmt.Sprintf(`00%d`, milliseconds)
} else if milliseconds >= 10 && milliseconds <= 99 {
millisecondsText = fmt.Sprintf(`0%d`, milliseconds)
} else {
millisecondsText = strconv.Itoa(milliseconds)
}
result = fmt.Sprintf(`%d%s%s %s%s%s.%s`, year, monthText, dayText, hoursText, minutesText, secondsText, millisecondsText)
return
}
+33
View File
@@ -0,0 +1,33 @@
package Meta
type Impact byte
const (
ImpactNone = Impact(iota)
ImpactLow = Impact(iota)
ImpactMiddle = Impact(iota)
ImpactHigh = Impact(iota)
ImpactCritical = Impact(iota)
ImpactUnknown = Impact(iota)
)
func FormatImpact(pri Impact) (result string) {
switch pri {
case ImpactCritical:
result = `I:CRITICAL`
case ImpactHigh:
result = `I:HIGH`
case ImpactLow:
result = `I:LOW`
case ImpactMiddle:
result = `I:MIDDLE`
case ImpactNone:
result = `I:NONE`
case ImpactUnknown:
result = `I:UNKNOWN`
default:
result = `I:N/A`
}
return
}
+33
View File
@@ -0,0 +1,33 @@
package Meta
type Level byte
const (
LevelWARN = Level(iota)
LevelDEBUG = Level(iota)
LevelERROR = Level(iota)
LevelINFO = Level(iota)
LevelTALKATIVE = Level(iota)
LevelSECURITY = Level(iota)
)
func FormatLevel(lvl Level) (result string) {
switch lvl {
case LevelDEBUG:
result = `L:DEBUG`
case LevelERROR:
result = `L:ERROR`
case LevelINFO:
result = `L:INFO`
case LevelSECURITY:
result = `L:SECURITY`
case LevelTALKATIVE:
result = `L:TALKATIVE`
case LevelWARN:
result = `L:WARN`
default:
result = `L:N/A`
}
return
}
+34
View File
@@ -0,0 +1,34 @@
package Meta
type MessageName string
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`
)
+3
View File
@@ -0,0 +1,3 @@
package Meta
type Sender string
+33
View File
@@ -0,0 +1,33 @@
package Meta
type Severity byte
const (
SeverityNone = Severity(iota)
SeverityLow = Severity(iota)
SeverityMiddle = Severity(iota)
SeverityHigh = Severity(iota)
SeverityCritical = Severity(iota)
SeverityUnknown = Severity(iota)
)
func FormatSeverity(pri Severity) (result string) {
switch pri {
case SeverityCritical:
result = `S:CRITICAL`
case SeverityHigh:
result = `S:HIGH`
case SeverityLow:
result = `S:LOW`
case SeverityMiddle:
result = `S:MIDDLE`
case SeverityNone:
result = `S:NONE`
case SeverityUnknown:
result = `S:UNKNOWN`
default:
result = `S:N/A`
}
return
}
+38
View File
@@ -0,0 +1,38 @@
package Log
import "github.com/SommerEngineering/Ocean/Log/Meta"
import "time"
// 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
for {
if stopNextTime {
break
}
nextEntry, ok := <-logBuffer
if !ok {
// The channel was closed!
stopNextTime = true
nextEntry = Meta.Entry{}
nextEntry.Project = projectName
nextEntry.Time = time.Now().UTC()
nextEntry.Sender = senderName
nextEntry.Category = Meta.CategorySYSTEM
nextEntry.Level = Meta.LevelWARN
nextEntry.Severity = Meta.SeverityCritical
nextEntry.Impact = Meta.ImpactNone
nextEntry.MessageName = Meta.MessageNameCOMMUNICATION
nextEntry.MessageDescription = `The logging channel was closed!`
}
deviceDelay(nextEntry)
}
LogFull(senderName, Meta.CategorySYSTEM, Meta.LevelWARN, Meta.SeverityCritical, Meta.ImpactNone, Meta.MessageNameSHUTDOWN, `The scheduler is down now.`)
}
+61
View File
@@ -0,0 +1,61 @@
package Log
/*
This function is used just internal by Ocean to change some configuration afterwards, after the first runtime stage
(transition from not configured state in to the desired configurated stage, after the configuration database is ready).
Please do not call this function by your self!
*/
func SetBufferSize(bufferSize int) {
logBufferSize = bufferSize
}
/*
This function is used just internal by Ocean to change some configuration afterwards, after the first runtime stage
(transition from not configured state in to the desired configurated stage, after the configuration database is ready).
Please do not call this function by your self!
*/
func SetTimeoutSeconds(timeoutSeconds int) {
logBufferTimeoutSeconds = timeoutSeconds
}
/*
This function is used just internal by Ocean to change some configuration afterwards, after the first runtime stage
(transition from not configured state in to the desired configurated stage, after the configuration database is ready).
Please do not call this function by your self!
*/
func SetDeviceDelayNumberEvents(numberEvents int) {
logDeviceDelayNumberEvents = numberEvents
}
/*
This function is used just internal by Ocean to change some configuration afterwards, after the first runtime stage
(transition from not configured state in to the desired configurated stage, after the configuration database is ready).
Please do not call this function by your self!
*/
func SetDeviceDelayTimeoutSeconds(seconds int) {
logDeviceDelayTimeoutSeconds = seconds
}
/*
This function is used just internal by Ocean to change some configuration afterwards, after the first runtime stage
(transition from not configured state in to the desired configurated stage, after the configuration database is ready).
Please do not call this function by your self!
*/
func SetProjectName(project string) {
projectName = project
}
/*
This function is used just internal by Ocean to change some configuration afterwards, after the first runtime stage
(transition from not configured state in to the desired configurated stage, after the configuration database is ready).
Please do not call this function by your self!
*/
func ApplyConfigurationChanges() {
mutexChannel.Lock()
channelReady = false
close(entriesBuffer)
mutexChannel.Unlock()
initCode()
}
+37
View File
@@ -0,0 +1,37 @@
package Log
import "time"
import "fmt"
import "github.com/SommerEngineering/Ocean/Log/Device"
import "github.com/SommerEngineering/Ocean/Log/Meta"
func initTimer() {
if timerIsRunning == true {
LogFull(senderName, Meta.CategorySYSTEM, Meta.LevelWARN, Meta.SeverityHigh, Meta.ImpactNone, Meta.MessageNameSTARTUP, `The logging timer is already running.`)
return
}
timerIsRunning = true
LogShort(senderName, Meta.CategorySYSTEM, Meta.LevelINFO, Meta.MessageNameSTARTUP, `Create the logging timer now.`, fmt.Sprintf(`Timeout=%d seconds`, logDeviceDelayTimeoutSeconds))
go func() {
for {
time.Sleep(time.Duration(logDeviceDelayTimeoutSeconds) * time.Second)
mutexDeviceDelays.Lock()
dataArray := logEntryListToArray(deviceDelayBuffer)
deviceDelayBuffer.Init()
mutexDeviceDelays.Unlock()
mutexDevices.RLock()
for entry := devices.Front(); entry != nil; entry = entry.Next() {
dev := entry.Value.(Device.Device)
go dev.Log(dataArray)
}
mutexDevices.RUnlock()
}
}()
}
+25
View File
@@ -0,0 +1,25 @@
package Log
import "github.com/SommerEngineering/Ocean/Log/Meta"
import "container/list"
import "sync"
var (
entriesBuffer chan Meta.Entry = 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`
)