Do proper averaging and collect more samples for the memory analytics pane.
Added an ETag for the alerts endpoint when you're not logged to save bandwidth. The Page Manager now uses dyntmpl. The Setting Manager now uses dyntmpl. The Word Filter Manager now uses dyntmpl. Fixed the padding on the noavatar alerts for Nox. Tweaked the panel_word_filters_to phrase. Tweaked the panel_statistics_memory_head phrase. Added the panel_statistics_memory_chart_aria phrase. Added the panel_statistics_memory_table_aria phrase. Added the panel_statistics_memory_no_memory phrase.
This commit is contained in:
parent
5dc238f196
commit
a8e1076f7c
|
@ -1,6 +1,8 @@
|
|||
package counters
|
||||
|
||||
import (
|
||||
"time"
|
||||
"sync"
|
||||
"runtime"
|
||||
"database/sql"
|
||||
|
||||
|
@ -12,6 +14,9 @@ var MemoryCounter *DefaultMemoryCounter
|
|||
|
||||
type DefaultMemoryCounter struct {
|
||||
insert *sql.Stmt
|
||||
totMem uint64
|
||||
totCount uint64
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func NewMemoryCounter(acc *qgen.Accumulator) (*DefaultMemoryCounter, error) {
|
||||
|
@ -21,13 +26,35 @@ func NewMemoryCounter(acc *qgen.Accumulator) (*DefaultMemoryCounter, error) {
|
|||
c.AddScheduledFifteenMinuteTask(co.Tick)
|
||||
//c.AddScheduledSecondTask(co.Tick)
|
||||
c.AddShutdownTask(co.Tick)
|
||||
ticker := time.NewTicker(time.Minute)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
var m runtime.MemStats
|
||||
runtime.ReadMemStats(&m)
|
||||
co.Lock()
|
||||
co.totCount++
|
||||
co.totMem += m.Sys
|
||||
co.Unlock()
|
||||
}
|
||||
}
|
||||
}()
|
||||
return co, acc.FirstError()
|
||||
}
|
||||
|
||||
func (co *DefaultMemoryCounter) Tick() (err error) {
|
||||
var m runtime.MemStats
|
||||
runtime.ReadMemStats(&m)
|
||||
c.DebugLogf("Inserting a memchunk with a value of %d", m.Sys)
|
||||
_, err = co.insert.Exec(m.Sys)
|
||||
var avgMem uint64
|
||||
co.Lock()
|
||||
co.totCount++
|
||||
co.totMem += m.Sys
|
||||
avgMem = co.totMem / co.totCount
|
||||
co.totMem = 0
|
||||
co.totCount = 0
|
||||
co.Unlock()
|
||||
c.DebugLogf("Inserting a memchunk with a value of %d", avgMem)
|
||||
_, err = co.insert.Exec(avgMem)
|
||||
return err
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
*
|
||||
* Gosora Task System
|
||||
* Copyright Azareal 2017 - 2019
|
||||
* Copyright Azareal 2017 - 2020
|
||||
*
|
||||
*/
|
||||
package common
|
||||
|
|
|
@ -844,7 +844,7 @@
|
|||
"panel_group_extended_permissions":"Extended Permissions",
|
||||
|
||||
"panel_word_filters_head":"Word Filters",
|
||||
"panel_word_filters_to":"...to...",
|
||||
"panel_word_filters_to":"to",
|
||||
"panel_word_filters_edit_button_aria":"Edit Word Filter",
|
||||
"panel_word_filters_update_button":"Update",
|
||||
"panel_word_filters_delete_button_aria":"Delete Word Filter",
|
||||
|
@ -883,7 +883,7 @@
|
|||
"panel_statistics_operating_systems_head":"Operating Systems",
|
||||
"panel_statistics_topic_counts_head":"Topic Counts",
|
||||
"panel_statistics_requests_head":"Requests",
|
||||
"panel_statistics_memory_head":"Memory",
|
||||
"panel_statistics_memory_head":"Memory Usage",
|
||||
|
||||
"panel_statistics_time_range_one_year":"1 year",
|
||||
"panel_statistics_time_range_three_months":"3 months",
|
||||
|
@ -897,11 +897,13 @@
|
|||
"panel_statistics_post_counts_chart_aria":"Post Chart",
|
||||
"panel_statistics_topic_counts_chart_aria":"Topic Chart",
|
||||
"panel_statistics_requests_chart_aria":"Requests Chart",
|
||||
"panel_statistics_memory_chart_aria":"Memory Use Chart",
|
||||
"panel_statistics_details_head":"Details",
|
||||
"panel_statistics_post_counts_table_aria":"Post Table, this has the same information as the post chart",
|
||||
"panel_statistics_topic_counts_table_aria":"Topic Table, this has the same information as the topic chart",
|
||||
"panel_statistics_route_views_table_aria":"View Table, this has the same information as the view chart",
|
||||
"panel_statistics_requests_table_aria":"View Table, this has the same information as the view chart",
|
||||
"panel_statistics_memory_table_aria":"Memory Use Table, this has the same information as the memory use chart",
|
||||
"panel_statistics_views_suffix":" views",
|
||||
"panel_statistics_posts_suffix":" posts",
|
||||
"panel_statistics_topics_suffix":" topics",
|
||||
|
@ -913,6 +915,7 @@
|
|||
"panel_statistics_referrers_no_referrers":"No referrers could be found in the selected time range",
|
||||
"panel_statistics_routes_no_routes":"No route view counts could be found in the selected time range",
|
||||
"panel_statistics_operating_systems_no_operating_systems":"No operating systems could be found in the selected time range",
|
||||
"panel_statistics_memory_no_memory":"No memory chunks could be found in the selected time range",
|
||||
|
||||
"panel_logs_menu_head":"Logs",
|
||||
"panel_logs_registration_head":"Registrations",
|
||||
|
|
14
routes.go
14
routes.go
|
@ -67,6 +67,20 @@ func routeAPI(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError
|
|||
// TODO: Split this into it's own function
|
||||
case "alerts": // A feed of events tailored for a specific user
|
||||
if !user.Loggedin {
|
||||
var etag string
|
||||
_, ok := w.(c.GzipResponseWriter)
|
||||
if ok {
|
||||
etag = "\""+strconv.FormatInt(c.StartTime.Unix(), 10)+"-ng\""
|
||||
} else {
|
||||
etag = "\""+strconv.FormatInt(c.StartTime.Unix(), 10)+"-n\""
|
||||
}
|
||||
w.Header().Set("ETag", etag)
|
||||
if match := r.Header.Get("If-None-Match"); match != "" {
|
||||
if strings.Contains(match, etag) {
|
||||
w.WriteHeader(http.StatusNotModified)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
w.Write(phraseLoginAlerts)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -119,6 +119,44 @@ func analyticsRowsToViewMap(rows *sql.Rows, labelList []int64, viewMap map[int64
|
|||
return viewMap, rows.Err()
|
||||
}
|
||||
|
||||
type pAvg struct {
|
||||
Avg int64
|
||||
Tot int64
|
||||
}
|
||||
|
||||
func analyticsRowsToAverageMap(rows *sql.Rows, labelList []int64, avgMap map[int64]int64) (map[int64]int64, error) {
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var count int64
|
||||
var createdAt time.Time
|
||||
err := rows.Scan(&count, &createdAt)
|
||||
if err != nil {
|
||||
return avgMap, err
|
||||
}
|
||||
var unixCreatedAt = createdAt.Unix()
|
||||
// TODO: Bulk log this
|
||||
if c.Dev.SuperDebug {
|
||||
log.Print("count: ", count)
|
||||
log.Print("createdAt: ", createdAt)
|
||||
log.Print("unixCreatedAt: ", unixCreatedAt)
|
||||
}
|
||||
var pAvgMap = make(map[int64]pAvg)
|
||||
for _, value := range labelList {
|
||||
if unixCreatedAt > value {
|
||||
prev := pAvgMap[value]
|
||||
prev.Avg += count
|
||||
prev.Tot++
|
||||
pAvgMap[value] = prev
|
||||
break
|
||||
}
|
||||
}
|
||||
for key, pAvg := range pAvgMap {
|
||||
avgMap[key] = pAvg.Avg / pAvg.Tot
|
||||
}
|
||||
}
|
||||
return avgMap, rows.Err()
|
||||
}
|
||||
|
||||
func PreAnalyticsDetail(w http.ResponseWriter, r *http.Request, user *c.User) (*c.BasePanelPage, c.RouteError) {
|
||||
basePage, ferr := buildBasePage(w, r, user, "analytics", "analytics")
|
||||
if ferr != nil {
|
||||
|
@ -473,46 +511,29 @@ func AnalyticsMemory(w http.ResponseWriter, r *http.Request, user c.User) c.Rout
|
|||
if err != nil {
|
||||
return c.LocalError(err.Error(), w, r, user)
|
||||
}
|
||||
revLabelList, labelList, viewMap := analyticsTimeRangeToLabelList(timeRange)
|
||||
revLabelList, labelList, avgMap := analyticsTimeRangeToLabelList(timeRange)
|
||||
|
||||
c.DebugLog("in panel.AnalyticsMemory")
|
||||
rows, err := qgen.NewAcc().Select("memchunks").Columns("count, createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return c.InternalError(err, w, r)
|
||||
}
|
||||
viewMap, err = analyticsRowsToViewMap(rows, labelList, viewMap)
|
||||
avgMap, err = analyticsRowsToAverageMap(rows, labelList, avgMap)
|
||||
if err != nil {
|
||||
return c.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
var divBy int64 = 1
|
||||
switch timeRange.Range {
|
||||
case "one-year":
|
||||
divBy = 2 * 30 * 12
|
||||
case "three-months":
|
||||
divBy = 2 * 30 * 3
|
||||
case "one-month":
|
||||
divBy = 2 * 30
|
||||
case "one-week":
|
||||
divBy = 1 * 7
|
||||
case "two-days":
|
||||
divBy = 4
|
||||
case "one-day":
|
||||
divBy = 2
|
||||
}
|
||||
|
||||
// TODO: Adjust for the missing chunks in week and month
|
||||
var viewList []int64
|
||||
var viewItems []c.PanelAnalyticsItemUnit
|
||||
var avgList []int64
|
||||
var avgItems []c.PanelAnalyticsItemUnit
|
||||
for _, value := range revLabelList {
|
||||
viewMap[value] = viewMap[value] / divBy
|
||||
viewList = append(viewList, viewMap[value])
|
||||
cv, cu := c.ConvertByteUnit(float64(viewMap[value]))
|
||||
viewItems = append(viewItems, c.PanelAnalyticsItemUnit{Time: value, Unit: cu, Count: int64(cv)})
|
||||
avgList = append(avgList, avgMap[value])
|
||||
cv, cu := c.ConvertByteUnit(float64(avgMap[value]))
|
||||
avgItems = append(avgItems, c.PanelAnalyticsItemUnit{Time: value, Unit: cu, Count: int64(cv)})
|
||||
}
|
||||
graph := c.PanelTimeGraph{Series: [][]int64{viewList}, Labels: labelList}
|
||||
graph := c.PanelTimeGraph{Series: [][]int64{avgList}, Labels: labelList}
|
||||
c.DebugLogf("graph: %+v\n", graph)
|
||||
pi := c.PanelAnalyticsStdUnit{graph, viewItems, timeRange.Range, timeRange.Unit, "time"}
|
||||
pi := c.PanelAnalyticsStdUnit{graph, avgItems, timeRange.Range, timeRange.Unit, "time"}
|
||||
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "panel_analytics_right","analytics","panel_analytics_memory", pi})
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ func Pages(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError {
|
|||
|
||||
pageList := c.Paginate(pageCount, perPage, 5)
|
||||
pi := c.PanelCustomPagesPage{basePage, cPages, c.Paginator{pageList, page, lastPage}}
|
||||
return renderTemplate("panel_pages", w, r, basePage.Header, &pi)
|
||||
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"panel_page_list","","panel_pages",&pi})
|
||||
}
|
||||
|
||||
func PagesCreateSubmit(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError {
|
||||
|
@ -90,7 +90,7 @@ func PagesEdit(w http.ResponseWriter, r *http.Request, user c.User, spid string)
|
|||
}
|
||||
|
||||
pi := c.PanelCustomPageEditPage{basePage, page}
|
||||
return renderTemplate("panel_pages_edit", w, r, basePage.Header, &pi)
|
||||
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"panel_page_edit","","panel_pages_edit",&pi})
|
||||
}
|
||||
|
||||
func PagesEditSubmit(w http.ResponseWriter, r *http.Request, user c.User, spid string) c.RouteError {
|
||||
|
|
|
@ -50,7 +50,7 @@ func Settings(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError
|
|||
}
|
||||
|
||||
pi := c.PanelPage{basePage, tList, settingList}
|
||||
return renderTemplate("panel_settings", w, r, basePage.Header, &pi)
|
||||
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"","","panel_settings",&pi})
|
||||
}
|
||||
|
||||
func SettingEdit(w http.ResponseWriter, r *http.Request, user c.User, sname string) c.RouteError {
|
||||
|
@ -90,7 +90,7 @@ func SettingEdit(w http.ResponseWriter, r *http.Request, user c.User, sname stri
|
|||
|
||||
pSetting := &c.PanelSetting{setting, phrases.GetSettingPhrase(setting.Name)}
|
||||
pi := c.PanelSettingPage{basePage, itemList, pSetting}
|
||||
return renderTemplate("panel_setting", w, r, basePage.Header, &pi)
|
||||
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"","","panel_setting",&pi})
|
||||
}
|
||||
|
||||
func SettingEditSubmit(w http.ResponseWriter, r *http.Request, user c.User, sname string) c.RouteError {
|
||||
|
|
|
@ -25,7 +25,7 @@ func WordFilters(w http.ResponseWriter, r *http.Request, user c.User) c.RouteErr
|
|||
}
|
||||
|
||||
pi := c.PanelPage{basePage, tList, filterList}
|
||||
return renderTemplate("panel_word_filters", w, r, basePage.Header, &pi)
|
||||
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"","","panel_word_filters",&pi})
|
||||
}
|
||||
|
||||
func WordFiltersCreateSubmit(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError {
|
||||
|
@ -67,7 +67,7 @@ func WordFiltersEdit(w http.ResponseWriter, r *http.Request, user c.User, wfid s
|
|||
_ = wfid
|
||||
|
||||
pi := c.PanelPage{basePage, tList, nil}
|
||||
return renderTemplate("panel_word_filters_edit", w, r, basePage.Header, &pi)
|
||||
return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage,"","","panel_word_filters_edit",&pi})
|
||||
}
|
||||
|
||||
func WordFiltersEditSubmit(w http.ResponseWriter, r *http.Request, user c.User, wfid string) c.RouteError {
|
||||
|
|
|
@ -13,20 +13,20 @@
|
|||
</div>
|
||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/memory/" method="get"></form>
|
||||
<div id="panel_analytics_memory" class="colstack_graph_holder">
|
||||
<div class="ct_chart" aria-label="{{lang "panel_statistics_post_counts_chart_aria"}}"></div>
|
||||
<div class="ct_chart" aria-label="{{lang "panel_statistics_memory_chart_aria"}}"></div>
|
||||
</div>
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem">
|
||||
<h1>{{lang "panel_statistics_details_head"}}</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div id="panel_analytics_posts_table" class="colstack_item rowlist" aria-label="{{lang "panel_statistics_post_counts_table_aria"}}">
|
||||
<div id="panel_analytics_posts_table" class="colstack_item rowlist" aria-label="{{lang "panel_statistics_memory_table_aria"}}">
|
||||
{{range .ViewItems}}
|
||||
<div class="rowitem panel_compactrow editable_parent">
|
||||
<a class="panel_upshift {{if or (or (or (eq $.TimeRange "six-hours") (eq $.TimeRange "twelve-hours")) (eq $.TimeRange "one-day")) (eq $.TimeRange "two-days")}}unix_to_24_hour_time{{else}}unix_to_date{{end}}">{{.Time}}</a>
|
||||
<a class="panel_upshift unix_{{if or (or (or (eq $.TimeRange "six-hours") (eq $.TimeRange "twelve-hours")) (eq $.TimeRange "one-day")) (eq $.TimeRange "two-days")}}to_24_hour_time{{else}}to_date{{end}}">{{.Time}}</a>
|
||||
<span class="panel_compacttext to_right">{{.Count}}{{.Unit}}</span>
|
||||
</div>
|
||||
{{else}}<div class="rowitem passive rowmsg">{{lang "panel_statistics_post_counts_no_post_counts"}}</div>{{end}}
|
||||
{{else}}<div class="rowitem passive rowmsg">{{lang "panel_statistics_memory_no_memory"}}</div>{{end}}
|
||||
</div>
|
||||
<script>
|
||||
let rawLabels = [{{range .Graph.Labels}}
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
{{template "header.html" . }}
|
||||
<div id="panel_page_list" class="colstack panel_stack">
|
||||
{{template "panel_menu.html" . }}
|
||||
<main class="colstack_right">
|
||||
{{template "panel_before_head.html" . }}
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><h1>{{lang "panel_pages_head"}}</h1></div>
|
||||
</div>
|
||||
|
@ -45,6 +40,3 @@
|
|||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
{{template "header.html" . }}
|
||||
<div id="panel_page_edit" class="colstack panel_stack">
|
||||
{{template "panel_menu.html" . }}
|
||||
<main class="colstack_right">
|
||||
{{template "panel_before_head.html" . }}
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><h1>{{lang "panel_pages_edit_head"}}</h1></div>
|
||||
</div>
|
||||
|
@ -26,7 +21,3 @@
|
|||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -1,9 +1,3 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack panel_stack">
|
||||
|
||||
{{template "panel_menu.html" . }}
|
||||
<main class="colstack_right">
|
||||
{{template "panel_before_head.html" . }}
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><h1>{{.Setting.FriendlyName}}</h1></div>
|
||||
</div>
|
||||
|
@ -41,7 +35,3 @@
|
|||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack panel_stack">
|
||||
{{template "panel_menu.html" . }}
|
||||
<main class="colstack_right">
|
||||
{{template "panel_before_head.html" . }}
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><h1>{{lang "panel_settings_head"}}</h1></div>
|
||||
</div>
|
||||
|
@ -14,6 +9,3 @@
|
|||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -1,9 +1,3 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack panel_stack">
|
||||
|
||||
{{template "panel_menu.html" . }}
|
||||
<main class="colstack_right">
|
||||
{{template "panel_before_head.html" . }}
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><h1>{{lang "panel_word_filters_head"}}</h1></div>
|
||||
</div>
|
||||
|
@ -44,7 +38,3 @@
|
|||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -74,6 +74,15 @@ li a {
|
|||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
.alertItem:not(.withAvatar) {
|
||||
padding-top: 0px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
.alertItem:not(.withAvatar) a {
|
||||
padding-top: 14px;
|
||||
padding-bottom: 18px;
|
||||
font-size: 17px;
|
||||
}
|
||||
.alertItem.withAvatar {
|
||||
background: none !important;
|
||||
height: 66px;
|
||||
|
|
Loading…
Reference in New Issue