Add documentation
This commit is contained in:
parent
69d0bdabf4
commit
6dab89a1d4
@ -4,6 +4,7 @@ import (
|
|||||||
"github.com/SommerEngineering/Ocean/BinaryAssets/SourceCodePro"
|
"github.com/SommerEngineering/Ocean/BinaryAssets/SourceCodePro"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Reads the content for a file.
|
||||||
func GetData(filename string) (data []byte) {
|
func GetData(filename string) (data []byte) {
|
||||||
if obj, err := SourceCodePro.Asset(filename); err != nil {
|
if obj, err := SourceCodePro.Asset(filename); err != nil {
|
||||||
return
|
return
|
||||||
|
@ -10,8 +10,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Handler to access the binary assets from the web.
|
||||||
func HandlerBinaryAssets(response http.ResponseWriter, request *http.Request) {
|
func HandlerBinaryAssets(response http.ResponseWriter, request *http.Request) {
|
||||||
|
|
||||||
|
// Case: The server is going down.
|
||||||
if Shutdown.IsDown() {
|
if Shutdown.IsDown() {
|
||||||
http.NotFound(response, request)
|
http.NotFound(response, request)
|
||||||
return
|
return
|
||||||
@ -31,12 +33,14 @@ func HandlerBinaryAssets(response http.ResponseWriter, request *http.Request) {
|
|||||||
fileType = mimeType.MimeType
|
fileType = mimeType.MimeType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Case: No MIME type determined?
|
||||||
if fileType == `` {
|
if fileType == `` {
|
||||||
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelSECURITY, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNameNOTFOUND, `The mime type is unknown.`, path)
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelSECURITY, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNameNOTFOUND, `The mime type is unknown.`, path)
|
||||||
http.NotFound(response, request)
|
http.NotFound(response, request)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read the content:
|
||||||
contentData := GetData(path)
|
contentData := GetData(path)
|
||||||
if contentData == nil {
|
if contentData == nil {
|
||||||
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameDATABASE, `The desired file was not found.`, path)
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameDATABASE, `The desired file was not found.`, path)
|
||||||
@ -44,6 +48,7 @@ func HandlerBinaryAssets(response http.ResponseWriter, request *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write the meta data and the content to the client:
|
||||||
fileLenText := fmt.Sprintf(`%d`, len(contentData))
|
fileLenText := fmt.Sprintf(`%d`, len(contentData))
|
||||||
response.Header().Add(`Content-Length`, fileLenText)
|
response.Header().Add(`Content-Length`, fileLenText)
|
||||||
response.Header().Add(`Content-Type`, fileType)
|
response.Header().Add(`Content-Type`, fileType)
|
||||||
|
@ -5,5 +5,5 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
senderName LM.Sender = `System::BinaryAssets`
|
senderName LM.Sender = `System::BinaryAssets` // This is the name for logging event from this package
|
||||||
)
|
)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package Configuration
|
package Configuration
|
||||||
|
|
||||||
|
// This is the init function for this package.
|
||||||
func init() {
|
func init() {
|
||||||
readConfiguration()
|
readConfiguration()
|
||||||
isInit = true
|
isInit = true
|
||||||
|
@ -8,8 +8,8 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Function to read the configuration file.
|
||||||
func readConfiguration() {
|
func readConfiguration() {
|
||||||
|
|
||||||
if isInit {
|
if isInit {
|
||||||
Log.LogFull(senderName, Meta.CategorySYSTEM, Meta.LevelWARN, Meta.SeverityNone, Meta.ImpactNone, Meta.MessageNameINIT, `The configuration package is already init!`)
|
Log.LogFull(senderName, Meta.CategorySYSTEM, Meta.LevelWARN, Meta.SeverityNone, Meta.ImpactNone, Meta.MessageNameINIT, `The configuration package is already init!`)
|
||||||
return
|
return
|
||||||
@ -17,13 +17,14 @@ func readConfiguration() {
|
|||||||
Log.LogShort(senderName, Meta.CategorySYSTEM, Meta.LevelINFO, Meta.MessageNameCONFIGURATION, `Init of configuration starting.`)
|
Log.LogShort(senderName, Meta.CategorySYSTEM, Meta.LevelINFO, Meta.MessageNameCONFIGURATION, `Init of configuration starting.`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Access to the working directory?
|
||||||
currentDir, dirError := os.Getwd()
|
currentDir, dirError := os.Getwd()
|
||||||
|
|
||||||
if dirError != nil {
|
if dirError != nil {
|
||||||
panic(`Was not able to read the working directory: ` + dirError.Error())
|
panic(`Was not able to read the working directory: ` + dirError.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Access to the configuration file?
|
||||||
currentPath := filepath.Join(currentDir, filename)
|
currentPath := filepath.Join(currentDir, filename)
|
||||||
if _, errFile := os.Stat(currentPath); errFile != nil {
|
if _, errFile := os.Stat(currentPath); errFile != nil {
|
||||||
if os.IsNotExist(errFile) {
|
if os.IsNotExist(errFile) {
|
||||||
@ -33,6 +34,7 @@ func readConfiguration() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Open the file:
|
||||||
file, fileError := os.Open(currentPath)
|
file, fileError := os.Open(currentPath)
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
@ -41,6 +43,7 @@ func readConfiguration() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to decode / parse the file:
|
||||||
decoder := json.NewDecoder(file)
|
decoder := json.NewDecoder(file)
|
||||||
decError := decoder.Decode(&configuration)
|
decError := decoder.Decode(&configuration)
|
||||||
|
|
||||||
|
@ -9,5 +9,5 @@ var (
|
|||||||
filename = "configuration.json" // Where is the configuration located?
|
filename = "configuration.json" // Where is the configuration located?
|
||||||
configuration Meta.Configuration = Meta.Configuration{} // The loaded configuration
|
configuration Meta.Configuration = Meta.Configuration{} // The loaded configuration
|
||||||
isInit = false // Is the configuration loaded?
|
isInit = false // Is the configuration loaded?
|
||||||
senderName LM.Sender = `System::Configuration`
|
senderName LM.Sender = `System::Configuration` // This is the name for logging event from this package
|
||||||
)
|
)
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"gopkg.in/mgo.v2/bson"
|
"gopkg.in/mgo.v2/bson"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Internal function to check the system configuration.
|
||||||
func checkConfiguration() {
|
func checkConfiguration() {
|
||||||
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameDATABASE, `Check now the configuration database.`)
|
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.`)
|
defer Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameDATABASE, `Done checking the configuration database.`)
|
||||||
@ -53,15 +54,14 @@ func checkConfiguration() {
|
|||||||
Disallow:`)
|
Disallow:`)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Use this function to ensure that the database contains at least a default value for the configuration.
|
||||||
Use this function to ensure that the database contains at least a default value for the configuration.
|
|
||||||
*/
|
|
||||||
func CheckSingleConfigurationPresentsAndAddIfMissing(name, value string) {
|
func CheckSingleConfigurationPresentsAndAddIfMissing(name, value string) {
|
||||||
if !checkSingleConfigurationPresents(name) {
|
if !checkSingleConfigurationPresents(name) {
|
||||||
addSingleConfiguration(name, value)
|
addSingleConfiguration(name, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if a configuration value is present.
|
||||||
func checkSingleConfigurationPresents(name string) (result bool) {
|
func checkSingleConfigurationPresents(name string) (result bool) {
|
||||||
selection := bson.D{{"Name", name}}
|
selection := bson.D{{"Name", name}}
|
||||||
count, _ := collection.Find(selection).Count()
|
count, _ := collection.Find(selection).Count()
|
||||||
@ -69,6 +69,7 @@ func checkSingleConfigurationPresents(name string) (result bool) {
|
|||||||
return count > 0
|
return count > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adds a configuration value.
|
||||||
func addSingleConfiguration(name, value string) {
|
func addSingleConfiguration(name, value string) {
|
||||||
entry := ConfigurationDBEntry{}
|
entry := ConfigurationDBEntry{}
|
||||||
entry.Name = name
|
entry.Name = name
|
||||||
|
@ -7,8 +7,8 @@ import (
|
|||||||
"gopkg.in/mgo.v2"
|
"gopkg.in/mgo.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// The init function for this package.
|
||||||
func init() {
|
func init() {
|
||||||
|
|
||||||
config := Configuration.Read()
|
config := Configuration.Read()
|
||||||
|
|
||||||
// Connect to MongoDB:
|
// Connect to MongoDB:
|
||||||
@ -40,6 +40,8 @@ func init() {
|
|||||||
// Take care about the index:
|
// Take care about the index:
|
||||||
collection.EnsureIndexKey(`Name`)
|
collection.EnsureIndexKey(`Name`)
|
||||||
|
|
||||||
|
// Check the system configuration:
|
||||||
checkConfiguration()
|
checkConfiguration()
|
||||||
|
|
||||||
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameDATABASE, `The configuration database is now ready.`)
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameDATABASE, `The configuration database is now ready.`)
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,7 @@ import (
|
|||||||
"gopkg.in/mgo.v2/bson"
|
"gopkg.in/mgo.v2/bson"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
// This function reads the current configuration value.
|
||||||
This function reads the current configuration value.
|
|
||||||
*/
|
|
||||||
func Read(name string) (value string) {
|
func Read(name string) (value string) {
|
||||||
if name == `` {
|
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!`)
|
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!`)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package ConfigurationDB
|
package ConfigurationDB
|
||||||
|
|
||||||
|
// The type for a configuration entry.
|
||||||
type ConfigurationDBEntry struct {
|
type ConfigurationDBEntry struct {
|
||||||
Name string `bson:"Name"`
|
Name string `bson:"Name"`
|
||||||
Value string `bson:"Value"`
|
Value string `bson:"Value"`
|
||||||
|
@ -5,15 +5,11 @@ import (
|
|||||||
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
// The type for the shutdown function.
|
||||||
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 {
|
type ShutdownFunction struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// If the Ocean server is shutting down, this function is called to close the database.
|
||||||
If the Ocean server is shutting down, this function is called to close the database.
|
|
||||||
*/
|
|
||||||
func (a ShutdownFunction) Shutdown() {
|
func (a ShutdownFunction) Shutdown() {
|
||||||
Log.LogShort(senderName, LM.CategoryAPP, LM.LevelWARN, LM.MessageNameSHUTDOWN, `Close now the configuration database connection.`)
|
Log.LogShort(senderName, LM.CategoryAPP, LM.LevelWARN, LM.MessageNameSHUTDOWN, `Close now the configuration database connection.`)
|
||||||
db.Logout()
|
db.Logout()
|
||||||
|
@ -7,9 +7,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
session *mgo.Session = nil
|
session *mgo.Session = nil // The database session
|
||||||
db *mgo.Database = nil
|
db *mgo.Database = nil // The database
|
||||||
collection *mgo.Collection = nil
|
collection *mgo.Collection = nil // The database collection
|
||||||
config Meta.Configuration = Meta.Configuration{}
|
config Meta.Configuration = Meta.Configuration{} // The configuration file's data
|
||||||
senderName LM.Sender = `System::ConfigurationDB`
|
senderName LM.Sender = `System::ConfigurationDB` // This is the name for logging event from this package
|
||||||
)
|
)
|
||||||
|
@ -6,15 +6,14 @@ import (
|
|||||||
"gopkg.in/mgo.v2/bson"
|
"gopkg.in/mgo.v2/bson"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
// This function writes the configuration value.
|
||||||
This function writes the configuration value.
|
|
||||||
*/
|
|
||||||
func Write(name, value string) {
|
func Write(name, value string) {
|
||||||
if name == `` {
|
if name == `` {
|
||||||
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNameDATABASE, `Was not able to write a configuration to the database.`, `The given name was nil!`)
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNameDATABASE, `Was not able to write a configuration to the database.`, `The given name was nil!`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read the current value:
|
||||||
result := ConfigurationDBEntry{}
|
result := ConfigurationDBEntry{}
|
||||||
if errFind := collection.Find(bson.D{{"Name", name}}).One(&result); errFind != nil {
|
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 write a configuration to the database.`, `Error while find.`, errFind.Error())
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNameDATABASE, `Was not able to write a configuration to the database.`, `Error while find.`, errFind.Error())
|
||||||
@ -22,6 +21,8 @@ func Write(name, value string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
result.Value = value
|
result.Value = value
|
||||||
|
|
||||||
|
// Update the database:
|
||||||
collection.Update(bson.D{{"Name", name}}, result)
|
collection.Update(bson.D{{"Name", name}}, result)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -4,25 +4,19 @@ import (
|
|||||||
"gopkg.in/mgo.v2"
|
"gopkg.in/mgo.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
// Get the database instance of the mgo MongoDB driver.
|
||||||
Get the database instance of the MGo Mongo driver.
|
|
||||||
*/
|
|
||||||
func DB() (session *mgo.Session, database *mgo.Database) {
|
func DB() (session *mgo.Session, database *mgo.Database) {
|
||||||
session = mainSession.Copy()
|
session = mainSession.Copy()
|
||||||
database = session.DB(databaseDB)
|
database = session.DB(databaseDB)
|
||||||
database.Login(databaseUsername, databasePassword)
|
database.Login(databaseUsername, databasePassword)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Get directly the GridFS instance of the mgo MongoDB driver.
|
||||||
Get directly the GridFS instance of the Mgo Mongo driver.
|
|
||||||
*/
|
|
||||||
func GridFS() (session *mgo.Session, filesystem *mgo.GridFS) {
|
func GridFS() (session *mgo.Session, filesystem *mgo.GridFS) {
|
||||||
session = mainSession.Copy()
|
session = mainSession.Copy()
|
||||||
database := session.DB(databaseDB)
|
database := session.DB(databaseDB)
|
||||||
database.Login(databaseUsername, databasePassword)
|
database.Login(databaseUsername, databasePassword)
|
||||||
filesystem = database.GridFS(`fs`)
|
filesystem = database.GridFS(`fs`)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,11 @@ import (
|
|||||||
"gopkg.in/mgo.v2"
|
"gopkg.in/mgo.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// The init function for this package.
|
||||||
func init() {
|
func init() {
|
||||||
|
|
||||||
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameDATABASE, `Init the customer database.`)
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameDATABASE, `Init the customer database.`)
|
||||||
|
|
||||||
|
// Read the configuration values:
|
||||||
databaseHost := ConfigurationDB.Read(`CustomerDBHost`)
|
databaseHost := ConfigurationDB.Read(`CustomerDBHost`)
|
||||||
databaseDB = ConfigurationDB.Read(`CustomerDBDatabase`)
|
databaseDB = ConfigurationDB.Read(`CustomerDBDatabase`)
|
||||||
databaseUsername = ConfigurationDB.Read(`CustomerDBUsername`)
|
databaseUsername = ConfigurationDB.Read(`CustomerDBUsername`)
|
||||||
|
@ -5,15 +5,11 @@ import (
|
|||||||
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
// The shutdown type.
|
||||||
Please do not use this type. It is an internal type of Ocean to provide a shutdown function!
|
|
||||||
*/
|
|
||||||
type ShutdownFunction struct {
|
type ShutdownFunction struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// The shutdown function for this package.
|
||||||
This function is called if the Ocean server is shutting down.
|
|
||||||
*/
|
|
||||||
func (a ShutdownFunction) Shutdown() {
|
func (a ShutdownFunction) Shutdown() {
|
||||||
Log.LogShort(senderName, LM.CategoryAPP, LM.LevelWARN, LM.MessageNameSHUTDOWN, `Close now the customer database connection.`)
|
Log.LogShort(senderName, LM.CategoryAPP, LM.LevelWARN, LM.MessageNameSHUTDOWN, `Close now the customer database connection.`)
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
mainSession *mgo.Session = nil
|
mainSession *mgo.Session = nil // The session for the customer database
|
||||||
senderName LM.Sender = `System::CustomerDB`
|
senderName LM.Sender = `System::CustomerDB` // This is the name for logging event from this package
|
||||||
databaseUsername string = ``
|
databaseUsername string = `` // The user's name
|
||||||
databasePassword string = ``
|
databasePassword string = `` // The user's password
|
||||||
databaseDB string = ``
|
databaseDB string = `` // The database
|
||||||
)
|
)
|
||||||
|
@ -4,10 +4,12 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Function to add a new public handler.
|
||||||
func AddPublicHandler(pattern string, handler func(http.ResponseWriter, *http.Request)) {
|
func AddPublicHandler(pattern string, handler func(http.ResponseWriter, *http.Request)) {
|
||||||
muxPublic.HandleFunc(pattern, handler)
|
muxPublic.HandleFunc(pattern, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function to add a new private handler.
|
||||||
func AddAdminHandler(pattern string, handler func(http.ResponseWriter, *http.Request)) {
|
func AddAdminHandler(pattern string, handler func(http.ResponseWriter, *http.Request)) {
|
||||||
muxAdmin.HandleFunc(pattern, handler)
|
muxAdmin.HandleFunc(pattern, handler)
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,13 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Returns the muxer for the public web server.
|
||||||
func GetPublicMux() (mux *http.ServeMux) {
|
func GetPublicMux() (mux *http.ServeMux) {
|
||||||
mux = muxPublic
|
mux = muxPublic
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the muxer for the private web server.
|
||||||
func GetAdminMux() (mux *http.ServeMux) {
|
func GetAdminMux() (mux *http.ServeMux) {
|
||||||
mux = muxAdmin
|
mux = muxAdmin
|
||||||
return
|
return
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
package Handlers
|
package Handlers
|
||||||
|
|
||||||
|
// The init function for this package.
|
||||||
func init() {
|
func init() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
senderName LM.Sender = `System::Handlers`
|
senderName LM.Sender = `System::Handlers` // This is the name for logging event from this package
|
||||||
muxPublic *http.ServeMux = http.NewServeMux()
|
muxPublic *http.ServeMux = http.NewServeMux() // The muxer for the public web server
|
||||||
muxAdmin *http.ServeMux = http.NewServeMux()
|
muxAdmin *http.ServeMux = http.NewServeMux() // The muxer for the private web server
|
||||||
)
|
)
|
||||||
|
@ -10,38 +10,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func InitCacheNow() {
|
|
||||||
startCacheTimerLock.Lock()
|
|
||||||
defer startCacheTimerLock.Unlock()
|
|
||||||
|
|
||||||
if cacheTimerRunning {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cacheTimerLogic(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func StartCacheTimer() {
|
|
||||||
initCacheTimer()
|
|
||||||
}
|
|
||||||
|
|
||||||
func initCacheTimer() {
|
|
||||||
startCacheTimerLock.Lock()
|
|
||||||
defer startCacheTimerLock.Unlock()
|
|
||||||
|
|
||||||
if cacheTimerRunning {
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
cacheTimerRunning = true
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
cacheTimerLogic(true)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
func cacheTimerLogic(waiting bool) {
|
func cacheTimerLogic(waiting bool) {
|
||||||
if Shutdown.IsDown() {
|
if Shutdown.IsDown() {
|
||||||
return
|
return
|
@ -6,6 +6,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Function to convert the HTTP data back to a message.
|
||||||
func Data2Message(target interface{}, data map[string][]string) (channel, command string, obj interface{}) {
|
func Data2Message(target interface{}, data map[string][]string) (channel, command string, obj interface{}) {
|
||||||
if data == nil || len(data) == 0 {
|
if data == nil || len(data) == 0 {
|
||||||
channel = ``
|
channel = ``
|
||||||
@ -14,12 +15,15 @@ func Data2Message(target interface{}, data map[string][]string) (channel, comman
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use reflection for the target type:
|
||||||
element := reflect.ValueOf(target)
|
element := reflect.ValueOf(target)
|
||||||
element = element.Elem()
|
element = element.Elem()
|
||||||
elementType := element.Type()
|
elementType := element.Type()
|
||||||
|
|
||||||
channel = data[`channel`][0]
|
channel = data[`channel`][0]
|
||||||
command = data[`command`][0]
|
command = data[`command`][0]
|
||||||
|
|
||||||
|
// Use the order of the destination type's fields:
|
||||||
for i := 0; i < element.NumField(); i++ {
|
for i := 0; i < element.NumField(); i++ {
|
||||||
field := element.Field(i)
|
field := element.Field(i)
|
||||||
switch field.Kind().String() {
|
switch field.Kind().String() {
|
||||||
@ -53,6 +57,7 @@ func Data2Message(target interface{}, data map[string][]string) (channel, comman
|
|||||||
v, _ := strconv.ParseUint(mapValue, 16, 8)
|
v, _ := strconv.ParseUint(mapValue, 16, 8)
|
||||||
field.SetUint(v)
|
field.SetUint(v)
|
||||||
|
|
||||||
|
// Case: Arrays...
|
||||||
case `slice`:
|
case `slice`:
|
||||||
sliceInterface := field.Interface()
|
sliceInterface := field.Interface()
|
||||||
sliceKind := reflect.ValueOf(sliceInterface).Type().String()
|
sliceKind := reflect.ValueOf(sliceInterface).Type().String()
|
||||||
|
20
ICCC/Doc.go
20
ICCC/Doc.go
@ -1,21 +1,15 @@
|
|||||||
/*
|
/*
|
||||||
This is the "[I]nter-[C]omponent [C]ommunication [C]hannel". It is a minimal
|
This is the "[I]nter-[C]omponent [C]ommunication [C]hannel". It is a minimal messaging service to connect different servers or even different parts of huge systems across programming languages.
|
||||||
messaging service to connect different servers or even different parts of
|
|
||||||
huge systems across programming languages.
|
|
||||||
|
|
||||||
The basis idea is to create such messaging service on top of HTTP, because
|
The basis idea is to create such messaging service on top of HTTP, because every programming language is able to process HTTP. Therefore, all messages are transformed to HTTP form values (with URL encoding).
|
||||||
every programming language is able to process HTTP. Therefore, all messages
|
|
||||||
are transformed to HTTP form values (with URL encoding).
|
|
||||||
|
|
||||||
To be able to marshal / parse the data back to objects, some additional
|
To be able to marshal / parse the data back to objects, some additional information is added:
|
||||||
information is added:
|
|
||||||
|
|
||||||
Example 01:
|
Example 01:
|
||||||
name=str:Surname
|
name=str:Surname
|
||||||
value=Sommer
|
value=Sommer
|
||||||
|
|
||||||
The HTTP form name is 'str:Surname' and the value is 'Sommer'. The 'str' is
|
The HTTP form name is 'str:Surname' and the value is 'Sommer'. The 'str' is the indicator for the data type, in this case it is a string.
|
||||||
the indicator for the data type, in this case it is a string.
|
|
||||||
|
|
||||||
Known data types are:
|
Known data types are:
|
||||||
* str := string
|
* str := string
|
||||||
@ -48,7 +42,9 @@ channel=CHANNEL
|
|||||||
[any count of data tuples]
|
[any count of data tuples]
|
||||||
InternalCommPassword=[configured communication password e.g. an UUID etc.]
|
InternalCommPassword=[configured communication password e.g. an UUID etc.]
|
||||||
|
|
||||||
If you want to build a distributed system across the Internet, please use e.g. SSH tunnels
|
If you want to build a distributed system across the Internet, please use e.g. SSH tunnels to keep things secret.
|
||||||
to keep things secret.
|
|
||||||
|
Constrains to the environment:
|
||||||
|
The web server cannot reorder the fields of the request or response. The order of fields at the data object (message) must correspond with the order of fields inside the HTTP message. Therefore, a reorder is not possible at the moment.
|
||||||
*/
|
*/
|
||||||
package ICCC
|
package ICCC
|
||||||
|
@ -8,36 +8,49 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// The HTTP handler for ICCC.
|
||||||
func ICCCHandler(response http.ResponseWriter, request *http.Request) {
|
func ICCCHandler(response http.ResponseWriter, request *http.Request) {
|
||||||
|
|
||||||
|
// Cannot parse the form?
|
||||||
if errParse := request.ParseForm(); errParse != nil {
|
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!`)
|
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)
|
http.NotFound(response, request)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read the data out of the request:
|
||||||
messageData := map[string][]string(request.PostForm)
|
messageData := map[string][]string(request.PostForm)
|
||||||
|
|
||||||
|
// The data must contain at least three fields (command, channel & communication password)
|
||||||
if len(messageData) < 3 {
|
if len(messageData) < 3 {
|
||||||
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameNETWORK, `The ICCC message contains not enough data: At least the channel, command and password is required!`)
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameNETWORK, `The ICCC message contains not enough data: At least the channel, command and password is required!`)
|
||||||
http.NotFound(response, request)
|
http.NotFound(response, request)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read the meta data:
|
||||||
channel := messageData[`channel`][0]
|
channel := messageData[`channel`][0]
|
||||||
command := messageData[`command`][0]
|
command := messageData[`command`][0]
|
||||||
password := messageData[`InternalCommPassword`][0]
|
password := messageData[`InternalCommPassword`][0]
|
||||||
|
|
||||||
|
// Check the password:
|
||||||
if password != Tools.InternalCommPassword() {
|
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)
|
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)
|
http.NotFound(response, request)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build the key for the mapping of the listener cache:
|
||||||
key := fmt.Sprintf(`%s::%s`, channel, command)
|
key := fmt.Sprintf(`%s::%s`, channel, command)
|
||||||
|
|
||||||
|
// Get the matching listener
|
||||||
listener := listeners[key]
|
listener := listeners[key]
|
||||||
|
|
||||||
if listener == nil {
|
if listener == nil {
|
||||||
|
// Case: No such listener
|
||||||
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())
|
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 {
|
} else {
|
||||||
|
// Case: Everything is fine => deliver the message
|
||||||
listener(messageData)
|
listener(messageData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,16 +7,23 @@ import (
|
|||||||
"github.com/SommerEngineering/Ocean/Tools"
|
"github.com/SommerEngineering/Ocean/Tools"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Init this package.
|
||||||
func init() {
|
func init() {
|
||||||
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Start init of ICCC.`)
|
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.`)
|
defer Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Done init ICCC.`)
|
||||||
|
|
||||||
|
// Create the list as cache for all global listener (not only listener from this server):
|
||||||
cacheListenerDatabase = list.New()
|
cacheListenerDatabase = list.New()
|
||||||
|
|
||||||
|
// Create a mapping as cache for all local listener end-points (functions):
|
||||||
listeners = make(map[string]func(data map[string][]string))
|
listeners = make(map[string]func(data map[string][]string))
|
||||||
|
|
||||||
// Using the local IP address:
|
// Using the local IP address:
|
||||||
correctAddressWithPort = Tools.LocalIPAddressAndPort()
|
correctAddressWithPort = Tools.LocalIPAddressAndPort()
|
||||||
|
|
||||||
|
// Init the database:
|
||||||
initDB()
|
initDB()
|
||||||
|
|
||||||
|
// Register this server to the listener (if not present):
|
||||||
registerHost2Database()
|
registerHost2Database()
|
||||||
}
|
}
|
||||||
|
13
ICCC/InitCacheNow.go
Normal file
13
ICCC/InitCacheNow.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package ICCC
|
||||||
|
|
||||||
|
// Starts the timer cache once and exit it after (no thread, no endless loop).
|
||||||
|
func InitCacheNow() {
|
||||||
|
startCacheTimerLock.Lock()
|
||||||
|
defer startCacheTimerLock.Unlock()
|
||||||
|
|
||||||
|
if cacheTimerRunning {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheTimerLogic(false)
|
||||||
|
}
|
21
ICCC/InitCacheTimer.go
Normal file
21
ICCC/InitCacheTimer.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package ICCC
|
||||||
|
|
||||||
|
// Setup and starts the cache timer.
|
||||||
|
func initCacheTimer() {
|
||||||
|
startCacheTimerLock.Lock()
|
||||||
|
defer startCacheTimerLock.Unlock()
|
||||||
|
|
||||||
|
if cacheTimerRunning {
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
cacheTimerRunning = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start another thread with the timer logic:
|
||||||
|
go func() {
|
||||||
|
// Endless loop:
|
||||||
|
for {
|
||||||
|
cacheTimerLogic(true)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
@ -7,6 +7,7 @@ import (
|
|||||||
"gopkg.in/mgo.v2"
|
"gopkg.in/mgo.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Init the database.
|
||||||
func initDB() {
|
func initDB() {
|
||||||
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Start init of the ICCC collections.`)
|
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.`)
|
defer Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Done init the ICCC collection.`)
|
||||||
@ -14,6 +15,7 @@ func initDB() {
|
|||||||
// Get the database:
|
// Get the database:
|
||||||
dbSession, db = CustomerDB.DB()
|
dbSession, db = CustomerDB.DB()
|
||||||
|
|
||||||
|
// Case: Error?
|
||||||
if db == nil {
|
if db == nil {
|
||||||
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameDATABASE, `Was not able to get the customer database.`)
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameDATABASE, `Was not able to get the customer database.`)
|
||||||
return
|
return
|
||||||
@ -23,7 +25,9 @@ func initDB() {
|
|||||||
collectionListener = db.C(`ICCCListener`)
|
collectionListener = db.C(`ICCCListener`)
|
||||||
collectionHosts = db.C(`ICCCHosts`)
|
collectionHosts = db.C(`ICCCHosts`)
|
||||||
|
|
||||||
|
//
|
||||||
// Take care about the indexes for ICCCListener:
|
// Take care about the indexes for ICCCListener:
|
||||||
|
//
|
||||||
collectionListener.EnsureIndexKey(`Command`)
|
collectionListener.EnsureIndexKey(`Command`)
|
||||||
collectionListener.EnsureIndexKey(`Command`, `IsActive`)
|
collectionListener.EnsureIndexKey(`Command`, `IsActive`)
|
||||||
|
|
||||||
@ -45,7 +49,9 @@ func initDB() {
|
|||||||
indexName1.Unique = true
|
indexName1.Unique = true
|
||||||
collectionListener.EnsureIndex(indexName1)
|
collectionListener.EnsureIndex(indexName1)
|
||||||
|
|
||||||
|
//
|
||||||
// Index for hosts:
|
// Index for hosts:
|
||||||
|
//
|
||||||
collectionHosts.EnsureIndexKey(`Hostname`, `IPAddressPort`)
|
collectionHosts.EnsureIndexKey(`Hostname`, `IPAddressPort`)
|
||||||
|
|
||||||
indexName2 := mgo.Index{}
|
indexName2 := mgo.Index{}
|
||||||
|
@ -6,8 +6,13 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Function to convert an ICCC message to HTTP data.
|
||||||
func message2Data(channel, command string, message interface{}) (data map[string][]string) {
|
func message2Data(channel, command string, message interface{}) (data map[string][]string) {
|
||||||
|
|
||||||
|
// Create the map:
|
||||||
data = make(map[string][]string)
|
data = make(map[string][]string)
|
||||||
|
|
||||||
|
// Add the meta information:
|
||||||
data[`command`] = []string{command}
|
data[`command`] = []string{command}
|
||||||
data[`channel`] = []string{channel}
|
data[`channel`] = []string{channel}
|
||||||
|
|
||||||
@ -15,9 +20,12 @@ func message2Data(channel, command string, message interface{}) (data map[string
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use reflection to determine the types:
|
||||||
element := reflect.ValueOf(message)
|
element := reflect.ValueOf(message)
|
||||||
elementType := element.Type()
|
elementType := element.Type()
|
||||||
|
|
||||||
|
// Iterate over all fields of the data type.
|
||||||
|
// Transform the data regarding the type.
|
||||||
for i := 0; i < element.NumField(); i++ {
|
for i := 0; i < element.NumField(); i++ {
|
||||||
field := element.Field(i)
|
field := element.Field(i)
|
||||||
keyName := elementType.Field(i).Name
|
keyName := elementType.Field(i).Name
|
||||||
@ -44,6 +52,7 @@ func message2Data(channel, command string, message interface{}) (data map[string
|
|||||||
key := fmt.Sprintf(`ui8:%s`, keyName)
|
key := fmt.Sprintf(`ui8:%s`, keyName)
|
||||||
data[key] = []string{strconv.FormatUint(field.Uint(), 16)}
|
data[key] = []string{strconv.FormatUint(field.Uint(), 16)}
|
||||||
|
|
||||||
|
// Case: Arrays...
|
||||||
case `slice`:
|
case `slice`:
|
||||||
sliceLen := field.Len()
|
sliceLen := field.Len()
|
||||||
if sliceLen > 0 {
|
if sliceLen > 0 {
|
||||||
|
@ -7,10 +7,13 @@ import (
|
|||||||
"gopkg.in/mgo.v2/bson"
|
"gopkg.in/mgo.v2/bson"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// The internal function to register a command to ICCC.
|
||||||
func register2Database(channel, command string) {
|
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)
|
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 :)
|
// Case: Exist and active :)
|
||||||
|
//
|
||||||
emptyEntry := Scheme.Listener{}
|
emptyEntry := Scheme.Listener{}
|
||||||
selection := bson.D{{`Channel`, channel}, {`Command`, command}, {`IPAddressPort`, correctAddressWithPort}, {`IsActive`, true}}
|
selection := bson.D{{`Channel`, channel}, {`Command`, command}, {`IPAddressPort`, correctAddressWithPort}, {`IsActive`, true}}
|
||||||
count1, _ := collectionListener.Find(selection).Count()
|
count1, _ := collectionListener.Find(selection).Count()
|
||||||
@ -20,7 +23,9 @@ func register2Database(channel, command string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
// Case: Exist but not active
|
// Case: Exist but not active
|
||||||
|
//
|
||||||
selection = bson.D{{`Channel`, channel}, {`Command`, command}, {`IPAddressPort`, correctAddressWithPort}, {`IsActive`, false}}
|
selection = bson.D{{`Channel`, channel}, {`Command`, command}, {`IPAddressPort`, correctAddressWithPort}, {`IsActive`, false}}
|
||||||
notActiveEntry := Scheme.Listener{}
|
notActiveEntry := Scheme.Listener{}
|
||||||
collectionListener.Find(selection).One(¬ActiveEntry)
|
collectionListener.Find(selection).One(¬ActiveEntry)
|
||||||
@ -32,7 +37,9 @@ func register2Database(channel, command string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
// Case: Not exist
|
// 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!`)
|
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 := Scheme.Listener{}
|
||||||
|
@ -8,20 +8,31 @@ import (
|
|||||||
"gopkg.in/mgo.v2/bson"
|
"gopkg.in/mgo.v2/bson"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Function to register this server to the ICCC.
|
||||||
func registerHost2Database() {
|
func registerHost2Database() {
|
||||||
|
|
||||||
|
// Create the host entry:
|
||||||
host := Scheme.Host{}
|
host := Scheme.Host{}
|
||||||
host.Hostname = Tools.ThisHostname()
|
host.Hostname = Tools.ThisHostname()
|
||||||
host.IPAddressPort = correctAddressWithPort
|
host.IPAddressPort = correctAddressWithPort
|
||||||
|
|
||||||
|
// The query to find already existing entries:
|
||||||
selection := bson.D{{`Hostname`, host.Hostname}, {`IPAddressPort`, host.IPAddressPort}}
|
selection := bson.D{{`Hostname`, host.Hostname}, {`IPAddressPort`, host.IPAddressPort}}
|
||||||
|
|
||||||
|
// Count the already existing entries:
|
||||||
count, _ := collectionHosts.Find(selection).Count()
|
count, _ := collectionHosts.Find(selection).Count()
|
||||||
|
|
||||||
|
// Already exist?
|
||||||
if count == 1 {
|
if count == 1 {
|
||||||
|
// Case: Exists!
|
||||||
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `This host is already registered!`, `host=`+host.Hostname, `address=`+host.IPAddressPort)
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `This host is already registered!`, `host=`+host.Hostname, `address=`+host.IPAddressPort)
|
||||||
} else {
|
} else {
|
||||||
|
// Case: Not exist.
|
||||||
if errInsert := collectionHosts.Insert(host); errInsert != nil {
|
if errInsert := collectionHosts.Insert(host); errInsert != nil {
|
||||||
|
// Case: Was not able insert in the database
|
||||||
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)
|
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 {
|
} else {
|
||||||
|
// Case: Everything was fine.
|
||||||
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `This host is now registered.`, `host=`+host.Hostname, `address=`+host.IPAddressPort)
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `This host is now registered.`, `host=`+host.Hostname, `address=`+host.IPAddressPort)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,15 @@ import (
|
|||||||
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Register a command to ICCC for a specific channel.
|
||||||
func Registrar(channel, command string, callback func(data map[string][]string)) {
|
func Registrar(channel, command string, callback func(data map[string][]string)) {
|
||||||
listenersLock.Lock()
|
listenersLock.Lock()
|
||||||
defer listenersLock.Unlock()
|
defer listenersLock.Unlock()
|
||||||
|
|
||||||
|
// Write the command to the database:
|
||||||
register2Database(channel, command)
|
register2Database(channel, command)
|
||||||
|
|
||||||
|
// Register the command at the local cache:
|
||||||
listeners[fmt.Sprintf(`%s::%s`, channel, command)] = callback
|
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)
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `The registrar has registered a new ICCC command.`, `channel=`+channel, `command=`+command)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package Scheme
|
package Scheme
|
||||||
|
|
||||||
|
// Scheme for the host database entry.
|
||||||
type Host struct {
|
type Host struct {
|
||||||
Hostname string `bson:"Hostname"`
|
Hostname string `bson:"Hostname"`
|
||||||
IPAddressPort string `bson:"IPAddressPort"`
|
IPAddressPort string `bson:"IPAddressPort"`
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package Scheme
|
package Scheme
|
||||||
|
|
||||||
|
// Type for the listener entries at the database.
|
||||||
type Listener struct {
|
type Listener struct {
|
||||||
Channel string `bson:"Channel"`
|
Channel string `bson:"Channel"`
|
||||||
Command string `bson:"Command"`
|
Command string `bson:"Command"`
|
||||||
|
@ -9,11 +9,17 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Send a message to a listener.
|
||||||
func sendMessage(listener Scheme.Listener, data map[string][]string) {
|
func sendMessage(listener Scheme.Listener, data map[string][]string) {
|
||||||
|
// Convert the data and encode it:
|
||||||
valuesHTTP := url.Values(data)
|
valuesHTTP := url.Values(data)
|
||||||
|
|
||||||
|
// Add the communication password:
|
||||||
valuesHTTP.Add(`InternalCommPassword`, Tools.InternalCommPassword())
|
valuesHTTP.Add(`InternalCommPassword`, Tools.InternalCommPassword())
|
||||||
|
|
||||||
|
// Try to deliver the message:
|
||||||
if _, err := http.PostForm(`http://`+listener.IPAddressPort+`/ICCC`, valuesHTTP); err != nil {
|
if _, err := http.PostForm(`http://`+listener.IPAddressPort+`/ICCC`, valuesHTTP); err != nil {
|
||||||
|
// Case: Was not possible to deliver.
|
||||||
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNameNETWORK, `Was not able to send the ICCC message.`, err.Error())
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNameNETWORK, `Was not able to send the ICCC message.`, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,27 +7,32 @@ import (
|
|||||||
"gopkg.in/mgo.v2/bson"
|
"gopkg.in/mgo.v2/bson"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
// Type to provide a shutdown function.
|
||||||
Please do not use this type. It is an internal type of Ocean to provide a shutdown function!
|
|
||||||
*/
|
|
||||||
type ShutdownFunction struct {
|
type ShutdownFunction struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// The shutdown function for ICCC.
|
||||||
This function is called if the Ocean server is shutting down.
|
|
||||||
*/
|
|
||||||
func (a ShutdownFunction) Shutdown() {
|
func (a ShutdownFunction) Shutdown() {
|
||||||
Log.LogShort(senderName, LM.CategoryAPP, LM.LevelWARN, LM.MessageNameSHUTDOWN, `Shutting down now all ICCC listener for this host.`)
|
Log.LogShort(senderName, LM.CategoryAPP, LM.LevelWARN, LM.MessageNameSHUTDOWN, `Shutting down now all ICCC listener for this host.`)
|
||||||
|
|
||||||
|
// Define the database query:
|
||||||
selection := bson.D{{`IPAddressPort`, correctAddressWithPort}}
|
selection := bson.D{{`IPAddressPort`, correctAddressWithPort}}
|
||||||
|
|
||||||
|
// Reserve the memory for an answer:
|
||||||
entry := Scheme.Listener{}
|
entry := Scheme.Listener{}
|
||||||
|
|
||||||
|
// Execute the query and iterate over the results:
|
||||||
iterator := collectionListener.Find(selection).Iter()
|
iterator := collectionListener.Find(selection).Iter()
|
||||||
for iterator.Next(&entry) {
|
for iterator.Next(&entry) {
|
||||||
|
// Update the entry and set it to active=false:
|
||||||
selectionUpdate := bson.D{{`Channel`, entry.Channel}, {`Command`, entry.Command}, {`IPAddressPort`, correctAddressWithPort}}
|
selectionUpdate := bson.D{{`Channel`, entry.Channel}, {`Command`, entry.Command}, {`IPAddressPort`, correctAddressWithPort}}
|
||||||
entry.IsActive = false
|
entry.IsActive = false
|
||||||
|
|
||||||
|
// Update the entry:
|
||||||
collectionListener.Update(selectionUpdate, entry)
|
collectionListener.Update(selectionUpdate, entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disconnect the database:
|
||||||
db.Logout()
|
db.Logout()
|
||||||
dbSession.Close()
|
dbSession.Close()
|
||||||
Log.LogShort(senderName, LM.CategoryAPP, LM.LevelWARN, LM.MessageNameSHUTDOWN, `Done shutting down now all ICCC listener for this host.`)
|
Log.LogShort(senderName, LM.CategoryAPP, LM.LevelWARN, LM.MessageNameSHUTDOWN, `Done shutting down now all ICCC listener for this host.`)
|
||||||
|
6
ICCC/StartCacheTimer.go
Normal file
6
ICCC/StartCacheTimer.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package ICCC
|
||||||
|
|
||||||
|
// Starts the cache timer thread.
|
||||||
|
func StartCacheTimer() {
|
||||||
|
initCacheTimer()
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package SystemMessages
|
package SystemMessages
|
||||||
|
|
||||||
|
// Message type for the startup message:
|
||||||
type ICCCStartUpMessage struct {
|
type ICCCStartUpMessage struct {
|
||||||
PublicIPAddressAndPort string
|
PublicIPAddressAndPort string
|
||||||
AdminIPAddressAndPort string
|
AdminIPAddressAndPort string
|
||||||
|
@ -7,26 +7,27 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Some pre-defined channels:
|
||||||
const (
|
const (
|
||||||
ChannelSYSTEM string = `System`
|
ChannelSYSTEM string = `System` // The common system channel.
|
||||||
ChannelNUMGEN string = `System::NumGen`
|
ChannelNUMGEN string = `System::NumGen` // A channel for the number generator.
|
||||||
ChannelSHUTDOWN string = `System::Shutdown`
|
ChannelSHUTDOWN string = `System::Shutdown` // A channel for system shutdown messages.
|
||||||
ChannelSTARTUP string = `System::Startup`
|
ChannelSTARTUP string = `System::Startup` // A channel for system startup messages.
|
||||||
ChannelICCC string = `System::ICCC`
|
ChannelICCC string = `System::ICCC` // A common ICCC channel.
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
senderName LM.Sender = `ICCC`
|
senderName LM.Sender = `ICCC` // This is the name for logging event from this package
|
||||||
db *mgo.Database = nil
|
db *mgo.Database = nil // The database
|
||||||
dbSession *mgo.Session = nil
|
dbSession *mgo.Session = nil // The database session
|
||||||
collectionListener *mgo.Collection = nil
|
collectionListener *mgo.Collection = nil // The database collection for listeners
|
||||||
collectionHosts *mgo.Collection = nil
|
collectionHosts *mgo.Collection = nil // The database collection for hosts
|
||||||
reservedSystemChannels []string = []string{ChannelSYSTEM, ChannelNUMGEN, ChannelSHUTDOWN, ChannelSTARTUP, ChannelICCC}
|
reservedSystemChannels []string = []string{ChannelSYSTEM, ChannelNUMGEN, ChannelSHUTDOWN, ChannelSTARTUP, ChannelICCC} // The reserved and pre-defined system channels
|
||||||
listeners map[string]func(data map[string][]string) = nil
|
listeners map[string]func(data map[string][]string) = nil // The listener cache for all local available listeners with local functions
|
||||||
listenersLock sync.RWMutex = sync.RWMutex{}
|
listenersLock sync.RWMutex = sync.RWMutex{} // The mutex for the listener cache
|
||||||
cacheListenerDatabase *list.List = nil
|
cacheListenerDatabase *list.List = nil // The globally cache for all listeners from all servers
|
||||||
cacheListenerDatabaseLock sync.RWMutex = sync.RWMutex{}
|
cacheListenerDatabaseLock sync.RWMutex = sync.RWMutex{} // The mutex for the globally cache
|
||||||
startCacheTimerLock sync.Mutex = sync.Mutex{}
|
startCacheTimerLock sync.Mutex = sync.Mutex{} // Mutex for the start timer
|
||||||
cacheTimerRunning bool = false
|
cacheTimerRunning bool = false // Is the timer running?
|
||||||
correctAddressWithPort string = ``
|
correctAddressWithPort string = `` // The IP address and port of the this local server
|
||||||
)
|
)
|
||||||
|
@ -6,20 +6,27 @@ import (
|
|||||||
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Function to broadcast a message to all listeners.
|
||||||
func WriteMessage2All(channel, command string, message interface{}) {
|
func WriteMessage2All(channel, command string, message interface{}) {
|
||||||
cacheListenerDatabaseLock.RLock()
|
cacheListenerDatabaseLock.RLock()
|
||||||
defer cacheListenerDatabaseLock.RUnlock()
|
defer cacheListenerDatabaseLock.RUnlock()
|
||||||
|
|
||||||
|
// Convert the message to HTTP data:
|
||||||
data := message2Data(channel, command, message)
|
data := message2Data(channel, command, message)
|
||||||
counter := 0
|
counter := 0
|
||||||
|
|
||||||
|
// Loop over all listeners which are currently available at the cache:
|
||||||
for entry := cacheListenerDatabase.Front(); entry != nil; entry = entry.Next() {
|
for entry := cacheListenerDatabase.Front(); entry != nil; entry = entry.Next() {
|
||||||
listener := entry.Value.(Scheme.Listener)
|
listener := entry.Value.(Scheme.Listener)
|
||||||
|
|
||||||
|
// If the channel and the command matches, deliver the message:
|
||||||
if listener.Channel == channel && listener.Command == command {
|
if listener.Channel == channel && listener.Command == command {
|
||||||
go sendMessage(listener, data)
|
go sendMessage(listener, data)
|
||||||
counter++
|
counter++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Was not able to deliver to any listener?
|
||||||
if counter == 0 {
|
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)
|
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)
|
||||||
}
|
}
|
||||||
|
@ -7,16 +7,22 @@ import (
|
|||||||
"github.com/SommerEngineering/Ocean/Tools"
|
"github.com/SommerEngineering/Ocean/Tools"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Function to write a message to any listener.
|
||||||
func WriteMessage2Any(channel, command string, message interface{}) {
|
func WriteMessage2Any(channel, command string, message interface{}) {
|
||||||
cacheListenerDatabaseLock.RLock()
|
cacheListenerDatabaseLock.RLock()
|
||||||
defer cacheListenerDatabaseLock.RUnlock()
|
defer cacheListenerDatabaseLock.RUnlock()
|
||||||
|
|
||||||
|
// Convert the message to HTTP data:
|
||||||
data := message2Data(channel, command, message)
|
data := message2Data(channel, command, message)
|
||||||
maxCount := cacheListenerDatabase.Len()
|
maxCount := cacheListenerDatabase.Len()
|
||||||
entries := make([]Scheme.Listener, 0, maxCount)
|
entries := make([]Scheme.Listener, 0, maxCount)
|
||||||
counter := 0
|
counter := 0
|
||||||
|
|
||||||
|
// Loop over all listeners which are currently present at the cache:
|
||||||
for entry := cacheListenerDatabase.Front(); entry != nil; entry = entry.Next() {
|
for entry := cacheListenerDatabase.Front(); entry != nil; entry = entry.Next() {
|
||||||
listener := entry.Value.(Scheme.Listener)
|
listener := entry.Value.(Scheme.Listener)
|
||||||
|
|
||||||
|
// If the channel and the command matches, store the listener:
|
||||||
if listener.Channel == channel && listener.Command == command {
|
if listener.Channel == channel && listener.Command == command {
|
||||||
entries = entries[:len(entries)+1]
|
entries = entries[:len(entries)+1]
|
||||||
entries[counter] = listener
|
entries[counter] = listener
|
||||||
@ -25,9 +31,11 @@ func WriteMessage2Any(channel, command string, message interface{}) {
|
|||||||
|
|
||||||
count := len(entries)
|
count := len(entries)
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
|
// Case: Find at least one possible listener. Choose a random one and deliver:
|
||||||
listener := entries[Tools.RandomInteger(count)]
|
listener := entries[Tools.RandomInteger(count)]
|
||||||
go sendMessage(listener, data)
|
go sendMessage(listener, data)
|
||||||
} else {
|
} else {
|
||||||
|
// Case: Find no listener at all.
|
||||||
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)
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"github.com/SommerEngineering/Ocean/Log/Meta"
|
"github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// The interface for every logging device.
|
||||||
type Device interface {
|
type Device interface {
|
||||||
Log(logEntries []Meta.Entry)
|
Log(logEntries []Meta.Entry)
|
||||||
Flush()
|
Flush()
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"github.com/SommerEngineering/Ocean/Log"
|
"github.com/SommerEngineering/Ocean/Log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Function with the setup of the logging device.
|
||||||
func ActivateLoggingDevice() {
|
func ActivateLoggingDevice() {
|
||||||
Log.AddLoggingDevice(Console{})
|
Log.AddLoggingDevice(Console{})
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,11 @@ import (
|
|||||||
"github.com/SommerEngineering/Ocean/Log/Meta"
|
"github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// The logging device.
|
||||||
type Console struct {
|
type Console struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This function is the interface between the logging system and the console logger.
|
||||||
func (dev Console) Log(entries []Meta.Entry) {
|
func (dev Console) Log(entries []Meta.Entry) {
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
fmt.Println(entry.Format())
|
fmt.Println(entry.Format())
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"github.com/SommerEngineering/Ocean/Log"
|
"github.com/SommerEngineering/Ocean/Log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Function with the setup of the logging device.
|
||||||
func ActivateLoggingDevice() {
|
func ActivateLoggingDevice() {
|
||||||
Log.AddLoggingDevice(Database{})
|
Log.AddLoggingDevice(Database{})
|
||||||
}
|
}
|
||||||
|
18
Log/DeviceDatabase/CacheFull.go
Normal file
18
Log/DeviceDatabase/CacheFull.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package DeviceDatabase
|
||||||
|
|
||||||
|
// Function to check if the cache is full. If so, write all events to the database.
|
||||||
|
func cacheFull() {
|
||||||
|
mutexCacheFull.Lock()
|
||||||
|
defer mutexCacheFull.Unlock()
|
||||||
|
|
||||||
|
// Is the cache full?
|
||||||
|
if len(cache) < cacheSizeNumberOfEvents {
|
||||||
|
// Case: Cache is not full.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case: The cache is full. Write all events to the database.
|
||||||
|
for counter := 0; counter < cacheSizeNumberOfEvents; counter++ {
|
||||||
|
write2Database(<-cache)
|
||||||
|
}
|
||||||
|
}
|
37
Log/DeviceDatabase/CacheRefreshMessageNames.go
Normal file
37
Log/DeviceDatabase/CacheRefreshMessageNames.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package DeviceDatabase
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/SommerEngineering/Ocean/Log"
|
||||||
|
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
"github.com/SommerEngineering/Ocean/Shutdown"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Function for the thread which maintain the message name cache.
|
||||||
|
func cacheRefreshMessageNames() {
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `The message names' refresh thread is now running.`)
|
||||||
|
|
||||||
|
// Create an own thread:
|
||||||
|
go func() {
|
||||||
|
// Endless loop:
|
||||||
|
for true {
|
||||||
|
// Read the message names rom the DB:
|
||||||
|
data := readMessageNamesFromDB()
|
||||||
|
mutexCacheMessageNames.Lock()
|
||||||
|
|
||||||
|
// Overwrite the cache:
|
||||||
|
cacheMessageNames = data
|
||||||
|
mutexCacheMessageNames.Unlock()
|
||||||
|
|
||||||
|
// Sleep some time:
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelTALKATIVE, LM.MessageNameEXECUTE, `The message names' cache was refreshed.`)
|
||||||
|
time.Sleep(time.Duration(nameCachesRefreshTimeSeconds) * time.Second)
|
||||||
|
|
||||||
|
// Case: The server goes down now.
|
||||||
|
if Shutdown.IsDown() {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityLow, LM.ImpactLow, LM.MessageNameSHUTDOWN, `The message name's refresh thread is now shutting down.`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
38
Log/DeviceDatabase/CacheRefreshSenderNames.go
Normal file
38
Log/DeviceDatabase/CacheRefreshSenderNames.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package DeviceDatabase
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/SommerEngineering/Ocean/Log"
|
||||||
|
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
"github.com/SommerEngineering/Ocean/Shutdown"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Function for the thread which maintain the sender name cache.
|
||||||
|
func cacheRefreshSenderNames() {
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `The sender names' refresh thread is now running.`)
|
||||||
|
|
||||||
|
// Use an extra thread:
|
||||||
|
go func() {
|
||||||
|
// Endless lopp:
|
||||||
|
for true {
|
||||||
|
|
||||||
|
// Read the sender names from the DB:
|
||||||
|
data := readSenderNamesFromDB()
|
||||||
|
mutexCacheSenderNames.Lock()
|
||||||
|
|
||||||
|
// Overwrite the cache:
|
||||||
|
cacheSenderNames = data
|
||||||
|
mutexCacheSenderNames.Unlock()
|
||||||
|
|
||||||
|
// Sleep some time:
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelTALKATIVE, LM.MessageNameEXECUTE, `The sender names' cache was refreshed.`)
|
||||||
|
time.Sleep(time.Duration(nameCachesRefreshTimeSeconds) * time.Second)
|
||||||
|
|
||||||
|
// Case: The server is going down now.
|
||||||
|
if Shutdown.IsDown() {
|
||||||
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityLow, LM.ImpactLow, LM.MessageNameSHUTDOWN, `The sender name's refresh thread is now shutting down.`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package DeviceDatabase
|
package DeviceDatabase
|
||||||
|
|
||||||
|
// Flush the cache and write all messages to the database.
|
||||||
func (dev Database) Flush() {
|
func (dev Database) Flush() {
|
||||||
mutexCacheFull.Lock()
|
mutexCacheFull.Lock()
|
||||||
defer mutexCacheFull.Unlock()
|
defer mutexCacheFull.Unlock()
|
||||||
@ -9,6 +10,7 @@ func (dev Database) Flush() {
|
|||||||
write2Database(<-cache)
|
write2Database(<-cache)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Shutdown the database connection:
|
||||||
logDB.Logout()
|
logDB.Logout()
|
||||||
logDBSession.Close()
|
logDBSession.Close()
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,9 @@ import (
|
|||||||
"github.com/SommerEngineering/Ocean/Log/Meta"
|
"github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Function to format a logging database entry as string.
|
||||||
func (entry LogDBEntry) Format() (result string) {
|
func (entry LogDBEntry) Format() (result string) {
|
||||||
|
// First, we convert the logging db entry to the common logging type:
|
||||||
converted := Meta.Entry{}
|
converted := Meta.Entry{}
|
||||||
converted.Time = entry.TimeUTC
|
converted.Time = entry.TimeUTC
|
||||||
converted.Project = entry.Project
|
converted.Project = entry.Project
|
||||||
@ -18,6 +19,7 @@ func (entry LogDBEntry) Format() (result string) {
|
|||||||
converted.MessageDescription = entry.MessageDescription
|
converted.MessageDescription = entry.MessageDescription
|
||||||
converted.Parameters = entry.Parameters
|
converted.Parameters = entry.Parameters
|
||||||
|
|
||||||
|
// Second, we can use then the format operation of these type:
|
||||||
result = converted.Format()
|
result = converted.Format()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -8,12 +8,19 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// The init function for this package.
|
||||||
func init() {
|
func init() {
|
||||||
|
|
||||||
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `Starting now the database logging.`)
|
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.`)
|
defer Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `Starting the database logging done.`)
|
||||||
|
|
||||||
|
// Init the database first:
|
||||||
initDatabase()
|
initDatabase()
|
||||||
|
|
||||||
|
//
|
||||||
|
// Read all configuration values:
|
||||||
|
//
|
||||||
|
|
||||||
if value, err := strconv.Atoi(ConfigurationDB.Read(`LogDBCacheSizeNumberOfEvents`)); err != nil {
|
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))
|
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 {
|
} else {
|
||||||
@ -35,8 +42,15 @@ func init() {
|
|||||||
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `Configuration LogDBWebInterfaceNameCacheRefreshTimeSeconds was loaded.`, fmt.Sprintf(`The value is %d.`, nameCachesRefreshTimeSeconds))
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, `Configuration LogDBWebInterfaceNameCacheRefreshTimeSeconds was loaded.`, fmt.Sprintf(`The value is %d.`, nameCachesRefreshTimeSeconds))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create the cache:
|
||||||
cache = make(chan LogDBEntry, cacheSizeNumberOfEvents)
|
cache = make(chan LogDBEntry, cacheSizeNumberOfEvents)
|
||||||
initTimeout()
|
|
||||||
|
// Starts a thread to write events based on time-outs:
|
||||||
|
startTimeout()
|
||||||
|
|
||||||
|
// Starts a thread to refresh the sender name cache:
|
||||||
cacheRefreshSenderNames()
|
cacheRefreshSenderNames()
|
||||||
|
|
||||||
|
// Starts a thread to refresh the message name cache:
|
||||||
cacheRefreshMessageNames()
|
cacheRefreshMessageNames()
|
||||||
}
|
}
|
||||||
|
@ -12,19 +12,26 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Init the database for the logging.
|
||||||
func initDatabase() {
|
func initDatabase() {
|
||||||
|
|
||||||
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Checking and init the logging database collection.`)
|
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.`)
|
defer Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Checking and init the logging database collection done.`)
|
||||||
|
|
||||||
|
// Read the configuration values for the logging database:
|
||||||
databaseHost := ConfigurationDB.Read(`LogDBHost`)
|
databaseHost := ConfigurationDB.Read(`LogDBHost`)
|
||||||
databaseDB := ConfigurationDB.Read(`LogDBDatabase`)
|
databaseDB := ConfigurationDB.Read(`LogDBDatabase`)
|
||||||
databaseUsername := ConfigurationDB.Read(`LogDBUsername`)
|
databaseUsername := ConfigurationDB.Read(`LogDBUsername`)
|
||||||
databasePassword := ConfigurationDB.Read(`LogDBPassword`)
|
databasePassword := ConfigurationDB.Read(`LogDBPassword`)
|
||||||
|
|
||||||
|
// Should the logging events at the database expire?
|
||||||
expire := strings.ToLower(ConfigurationDB.Read(`LogDBEventsExpire`)) == `true`
|
expire := strings.ToLower(ConfigurationDB.Read(`LogDBEventsExpire`)) == `true`
|
||||||
|
|
||||||
|
// The default values for the TTL (time to live):
|
||||||
expireAfterDays := 21900 // 60 years ~ maximum of MongoDB
|
expireAfterDays := 21900 // 60 years ~ maximum of MongoDB
|
||||||
expireValue4DisabledFunction := 21900 // 60 years ~ maximum of MongoDB
|
expireValue4DisabledFunction := 21900 // 60 years ~ maximum of MongoDB
|
||||||
|
|
||||||
|
// Try to read the configured value for the TTL:
|
||||||
if value, errValue := strconv.Atoi(ConfigurationDB.Read(`LogDBEventsExpireAfterDays`)); errValue != nil {
|
if value, errValue := strconv.Atoi(ConfigurationDB.Read(`LogDBEventsExpireAfterDays`)); errValue != nil {
|
||||||
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityMiddle, LM.ImpactUnknown, LM.MessageNameCONFIGURATION, `It was not possible to read the configuration for the expire time of logging events. Log events will not expire any more.`, errValue.Error())
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityMiddle, LM.ImpactUnknown, LM.MessageNameCONFIGURATION, `It was not possible to read the configuration for the expire time of logging events. Log events will not expire any more.`, errValue.Error())
|
||||||
expire = false
|
expire = false
|
||||||
@ -86,6 +93,9 @@ func initDatabase() {
|
|||||||
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNameDATABASE, fmt.Sprintf(`Update the expire policy for the logging database done.`))
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityUnknown, LM.ImpactUnknown, LM.MessageNameDATABASE, fmt.Sprintf(`Update the expire policy for the logging database done.`))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Ensure that all necessary indexes are existing:
|
||||||
|
//
|
||||||
indexProject := mgo.Index{}
|
indexProject := mgo.Index{}
|
||||||
indexProject.Key = []string{`Project`}
|
indexProject.Key = []string{`Project`}
|
||||||
logDBCollection.EnsureIndex(indexProject)
|
logDBCollection.EnsureIndex(indexProject)
|
||||||
|
@ -7,21 +7,27 @@ import (
|
|||||||
func ReadCustom(timeRange, logLevel, logCategory, logImpact, logSeverity, logMessageName, logSender, logPage string) (events []LogDBEntry) {
|
func ReadCustom(timeRange, logLevel, logCategory, logImpact, logSeverity, logMessageName, logSender, logPage string) (events []LogDBEntry) {
|
||||||
|
|
||||||
//
|
//
|
||||||
// TODO => Is currently stub
|
// TODO => Is currently a stub
|
||||||
//
|
//
|
||||||
|
|
||||||
|
// Define the query:
|
||||||
query := logDBCollection.Find(bson.D{}).Sort(`-TimeUTC`).Limit(26)
|
query := logDBCollection.Find(bson.D{}).Sort(`-TimeUTC`).Limit(26)
|
||||||
count := 26
|
count := 26
|
||||||
|
|
||||||
|
// Execute the query and count the results:
|
||||||
if n, err := query.Count(); err == nil {
|
if n, err := query.Count(); err == nil {
|
||||||
count = n
|
count = n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The iterator for the results:
|
||||||
iter := query.Iter()
|
iter := query.Iter()
|
||||||
entry := LogDBEntry{}
|
entry := LogDBEntry{}
|
||||||
pos := 0
|
pos := 0
|
||||||
|
|
||||||
|
// Reserve the memory for the results:
|
||||||
events = make([]LogDBEntry, count)
|
events = make([]LogDBEntry, count)
|
||||||
|
|
||||||
|
// Loop over all entries and store it:
|
||||||
for iter.Next(&entry) {
|
for iter.Next(&entry) {
|
||||||
events[pos] = entry
|
events[pos] = entry
|
||||||
pos++
|
pos++
|
||||||
|
@ -4,20 +4,26 @@ import (
|
|||||||
"gopkg.in/mgo.v2/bson"
|
"gopkg.in/mgo.v2/bson"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Read the latest logging events from the database.
|
||||||
func ReadLatest() (events []LogDBEntry) {
|
func ReadLatest() (events []LogDBEntry) {
|
||||||
|
// Define the query:
|
||||||
query := logDBCollection.Find(bson.D{}).Sort(`-TimeUTC`).Limit(26)
|
query := logDBCollection.Find(bson.D{}).Sort(`-TimeUTC`).Limit(26)
|
||||||
count := 26
|
count := 26
|
||||||
|
|
||||||
|
// Execute the query and count the results:
|
||||||
if n, err := query.Count(); err == nil {
|
if n, err := query.Count(); err == nil {
|
||||||
count = n
|
count = n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The iterator for the results:
|
||||||
iter := query.Iter()
|
iter := query.Iter()
|
||||||
entry := LogDBEntry{}
|
entry := LogDBEntry{}
|
||||||
pos := 0
|
pos := 0
|
||||||
|
|
||||||
|
// Reserve the memory for the results:
|
||||||
events = make([]LogDBEntry, count)
|
events = make([]LogDBEntry, count)
|
||||||
|
|
||||||
|
// Loop over all entries and store it:
|
||||||
for iter.Next(&entry) {
|
for iter.Next(&entry) {
|
||||||
events[pos] = entry
|
events[pos] = entry
|
||||||
pos++
|
pos++
|
||||||
|
@ -1,51 +1,9 @@
|
|||||||
package DeviceDatabase
|
package DeviceDatabase
|
||||||
|
|
||||||
import (
|
// Read the message names out of the cache.
|
||||||
"github.com/SommerEngineering/Ocean/Log"
|
|
||||||
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
|
||||||
"github.com/SommerEngineering/Ocean/Shutdown"
|
|
||||||
"gopkg.in/mgo.v2/bson"
|
|
||||||
"sort"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ReadMessageNames() (messageNames []string) {
|
func ReadMessageNames() (messageNames []string) {
|
||||||
mutexCacheMessageNames.RLock()
|
mutexCacheMessageNames.RLock()
|
||||||
defer mutexCacheMessageNames.RUnlock()
|
defer mutexCacheMessageNames.RUnlock()
|
||||||
messageNames = cacheMessageNames
|
messageNames = cacheMessageNames
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func cacheRefreshMessageNames() {
|
|
||||||
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `The message names' refresh thread is now running.`)
|
|
||||||
go func() {
|
|
||||||
for true {
|
|
||||||
|
|
||||||
data := readMessageNamesFromDB()
|
|
||||||
mutexCacheMessageNames.Lock()
|
|
||||||
cacheMessageNames = data
|
|
||||||
mutexCacheMessageNames.Unlock()
|
|
||||||
|
|
||||||
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelTALKATIVE, LM.MessageNameEXECUTE, `The message names' cache was refreshed.`)
|
|
||||||
time.Sleep(time.Duration(nameCachesRefreshTimeSeconds) * time.Second)
|
|
||||||
|
|
||||||
if Shutdown.IsDown() {
|
|
||||||
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityLow, LM.ImpactLow, LM.MessageNameSHUTDOWN, `The message name's refresh thread is now shutting down.`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
func readMessageNamesFromDB() (result []string) {
|
|
||||||
|
|
||||||
var nextMessageNames []string
|
|
||||||
if err := logDBCollection.Find(bson.D{}).Distinct(`MessageName`, &nextMessageNames); err != nil {
|
|
||||||
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.MessageNameDATABASE, `Was not able to read the message names from the database.`, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Strings(nextMessageNames)
|
|
||||||
result = nextMessageNames
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
23
Log/DeviceDatabase/ReadMessageNamesFromDB.go
Normal file
23
Log/DeviceDatabase/ReadMessageNamesFromDB.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package DeviceDatabase
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/SommerEngineering/Ocean/Log"
|
||||||
|
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
"gopkg.in/mgo.v2/bson"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Read the message names from the database without any cache.
|
||||||
|
func readMessageNamesFromDB() (result []string) {
|
||||||
|
var nextMessageNames []string
|
||||||
|
if err := logDBCollection.Find(bson.D{}).Distinct(`MessageName`, &nextMessageNames); err != nil {
|
||||||
|
// Case: Error, was not able to write the event to the database:
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.MessageNameDATABASE, `Was not able to read the message names from the database.`, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort the sender names:
|
||||||
|
sort.Strings(nextMessageNames)
|
||||||
|
result = nextMessageNames
|
||||||
|
return
|
||||||
|
}
|
@ -1,51 +1,9 @@
|
|||||||
package DeviceDatabase
|
package DeviceDatabase
|
||||||
|
|
||||||
import (
|
// Read the sender names out of the cache.
|
||||||
"github.com/SommerEngineering/Ocean/Log"
|
|
||||||
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
|
||||||
"github.com/SommerEngineering/Ocean/Shutdown"
|
|
||||||
"gopkg.in/mgo.v2/bson"
|
|
||||||
"sort"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ReadSenderNames() (senderNames []string) {
|
func ReadSenderNames() (senderNames []string) {
|
||||||
mutexCacheSenderNames.RLock()
|
mutexCacheSenderNames.RLock()
|
||||||
defer mutexCacheSenderNames.RUnlock()
|
defer mutexCacheSenderNames.RUnlock()
|
||||||
senderNames = cacheSenderNames
|
senderNames = cacheSenderNames
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func cacheRefreshSenderNames() {
|
|
||||||
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `The sender names' refresh thread is now running.`)
|
|
||||||
go func() {
|
|
||||||
for true {
|
|
||||||
|
|
||||||
data := readSenderNamesFromDB()
|
|
||||||
mutexCacheSenderNames.Lock()
|
|
||||||
cacheSenderNames = data
|
|
||||||
mutexCacheSenderNames.Unlock()
|
|
||||||
|
|
||||||
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelTALKATIVE, LM.MessageNameEXECUTE, `The sender names' cache was refreshed.`)
|
|
||||||
time.Sleep(time.Duration(nameCachesRefreshTimeSeconds) * time.Second)
|
|
||||||
|
|
||||||
if Shutdown.IsDown() {
|
|
||||||
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelWARN, LM.SeverityLow, LM.ImpactLow, LM.MessageNameSHUTDOWN, `The sender name's refresh thread is now shutting down.`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
func readSenderNamesFromDB() (result []string) {
|
|
||||||
|
|
||||||
var nextSenderNames []string
|
|
||||||
if err := logDBCollection.Find(bson.D{}).Distinct(`Sender`, &nextSenderNames); err != nil {
|
|
||||||
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.MessageNameDATABASE, `Was not able to read the sender names from the database.`, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Strings(nextSenderNames)
|
|
||||||
result = nextSenderNames
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
23
Log/DeviceDatabase/ReadSenderNamesFromDB.go
Normal file
23
Log/DeviceDatabase/ReadSenderNamesFromDB.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package DeviceDatabase
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/SommerEngineering/Ocean/Log"
|
||||||
|
LM "github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
|
"gopkg.in/mgo.v2/bson"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reads the sender names from the database without any caching.
|
||||||
|
func readSenderNamesFromDB() (result []string) {
|
||||||
|
var nextSenderNames []string
|
||||||
|
if err := logDBCollection.Find(bson.D{}).Distinct(`Sender`, &nextSenderNames); err != nil {
|
||||||
|
// Case: Was not possible to write to the database.
|
||||||
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.MessageNameDATABASE, `Was not able to read the sender names from the database.`, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort the sender names:
|
||||||
|
sort.Strings(nextSenderNames)
|
||||||
|
result = nextSenderNames
|
||||||
|
return
|
||||||
|
}
|
@ -4,14 +4,13 @@ import (
|
|||||||
"github.com/SommerEngineering/Ocean/Log/Meta"
|
"github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Database struct {
|
// This function is the interface between the logging system and the database logger.
|
||||||
}
|
|
||||||
|
|
||||||
func (dev Database) Log(entries []Meta.Entry) {
|
func (dev Database) Log(entries []Meta.Entry) {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Cannot log here to prevent endless loop (consumer is also producer)
|
// Cannot log here to prevent endless loop (consumer is also producer)
|
||||||
//
|
//
|
||||||
|
|
||||||
|
// Write every incoming batch to the cache:
|
||||||
write2Cache(entries)
|
write2Cache(entries)
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// The type for the database logging.
|
||||||
type LogDBEntry struct {
|
type LogDBEntry struct {
|
||||||
TimeUTC time.Time `bson:"TimeUTC"`
|
TimeUTC time.Time `bson:"TimeUTC"`
|
||||||
Project string `bson:"Project"`
|
Project string `bson:"Project"`
|
||||||
@ -17,8 +18,13 @@ type LogDBEntry struct {
|
|||||||
Parameters []string `bson:"Parameters"`
|
Parameters []string `bson:"Parameters"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A type for the TTL (time to live) for the index.
|
||||||
type TTLUpdateResult struct {
|
type TTLUpdateResult struct {
|
||||||
ExpireAfterSeconds_old int32 `bson:"expireAfterSeconds_old"`
|
ExpireAfterSeconds_old int32 `bson:"expireAfterSeconds_old"`
|
||||||
ExpireAfterSeconds_new int32 `bson:"expireAfterSeconds_new"`
|
ExpireAfterSeconds_new int32 `bson:"expireAfterSeconds_new"`
|
||||||
Ok int32 `bson:"ok"`
|
Ok int32 `bson:"ok"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The logging device.
|
||||||
|
type Database struct {
|
||||||
|
}
|
||||||
|
@ -5,31 +5,22 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Case: The cache is full
|
// The timeout function writes the logging events to the database afer some time.
|
||||||
func cacheFull() {
|
func startTimeout() {
|
||||||
mutexCacheFull.Lock()
|
|
||||||
defer mutexCacheFull.Unlock()
|
|
||||||
|
|
||||||
if len(cache) < cacheSizeNumberOfEvents {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for counter := 0; counter < cacheSizeNumberOfEvents; counter++ {
|
|
||||||
write2Database(<-cache)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Case: Time out
|
|
||||||
func initTimeout() {
|
|
||||||
|
|
||||||
|
// Starts a new thread:
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
|
|
||||||
|
// Case: The system goes down now.
|
||||||
if Shutdown.IsDown() {
|
if Shutdown.IsDown() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait for the right time:
|
||||||
time.Sleep(time.Duration(cacheSizeTime2FlushSeconds) * time.Second)
|
time.Sleep(time.Duration(cacheSizeTime2FlushSeconds) * time.Second)
|
||||||
|
|
||||||
|
// Write the events to the database:
|
||||||
mutexCacheFull.Lock()
|
mutexCacheFull.Lock()
|
||||||
amount := len(cache)
|
amount := len(cache)
|
||||||
for counter := 0; counter < amount; counter++ {
|
for counter := 0; counter < amount; counter++ {
|
@ -7,6 +7,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
// This is the name for logging event from this package:
|
||||||
senderName LM.Sender = `System::Logger::Database`
|
senderName LM.Sender = `System::Logger::Database`
|
||||||
mutexCacheFull sync.Mutex = sync.Mutex{}
|
mutexCacheFull sync.Mutex = sync.Mutex{}
|
||||||
mutexCacheSenderNames sync.RWMutex = sync.RWMutex{}
|
mutexCacheSenderNames sync.RWMutex = sync.RWMutex{}
|
||||||
|
@ -4,12 +4,17 @@ import (
|
|||||||
"github.com/SommerEngineering/Ocean/Log/Meta"
|
"github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// This function writes a batch of log entries to the cache.
|
||||||
func write2Cache(entries []Meta.Entry) {
|
func write2Cache(entries []Meta.Entry) {
|
||||||
|
// Loop over each entry:
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
|
// If the cache is full, execute it:
|
||||||
if len(cache) == cacheSizeNumberOfEvents {
|
if len(cache) == cacheSizeNumberOfEvents {
|
||||||
|
// Execute the cache with a new thread:
|
||||||
go cacheFull()
|
go cacheFull()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert the log entry to the database format:
|
||||||
logDBentry := LogDBEntry{}
|
logDBentry := LogDBEntry{}
|
||||||
logDBentry.Category = entry.Category.Format()
|
logDBentry.Category = entry.Category.Format()
|
||||||
logDBentry.Impact = entry.Impact.Format()
|
logDBentry.Impact = entry.Impact.Format()
|
||||||
@ -21,6 +26,8 @@ func write2Cache(entries []Meta.Entry) {
|
|||||||
logDBentry.Sender = string(entry.Sender)
|
logDBentry.Sender = string(entry.Sender)
|
||||||
logDBentry.Severity = entry.Severity.Format()
|
logDBentry.Severity = entry.Severity.Format()
|
||||||
logDBentry.TimeUTC = entry.Time
|
logDBentry.TimeUTC = entry.Time
|
||||||
|
|
||||||
|
// Write it to the cache:
|
||||||
cache <- logDBentry
|
cache <- logDBentry
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,15 @@
|
|||||||
package DeviceDatabase
|
package DeviceDatabase
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Function to write a logging event to the database.
|
||||||
func write2Database(entry LogDBEntry) {
|
func write2Database(entry LogDBEntry) {
|
||||||
|
// Try to write the event to the database:
|
||||||
if err := logDBCollection.Insert(entry); err != nil {
|
if err := logDBCollection.Insert(entry); err != nil {
|
||||||
// Can not log here to prevent endless loop (consumer is also producer)
|
// Case: Error!
|
||||||
|
// Cannot log here to prevent endless loop (consumer is also producer)
|
||||||
|
fmt.Printf("Was not able to write a logging event to the database: '%s'. The log entry was: '%s'.\n", err.Error(), entry.Format())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,18 +5,18 @@ import (
|
|||||||
"github.com/SommerEngineering/Ocean/Log/Meta"
|
"github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Queue a log event before delivery to the devices.
|
||||||
func deviceDelay(newEntry Meta.Entry) {
|
func deviceDelay(newEntry Meta.Entry) {
|
||||||
defer checkDeviceDelaySize()
|
defer checkDeviceDelaySize()
|
||||||
|
|
||||||
// Insert the new entry at the correct position (time)!
|
// Insert the new entry at the correct position (time).
|
||||||
|
// To ensure that the causality is guaranteed.
|
||||||
for logEvent := deviceDelayBuffer.Front(); logEvent != nil; logEvent = logEvent.Next() {
|
for logEvent := deviceDelayBuffer.Front(); logEvent != nil; logEvent = logEvent.Next() {
|
||||||
currentEvent := logEvent.Value.(Meta.Entry)
|
currentEvent := logEvent.Value.(Meta.Entry)
|
||||||
if newEntry.Time.Before(currentEvent.Time) {
|
if newEntry.Time.Before(currentEvent.Time) {
|
||||||
|
|
||||||
mutexDeviceDelays.Lock()
|
mutexDeviceDelays.Lock()
|
||||||
deviceDelayBuffer.InsertBefore(newEntry, logEvent)
|
deviceDelayBuffer.InsertBefore(newEntry, logEvent)
|
||||||
mutexDeviceDelays.Unlock()
|
mutexDeviceDelays.Unlock()
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -27,19 +27,32 @@ func deviceDelay(newEntry Meta.Entry) {
|
|||||||
mutexDeviceDelays.Unlock()
|
mutexDeviceDelays.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the size of the buffer is huge enough to deliver entries.
|
||||||
func checkDeviceDelaySize() {
|
func checkDeviceDelaySize() {
|
||||||
|
|
||||||
|
// Get exklusive access:
|
||||||
mutexDeviceDelays.Lock()
|
mutexDeviceDelays.Lock()
|
||||||
|
|
||||||
|
// Is the size huge enough?
|
||||||
if deviceDelayBuffer.Len() >= logDeviceDelayNumberEvents {
|
if deviceDelayBuffer.Len() >= logDeviceDelayNumberEvents {
|
||||||
|
|
||||||
|
// Read all entries:
|
||||||
dataArray := logEntryListToArray(deviceDelayBuffer)
|
dataArray := logEntryListToArray(deviceDelayBuffer)
|
||||||
|
|
||||||
|
// Re-init the buffer:
|
||||||
deviceDelayBuffer.Init()
|
deviceDelayBuffer.Init()
|
||||||
|
|
||||||
|
// Loop over all devices:
|
||||||
mutexDevices.RLock()
|
mutexDevices.RLock()
|
||||||
for entry := devices.Front(); entry != nil; entry = entry.Next() {
|
for entry := devices.Front(); entry != nil; entry = entry.Next() {
|
||||||
dev := entry.Value.(Device.Device)
|
dev := entry.Value.(Device.Device)
|
||||||
|
|
||||||
|
// Deliver the data with a new thread:
|
||||||
go dev.Log(dataArray)
|
go dev.Log(dataArray)
|
||||||
}
|
}
|
||||||
mutexDevices.RUnlock()
|
mutexDevices.RUnlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Release the lock:
|
||||||
mutexDeviceDelays.Unlock()
|
mutexDeviceDelays.Unlock()
|
||||||
}
|
}
|
||||||
|
@ -4,22 +4,25 @@ import (
|
|||||||
"github.com/SommerEngineering/Ocean/Log/Device"
|
"github.com/SommerEngineering/Ocean/Log/Device"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
// Function to force all buffers to flush the events.
|
||||||
Please do not call this function your self! This function allows Ocean to flush the logging at the shutting down case.
|
|
||||||
*/
|
|
||||||
func Flush() {
|
func Flush() {
|
||||||
|
|
||||||
|
// Close the entry buffer:
|
||||||
mutexChannel.Lock()
|
mutexChannel.Lock()
|
||||||
channelReady = false
|
channelReady = false
|
||||||
close(entriesBuffer)
|
close(entriesBuffer)
|
||||||
mutexChannel.Unlock()
|
mutexChannel.Unlock()
|
||||||
|
|
||||||
|
// Wait that the scheduler is done:
|
||||||
<-schedulerExitSignal
|
<-schedulerExitSignal
|
||||||
|
|
||||||
|
// Get all log entries from the device delay buffer:
|
||||||
mutexDeviceDelays.Lock()
|
mutexDeviceDelays.Lock()
|
||||||
dataArray := logEntryListToArray(deviceDelayBuffer)
|
dataArray := logEntryListToArray(deviceDelayBuffer)
|
||||||
deviceDelayBuffer.Init()
|
deviceDelayBuffer.Init()
|
||||||
mutexDeviceDelays.Unlock()
|
mutexDeviceDelays.Unlock()
|
||||||
|
|
||||||
|
// Deliver all the events to all devices:
|
||||||
mutexDevices.RLock()
|
mutexDevices.RLock()
|
||||||
for entry := devices.Front(); entry != nil; entry = entry.Next() {
|
for entry := devices.Front(); entry != nil; entry = entry.Next() {
|
||||||
dev := entry.Value.(Device.Device)
|
dev := entry.Value.(Device.Device)
|
||||||
|
@ -7,12 +7,12 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Writes a log message to the channel.
|
||||||
func writeToChannel(logEntry Meta.Entry) {
|
func writeToChannel(logEntry Meta.Entry) {
|
||||||
select {
|
select {
|
||||||
case entriesBuffer <- logEntry:
|
case entriesBuffer <- logEntry:
|
||||||
case <-time.After(time.Duration(int64(logBufferTimeoutSeconds)) * time.Second):
|
case <-time.After(time.Duration(int64(logBufferTimeoutSeconds)) * time.Second):
|
||||||
|
// Warn: Cannot log here to prevent endless loop and memory leak!
|
||||||
// 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())
|
fmt.Println(`Warning: Was not able to write to the logging buffer! Message=` + logEntry.Format())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,6 +110,7 @@ func LogShort(sender Meta.Sender, category Meta.Category, level Meta.Level, mess
|
|||||||
TakeEntry(entry)
|
TakeEntry(entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Removes white spaces from the message.
|
||||||
func clearEntry(entry Meta.Entry) (result Meta.Entry) {
|
func clearEntry(entry Meta.Entry) (result Meta.Entry) {
|
||||||
entry.MessageDescription = removeWhitespaces(entry.MessageDescription)
|
entry.MessageDescription = removeWhitespaces(entry.MessageDescription)
|
||||||
entry.Parameters = clearParameters(entry.Parameters)
|
entry.Parameters = clearParameters(entry.Parameters)
|
||||||
@ -117,6 +118,7 @@ func clearEntry(entry Meta.Entry) (result Meta.Entry) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove white spaces from the parameters.
|
||||||
func clearParameters(oldParameters []string) (result []string) {
|
func clearParameters(oldParameters []string) (result []string) {
|
||||||
for n := 0; n < len(oldParameters); n++ {
|
for n := 0; n < len(oldParameters); n++ {
|
||||||
oldParameters[n] = removeWhitespaces(oldParameters[n])
|
oldParameters[n] = removeWhitespaces(oldParameters[n])
|
||||||
@ -126,6 +128,7 @@ func clearParameters(oldParameters []string) (result []string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Removes white spaces from a string.
|
||||||
func removeWhitespaces(text string) (result string) {
|
func removeWhitespaces(text string) (result string) {
|
||||||
text = strings.Replace(text, "\n", ` `, -1)
|
text = strings.Replace(text, "\n", ` `, -1)
|
||||||
text = strings.Replace(text, "\t", ` `, -1)
|
text = strings.Replace(text, "\t", ` `, -1)
|
||||||
|
41
Log/Init.go
41
Log/Init.go
@ -3,44 +3,29 @@ package Log
|
|||||||
import (
|
import (
|
||||||
"container/list"
|
"container/list"
|
||||||
"github.com/SommerEngineering/Ocean/Log/Meta"
|
"github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
func readProjectName() {
|
// Init the logging package.
|
||||||
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() {
|
func init() {
|
||||||
|
|
||||||
|
// Read the project name:
|
||||||
readProjectName()
|
readProjectName()
|
||||||
|
|
||||||
|
// Create the mutexe:
|
||||||
mutexDeviceDelays = sync.Mutex{}
|
mutexDeviceDelays = sync.Mutex{}
|
||||||
mutexPreChannelBuffer = sync.Mutex{}
|
mutexPreChannelBuffer = sync.Mutex{}
|
||||||
mutexChannel = sync.RWMutex{}
|
mutexChannel = sync.RWMutex{}
|
||||||
|
|
||||||
|
// Create buffers:
|
||||||
preChannelBuffer = list.New()
|
preChannelBuffer = list.New()
|
||||||
deviceDelayBuffer = list.New()
|
deviceDelayBuffer = list.New()
|
||||||
|
|
||||||
|
// Create the device list:
|
||||||
devices = list.New()
|
devices = list.New()
|
||||||
|
|
||||||
|
// Channel to exit the scheduler:
|
||||||
schedulerExitSignal = make(chan bool)
|
schedulerExitSignal = make(chan bool)
|
||||||
|
|
||||||
initTimer()
|
initTimer()
|
||||||
@ -48,8 +33,10 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func initCode() {
|
func initCode() {
|
||||||
|
// Creates the buffer for logging entries:
|
||||||
entriesBuffer = make(chan Meta.Entry, logBufferSize)
|
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)))
|
LogShort(senderName, Meta.CategorySYSTEM, Meta.LevelINFO, `Starting`, `The logger is now starting.`, `logBufferSize=`+strconv.Itoa(int(logBufferSize)), `logBufferTimeoutSeconds=`+strconv.Itoa(int(logBufferTimeoutSeconds)))
|
||||||
|
|
||||||
|
// Start the scheduler as new thread:
|
||||||
go scheduler(entriesBuffer)
|
go scheduler(entriesBuffer)
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"github.com/SommerEngineering/Ocean/Log/Meta"
|
"github.com/SommerEngineering/Ocean/Log/Meta"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Converts a list with logging events to an array.
|
||||||
func logEntryListToArray(data *list.List) (result []Meta.Entry) {
|
func logEntryListToArray(data *list.List) (result []Meta.Entry) {
|
||||||
count := data.Len()
|
count := data.Len()
|
||||||
result = make([]Meta.Entry, count, count)
|
result = make([]Meta.Entry, count, count)
|
@ -1,7 +1,8 @@
|
|||||||
package Log
|
package Log
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This function is used just internal by Ocean. Please do not call this function by your self!
|
A function to change the state of the logging after the database is
|
||||||
|
accessible.
|
||||||
*/
|
*/
|
||||||
func LoggingIsReady() {
|
func LoggingIsReady() {
|
||||||
channelReady = true
|
channelReady = true
|
||||||
|
@ -3,12 +3,13 @@ package Meta
|
|||||||
type Category byte
|
type Category byte
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CategoryBUSINESS = Category(iota)
|
CategoryBUSINESS = Category(iota) // Business category
|
||||||
CategorySYSTEM = Category(iota)
|
CategorySYSTEM = Category(iota) // System category
|
||||||
CategoryAPP = Category(iota)
|
CategoryAPP = Category(iota) // Application category
|
||||||
CategoryUSER = Category(iota)
|
CategoryUSER = Category(iota) // User category
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Formats a category as string.
|
||||||
func (cat Category) Format() (result string) {
|
func (cat Category) Format() (result string) {
|
||||||
switch cat {
|
switch cat {
|
||||||
case CategoryBUSINESS:
|
case CategoryBUSINESS:
|
||||||
@ -26,6 +27,7 @@ func (cat Category) Format() (result string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse a category from a string.
|
||||||
func ParseCategory(cat string) (value Category) {
|
func ParseCategory(cat string) (value Category) {
|
||||||
switch cat {
|
switch cat {
|
||||||
case `C:BUSINESS`:
|
case `C:BUSINESS`:
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Type for a log entry.
|
||||||
type Entry struct {
|
type Entry struct {
|
||||||
Project string
|
Project string
|
||||||
Time time.Time
|
Time time.Time
|
||||||
|
@ -7,18 +7,22 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Formats a log entry as string.
|
||||||
func (entry Entry) Format() (result string) {
|
func (entry Entry) Format() (result string) {
|
||||||
|
|
||||||
|
// Force the maximum length for a sender:
|
||||||
lenSender := len(entry.Sender)
|
lenSender := len(entry.Sender)
|
||||||
if lenSender > 40 {
|
if lenSender > 40 {
|
||||||
lenSender = 40
|
lenSender = 40
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Force the maximum length for the message name:
|
||||||
lenMessageName := len(entry.MessageName)
|
lenMessageName := len(entry.MessageName)
|
||||||
if lenMessageName > 26 {
|
if lenMessageName > 26 {
|
||||||
lenMessageName = 26
|
lenMessageName = 26
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Force the maximum length for the project name:
|
||||||
lenProject := len(entry.Project)
|
lenProject := len(entry.Project)
|
||||||
if lenProject > 10 {
|
if lenProject > 10 {
|
||||||
lenProject = 10
|
lenProject = 10
|
||||||
@ -29,21 +33,23 @@ func (entry Entry) Format() (result string) {
|
|||||||
messageDescription = strings.Replace(messageDescription, "\t", ` `, -1)
|
messageDescription = strings.Replace(messageDescription, "\t", ` `, -1)
|
||||||
messageDescription = strings.Replace(messageDescription, "\r", ` `, -1)
|
messageDescription = strings.Replace(messageDescription, "\r", ` `, -1)
|
||||||
|
|
||||||
|
// Format the basic fields of the log message:
|
||||||
result = fmt.Sprintf(` [■] P:%10s [■] %s [■] %10s [■] %11s [■] %10s [■] %10s [■] sender: %-40s [■] name: %-26s [■] %s [■]`, entry.Project[:lenProject], formatTime(entry.Time), entry.Category.Format(), entry.Level.Format(), entry.Severity.Format(), entry.Impact.Format(), entry.Sender[:lenSender], entry.MessageName[:lenMessageName], messageDescription)
|
result = fmt.Sprintf(` [■] P:%10s [■] %s [■] %10s [■] %11s [■] %10s [■] %10s [■] sender: %-40s [■] name: %-26s [■] %s [■]`, entry.Project[:lenProject], formatTime(entry.Time), entry.Category.Format(), entry.Level.Format(), entry.Severity.Format(), entry.Impact.Format(), entry.Sender[:lenSender], entry.MessageName[:lenMessageName], messageDescription)
|
||||||
|
|
||||||
|
// Formats the parameters:
|
||||||
for _, param := range entry.Parameters {
|
for _, param := range entry.Parameters {
|
||||||
|
|
||||||
paramText := param
|
paramText := param
|
||||||
paramText = strings.Replace(paramText, "\n", ` `, -1)
|
paramText = strings.Replace(paramText, "\n", ` `, -1)
|
||||||
paramText = strings.Replace(paramText, "\t", ` `, -1)
|
paramText = strings.Replace(paramText, "\t", ` `, -1)
|
||||||
paramText = strings.Replace(paramText, "\r", ` `, -1)
|
paramText = strings.Replace(paramText, "\r", ` `, -1)
|
||||||
|
|
||||||
result = fmt.Sprintf(`%s %s [■]`, result, paramText)
|
result = fmt.Sprintf(`%s %s [■]`, result, paramText)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Formats the given time as YYYYMMdd HHmmss.fff! This function is necessary
|
||||||
|
// to prevent a import cycle.
|
||||||
func formatTime(t1 time.Time) (result string) {
|
func formatTime(t1 time.Time) (result string) {
|
||||||
var year int = t1.Year()
|
var year int = t1.Year()
|
||||||
var month int = int(t1.Month())
|
var month int = int(t1.Month())
|
||||||
|
@ -3,14 +3,15 @@ package Meta
|
|||||||
type Impact byte
|
type Impact byte
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ImpactNone = Impact(iota)
|
ImpactNone = Impact(iota) // None impact
|
||||||
ImpactLow = Impact(iota)
|
ImpactLow = Impact(iota) // Low impact
|
||||||
ImpactMiddle = Impact(iota)
|
ImpactMiddle = Impact(iota) // Middle impact
|
||||||
ImpactHigh = Impact(iota)
|
ImpactHigh = Impact(iota) // High impact
|
||||||
ImpactCritical = Impact(iota)
|
ImpactCritical = Impact(iota) // Critical impact
|
||||||
ImpactUnknown = Impact(iota)
|
ImpactUnknown = Impact(iota) // Unknown impact
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Formats a impact as string.
|
||||||
func (pri Impact) Format() (result string) {
|
func (pri Impact) Format() (result string) {
|
||||||
switch pri {
|
switch pri {
|
||||||
case ImpactCritical:
|
case ImpactCritical:
|
||||||
@ -32,6 +33,7 @@ func (pri Impact) Format() (result string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse a impact from a string.
|
||||||
func ParseImpact(pri string) (value Impact) {
|
func ParseImpact(pri string) (value Impact) {
|
||||||
switch pri {
|
switch pri {
|
||||||
case `I:CRITICAL`:
|
case `I:CRITICAL`:
|
||||||
|
@ -3,14 +3,15 @@ package Meta
|
|||||||
type Level byte
|
type Level byte
|
||||||
|
|
||||||
const (
|
const (
|
||||||
LevelWARN = Level(iota)
|
LevelWARN = Level(iota) // Level: Warning
|
||||||
LevelDEBUG = Level(iota)
|
LevelDEBUG = Level(iota) // Level: Debug
|
||||||
LevelERROR = Level(iota)
|
LevelERROR = Level(iota) // Level: Error
|
||||||
LevelINFO = Level(iota)
|
LevelINFO = Level(iota) // Level: Information
|
||||||
LevelTALKATIVE = Level(iota)
|
LevelTALKATIVE = Level(iota) // Level: Talkative (even more events as debug)
|
||||||
LevelSECURITY = Level(iota)
|
LevelSECURITY = Level(iota) // Level: Security
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Formats a level as string.
|
||||||
func (lvl Level) Format() (result string) {
|
func (lvl Level) Format() (result string) {
|
||||||
switch lvl {
|
switch lvl {
|
||||||
case LevelDEBUG:
|
case LevelDEBUG:
|
||||||
@ -32,6 +33,7 @@ func (lvl Level) Format() (result string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse a level from a string.
|
||||||
func ParseLevel(lvl string) (value Level) {
|
func ParseLevel(lvl string) (value Level) {
|
||||||
switch lvl {
|
switch lvl {
|
||||||
case `L:DEBUG`:
|
case `L:DEBUG`:
|
||||||
|
@ -2,37 +2,38 @@ package Meta
|
|||||||
|
|
||||||
type MessageName string
|
type MessageName string
|
||||||
|
|
||||||
|
// Some pre-defined message names:
|
||||||
const (
|
const (
|
||||||
MessageNameSTARTUP = `Startup`
|
MessageNameSTARTUP = `Startup` // e.g. the server startup
|
||||||
MessageNameINIT = `Init`
|
MessageNameINIT = `Init` // some kind of init event
|
||||||
MessageNameSHUTDOWN = `Shutdown`
|
MessageNameSHUTDOWN = `Shutdown` // some kind of shutdown event
|
||||||
MessageNameEXECUTE = `Execute`
|
MessageNameEXECUTE = `Execute` // some kind of execution context
|
||||||
MessageNameDATABASE = `Database`
|
MessageNameDATABASE = `Database` // events which are related to database issues
|
||||||
MessageNameNETWORK = `Network`
|
MessageNameNETWORK = `Network` // events which are related to network issues
|
||||||
MessageNameLOGIN = `Login`
|
MessageNameLOGIN = `Login` // some kind of login event
|
||||||
MessageNameLOGOUT = `Logout`
|
MessageNameLOGOUT = `Logout` // some kind of logout event
|
||||||
MessageNameSESSION = `Session`
|
MessageNameSESSION = `Session` // some kind of session event
|
||||||
MessageNameTIMEOUT = `Timeout`
|
MessageNameTIMEOUT = `Timeout` // some kind of timeout event
|
||||||
MessageNameFILESYSTEM = `Filesystem`
|
MessageNameFILESYSTEM = `Filesystem` // events which are related to the filesystem
|
||||||
MessageNameCOMMUNICATION = `Communication`
|
MessageNameCOMMUNICATION = `Communication` // events which are related to communication issues
|
||||||
MessageNameWRITE = `Write`
|
MessageNameWRITE = `Write` // some kind of write event
|
||||||
MessageNameREAD = `Read`
|
MessageNameREAD = `Read` // some kind of read event
|
||||||
MessageNameALGORITHM = `Algorithm`
|
MessageNameALGORITHM = `Algorithm` // some kind of algorithm event
|
||||||
MessageNameCONFIGURATION = `Configuration`
|
MessageNameCONFIGURATION = `Configuration` // some kind of configuration event
|
||||||
MessageNameTIMER = `Timer`
|
MessageNameTIMER = `Timer` // some kind of timer event
|
||||||
MessageNameINPUT = `Input`
|
MessageNameINPUT = `Input` // some kind of input event
|
||||||
MessageNameOUTPUT = `Output`
|
MessageNameOUTPUT = `Output` // some kind of output event
|
||||||
MessageNameBROWSER = `Browser`
|
MessageNameBROWSER = `Browser` // some kind of browser event
|
||||||
MessageNameSECURITY = `Security`
|
MessageNameSECURITY = `Security` // some kind of security event
|
||||||
MessageNameNOTFOUND = `NotFound`
|
MessageNameNOTFOUND = `NotFound` // something was not found
|
||||||
MessageNameANALYSIS = `Analysis`
|
MessageNameANALYSIS = `Analysis` // some kind of analysis event
|
||||||
MessageNameSTATE = `State`
|
MessageNameSTATE = `State` // some kind of state-related event
|
||||||
MessageNameGENERATOR = `Generator`
|
MessageNameGENERATOR = `Generator` // some kind of generator event
|
||||||
MessageNamePRODUCER = `Producer`
|
MessageNamePRODUCER = `Producer` // some kind of producer event
|
||||||
MessageNameCONSUMER = `Consumer`
|
MessageNameCONSUMER = `Consumer` // some kind of consumer event
|
||||||
MessageNamePASSWORD = `Password`
|
MessageNamePASSWORD = `Password` // some kind of password event
|
||||||
MessageNamePARSE = `Parse`
|
MessageNamePARSE = `Parse` // some kind of parser-related event
|
||||||
MessageNameUSER = `User`
|
MessageNameUSER = `User` // some kind of user event
|
||||||
MessageNameREQUEST = `Request`
|
MessageNameREQUEST = `Request` // some kind of request-related event
|
||||||
MessageNameRESPONSE = `Response`
|
MessageNameRESPONSE = `Response` // some kind of response-related event
|
||||||
)
|
)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
package Meta
|
package Meta
|
||||||
|
|
||||||
|
// Type for the sender name
|
||||||
type Sender string
|
type Sender string
|
||||||
|
@ -3,14 +3,15 @@ package Meta
|
|||||||
type Severity byte
|
type Severity byte
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SeverityNone = Severity(iota)
|
SeverityNone = Severity(iota) // None severity
|
||||||
SeverityLow = Severity(iota)
|
SeverityLow = Severity(iota) // Low severity
|
||||||
SeverityMiddle = Severity(iota)
|
SeverityMiddle = Severity(iota) // Middle severity
|
||||||
SeverityHigh = Severity(iota)
|
SeverityHigh = Severity(iota) // High severity
|
||||||
SeverityCritical = Severity(iota)
|
SeverityCritical = Severity(iota) // Critical severity
|
||||||
SeverityUnknown = Severity(iota)
|
SeverityUnknown = Severity(iota) // Unknown severity
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Format the severity as string.
|
||||||
func (pri Severity) Format() (result string) {
|
func (pri Severity) Format() (result string) {
|
||||||
switch pri {
|
switch pri {
|
||||||
case SeverityCritical:
|
case SeverityCritical:
|
||||||
@ -32,6 +33,7 @@ func (pri Severity) Format() (result string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse the severity from a string.
|
||||||
func ParseSeverity(pri string) (value Severity) {
|
func ParseSeverity(pri string) (value Severity) {
|
||||||
switch pri {
|
switch pri {
|
||||||
case `S:CRITICAL`:
|
case `S:CRITICAL`:
|
||||||
|
38
Log/ReadProjectName.go
Normal file
38
Log/ReadProjectName.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package Log
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Read the project name out of the local configuration file "project.name".
|
||||||
|
func readProjectName() {
|
||||||
|
// Try to get access to the working directory:
|
||||||
|
if currentDir, dirError := os.Getwd(); dirError != nil {
|
||||||
|
// Case: Error! Stop the server.
|
||||||
|
panic(`Cannot read the current working directory and therefore cannot read the project name!`)
|
||||||
|
} else {
|
||||||
|
// Try to get access to the file:
|
||||||
|
filename := filepath.Join(currentDir, `project.name`)
|
||||||
|
if _, errFile := os.Stat(filename); errFile != nil {
|
||||||
|
// Cases: Error.
|
||||||
|
if os.IsNotExist(errFile) {
|
||||||
|
panic(`Cannot read the project name file 'project.name': File not found!`)
|
||||||
|
} else {
|
||||||
|
panic(`Cannot read the project name file 'project.name': ` + errFile.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to read the file:
|
||||||
|
if projectNameBytes, errRead := ioutil.ReadFile(filename); errRead != nil {
|
||||||
|
// Case: Error.
|
||||||
|
panic(`Cannot read the project name file 'project.name': ` + errRead.Error())
|
||||||
|
} else {
|
||||||
|
// Store the project name:
|
||||||
|
projectName = string(projectNameBytes)
|
||||||
|
projectName = strings.TrimSpace(projectName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,21 +5,30 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Note: The scheduler is the consumer for the logging channel!
|
/*
|
||||||
|
The scheduler function which runs at a own thread.
|
||||||
|
Pleae note: The scheduler is the consumer for the logging channel.
|
||||||
|
*/
|
||||||
func scheduler(logBuffer chan Meta.Entry) {
|
func scheduler(logBuffer chan Meta.Entry) {
|
||||||
|
|
||||||
LogShort(senderName, Meta.CategorySYSTEM, Meta.LevelINFO, Meta.MessageNameSTARTUP, `The scheduler runs now.`)
|
LogShort(senderName, Meta.CategorySYSTEM, Meta.LevelINFO, Meta.MessageNameSTARTUP, `The scheduler runs now.`)
|
||||||
var stopNextTime = false
|
var stopNextTime = false
|
||||||
|
|
||||||
|
// Endless loop:
|
||||||
for {
|
for {
|
||||||
|
|
||||||
|
// Enable the loop to stop:
|
||||||
if stopNextTime {
|
if stopNextTime {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read one entry from the buffer (channel):
|
||||||
nextEntry, ok := <-logBuffer
|
nextEntry, ok := <-logBuffer
|
||||||
|
|
||||||
|
// Case: The channel was closed.
|
||||||
if !ok {
|
if !ok {
|
||||||
// The channel was closed!
|
|
||||||
|
// Create a log message for this event.
|
||||||
stopNextTime = true
|
stopNextTime = true
|
||||||
nextEntry = Meta.Entry{}
|
nextEntry = Meta.Entry{}
|
||||||
nextEntry.Project = projectName
|
nextEntry.Project = projectName
|
||||||
@ -33,9 +42,11 @@ func scheduler(logBuffer chan Meta.Entry) {
|
|||||||
nextEntry.MessageDescription = `The logging channel was closed!`
|
nextEntry.MessageDescription = `The logging channel was closed!`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Queue the log event for the delivery to the devices:
|
||||||
deviceDelay(nextEntry)
|
deviceDelay(nextEntry)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Exit the scheduler. Send the signal.
|
||||||
LogFull(senderName, Meta.CategorySYSTEM, Meta.LevelWARN, Meta.SeverityCritical, Meta.ImpactNone, Meta.MessageNameSHUTDOWN, `The scheduler is down now.`)
|
LogFull(senderName, Meta.CategorySYSTEM, Meta.LevelWARN, Meta.SeverityCritical, Meta.ImpactNone, Meta.MessageNameSHUTDOWN, `The scheduler is down now.`)
|
||||||
schedulerExitSignal <- true
|
schedulerExitSignal <- true
|
||||||
}
|
}
|
||||||
|
20
Log/Timer.go
20
Log/Timer.go
@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
func initTimer() {
|
func initTimer() {
|
||||||
|
|
||||||
|
// Ensure that the timer runs only once:
|
||||||
if timerIsRunning == true {
|
if timerIsRunning == true {
|
||||||
LogFull(senderName, Meta.CategorySYSTEM, Meta.LevelWARN, Meta.SeverityHigh, Meta.ImpactNone, Meta.MessageNameSTARTUP, `The logging timer is already running.`)
|
LogFull(senderName, Meta.CategorySYSTEM, Meta.LevelWARN, Meta.SeverityHigh, Meta.ImpactNone, Meta.MessageNameSTARTUP, `The logging timer is already running.`)
|
||||||
return
|
return
|
||||||
@ -17,23 +18,40 @@ func initTimer() {
|
|||||||
timerIsRunning = true
|
timerIsRunning = true
|
||||||
LogShort(senderName, Meta.CategorySYSTEM, Meta.LevelINFO, Meta.MessageNameSTARTUP, `Create the logging timer now.`, fmt.Sprintf(`Timeout=%d seconds`, logDeviceDelayTimeoutSeconds))
|
LogShort(senderName, Meta.CategorySYSTEM, Meta.LevelINFO, Meta.MessageNameSTARTUP, `Create the logging timer now.`, fmt.Sprintf(`Timeout=%d seconds`, logDeviceDelayTimeoutSeconds))
|
||||||
|
|
||||||
|
// Start the timer at a own thread:
|
||||||
go func() {
|
go func() {
|
||||||
|
|
||||||
|
// An endless loop:
|
||||||
for {
|
for {
|
||||||
|
|
||||||
|
// Wait for the next run time:
|
||||||
time.Sleep(time.Duration(logDeviceDelayTimeoutSeconds) * time.Second)
|
time.Sleep(time.Duration(logDeviceDelayTimeoutSeconds) * time.Second)
|
||||||
|
|
||||||
|
// Get exklusive access to the buffer:
|
||||||
mutexDeviceDelays.Lock()
|
mutexDeviceDelays.Lock()
|
||||||
|
|
||||||
|
// Read all the data from the device delay buffer:
|
||||||
dataArray := logEntryListToArray(deviceDelayBuffer)
|
dataArray := logEntryListToArray(deviceDelayBuffer)
|
||||||
|
|
||||||
|
// Re-init the buffer:
|
||||||
deviceDelayBuffer.Init()
|
deviceDelayBuffer.Init()
|
||||||
|
|
||||||
|
// Release the lock to the buffer:
|
||||||
mutexDeviceDelays.Unlock()
|
mutexDeviceDelays.Unlock()
|
||||||
|
|
||||||
|
// Read-lock to read the devices list:
|
||||||
mutexDevices.RLock()
|
mutexDevices.RLock()
|
||||||
|
|
||||||
|
// For each logging device:
|
||||||
for entry := devices.Front(); entry != nil; entry = entry.Next() {
|
for entry := devices.Front(); entry != nil; entry = entry.Next() {
|
||||||
dev := entry.Value.(Device.Device)
|
dev := entry.Value.(Device.Device)
|
||||||
|
|
||||||
|
// Deliver the current logging events with an extra thread:
|
||||||
go dev.Log(dataArray)
|
go dev.Log(dataArray)
|
||||||
}
|
}
|
||||||
mutexDevices.RUnlock()
|
|
||||||
|
|
||||||
|
// Release the read-lock:
|
||||||
|
mutexDevices.RUnlock()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
@ -7,22 +7,22 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
entriesBuffer chan Meta.Entry = nil
|
entriesBuffer chan Meta.Entry = nil // The channel / buffer for new log entries
|
||||||
schedulerExitSignal chan bool = nil
|
schedulerExitSignal chan bool = nil // Exit signal for the scheduler
|
||||||
logBufferSize int = 500
|
logBufferSize int = 500 // Buffer size for the logging
|
||||||
logBufferTimeoutSeconds int = 4
|
logBufferTimeoutSeconds int = 4 // Timeout for the logging
|
||||||
logDeviceDelayNumberEvents int = 600
|
logDeviceDelayNumberEvents int = 600 // Delay of # of events for the devices
|
||||||
logDeviceDelayTimeoutSeconds int = 5
|
logDeviceDelayTimeoutSeconds int = 5 // Timeout for the logging devices
|
||||||
channelReady bool = false
|
channelReady bool = false // State of the channel
|
||||||
preChannelBufferUsed bool = false
|
preChannelBufferUsed bool = false // State of the logging (pre or ready?)
|
||||||
preChannelBuffer *list.List = nil
|
preChannelBuffer *list.List = nil // Extra buffer for the pre logging phase
|
||||||
deviceDelayBuffer *list.List = nil
|
deviceDelayBuffer *list.List = nil // Buffer for the batch write to the devices
|
||||||
devices *list.List = nil
|
devices *list.List = nil // List of all devices
|
||||||
mutexDeviceDelays sync.Mutex = sync.Mutex{}
|
mutexDeviceDelays sync.Mutex = sync.Mutex{} // Mutex for buffer
|
||||||
mutexPreChannelBuffer sync.Mutex = sync.Mutex{}
|
mutexPreChannelBuffer sync.Mutex = sync.Mutex{} // Mutex for buffer
|
||||||
mutexChannel sync.RWMutex = sync.RWMutex{}
|
mutexChannel sync.RWMutex = sync.RWMutex{} // Mutex for the main channel
|
||||||
mutexDevices sync.RWMutex = sync.RWMutex{}
|
mutexDevices sync.RWMutex = sync.RWMutex{} // Mutex for the devices
|
||||||
timerIsRunning bool = false
|
timerIsRunning bool = false // Status of timer
|
||||||
projectName string = `not set`
|
projectName string = `not set` // The project name for the logging
|
||||||
senderName Meta.Sender = `System::Log`
|
senderName Meta.Sender = `System::Log` // This is the name for logging event from this package
|
||||||
)
|
)
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Handler for some CSS data for the web logger.
|
||||||
func HandlerCSSNormalize(response http.ResponseWriter, request *http.Request) {
|
func HandlerCSSNormalize(response http.ResponseWriter, request *http.Request) {
|
||||||
|
|
||||||
if Shutdown.IsDown() {
|
if Shutdown.IsDown() {
|
||||||
@ -19,6 +20,7 @@ func HandlerCSSNormalize(response http.ResponseWriter, request *http.Request) {
|
|||||||
fmt.Fprint(response, Assets.CSSNormalize)
|
fmt.Fprint(response, Assets.CSSNormalize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handler for some CSS data for the web logger.
|
||||||
func HandlerCSSWebflow(response http.ResponseWriter, request *http.Request) {
|
func HandlerCSSWebflow(response http.ResponseWriter, request *http.Request) {
|
||||||
|
|
||||||
if Shutdown.IsDown() {
|
if Shutdown.IsDown() {
|
||||||
@ -30,6 +32,7 @@ func HandlerCSSWebflow(response http.ResponseWriter, request *http.Request) {
|
|||||||
fmt.Fprint(response, Assets.CSSWebflow)
|
fmt.Fprint(response, Assets.CSSWebflow)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handler for some CSS data for the web logger.
|
||||||
func HandlerCSSLog(response http.ResponseWriter, request *http.Request) {
|
func HandlerCSSLog(response http.ResponseWriter, request *http.Request) {
|
||||||
|
|
||||||
if Shutdown.IsDown() {
|
if Shutdown.IsDown() {
|
||||||
@ -41,6 +44,7 @@ func HandlerCSSLog(response http.ResponseWriter, request *http.Request) {
|
|||||||
fmt.Fprint(response, Assets.CSSLog)
|
fmt.Fprint(response, Assets.CSSLog)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handler for some JS for the web logger.
|
||||||
func HandlerJSModernizr(response http.ResponseWriter, request *http.Request) {
|
func HandlerJSModernizr(response http.ResponseWriter, request *http.Request) {
|
||||||
|
|
||||||
if Shutdown.IsDown() {
|
if Shutdown.IsDown() {
|
||||||
@ -52,6 +56,7 @@ func HandlerJSModernizr(response http.ResponseWriter, request *http.Request) {
|
|||||||
fmt.Fprint(response, Assets.JSModernizr)
|
fmt.Fprint(response, Assets.JSModernizr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handler for some JS for the web logger.
|
||||||
func HandlerJSWebflow(response http.ResponseWriter, request *http.Request) {
|
func HandlerJSWebflow(response http.ResponseWriter, request *http.Request) {
|
||||||
|
|
||||||
if Shutdown.IsDown() {
|
if Shutdown.IsDown() {
|
||||||
@ -63,6 +68,7 @@ func HandlerJSWebflow(response http.ResponseWriter, request *http.Request) {
|
|||||||
fmt.Fprint(response, Assets.JSWebflow)
|
fmt.Fprint(response, Assets.JSWebflow)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handler for some JS for the web logger.
|
||||||
func HandlerJSjQuery(response http.ResponseWriter, request *http.Request) {
|
func HandlerJSjQuery(response http.ResponseWriter, request *http.Request) {
|
||||||
|
|
||||||
if Shutdown.IsDown() {
|
if Shutdown.IsDown() {
|
||||||
@ -74,6 +80,7 @@ func HandlerJSjQuery(response http.ResponseWriter, request *http.Request) {
|
|||||||
fmt.Fprint(response, Assets.JSjQuery)
|
fmt.Fprint(response, Assets.JSjQuery)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handler for some JS for the web logger.
|
||||||
func HandlerJSjQueryMap(response http.ResponseWriter, request *http.Request) {
|
func HandlerJSjQueryMap(response http.ResponseWriter, request *http.Request) {
|
||||||
|
|
||||||
if Shutdown.IsDown() {
|
if Shutdown.IsDown() {
|
||||||
|
@ -11,30 +11,32 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Handler for accessing the web logging.
|
||||||
func HandlerWebLog(response http.ResponseWriter, request *http.Request) {
|
func HandlerWebLog(response http.ResponseWriter, request *http.Request) {
|
||||||
|
|
||||||
|
// Case: The system goes down now.
|
||||||
if Shutdown.IsDown() {
|
if Shutdown.IsDown() {
|
||||||
http.NotFound(response, request)
|
http.NotFound(response, request)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Execute the HTTP form:
|
||||||
request.ParseForm()
|
request.ParseForm()
|
||||||
countParameters := len(request.Form)
|
countParameters := len(request.Form)
|
||||||
|
|
||||||
|
// Setup the data for the HTML template:
|
||||||
data := Scheme.Viewer{}
|
data := Scheme.Viewer{}
|
||||||
data.Title = `Web Log Viewer`
|
data.Title = `Web Log Viewer`
|
||||||
data.Sender = DeviceDatabase.ReadSenderNames()
|
data.Sender = DeviceDatabase.ReadSenderNames()
|
||||||
data.MessageNames = DeviceDatabase.ReadMessageNames()
|
data.MessageNames = DeviceDatabase.ReadMessageNames()
|
||||||
|
|
||||||
|
// To less parameters?
|
||||||
if countParameters < 9 {
|
if countParameters < 9 {
|
||||||
|
|
||||||
// Initial view => refresh & first page (latest logs)
|
// Initial view => refresh & first page (latest logs)
|
||||||
data.Events = readLatest()
|
data.Events = readLatest()
|
||||||
data.SetLiveView = true
|
data.SetLiveView = true
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
// Case: Custom view
|
||||||
// Custom view
|
|
||||||
currentLevel := request.FormValue(`Level`)
|
currentLevel := request.FormValue(`Level`)
|
||||||
currentTimeRange := request.FormValue(`timeRange`)
|
currentTimeRange := request.FormValue(`timeRange`)
|
||||||
currentCategory := request.FormValue(`Category`)
|
currentCategory := request.FormValue(`Category`)
|
||||||
@ -45,6 +47,7 @@ func HandlerWebLog(response http.ResponseWriter, request *http.Request) {
|
|||||||
currentPage := request.FormValue(`CurrentPage`)
|
currentPage := request.FormValue(`CurrentPage`)
|
||||||
currentLiveView := request.FormValue(`LiveView`)
|
currentLiveView := request.FormValue(`LiveView`)
|
||||||
|
|
||||||
|
// Store the events for the template:
|
||||||
data.Events = readCustom(currentTimeRange, currentLevel, currentCategory, currentImpact, currentSeverity, currentMessageName, currentSender, currentPage)
|
data.Events = readCustom(currentTimeRange, currentLevel, currentCategory, currentImpact, currentSeverity, currentMessageName, currentSender, currentPage)
|
||||||
|
|
||||||
if strings.ToLower(currentLiveView) == `true` {
|
if strings.ToLower(currentLiveView) == `true` {
|
||||||
@ -94,6 +97,7 @@ func HandlerWebLog(response http.ResponseWriter, request *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write the MIME type and execute the template:
|
||||||
MimeTypes.Write2HTTP(response, MimeTypes.TypeWebHTML)
|
MimeTypes.Write2HTTP(response, MimeTypes.TypeWebHTML)
|
||||||
if executeError := templates.ExecuteTemplate(response, `WebLog`, data); executeError != nil {
|
if executeError := templates.ExecuteTemplate(response, `WebLog`, data); executeError != nil {
|
||||||
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameEXECUTE, `Was not able to execute the web log viewer template.`, executeError.Error())
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactCritical, LM.MessageNameEXECUTE, `Was not able to execute the web log viewer template.`, executeError.Error())
|
||||||
|
@ -7,11 +7,13 @@ import (
|
|||||||
"html/template"
|
"html/template"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// The init function for this package.
|
||||||
func init() {
|
func init() {
|
||||||
|
|
||||||
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Init the web log.`)
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Init the web log.`)
|
||||||
defer Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Init the web log done.`)
|
defer Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameINIT, `Init the web log done.`)
|
||||||
|
|
||||||
|
// Create the cache of all web logging templates:
|
||||||
templates = template.New(`root`)
|
templates = template.New(`root`)
|
||||||
if _, err := templates.Parse(WebTemp.Viewer); err != nil {
|
if _, err := templates.Parse(WebTemp.Viewer); err != nil {
|
||||||
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNamePARSE, `Was not able to parse the template for the web log viewer.`, err.Error())
|
Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.SeverityCritical, LM.ImpactUnknown, LM.MessageNamePARSE, `Was not able to parse the template for the web log viewer.`, err.Error())
|
||||||
|
@ -7,18 +7,24 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Read a custom event range from the database.
|
||||||
func readCustom(timeRange, logLevel, logCategory, logImpact, logSeverity, logMessageName, logSender, logPage string) (events []Scheme.LogEvent) {
|
func readCustom(timeRange, logLevel, logCategory, logImpact, logSeverity, logMessageName, logSender, logPage string) (events []Scheme.LogEvent) {
|
||||||
|
|
||||||
|
// Get the custom events:
|
||||||
eventsFromDB := DeviceDatabase.ReadCustom(timeRange, logLevel, logCategory, logImpact, logSeverity, logMessageName, logSender, logPage)
|
eventsFromDB := DeviceDatabase.ReadCustom(timeRange, logLevel, logCategory, logImpact, logSeverity, logMessageName, logSender, logPage)
|
||||||
count := len(eventsFromDB)
|
count := len(eventsFromDB)
|
||||||
|
|
||||||
|
// Array with all events, prepared for the website:
|
||||||
events = make([]Scheme.LogEvent, count)
|
events = make([]Scheme.LogEvent, count)
|
||||||
|
|
||||||
|
// Copy each event to the right format:
|
||||||
for n := 0; n < count; n++ {
|
for n := 0; n < count; n++ {
|
||||||
eventFromDB := eventsFromDB[n]
|
eventFromDB := eventsFromDB[n]
|
||||||
events[n] = Scheme.LogEvent{}
|
events[n] = Scheme.LogEvent{}
|
||||||
events[n].LogLine = eventFromDB.Format()
|
events[n].LogLine = eventFromDB.Format()
|
||||||
events[n].LogLevel = fmt.Sprintf("log%s", strings.ToLower(eventFromDB.Level[2:]))
|
events[n].LogLevel = fmt.Sprintf("log%s", strings.ToLower(eventFromDB.Level[2:]))
|
||||||
|
|
||||||
|
// Vary the color of each line:
|
||||||
if n%2 == 0 {
|
if n%2 == 0 {
|
||||||
events[n].AB = Scheme.B
|
events[n].AB = Scheme.B
|
||||||
} else {
|
} else {
|
||||||
|
@ -7,18 +7,23 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Read the latest log events from the database
|
||||||
func readLatest() (events []Scheme.LogEvent) {
|
func readLatest() (events []Scheme.LogEvent) {
|
||||||
|
// Get the latest events from the database
|
||||||
eventsFromDB := DeviceDatabase.ReadLatest()
|
eventsFromDB := DeviceDatabase.ReadLatest()
|
||||||
count := len(eventsFromDB)
|
count := len(eventsFromDB)
|
||||||
|
|
||||||
|
// Array for the log events, prepared for the website:
|
||||||
events = make([]Scheme.LogEvent, count)
|
events = make([]Scheme.LogEvent, count)
|
||||||
|
|
||||||
|
// Copy each event to the right format for the website:
|
||||||
for n := 0; n < count; n++ {
|
for n := 0; n < count; n++ {
|
||||||
eventFromDB := eventsFromDB[n]
|
eventFromDB := eventsFromDB[n]
|
||||||
events[n] = Scheme.LogEvent{}
|
events[n] = Scheme.LogEvent{}
|
||||||
events[n].LogLine = eventFromDB.Format()
|
events[n].LogLine = eventFromDB.Format()
|
||||||
events[n].LogLevel = fmt.Sprintf("log%s", strings.ToLower(eventFromDB.Level[2:]))
|
events[n].LogLevel = fmt.Sprintf("log%s", strings.ToLower(eventFromDB.Level[2:]))
|
||||||
|
|
||||||
|
// Vary the color of each line:
|
||||||
if n%2 == 0 {
|
if n%2 == 0 {
|
||||||
events[n].AB = Scheme.B
|
events[n].AB = Scheme.B
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package Scheme
|
package Scheme
|
||||||
|
|
||||||
const (
|
const (
|
||||||
A string = `loga`
|
A string = `loga` // Web log line, kind A (different color for each line)
|
||||||
B string = `logb`
|
B string = `logb` // Web log line, kind B (different color for each line)
|
||||||
)
|
)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package Scheme
|
package Scheme
|
||||||
|
|
||||||
|
// The type for the web logger viewer template
|
||||||
type Viewer struct {
|
type Viewer struct {
|
||||||
Title string
|
Title string
|
||||||
SetLiveView bool
|
SetLiveView bool
|
||||||
@ -16,12 +17,7 @@ type Viewer struct {
|
|||||||
Events []LogEvent
|
Events []LogEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
// <li class="logline loga logwarn">
|
// Type for a log event
|
||||||
// <div>....</div>
|
|
||||||
// </li>
|
|
||||||
// <li class="logline logb logwarn">
|
|
||||||
// <div>....</div>
|
|
||||||
// </li>
|
|
||||||
type LogEvent struct {
|
type LogEvent struct {
|
||||||
LogLine string
|
LogLine string
|
||||||
LogLevel string // logwarn || logdebug || logerror || loginfo || logtalkative || logsecurity
|
LogLevel string // logwarn || logdebug || logerror || loginfo || logtalkative || logsecurity
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package Templates
|
package Templates
|
||||||
|
|
||||||
|
// The template for the web log viewer:
|
||||||
var Viewer string = `
|
var Viewer string = `
|
||||||
{{define "WebLog"}}
|
{{define "WebLog"}}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
|
@ -6,6 +6,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
templates *template.Template = nil
|
templates *template.Template = nil // The web logging templates
|
||||||
senderName LM.Sender = `System::WebLog`
|
senderName LM.Sender = `System::WebLog` // This is the name for logging event from this package
|
||||||
)
|
)
|
||||||
|
5
Main.go
5
Main.go
@ -6,6 +6,11 @@ import (
|
|||||||
"github.com/SommerEngineering/Ocean/System"
|
"github.com/SommerEngineering/Ocean/System"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is the entry point of Ocean in case of using it as e.g. messaging broker
|
||||||
|
or logging service, etc. This function does not matter if Ocean is used as
|
||||||
|
framework.
|
||||||
|
*/
|
||||||
func main() {
|
func main() {
|
||||||
Log.LogShort(senderName, LM.CategoryAPP, LM.LevelINFO, LM.MessageNameSTARTUP, `Ocean is starting.`)
|
Log.LogShort(senderName, LM.CategoryAPP, LM.LevelINFO, LM.MessageNameSTARTUP, `Ocean is starting.`)
|
||||||
System.InitHandlers()
|
System.InitHandlers()
|
||||||
|
@ -4,11 +4,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MimeType struct {
|
// A function to detect a MIME type.
|
||||||
MimeType string
|
|
||||||
FileExtension []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func DetectType(filename string) (mime MimeType, err error) {
|
func DetectType(filename string) (mime MimeType, err error) {
|
||||||
for _, typeElement := range allTypes {
|
for _, typeElement := range allTypes {
|
||||||
for _, extension := range typeElement.FileExtension {
|
for _, extension := range typeElement.FileExtension {
|
@ -1,8 +1,9 @@
|
|||||||
package MimeTypes
|
package MimeTypes
|
||||||
|
|
||||||
var allTypes [32]MimeType
|
// The init function for this package.
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
||||||
|
// Store the instances for all known types:
|
||||||
allTypes[0] = TypeWebHTML
|
allTypes[0] = TypeWebHTML
|
||||||
allTypes[1] = TypeWebCSS
|
allTypes[1] = TypeWebCSS
|
||||||
allTypes[2] = TypeWebJavaScript
|
allTypes[2] = TypeWebJavaScript
|
||||||
@ -35,4 +36,6 @@ func init() {
|
|||||||
allTypes[29] = TypeFontTTF
|
allTypes[29] = TypeFontTTF
|
||||||
allTypes[30] = TypeFontWOFF
|
allTypes[30] = TypeFontWOFF
|
||||||
allTypes[31] = TypeWebJSON
|
allTypes[31] = TypeWebJSON
|
||||||
|
allTypes[32] = TypeCSV
|
||||||
|
allTypes[33] = TypeWebDart
|
||||||
}
|
}
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
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 TypeWebDart = MimeType{MimeType: "application/dart", FileExtension: []string{".dart"}}
|
|
||||||
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"}}
|
|
||||||
var TypeCSV = MimeType{MimeType: "text/csv", FileExtension: []string{".csv"}}
|
|
7
MimeTypes/Scheme.go
Normal file
7
MimeTypes/Scheme.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package MimeTypes
|
||||||
|
|
||||||
|
// The type for a MIME type.
|
||||||
|
type MimeType struct {
|
||||||
|
MimeType string
|
||||||
|
FileExtension []string
|
||||||
|
}
|
41
MimeTypes/Variables.go
Normal file
41
MimeTypes/Variables.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package MimeTypes
|
||||||
|
|
||||||
|
var (
|
||||||
|
allTypes [34]MimeType // Array for all known types
|
||||||
|
|
||||||
|
// Create instances for each known types:
|
||||||
|
TypeWebHTML = MimeType{MimeType: "text/html", FileExtension: []string{".html", ".htm"}}
|
||||||
|
TypeWebCSS = MimeType{MimeType: "text/css", FileExtension: []string{".css"}}
|
||||||
|
TypeWebJavaScript = MimeType{MimeType: "text/javascript", FileExtension: []string{".js"}}
|
||||||
|
TypeWebDart = MimeType{MimeType: "application/dart", FileExtension: []string{".dart"}}
|
||||||
|
TypeXML = MimeType{MimeType: "text/xml", FileExtension: []string{".xml"}}
|
||||||
|
TypeArchiveZIP = MimeType{MimeType: "application/zip", FileExtension: []string{".zip"}}
|
||||||
|
TypeArchiveGZ = MimeType{MimeType: "application/gzip", FileExtension: []string{".gz"}}
|
||||||
|
TypeWebOCTET = MimeType{MimeType: "application/octet-stream", FileExtension: []string{".bin", ".exe", ".dll", ".class"}}
|
||||||
|
TypeDocumentPDF = MimeType{MimeType: "application/pdf", FileExtension: []string{".pdf"}}
|
||||||
|
TypeDocumentLaTeX = MimeType{MimeType: "application/x-latex", FileExtension: []string{".tex", ".latex"}}
|
||||||
|
TypeShockwave = MimeType{MimeType: "application/x-shockwave-flash", FileExtension: []string{".swf"}}
|
||||||
|
TypeArchiveTAR = MimeType{MimeType: "application/x-tar", FileExtension: []string{".tar"}}
|
||||||
|
TypeAudioWAV = MimeType{MimeType: "application/x-wav", FileExtension: []string{".wav"}}
|
||||||
|
TypeAudioMP3 = MimeType{MimeType: "audio/mpeg", FileExtension: []string{".mp3"}}
|
||||||
|
TypeAudioAAC = MimeType{MimeType: "audio/aac", FileExtension: []string{".aac", ".m4a"}}
|
||||||
|
TypeAudioOGG = MimeType{MimeType: "audio/ogg", FileExtension: []string{"vogg", ".oga"}}
|
||||||
|
TypeAudioWMA = MimeType{MimeType: "audio/x-ms-wma", FileExtension: []string{".wma"}}
|
||||||
|
TypeImageGIF = MimeType{MimeType: "image/gif", FileExtension: []string{".gif"}}
|
||||||
|
TypeImageCommon = MimeType{MimeType: "image", FileExtension: []string{}}
|
||||||
|
TypeUnknown = MimeType{MimeType: "application/octet-stream", FileExtension: []string{}}
|
||||||
|
TypeImageJPEG = MimeType{MimeType: "image/jpeg", FileExtension: []string{".jpg", ".jpeg"}}
|
||||||
|
TypeImagePNG = MimeType{MimeType: "image/png", FileExtension: []string{".png"}}
|
||||||
|
TypePlainText = MimeType{MimeType: "text/plain", FileExtension: []string{".txt"}}
|
||||||
|
TypeVideoMPEG = MimeType{MimeType: "video/mpeg", FileExtension: []string{".mpeg", ".mpg"}}
|
||||||
|
TypeVideoMOV = MimeType{MimeType: "video/quicktime", FileExtension: []string{".mov", ".qt"}}
|
||||||
|
TypeVideoAVI = MimeType{MimeType: "video/x-msvideo", FileExtension: []string{".avi"}}
|
||||||
|
TypeVideoMP4 = MimeType{MimeType: "video/mp4", FileExtension: []string{".mp4"}}
|
||||||
|
TypeFontEOT = MimeType{MimeType: "application/vnd.ms-fontobject", FileExtension: []string{".eot"}}
|
||||||
|
TypeFontOTF = MimeType{MimeType: "application/font-sfnt", FileExtension: []string{".otf"}}
|
||||||
|
TypeImageSVG = MimeType{MimeType: "image/svg+xml", FileExtension: []string{".svg"}}
|
||||||
|
TypeFontTTF = MimeType{MimeType: "application/font-sfnt", FileExtension: []string{".ttf"}}
|
||||||
|
TypeFontWOFF = MimeType{MimeType: "application/font-woff", FileExtension: []string{".woff"}}
|
||||||
|
TypeWebJSON = MimeType{MimeType: "application/json", FileExtension: []string{".json"}}
|
||||||
|
TypeCSV = MimeType{MimeType: "text/csv", FileExtension: []string{".csv"}}
|
||||||
|
)
|
@ -4,6 +4,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Function to write a MIME type to a client.
|
||||||
func Write2HTTP(response http.ResponseWriter, mime MimeType) {
|
func Write2HTTP(response http.ResponseWriter, mime MimeType) {
|
||||||
response.Header().Add(`Content-Type`, mime.MimeType)
|
response.Header().Add(`Content-Type`, mime.MimeType)
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,11 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Init this package.
|
||||||
func init() {
|
func init() {
|
||||||
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `Init the number generator.`)
|
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `Init the number generator.`)
|
||||||
|
|
||||||
|
// Get the exklusive access to the channel list:
|
||||||
channelListLock.Lock()
|
channelListLock.Lock()
|
||||||
defer channelListLock.Unlock()
|
defer channelListLock.Unlock()
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user