Initial commit of Ocean's local development
This commit is contained in:
parent
9944a9e1df
commit
86451938ec
19
Configuration/Doc.go
Normal file
19
Configuration/Doc.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
This package reads the configuration file from the disk (file: configuration.json).
|
||||||
|
This configuratin is just used to specific the configuration database to go further.
|
||||||
|
|
||||||
|
An example configuration.json file:
|
||||||
|
|
||||||
|
{
|
||||||
|
"ConfigDBHostname" : "localhost:27017",
|
||||||
|
"ConfigDBDatabase" : "MyDatabase",
|
||||||
|
"ConfigDBConfigurationCollection" : "Configuration",
|
||||||
|
"ConfigDBConfigurationCollectionUsername" : "ConfigurationUsername",
|
||||||
|
"ConfigDBConfigurationCollectionPassword" : "ConfigurationPassword"
|
||||||
|
}
|
||||||
|
|
||||||
|
Hint #1: Ocean is using MongoDB as database ;-)
|
||||||
|
Hint #2: Normally, you do not use this package at all, because the application configuration should persist
|
||||||
|
inside the configuration database. Your task: Provide a configuration.json file at the installation directory ;-)
|
||||||
|
*/
|
||||||
|
package Configuration
|
6
Configuration/Init.go
Normal file
6
Configuration/Init.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package Configuration
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
readConfiguration()
|
||||||
|
isInit = true
|
||||||
|
}
|
10
Configuration/Meta/Structure.go
Normal file
10
Configuration/Meta/Structure.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package Meta
|
||||||
|
|
||||||
|
// The type which matches the configuration file:
|
||||||
|
type Configuration struct {
|
||||||
|
ConfigDBHostname string
|
||||||
|
ConfigDBDatabase string
|
||||||
|
ConfigDBConfigurationCollection string
|
||||||
|
ConfigDBConfigurationCollectionUsername string
|
||||||
|
ConfigDBConfigurationCollectionPassword string
|
||||||
|
}
|
14
Configuration/PublicRead.go
Normal file
14
Configuration/PublicRead.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package Configuration
|
||||||
|
|
||||||
|
import "github.com/SommerEngineering/Ocean/Configuration/Meta"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Read the whole configuration and enable Ocean to get the configuration database.
|
||||||
|
|
||||||
|
Hint: Normally, you do not use this package at all, because the application configuration should persist
|
||||||
|
inside the configuration database.
|
||||||
|
*/
|
||||||
|
func Read() (result Meta.Configuration) {
|
||||||
|
result = configuration
|
||||||
|
return
|
||||||
|
}
|
52
Configuration/ReadConfiguration.go
Normal file
52
Configuration/ReadConfiguration.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package Configuration
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
import "os"
|
||||||
|
import "path/filepath"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
func readConfiguration() {
|
||||||
|
|
||||||
|
if isInit {
|
||||||
|
Log.LogFull(senderName, Meta.CategorySYSTEM, Meta.LevelWARN, Meta.SeverityNone, Meta.ImpactNone, Meta.MessageNameINIT, `The configuration package is already init!`)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
Log.LogShort(senderName, Meta.CategorySYSTEM, Meta.LevelINFO, Meta.MessageNameCONFIGURATION, `Init of configuration starting.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
currentDir, dirError := os.Getwd()
|
||||||
|
|
||||||
|
if dirError != nil {
|
||||||
|
panic(`Was not able to read the working directory: ` + dirError.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
currentPath := filepath.Join(currentDir, filename)
|
||||||
|
if _, errFile := os.Stat(currentPath); errFile != nil {
|
||||||
|
if os.IsNotExist(errFile) {
|
||||||
|
panic(`It was not possible to find the necessary configuration file 'configuration.json' at the application directory.`)
|
||||||
|
} else {
|
||||||
|
panic(`There was an error while open the configuration: ` + errFile.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file, fileError := os.Open(currentPath)
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
if fileError != nil {
|
||||||
|
panic(`The configuration file is not accessible: ` + fileError.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder := json.NewDecoder(file)
|
||||||
|
decError := decoder.Decode(&configuration)
|
||||||
|
|
||||||
|
if decError != nil {
|
||||||
|
panic(`Decoding of the configuration file was not possible: ` + decError.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.LogShort(senderName, Meta.CategorySYSTEM, Meta.LevelINFO, Meta.MessageNameINIT, `Init of configuration is done.`)
|
||||||
|
isInit = true
|
||||||
|
return
|
||||||
|
}
|
11
Configuration/Variables.go
Normal file
11
Configuration/Variables.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package Configuration
|
||||||
|
|
||||||
|
import "github.com/SommerEngineering/Ocean/Configuration/Meta"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
var (
|
||||||
|
filename = "configuration.json" // Where is the configuration located?
|
||||||
|
configuration Meta.Configuration = Meta.Configuration{} // The loaded configuration
|
||||||
|
isInit = false // Is the configuration loaded?
|
||||||
|
senderName LM.Sender = `System::Configuration`
|
||||||
|
)
|
65
ConfigurationDB/CheckConfiguration.go
Normal file
65
ConfigurationDB/CheckConfiguration.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package ConfigurationDB
|
||||||
|
|
||||||
|
import "labix.org/v2/mgo/bson"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
func checkConfiguration() {
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameDATABASE, `Check now the configuration database.`)
|
||||||
|
defer Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameDATABASE, `Done checking the configuration database.`)
|
||||||
|
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`InternalCommPassword`, `please replace this with e.g. a random GUID, etc.`)
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`CustomerDBHost`, `localhost:27017`)
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`CustomerDBDatabase`, `Ocean`)
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`CustomerDBUsername`, `root`)
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`CustomerDBPassword`, `please replace this with a good password`)
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`LogDBHost`, `localhost:27017`)
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`LogDBDatabase`, `Ocean`)
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`LogDBUsername`, `root`)
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`LogDBPassword`, `please replace this with a good password`)
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`LogDBCacheSizeNumberOfEvents`, `50`)
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`LogDBCacheSizeTime2FlushSeconds`, `6`)
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`LogBufferSize`, `500`)
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`LogDeviceDelayNumberEvents`, `600`)
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`LogDeviceDelayTime2FlushSeconds`, `5`)
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`LogTimeoutSeconds`, `4`)
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`LogStaticFileRequests`, `false`)
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`LogUseDatabaseLogging`, `false`)
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`LogUseConsoleLogging`, `true`)
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`NumGenActiveHosts`, `please replace this with the correct hostname of the host which is the master number generator`)
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`NumGenGetHandler`, `http://localhost:80/next/number`)
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`NumGenBufferSize`, `12`)
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`OceanHostnameAndPort`, `:60000`)
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`OceanUtilizeCPUs`, `2`)
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`FilenameWebResources`, `web.zip`)
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`MapStaticFiles2Root`, `false`)
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`MapStaticFiles2RootRootFile`, `index.html`)
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`EnableStaticFiles`, `true`)
|
||||||
|
CheckSingleConfigurationPresentsAndAddIfMissing(`robots.txt`, `User-agent: *
|
||||||
|
Disallow:`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Use this function to ensure that the database contains at least a default value for the configuration.
|
||||||
|
*/
|
||||||
|
func CheckSingleConfigurationPresentsAndAddIfMissing(name, value string) {
|
||||||
|
if !checkSingleConfigurationPresents(name) {
|
||||||
|
addSingleConfiguration(name, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkSingleConfigurationPresents(name string) (result bool) {
|
||||||
|
selection := bson.D{{"Name", name}}
|
||||||
|
count, _ := collection.Find(selection).Count()
|
||||||
|
|
||||||
|
return count > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func addSingleConfiguration(name, value string) {
|
||||||
|
entry := ConfigurationDBEntry{}
|
||||||
|
entry.Name = name
|
||||||
|
entry.Value = value
|
||||||
|
collection.Insert(entry)
|
||||||
|
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameDATABASE, `Add a missing configuration to the configuration database.`, `Name=`+name, `Value=`+value)
|
||||||
|
}
|
10
ConfigurationDB/Doc.go
Normal file
10
ConfigurationDB/Doc.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
This package provides the configuration database access: Ocean uses MongoDB as database! The configuration is represented as
|
||||||
|
name value pairs. Both, the name and the value, are strings. If you need numbers for the configuration, you have to convert
|
||||||
|
these strings in to numbers afterwards. Use the Read() function to read a specific configuration value.
|
||||||
|
|
||||||
|
To provide own application configurations, use the function CheckSingleConfigurationPresentsAndAddIfMissing() to ensure, that
|
||||||
|
at least a default value is present in the database. Collect all these function calls inside a init() function somewhere at your
|
||||||
|
application.
|
||||||
|
*/
|
||||||
|
package ConfigurationDB
|
38
ConfigurationDB/Init.go
Normal file
38
ConfigurationDB/Init.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package ConfigurationDB
|
||||||
|
|
||||||
|
import "labix.org/v2/mgo"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Configuration"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
config := Configuration.Read()
|
||||||
|
|
||||||
|
// Connect to MongoDB:
|
||||||
|
if newSession, errDial := mgo.Dial(config.ConfigDBHostname); 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 `+config.ConfigDBHostname, errDial.Error())
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
session = newSession
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the correct database:
|
||||||
|
db = session.DB(config.ConfigDBDatabase)
|
||||||
|
|
||||||
|
// Login:
|
||||||
|
if errLogin := db.Login(config.ConfigDBConfigurationCollectionUsername, config.ConfigDBConfigurationCollectionPassword); errLogin != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelSECURITY, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNameDATABASE, `It was not possible to login the user `+config.ConfigDBConfigurationCollectionUsername, errLogin.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the collection:
|
||||||
|
collection = db.C(config.ConfigDBConfigurationCollection)
|
||||||
|
|
||||||
|
// Take care about the index:
|
||||||
|
collection.EnsureIndexKey(`Name`)
|
||||||
|
collection.EnsureIndexKey(`Value`)
|
||||||
|
|
||||||
|
checkConfiguration()
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameDATABASE, `The configuration database is now ready.`)
|
||||||
|
}
|
24
ConfigurationDB/Read.go
Normal file
24
ConfigurationDB/Read.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package ConfigurationDB
|
||||||
|
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
import "labix.org/v2/mgo/bson"
|
||||||
|
|
||||||
|
/*
|
||||||
|
This function reads the current configuration value.
|
||||||
|
*/
|
||||||
|
func Read(name string) (value string) {
|
||||||
|
if name == `` {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNameDATABASE, `Was not able to read a configuration out of the database.`, `The given name was nil!`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result := ConfigurationDBEntry{}
|
||||||
|
if errFind := collection.Find(bson.D{{"Name", name}}).One(&result); errFind != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNameDATABASE, `Was not able to read a configuration out of the database.`, `Error while find.`, errFind.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
value = result.Value
|
||||||
|
return
|
||||||
|
}
|
6
ConfigurationDB/Scheme.go
Normal file
6
ConfigurationDB/Scheme.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package ConfigurationDB
|
||||||
|
|
||||||
|
type ConfigurationDBEntry struct {
|
||||||
|
Name string `bson:"Name"`
|
||||||
|
Value string `bson:"Value"`
|
||||||
|
}
|
19
ConfigurationDB/Shutdown.go
Normal file
19
ConfigurationDB/Shutdown.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package ConfigurationDB
|
||||||
|
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Do not use this type by your own! It is a Ocean internal type to provide a shutdown function for the configuration database.
|
||||||
|
*/
|
||||||
|
type ShutdownFunction struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
If the Ocean server is shutting down, this function is called to close the database.
|
||||||
|
*/
|
||||||
|
func (a ShutdownFunction) Shutdown() {
|
||||||
|
Log.LogShort(senderName, LM.CategoryAPP, LM.LevelWARN, LM.MessageNameSHUTDOWN, `Close now the configuration database connection.`)
|
||||||
|
db.Logout()
|
||||||
|
session.Close()
|
||||||
|
}
|
13
ConfigurationDB/Variables.go
Normal file
13
ConfigurationDB/Variables.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package ConfigurationDB
|
||||||
|
|
||||||
|
import "labix.org/v2/mgo"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Configuration/Meta"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
var (
|
||||||
|
session *mgo.Session = nil
|
||||||
|
db *mgo.Database = nil
|
||||||
|
collection *mgo.Collection = nil
|
||||||
|
config Meta.Configuration = Meta.Configuration{}
|
||||||
|
senderName LM.Sender = `System::ConfigurationDB`
|
||||||
|
)
|
19
CustomerDB/AccessDB.go
Normal file
19
CustomerDB/AccessDB.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package CustomerDB
|
||||||
|
|
||||||
|
import "labix.org/v2/mgo"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get the database instance of the MGo Mongo driver.
|
||||||
|
*/
|
||||||
|
func DB() (result *mgo.Database) {
|
||||||
|
result = db
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get directly the GridFS instance of the Mgo Mongo driver.
|
||||||
|
*/
|
||||||
|
func GridFS() (result *mgo.GridFS) {
|
||||||
|
result = gridFS
|
||||||
|
return
|
||||||
|
}
|
5
CustomerDB/Doc.go
Normal file
5
CustomerDB/Doc.go
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/*
|
||||||
|
This package provides access to the customer database. To specific the customer database, please use the configuration database.
|
||||||
|
By the way: Ocean uses MongoDB as database ;-) This package is just a facade to hide the complexity from MongoDB and its driver.
|
||||||
|
*/
|
||||||
|
package CustomerDB
|
52
CustomerDB/Init.go
Normal file
52
CustomerDB/Init.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package CustomerDB
|
||||||
|
|
||||||
|
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 init() {
|
||||||
|
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameDATABASE, `Init the customer database.`)
|
||||||
|
|
||||||
|
databaseHost := ConfigurationDB.Read(`CustomerDBHost`)
|
||||||
|
databaseDB := ConfigurationDB.Read(`CustomerDBDatabase`)
|
||||||
|
databaseUsername := ConfigurationDB.Read(`CustomerDBUsername`)
|
||||||
|
databasePassword := ConfigurationDB.Read(`CustomerDBPassword`)
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
session = newSession
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the correct database:
|
||||||
|
db = session.DB(databaseDB)
|
||||||
|
if db == nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameDATABASE, `Was not able to get the customer database.`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Login:
|
||||||
|
if errLogin := db.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 GridFS:
|
||||||
|
gridFS = db.GridFS(`fs`)
|
||||||
|
if gridFS == nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameDATABASE, `Was not able to get the GridFS from the database.`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the indexes for the GridFS:
|
||||||
|
filesCollection := gridFS.Files
|
||||||
|
filesCollection.EnsureIndexKey(`uploadDate`)
|
||||||
|
filesCollection.EnsureIndexKey(`filename`)
|
||||||
|
filesCollection.EnsureIndexKey(`filename`, `uploadDate`)
|
||||||
|
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameDATABASE, `Customer database is now ready.`)
|
||||||
|
}
|
19
CustomerDB/Shutdown.go
Normal file
19
CustomerDB/Shutdown.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package CustomerDB
|
||||||
|
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Please do not use this type. It is an internal type of Ocean to provide a shutdown function!
|
||||||
|
*/
|
||||||
|
type ShutdownFunction struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
This function is called if the Ocean server is shutting down.
|
||||||
|
*/
|
||||||
|
func (a ShutdownFunction) Shutdown() {
|
||||||
|
Log.LogShort(senderName, LM.CategoryAPP, LM.LevelWARN, LM.MessageNameSHUTDOWN, `Close now the customer database connection.`)
|
||||||
|
db.Logout()
|
||||||
|
session.Close()
|
||||||
|
}
|
11
CustomerDB/Variables.go
Normal file
11
CustomerDB/Variables.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package CustomerDB
|
||||||
|
|
||||||
|
import "labix.org/v2/mgo"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
var (
|
||||||
|
session *mgo.Session = nil
|
||||||
|
db *mgo.Database = nil
|
||||||
|
gridFS *mgo.GridFS = nil
|
||||||
|
senderName LM.Sender = `System::CustomerDB`
|
||||||
|
)
|
122
ICCC/Data2Message.go
Normal file
122
ICCC/Data2Message.go
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
package ICCC
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
import "reflect"
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func Data2Message(target interface{}, data map[string][]string) (channel, command string, obj interface{}) {
|
||||||
|
if data == nil || len(data) == 0 {
|
||||||
|
channel = ``
|
||||||
|
command = ``
|
||||||
|
obj = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
element := reflect.ValueOf(target)
|
||||||
|
element = element.Elem()
|
||||||
|
elementType := element.Type()
|
||||||
|
|
||||||
|
channel = data[`channel`][0]
|
||||||
|
command = data[`command`][0]
|
||||||
|
for i := 0; i < element.NumField(); i++ {
|
||||||
|
field := element.Field(i)
|
||||||
|
switch field.Kind().String() {
|
||||||
|
|
||||||
|
case `int64`:
|
||||||
|
mapName := fmt.Sprintf(`int:%s`, elementType.Field(i).Name)
|
||||||
|
mapValue := data[mapName][0]
|
||||||
|
v, _ := strconv.ParseInt(mapValue, 10, 64)
|
||||||
|
field.SetInt(v)
|
||||||
|
|
||||||
|
case `string`:
|
||||||
|
mapName := fmt.Sprintf(`str:%s`, elementType.Field(i).Name)
|
||||||
|
mapValue := data[mapName][0]
|
||||||
|
field.SetString(mapValue)
|
||||||
|
|
||||||
|
case `float64`:
|
||||||
|
mapName := fmt.Sprintf(`f64:%s`, elementType.Field(i).Name)
|
||||||
|
mapValue := data[mapName][0]
|
||||||
|
v, _ := strconv.ParseFloat(mapValue, 64)
|
||||||
|
field.SetFloat(v)
|
||||||
|
|
||||||
|
case `bool`:
|
||||||
|
mapName := fmt.Sprintf(`bool:%s`, elementType.Field(i).Name)
|
||||||
|
mapValue := data[mapName][0]
|
||||||
|
v, _ := strconv.ParseBool(mapValue)
|
||||||
|
field.SetBool(v)
|
||||||
|
|
||||||
|
case `uint8`:
|
||||||
|
mapName := fmt.Sprintf(`ui8:%s`, elementType.Field(i).Name)
|
||||||
|
mapValue := data[mapName][0]
|
||||||
|
v, _ := strconv.ParseUint(mapValue, 16, 8)
|
||||||
|
field.SetUint(v)
|
||||||
|
|
||||||
|
case `slice`:
|
||||||
|
sliceInterface := field.Interface()
|
||||||
|
sliceKind := reflect.ValueOf(sliceInterface).Type().String()
|
||||||
|
|
||||||
|
switch sliceKind {
|
||||||
|
case `[]uint8`: // bytes
|
||||||
|
mapName := fmt.Sprintf(`ui8[]:%s`, elementType.Field(i).Name)
|
||||||
|
mapValues := data[mapName]
|
||||||
|
fieldLen := len(mapValues)
|
||||||
|
fieldData := make([]uint8, fieldLen, fieldLen)
|
||||||
|
for n, mapValue := range mapValues {
|
||||||
|
v, _ := strconv.ParseUint(mapValue, 16, 8)
|
||||||
|
fieldData[n] = byte(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldDataValue := reflect.ValueOf(fieldData)
|
||||||
|
field.Set(fieldDataValue)
|
||||||
|
|
||||||
|
case `[]int64`:
|
||||||
|
mapName := fmt.Sprintf(`int[]:%s`, elementType.Field(i).Name)
|
||||||
|
mapValues := data[mapName]
|
||||||
|
fieldLen := len(mapValues)
|
||||||
|
fieldData := make([]int64, fieldLen, fieldLen)
|
||||||
|
for n, mapValue := range mapValues {
|
||||||
|
v, _ := strconv.ParseInt(mapValue, 10, 64)
|
||||||
|
fieldData[n] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldDataValue := reflect.ValueOf(fieldData)
|
||||||
|
field.Set(fieldDataValue)
|
||||||
|
|
||||||
|
case `[]bool`:
|
||||||
|
mapName := fmt.Sprintf(`bool[]:%s`, elementType.Field(i).Name)
|
||||||
|
mapValues := data[mapName]
|
||||||
|
fieldLen := len(mapValues)
|
||||||
|
fieldData := make([]bool, fieldLen, fieldLen)
|
||||||
|
for n, mapValue := range mapValues {
|
||||||
|
v, _ := strconv.ParseBool(mapValue)
|
||||||
|
fieldData[n] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldDataValue := reflect.ValueOf(fieldData)
|
||||||
|
field.Set(fieldDataValue)
|
||||||
|
|
||||||
|
case `[]string`:
|
||||||
|
mapName := fmt.Sprintf(`str[]:%s`, elementType.Field(i).Name)
|
||||||
|
mapValues := data[mapName]
|
||||||
|
fieldDataValue := reflect.ValueOf(mapValues)
|
||||||
|
field.Set(fieldDataValue)
|
||||||
|
|
||||||
|
case `[]float64`:
|
||||||
|
mapName := fmt.Sprintf(`f64[]:%s`, elementType.Field(i).Name)
|
||||||
|
mapValues := data[mapName]
|
||||||
|
fieldLen := len(mapValues)
|
||||||
|
fieldData := make([]float64, fieldLen, fieldLen)
|
||||||
|
for n, mapValue := range mapValues {
|
||||||
|
v, _ := strconv.ParseFloat(mapValue, 64)
|
||||||
|
fieldData[n] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldDataValue := reflect.ValueOf(fieldData)
|
||||||
|
field.Set(fieldDataValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = target
|
||||||
|
return
|
||||||
|
}
|
4
ICCC/Doc.go
Normal file
4
ICCC/Doc.go
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/*
|
||||||
|
This is the "[I]nter Data [C]enter Se[c]ure [C]ommunication" :)
|
||||||
|
*/
|
||||||
|
package ICCC
|
34
ICCC/HTTPConnector.go
Normal file
34
ICCC/HTTPConnector.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package ICCC
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
import "net/http"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Tools"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
func ICCCHandler(response http.ResponseWriter, request *http.Request) {
|
||||||
|
if errParse := request.ParseForm(); errParse != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameNETWORK, `Was not able to parse the HTTP form data from an ICCC message!`)
|
||||||
|
http.NotFound(response, request)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
messageData := map[string][]string(request.PostForm)
|
||||||
|
channel := messageData[`channel`][0]
|
||||||
|
command := messageData[`command`][0]
|
||||||
|
password := messageData[`InternalCommPassword`][0]
|
||||||
|
|
||||||
|
if password != Tools.InternalCommPassword() {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelSECURITY, LM.SeverityCritical, LM.ImpactNone, LM.MessageNamePASSWORD, `Received a ICCC message with wrong password!`, request.RemoteAddr)
|
||||||
|
http.NotFound(response, request)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
key := fmt.Sprintf(`%s::%s`, channel, command)
|
||||||
|
listener := listeners[key]
|
||||||
|
if listener == nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNameCONFIGURATION, `Was not able to find the correct listener for these ICCC message.`, `channel=`+channel, `command`+command, `hostname=`+Tools.ThisHostname())
|
||||||
|
} else {
|
||||||
|
listener(messageData)
|
||||||
|
}
|
||||||
|
}
|
25
ICCC/Init.go
Normal file
25
ICCC/Init.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package ICCC
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
import "container/list"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Tools"
|
||||||
|
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.MessageNameINIT, `Start init of ICCC.`)
|
||||||
|
defer Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Done init ICCC.`)
|
||||||
|
|
||||||
|
cacheListenerDatabase = list.New()
|
||||||
|
listeners = make(map[string]func(data map[string][]string))
|
||||||
|
|
||||||
|
allHostsIPAddresses := Tools.ReadAllIPAddresses4ThisHost()
|
||||||
|
oceanHostnameAndPort := ConfigurationDB.Read(`OceanHostnameAndPort`)
|
||||||
|
port := oceanHostnameAndPort[strings.Index(oceanHostnameAndPort, `:`):]
|
||||||
|
correctAddressWithPort = allHostsIPAddresses[0] + port
|
||||||
|
|
||||||
|
initDB()
|
||||||
|
registerHost2Database()
|
||||||
|
initCacheTimer()
|
||||||
|
}
|
58
ICCC/InitDB.go
Normal file
58
ICCC/InitDB.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package ICCC
|
||||||
|
|
||||||
|
import "labix.org/v2/mgo"
|
||||||
|
import "github.com/SommerEngineering/Ocean/CustomerDB"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
func initDB() {
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Start init of the ICCC collections.`)
|
||||||
|
defer Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Done init the ICCC collection.`)
|
||||||
|
|
||||||
|
// Get the database:
|
||||||
|
db = CustomerDB.DB()
|
||||||
|
|
||||||
|
if db == nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameDATABASE, `Was not able to get the customer database.`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get my collections:
|
||||||
|
collectionListener = db.C(`ICCCListener`)
|
||||||
|
collectionHosts = db.C(`ICCCHosts`)
|
||||||
|
|
||||||
|
// Take care about the indexes for ICCCListener:
|
||||||
|
collectionListener.EnsureIndexKey(`Command`)
|
||||||
|
collectionListener.EnsureIndexKey(`Command`, `IsActive`)
|
||||||
|
|
||||||
|
collectionListener.EnsureIndexKey(`Command`, `Channel`)
|
||||||
|
collectionListener.EnsureIndexKey(`Command`, `Channel`, `IsActive`)
|
||||||
|
|
||||||
|
collectionListener.EnsureIndexKey(`Channel`)
|
||||||
|
collectionListener.EnsureIndexKey(`Channel`, `IsActive`)
|
||||||
|
collectionListener.EnsureIndexKey(`Channel`, `Command`, `IPAddressPort`, `IsActive`)
|
||||||
|
collectionListener.EnsureIndexKey(`Channel`, `Command`, `IsActive`)
|
||||||
|
|
||||||
|
collectionListener.EnsureIndexKey(`IsActive`)
|
||||||
|
collectionListener.EnsureIndexKey(`IsActive`, `IPAddressPort`)
|
||||||
|
|
||||||
|
collectionListener.EnsureIndexKey(`IPAddressPort`)
|
||||||
|
|
||||||
|
indexName1 := mgo.Index{}
|
||||||
|
indexName1.Key = []string{`Channel`, `Command`, `IPAddressPort`}
|
||||||
|
indexName1.Unique = true
|
||||||
|
collectionListener.EnsureIndex(indexName1)
|
||||||
|
|
||||||
|
// Index for hosts:
|
||||||
|
collectionHosts.EnsureIndexKey(`Hostname`, `IPAddressPort`)
|
||||||
|
|
||||||
|
indexName2 := mgo.Index{}
|
||||||
|
indexName2.Key = []string{`Hostname`}
|
||||||
|
indexName2.Unique = true
|
||||||
|
collectionHosts.EnsureIndex(indexName2)
|
||||||
|
|
||||||
|
indexName3 := mgo.Index{}
|
||||||
|
indexName3.Key = []string{`IPAddressPort`}
|
||||||
|
indexName3.Unique = true
|
||||||
|
collectionHosts.EnsureIndex(indexName3)
|
||||||
|
}
|
41
ICCC/ListenerCache.go
Normal file
41
ICCC/ListenerCache.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package ICCC
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
import "time"
|
||||||
|
import "labix.org/v2/mgo/bson"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Shutdown"
|
||||||
|
import "github.com/SommerEngineering/Ocean/ICCC/Scheme"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
func initCacheTimer() {
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
|
||||||
|
if Shutdown.IsDown() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lastCount := cacheListenerDatabase.Len()
|
||||||
|
selection := bson.D{{`IsActive`, true}}
|
||||||
|
entriesIterator := collectionListener.Find(selection).Iter()
|
||||||
|
entry := Scheme.Listener{}
|
||||||
|
|
||||||
|
cacheListenerDatabaseLock.Lock()
|
||||||
|
cacheListenerDatabase.Init()
|
||||||
|
for entriesIterator.Next(&entry) {
|
||||||
|
cacheListenerDatabase.PushBack(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheListenerDatabaseLock.Unlock()
|
||||||
|
nextDuration := time.Duration(5) * time.Minute
|
||||||
|
if cacheListenerDatabase.Len() == 0 {
|
||||||
|
nextDuration = time.Duration(10) * time.Second
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameEXECUTE, `The listener cache was refreshed with the values from the database.`, fmt.Sprintf(`last count=%d`, lastCount), fmt.Sprintf(`new count=%d`, cacheListenerDatabase.Len()))
|
||||||
|
time.Sleep(nextDuration)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
95
ICCC/Message2Data.go
Normal file
95
ICCC/Message2Data.go
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package ICCC
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
import "reflect"
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func message2Data(channel, command string, message interface{}) (data map[string][]string) {
|
||||||
|
if message == nil {
|
||||||
|
data = make(map[string][]string)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
element := reflect.ValueOf(message)
|
||||||
|
elementType := element.Type()
|
||||||
|
|
||||||
|
data = make(map[string][]string)
|
||||||
|
data[`command`] = []string{command}
|
||||||
|
data[`channel`] = []string{channel}
|
||||||
|
|
||||||
|
for i := 0; i < element.NumField(); i++ {
|
||||||
|
field := element.Field(i)
|
||||||
|
keyName := elementType.Field(i).Name
|
||||||
|
|
||||||
|
switch field.Kind().String() {
|
||||||
|
|
||||||
|
case `int64`:
|
||||||
|
key := fmt.Sprintf(`int:%s`, keyName)
|
||||||
|
data[key] = []string{strconv.FormatInt(field.Int(), 10)}
|
||||||
|
|
||||||
|
case `string`:
|
||||||
|
key := fmt.Sprintf(`str:%s`, keyName)
|
||||||
|
data[key] = []string{field.String()}
|
||||||
|
|
||||||
|
case `float64`:
|
||||||
|
key := fmt.Sprintf(`f64:%s`, keyName)
|
||||||
|
data[key] = []string{strconv.FormatFloat(field.Float(), 'f', 9, 64)}
|
||||||
|
|
||||||
|
case `bool`:
|
||||||
|
key := fmt.Sprintf(`bool:%s`, keyName)
|
||||||
|
data[key] = []string{strconv.FormatBool(field.Bool())}
|
||||||
|
|
||||||
|
case `uint8`: // byte
|
||||||
|
key := fmt.Sprintf(`ui8:%s`, keyName)
|
||||||
|
data[key] = []string{strconv.FormatUint(field.Uint(), 16)}
|
||||||
|
|
||||||
|
case `slice`:
|
||||||
|
sliceLen := field.Len()
|
||||||
|
if sliceLen > 0 {
|
||||||
|
sliceKind := field.Index(0).Kind()
|
||||||
|
key := ``
|
||||||
|
dataValues := make([]string, sliceLen, sliceLen)
|
||||||
|
switch sliceKind.String() {
|
||||||
|
case `uint8`: // bytes
|
||||||
|
key = fmt.Sprintf(`ui8[]:%s`, keyName)
|
||||||
|
values := field.Interface().([]uint8)
|
||||||
|
for index, value := range values {
|
||||||
|
dataValues[index] = strconv.FormatUint(uint64(value), 16)
|
||||||
|
}
|
||||||
|
|
||||||
|
case `int64`:
|
||||||
|
key = fmt.Sprintf(`int[]:%s`, keyName)
|
||||||
|
values := field.Interface().([]int64)
|
||||||
|
for index, value := range values {
|
||||||
|
dataValues[index] = strconv.FormatInt(value, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
case `bool`:
|
||||||
|
key = fmt.Sprintf(`bool[]:%s`, keyName)
|
||||||
|
values := field.Interface().([]bool)
|
||||||
|
for index, value := range values {
|
||||||
|
dataValues[index] = strconv.FormatBool(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
case `string`:
|
||||||
|
key = fmt.Sprintf(`str[]:%s`, keyName)
|
||||||
|
values := field.Interface().([]string)
|
||||||
|
for index, value := range values {
|
||||||
|
dataValues[index] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
case `float64`:
|
||||||
|
key = fmt.Sprintf(`f64[]:%s`, keyName)
|
||||||
|
values := field.Interface().([]float64)
|
||||||
|
for index, value := range values {
|
||||||
|
dataValues[index] = strconv.FormatFloat(value, 'f', 9, 64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data[key] = dataValues
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
47
ICCC/Register2Database.go
Normal file
47
ICCC/Register2Database.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package ICCC
|
||||||
|
|
||||||
|
import "labix.org/v2/mgo/bson"
|
||||||
|
import "github.com/SommerEngineering/Ocean/ICCC/Scheme"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
func register2Database(channel, command string) {
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `Register this ICCC command in to the database.`, `channel=`+channel, `command=`+command)
|
||||||
|
|
||||||
|
// Case: Exist and active :)
|
||||||
|
emptyEntry := Scheme.Listener{}
|
||||||
|
selection := bson.D{{`Channel`, channel}, {`Command`, command}, {`IPAddressPort`, correctAddressWithPort}, {`IsActive`, true}}
|
||||||
|
count1, _ := collectionListener.Find(selection).Count()
|
||||||
|
|
||||||
|
if count1 == 1 {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.SeverityHigh, LM.ImpactHigh, LM.MessageNameSTARTUP, `This ICCC command is already known and active.`, `Please shutdown the system next time!`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case: Exist but not active
|
||||||
|
selection = bson.D{{`Channel`, channel}, {`Command`, command}, {`IPAddressPort`, correctAddressWithPort}, {`IsActive`, false}}
|
||||||
|
notActiveEntry := Scheme.Listener{}
|
||||||
|
collectionListener.Find(selection).One(¬ActiveEntry)
|
||||||
|
|
||||||
|
if notActiveEntry != emptyEntry {
|
||||||
|
notActiveEntry.IsActive = true
|
||||||
|
collectionListener.Update(selection, notActiveEntry)
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.SeverityNone, LM.ImpactNone, LM.MessageNameSTARTUP, `This ICCC command is already known but it was not active.`, `The command is active now!`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case: Not exist
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityCritical, LM.ImpactNone, LM.MessageNameCONFIGURATION, `This ICCC command is not known.`, `Create now a new entry!`)
|
||||||
|
|
||||||
|
entry := Scheme.Listener{}
|
||||||
|
entry.Channel = channel
|
||||||
|
entry.Command = command
|
||||||
|
entry.IsActive = true
|
||||||
|
entry.IPAddressPort = correctAddressWithPort
|
||||||
|
|
||||||
|
if err := collectionListener.Insert(entry); err != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameDATABASE, `It was not possible to add this ICCC command!`, err.Error(), `channel=`+channel, `command=`+command)
|
||||||
|
} else {
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `This ICCC command is now known and active.`)
|
||||||
|
}
|
||||||
|
}
|
26
ICCC/RegisterHost2Database.go
Normal file
26
ICCC/RegisterHost2Database.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package ICCC
|
||||||
|
|
||||||
|
import "labix.org/v2/mgo/bson"
|
||||||
|
import "github.com/SommerEngineering/Ocean/ICCC/Scheme"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Tools"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
func registerHost2Database() {
|
||||||
|
host := Scheme.Host{}
|
||||||
|
host.Hostname = Tools.ThisHostname()
|
||||||
|
host.IPAddressPort = correctAddressWithPort
|
||||||
|
|
||||||
|
selection := bson.D{{`Hostname`, host.Hostname}, {`IPAddressPort`, host.IPAddressPort}}
|
||||||
|
count, _ := collectionHosts.Find(selection).Count()
|
||||||
|
|
||||||
|
if count == 1 {
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `This host is already registered!`, `host=`+host.Hostname, `address=`+host.IPAddressPort)
|
||||||
|
} else {
|
||||||
|
if errInsert := collectionHosts.Insert(host); errInsert != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameDATABASE, `Was not able to register this host.`, errInsert.Error(), `host=`+host.Hostname, `address=`+host.IPAddressPort)
|
||||||
|
} else {
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `This host is now registered.`, `host=`+host.Hostname, `address=`+host.IPAddressPort)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
ICCC/Registrar.go
Normal file
15
ICCC/Registrar.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package ICCC
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
func Registrar(channel, command string, callback func(data map[string][]string)) {
|
||||||
|
listenersLock.Lock()
|
||||||
|
defer listenersLock.Unlock()
|
||||||
|
|
||||||
|
register2Database(channel, command)
|
||||||
|
listeners[fmt.Sprintf(`%s::%s`, channel, command)] = callback
|
||||||
|
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `The registrar has registered a new ICCC command.`, `channel=`+channel, `command=`+command)
|
||||||
|
}
|
6
ICCC/Scheme/Host.go
Normal file
6
ICCC/Scheme/Host.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package Scheme
|
||||||
|
|
||||||
|
type Host struct {
|
||||||
|
Hostname string `bson:"Hostname"`
|
||||||
|
IPAddressPort string `bson:"IPAddressPort"`
|
||||||
|
}
|
8
ICCC/Scheme/Listener.go
Normal file
8
ICCC/Scheme/Listener.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package Scheme
|
||||||
|
|
||||||
|
type Listener struct {
|
||||||
|
Channel string `bson:"Channel"`
|
||||||
|
Command string `bson:"Command"`
|
||||||
|
IsActive bool `bson:"IsActive"`
|
||||||
|
IPAddressPort string `bson:"IPAddressPort"`
|
||||||
|
}
|
19
ICCC/Send.go
Normal file
19
ICCC/Send.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package ICCC
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
import "net/url"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Tools"
|
||||||
|
import "github.com/SommerEngineering/Ocean/ICCC/Scheme"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
func sendMessage(listener Scheme.Listener, data map[string][]string) {
|
||||||
|
|
||||||
|
valuesHTTP := url.Values(data)
|
||||||
|
valuesHTTP.Add(`InternalCommPassword`, Tools.InternalCommPassword())
|
||||||
|
if _, err := http.PostForm(`http://`+listener.IPAddressPort+`/ICCC`, valuesHTTP); err != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNameNETWORK, `Was not able to send the ICCC message.`, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
30
ICCC/Shutdown.go
Normal file
30
ICCC/Shutdown.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package ICCC
|
||||||
|
|
||||||
|
import "labix.org/v2/mgo/bson"
|
||||||
|
import "github.com/SommerEngineering/Ocean/ICCC/Scheme"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Please do not use this type. It is an internal type of Ocean to provide a shutdown function!
|
||||||
|
*/
|
||||||
|
type ShutdownFunction struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
This function is called if the Ocean server is shutting down.
|
||||||
|
*/
|
||||||
|
func (a ShutdownFunction) Shutdown() {
|
||||||
|
Log.LogShort(senderName, LM.CategoryAPP, LM.LevelWARN, LM.MessageNameSHUTDOWN, `Shutting down now all ICCC listener for this host.`)
|
||||||
|
|
||||||
|
selection := bson.D{{`IPAddressPort`, correctAddressWithPort}}
|
||||||
|
entry := Scheme.Listener{}
|
||||||
|
iterator := collectionListener.Find(selection).Iter()
|
||||||
|
for iterator.Next(&entry) {
|
||||||
|
selectionUpdate := bson.D{{`Channel`, entry.Channel}, {`Command`, entry.Command}, {`IPAddressPort`, correctAddressWithPort}}
|
||||||
|
entry.IsActive = false
|
||||||
|
collectionListener.Update(selectionUpdate, entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.LogShort(senderName, LM.CategoryAPP, LM.LevelWARN, LM.MessageNameSHUTDOWN, `Done shutting down now all ICCC listener for this host.`)
|
||||||
|
}
|
27
ICCC/Variables.go
Normal file
27
ICCC/Variables.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package ICCC
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
import "container/list"
|
||||||
|
import "labix.org/v2/mgo"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
const (
|
||||||
|
ChannelSYSTEM string = `System`
|
||||||
|
ChannelNUMGEN string = `System::NumGen`
|
||||||
|
ChannelSHUTDOWN string = `System::Shutdown`
|
||||||
|
ChannelSTARTUP string = `System::Startup`
|
||||||
|
ChannelICCC string = `System::ICCC`
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
senderName LM.Sender = `ICCC`
|
||||||
|
db *mgo.Database = nil
|
||||||
|
collectionListener *mgo.Collection = nil
|
||||||
|
collectionHosts *mgo.Collection = nil
|
||||||
|
reservedSystemChannels []string = []string{ChannelSYSTEM, ChannelNUMGEN, ChannelSHUTDOWN, ChannelSTARTUP, ChannelICCC}
|
||||||
|
listeners map[string]func(data map[string][]string) = nil
|
||||||
|
listenersLock sync.RWMutex = sync.RWMutex{}
|
||||||
|
cacheListenerDatabase *list.List = nil
|
||||||
|
cacheListenerDatabaseLock sync.RWMutex = sync.RWMutex{}
|
||||||
|
correctAddressWithPort string = ``
|
||||||
|
)
|
26
ICCC/WriteMessage2All.go
Normal file
26
ICCC/WriteMessage2All.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package ICCC
|
||||||
|
|
||||||
|
import "github.com/SommerEngineering/Ocean/ICCC/Scheme"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
func WriteMessage2All(channel, command string, message interface{}) {
|
||||||
|
cacheListenerDatabaseLock.RLock()
|
||||||
|
defer cacheListenerDatabaseLock.RUnlock()
|
||||||
|
|
||||||
|
data := message2Data(channel, command, message)
|
||||||
|
counter := 0
|
||||||
|
for entry := cacheListenerDatabase.Front(); entry != nil; entry = entry.Next() {
|
||||||
|
listener := entry.Value.(Scheme.Listener)
|
||||||
|
if listener.Channel == channel && listener.Command == command {
|
||||||
|
go sendMessage(listener, data)
|
||||||
|
counter++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if counter == 0 {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNameCONFIGURATION, `It was not able to deliver this message, because no listener was found!`, `channel=`+channel, `command=`+command)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
31
ICCC/WriteMessage2Any.go
Normal file
31
ICCC/WriteMessage2Any.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package ICCC
|
||||||
|
|
||||||
|
import "github.com/SommerEngineering/Ocean/Tools"
|
||||||
|
import "github.com/SommerEngineering/Ocean/ICCC/Scheme"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
func WriteMessage2Any(channel, command string, message interface{}) {
|
||||||
|
cacheListenerDatabaseLock.RLock()
|
||||||
|
defer cacheListenerDatabaseLock.RUnlock()
|
||||||
|
|
||||||
|
data := message2Data(channel, command, message)
|
||||||
|
maxCount := cacheListenerDatabase.Len()
|
||||||
|
entries := make([]Scheme.Listener, 0, maxCount)
|
||||||
|
counter := 0
|
||||||
|
for entry := cacheListenerDatabase.Front(); entry != nil; entry = entry.Next() {
|
||||||
|
listener := entry.Value.(Scheme.Listener)
|
||||||
|
if listener.Channel == channel && listener.Command == command {
|
||||||
|
entries = entries[:len(entries)+1]
|
||||||
|
entries[counter] = listener
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
count := len(entries)
|
||||||
|
if count > 0 {
|
||||||
|
listener := entries[Tools.RandomInteger(count)]
|
||||||
|
go sendMessage(listener, data)
|
||||||
|
} else {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNameCONFIGURATION, `It was not able to deliver this message to any listener, because no listener was found!`, `channel=`+channel, `command=`+command)
|
||||||
|
}
|
||||||
|
}
|
19
Log/AddDevice.go
Normal file
19
Log/AddDevice.go
Normal 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
Log/Array.go
Normal file
16
Log/Array.go
Normal 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
Log/Device/Device.go
Normal file
8
Log/Device/Device.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package Device
|
||||||
|
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
type Device interface {
|
||||||
|
Log(logEntries []Meta.Entry)
|
||||||
|
Flush()
|
||||||
|
}
|
4
Log/Device/Doc.go
Normal file
4
Log/Device/Doc.go
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/*
|
||||||
|
This is the logging device interface you have to fulfill to be a logging device :)
|
||||||
|
*/
|
||||||
|
package Device
|
7
Log/DeviceConsole/ActivateLoggingDevice.go
Normal file
7
Log/DeviceConsole/ActivateLoggingDevice.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package DeviceConsole
|
||||||
|
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
|
||||||
|
func ActivateLoggingDevice() {
|
||||||
|
Log.AddLoggingDevice(Console{})
|
||||||
|
}
|
17
Log/DeviceConsole/Console.go
Normal file
17
Log/DeviceConsole/Console.go
Normal 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
Log/DeviceConsole/Doc.go
Normal file
4
Log/DeviceConsole/Doc.go
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/*
|
||||||
|
This is the console logging device. Any received entry is logged to the console.
|
||||||
|
*/
|
||||||
|
package DeviceConsole
|
7
Log/DeviceDatabase/ActivateLoggingDevice.go
Normal file
7
Log/DeviceDatabase/ActivateLoggingDevice.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package DeviceDatabase
|
||||||
|
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
|
||||||
|
func ActivateLoggingDevice() {
|
||||||
|
Log.AddLoggingDevice(Database{})
|
||||||
|
}
|
5
Log/DeviceDatabase/Doc.go
Normal file
5
Log/DeviceDatabase/Doc.go
Normal 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
Log/DeviceDatabase/Flush.go
Normal file
14
Log/DeviceDatabase/Flush.go
Normal 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
Log/DeviceDatabase/Init.go
Normal file
31
Log/DeviceDatabase/Init.go
Normal 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
Log/DeviceDatabase/InitDB.go
Normal file
165
Log/DeviceDatabase/InitDB.go
Normal 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
Log/DeviceDatabase/ReadFromCache.go
Normal file
39
Log/DeviceDatabase/ReadFromCache.go
Normal 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
Log/DeviceDatabase/ReceiveLogging.go
Normal file
15
Log/DeviceDatabase/ReceiveLogging.go
Normal 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
Log/DeviceDatabase/Scheme.go
Normal file
16
Log/DeviceDatabase/Scheme.go
Normal 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
Log/DeviceDatabase/Variables.go
Normal file
16
Log/DeviceDatabase/Variables.go
Normal 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
Log/DeviceDatabase/Write2Cache.go
Normal file
24
Log/DeviceDatabase/Write2Cache.go
Normal 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
Log/DeviceDatabase/Write2Database.go
Normal file
7
Log/DeviceDatabase/Write2Database.go
Normal 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
Log/DeviceDelay.go
Normal file
43
Log/DeviceDelay.go
Normal 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
Log/Flush.go
Normal file
33
Log/Flush.go
Normal 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
Log/Handlers.go
Normal file
134
Log/Handlers.go
Normal 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
Log/Init.go
Normal file
52
Log/Init.go
Normal 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
Log/LoggingIsReady.go
Normal file
9
Log/LoggingIsReady.go
Normal 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
Log/Meta/Category.go
Normal file
27
Log/Meta/Category.go
Normal 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
Log/Meta/Entry.go
Normal file
16
Log/Meta/Entry.go
Normal 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
Log/Meta/Format.go
Normal file
96
Log/Meta/Format.go
Normal 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
Log/Meta/Impact.go
Normal file
33
Log/Meta/Impact.go
Normal 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
Log/Meta/Level.go
Normal file
33
Log/Meta/Level.go
Normal 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
Log/Meta/MessageNames.go
Normal file
34
Log/Meta/MessageNames.go
Normal 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
Log/Meta/Sender.go
Normal file
3
Log/Meta/Sender.go
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package Meta
|
||||||
|
|
||||||
|
type Sender string
|
33
Log/Meta/Severity.go
Normal file
33
Log/Meta/Severity.go
Normal 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
Log/Scheduler.go
Normal file
38
Log/Scheduler.go
Normal 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
Log/SetConfiguration.go
Normal file
61
Log/SetConfiguration.go
Normal 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
Log/Timer.go
Normal file
37
Log/Timer.go
Normal 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
Log/Variables.go
Normal file
25
Log/Variables.go
Normal 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`
|
||||||
|
)
|
38
MimeTypes/Init.go
Normal file
38
MimeTypes/Init.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package MimeTypes
|
||||||
|
|
||||||
|
var allTypes [32]MimeType
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
allTypes[0] = TypeWebHTML
|
||||||
|
allTypes[1] = TypeWebCSS
|
||||||
|
allTypes[2] = TypeWebJavaScript
|
||||||
|
allTypes[3] = TypeXML
|
||||||
|
allTypes[4] = TypeArchiveZIP
|
||||||
|
allTypes[5] = TypeArchiveGZ
|
||||||
|
allTypes[6] = TypeWebOCTET
|
||||||
|
allTypes[7] = TypeDocumentPDF
|
||||||
|
allTypes[8] = TypeDocumentLaTeX
|
||||||
|
allTypes[9] = TypeShockwave
|
||||||
|
allTypes[10] = TypeArchiveTAR
|
||||||
|
allTypes[11] = TypeAudioWAV
|
||||||
|
allTypes[12] = TypeAudioMP3
|
||||||
|
allTypes[13] = TypeAudioAAC
|
||||||
|
allTypes[14] = TypeAudioOGG
|
||||||
|
allTypes[15] = TypeAudioWMA
|
||||||
|
allTypes[16] = TypeImageGIF
|
||||||
|
allTypes[17] = TypeImageCommon
|
||||||
|
allTypes[18] = TypeUnknown
|
||||||
|
allTypes[19] = TypeImageJPEG
|
||||||
|
allTypes[20] = TypeImagePNG
|
||||||
|
allTypes[21] = TypePlainText
|
||||||
|
allTypes[22] = TypeVideoMPEG
|
||||||
|
allTypes[23] = TypeVideoMOV
|
||||||
|
allTypes[24] = TypeVideoAVI
|
||||||
|
allTypes[25] = TypeVideoMP4
|
||||||
|
allTypes[26] = TypeFontEOT
|
||||||
|
allTypes[27] = TypeFontOTF
|
||||||
|
allTypes[28] = TypeImageSVG
|
||||||
|
allTypes[29] = TypeFontTTF
|
||||||
|
allTypes[30] = TypeFontWOFF
|
||||||
|
allTypes[31] = TypeWebJSON
|
||||||
|
}
|
34
MimeTypes/KnownTypes.go
Normal file
34
MimeTypes/KnownTypes.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package MimeTypes
|
||||||
|
|
||||||
|
var TypeWebHTML = MimeType{MimeType: "text/html", FileExtension: []string{".html", ".htm"}}
|
||||||
|
var TypeWebCSS = MimeType{MimeType: "text/css", FileExtension: []string{".css"}}
|
||||||
|
var TypeWebJavaScript = MimeType{MimeType: "text/javascript", FileExtension: []string{".js"}}
|
||||||
|
var TypeXML = MimeType{MimeType: "text/xml", FileExtension: []string{".xml"}}
|
||||||
|
var TypeArchiveZIP = MimeType{MimeType: "application/zip", FileExtension: []string{".zip"}}
|
||||||
|
var TypeArchiveGZ = MimeType{MimeType: "application/gzip", FileExtension: []string{".gz"}}
|
||||||
|
var TypeWebOCTET = MimeType{MimeType: "application/octet-stream", FileExtension: []string{".bin", ".exe", ".dll", ".class"}}
|
||||||
|
var TypeDocumentPDF = MimeType{MimeType: "application/pdf", FileExtension: []string{".pdf"}}
|
||||||
|
var TypeDocumentLaTeX = MimeType{MimeType: "application/x-latex", FileExtension: []string{".tex", ".latex"}}
|
||||||
|
var TypeShockwave = MimeType{MimeType: "application/x-shockwave-flash", FileExtension: []string{".swf"}}
|
||||||
|
var TypeArchiveTAR = MimeType{MimeType: "application/x-tar", FileExtension: []string{".tar"}}
|
||||||
|
var TypeAudioWAV = MimeType{MimeType: "application/x-wav", FileExtension: []string{".wav"}}
|
||||||
|
var TypeAudioMP3 = MimeType{MimeType: "audio/mpeg", FileExtension: []string{".mp3"}}
|
||||||
|
var TypeAudioAAC = MimeType{MimeType: "audio/aac", FileExtension: []string{".aac", ".m4a"}}
|
||||||
|
var TypeAudioOGG = MimeType{MimeType: "audio/ogg", FileExtension: []string{"vogg", ".oga"}}
|
||||||
|
var TypeAudioWMA = MimeType{MimeType: "audio/x-ms-wma", FileExtension: []string{".wma"}}
|
||||||
|
var TypeImageGIF = MimeType{MimeType: "image/gif", FileExtension: []string{".gif"}}
|
||||||
|
var TypeImageCommon = MimeType{MimeType: "image", FileExtension: []string{}}
|
||||||
|
var TypeUnknown = MimeType{MimeType: "application/octet-stream", FileExtension: []string{}}
|
||||||
|
var TypeImageJPEG = MimeType{MimeType: "image/jpeg", FileExtension: []string{".jpg", ".jpeg"}}
|
||||||
|
var TypeImagePNG = MimeType{MimeType: "image/png", FileExtension: []string{".png"}}
|
||||||
|
var TypePlainText = MimeType{MimeType: "text/plain", FileExtension: []string{".txt"}}
|
||||||
|
var TypeVideoMPEG = MimeType{MimeType: "video/mpeg", FileExtension: []string{".mpeg", ".mpg"}}
|
||||||
|
var TypeVideoMOV = MimeType{MimeType: "video/quicktime", FileExtension: []string{".mov", ".qt"}}
|
||||||
|
var TypeVideoAVI = MimeType{MimeType: "video/x-msvideo", FileExtension: []string{".avi"}}
|
||||||
|
var TypeVideoMP4 = MimeType{MimeType: "video/mp4", FileExtension: []string{".mp4"}}
|
||||||
|
var TypeFontEOT = MimeType{MimeType: "application/vnd.ms-fontobject", FileExtension: []string{".eot"}}
|
||||||
|
var TypeFontOTF = MimeType{MimeType: "application/font-sfnt", FileExtension: []string{".otf"}}
|
||||||
|
var TypeImageSVG = MimeType{MimeType: "image/svg+xml", FileExtension: []string{".svg"}}
|
||||||
|
var TypeFontTTF = MimeType{MimeType: "application/font-sfnt", FileExtension: []string{".ttf"}}
|
||||||
|
var TypeFontWOFF = MimeType{MimeType: "application/font-woff", FileExtension: []string{".woff"}}
|
||||||
|
var TypeWebJSON = MimeType{MimeType: "application/json", FileExtension: []string{".json"}}
|
22
MimeTypes/MimeTypes.go
Normal file
22
MimeTypes/MimeTypes.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package MimeTypes
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
type MimeType struct {
|
||||||
|
MimeType string
|
||||||
|
FileExtension []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func DetectType(filename string) (mime MimeType, err error) {
|
||||||
|
for _, typeElement := range allTypes {
|
||||||
|
for _, extension := range typeElement.FileExtension {
|
||||||
|
if strings.HasSuffix(filename, extension) {
|
||||||
|
mime = typeElement
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mime = TypeUnknown
|
||||||
|
return
|
||||||
|
}
|
7
MimeTypes/Write2HTTP.go
Normal file
7
MimeTypes/Write2HTTP.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package MimeTypes
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
func Write2HTTP(response http.ResponseWriter, mime MimeType) {
|
||||||
|
response.Header().Add(`Content-Type`, mime.MimeType)
|
||||||
|
}
|
6
NumGen/BadNumber.go
Normal file
6
NumGen/BadNumber.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package NumGen
|
||||||
|
|
||||||
|
func BadNumber() (result int64) {
|
||||||
|
result = badNumber64
|
||||||
|
return
|
||||||
|
}
|
30
NumGen/GetNumber.go
Normal file
30
NumGen/GetNumber.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package NumGen
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
import "net/url"
|
||||||
|
import "strconv"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Shutdown"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
func GetNextInt64(name string) (result int64) {
|
||||||
|
result = badNumber64
|
||||||
|
|
||||||
|
if Shutdown.IsDown() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if responseData, errRequest := http.PostForm(getHandler, url.Values{"name": {name}, "password": {correctPassword}}); errRequest != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameGENERATOR, `Requesting the next number was not possible.`, errRequest.Error())
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
nextNumberText := responseData.Header.Get(`nextNumber`)
|
||||||
|
if number, errAtio := strconv.Atoi(nextNumberText); errAtio != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameGENERATOR, `It was not possible to convert the answer into an int64.`, errAtio.Error())
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
result = int64(number)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
41
NumGen/HandlerGetNext.go
Normal file
41
NumGen/HandlerGetNext.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package NumGen
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
import "net/http"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Shutdown"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
func HandlerGetNext(response http.ResponseWriter, request *http.Request) {
|
||||||
|
if Shutdown.IsDown() {
|
||||||
|
http.NotFound(response, request)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isActive {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityCritical, LM.ImpactNone, LM.MessageNameCONFIGURATION, `Called the get handler on an inactive host.`, `Wrong configuration?`)
|
||||||
|
http.NotFound(response, request)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if correctPassword == `` {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameSECURITY, `No communication password was set.`)
|
||||||
|
http.NotFound(response, request)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
name := request.FormValue(`name`)
|
||||||
|
pwd := request.FormValue(`password`)
|
||||||
|
|
||||||
|
if pwd != correctPassword {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelSECURITY, LM.SeverityCritical, LM.ImpactNone, LM.MessageNamePASSWORD, `A wrong password was used to access this system handler.`, `This should never happens: Is this a hacking attempt?`, `IP address of requester=`+request.RemoteAddr)
|
||||||
|
http.NotFound(response, request)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelDEBUG, LM.MessageNameANALYSIS, `Next number requested.`, name, pwd)
|
||||||
|
channel := requestChannel4Name(name)
|
||||||
|
nextNumber := <-channel
|
||||||
|
|
||||||
|
response.Header().Add(`nextNumber`, fmt.Sprintf(`%d`, nextNumber))
|
||||||
|
}
|
38
NumGen/Init.go
Normal file
38
NumGen/Init.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package NumGen
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
import "strconv"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Tools"
|
||||||
|
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, `Init the number generator.`)
|
||||||
|
|
||||||
|
channelListLock.Lock()
|
||||||
|
defer channelListLock.Unlock()
|
||||||
|
|
||||||
|
correctPassword = ConfigurationDB.Read(`InternalCommPassword`)
|
||||||
|
activeHost := ConfigurationDB.Read(`NumGenActiveHosts`)
|
||||||
|
isActive = strings.Contains(activeHost, Tools.ThisHostname())
|
||||||
|
getHandler = ConfigurationDB.Read(`NumGenGetHandler`)
|
||||||
|
|
||||||
|
if isActive {
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.MessageNameCONFIGURATION, `The number generator is active on this host.`, `This host is producer and consumer.`)
|
||||||
|
|
||||||
|
channelBufferSizeText := ConfigurationDB.Read(`NumGenBufferSize`)
|
||||||
|
if bufferSizeNumber, errBufferSizeNumber := strconv.Atoi(channelBufferSizeText); errBufferSizeNumber != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactMiddle, LM.MessageNameCONFIGURATION, `Was not able to parse the configuration value of NumGenBufferSize.`, errBufferSizeNumber.Error(), `Use the default value now!`)
|
||||||
|
} else {
|
||||||
|
channelBufferSize = bufferSizeNumber
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `The buffer size for the number generator was loaded.`, `Buffer size=`+channelBufferSizeText)
|
||||||
|
}
|
||||||
|
|
||||||
|
channelList = make(map[string]chan int64)
|
||||||
|
|
||||||
|
initDB()
|
||||||
|
} else {
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.MessageNameCONFIGURATION, `The number generator is not active on this host.`, `This host is just a consumer.`)
|
||||||
|
}
|
||||||
|
}
|
28
NumGen/InitDB.go
Normal file
28
NumGen/InitDB.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package NumGen
|
||||||
|
|
||||||
|
import "labix.org/v2/mgo"
|
||||||
|
import "github.com/SommerEngineering/Ocean/CustomerDB"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
func initDB() {
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Start init of number generator collection.`)
|
||||||
|
defer Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Done init of number generator collection.`)
|
||||||
|
|
||||||
|
// Get the database:
|
||||||
|
db = CustomerDB.DB()
|
||||||
|
|
||||||
|
if db == nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameDATABASE, `Was not able to get the customer database.`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get my collection:
|
||||||
|
collectionNumGen = db.C(`NumGen`)
|
||||||
|
|
||||||
|
// Take care about the indexes:
|
||||||
|
indexName := mgo.Index{}
|
||||||
|
indexName.Key = []string{`Name`}
|
||||||
|
indexName.Unique = true
|
||||||
|
collectionNumGen.EnsureIndex(indexName)
|
||||||
|
}
|
68
NumGen/Producer.go
Normal file
68
NumGen/Producer.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package NumGen
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
import "labix.org/v2/mgo/bson"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Shutdown"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
func producer(name string) {
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `The NumGen producer is now starting.`, `name=`+name)
|
||||||
|
|
||||||
|
// Get my channel:
|
||||||
|
myChannel := requestChannel4Name(name)
|
||||||
|
|
||||||
|
// Read my next free number:
|
||||||
|
currentNextFreeNumber := nextFreeNumberFromDatabase(name)
|
||||||
|
|
||||||
|
// Where is the next "reload"?
|
||||||
|
nextReload := currentNextFreeNumber + int64(channelBufferSize)
|
||||||
|
|
||||||
|
// Set the next free number to the database:
|
||||||
|
updateNextFreeNumber2Database(name, nextReload)
|
||||||
|
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `The NumGen producer is now running.`, `name=`+name)
|
||||||
|
for nextNumber := currentNextFreeNumber; true; {
|
||||||
|
if Shutdown.IsDown() {
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSHUTDOWN, `The NumGen producer is now down.`, `name=`+name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if nextNumber > nextReload {
|
||||||
|
nextReload = nextReload + int64(channelBufferSize)
|
||||||
|
updateNextFreeNumber2Database(name, nextReload)
|
||||||
|
|
||||||
|
// Enables the administrator to monitor the frequence of chunks and is able to reconfigure the settings:
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelDEBUG, LM.MessageNamePRODUCER, `The NumGen producer creates the next chunk.`, `name=`+name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enqueue the next number:
|
||||||
|
select {
|
||||||
|
case myChannel <- nextNumber:
|
||||||
|
nextNumber++
|
||||||
|
case <-time.After(time.Millisecond * 500):
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func nextFreeNumberFromDatabase(name string) (result int64) {
|
||||||
|
selection := bson.D{{`Name`, name}}
|
||||||
|
searchResult := NumberGenScheme{}
|
||||||
|
|
||||||
|
count, _ := collectionNumGen.Find(selection).Count()
|
||||||
|
if count == 1 {
|
||||||
|
collectionNumGen.Find(selection).One(&searchResult)
|
||||||
|
result = searchResult.NextFreeNumber
|
||||||
|
} else {
|
||||||
|
searchResult.Name = name
|
||||||
|
searchResult.NextFreeNumber = startValue64
|
||||||
|
collectionNumGen.Insert(searchResult)
|
||||||
|
result = searchResult.NextFreeNumber
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateNextFreeNumber2Database(name string, nextFreeNumber int64) {
|
||||||
|
selection := bson.D{{`Name`, name}}
|
||||||
|
collectionNumGen.Update(selection, NumberGenScheme{Name: name, NextFreeNumber: nextFreeNumber})
|
||||||
|
}
|
38
NumGen/RequestChannel.go
Normal file
38
NumGen/RequestChannel.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package NumGen
|
||||||
|
|
||||||
|
import "github.com/SommerEngineering/Ocean/Shutdown"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
func requestChannel4Name(name string) (result chan int64) {
|
||||||
|
|
||||||
|
if Shutdown.IsDown() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isActive {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityCritical, LM.ImpactNone, LM.MessageNameCONFIGURATION, `Called the requestChannel4Name() on an inactive host.`, `Wrong configuration?`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
channelListLock.RLock()
|
||||||
|
channel, isPresent := channelList[name]
|
||||||
|
channelListLock.RUnlock()
|
||||||
|
|
||||||
|
if isPresent {
|
||||||
|
result = channel
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the entry:
|
||||||
|
newChannel := make(chan int64, channelBufferSize)
|
||||||
|
result = newChannel
|
||||||
|
|
||||||
|
channelListLock.Lock()
|
||||||
|
channelList[name] = newChannel
|
||||||
|
channelListLock.Unlock()
|
||||||
|
|
||||||
|
// Create the new producer:
|
||||||
|
go producer(name)
|
||||||
|
return
|
||||||
|
}
|
6
NumGen/Scheme.go
Normal file
6
NumGen/Scheme.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package NumGen
|
||||||
|
|
||||||
|
type NumberGenScheme struct {
|
||||||
|
Name string `bson:"Name"`
|
||||||
|
NextFreeNumber int64 `bson:"NextFreeNumber"`
|
||||||
|
}
|
11
NumGen/Shutdown.go
Normal file
11
NumGen/Shutdown.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package NumGen
|
||||||
|
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
type ShutdownFunction struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a ShutdownFunction) Shutdown() {
|
||||||
|
Log.LogShort(senderName, LM.CategoryAPP, LM.LevelWARN, LM.MessageNameSHUTDOWN, `Shutting down the number generator.`)
|
||||||
|
}
|
22
NumGen/Variables.go
Normal file
22
NumGen/Variables.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package NumGen
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
import "labix.org/v2/mgo"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
var (
|
||||||
|
correctPassword string = ``
|
||||||
|
senderName LM.Sender = `System::NumGen::Producer`
|
||||||
|
isActive bool = false
|
||||||
|
getHandler string = ``
|
||||||
|
db *mgo.Database = nil
|
||||||
|
collectionNumGen *mgo.Collection = nil
|
||||||
|
channelBufferSize int = 10
|
||||||
|
channelList map[string]chan int64 = nil
|
||||||
|
channelListLock sync.RWMutex = sync.RWMutex{}
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
badNumber64 int64 = 9222222222222222222
|
||||||
|
startValue64 int64 = -9223372036854775808
|
||||||
|
)
|
17
Robots/Handler.go
Normal file
17
Robots/Handler.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package Robots
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
import "net/http"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Shutdown"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
func HandlerRobots(response http.ResponseWriter, request *http.Request) {
|
||||||
|
if Shutdown.IsDown() {
|
||||||
|
http.NotFound(response, request)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameNETWORK, `The robots.txt was requested.`, request.RemoteAddr)
|
||||||
|
fmt.Fprintf(response, `%s`, robotsContent)
|
||||||
|
}
|
10
Robots/Init.go
Normal file
10
Robots/Init.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package Robots
|
||||||
|
|
||||||
|
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, `Init the robots component.`)
|
||||||
|
robotsContent = ConfigurationDB.Read(`robots.txt`)
|
||||||
|
}
|
9
Robots/Variables.go
Normal file
9
Robots/Variables.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package Robots
|
||||||
|
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
var (
|
||||||
|
senderName LM.Sender = `System::Robots`
|
||||||
|
robotsContent string = `User-agent: *
|
||||||
|
Disallow: /`
|
||||||
|
)
|
6
Shutdown/Check.go
Normal file
6
Shutdown/Check.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package Shutdown
|
||||||
|
|
||||||
|
func IsDown() (result bool) {
|
||||||
|
result = stopAllRequests
|
||||||
|
return
|
||||||
|
}
|
13
Shutdown/Init.go
Normal file
13
Shutdown/Init.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package Shutdown
|
||||||
|
|
||||||
|
import "container/list"
|
||||||
|
import "os/signal"
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
func InitShutdown() {
|
||||||
|
shutdownHandlers = list.New()
|
||||||
|
|
||||||
|
// Apply the shutdown handler:
|
||||||
|
signal.Notify(shutdownSignal, os.Interrupt, os.Kill)
|
||||||
|
go executeShutdown()
|
||||||
|
}
|
29
Shutdown/Shutdown.go
Normal file
29
Shutdown/Shutdown.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package Shutdown
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
type ShutdownHandler interface {
|
||||||
|
Shutdown()
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddShutdownHandler(handler ShutdownHandler) {
|
||||||
|
shutdownHandlers.PushBack(handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func executeShutdown() {
|
||||||
|
sig := <-shutdownSignal
|
||||||
|
stopAllRequests = true
|
||||||
|
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameSHUTDOWN, `The system was called to shutting down.`, sig.String(), `Call now all shutdown handlers.`)
|
||||||
|
for handler := shutdownHandlers.Front(); handler != nil; handler = handler.Next() {
|
||||||
|
h := handler.Value.(ShutdownHandler)
|
||||||
|
h.Shutdown()
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameSHUTDOWN, `The system is shutting down now.`)
|
||||||
|
Log.Flush()
|
||||||
|
|
||||||
|
os.Exit(6)
|
||||||
|
}
|
12
Shutdown/Variables.go
Normal file
12
Shutdown/Variables.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package Shutdown
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
import "container/list"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
var (
|
||||||
|
shutdownSignal chan os.Signal = make(chan os.Signal)
|
||||||
|
shutdownHandlers *list.List = nil
|
||||||
|
senderName LM.Sender = `System::Shutdown`
|
||||||
|
stopAllRequests bool = false
|
||||||
|
)
|
47
StaticFiles/FindAndReadFile.go
Normal file
47
StaticFiles/FindAndReadFile.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package StaticFiles
|
||||||
|
|
||||||
|
import "io/ioutil"
|
||||||
|
import "bytes"
|
||||||
|
import "archive/zip"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Shutdown"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
func FindAndReadFile(filename string) (result []byte) {
|
||||||
|
if Shutdown.IsDown() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the path:
|
||||||
|
path := filename
|
||||||
|
|
||||||
|
// Read the content from the ZIP file:
|
||||||
|
reader, readerError := zip.NewReader(bytes.NewReader(zipData), int64(len(zipData)))
|
||||||
|
if readerError != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameREAD, `Was not able to read the ZIP file.`, readerError.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range reader.File {
|
||||||
|
if file.Name == path {
|
||||||
|
|
||||||
|
fileReader, openError := file.Open()
|
||||||
|
defer fileReader.Close()
|
||||||
|
|
||||||
|
if openError == nil {
|
||||||
|
contentData, readError := ioutil.ReadAll(fileReader)
|
||||||
|
|
||||||
|
if readError != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameREAD, `Was not able to read the content of the desired file.`, readError.Error(), path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result = contentData
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameNOTFOUND, `The desired file is not part of the ZIP file.`, `Do you use an old version?`, path)
|
||||||
|
return
|
||||||
|
}
|
57
StaticFiles/Handler.go
Normal file
57
StaticFiles/Handler.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package StaticFiles
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
import "strings"
|
||||||
|
import "net/http"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Shutdown"
|
||||||
|
import "github.com/SommerEngineering/Ocean/MimeTypes"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
func HandlerStaticFiles(response http.ResponseWriter, request *http.Request) {
|
||||||
|
if Shutdown.IsDown() {
|
||||||
|
http.NotFound(response, request)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the path:
|
||||||
|
path := strings.Replace(request.RequestURI, `/staticFiles/`, ``, 1)
|
||||||
|
path = strings.Replace(path, `%20`, ` `, -1)
|
||||||
|
fileType := ``
|
||||||
|
|
||||||
|
// Determine the MIME type:
|
||||||
|
if mimeType, errMime := MimeTypes.DetectType(path); errMime != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityMiddle, LM.ImpactMiddle, LM.MessageNameNOTFOUND, `Was not able to detect the MIME type of the font.`, errMime.Error(), path)
|
||||||
|
http.NotFound(response, request)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
fileType = mimeType.MimeType
|
||||||
|
}
|
||||||
|
|
||||||
|
if fileType == `` {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelSECURITY, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNameNOTFOUND, `The mime type is unknown.`, path)
|
||||||
|
http.NotFound(response, request)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
contentData := FindAndReadFile(path)
|
||||||
|
if contentData == nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameDATABASE, `The desired file was not found.`, path)
|
||||||
|
http.NotFound(response, request)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fileLenText := fmt.Sprintf(`%d`, len(contentData))
|
||||||
|
response.Header().Add(`Content-Length`, fileLenText)
|
||||||
|
response.Header().Add(`Content-Type`, fileType)
|
||||||
|
response.Write(contentData)
|
||||||
|
|
||||||
|
if logStaticFileRequests {
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameBROWSER, `A static file was requested.`, path)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
http.NotFound(response, request)
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameNOTFOUND, `The desired file is not part of the ZIP file.`, `Do you use an old version?`, path)
|
||||||
|
return
|
||||||
|
}
|
44
StaticFiles/Init.go
Normal file
44
StaticFiles/Init.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package StaticFiles
|
||||||
|
|
||||||
|
import "io/ioutil"
|
||||||
|
import "github.com/SommerEngineering/Ocean/CustomerDB"
|
||||||
|
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 static files component.`)
|
||||||
|
defer Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `Starting the static files component done.`)
|
||||||
|
|
||||||
|
if ConfigurationDB.Read(`EnableStaticFiles`) != `true` {
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `Static files are disabled.`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `Static files are enabled.`)
|
||||||
|
|
||||||
|
// Read the configuration:
|
||||||
|
if ConfigurationDB.Read(`MapStaticFiles2Root`) == `true` {
|
||||||
|
startFile4Map2Root = ConfigurationDB.Read(`MapStaticFiles2RootRootFile`)
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `The desired root document was set.`, `rootDocument=`+startFile4Map2Root)
|
||||||
|
}
|
||||||
|
|
||||||
|
logStaticFileRequests = ConfigurationDB.Read(`LogStaticFileRequests`) == `true`
|
||||||
|
|
||||||
|
// Read the static files' data from GridFS:
|
||||||
|
gridFS := CustomerDB.GridFS()
|
||||||
|
if gridFile, errGridFile := gridFS.Open(`staticFiles.zip`); errGridFile != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameDATABASE, `Was not able to open the static files out of the GridFS!`, errGridFile.Error())
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
defer gridFile.Close()
|
||||||
|
if data, ioError := ioutil.ReadAll(gridFile); ioError != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameDATABASE, `Was not able to read the static files.`, ioError.Error())
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
zipData = data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
19
StaticFiles/Map2Root.go
Normal file
19
StaticFiles/Map2Root.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package StaticFiles
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Shutdown"
|
||||||
|
|
||||||
|
func HandlerMapStaticFiles2Root(response http.ResponseWriter, request *http.Request) {
|
||||||
|
if Shutdown.IsDown() {
|
||||||
|
http.NotFound(response, request)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if request.RequestURI == `/` {
|
||||||
|
request.RequestURI = `/staticFiles/` + startFile4Map2Root
|
||||||
|
} else {
|
||||||
|
request.RequestURI = `/staticFiles` + request.RequestURI
|
||||||
|
}
|
||||||
|
|
||||||
|
HandlerStaticFiles(response, request)
|
||||||
|
}
|
10
StaticFiles/Variables.go
Normal file
10
StaticFiles/Variables.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package StaticFiles
|
||||||
|
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
var (
|
||||||
|
senderName LM.Sender = `System::StaticFiles`
|
||||||
|
startFile4Map2Root string = `index.html`
|
||||||
|
logStaticFileRequests bool = false
|
||||||
|
zipData []byte = nil
|
||||||
|
)
|
8
System/ICCCStart.go
Normal file
8
System/ICCCStart.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package System
|
||||||
|
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
func icccSystemStart(data map[string][]string) {
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `The system is now up and ready.`)
|
||||||
|
}
|
90
System/Init.go
Normal file
90
System/Init.go
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package System
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
import "runtime"
|
||||||
|
import "strconv"
|
||||||
|
import "github.com/SommerEngineering/Ocean/ICCC"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Shutdown"
|
||||||
|
import "github.com/SommerEngineering/Ocean/ConfigurationDB"
|
||||||
|
import "github.com/SommerEngineering/Ocean/CustomerDB"
|
||||||
|
import "github.com/SommerEngineering/Ocean/NumGen"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
func initSystem() {
|
||||||
|
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `The system is now starting.`)
|
||||||
|
|
||||||
|
// Set the desired amount of CPUs:
|
||||||
|
utilizeCPUs := 2
|
||||||
|
if value, err := strconv.Atoi(ConfigurationDB.Read(`OceanUtilizeCPUs`)); err != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNameCONFIGURATION, `Was not able to read the OceanUtilizeCPUs configuration.`, `Use the default value instead.`)
|
||||||
|
} else {
|
||||||
|
utilizeCPUs = value
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime.GOMAXPROCS(utilizeCPUs)
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `Configuration OceanUtilizeCPUs is set.`, fmt.Sprintf(`value=%d`, utilizeCPUs))
|
||||||
|
|
||||||
|
// Apply all desired logging devices:
|
||||||
|
initLoggingDevices()
|
||||||
|
|
||||||
|
// Set the logging buffer size:
|
||||||
|
logBufferSize := 500
|
||||||
|
if value, err := strconv.Atoi(ConfigurationDB.Read(`LogBufferSize`)); err != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNameCONFIGURATION, `Was not able to read the LogBufferSize configuration.`, `Use the default value instead.`)
|
||||||
|
} else {
|
||||||
|
logBufferSize = value
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.SetBufferSize(logBufferSize)
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `Configuration LogBufferSize is set.`, fmt.Sprintf(`value=%d`, logBufferSize))
|
||||||
|
|
||||||
|
// Set the logging device delay (number of events):
|
||||||
|
logDeviceDelayNumberEvents := 600
|
||||||
|
if value, err := strconv.Atoi(ConfigurationDB.Read(`LogDeviceDelayNumberEvents`)); err != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNameCONFIGURATION, `Was not able to read the LogDeviceDelayNumberEvents configuration.`, `Use the default value instead.`)
|
||||||
|
} else {
|
||||||
|
logDeviceDelayNumberEvents = value
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.SetDeviceDelayNumberEvents(logDeviceDelayNumberEvents)
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `Configuration LogDeviceDelayNumberEvents is set.`, fmt.Sprintf(`value=%d`, logDeviceDelayNumberEvents))
|
||||||
|
|
||||||
|
// Set the logging device delay time to flush (seconds):
|
||||||
|
logDeviceDelayTime2FlushSeconds := 5
|
||||||
|
if value, err := strconv.Atoi(ConfigurationDB.Read(`LogDeviceDelayTime2FlushSeconds`)); err != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNameCONFIGURATION, `Was not able to read the LogDeviceDelayTime2FlushSeconds configuration.`, `Use the default value instead.`)
|
||||||
|
} else {
|
||||||
|
logDeviceDelayTime2FlushSeconds = value
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.SetDeviceDelayTimeoutSeconds(logDeviceDelayTime2FlushSeconds)
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `Configuration LogDeviceDelayTime2FlushSeconds is set.`, fmt.Sprintf(`value=%d`, logDeviceDelayTime2FlushSeconds))
|
||||||
|
|
||||||
|
// Set the logging timeout (seconds):
|
||||||
|
logTimeoutSeconds := 3
|
||||||
|
if value, err := strconv.Atoi(ConfigurationDB.Read(`LogTimeoutSeconds`)); err != nil {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNameCONFIGURATION, `Was not able to read the LogTimeoutSeconds configuration.`, `Use the default value instead.`)
|
||||||
|
} else {
|
||||||
|
logTimeoutSeconds = value
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.SetTimeoutSeconds(logTimeoutSeconds)
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `Configuration LogTimeoutSeconds is set.`, fmt.Sprintf(`value=%d`, logTimeoutSeconds))
|
||||||
|
|
||||||
|
// Apply these changes:
|
||||||
|
Log.ApplyConfigurationChanges()
|
||||||
|
Log.LoggingIsReady()
|
||||||
|
|
||||||
|
// Register all system shutdown handlers:
|
||||||
|
Shutdown.InitShutdown()
|
||||||
|
Shutdown.AddShutdownHandler(ICCC.ShutdownFunction{})
|
||||||
|
Shutdown.AddShutdownHandler(NumGen.ShutdownFunction{})
|
||||||
|
Shutdown.AddShutdownHandler(ConfigurationDB.ShutdownFunction{})
|
||||||
|
Shutdown.AddShutdownHandler(CustomerDB.ShutdownFunction{})
|
||||||
|
// The logging subsystem is not registered here, because it will be automated called at the end
|
||||||
|
|
||||||
|
// Register all system ICCC commands:
|
||||||
|
ICCC.Registrar(ICCC.ChannelSYSTEM, `System::Start`, icccSystemStart)
|
||||||
|
}
|
30
System/InitHandlers.go
Normal file
30
System/InitHandlers.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package System
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
import "github.com/SommerEngineering/Ocean/ICCC"
|
||||||
|
import "github.com/SommerEngineering/Ocean/WebContent"
|
||||||
|
import "github.com/SommerEngineering/Ocean/StaticFiles"
|
||||||
|
import "github.com/SommerEngineering/Ocean/NumGen"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Robots"
|
||||||
|
import "github.com/SommerEngineering/Ocean/ConfigurationDB"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
func InitHandlers() {
|
||||||
|
|
||||||
|
initSystem()
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `Register now all system handlers.`)
|
||||||
|
|
||||||
|
http.HandleFunc(`/framework/`, WebContent.HandlerDeliverFramework)
|
||||||
|
http.HandleFunc(`/staticFiles/`, StaticFiles.HandlerStaticFiles)
|
||||||
|
http.HandleFunc(`/next/number`, NumGen.HandlerGetNext)
|
||||||
|
http.HandleFunc(`/robots.txt`, Robots.HandlerRobots)
|
||||||
|
http.HandleFunc(`/ICCC`, ICCC.ICCCHandler)
|
||||||
|
|
||||||
|
if ConfigurationDB.Read(`MapStaticFiles2Root`) == "true" {
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `The static files are mapped to the root.`)
|
||||||
|
http.HandleFunc(`/`, StaticFiles.HandlerMapStaticFiles2Root)
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `Done with registering all system handler.`)
|
||||||
|
}
|
35
System/RegisterLoggingDevices.go
Normal file
35
System/RegisterLoggingDevices.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package System
|
||||||
|
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log/DeviceConsole"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log/DeviceDatabase"
|
||||||
|
import "github.com/SommerEngineering/Ocean/ConfigurationDB"
|
||||||
|
import "github.com/SommerEngineering/Ocean/Log"
|
||||||
|
import LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
|
||||||
|
func initLoggingDevices() {
|
||||||
|
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Init the logging devices.`)
|
||||||
|
defer Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Init the logging devices done.`)
|
||||||
|
|
||||||
|
if ConfigurationDB.Read(`LogUseDatabaseLogging`) == `true` {
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `The database logger is active.`)
|
||||||
|
activateDatabaseLogger()
|
||||||
|
} else {
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `The database logger is NOT active.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ConfigurationDB.Read(`LogUseConsoleLogging`) == `true` {
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `The console logger is active.`)
|
||||||
|
activateConsoleLogger()
|
||||||
|
} else {
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `The console logger is NOT active.`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func activateDatabaseLogger() {
|
||||||
|
DeviceDatabase.ActivateLoggingDevice()
|
||||||
|
}
|
||||||
|
|
||||||
|
func activateConsoleLogger() {
|
||||||
|
DeviceConsole.ActivateLoggingDevice()
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user