Add documentation

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

View File

@ -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

View File

@ -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)

View File

@ -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
) )

View File

@ -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

View File

@ -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)

View File

@ -6,8 +6,8 @@ import (
) )
var ( 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
) )

View File

@ -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

View File

@ -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.`)
} }

View File

@ -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!`)

View File

@ -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"`

View File

@ -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()

View File

@ -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
) )

View File

@ -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
} }

View File

@ -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
} }

View File

@ -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`)

View File

@ -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.`)
} }

View File

@ -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
) )

View File

@ -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)
} }

View File

@ -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

View File

@ -1,5 +1,5 @@
package Handlers package Handlers
// The init function for this package.
func init() { func init() {
} }

View File

@ -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
) )

View File

@ -1,72 +1,40 @@
package ICCC package ICCC
import ( import (
"fmt" "fmt"
"github.com/SommerEngineering/Ocean/ICCC/Scheme" "github.com/SommerEngineering/Ocean/ICCC/Scheme"
"github.com/SommerEngineering/Ocean/Log" "github.com/SommerEngineering/Ocean/Log"
LM "github.com/SommerEngineering/Ocean/Log/Meta" LM "github.com/SommerEngineering/Ocean/Log/Meta"
"github.com/SommerEngineering/Ocean/Shutdown" "github.com/SommerEngineering/Ocean/Shutdown"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
"time" "time"
) )
func InitCacheNow() { func cacheTimerLogic(waiting bool) {
startCacheTimerLock.Lock() if Shutdown.IsDown() {
defer startCacheTimerLock.Unlock() return
}
if cacheTimerRunning {
return lastCount := cacheListenerDatabase.Len()
} selection := bson.D{{`IsActive`, true}}
entriesIterator := collectionListener.Find(selection).Iter()
cacheTimerLogic(false) entry := Scheme.Listener{}
}
cacheListenerDatabaseLock.Lock()
func StartCacheTimer() { cacheListenerDatabase.Init()
initCacheTimer() for entriesIterator.Next(&entry) {
} cacheListenerDatabase.PushBack(entry)
}
func initCacheTimer() {
startCacheTimerLock.Lock() cacheListenerDatabaseLock.Unlock()
defer startCacheTimerLock.Unlock() Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameEXECUTE, `The listener cache was refreshed with the values from the database.`, fmt.Sprintf(`last count=%d`, lastCount), fmt.Sprintf(`new count=%d`, cacheListenerDatabase.Len()))
if cacheTimerRunning { if waiting {
return nextDuration := time.Duration(5) * time.Minute
} else { if cacheListenerDatabase.Len() == 0 {
cacheTimerRunning = true nextDuration = time.Duration(10) * time.Second
} }
go func() { time.Sleep(nextDuration)
for { }
cacheTimerLogic(true) }
}
}()
}
func cacheTimerLogic(waiting bool) {
if Shutdown.IsDown() {
return
}
lastCount := cacheListenerDatabase.Len()
selection := bson.D{{`IsActive`, true}}
entriesIterator := collectionListener.Find(selection).Iter()
entry := Scheme.Listener{}
cacheListenerDatabaseLock.Lock()
cacheListenerDatabase.Init()
for entriesIterator.Next(&entry) {
cacheListenerDatabase.PushBack(entry)
}
cacheListenerDatabaseLock.Unlock()
Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameEXECUTE, `The listener cache was refreshed with the values from the database.`, fmt.Sprintf(`last count=%d`, lastCount), fmt.Sprintf(`new count=%d`, cacheListenerDatabase.Len()))
if waiting {
nextDuration := time.Duration(5) * time.Minute
if cacheListenerDatabase.Len() == 0 {
nextDuration = time.Duration(10) * time.Second
}
time.Sleep(nextDuration)
}
}

View File

@ -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()

View File

@ -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

View File

@ -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)
} }
} }

View File

@ -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
View 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
View 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)
}
}()
}

View File

@ -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{}

View File

@ -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 {

View File

@ -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(&notActiveEntry) collectionListener.Find(selection).One(&notActiveEntry)
@ -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{}

View File

@ -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)
} }
} }

View File

@ -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)

View File

@ -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"`

View File

@ -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"`

View File

@ -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())
} }

View File

@ -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
View File

@ -0,0 +1,6 @@
package ICCC
// Starts the cache timer thread.
func StartCacheTimer() {
initCacheTimer()
}

View File

@ -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

View File

@ -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
) )

View File

@ -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)
} }

View File

@ -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)
} }
} }

View File

@ -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()

View File

@ -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{})
} }

View File

@ -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())

View File

@ -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{})
} }

View File

@ -0,0 +1,18 @@
package DeviceDatabase
// Function to check if the cache is full. If so, write all events to the database.
func cacheFull() {
mutexCacheFull.Lock()
defer mutexCacheFull.Unlock()
// Is the cache full?
if len(cache) < cacheSizeNumberOfEvents {
// Case: Cache is not full.
return
}
// Case: The cache is full. Write all events to the database.
for counter := 0; counter < cacheSizeNumberOfEvents; counter++ {
write2Database(<-cache)
}
}

View 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
}
}
}()
}

View 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
}
}
}()
}

View File

@ -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()
} }

View File

@ -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
} }

View File

@ -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()
} }

View File

@ -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)

View File

@ -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++

View File

@ -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++

View File

@ -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
}

View 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
}

View File

@ -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
}

View 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
}

View File

@ -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)
} }

View File

@ -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 {
}

View File

@ -1,41 +1,32 @@
package DeviceDatabase package DeviceDatabase
import ( import (
"github.com/SommerEngineering/Ocean/Shutdown" "github.com/SommerEngineering/Ocean/Shutdown"
"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() // Starts a new thread:
go func() {
if len(cache) < cacheSizeNumberOfEvents { for {
return
} // Case: The system goes down now.
if Shutdown.IsDown() {
for counter := 0; counter < cacheSizeNumberOfEvents; counter++ { return
write2Database(<-cache) }
}
} // Wait for the right time:
time.Sleep(time.Duration(cacheSizeTime2FlushSeconds) * time.Second)
// Case: Time out
func initTimeout() { // Write the events to the database:
mutexCacheFull.Lock()
go func() { amount := len(cache)
for { for counter := 0; counter < amount; counter++ {
write2Database(<-cache)
if Shutdown.IsDown() { }
return mutexCacheFull.Unlock()
} }
}()
time.Sleep(time.Duration(cacheSizeTime2FlushSeconds) * time.Second) }
mutexCacheFull.Lock()
amount := len(cache)
for counter := 0; counter < amount; counter++ {
write2Database(<-cache)
}
mutexCacheFull.Unlock()
}
}()
}

View File

@ -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{}

View File

@ -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
} }
} }

View File

@ -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())
} }
} }

View File

@ -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()
} }

View File

@ -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)

View File

@ -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)

View File

@ -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)
} }

View File

@ -1,18 +1,19 @@
package Log package Log
import ( import (
"container/list" "container/list"
"github.com/SommerEngineering/Ocean/Log/Meta" "github.com/SommerEngineering/Ocean/Log/Meta"
) )
func logEntryListToArray(data *list.List) (result []Meta.Entry) { // Converts a list with logging events to an array.
count := data.Len() func logEntryListToArray(data *list.List) (result []Meta.Entry) {
result = make([]Meta.Entry, count, count) count := data.Len()
position := 0 result = make([]Meta.Entry, count, count)
for entry := data.Front(); entry != nil; entry = entry.Next() { position := 0
result[position] = entry.Value.(Meta.Entry) for entry := data.Front(); entry != nil; entry = entry.Next() {
position++ result[position] = entry.Value.(Meta.Entry)
} position++
}
return
} return
}

View File

@ -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

View File

@ -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`:

View File

@ -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

View File

@ -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())

View File

@ -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`:

View File

@ -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`:

View File

@ -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
) )

View File

@ -1,3 +1,4 @@
package Meta package Meta
// Type for the sender name
type Sender string type Sender string

View File

@ -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
View File

@ -0,0 +1,38 @@
package Log
import (
"io/ioutil"
"os"
"path/filepath"
"strings"
)
// Read the project name out of the local configuration file "project.name".
func readProjectName() {
// Try to get access to the working directory:
if currentDir, dirError := os.Getwd(); dirError != nil {
// Case: Error! Stop the server.
panic(`Cannot read the current working directory and therefore cannot read the project name!`)
} else {
// Try to get access to the file:
filename := filepath.Join(currentDir, `project.name`)
if _, errFile := os.Stat(filename); errFile != nil {
// Cases: Error.
if os.IsNotExist(errFile) {
panic(`Cannot read the project name file 'project.name': File not found!`)
} else {
panic(`Cannot read the project name file 'project.name': ` + errFile.Error())
}
}
// Try to read the file:
if projectNameBytes, errRead := ioutil.ReadFile(filename); errRead != nil {
// Case: Error.
panic(`Cannot read the project name file 'project.name': ` + errRead.Error())
} else {
// Store the project name:
projectName = string(projectNameBytes)
projectName = strings.TrimSpace(projectName)
}
}
}

View File

@ -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
} }

View File

@ -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()
} }
}() }()
} }

View File

@ -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
) )

View File

@ -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() {

View File

@ -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())

View File

@ -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())

View File

@ -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 {

View File

@ -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 {

View File

@ -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)
) )

View File

@ -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

View File

@ -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>

View File

@ -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
) )

View File

@ -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()

View File

@ -1,24 +1,20 @@
package MimeTypes package MimeTypes
import ( import (
"strings" "strings"
) )
type MimeType struct { // A function to detect a MIME type.
MimeType string func DetectType(filename string) (mime MimeType, err error) {
FileExtension []string for _, typeElement := range allTypes {
} for _, extension := range typeElement.FileExtension {
if strings.HasSuffix(filename, extension) {
func DetectType(filename string) (mime MimeType, err error) { mime = typeElement
for _, typeElement := range allTypes { return
for _, extension := range typeElement.FileExtension { }
if strings.HasSuffix(filename, extension) { }
mime = typeElement }
return
} mime = TypeUnknown
} return
} }
mime = TypeUnknown
return
}

View File

@ -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
} }

View File

@ -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
View 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
View 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"}}
)

View File

@ -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)
} }

View File

@ -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