| @@ -20,6 +20,7 @@ type DataSource interface { | |||
| PushEndpoint | |||
| QueryData(inputs vos.DataQueryParam) []*vos.DataQueryResp // 查询一段时间 | |||
| QueryDataInstant(ql string) []*vos.DataQueryInstanceResp // 查询一个时间点数据 等同于prometheus instant_query | |||
| QueryTagKeys(recv vos.CommonTagQueryParam) *vos.TagKeyQueryResp // 获取标签的names | |||
| QueryTagValues(recv vos.CommonTagQueryParam) *vos.TagValueQueryResp // 根据一个label_name获取 values | |||
| QueryTagPairs(recv vos.CommonTagQueryParam) *vos.TagPairQueryResp // 根据匹配拿到所有 series 上面三个使用统一的结构体 | |||
| @@ -24,7 +24,7 @@ const ( | |||
| LABEL_IDENT = "ident" | |||
| LABEL_NAME = "__name__" | |||
| DEFAULT_QL = `{__name__=~".*a.*|.*e.*"}` | |||
| DEFAULT_STEP = 14 | |||
| DEFAULT_STEP = 15 | |||
| ) | |||
| type commonQueryObj struct { | |||
| @@ -207,13 +207,15 @@ func (pd *PromeDataSource) QueryData(inputs vos.DataQueryParam) []*vos.DataQuery | |||
| startT := tsToUtcTs(inputs.Start) | |||
| endT := tsToUtcTs(inputs.End) | |||
| // TODO 前端传入分辨率还是后端计算,grafana和prometheus ui都是前端传入 | |||
| delta := (inputs.End - inputs.Start) / 3600 | |||
| if delta <= 0 { | |||
| delta = 1 | |||
| resolution := time.Second * time.Duration(inputs.Step) | |||
| if inputs.Step == 0 { | |||
| // step==0 说明要自己算 grafana和prometheus ui都是前端传入 | |||
| delta := (inputs.End - inputs.Start) / 3600 | |||
| if delta <= 0 { | |||
| delta = 1 | |||
| } | |||
| resolution = time.Second * time.Duration(delta*DEFAULT_STEP) | |||
| } | |||
| resolution := time.Second * time.Duration(delta*DEFAULT_STEP) | |||
| q, err := pd.QueryEngine.NewRangeQuery(pd.Queryable, qlStrFinal, startT, endT, resolution) | |||
| if err != nil { | |||
| logger.Errorf("[prome_query_error][QueryData_error_may_be_parse_ql_error][args:%+v][err:%+v]", input, err) | |||
| @@ -253,7 +255,8 @@ func (pd *PromeDataSource) QueryData(inputs vos.DataQueryParam) []*vos.DataQuery | |||
| pNum := len(m.Points) | |||
| for _, p := range m.Points { | |||
| tmpP := &vos.Point{ | |||
| Timestamp: p.T, | |||
| // 毫秒时间时间戳转 秒时间戳 | |||
| Timestamp: p.T / 1e3, | |||
| Value: vos.JsonFloat(p.V), | |||
| } | |||
| oneResp.Values = append(oneResp.Values, tmpP) | |||
| @@ -266,7 +269,7 @@ func (pd *PromeDataSource) QueryData(inputs vos.DataQueryParam) []*vos.DataQuery | |||
| } | |||
| tagStr = strings.TrimRight(tagStr, ",") | |||
| oneResp.Tags = tagStr | |||
| oneResp.Resolution = delta * DEFAULT_STEP | |||
| oneResp.Resolution = int64(resolution / time.Second) | |||
| oneResp.PNum = pNum | |||
| respD = append(respD, oneResp) | |||
| @@ -616,6 +619,36 @@ func (pd *PromeDataSource) QueryTagPairs(recv vos.CommonTagQueryParam) *vos.TagP | |||
| return respD | |||
| } | |||
| func (pd *PromeDataSource) QueryDataInstant(ql string) []*vos.DataQueryInstanceResp { | |||
| respD := make([]*vos.DataQueryInstanceResp, 0) | |||
| pv := pd.QueryVector(ql) | |||
| if pv == nil { | |||
| return respD | |||
| } | |||
| for _, s := range pv { | |||
| metricOne := make(map[string]interface{}) | |||
| valueOne := make([]float64, 0) | |||
| for _, l := range s.Metric { | |||
| if l.Name == LABEL_NAME { | |||
| continue | |||
| } | |||
| metricOne[l.Name] = l.Value | |||
| } | |||
| // 毫秒时间时间戳转 秒时间戳 | |||
| valueOne = append(valueOne, float64(s.Point.T)/1e3) | |||
| valueOne = append(valueOne, s.Point.V) | |||
| respD = append(respD, &vos.DataQueryInstanceResp{ | |||
| Metric: metricOne, | |||
| Value: valueOne, | |||
| }) | |||
| } | |||
| return respD | |||
| } | |||
| func (pd *PromeDataSource) QueryVector(ql string) promql.Vector { | |||
| t := time.Now() | |||
| q, err := pd.QueryEngine.NewInstantQuery(pd.Queryable, ql, t) | |||
| @@ -0,0 +1,306 @@ | |||
| [ | |||
| { | |||
| "id": 0, | |||
| "name": "jmx_exporter", | |||
| "tags": "", | |||
| "configs": "{\"tags\":[{\"tagName\":\"java_app\",\"key\":\"java_app\",\"value\":\"*\",\"prefix\":false}]}", | |||
| "chart_groups": [ | |||
| { | |||
| "id": 0, | |||
| "dashboard_id": 0, | |||
| "name": "jvm统计", | |||
| "weight": 1, | |||
| "charts": [ | |||
| { | |||
| "id": 278, | |||
| "group_id": 75, | |||
| "configs": "{\"name\":\"jvm版本信息\",\"mode\":\"promethues\",\"prome_ql\":[\"avg(jvm_info{java_app=~\\\"$java_app\\\"}) without (runtime,vendor)\"],\"layout\":{\"h\":2,\"w\":12,\"x\":0,\"y\":0,\"i\":\"0\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 309, | |||
| "group_id": 75, | |||
| "configs": "{\"name\":\"java进程启动时间 单位:小时\",\"mode\":\"promethues\",\"prome_ql\":[\"(time() - process_start_time_seconds{java_app=~\\\"$java_app\\\"})/3600\"],\"layout\":{\"h\":2,\"w\":12,\"x\":12,\"y\":0,\"i\":\"1\"}}", | |||
| "weight": 0 | |||
| } | |||
| ] | |||
| }, | |||
| { | |||
| "id": 0, | |||
| "dashboard_id": 0, | |||
| "name": "jvm内存使用", | |||
| "weight": 2, | |||
| "charts": [ | |||
| { | |||
| "id": 279, | |||
| "group_id": 76, | |||
| "configs": "{\"name\":\"jvm内存使用 - nonheap 非堆区\",\"mode\":\"promethues\",\"prome_ql\":[\"jvm_memory_bytes_used{java_app=~\\\"$java_app\\\",area=\\\"nonheap\\\"}\"],\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 280, | |||
| "group_id": 76, | |||
| "configs": "{\"name\":\"jvm内存使用 - heap堆区\",\"mode\":\"promethues\",\"prome_ql\":[\"jvm_memory_bytes_used{java_app=~\\\"$java_app\\\",area=\\\"heap\\\"}\"],\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 281, | |||
| "group_id": 76, | |||
| "configs": "{\"name\":\"提交给 Java虚拟机使用的内存量 heap 堆区\",\"mode\":\"promethues\",\"prome_ql\":[\"jvm_memory_bytes_committed{java_app=~\\\"$java_app\\\",area=\\\"heap\\\"}\"],\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 282, | |||
| "group_id": 76, | |||
| "configs": "{\"name\":\"提交给 Java虚拟机使用的内存量 nonheap 非堆区\",\"mode\":\"promethues\",\"prome_ql\":[\"jvm_memory_bytes_committed{java_app=~\\\"$java_app\\\",area=\\\"nonheap\\\"}\"],\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 283, | |||
| "group_id": 76, | |||
| "configs": "{\"name\":\"jvm最大内存 \",\"mode\":\"promethues\",\"prome_ql\":[\"jvm_memory_bytes_max{java_app=~\\\"$java_app\\\",area=\\\"heap\\\"}\"],\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":2,\"i\":\"4\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 285, | |||
| "group_id": 76, | |||
| "configs": "{\"name\":\"jvm 初始化内存\",\"mode\":\"promethues\",\"prome_ql\":[\"jvm_memory_bytes_init{java_app=~\\\"$java_app\\\",area=\\\"heap\\\"}\"],\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":2,\"i\":\"5\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 286, | |||
| "group_id": 76, | |||
| "configs": "{\"name\":\"jvm内存使用百分比% heap堆区 \",\"mode\":\"promethues\",\"prome_ql\":[\"100 * jvm_memory_bytes_used{java_app=~\\\"$java_app\\\",area=\\\"heap\\\"}/jvm_memory_bytes_max{java_app=~\\\"$java_app\\\",area=\\\"heap\\\"}\"],\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":2,\"i\":\"6\"}}", | |||
| "weight": 0 | |||
| } | |||
| ] | |||
| }, | |||
| { | |||
| "id": 0, | |||
| "dashboard_id": 0, | |||
| "name": "jvm内存池", | |||
| "weight": 3, | |||
| "charts": [ | |||
| { | |||
| "id": 287, | |||
| "group_id": 77, | |||
| "configs": "{\"name\":\"jvm内存池分pool展示\",\"mode\":\"promethues\",\"prome_ql\":[\"jvm_memory_pool_bytes_max{java_app=~\\\"$java_app\\\"}\"],\"layout\":{\"h\":2,\"w\":24,\"x\":0,\"y\":0,\"i\":\"0\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 316, | |||
| "group_id": 77, | |||
| "configs": "{\"name\":\" JVM 缓冲池使用缓存大小\",\"mode\":\"promethues\",\"prome_ql\":[\"jvm_buffer_pool_used_bytes{java_app=~\\\"$java_app\\\"}\"],\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":2,\"i\":\"1\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 317, | |||
| "group_id": 77, | |||
| "configs": "{\"name\":\"JVM 缓冲池的字节容量\",\"mode\":\"promethues\",\"prome_ql\":[\"jvm_buffer_pool_capacity_bytes{java_app=~\\\"$java_app\\\"}\"],\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":2,\"i\":\"2\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 318, | |||
| "group_id": 77, | |||
| "configs": "{\"name\":\"JVM 缓冲池使用的字节大小\",\"mode\":\"promethues\",\"prome_ql\":[\"jvm_buffer_pool_used_bytes{java_app=~\\\"$java_app\\\"}\"],\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":2,\"i\":\"3\"}}", | |||
| "weight": 0 | |||
| } | |||
| ] | |||
| }, | |||
| { | |||
| "id": 0, | |||
| "dashboard_id": 0, | |||
| "name": "jvm gc情况", | |||
| "weight": 4, | |||
| "charts": [ | |||
| { | |||
| "id": 288, | |||
| "group_id": 78, | |||
| "configs": "{\"name\":\"新生代gc耗时 1分钟\",\"mode\":\"promethues\",\"prome_ql\":[\"increase(jvm_gc_collection_seconds_sum{java_app=~\\\"$java_app\\\",gc=\\\"G1 Young Generation\\\" }[1m])\"],\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":0,\"i\":\"0\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 289, | |||
| "group_id": 78, | |||
| "configs": "{\"name\":\"老生代gc耗时 1分钟\",\"mode\":\"promethues\",\"prome_ql\":[\"increase(jvm_gc_collection_seconds_sum{java_app=~\\\"$java_app\\\",gc=\\\"G1 Old Generation\\\" }[1m])\"],\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":0,\"i\":\"1\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 290, | |||
| "group_id": 78, | |||
| "configs": "{\"name\":\"新生代gc次数 1分钟\",\"mode\":\"promethues\",\"prome_ql\":[\"increase(jvm_gc_collection_seconds_count{java_app=~\\\"$java_app\\\",gc=\\\"G1 Young Generation\\\" }[1m])\"],\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":0,\"i\":\"2\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 291, | |||
| "group_id": 78, | |||
| "configs": "{\"name\":\"老生代gc次数 1分钟\",\"mode\":\"promethues\",\"prome_ql\":[\"increase(jvm_gc_collection_seconds_count{java_app=~\\\"$java_app\\\",gc=\\\"G1 Old Generation\\\" }[1m])\"],\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":2,\"i\":\"3\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 292, | |||
| "group_id": 78, | |||
| "configs": "{\"name\":\"新生代平均gc耗时 秒\",\"mode\":\"promethues\",\"prome_ql\":[\"jvm_gc_collection_seconds_sum{java_app=~\\\"$java_app\\\",gc=\\\"G1 Young Generation\\\" }/jvm_gc_collection_seconds_count{java_app=~\\\"$java_app\\\",gc=\\\"G1 Young Generation\\\" }\"],\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":2,\"i\":\"4\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 293, | |||
| "group_id": 78, | |||
| "configs": "{\"name\":\"老生代平均gc耗时\",\"mode\":\"promethues\",\"prome_ql\":[\"jvm_gc_collection_seconds_sum{java_app=~\\\"$java_app\\\",gc=\\\"G1 Old Generation\\\"}/jvm_gc_collection_seconds_count{java_app=~\\\"$java_app\\\",gc=\\\"G1 Old Generation\\\" }\"],\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":2,\"i\":\"5\"}}", | |||
| "weight": 0 | |||
| } | |||
| ] | |||
| }, | |||
| { | |||
| "id": 0, | |||
| "dashboard_id": 0, | |||
| "name": "jvm线程情况", | |||
| "weight": 5, | |||
| "charts": [ | |||
| { | |||
| "id": 294, | |||
| "group_id": 79, | |||
| "configs": "{\"name\":\"当前线程数\",\"mode\":\"promethues\",\"prome_ql\":[\"jvm_threads_current{java_app=~\\\"$java_app\\\"}\"],\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":0,\"i\":\"0\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 295, | |||
| "group_id": 79, | |||
| "configs": "{\"name\":\"守护线程数\",\"mode\":\"promethues\",\"prome_ql\":[\"jvm_threads_daemon{java_app=~\\\"$java_app\\\"}\"],\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":0,\"i\":\"1\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 296, | |||
| "group_id": 79, | |||
| "configs": "{\"name\":\"死锁线程数\",\"mode\":\"promethues\",\"prome_ql\":[\"jvm_threads_deadlocked{java_app=~\\\"$java_app\\\"}\"],\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":0,\"i\":\"2\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 297, | |||
| "group_id": 79, | |||
| "configs": "{\"name\":\"活动线程峰值\",\"mode\":\"promethues\",\"prome_ql\":[\"jvm_threads_peak{java_app=~\\\"$java_app\\\"}\"],\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":0,\"i\":\"3\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 298, | |||
| "group_id": 79, | |||
| "configs": "{\"name\":\"自JVM启动后,启动的线程总量(包括daemon,non-daemon和终止了的)\",\"mode\":\"promethues\",\"prome_ql\":[\"jvm_threads_started_total{java_app=~\\\"$java_app\\\"}\"],\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":2,\"i\":\"4\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 299, | |||
| "group_id": 79, | |||
| "configs": "{\"name\":\"当前TERMINATED线程个数\",\"mode\":\"promethues\",\"prome_ql\":[\"jvm_threads_state{java_app=~\\\"$java_app\\\",state=\\\"TERMINATED\\\"}\"],\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":4,\"i\":\"5\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 300, | |||
| "group_id": 79, | |||
| "configs": "{\"name\":\"当前RUNNABLE线程个数\",\"mode\":\"promethues\",\"prome_ql\":[\"jvm_threads_state{java_app=~\\\"$java_app\\\",state=\\\"RUNNABLE\\\"}\"],\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":2,\"i\":\"6\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 301, | |||
| "group_id": 79, | |||
| "configs": "{\"name\":\"当前NEW线程个数\",\"mode\":\"promethues\",\"prome_ql\":[\"jvm_threads_state{java_app=~\\\"$java_app\\\",state=\\\"NEW\\\"}\"],\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":2,\"i\":\"7\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 302, | |||
| "group_id": 79, | |||
| "configs": "{\"name\":\"当前TIMED_WAITING线程个数\",\"mode\":\"promethues\",\"prome_ql\":[\"jvm_threads_state{java_app=~\\\"$java_app\\\",state=\\\"TIMED_WAITING\\\"}\"],\"layout\":{\"h\":2,\"w\":6,\"x\":0,\"y\":4,\"i\":\"8\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 303, | |||
| "group_id": 79, | |||
| "configs": "{\"name\":\"当前BLOCKED线程个数\",\"mode\":\"promethues\",\"prome_ql\":[\"jvm_threads_state{java_app=~\\\"$java_app\\\",state=\\\"BLOCKED\\\"}\"],\"layout\":{\"h\":2,\"w\":6,\"x\":18,\"y\":4,\"i\":\"9\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 304, | |||
| "group_id": 79, | |||
| "configs": "{\"name\":\"当前WAITING线程个数\",\"mode\":\"promethues\",\"prome_ql\":[\"jvm_threads_state{java_app=~\\\"$java_app\\\",state=\\\"WAITING\\\"}\"],\"layout\":{\"h\":2,\"w\":6,\"x\":12,\"y\":4,\"i\":\"10\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 305, | |||
| "group_id": 79, | |||
| "configs": "{\"name\":\"当前线程状态汇总\",\"mode\":\"promethues\",\"prome_ql\":[\"jvm_threads_state{java_app=~\\\"$java_app\\\"}\"],\"layout\":{\"h\":2,\"w\":6,\"x\":6,\"y\":2,\"i\":\"11\"}}", | |||
| "weight": 0 | |||
| } | |||
| ] | |||
| }, | |||
| { | |||
| "id": 0, | |||
| "dashboard_id": 0, | |||
| "name": "加载类情况", | |||
| "weight": 6, | |||
| "charts": [ | |||
| { | |||
| "id": 306, | |||
| "group_id": 80, | |||
| "configs": "{\"name\":\"jvm 当前加载的类个数 \",\"mode\":\"promethues\",\"prome_ql\":[\"jvm_classes_loaded{java_app=~\\\"$java_app\\\"}\"],\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":0,\"i\":\"0\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 307, | |||
| "group_id": 80, | |||
| "configs": "{\"name\":\"jvm启动以来加载的类总个数\",\"mode\":\"promethues\",\"prome_ql\":[\"jvm_classes_loaded_total{java_app=~\\\"$java_app\\\"}\"],\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":0,\"i\":\"1\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 308, | |||
| "group_id": 80, | |||
| "configs": "{\"name\":\"jvm启动以来卸载的类总个数\",\"mode\":\"promethues\",\"prome_ql\":[\"jvm_classes_unloaded_total{java_app=~\\\"$java_app\\\"}\"],\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":0,\"i\":\"2\"}}", | |||
| "weight": 0 | |||
| } | |||
| ] | |||
| }, | |||
| { | |||
| "id": 0, | |||
| "dashboard_id": 0, | |||
| "name": "机器指标(配置了java.lang才有)", | |||
| "weight": 7, | |||
| "charts": [ | |||
| { | |||
| "id": 311, | |||
| "group_id": 81, | |||
| "configs": "{\"name\":\"java进程打开fd数\",\"mode\":\"promethues\",\"prome_ql\":[\"os_open_file_descriptor_count{java_app=~\\\"$java_app\\\"}\"],\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":0,\"i\":\"0\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 312, | |||
| "group_id": 81, | |||
| "configs": "{\"name\":\"机器总内存\",\"mode\":\"promethues\",\"prome_ql\":[\"os_total_memory_size{java_app=~\\\"$java_app\\\"}\"],\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":0,\"i\":\"1\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 313, | |||
| "group_id": 81, | |||
| "configs": "{\"name\":\"机器可用内存数\",\"mode\":\"promethues\",\"prome_ql\":[\"os_free_memory_size{java_app=~\\\"$java_app\\\"}\"],\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":0,\"i\":\"2\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 314, | |||
| "group_id": 81, | |||
| "configs": "{\"name\":\"机器近期cpu使用率\",\"mode\":\"promethues\",\"link\":\"https://docs.oracle.com/javase/7/docs/jre/api/management/extension/com/sun/management/OperatingSystemMXBean.html#getSystemCpuLoad()\",\"prome_ql\":[\"100 * os_system_cpu_load{java_app=~\\\"$java_app\\\"}\"],\"layout\":{\"h\":2,\"w\":8,\"x\":0,\"y\":2,\"i\":\"3\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 315, | |||
| "group_id": 81, | |||
| "configs": "{\"name\":\"java进程cpu使用\",\"mode\":\"promethues\",\"link\":\"https://docs.oracle.com/javase/7/docs/jre/api/management/extension/com/sun/management/OperatingSystemMXBean.html#getProcessCpuLoad()\",\"prome_ql\":[\"os_process_cpu_load{java_app=~\\\"$java_app\\\"}\"],\"layout\":{\"h\":2,\"w\":8,\"x\":8,\"y\":2,\"i\":\"4\"}}", | |||
| "weight": 0 | |||
| }, | |||
| { | |||
| "id": 319, | |||
| "group_id": 81, | |||
| "configs": "{\"name\":\"jvm cpu百分比\",\"mode\":\"promethues\",\"prome_ql\":[\"100 *(os_process_cpu_load{java_app=~\\\"$java_app\\\"}/os_system_cpu_load{java_app=~\\\"$java_app\\\"})\"],\"layout\":{\"h\":2,\"w\":8,\"x\":16,\"y\":2,\"i\":\"5\"}}", | |||
| "weight": 0 | |||
| } | |||
| ] | |||
| } | |||
| ] | |||
| } | |||
| ] | |||
| @@ -2,6 +2,7 @@ package http | |||
| import ( | |||
| "net/http" | |||
| "strings" | |||
| "github.com/didi/nightingale/v5/pkg/ierr" | |||
| "github.com/gin-gonic/gin" | |||
| @@ -23,7 +24,17 @@ func admin() gin.HandlerFunc { | |||
| c.Set("username", username) | |||
| user := loginUser(c) | |||
| if user.Role != "Admin" { | |||
| roles := strings.Fields(user.RolesForDB) | |||
| found := false | |||
| for i := 0; i < len(roles); i++ { | |||
| if roles[i] == "Admin" { | |||
| found = true | |||
| break | |||
| } | |||
| } | |||
| if !found { | |||
| ierr.Bomb(http.StatusForbidden, "forbidden") | |||
| } | |||
| @@ -42,10 +42,12 @@ func configRoutes(r *gin.Engine) { | |||
| // 开源版本,为了支持图表分享功能,允许匿名查询数据 | |||
| guest.POST("/query", GetData) | |||
| guest.POST("/instant-query", GetDataInstant) | |||
| guest.POST("/tag-pairs", GetTagPairs) | |||
| guest.POST("/tag-keys", GetTagKeys) | |||
| guest.POST("/tag-values", GetTagValues) | |||
| guest.POST("/tag-metrics", GetMetrics) | |||
| guest.GET("/check-promql", checkPromeQl) | |||
| } | |||
| // for brower, expose location in nginx.conf | |||
| @@ -138,7 +140,6 @@ func configRoutes(r *gin.Engine) { | |||
| pages.DELETE("/alert-rule-group/:id", login(), alertRuleGroupDel) | |||
| pages.POST("/alert-rule-group/:id/favorites", login(), alertRuleGroupFavoriteAdd) | |||
| pages.DELETE("/alert-rule-group/:id/favorites", login(), alertRuleGroupFavoriteDel) | |||
| pages.GET("/check-promql", checkPromeQl) | |||
| pages.POST("/alert-rules", login(), alertRuleAdd) | |||
| pages.PUT("/alert-rules/status", login(), alertRuleStatusPut) | |||
| @@ -169,6 +170,7 @@ func configRoutes(r *gin.Engine) { | |||
| pages.GET("/tpl/content", tplGet) | |||
| pages.GET("/status", Status) | |||
| } | |||
| // for thirdparty, do not expose location in nginx.conf | |||
| @@ -288,11 +290,12 @@ func configRoutes(r *gin.Engine) { | |||
| v1.GET("/status", Status) | |||
| v1.POST("/query", GetData) | |||
| v1.GET("/check-promql", checkPromeQl) | |||
| v1.POST("/instant-query", GetDataInstant) | |||
| v1.POST("/tag-keys", GetTagKeys) | |||
| v1.POST("/tag-values", GetTagValues) | |||
| v1.POST("/tag-metrics", GetMetrics) | |||
| v1.POST("/tag-pairs", GetTagPairs) | |||
| v1.GET("/check-promql", checkPromeQl) | |||
| } | |||
| push := r.Group("/v1/n9e/series").Use(gzip.Gzip(gzip.DefaultCompression)) | |||
| @@ -129,8 +129,11 @@ func alertRuleGroupFavoriteDel(c *gin.Context) { | |||
| } | |||
| func alertRuleWritePermCheck(alertRuleGroup *models.AlertRuleGroup, user *models.User) { | |||
| if user.Role == "Admin" { | |||
| return | |||
| roles := strings.Fields(user.RolesForDB) | |||
| for i := 0; i < len(roles); i++ { | |||
| if roles[i] == "Admin" { | |||
| return | |||
| } | |||
| } | |||
| gids := IdsInt64(alertRuleGroup.UserGroupIds) | |||
| @@ -205,3 +205,17 @@ func GetData(c *gin.Context) { | |||
| resp := dataSource.QueryData(input) | |||
| renderData(c, resp, nil) | |||
| } | |||
| func GetDataInstant(c *gin.Context) { | |||
| dataSource, err := backend.GetDataSourceFor("") | |||
| if err != nil { | |||
| logger.Warningf("could not find datasource") | |||
| renderMessage(c, err) | |||
| return | |||
| } | |||
| var input vos.DataQueryInstantParam | |||
| dangerous(c.ShouldBindJSON(&input)) | |||
| resp := dataSource.QueryDataInstant(input.PromeQl) | |||
| renderData(c, resp, nil) | |||
| } | |||
| @@ -2,6 +2,7 @@ package http | |||
| import ( | |||
| "encoding/json" | |||
| "strings" | |||
| "time" | |||
| "github.com/gin-gonic/gin" | |||
| @@ -20,7 +21,14 @@ func userGets(c *gin.Context) { | |||
| list, err := models.UserGets(query, limit, offset(c, limit)) | |||
| dangerous(err) | |||
| admin := loginUser(c).Role == "Admin" | |||
| admin := false | |||
| roles := strings.Fields(loginUser(c).RolesForDB) | |||
| for i := 0; i < len(roles); i++ { | |||
| if roles[i] == "Admin" { | |||
| admin = true | |||
| break | |||
| } | |||
| } | |||
| renderData(c, gin.H{ | |||
| "list": list, | |||
| @@ -36,7 +44,7 @@ type userAddForm struct { | |||
| Phone string `json:"phone"` | |||
| Email string `json:"email"` | |||
| Portrait string `json:"portrait"` | |||
| Role string `json:"role"` | |||
| Roles []string `json:"roles"` | |||
| Contacts json.RawMessage `json:"contacts"` | |||
| } | |||
| @@ -50,23 +58,23 @@ func userAddPost(c *gin.Context) { | |||
| now := time.Now().Unix() | |||
| username := loginUsername(c) | |||
| u := models.User{ | |||
| Username: f.Username, | |||
| Password: password, | |||
| Nickname: f.Nickname, | |||
| Phone: f.Phone, | |||
| Email: f.Email, | |||
| Portrait: f.Portrait, | |||
| Role: f.Role, | |||
| Contacts: f.Contacts, | |||
| CreateAt: now, | |||
| UpdateAt: now, | |||
| CreateBy: username, | |||
| UpdateBy: username, | |||
| if len(f.Roles) == 0 { | |||
| bomb(200, "roles empty") | |||
| } | |||
| if u.Role == "" { | |||
| u.Role = "Standard" | |||
| u := models.User{ | |||
| Username: f.Username, | |||
| Password: password, | |||
| Nickname: f.Nickname, | |||
| Phone: f.Phone, | |||
| Email: f.Email, | |||
| Portrait: f.Portrait, | |||
| RolesForDB: strings.Join(f.Roles, " "), | |||
| Contacts: f.Contacts, | |||
| CreateAt: now, | |||
| UpdateAt: now, | |||
| CreateBy: username, | |||
| UpdateBy: username, | |||
| } | |||
| renderMessage(c, u.Add()) | |||
| @@ -81,7 +89,7 @@ type userProfileForm struct { | |||
| Phone string `json:"phone"` | |||
| Email string `json:"email"` | |||
| Portrait string `json:"portrait"` | |||
| Role string `json:"role"` | |||
| Roles []string `json:"roles"` | |||
| Status int `json:"status"` | |||
| Contacts json.RawMessage `json:"contacts"` | |||
| } | |||
| @@ -90,12 +98,16 @@ func userProfilePut(c *gin.Context) { | |||
| var f userProfileForm | |||
| bind(c, &f) | |||
| if len(f.Roles) == 0 { | |||
| bomb(200, "roles empty") | |||
| } | |||
| target := User(urlParamInt64(c, "id")) | |||
| target.Nickname = f.Nickname | |||
| target.Phone = f.Phone | |||
| target.Email = f.Email | |||
| target.Portrait = f.Portrait | |||
| target.Role = f.Role | |||
| target.RolesForDB = strings.Join(f.Roles, " ") | |||
| target.Status = f.Status | |||
| target.Contacts = f.Contacts | |||
| target.UpdateAt = time.Now().Unix() | |||
| @@ -107,7 +119,7 @@ func userProfilePut(c *gin.Context) { | |||
| "phone", | |||
| "email", | |||
| "portrait", | |||
| "role", | |||
| "roles", | |||
| "status", | |||
| "contacts", | |||
| "update_at", | |||
| @@ -44,7 +44,7 @@ func ResourceTotalByClasspathId(classpathIds []int64, query string) (int64, erro | |||
| } | |||
| q := "%" + query + "%" | |||
| num, err := DB.Where("ident in (select res_ident from classpath_resource where classpath_id in ("+str.IdsString(classpathIds)+")) and (ident like ? or alias like ?)", q, q).Count(new(Resource)) | |||
| num, err := DB.Where("ident in (select res_ident from classpath_resource where classpath_id in ("+str.IdsString(classpathIds)+")) and (ident like ? or alias like ? or tags like ? or note like ?)", q, q, q, q).Count(new(Resource)) | |||
| if err != nil { | |||
| logger.Errorf("mysql.error count resource in classpath(id=%v) query=%s fail: %v", classpathIds, query, err) | |||
| return 0, internalServerError | |||
| @@ -60,7 +60,7 @@ func ResourceGetsByClasspathId(classpathIds []int64, query string, limit, offset | |||
| q := "%" + query + "%" | |||
| var objs []Resource | |||
| err := DB.Where("ident in (select res_ident from classpath_resource where classpath_id in ("+str.IdsString(classpathIds)+")) and (ident like ? or alias like ?)", q, q).OrderBy("ident").Limit(limit, offset).Find(&objs) | |||
| err := DB.Where("ident in (select res_ident from classpath_resource where classpath_id in ("+str.IdsString(classpathIds)+")) and (ident like ? or alias like ? or tags like ? or note like ?)", q, q, q, q).OrderBy("ident").Limit(limit, offset).Find(&objs) | |||
| if err != nil { | |||
| logger.Errorf("mysql.error query resource in classpath(id=%d) query=%s fail: %v", classpathIds, query, err) | |||
| return nil, internalServerError | |||
| @@ -1,6 +1,9 @@ | |||
| package models | |||
| import "github.com/toolkits/pkg/logger" | |||
| import ( | |||
| "github.com/toolkits/pkg/logger" | |||
| "xorm.io/builder" | |||
| ) | |||
| type RoleOperation struct { | |||
| RoleName string | |||
| @@ -11,8 +14,11 @@ func (RoleOperation) TableName() string { | |||
| return "role_operation" | |||
| } | |||
| func RoleHasOperation(roleName, operation string) (bool, error) { | |||
| num, err := DB.Where("role_name=? and operation=?", roleName, operation).Count(new(RoleOperation)) | |||
| func RoleHasOperation(roles []string, operation string) (bool, error) { | |||
| cond := builder.NewCond() | |||
| cond = cond.And(builder.In("role_name", roles)) | |||
| cond = cond.And(builder.Eq{"operation": operation}) | |||
| num, err := DB.Where(cond).Count(new(RoleOperation)) | |||
| if err != nil { | |||
| logger.Errorf("mysql.error query role_operation fail: %v", err) | |||
| return false, internalServerError | |||
| @@ -15,20 +15,21 @@ import ( | |||
| ) | |||
| type User struct { | |||
| Id int64 `json:"id"` | |||
| Username string `json:"username"` | |||
| Nickname string `json:"nickname"` | |||
| Password string `json:"-"` | |||
| Phone string `json:"phone"` | |||
| Email string `json:"email"` | |||
| Portrait string `json:"portrait"` | |||
| Status int `json:"status"` | |||
| Role string `json:"role"` | |||
| Contacts json.RawMessage `json:"contacts"` //内容为 map[string]string 结构 | |||
| CreateAt int64 `json:"create_at"` | |||
| CreateBy string `json:"create_by"` | |||
| UpdateAt int64 `json:"update_at"` | |||
| UpdateBy string `json:"update_by"` | |||
| Id int64 `json:"id"` | |||
| Username string `json:"username"` | |||
| Nickname string `json:"nickname"` | |||
| Password string `json:"-"` | |||
| Phone string `json:"phone"` | |||
| Email string `json:"email"` | |||
| Portrait string `json:"portrait"` | |||
| Status int `json:"status"` | |||
| RolesForDB string `json:"-" xorm:"'roles'"` // 这个字段写入数据库 | |||
| RolesForFE []string `json:"roles" xorm:"-"` // 这个字段和前端交互 | |||
| Contacts json.RawMessage `json:"contacts"` // 内容为 map[string]string 结构 | |||
| CreateAt int64 `json:"create_at"` | |||
| CreateBy string `json:"create_by"` | |||
| UpdateAt int64 `json:"update_at"` | |||
| UpdateBy string `json:"update_by"` | |||
| } | |||
| func (u *User) TableName() string { | |||
| @@ -110,16 +111,16 @@ func InitRoot() { | |||
| now := time.Now().Unix() | |||
| u = User{ | |||
| Username: "root", | |||
| Password: pass, | |||
| Nickname: "超管", | |||
| Portrait: "", | |||
| Role: "Admin", | |||
| Contacts: []byte("{}"), | |||
| CreateAt: now, | |||
| UpdateAt: now, | |||
| CreateBy: "system", | |||
| UpdateBy: "system", | |||
| Username: "root", | |||
| Password: pass, | |||
| Nickname: "超管", | |||
| Portrait: "", | |||
| RolesForDB: "Admin", | |||
| Contacts: []byte("{}"), | |||
| CreateAt: now, | |||
| UpdateAt: now, | |||
| CreateBy: "system", | |||
| UpdateBy: "system", | |||
| } | |||
| _, err = DB.Insert(u) | |||
| @@ -151,6 +152,8 @@ func UserGet(where string, args ...interface{}) (*User, error) { | |||
| return nil, nil | |||
| } | |||
| obj.RolesForFE = strings.Fields(obj.RolesForDB) | |||
| return &obj, nil | |||
| } | |||
| @@ -188,6 +191,10 @@ func UserGets(query string, limit, offset int) ([]User, error) { | |||
| return []User{}, nil | |||
| } | |||
| for i := 0; i < len(users); i++ { | |||
| users[i].RolesForFE = strings.Fields(users[i].RolesForDB) | |||
| } | |||
| return users, nil | |||
| } | |||
| @@ -204,6 +211,10 @@ func UserGetAll() ([]User, error) { | |||
| return []User{}, nil | |||
| } | |||
| for i := 0; i < len(users); i++ { | |||
| users[i].RolesForFE = strings.Fields(users[i].RolesForDB) | |||
| } | |||
| return users, nil | |||
| } | |||
| @@ -223,23 +234,31 @@ func UserGetsByIds(ids []int64) ([]User, error) { | |||
| return []User{}, nil | |||
| } | |||
| for i := 0; i < len(users); i++ { | |||
| users[i].RolesForFE = strings.Fields(users[i].RolesForDB) | |||
| } | |||
| return users, nil | |||
| } | |||
| func UserGetsByIdsStr(ids []string) ([]User, error) { | |||
| var objs []User | |||
| var users []User | |||
| err := DB.Where("id in (" + strings.Join(ids, ",") + ")").Find(&objs) | |||
| err := DB.Where("id in (" + strings.Join(ids, ",") + ")").Find(&users) | |||
| if err != nil { | |||
| logger.Errorf("mysql.error: UserGetsByIds fail: %v", err) | |||
| return nil, internalServerError | |||
| } | |||
| if len(objs) == 0 { | |||
| if len(users) == 0 { | |||
| return []User{}, nil | |||
| } | |||
| return objs, nil | |||
| for i := 0; i < len(users); i++ { | |||
| users[i].RolesForFE = strings.Fields(users[i].RolesForDB) | |||
| } | |||
| return users, nil | |||
| } | |||
| func PassLogin(username, pass string) (*User, error) { | |||
| @@ -312,7 +331,8 @@ func LdapLogin(username, pass string) (*User, error) { | |||
| user.Password = "******" | |||
| user.Portrait = "/img/linux.jpeg" | |||
| user.Role = "Standard" | |||
| user.RolesForDB = "Standard" | |||
| user.RolesForFE = []string{"Standard"} | |||
| user.Contacts = []byte("{}") | |||
| user.CreateAt = now | |||
| user.UpdateAt = now | |||
| @@ -474,8 +494,11 @@ func (u *User) MyUserGroups() ([]UserGroup, error) { | |||
| func (u *User) CanModifyUserGroup(ug *UserGroup) (bool, error) { | |||
| // 我是管理员,自然可以 | |||
| if u.Role == "Admin" { | |||
| return true, nil | |||
| roles := strings.Fields(u.RolesForDB) | |||
| for i := 0; i < len(roles); i++ { | |||
| if roles[i] == "Admin" { | |||
| return true, nil | |||
| } | |||
| } | |||
| // 我是创建者,自然可以 | |||
| @@ -493,11 +516,14 @@ func (u *User) CanModifyUserGroup(ug *UserGroup) (bool, error) { | |||
| } | |||
| func (u *User) CanDo(op string) (bool, error) { | |||
| if u.Role == "Admin" { | |||
| return true, nil | |||
| roles := strings.Fields(u.RolesForDB) | |||
| for i := 0; i < len(roles); i++ { | |||
| if roles[i] == "Admin" { | |||
| return true, nil | |||
| } | |||
| } | |||
| return RoleHasOperation(u.Role, op) | |||
| return RoleHasOperation(roles, op) | |||
| } | |||
| // MustPerm return *User for link program | |||
| @@ -333,12 +333,12 @@ insert into metric_description(metric, description) values('system_cpu_num_cores | |||
| insert into metric_description(metric, description) values('system_cpu_steal', '等待处理其他虚拟核的时间占比(单位:%)'); | |||
| insert into metric_description(metric, description) values('system_cpu_system', '内核态CPU时间占比(单位:%)'); | |||
| insert into metric_description(metric, description) values('system_cpu_user', '用户态CPU时间占比(单位:%)'); | |||
| insert into metric_description(metric, description) values('system_disk_bytes_free', '磁盘某分区余量大小(单位:kb)'); | |||
| insert into metric_description(metric, description) values('system_disk_bytes_free', '磁盘某分区余量大小(单位:byte)'); | |||
| insert into metric_description(metric, description) values('system_disk_used_percent', '磁盘某分区用量占比(单位:%)'); | |||
| insert into metric_description(metric, description) values('system_disk_read_time', '设备读操作耗时(单位:ms)'); | |||
| insert into metric_description(metric, description) values('system_disk_read_time_percent', '读取磁盘时间百分比(单位:%)'); | |||
| insert into metric_description(metric, description) values('system_disk_bytes_total', '磁盘某分区总量(单位:kb)'); | |||
| insert into metric_description(metric, description) values('system_disk_bytes_used', '磁盘某分区用量大小(单位:kb)'); | |||
| insert into metric_description(metric, description) values('system_disk_bytes_total', '磁盘某分区总量(单位:byte)'); | |||
| insert into metric_description(metric, description) values('system_disk_bytes_used', '磁盘某分区用量大小(单位:byte)'); | |||
| insert into metric_description(metric, description) values('system_disk_write_time', '设备写操作耗时(单位:ms)'); | |||
| insert into metric_description(metric, description) values('system_disk_write_time_percent', ''); | |||
| insert into metric_description(metric, description) values('system_files_allocated', '系统已分配文件句柄数'); | |||
| @@ -31,6 +31,11 @@ type DataQueryParam struct { | |||
| Limit int `json:"limit"` | |||
| Start int64 `json:"start"` | |||
| End int64 `json:"end"` | |||
| Step int64 `json:"step"` | |||
| } | |||
| type DataQueryInstantParam struct { | |||
| PromeQl string `json:"prome_ql"` | |||
| } | |||
| type DataQueryParamOne struct { | |||
| @@ -59,6 +64,11 @@ type DataQueryResp struct { | |||
| PNum int `json:"pNum"` | |||
| } | |||
| type DataQueryInstanceResp struct { | |||
| Metric map[string]interface{} `json:"metric"` | |||
| Value []float64 `json:"value"` | |||
| } | |||
| type DataQL struct { | |||
| Start int64 `json:"start"` | |||
| End int64 `json:"end"` | |||