Skip to content

Commit fc8cf4c

Browse files
author
pzrr@qq.com
committed
Merge branch 'develop' of https://github.com/devfeel/dotweb into develop
2 parents a4bb5e3 + 03a2897 commit fc8cf4c

10 files changed

Lines changed: 383 additions & 40 deletions

File tree

‎README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ func StartServer() error {
4545
* 内建Cache支持
4646
* 支持接入第三方模板引擎(需实现dotweb.Renderer接口)
4747
* 模块可配置化,85%模块可通过配置维护
48+
* 自集成基础统计数据,并支持按分钟为单位的间隔时间统计数据输出
4849

4950
#### Config Example
5051
* [dotweb.conf](https://github.com/devfeel/dotweb/blob/master/example/config/dotweb.conf)

‎core/context.go‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,17 @@ func (ctx *ItemContext) GetInt(key string) int {
8080
return value.(int)
8181
}
8282

83+
/*
84+
* 读取指定key在AppContext中的内容,以int格式输出
85+
*/
86+
func (ctx *ItemContext) GetUInt64(key string) uint64 {
87+
value, exists := ctx.Get(key)
88+
if !exists {
89+
return 0
90+
}
91+
return value.(uint64)
92+
}
93+
8394
//check exists key
8495
func (ctx *ItemContext) Exists(key string) bool {
8596
_, exists := ctx.contextMap[key]

‎core/state.go‎

Lines changed: 276 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,84 @@
11
package core
22

33
import (
4+
"github.com/devfeel/dotweb/framework/json"
5+
"strconv"
6+
"strings"
7+
"sync"
48
"sync/atomic"
59
"time"
610
)
711

812
var GlobalState *ServerStateInfo
913

14+
const (
15+
minuteTimeLayout = "200601021504"
16+
dateTimeLayout = "2006-01-02 15:04:05"
17+
defaultReserveMinutes = 60
18+
defaultCheckTimeMinutes = 10
19+
)
20+
1021
func init() {
1122
GlobalState = &ServerStateInfo{
12-
ServerStartTime: time.Now(),
13-
TotalRequestCount: 0,
14-
TotalErrorCount: 0,
23+
ServerStartTime: time.Now(),
24+
TotalRequestCount: 0,
25+
TotalErrorCount: 0,
26+
IntervalRequestData: NewItemContext(),
27+
DetailRequestPageData: NewItemContext(),
28+
IntervalErrorData: NewItemContext(),
29+
DetailErrorPageData: NewItemContext(),
30+
DetailErrorData: NewItemContext(),
31+
DetailHTTPCodeData: NewItemContext(),
32+
dataChan_Request: make(chan *RequestInfo, 1000),
33+
dataChan_Error: make(chan *ErrorInfo, 1000),
34+
dataChan_HttpCode: make(chan *HttpCodeInfo, 1000),
35+
infoPool: &pool{
36+
requestInfo: sync.Pool{
37+
New: func() interface{} {
38+
return &RequestInfo{}
39+
},
40+
},
41+
errorInfo: sync.Pool{
42+
New: func() interface{} {
43+
return &ErrorInfo{}
44+
},
45+
},
46+
httpCodeInfo: sync.Pool{
47+
New: func() interface{} {
48+
return &HttpCodeInfo{}
49+
},
50+
},
51+
},
1552
}
53+
go GlobalState.handleInfo()
54+
go time.AfterFunc(time.Duration(defaultCheckTimeMinutes)*time.Minute, GlobalState.checkAndRemoveIntervalData)
55+
}
56+
57+
//pool定义
58+
type pool struct {
59+
requestInfo sync.Pool
60+
errorInfo sync.Pool
61+
httpCodeInfo sync.Pool
62+
}
63+
64+
//http request count info
65+
type RequestInfo struct {
66+
Url string
67+
Num uint64
68+
}
69+
70+
//error count info
71+
type ErrorInfo struct {
72+
Url string
73+
ErrMsg string
74+
Num uint64
75+
}
76+
77+
//httpcode count info
78+
type HttpCodeInfo struct {
79+
Url string
80+
Code int
81+
Num uint64
1682
}
1783

1884
//服务器状态信息
@@ -21,18 +87,219 @@ type ServerStateInfo struct {
2187
ServerStartTime time.Time
2288
//该运行期间总访问次数
2389
TotalRequestCount uint64
24-
//该运行期间错误次数
90+
//单位时间内请求数据 - 按分钟为单位
91+
IntervalRequestData *ItemContext
92+
//明细请求页面数据 - 以不带参数的访问url为key
93+
DetailRequestPageData *ItemContext
94+
//该运行期间异常次数
2595
TotalErrorCount uint64
96+
//单位时间内异常次数 - 按分钟为单位
97+
IntervalErrorData *ItemContext
98+
//明细异常页面数据 - 以不带参数的访问url为key
99+
DetailErrorPageData *ItemContext
100+
//明细异常数据 - 以不带参数的访问url为key
101+
DetailErrorData *ItemContext
102+
//明细Http状态码数据 - 以HttpCode为key,例如200、500等
103+
DetailHTTPCodeData *ItemContext
104+
105+
dataChan_Request chan *RequestInfo
106+
dataChan_Error chan *ErrorInfo
107+
dataChan_HttpCode chan *HttpCodeInfo
108+
//对象池
109+
infoPool *pool
26110
}
27111

28-
//增加请求数
29-
func (state *ServerStateInfo) AddRequestCount(num uint64) uint64 {
30-
atomic.AddUint64(&state.TotalRequestCount, num)
112+
//ShowHtmlData show server state data html-string format
113+
func (state *ServerStateInfo) ShowHtmlData() string {
114+
data := "<html><body><div>"
115+
data += "ServerStartTime : " + state.ServerStartTime.Format(dateTimeLayout)
116+
data += "<br>"
117+
data += "TotalRequestCount : " + strconv.FormatUint(state.TotalRequestCount, 10)
118+
data += "<br>"
119+
data += "TotalErrorCount : " + strconv.FormatUint(state.TotalErrorCount, 10)
120+
data += "<br>"
121+
state.IntervalRequestData.RLock()
122+
data += "IntervalRequestData : " + jsonutil.GetJsonString(state.IntervalRequestData.GetCurrentMap())
123+
state.IntervalRequestData.RUnlock()
124+
data += "<br>"
125+
state.DetailRequestPageData.RLock()
126+
data += "DetailRequestPageData : " + jsonutil.GetJsonString(state.DetailRequestPageData.GetCurrentMap())
127+
state.DetailRequestPageData.RUnlock()
128+
data += "<br>"
129+
state.IntervalErrorData.RLock()
130+
data += "IntervalErrorData : " + jsonutil.GetJsonString(state.IntervalErrorData.GetCurrentMap())
131+
state.IntervalErrorData.RUnlock()
132+
data += "<br>"
133+
state.DetailErrorPageData.RLock()
134+
data += "DetailErrorPageData : " + jsonutil.GetJsonString(state.DetailErrorPageData.GetCurrentMap())
135+
state.DetailErrorPageData.RUnlock()
136+
data += "<br>"
137+
state.DetailErrorData.RLock()
138+
data += "DetailErrorData : " + jsonutil.GetJsonString(state.DetailErrorData.GetCurrentMap())
139+
state.DetailErrorData.RUnlock()
140+
data += "<br>"
141+
state.DetailHTTPCodeData.RLock()
142+
data += "DetailHttpCodeData : " + jsonutil.GetJsonString(state.DetailHTTPCodeData.GetCurrentMap())
143+
state.DetailHTTPCodeData.RUnlock()
144+
data += "</div></body></html>"
145+
return data
146+
}
147+
148+
//QueryIntervalRequestData query request count by query time
149+
func (state *ServerStateInfo) QueryIntervalRequestData(queryKey string) uint64 {
150+
return state.IntervalRequestData.GetUInt64(queryKey)
151+
}
152+
153+
//QueryIntervalErrorData query error count by query time
154+
func (state *ServerStateInfo) QueryIntervalErrorData(queryKey string) uint64 {
155+
return state.IntervalErrorData.GetUInt64(queryKey)
156+
}
157+
158+
//AddRequestCount 增加请求数
159+
func (state *ServerStateInfo) AddRequestCount(page string, num uint64) uint64 {
160+
if strings.Index(page, "/dotweb/") != 0 {
161+
atomic.AddUint64(&state.TotalRequestCount, num)
162+
state.addRequestData(page, num)
163+
}
31164
return state.TotalRequestCount
32165
}
33166

34-
//增加错误数
35-
func (state *ServerStateInfo) AddErrorCount(num uint64) uint64 {
167+
//AddHttpCodeCount 增加Http状态码数据
168+
func (state *ServerStateInfo) AddTTPCodeCount(page string, code int, num uint64) uint64 {
169+
if strings.Index(page, "/dotweb/") != 0 {
170+
state.addHTTPCodeData(page, code, num)
171+
}
172+
return state.TotalErrorCount
173+
}
174+
175+
//AddErrorCount 增加错误数
176+
func (state *ServerStateInfo) AddErrorCount(page string, err error, num uint64) uint64 {
36177
atomic.AddUint64(&state.TotalErrorCount, num)
178+
state.addErrorData(page, err, num)
37179
return state.TotalErrorCount
38180
}
181+
182+
func (state *ServerStateInfo) addRequestData(page string, num uint64) {
183+
//get from pool
184+
info := state.infoPool.requestInfo.Get().(*RequestInfo)
185+
info.Url = page
186+
info.Num = num
187+
state.dataChan_Request <- info
188+
}
189+
190+
func (state *ServerStateInfo) addErrorData(page string, err error, num uint64) {
191+
//get from pool
192+
info := state.infoPool.errorInfo.Get().(*ErrorInfo)
193+
info.Url = page
194+
info.ErrMsg = err.Error()
195+
info.Num = num
196+
state.dataChan_Error <- info
197+
}
198+
199+
func (state *ServerStateInfo) addHTTPCodeData(page string, code int, num uint64) {
200+
//get from pool
201+
info := state.infoPool.httpCodeInfo.Get().(*HttpCodeInfo)
202+
info.Url = page
203+
info.Code = code
204+
info.Num = num
205+
state.dataChan_HttpCode <- info
206+
}
207+
208+
//处理日志内部函数
209+
func (state *ServerStateInfo) handleInfo() {
210+
for {
211+
select {
212+
case info := <-state.dataChan_Request:
213+
{
214+
//set detail page data
215+
key := strings.ToLower(info.Url)
216+
val := state.DetailRequestPageData.GetUInt64(key)
217+
state.DetailRequestPageData.Set(key, val+info.Num)
218+
219+
//set interval data
220+
key = time.Now().Format(minuteTimeLayout)
221+
val = state.IntervalRequestData.GetUInt64(key)
222+
state.IntervalRequestData.Set(key, val+info.Num)
223+
224+
//put info obj
225+
state.infoPool.requestInfo.Put(info)
226+
}
227+
case info := <-state.dataChan_Error:
228+
{
229+
//set detail error page data
230+
key := strings.ToLower(info.Url)
231+
val := state.DetailErrorPageData.GetUInt64(key)
232+
state.DetailErrorPageData.Set(key, val+info.Num)
233+
234+
//set detail error data
235+
key = info.ErrMsg
236+
val = state.DetailErrorData.GetUInt64(key)
237+
state.DetailErrorData.Set(key, val+info.Num)
238+
239+
//set interval data
240+
key = time.Now().Format(minuteTimeLayout)
241+
val = state.IntervalErrorData.GetUInt64(key)
242+
state.IntervalErrorData.Set(key, val+info.Num)
243+
244+
//put info obj
245+
state.infoPool.errorInfo.Put(info)
246+
}
247+
case info := <-state.dataChan_HttpCode:
248+
{
249+
//set detail error page data
250+
key := strconv.Itoa(info.Code)
251+
val := state.DetailHTTPCodeData.GetUInt64(key)
252+
state.DetailHTTPCodeData.Set(key, val+info.Num)
253+
254+
//put info obj
255+
state.infoPool.httpCodeInfo.Put(info)
256+
}
257+
}
258+
}
259+
}
260+
261+
//check and remove need to remove interval data with request and error
262+
func (state *ServerStateInfo) checkAndRemoveIntervalData() {
263+
var needRemoveKey []string
264+
now, _ := time.Parse(minuteTimeLayout, time.Now().Format(minuteTimeLayout))
265+
266+
//check IntervalRequestData
267+
state.IntervalRequestData.RLock()
268+
if state.IntervalRequestData.Len() > defaultReserveMinutes {
269+
for k := range state.IntervalRequestData.GetCurrentMap() {
270+
if t, err := time.Parse(minuteTimeLayout, k); err != nil {
271+
needRemoveKey = append(needRemoveKey, k)
272+
} else {
273+
if now.Sub(t) > (defaultReserveMinutes * time.Minute) {
274+
needRemoveKey = append(needRemoveKey, k)
275+
}
276+
}
277+
}
278+
}
279+
state.IntervalRequestData.RUnlock()
280+
//remove keys
281+
for _, v := range needRemoveKey {
282+
state.IntervalRequestData.Remove(v)
283+
}
284+
285+
//check IntervalErrorData
286+
needRemoveKey = []string{}
287+
state.IntervalErrorData.RLock()
288+
if state.IntervalErrorData.Len() > defaultReserveMinutes {
289+
for k := range state.IntervalErrorData.GetCurrentMap() {
290+
if t, err := time.Parse(minuteTimeLayout, k); err != nil {
291+
needRemoveKey = append(needRemoveKey, k)
292+
} else {
293+
if now.Sub(t) > (defaultReserveMinutes * time.Minute) {
294+
needRemoveKey = append(needRemoveKey, k)
295+
}
296+
}
297+
}
298+
}
299+
state.IntervalErrorData.RUnlock()
300+
//remove keys
301+
for _, v := range needRemoveKey {
302+
state.IntervalErrorData.Remove(v)
303+
}
304+
time.AfterFunc(time.Duration(defaultCheckTimeMinutes)*time.Minute, state.checkAndRemoveIntervalData)
305+
}

0 commit comments

Comments
 (0)