Add profiler, edit to database behaviour (Add retries)
parent
2e0ef0b7d8
commit
b03e2d2af1
|
@ -6,3 +6,4 @@ bin/
|
||||||
config.json
|
config.json
|
||||||
chromagies
|
chromagies
|
||||||
log.txt
|
log.txt
|
||||||
|
__debug*
|
4
main.go
4
main.go
|
@ -37,7 +37,7 @@ func main() {
|
||||||
|
|
||||||
var conf config.Server = (config.GetConfig()).Server
|
var conf config.Server = (config.GetConfig()).Server
|
||||||
|
|
||||||
http.ListenAndServe(fmt.Sprintf("%s:%s", conf.IP, conf.Port), router)
|
http.ListenAndServe(fmt.Sprintf("%s:%d", conf.IP, conf.Port), router)
|
||||||
stopServer()
|
stopServer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ func initConfig() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Cannot read/write config file !")
|
log.Fatalln("Cannot read/write config file !")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,32 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"chromagies/src/database"
|
||||||
|
"chromagies/src/profiler"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
profiler_API_PROCESS_REQUEST string = "API Process Request"
|
||||||
|
)
|
||||||
|
|
||||||
var apiRootPath string = "/api/v1"
|
var apiRootPath string = "/api/v1"
|
||||||
|
|
||||||
func Init(router *mux.Router) {
|
func Init(router *mux.Router) {
|
||||||
|
|
||||||
|
profiler.Register(profiler_API_PROCESS_REQUEST, 10000, "µs")
|
||||||
|
|
||||||
router.HandleFunc(apiRootPath, apiRoot).Methods("GET")
|
router.HandleFunc(apiRootPath, apiRoot).Methods("GET")
|
||||||
|
|
||||||
routerAuth := router.PathPrefix(apiRootPath + "/auth").Subrouter()
|
routerAuth := router.PathPrefix(apiRootPath + "/auth").Subrouter()
|
||||||
routerPage := router.PathPrefix(apiRootPath + "/page").Subrouter()
|
routerPage := router.PathPrefix(apiRootPath + "/page").Subrouter()
|
||||||
routerUser := router.PathPrefix(apiRootPath + "/user").Subrouter()
|
routerUser := router.PathPrefix(apiRootPath + "/user").Subrouter()
|
||||||
routerTag := router.PathPrefix(apiRootPath + "/tag").Subrouter()
|
routerTag := router.PathPrefix(apiRootPath + "/tag").Subrouter()
|
||||||
|
routerDebug := router.PathPrefix(apiRootPath + "/debug").Subrouter()
|
||||||
|
|
||||||
routerAuth.HandleFunc("", apiAuth)
|
routerAuth.HandleFunc("", apiAuth)
|
||||||
routerAuth.HandleFunc("/login", apiAuthLogin)
|
routerAuth.HandleFunc("/login", apiAuthLogin)
|
||||||
|
@ -32,67 +42,118 @@ func Init(router *mux.Router) {
|
||||||
|
|
||||||
routerTag.HandleFunc("", apiTag)
|
routerTag.HandleFunc("", apiTag)
|
||||||
routerTag.HandleFunc("/{name}", apiTagName)
|
routerTag.HandleFunc("/{name}", apiTagName)
|
||||||
|
|
||||||
|
routerDebug.HandleFunc("/profiler", apiDebugProfiler)
|
||||||
|
routerDebug.HandleFunc("/profiler/{name}", apiDebugProfilerName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// API ROOT
|
// API ROOT
|
||||||
|
|
||||||
func apiRoot(w http.ResponseWriter, r *http.Request) {
|
func apiRoot(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tStart := time.Now()
|
||||||
fmt.Fprintf(w, "API ROOT")
|
fmt.Fprintf(w, "API ROOT")
|
||||||
|
profiler.Add(profiler_API_PROCESS_REQUEST, time.Duration(time.Since(tStart).Microseconds()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// API Auth
|
// API Auth
|
||||||
|
|
||||||
func apiAuth(w http.ResponseWriter, r *http.Request) {
|
func apiAuth(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tStart := time.Now()
|
||||||
fmt.Fprintf(w, "API Auth")
|
fmt.Fprintf(w, "API Auth")
|
||||||
|
profiler.Add(profiler_API_PROCESS_REQUEST, time.Duration(time.Since(tStart).Microseconds()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiAuthLogin(w http.ResponseWriter, r *http.Request) {
|
func apiAuthLogin(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tStart := time.Now()
|
||||||
fmt.Fprintf(w, "API Auth Login")
|
fmt.Fprintf(w, "API Auth Login")
|
||||||
|
database.Ping()
|
||||||
|
profiler.Add(profiler_API_PROCESS_REQUEST, time.Duration(time.Since(tStart).Microseconds()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiAuthLogout(w http.ResponseWriter, r *http.Request) {
|
func apiAuthLogout(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tStart := time.Now()
|
||||||
fmt.Fprintf(w, "API Auth Logout")
|
fmt.Fprintf(w, "API Auth Logout")
|
||||||
|
profiler.Add(profiler_API_PROCESS_REQUEST, time.Duration(time.Since(tStart).Microseconds()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// API Page
|
// API Page
|
||||||
|
|
||||||
func apiPage(w http.ResponseWriter, r *http.Request) {
|
func apiPage(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tStart := time.Now()
|
||||||
fmt.Fprintf(w, "API Page")
|
fmt.Fprintf(w, "API Page")
|
||||||
|
profiler.Add(profiler_API_PROCESS_REQUEST, time.Duration(time.Since(tStart).Microseconds()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiPageFolder(w http.ResponseWriter, r *http.Request) {
|
func apiPageFolder(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tStart := time.Now()
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
fmt.Fprintf(w, "API Page Folder(%s)", vars["folder"])
|
fmt.Fprintf(w, "API Page Folder(%s)", vars["folder"])
|
||||||
|
profiler.Add(profiler_API_PROCESS_REQUEST, time.Duration(time.Since(tStart).Microseconds()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiPageFolderPage(w http.ResponseWriter, r *http.Request) {
|
func apiPageFolderPage(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tStart := time.Now()
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
fmt.Fprintf(w, "API Page Folder(%s) Page(%s)", vars["folder"], vars["page"])
|
fmt.Fprintf(w, "API Page Folder(%s) Page(%s)", vars["folder"], vars["page"])
|
||||||
|
profiler.Add(profiler_API_PROCESS_REQUEST, time.Duration(time.Since(tStart).Microseconds()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiPageFolderPageContent(w http.ResponseWriter, r *http.Request) {
|
func apiPageFolderPageContent(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tStart := time.Now()
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
fmt.Fprintf(w, "API Page Folder(%s) Page(%s) Content", vars["folder"], vars["page"])
|
fmt.Fprintf(w, "API Page Folder(%s) Page(%s) Content", vars["folder"], vars["page"])
|
||||||
|
profiler.Add(profiler_API_PROCESS_REQUEST, time.Duration(time.Since(tStart).Microseconds()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// API User
|
// API User
|
||||||
|
|
||||||
func apiUser(w http.ResponseWriter, r *http.Request) {
|
func apiUser(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tStart := time.Now()
|
||||||
fmt.Fprintf(w, "API User")
|
fmt.Fprintf(w, "API User")
|
||||||
|
profiler.Add(profiler_API_PROCESS_REQUEST, time.Duration(time.Since(tStart).Microseconds()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiUserName(w http.ResponseWriter, r *http.Request) {
|
func apiUserName(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tStart := time.Now()
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
fmt.Fprintf(w, "API User Name(%s)", vars["name"])
|
fmt.Fprintf(w, "API User Name(%s)", vars["name"])
|
||||||
|
profiler.Add(profiler_API_PROCESS_REQUEST, time.Duration(time.Since(tStart).Microseconds()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// API Tag
|
// API Tag
|
||||||
|
|
||||||
func apiTag(w http.ResponseWriter, r *http.Request) {
|
func apiTag(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tStart := time.Now()
|
||||||
fmt.Fprintf(w, "API Tag")
|
fmt.Fprintf(w, "API Tag")
|
||||||
|
profiler.Add(profiler_API_PROCESS_REQUEST, time.Duration(time.Since(tStart).Microseconds()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiTagName(w http.ResponseWriter, r *http.Request) {
|
func apiTagName(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tStart := time.Now()
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
fmt.Fprintf(w, "API Tag Name(%s)", vars["name"])
|
fmt.Fprintf(w, "API Tag Name(%s)", vars["name"])
|
||||||
|
profiler.Add(profiler_API_PROCESS_REQUEST, time.Duration(time.Since(tStart).Microseconds()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// API Tag
|
||||||
|
|
||||||
|
func apiDebugProfiler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tStart := time.Now()
|
||||||
|
|
||||||
|
var entries []string
|
||||||
|
profiler.GetAll(&entries)
|
||||||
|
|
||||||
|
for i := range entries {
|
||||||
|
var entry string = entries[i]
|
||||||
|
fmt.Fprintf(w, "API Debug Profiler(%s)\n\n%s\n\n", entry, string(profiler.Get(entry).ToString()))
|
||||||
|
}
|
||||||
|
|
||||||
|
profiler.Add(profiler_API_PROCESS_REQUEST, time.Duration(time.Since(tStart).Microseconds()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func apiDebugProfilerName(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tStart := time.Now()
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
fmt.Fprintf(w, "API Debug Profiler(%s)\n%s", vars["name"], string(profiler.Get(vars["name"]).ToString()))
|
||||||
|
profiler.Add(profiler_API_PROCESS_REQUEST, time.Duration(time.Since(tStart).Microseconds()))
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,10 +10,12 @@ const CONFIG_FILE_NAME string = "./config.json"
|
||||||
const CONFIG_FILE_DEFAULT_CONF string = `{
|
const CONFIG_FILE_DEFAULT_CONF string = `{
|
||||||
"Database": {
|
"Database": {
|
||||||
"Host": "localhost",
|
"Host": "localhost",
|
||||||
"Port": "3306",
|
"Port": 3306,
|
||||||
"Database": "Chromagies",
|
"Database": "Chromagies",
|
||||||
"User": "chromagies_user",
|
"User": "chromagies_user",
|
||||||
"Password": "12345678"
|
"Password": "12345678",
|
||||||
|
"RetriesOnError": 5,
|
||||||
|
"TimeBetweenRetriesMs": 100
|
||||||
},
|
},
|
||||||
|
|
||||||
"Logger": {
|
"Logger": {
|
||||||
|
@ -21,16 +23,18 @@ const CONFIG_FILE_DEFAULT_CONF string = `{
|
||||||
},
|
},
|
||||||
"Server": {
|
"Server": {
|
||||||
"IP": "0.0.0.0",
|
"IP": "0.0.0.0",
|
||||||
"Port": "12345"
|
"Port": 12345
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
type Database struct {
|
type Database struct {
|
||||||
Host string
|
Host string
|
||||||
Port string
|
Port uint16
|
||||||
Database string
|
Database string
|
||||||
User string
|
User string
|
||||||
Password string
|
Password string
|
||||||
|
RetriesOnError uint16
|
||||||
|
TimeBetweenRetriesMs uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
type Logger struct {
|
type Logger struct {
|
||||||
|
@ -39,7 +43,7 @@ type Logger struct {
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
IP string
|
IP string
|
||||||
Port string
|
Port uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
|
|
@ -2,38 +2,44 @@ package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"chromagies/src/config"
|
"chromagies/src/config"
|
||||||
|
"chromagies/src/profiler"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
_ "github.com/go-sql-driver/mysql"
|
_ "github.com/go-sql-driver/mysql"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
profiler_DATABASE_CONNECT string = "Database Connect"
|
||||||
|
profiler_DATABASE_QUERY string = "Database Query"
|
||||||
|
profiler_DATABASE_PING string = "Database Ping"
|
||||||
|
)
|
||||||
|
|
||||||
var db *sql.DB
|
var db *sql.DB
|
||||||
var dbConfig config.Database
|
var dbConfig config.Database
|
||||||
var dbPath string
|
var isInit bool
|
||||||
|
|
||||||
func Init() {
|
func Init() {
|
||||||
var err error
|
if isInit {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
dbConfig = config.GetConfig().Database
|
dbConfig = config.GetConfig().Database
|
||||||
dbPath = fmt.Sprintf("%s:%s@(%s:%s)/%s?parseTime=true", dbConfig.User, dbConfig.Password, dbConfig.Host, dbConfig.Port, dbConfig.Database)
|
|
||||||
|
|
||||||
db, err = sql.Open("mysql", dbPath)
|
profiler.Register(profiler_DATABASE_CONNECT, 20, "µs")
|
||||||
if err != nil {
|
profiler.Register(profiler_DATABASE_QUERY, 4000, "µs")
|
||||||
log.Println("Cannot connect to database !")
|
profiler.Register(profiler_DATABASE_PING, 20, "µs")
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = db.Ping()
|
isInit = true
|
||||||
if err != nil {
|
|
||||||
log.Println("Cannot ping the database !")
|
Connect()
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var query string = `SHOW GRANTS;`
|
var query string = `SHOW GRANTS;`
|
||||||
var rows *sql.Rows = executeQuery(query)
|
var rows *sql.Rows = executeQuery(query)
|
||||||
|
if rows != nil {
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var res string
|
var res string
|
||||||
rows.Scan(&res)
|
rows.Scan(&res)
|
||||||
|
@ -41,14 +47,111 @@ func Init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func Terminate() {
|
||||||
|
isInit = false
|
||||||
|
if db != nil {
|
||||||
|
db.Close()
|
||||||
|
db = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Connect() {
|
||||||
|
if !isInit {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if db != nil {
|
||||||
|
db.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var dbPath string = fmt.Sprintf("%s:%s@(%s:%d)/%s?parseTime=true", dbConfig.User, dbConfig.Password, dbConfig.Host, dbConfig.Port, dbConfig.Database)
|
||||||
|
log.Printf("Connecting to database %s at %s:%d as %s\n", dbConfig.Database, dbConfig.Host, dbConfig.Port, dbConfig.User)
|
||||||
|
|
||||||
|
tStart := time.Now()
|
||||||
|
for i := range dbConfig.RetriesOnError {
|
||||||
|
db, err = sql.Open("mysql", dbPath)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
log.Printf("Error while connecting the database [%d/%d] : %s\n", i+1, dbConfig.RetriesOnError, err)
|
||||||
|
time.Sleep((time.Duration)(dbConfig.TimeBetweenRetriesMs) * time.Millisecond)
|
||||||
|
}
|
||||||
|
profiler.Add(profiler_DATABASE_CONNECT, time.Duration(time.Since(tStart).Microseconds()))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Cannot connect to database !")
|
||||||
|
}
|
||||||
|
|
||||||
|
Ping()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Disconnect() {
|
||||||
|
if !isInit {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if db == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Ping() {
|
||||||
|
if !isInit {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if db == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
tStart := time.Now()
|
||||||
|
for i := range dbConfig.RetriesOnError {
|
||||||
|
err = db.Ping()
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
log.Printf("Error while pinging the database [%d/%d] : %s\n", i+1, dbConfig.RetriesOnError, err)
|
||||||
|
time.Sleep((time.Duration)(dbConfig.TimeBetweenRetriesMs) * time.Millisecond)
|
||||||
|
}
|
||||||
|
profiler.Add(profiler_DATABASE_PING, time.Duration(time.Since(tStart).Microseconds()))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Cannot ping the database !")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func executeQuery(query string) *sql.Rows {
|
func executeQuery(query string) *sql.Rows {
|
||||||
|
if !isInit {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if db == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
Ping()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
var rows *sql.Rows
|
var rows *sql.Rows
|
||||||
|
tStart := time.Now()
|
||||||
|
for i := range dbConfig.RetriesOnError {
|
||||||
rows, err = db.Query(query)
|
rows, err = db.Query(query)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
log.Printf("Error while querying the database [%d/%d] : %s\n", i+1, dbConfig.RetriesOnError, err)
|
||||||
|
time.Sleep((time.Duration)(dbConfig.TimeBetweenRetriesMs) * time.Millisecond)
|
||||||
|
}
|
||||||
|
profiler.Add(profiler_DATABASE_QUERY, time.Duration(time.Since(tStart).Microseconds()))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Error while query DB :")
|
log.Fatalln("Cannot query the database !")
|
||||||
log.Fatal(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return rows
|
return rows
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
package profiler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"maps"
|
||||||
|
"math"
|
||||||
|
"slices"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const PROFILER_DEFAULT_MAX_COUNT uint32 = 1000
|
||||||
|
const PROFILER_DEFAULT_UNIT string = ""
|
||||||
|
|
||||||
|
type Data struct {
|
||||||
|
count uint32
|
||||||
|
mean float64
|
||||||
|
min time.Duration
|
||||||
|
max time.Duration
|
||||||
|
lastValue time.Duration
|
||||||
|
unit string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Profile struct {
|
||||||
|
sync.RWMutex
|
||||||
|
unit string
|
||||||
|
count uint32
|
||||||
|
maxCount uint32
|
||||||
|
index uint32
|
||||||
|
sum time.Duration
|
||||||
|
mean float64
|
||||||
|
values []time.Duration
|
||||||
|
lastValue time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
var profiles map[string]*Profile = make(map[string]*Profile)
|
||||||
|
|
||||||
|
func Register(entry string, maxValues uint32, unit string) {
|
||||||
|
var profile *Profile
|
||||||
|
var exist bool
|
||||||
|
profile, exist = profiles[entry]
|
||||||
|
|
||||||
|
if exist && (profile != nil) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
profile = new(Profile)
|
||||||
|
profile.maxCount = maxValues
|
||||||
|
profile.values = make([]time.Duration, profile.maxCount)
|
||||||
|
profile.unit = unit
|
||||||
|
profiles[entry] = profile
|
||||||
|
}
|
||||||
|
|
||||||
|
func Add(entry string, duration time.Duration) {
|
||||||
|
var profile *Profile
|
||||||
|
var exist bool
|
||||||
|
profile, exist = profiles[entry]
|
||||||
|
|
||||||
|
if !exist || (profile == nil) {
|
||||||
|
profile = new(Profile)
|
||||||
|
profile.maxCount = PROFILER_DEFAULT_MAX_COUNT
|
||||||
|
profile.values = make([]time.Duration, profile.maxCount)
|
||||||
|
profile.unit = PROFILER_DEFAULT_UNIT
|
||||||
|
profiles[entry] = profile
|
||||||
|
}
|
||||||
|
|
||||||
|
profile.Lock()
|
||||||
|
|
||||||
|
var lastValue time.Duration = profile.values[profile.index]
|
||||||
|
profile.values[profile.index] = duration
|
||||||
|
profile.index++
|
||||||
|
if profile.index >= profile.maxCount {
|
||||||
|
profile.index = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if profile.count < profile.maxCount {
|
||||||
|
profile.count++
|
||||||
|
}
|
||||||
|
|
||||||
|
profile.sum += duration - lastValue
|
||||||
|
profile.mean = (((float64)(profile.sum)) / (float64)(time.Duration(profile.count)))
|
||||||
|
profile.lastValue = duration
|
||||||
|
|
||||||
|
profile.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAll(entries *[]string) {
|
||||||
|
*entries = slices.Collect(maps.Keys(profiles))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Get(entry string) Data {
|
||||||
|
var profile *Profile
|
||||||
|
var exist bool
|
||||||
|
profile, exist = profiles[entry]
|
||||||
|
|
||||||
|
if !exist || (profile == nil) {
|
||||||
|
profile = new(Profile)
|
||||||
|
profiles[entry] = profile
|
||||||
|
}
|
||||||
|
|
||||||
|
var data Data
|
||||||
|
|
||||||
|
profile.RLock()
|
||||||
|
|
||||||
|
data.mean = profile.mean
|
||||||
|
data.min = math.MaxInt64
|
||||||
|
data.max = 0
|
||||||
|
data.count = profile.count
|
||||||
|
data.unit = profile.unit
|
||||||
|
data.lastValue = profile.lastValue
|
||||||
|
for i := range profile.count {
|
||||||
|
value := profile.values[i]
|
||||||
|
if value < data.min {
|
||||||
|
data.min = value
|
||||||
|
}
|
||||||
|
if value > data.max {
|
||||||
|
data.max = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
profile.RUnlock()
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
func (data Data) ToString() string {
|
||||||
|
return fmt.Sprintf(`
|
||||||
|
- Last value : %d %s
|
||||||
|
- Mean : %f %s
|
||||||
|
- Min : %d %s
|
||||||
|
- Max : %d %s
|
||||||
|
- Number of values : %d
|
||||||
|
`, data.lastValue, data.unit, data.mean, data.unit, data.min, data.unit, data.max, data.unit, data.count)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Log(entry string) {
|
||||||
|
log.Println(Get(entry).ToString())
|
||||||
|
}
|
Loading…
Reference in New Issue