mirror of
https://github.com/planetmint/planetmint-go.git
synced 2025-11-27 15:53:41 +00:00
* avoid using app.Logger to have a consistent log from the beginning
* made lazyLoading local to the module * fixed ticker bug so that the tasks are executed in an endless loop * fixed timezone bug of Add Participant. only validator local time is used not the timestamp from the mqtt messages as these might come from a different time zone (resulted in delayed deletion from the activeActors DB) * improved connection management for the monitor * added a watchdog task to restart the monitor in case of connection loss (every 2 min) Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
This commit is contained in:
parent
c3bbf76dde
commit
f68d0df98b
@ -3,6 +3,7 @@ package monitor
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
"log"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/syndtr/goleveldb/leveldb/iterator"
|
"github.com/syndtr/goleveldb/leveldb/iterator"
|
||||||
@ -21,7 +22,7 @@ func (mms *MqttMonitor) AddParticipant(address string, lastSeenTS int64) (err er
|
|||||||
|
|
||||||
lastSeenBytes, err := json.Marshal(lastSeen)
|
lastSeenBytes, err := json.Marshal(lastSeen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mms.Log("[Monitor] Error serializing ConversionRequest: " + err.Error())
|
log.Println("[app] [Monitor] Error serializing ConversionRequest: " + err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
increaseCounter := false
|
increaseCounter := false
|
||||||
@ -37,9 +38,11 @@ func (mms *MqttMonitor) AddParticipant(address string, lastSeenTS int64) (err er
|
|||||||
err = mms.db.Put([]byte(address), lastSeenBytes, nil)
|
err = mms.db.Put([]byte(address), lastSeenBytes, nil)
|
||||||
mms.dbMutex.Unlock()
|
mms.dbMutex.Unlock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("[Monitor] storing addresses in DB: " + err.Error())
|
log.Println("[app] [Monitor] error storing addresses in DB: " + err.Error())
|
||||||
return
|
} else {
|
||||||
|
log.Println("[app] [Monitor] stored address in DB: " + address)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,8 +64,11 @@ func (mms *MqttMonitor) getAmountOfElements() (amount int64, err error) {
|
|||||||
|
|
||||||
// Check for any errors encountered during iteration
|
// Check for any errors encountered during iteration
|
||||||
if err := iter.Error(); err != nil {
|
if err := iter.Error(); err != nil {
|
||||||
log.Println("[Monitor] " + err.Error())
|
log.Println("[app] [Monitor] " + err.Error())
|
||||||
|
} else {
|
||||||
|
log.Println("[app] [Monitor] elements: " + strconv.FormatInt(amount, 10))
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
func (mms *MqttMonitor) getDataFromIter(iter iterator.Iterator) (lastSeen LastSeenEvent, err error) {
|
func (mms *MqttMonitor) getDataFromIter(iter iterator.Iterator) (lastSeen LastSeenEvent, err error) {
|
||||||
@ -70,13 +76,14 @@ func (mms *MqttMonitor) getDataFromIter(iter iterator.Iterator) (lastSeen LastSe
|
|||||||
value := iter.Value()
|
value := iter.Value()
|
||||||
err = json.Unmarshal(value, &lastSeen)
|
err = json.Unmarshal(value, &lastSeen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mms.Log("[Monitor] Failed to unmarshal entry: " + string(key) + " - " + err.Error())
|
log.Println("[app] [Monitor] Failed to unmarshal entry: " + string(key) + " - " + err.Error())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mms *MqttMonitor) CleanupDB() {
|
func (mms *MqttMonitor) CleanupDB() {
|
||||||
// Create an iterator for the database
|
// Create an iterator for the database
|
||||||
|
log.Println("[app] [Monitor] Starting clean-up process")
|
||||||
iter := mms.db.NewIterator(nil, nil)
|
iter := mms.db.NewIterator(nil, nil)
|
||||||
defer iter.Release() // Make sure to release the iterator at the end
|
defer iter.Release() // Make sure to release the iterator at the end
|
||||||
|
|
||||||
@ -85,21 +92,25 @@ func (mms *MqttMonitor) CleanupDB() {
|
|||||||
// Use iter.Key() and iter.Value() to access the key and value
|
// Use iter.Key() and iter.Value() to access the key and value
|
||||||
lastSeen, err := mms.getDataFromIter(iter)
|
lastSeen, err := mms.getDataFromIter(iter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mms.Log("[Monitor] Failed to unmarshal entry: " + string(iter.Key()) + " - " + err.Error())
|
log.Println("[app] [Monitor] Failed to unmarshal entry: " + string(iter.Key()) + " - " + err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
timeThreshold := time.Now().Add(-1 * mms.CleanupPeriodicityInMinutes * time.Minute).Unix()
|
timeThreshold := time.Now().Add(-1 * mms.CleanupPeriodicityInMinutes * time.Minute).Unix()
|
||||||
|
log.Println("[app] [Monitor] threshold " + strconv.FormatInt(timeThreshold, 10))
|
||||||
|
log.Println("[app] [Monitor] timestamp " + strconv.FormatInt(lastSeen.Timestamp, 10))
|
||||||
if lastSeen.Timestamp <= timeThreshold {
|
if lastSeen.Timestamp <= timeThreshold {
|
||||||
// If the entry is older than 12 hours, delete it
|
// If the entry is older than 12 hours, delete it
|
||||||
err := mms.deleteEntry(iter.Key())
|
err := mms.deleteEntry(iter.Key())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mms.Log("[Monitor] Failed to delete entry: " + err.Error())
|
log.Println("[app] [Monitor] Failed to delete entry: " + err.Error())
|
||||||
|
} else {
|
||||||
|
log.Println("[app] [Monitor] Delete entry: " + string(iter.Key()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for any errors encountered during iteration
|
// Check for any errors encountered during iteration
|
||||||
if err := iter.Error(); err != nil {
|
if err := iter.Error(); err != nil {
|
||||||
mms.Log(err.Error())
|
log.Println("[app] [Monitor] error during cleanup : " + err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package monitor
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -17,6 +18,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var MonitorMQTTClient util.MQTTClientI
|
var MonitorMQTTClient util.MQTTClientI
|
||||||
|
var clientMutex sync.Mutex
|
||||||
|
var localMqttClient util.MQTTClientI = nil
|
||||||
|
|
||||||
type MqttMonitor struct {
|
type MqttMonitor struct {
|
||||||
db *leveldb.DB
|
db *leveldb.DB
|
||||||
@ -29,6 +32,7 @@ type MqttMonitor struct {
|
|||||||
contextMutex sync.Mutex
|
contextMutex sync.Mutex
|
||||||
isTerminated bool
|
isTerminated bool
|
||||||
terminationMutex sync.Mutex
|
terminationMutex sync.Mutex
|
||||||
|
maxRetries time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mms *MqttMonitor) Terminate() {
|
func (mms *MqttMonitor) Terminate() {
|
||||||
@ -44,9 +48,9 @@ func (mms *MqttMonitor) IsTerminated() (isTerminated bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func LazyLoadMonitorMQTTClient() {
|
func (mms *MqttMonitor) lazyLoadMonitorMQTTClient() util.MQTTClientI {
|
||||||
if MonitorMQTTClient != nil {
|
if MonitorMQTTClient != nil {
|
||||||
return
|
return MonitorMQTTClient
|
||||||
}
|
}
|
||||||
|
|
||||||
conf := config.GetConfig()
|
conf := config.GetConfig()
|
||||||
@ -56,7 +60,7 @@ func LazyLoadMonitorMQTTClient() {
|
|||||||
uri = "ssl://" + hostPort
|
uri = "ssl://" + hostPort
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := mqtt.NewClientOptions().AddBroker(uri).SetKeepAlive(60)
|
opts := mqtt.NewClientOptions().AddBroker(uri).SetKeepAlive(60).SetCleanSession(true)
|
||||||
opts.SetClientID(conf.ValidatorAddress + "-monitor")
|
opts.SetClientID(conf.ValidatorAddress + "-monitor")
|
||||||
opts.SetUsername(conf.MqttUser)
|
opts.SetUsername(conf.MqttUser)
|
||||||
opts.SetPassword(conf.MqttPassword)
|
opts.SetPassword(conf.MqttPassword)
|
||||||
@ -65,7 +69,9 @@ func LazyLoadMonitorMQTTClient() {
|
|||||||
opts.SetTLSConfig(tlsConfig)
|
opts.SetTLSConfig(tlsConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
MonitorMQTTClient = mqtt.NewClient(opts)
|
log.Println("[app] [Monitor] create new client")
|
||||||
|
client := mqtt.NewClient(opts)
|
||||||
|
return client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMqttMonitorService(db *leveldb.DB, config config.Config) *MqttMonitor {
|
func NewMqttMonitorService(db *leveldb.DB, config config.Config) *MqttMonitor {
|
||||||
@ -73,13 +79,21 @@ func NewMqttMonitorService(db *leveldb.DB, config config.Config) *MqttMonitor {
|
|||||||
return service
|
return service
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mms *MqttMonitor) registerPeriodicTasks() {
|
func (mms *MqttMonitor) runPeriodicTasks() {
|
||||||
mms.ticker = time.NewTicker(mms.CleanupPeriodicityInMinutes * time.Minute)
|
tickerRestablishConnection := time.NewTicker(2 * time.Minute)
|
||||||
go func() {
|
tickerCleanup := time.NewTicker(5 * time.Minute)
|
||||||
for range mms.ticker.C { // Loop over the ticker channel
|
defer tickerRestablishConnection.Stop()
|
||||||
|
defer tickerCleanup.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-tickerRestablishConnection.C:
|
||||||
|
go mms.MonitorActiveParticipants()
|
||||||
|
case <-tickerCleanup.C:
|
||||||
go mms.CleanupDB()
|
go mms.CleanupDB()
|
||||||
}
|
}
|
||||||
}()
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mms *MqttMonitor) Start() (err error) {
|
func (mms *MqttMonitor) Start() (err error) {
|
||||||
@ -88,8 +102,9 @@ func (mms *MqttMonitor) Start() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
mms.numberOfElements = amount
|
mms.numberOfElements = amount
|
||||||
mms.registerPeriodicTasks()
|
go mms.runPeriodicTasks()
|
||||||
go mms.MonitorActiveParticipants()
|
go mms.MonitorActiveParticipants()
|
||||||
|
go mms.CleanupDB()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
func (mms *MqttMonitor) getRandomNumbers() (challenger int, challengee int) {
|
func (mms *MqttMonitor) getRandomNumbers() (challenger int, challengee int) {
|
||||||
@ -105,18 +120,19 @@ func (mms *MqttMonitor) SelectPoPParticipantsOutOfActiveActors() (challenger str
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
randomChallenger, randomChallengee := mms.getRandomNumbers()
|
randomChallenger, randomChallengee := mms.getRandomNumbers()
|
||||||
mms.Log("[Monitor] number of elements: " + strconv.Itoa(int(mms.numberOfElements)))
|
log.Println("[app] [Monitor] number of elements: " + strconv.Itoa(int(mms.numberOfElements)))
|
||||||
mms.Log("[Monitor] selected IDs: " + strconv.Itoa(randomChallenger) + " " + strconv.Itoa(randomChallengee))
|
log.Println("[app] [Monitor] selected IDs: " + strconv.Itoa(randomChallenger) + " " + strconv.Itoa(randomChallengee))
|
||||||
iter := mms.db.NewIterator(nil, nil)
|
iter := mms.db.NewIterator(nil, nil)
|
||||||
defer iter.Release()
|
defer iter.Release()
|
||||||
count := 0
|
count := 0
|
||||||
found := 0
|
found := 0
|
||||||
var lastSeen LastSeenEvent
|
var lastSeen LastSeenEvent
|
||||||
for iter.Next() {
|
for iter.Next() {
|
||||||
mms.Log("[Monitor] count: " + strconv.Itoa(count))
|
log.Println("[app] [Monitor] count: " + strconv.Itoa(count))
|
||||||
if count == randomChallenger {
|
if count == randomChallenger {
|
||||||
lastSeen, err = mms.getDataFromIter(iter)
|
lastSeen, err = mms.getDataFromIter(iter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Println("[app] [Monitor] could not get Data from ID" + strconv.Itoa(randomChallenger))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
challenger = lastSeen.Address
|
challenger = lastSeen.Address
|
||||||
@ -124,6 +140,7 @@ func (mms *MqttMonitor) SelectPoPParticipantsOutOfActiveActors() (challenger str
|
|||||||
} else if count == randomChallengee {
|
} else if count == randomChallengee {
|
||||||
lastSeen, err = mms.getDataFromIter(iter)
|
lastSeen, err = mms.getDataFromIter(iter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Println("[app] [Monitor] could not get Data from ID" + strconv.Itoa(randomChallengee))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
challengee = lastSeen.Address
|
challengee = lastSeen.Address
|
||||||
@ -135,6 +152,7 @@ func (mms *MqttMonitor) SelectPoPParticipantsOutOfActiveActors() (challenger str
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
log.Println("[app] [Monitor] challenger, challengee: " + challenger + " " + challengee)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,47 +175,66 @@ func (mms *MqttMonitor) MqttMsgHandler(_ mqtt.Client, msg mqtt.Message) {
|
|||||||
if err != nil || !valid {
|
if err != nil || !valid {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
payload, err := util.ToJSON(msg.Payload())
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
timeString, ok := payload["Time"].(string)
|
unixTime := time.Now().Unix()
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
unixTime, err := util.String2UnixTime(timeString)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = mms.AddParticipant(address, unixTime)
|
err = mms.AddParticipant(address, unixTime)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mms.Log("[Monitor] error adding active actor to DB: " + address + " " + err.Error())
|
log.Println("[app] [Monitor] error adding active actor to DB: " + address + " " + err.Error())
|
||||||
} else {
|
} else {
|
||||||
mms.Log("[Monitor] added active actor to DB: " + address)
|
log.Println("[app] [Monitor] added active actor to DB: " + address)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mms *MqttMonitor) MonitorActiveParticipants() {
|
func (mms *MqttMonitor) MonitorActiveParticipants() {
|
||||||
LazyLoadMonitorMQTTClient()
|
clientMutex.Lock()
|
||||||
for !mms.IsTerminated() {
|
if localMqttClient != nil {
|
||||||
if !MonitorMQTTClient.IsConnected() {
|
log.Println("[app] [Monitor] client is still working")
|
||||||
if token := MonitorMQTTClient.Connect(); token.Wait() && token.Error() != nil {
|
return
|
||||||
mms.Log("[Monitor] error connecting to mqtt: " + token.Error().Error())
|
|
||||||
panic(token.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
var messageHandler mqtt.MessageHandler = mms.MqttMsgHandler
|
|
||||||
|
|
||||||
// Subscribe to a topic
|
|
||||||
subscriptionTopic := "tele/#"
|
|
||||||
if token := MonitorMQTTClient.Subscribe(subscriptionTopic, 0, messageHandler); token.Wait() && token.Error() != nil {
|
|
||||||
mms.Log("[Monitor] error registering the mqtt subscription: " + token.Error().Error())
|
|
||||||
panic(token.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
}
|
}
|
||||||
|
localMqttClient = mms.lazyLoadMonitorMQTTClient()
|
||||||
|
client := localMqttClient
|
||||||
|
clientMutex.Unlock()
|
||||||
|
|
||||||
|
// Maximum reconnection attempts (adjust as needed)
|
||||||
|
mms.SetMaxRetries()
|
||||||
|
for !mms.IsTerminated() && mms.maxRetries > 0 {
|
||||||
|
if token := client.Connect(); token.Wait() && token.Error() != nil {
|
||||||
|
log.Println("[app] [Monitor] error connecting to mqtt: " + token.Error().Error())
|
||||||
|
mms.maxRetries--
|
||||||
|
time.Sleep(time.Second * 5)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var messageHandler mqtt.MessageHandler = mms.MqttMsgHandler
|
||||||
|
|
||||||
|
// Subscribe to a topic
|
||||||
|
subscriptionTopic := "tele/#"
|
||||||
|
if token := client.Subscribe(subscriptionTopic, 0, messageHandler); token.Wait() && token.Error() != nil {
|
||||||
|
log.Println("[app] [Monitor] error registering the mqtt subscription: " + token.Error().Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for !mms.IsTerminated() {
|
||||||
|
if !client.IsConnected() {
|
||||||
|
log.Println("[app] [Monitor] retry establishing a connection")
|
||||||
|
break // Exit inner loop on disconnect
|
||||||
|
}
|
||||||
|
time.Sleep(60 * time.Second) // Adjust sleep time based on your needs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if mms.maxRetries == 0 {
|
||||||
|
log.Println("[app] [Monitor] Reached maximum reconnection attempts. Exiting. New client will be activated soon.")
|
||||||
|
}
|
||||||
|
|
||||||
|
clientMutex.Lock()
|
||||||
|
localMqttClient = nil
|
||||||
|
clientMutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mms *MqttMonitor) SetMaxRetries() {
|
||||||
|
mms.maxRetries = 5
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mms *MqttMonitor) Log(msg string) {
|
func (mms *MqttMonitor) Log(msg string) {
|
||||||
|
|||||||
@ -23,7 +23,6 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestGMonitorActiveParticipants(t *testing.T) {
|
func TestGMonitorActiveParticipants(t *testing.T) {
|
||||||
monitor.LazyLoadMonitorMQTTClient()
|
|
||||||
cfg := config.GetConfig()
|
cfg := config.GetConfig()
|
||||||
db, err := leveldb.Open(storage.NewMemStorage(), nil)
|
db, err := leveldb.Open(storage.NewMemStorage(), nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@ -49,8 +48,6 @@ func TestGMonitorActiveParticipants(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCleanupRemoval(t *testing.T) {
|
func TestCleanupRemoval(t *testing.T) {
|
||||||
monitor.LazyLoadMonitorMQTTClient()
|
|
||||||
|
|
||||||
cfg := config.GetConfig()
|
cfg := config.GetConfig()
|
||||||
db, err := leveldb.Open(storage.NewMemStorage(), nil)
|
db, err := leveldb.Open(storage.NewMemStorage(), nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@ -77,8 +74,6 @@ func TestCleanupRemoval(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCleanupPrecisionTest(t *testing.T) {
|
func TestCleanupPrecisionTest(t *testing.T) {
|
||||||
monitor.LazyLoadMonitorMQTTClient()
|
|
||||||
|
|
||||||
cfg := config.GetConfig()
|
cfg := config.GetConfig()
|
||||||
db, err := leveldb.Open(storage.NewMemStorage(), nil)
|
db, err := leveldb.Open(storage.NewMemStorage(), nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -8,7 +10,7 @@ func String2UnixTime(timeInput string) (int64, error) {
|
|||||||
// Layout specifying the format of the input string
|
// Layout specifying the format of the input string
|
||||||
// Note: Go uses a specific reference time (Mon Jan 2 15:04:05 MST 2006) to define format layouts
|
// Note: Go uses a specific reference time (Mon Jan 2 15:04:05 MST 2006) to define format layouts
|
||||||
layout := "2006-01-02T15:04:05"
|
layout := "2006-01-02T15:04:05"
|
||||||
|
log.Println("[app] [Monitor] [time] add time string: " + timeInput)
|
||||||
// Parse the string into a time.Time struct in local time zone
|
// Parse the string into a time.Time struct in local time zone
|
||||||
parsedTime, err := time.Parse(layout, timeInput)
|
parsedTime, err := time.Parse(layout, timeInput)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -18,6 +20,6 @@ func String2UnixTime(timeInput string) (int64, error) {
|
|||||||
// Convert to UTC if not already
|
// Convert to UTC if not already
|
||||||
utcTime := parsedTime.UTC()
|
utcTime := parsedTime.UTC()
|
||||||
unixTime := utcTime.Unix()
|
unixTime := utcTime.Unix()
|
||||||
|
log.Println("[app] [Monitor] [time] unix time: " + strconv.FormatInt(unixTime, 10))
|
||||||
return unixTime, nil
|
return unixTime, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user