├── Gopkg.lock
├── Gopkg.toml
├── Gruntfile.js
├── LICENSE
├── Makefile
├── README.md
├── README_CN.md
├── dist
├── README.md
├── aliyun-log-plugin_darwin_amd64
├── aliyun-log-plugin_linux_amd64
├── aliyun-log-plugin_windows_amd64.exe
├── css
│ └── query-editor.css
├── datasource.js
├── datasource.js.map
├── datasource.ts
├── img
│ └── sls_logo.jpg
├── module.js
├── module.js.map
├── module.ts
├── partials
│ ├── annotations.editor.html
│ ├── config.html
│ ├── query.editor.html
│ └── query.options.html
├── plugin.json
├── query_ctrl.js
├── query_ctrl.js.map
└── query_ctrl.ts
├── img
├── demo1.png
├── demo2.png
├── demo3.png
└── demo4.png
├── jest.config.js
├── package-lock.json
├── package.json
├── pkg
├── datasource.go
├── models.go
└── plugin.go
├── spec
└── datasource.test.ts
├── src
├── css
│ └── query-editor.css
├── datasource.js
├── datasource.js.map
├── datasource.ts
├── img
│ └── sls_logo.jpg
├── module.js
├── module.js.map
├── module.ts
├── partials
│ ├── annotations.editor.html
│ ├── config.html
│ ├── query.editor.html
│ └── query.options.html
├── plugin.json
├── query_ctrl.js
├── query_ctrl.js.map
└── query_ctrl.ts
├── tsconfig.json
├── webpack
├── webpack.base.conf.js
├── webpack.dev.conf.js
└── webpack.prod.conf.js
└── yarn.lock
/Gopkg.lock:
--------------------------------------------------------------------------------
1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
2 |
3 |
4 | [[projects]]
5 | digest = "1:33b18e256b423958d757d255db2da1f6f35eb6e236e5e458a762ea91cbf28b81"
6 | name = "github.com/aliyun/aliyun-log-go-sdk"
7 | packages = ["."]
8 | pruneopts = ""
9 | revision = "d3a95d13616a4e6bd230af106d7abfb2f68a0aab"
10 | version = "v0.1.5"
11 |
12 | [[projects]]
13 | digest = "1:8561fc011218ed9821f6d73057154e692cdfd0f37e929ef6c4a574b6299136cb"
14 | name = "github.com/cenkalti/backoff"
15 | packages = ["."]
16 | pruneopts = ""
17 | revision = "4b4cebaf850ec58f1bb1fec5bdebdf8501c2bc3f"
18 | version = "v3.0.0"
19 |
20 | [[projects]]
21 | digest = "1:d69d2ba23955582a64e367ff2b0808cdbd048458c178cea48f11ab8c40bd7aea"
22 | name = "github.com/gogo/protobuf"
23 | packages = [
24 | "gogoproto",
25 | "proto",
26 | "protoc-gen-gogo/descriptor",
27 | ]
28 | pruneopts = ""
29 | revision = "5628607bb4c51c3157aacc3a50f0ab707582b805"
30 | version = "v1.3.1"
31 |
32 | [[projects]]
33 | branch = "master"
34 | digest = "1:107b233e45174dbab5b1324201d092ea9448e58243ab9f039e4c0f332e121e3a"
35 | name = "github.com/golang/glog"
36 | packages = ["."]
37 | pruneopts = ""
38 | revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998"
39 |
40 | [[projects]]
41 | branch = "master"
42 | digest = "1:4d197686fcd627da0b9d76901203272d24182ed4921342d9911e124777b3f5a2"
43 | name = "github.com/golang/protobuf"
44 | packages = [
45 | "proto",
46 | "ptypes",
47 | "ptypes/any",
48 | "ptypes/duration",
49 | "ptypes/timestamp",
50 | ]
51 | pruneopts = ""
52 | revision = "e91709a02e0e8ff8b86b7aa913fdc9ae9498e825"
53 |
54 | [[projects]]
55 | branch = "master"
56 | digest = "1:73460e6eaa31824af444a895e5858175883c73b88acc8df767a115b78b578ddb"
57 | name = "github.com/grafana/grafana_plugin_model"
58 | packages = ["go/datasource"]
59 | pruneopts = ""
60 | revision = "84176c64269d8060f99e750ee8aba6f062753336"
61 |
62 | [[projects]]
63 | digest = "1:01601b6de1f04283f90b2a4fd923a9393b519a78f878010a90c36c4417f74e35"
64 | name = "github.com/hashicorp/go-hclog"
65 | packages = ["."]
66 | pruneopts = ""
67 | revision = "6907afbebd2eef854f0be9194eb79b0ba75d7b29"
68 | version = "v0.8.0"
69 |
70 | [[projects]]
71 | digest = "1:de20979176f5f326a028fd0d3698f4ec18f6921b46c9d68a35200355c6e8e6b9"
72 | name = "github.com/hashicorp/go-plugin"
73 | packages = ["."]
74 | pruneopts = ""
75 | revision = "e8d22c780116115ae5624720c9af0c97afe4f551"
76 |
77 | [[projects]]
78 | branch = "master"
79 | digest = "1:f9a62feb8295380942889460d0008f9fc34c4ad288821efb2ec15168a91b3404"
80 | name = "github.com/hashicorp/yamux"
81 | packages = ["."]
82 | pruneopts = ""
83 | revision = "2f1d1f20f75d5404f53b9edf6b53ed5505508675"
84 |
85 | [[projects]]
86 | digest = "1:9adf43f9a17af07a6d587e3b493e2111ad8e07283d5cd58e44e70d23bf6dc644"
87 | name = "github.com/mitchellh/go-testing-interface"
88 | packages = ["."]
89 | pruneopts = ""
90 | revision = "6d0b8010fcc857872e42fc6c931227569016843c"
91 | version = "v1.0.0"
92 |
93 | [[projects]]
94 | digest = "1:94e9081cc450d2cdf4e6886fc2c06c07272f86477df2d74ee5931951fa3d2577"
95 | name = "github.com/oklog/run"
96 | packages = ["."]
97 | pruneopts = ""
98 | revision = "4dadeb3030eda0273a12382bb2348ffc7c9d1a39"
99 | version = "v1.0.0"
100 |
101 | [[projects]]
102 | digest = "1:f5c875bba9e42adaba2627ec78961b655f747d885615beb1b01e40318ada65ea"
103 | name = "github.com/pierrec/lz4"
104 | packages = [
105 | ".",
106 | "internal/xxh32",
107 | ]
108 | pruneopts = ""
109 | revision = "9085dacd1e1eca033047a5514195779360363ced"
110 | version = "2.4.0"
111 |
112 | [[projects]]
113 | digest = "1:1d7e1867c49a6dd9856598ef7c3123604ea3daabf5b83f303ff457bcbc410b1d"
114 | name = "github.com/pkg/errors"
115 | packages = ["."]
116 | pruneopts = ""
117 | revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4"
118 | version = "v0.8.1"
119 |
120 | [[projects]]
121 | branch = "master"
122 | digest = "1:09972eaa1645553c1cf5b0d2b471aa3aef8d9ab88ca45528e131cd32e8572fb9"
123 | name = "golang.org/x/net"
124 | packages = [
125 | "context",
126 | "http/httpguts",
127 | "http2",
128 | "http2/hpack",
129 | "idna",
130 | "internal/timeseries",
131 | "trace",
132 | ]
133 | pruneopts = ""
134 | revision = "eb5bcb51f2a31c7d5141d810b70815c05d9c9146"
135 |
136 | [[projects]]
137 | branch = "master"
138 | digest = "1:55c52474bb389797ed66db92966e2b9ddc98a25d9d05c8aa55787fe03d4d4084"
139 | name = "golang.org/x/sys"
140 | packages = ["unix"]
141 | pruneopts = ""
142 | revision = "4b34438f7a67ee5f45cc6132e2bad873a20324e9"
143 |
144 | [[projects]]
145 | digest = "1:5acd3512b047305d49e8763eef7ba423901e85d5dd2fd1e71778a0ea8de10bd4"
146 | name = "golang.org/x/text"
147 | packages = [
148 | "collate",
149 | "collate/build",
150 | "internal/colltab",
151 | "internal/gen",
152 | "internal/tag",
153 | "internal/triegen",
154 | "internal/ucd",
155 | "language",
156 | "secure/bidirule",
157 | "transform",
158 | "unicode/bidi",
159 | "unicode/cldr",
160 | "unicode/norm",
161 | "unicode/rangetable",
162 | ]
163 | pruneopts = ""
164 | revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
165 | version = "v0.3.0"
166 |
167 | [[projects]]
168 | branch = "master"
169 | digest = "1:6cbc03e8d4c5724d6228c88f1402d8cbd0a515561f73f0cecbb72f3d6576ff28"
170 | name = "google.golang.org/genproto"
171 | packages = ["googleapis/rpc/status"]
172 | pruneopts = ""
173 | revision = "64821d5d210748c883cd2b809589555ae4654203"
174 |
175 | [[projects]]
176 | digest = "1:c4e8733914b7b1b535988fb5d5bd3de60845fb4553227b7a1ce1b7180204e462"
177 | name = "google.golang.org/grpc"
178 | packages = [
179 | ".",
180 | "balancer",
181 | "balancer/base",
182 | "balancer/roundrobin",
183 | "binarylog/grpc_binarylog_v1",
184 | "codes",
185 | "connectivity",
186 | "credentials",
187 | "credentials/internal",
188 | "encoding",
189 | "encoding/proto",
190 | "grpclog",
191 | "health",
192 | "health/grpc_health_v1",
193 | "internal",
194 | "internal/backoff",
195 | "internal/balancerload",
196 | "internal/binarylog",
197 | "internal/channelz",
198 | "internal/envconfig",
199 | "internal/grpcrand",
200 | "internal/grpcsync",
201 | "internal/syscall",
202 | "internal/transport",
203 | "keepalive",
204 | "metadata",
205 | "naming",
206 | "peer",
207 | "resolver",
208 | "resolver/dns",
209 | "resolver/passthrough",
210 | "stats",
211 | "status",
212 | "tap",
213 | ]
214 | pruneopts = ""
215 | revision = "236199dd5f8031d698fb64091194aecd1c3895b2"
216 | version = "v1.20.0"
217 |
218 | [solve-meta]
219 | analyzer-name = "dep"
220 | analyzer-version = 1
221 | input-imports = [
222 | "github.com/aliyun/aliyun-log-go-sdk",
223 | "github.com/grafana/grafana_plugin_model/go/datasource",
224 | "github.com/hashicorp/go-hclog",
225 | "github.com/hashicorp/go-plugin",
226 | "golang.org/x/net/context",
227 | ]
228 | solver-name = "gps-cdcl"
229 | solver-version = 1
230 |
--------------------------------------------------------------------------------
/Gopkg.toml:
--------------------------------------------------------------------------------
1 |
2 | # Gopkg.toml example
3 | #
4 | # Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
5 | # for detailed Gopkg.toml documentation.
6 | #
7 | # required = ["github.com/user/thing/cmd/thing"]
8 | # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
9 | #
10 | # [[constraint]]
11 | # name = "github.com/user/project"
12 | # version = "1.0.0"
13 | #
14 | # [[constraint]]
15 | # name = "github.com/user/project2"
16 | # branch = "dev"
17 | # source = "github.com/myfork/project2"
18 | #
19 | # [[override]]
20 | # name = "github.com/x/y"
21 | # version = "2.4.0"
22 |
23 |
24 | [[constraint]]
25 | name = "github.com/bitly/go-simplejson"
26 | version = "0.5.0"
27 |
28 | [[constraint]]
29 | branch = "master"
30 | name = "github.com/grafana/grafana_plugin_model"
31 |
32 | [[constraint]]
33 | branch = "master"
34 | name = "golang.org/x/net"
35 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function(grunt) {
2 |
3 | require('load-grunt-tasks')(grunt);
4 |
5 | grunt.loadNpmTasks('grunt-execute');
6 | grunt.loadNpmTasks('grunt-contrib-clean');
7 |
8 | grunt.initConfig({
9 |
10 | clean: ["dist"],
11 |
12 | copy: {
13 | src_to_dist: {
14 | cwd: 'src',
15 | expand: true,
16 | src: ['**/*', '!**/*.js', '!**/*.scss'],
17 | dest: 'dist'
18 | },
19 | pluginDef: {
20 | expand: true,
21 | src: ['README.md'],
22 | dest: 'dist'
23 | }
24 | },
25 |
26 | watch: {
27 | rebuild_all: {
28 | files: ['src/**/*'],
29 | tasks: ['default'],
30 | options: {spawn: false}
31 | }
32 | },
33 |
34 | babel: {
35 | options: {
36 | sourceMap: true,
37 | presets: ['es2015']
38 | },
39 | dist: {
40 | options: {
41 | plugins: ['transform-es2015-modules-systemjs', 'transform-es2015-for-of']
42 | },
43 | files: [{
44 | cwd: 'src',
45 | expand: true,
46 | src: ['**/*.js'],
47 | dest: 'dist',
48 | ext:'.js'
49 | }]
50 | },
51 | distTestNoSystemJs: {
52 | files: [{
53 | cwd: 'src',
54 | expand: true,
55 | src: ['**/*.js'],
56 | dest: 'dist/test',
57 | ext:'.js'
58 | }]
59 | },
60 | distTestsSpecsNoSystemJs: {
61 | files: [{
62 | expand: true,
63 | cwd: 'spec',
64 | src: ['**/*.js'],
65 | dest: 'dist/test/spec',
66 | ext:'.js'
67 | }]
68 | }
69 | },
70 |
71 | mochaTest: {
72 | test: {
73 | options: {
74 | reporter: 'spec'
75 | },
76 | src: ['dist/test/spec/test-main.js', 'dist/test/spec/*_spec.js']
77 | }
78 | }
79 | });
80 |
81 | //grunt.registerTask('default', ['clean', 'copy:src_to_dist', 'copy:pluginDef', 'babel', 'mochaTest']);
82 | grunt.registerTask('default', ['clean', 'copy:src_to_dist', 'copy:pluginDef', 'babel']);
83 | };
84 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Grafana
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | all: grunt build
2 |
3 | grunt:
4 | grunt
5 |
6 | build:
7 | go build -i -o ./dist/aliyun-log-plugin_darwin_amd64 ./pkg
8 | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -i -o ./dist/aliyun-log-plugin_linux_amd64 ./pkg
9 | CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -i -o ./dist/aliyun-log-plugin_windows_amd64.exe ./pkg
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Aliyun log service Datasource
2 |
3 |
4 | More documentation about datasource plugins can be found in the [Docs](https://github.com/grafana/grafana/blob/master/docs/sources/plugins/developing/datasources.md).
5 |
6 |
7 | [**中文文档**](README_CN.md)
8 |
9 |
10 | ## Install
11 |
12 |
13 | Clone this project into grafana plugin directory , then restart grafana.
14 |
15 | In mac the plugin directory is /usr/local/var/lib/grafana/plugins.
16 |
17 | After install the plugin ,restart grafana
18 |
19 | ```
20 | brew services start grafana
21 | ```
22 |
23 | ## Add datasource
24 |
25 | In datasource management panel, add a datasource with the type "LogService".
26 |
27 | In Http settings, set Url = http://${log\_service\_endpoint} . e.g. Your projectName is accesslog in qingdao region, then the url is http://cn-qingdao.log.aliyuncs.com.
28 |
29 | Access : select `Server(Default)`
30 |
31 | log service details:
32 |
33 | set Project and logstore
34 |
35 | AccessId and AccessKey : it is better to use a sub user accessId and accessKey.
36 |
37 | To ensure data security, AK is saved and cleared without echo
38 |
39 |
40 | ## Add dashboard
41 |
42 |
43 | Add a panel, in the datasource option, choose the log service datasource that is just created.
44 |
45 | In the query : insert your query , e.g.
46 |
47 | ```
48 | *|select count(1) as c,count(1)/2 as c1, __time__- __time__%60 as t group by t limit 10000
49 | ```
50 |
51 | The X column ,insert t (**Second timestamp**)
52 |
53 | The Y column , insert c,c1 (**Multiple columns are separated by commas**)
54 |
55 | Save the dashboard
56 |
57 | ## Usage
58 |
59 | ### Variables
60 |
61 | In the top right corner of the dashboard panel, click dashboard Settings and select Variables.
62 |
63 | Reference variables `$VariableName`
64 |
65 | ### Flow graph
66 |
67 | The X-axis is set to the time column
68 |
69 | The Y-axis is set to the format `col1#:#col2`, where col1 is the aggregate column and col2 is the other columns
70 |
71 | The Query sample is set to
72 | ```
73 | * | select to_unixtime(time) as time,status,count from (select time_series(__time__, '1m', '%Y-%m-%d %H:%i', '0') as time,status,count(*) as count from log group by status,time order by time limit 10000)
74 | ```
75 |
76 | 
77 |
78 | ### Pie
79 |
80 | The X-axis is set to `pie`
81 |
82 | The Y-axis is set to categories and numeric columns (example `method,pv`)
83 |
84 | The Query sample is set to
85 | ```
86 | $hostname | select count(1) as pv ,method group by method
87 | ```
88 |
89 | 
90 |
91 | ### Table
92 |
93 | The X-axis is set to `table` or null
94 |
95 | The Y-axis is set to columns
96 |
97 | ### World map penel
98 |
99 | The X-axis is set to `map`
100 |
101 | The Y-axis is set to `country,geo,pv`
102 |
103 | The Query sample is set to
104 | ```
105 | * | select count(1) as pv ,geohash(ip_to_geo(arbitrary(remote_addr))) as geo,ip_to_country(remote_addr) as country from log group by country having geo <>'' limit 1000
106 | ```
107 |
108 | Location Data : `geohash`
109 |
110 | Location Name Field : `country`
111 |
112 | Geo_point/Geohash Field :" `geo`
113 |
114 | Metric Field : `pv`
115 |
116 | The query:
117 |
118 | 
119 |
120 | Parameter Settings:
121 |
122 | 
123 |
124 | ### Alert
125 |
126 | #### Mode of notification
127 |
128 | In the alert notification panel, select New channel to add
129 |
130 | #### Alert
131 |
132 | **Attention** :Dashboard alert only, not plug-in alert
133 |
134 | A sample of:
135 |
136 | 
137 |
138 | Add the alert panel:
139 |
140 | 
141 |
142 | - The red line on the chart represents the set threshold. Click on the right side and drag it up and down.
143 | - Evaluate every `1m` for `5m`, Is the result calculated every minute, and the threshold is exceeded for five consecutive minutes.
144 | - After setting for, if the state exceeds the threshold value and changes from Ok to Pending, the alarm will not be triggered. After continuously exceeding the threshold value for a period of time, the alarm will be sent. If the state changes from Pending to Alerting, the alarm will only be notified once.
145 | - WHEN `avg ()` OF `query (B, 5m, now)` IS ABOVE `89`, That means line B has an average of more than 89 alarms in the last five minutes.
146 | - Add notification mode and notification information under Notifications.
147 |
148 |
149 | ## Contributors
150 |
151 | [@WPH95](https://github.com/WPH95) made a great contribution to this project.
152 |
153 | Thanks for the excellent work by [@WPH95](https://github.com/WPH95).
154 |
--------------------------------------------------------------------------------
/README_CN.md:
--------------------------------------------------------------------------------
1 | ## 阿里云日志服务数据源
2 |
3 |
4 | ## 安装
5 |
6 |
7 | 克隆本项目到grafana插件目录下 , 然后重启grafana
8 |
9 | 在 mac 插件目录是 /usr/local/var/lib/grafana/plugins
10 |
11 | 重启命令为
12 |
13 | ```
14 | brew services restart grafana
15 | ```
16 |
17 | ## 添加数据源
18 |
19 | 在数据源管理面板, 添加 `LogService` 数据源
20 |
21 | 在 settings 面板, 设置 Url 为您日志服务 project 的 endpoint ( endpoint 在 project 的概览页可以看到).
22 |
23 | 例如你的 project 在 qingdao region, Url 可以填 `http://cn-qingdao.log.aliyuncs.com`
24 |
25 | Access 设置为 `Server(Default)`
26 |
27 |
28 | 设置 Project 和 logstore
29 |
30 | 设置 AccessId 和 AccessKeySecret , 最好配置为子账号的AK
31 |
32 | 为保证数据安全 , AK保存后清空 , 且不会回显
33 |
34 |
35 | ## 添加仪表盘
36 |
37 |
38 | 添加一个面板, 在 datasource 选项, 选择刚创建的日志服务数据源.
39 |
40 | 在 query 输入查询语句, 查询语法与日志服务控制台相同.
41 |
42 | ```
43 | *|select count(1) as c,count(1)/2 as c1, __time__- __time__%60 as t group by t limit 10000
44 | ```
45 |
46 | X轴设置为`t` (**秒级时间戳**)
47 |
48 | Y轴设置为`c,c1` (**多列用逗号分隔**)
49 |
50 | 保存仪表盘
51 |
52 | ## 使用
53 |
54 | ### 设置变量
55 |
56 | 在 dashboard 面板右上角点击 Dashboard settings, 选择 Variables
57 |
58 | 引用变量 `$VariableName`
59 |
60 | ### 设置流图
61 |
62 | X轴 设置为时间列
63 |
64 | Y轴 设置为 `col1#:#col2` 这种格式, 其中 col1 为 聚合列, col2 为其他列
65 |
66 | Query 设置样例为
67 | ```
68 | * | select to_unixtime(time) as time,status,count from (select time_series(__time__, '1m', '%Y-%m-%d %H:%i', '0') as time,status,count(*) as count from log group by status,time order by time limit 10000)
69 | ```
70 |
71 | 
72 |
73 | ### 设置饼图
74 |
75 | X轴 设置为`pie`
76 |
77 | Y轴 设置为类别和数字列 (样例为`method,pv`)
78 |
79 | Query 设置样例为
80 | ```
81 | $hostname | select count(1) as pv ,method group by method
82 | ```
83 |
84 | 
85 |
86 | ### 设置表格
87 |
88 | X轴 设置为`table` 或空
89 |
90 | Y轴 设置为列
91 |
92 | ### 设置地图
93 |
94 | X轴 设置为`map`
95 |
96 | Y轴 设置为 `country,geo,pv`
97 |
98 | Query 设置样例为
99 | ```
100 | * | select count(1) as pv ,geohash(ip_to_geo(arbitrary(remote_addr))) as geo,ip_to_country(remote_addr) as country from log group by country having geo <>'' limit 1000
101 | ```
102 |
103 | Location Data 设置为 `geohash`
104 |
105 | Location Name Field 设置为 `country`
106 |
107 | geo_point/geohash Field 设置为 `geo`
108 |
109 | Metric Field 设置为 `pv`
110 |
111 | 查询语句:
112 |
113 | 
114 |
115 | 参数设置:
116 |
117 | 
118 |
119 | ### 设置告警
120 |
121 | #### 通知方式
122 |
123 | 在告警通知方式面板, 选择 New channel 添加
124 |
125 | **注意** :选择dingding告警, 在钉钉机器人的安全设置里选自定义关键词, 添加 `Alerting`
126 |
127 | #### 添加告警
128 |
129 | **注意** :只支持dashboard告警, 不支持插件告警
130 |
131 | 样例如下:
132 |
133 | 
134 |
135 | 添加告警面板:
136 |
137 | 
138 |
139 | - 其中图表上红线代表设置的阈值, 点击右侧可以上下拖动
140 | - Evaluate every `1m` for `5m`, 代表计算每分钟的结果, 连续五分钟超过阈值告警
141 | - 设置for后, 如果超过阈值状态由Ok转为Pending, 不会触发告警, 连续超过阈值一段时候后发送告警, 状态由Pending转为Alerting, 告警只会通知一次
142 | - WHEN `avg ()` OF `query (B, 5m, now)` IS ABOVE `89`, 代表线条B最近五分钟的均值超过89告警
143 | - 在Notifications下添加通知方式及通知信息
144 |
145 |
146 |
147 |
--------------------------------------------------------------------------------
/dist/README.md:
--------------------------------------------------------------------------------
1 | ## Aliyun log service Datasource
2 |
3 |
4 | More documentation about datasource plugins can be found in the [Docs](https://github.com/grafana/grafana/blob/master/docs/sources/plugins/developing/datasources.md).
5 |
6 |
7 | [**中文文档**](README_CN.md)
8 |
9 |
10 | ## Install
11 |
12 |
13 | Clone this project into grafana plugin directory , then restart grafana.
14 |
15 | In mac the plugin directory is /usr/local/var/lib/grafana/plugins.
16 |
17 | After install the plugin ,restart grafana
18 |
19 | ```
20 | brew services start grafana
21 | ```
22 |
23 | ## Add datasource
24 |
25 | In datasource management panel, add a datasource with the type "LogService".
26 |
27 | In Http settings, set Url = http://${log\_service\_endpoint} . e.g. Your projectName is accesslog in qingdao region, then the url is http://cn-qingdao.log.aliyuncs.com.
28 |
29 | Access : select `Server(Default)`
30 |
31 | log service details:
32 |
33 | set Project and logstore
34 |
35 | AccessId and AccessKey : it is better to use a sub user accessId and accessKey.
36 |
37 |
38 | ## Add dashboard
39 |
40 |
41 | Add a panel, in the datasource option, choose the log service datasource that is just created.
42 |
43 | In the query : insert your query , e.g.
44 |
45 | ```
46 | *|select count(1) as c,count(1)/2 as c1, __time__- __time__%60 as t group by t limit 10000
47 | ```
48 |
49 | The X column ,insert t (**Second timestamp**)
50 |
51 | The Y column , insert c,c1 (**Multiple columns are separated by commas**)
52 |
53 | Save the dashboard
54 |
55 | ## Usage
56 |
57 | ### Variables
58 |
59 | In the top right corner of the dashboard panel, click dashboard Settings and select Variables.
60 |
61 | Reference variables `$VariableName`
62 |
63 | ### Flow graph
64 |
65 | The X-axis is set to the time column
66 |
67 | The Y-axis is set to the format `col1#:#col2`, where col1 is the aggregate column and col2 is the other columns
68 |
69 | The Query sample is set to
70 | ```
71 | * | select to_unixtime(time) as time,status,count from (select time_series(__time__, '1m', '%Y-%m-%d %H:%i', '0') as time,status,count(*) as count from log group by status,time order by time limit 10000)
72 | ```
73 |
74 | 
75 |
76 | ### Pie
77 |
78 | The X-axis is set to `pie`
79 |
80 | The Y-axis is set to categories and numeric columns (example `method,pv`)
81 |
82 | The Query sample is set to
83 | ```
84 | $hostname | select count(1) as pv ,method group by method
85 | ```
86 |
87 | 
88 |
89 | ### Table
90 |
91 | The X-axis is set to `table` or null
92 |
93 | The Y-axis is set to columns
94 |
95 | ### World map penel
96 |
97 | The X-axis is set to `map`
98 |
99 | The Y-axis is set to `country,geo,pv`
100 |
101 | The Query sample is set to
102 | ```
103 | * | select count(1) as pv ,geohash(ip_to_geo(arbitrary(remote_addr))) as geo,ip_to_country(remote_addr) as country from log group by country having geo <>'' limit 1000
104 | ```
105 |
106 | Location Data : `geohash`
107 |
108 | Location Name Field : `country`
109 |
110 | Geo_point/Geohash Field :" `geo`
111 |
112 | Metric Field : `pv`
113 |
114 | The query:
115 |
116 | 
117 |
118 | Parameter Settings:
119 |
120 | 
121 |
122 | ### Alert
123 |
124 | #### Mode of notification
125 |
126 | In the alert notification panel, select New channel to add
127 |
128 | #### Alert
129 |
130 | **Attention** :Dashboard alert only, not plug-in alert
131 |
132 | A sample of:
133 |
134 | 
135 |
136 | Add the alert panel:
137 |
138 | 
139 |
140 | - The red line on the chart represents the set threshold. Click on the right side and drag it up and down.
141 | - Evaluate every `1m` for `5m`, Is the result calculated every minute, and the threshold is exceeded for five consecutive minutes.
142 | - After setting for, if the state exceeds the threshold value and changes from Ok to Pending, the alarm will not be triggered. After continuously exceeding the threshold value for a period of time, the alarm will be sent. If the state changes from Pending to Alerting, the alarm will only be notified once.
143 | - WHEN `avg ()` OF `query (B, 5m, now)` IS ABOVE `89`, That means line B has an average of more than 89 alarms in the last five minutes.
144 | - Add notification mode and notification information under Notifications.
145 |
146 |
147 | ## Contributors
148 |
149 | [@WPH95](https://github.com/WPH95) made a great contribution to this project.
150 |
151 | Thanks for the excellent work by [@WPH95](https://github.com/WPH95).
152 |
--------------------------------------------------------------------------------
/dist/aliyun-log-plugin_darwin_amd64:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mayunlei/aliyun-log-grafana-datasource-plugin/1907f18d10e9e52a47169cf0e0bafb5f8a11f89d/dist/aliyun-log-plugin_darwin_amd64
--------------------------------------------------------------------------------
/dist/aliyun-log-plugin_linux_amd64:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mayunlei/aliyun-log-grafana-datasource-plugin/1907f18d10e9e52a47169cf0e0bafb5f8a11f89d/dist/aliyun-log-plugin_linux_amd64
--------------------------------------------------------------------------------
/dist/aliyun-log-plugin_windows_amd64.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mayunlei/aliyun-log-grafana-datasource-plugin/1907f18d10e9e52a47169cf0e0bafb5f8a11f89d/dist/aliyun-log-plugin_windows_amd64.exe
--------------------------------------------------------------------------------
/dist/css/query-editor.css:
--------------------------------------------------------------------------------
1 | .generic-datasource-query-row .query-keyword {
2 | width: 75px;
3 | }
--------------------------------------------------------------------------------
/dist/datasource.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | System.register(['lodash'], function (_export, _context) {
4 | "use strict";
5 |
6 | var _, _typeof, _createClass, GenericDatasource;
7 |
8 | function _classCallCheck(instance, Constructor) {
9 | if (!(instance instanceof Constructor)) {
10 | throw new TypeError("Cannot call a class as a function");
11 | }
12 | }
13 |
14 | function handleTsdbResponse(response) {
15 | var res = [];
16 | _.forEach(response.data.results, function (r) {
17 | _.forEach(r.series, function (s) {
18 | res.push({ target: s.name, datapoints: s.points });
19 | });
20 | _.forEach(r.tables, function (t) {
21 | t.type = 'table';
22 | t.refId = r.refId;
23 | res.push(t);
24 | });
25 | });
26 | response.data = res;
27 | console.log(res);
28 | return response;
29 | }
30 |
31 | _export('handleTsdbResponse', handleTsdbResponse);
32 |
33 | function mapToTextValue(result) {
34 | return _.map(result, function (d, i) {
35 | if (d && d.text && d.value) {
36 | return { text: d.text, value: d.value };
37 | } else if (_.isObject(d)) {
38 | return { text: d, value: i };
39 | }
40 | return { text: d, value: d };
41 | });
42 | }
43 | //# sourceMappingURL=datasource.js.map
44 |
45 | _export('mapToTextValue', mapToTextValue);
46 |
47 | return {
48 | setters: [function (_lodash) {
49 | _ = _lodash.default;
50 | }],
51 | execute: function () {
52 | _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
53 | return typeof obj;
54 | } : function (obj) {
55 | return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
56 | };
57 |
58 | _createClass = function () {
59 | function defineProperties(target, props) {
60 | for (var i = 0; i < props.length; i++) {
61 | var descriptor = props[i];
62 | descriptor.enumerable = descriptor.enumerable || false;
63 | descriptor.configurable = true;
64 | if ("value" in descriptor) descriptor.writable = true;
65 | Object.defineProperty(target, descriptor.key, descriptor);
66 | }
67 | }
68 |
69 | return function (Constructor, protoProps, staticProps) {
70 | if (protoProps) defineProperties(Constructor.prototype, protoProps);
71 | if (staticProps) defineProperties(Constructor, staticProps);
72 | return Constructor;
73 | };
74 | }();
75 |
76 | _export('GenericDatasource', GenericDatasource = function () {
77 | /** @ngInject */
78 | function GenericDatasource(instanceSettings, backendSrv, templateSrv) {
79 | _classCallCheck(this, GenericDatasource);
80 |
81 | this.backendSrv = backendSrv;
82 | this.templateSrv = templateSrv;
83 | this.type = instanceSettings.type;
84 | this.url = instanceSettings.url;
85 | this.name = instanceSettings.name;
86 | this.id = instanceSettings.id;
87 | this.withCredentials = instanceSettings.withCredentials;
88 | this.headers = { 'Content-Type': 'application/json' };
89 | if (typeof instanceSettings.basicAuth === 'string' && instanceSettings.basicAuth.length > 0) {
90 | this.headers['Authorization'] = instanceSettings.basicAuth;
91 | }
92 | }
93 |
94 | _createClass(GenericDatasource, [{
95 | key: 'query',
96 | value: function query(options) {
97 | var query = this.buildQueryParameters(options);
98 | query.targets = query.targets.filter(function (t) {
99 | return !t.hide;
100 | });
101 | if (query.targets.length <= 0) {
102 | return Promise.resolve({ data: [] });
103 | }
104 | return this.doTsdbRequest(query).then(handleTsdbResponse);
105 | }
106 | }, {
107 | key: 'testDatasource',
108 | value: function testDatasource() {
109 | var to = new Date().getTime();
110 | var from = to - 5000;
111 | var str = '{"requestId":"Q100","timezone":"","range":{"from":"' + from + '","to":"' + to + '"},' + '"targets":[{"queryType":"query","target":"count","refId":"A","type":"timeserie","datasourceId":' + this.id + ',' + '"query":"* | select count(*) as count","ycol":"count"}]}';
112 | var query = JSON.parse(str);
113 | return this.doTsdbRequest(query).then(function (response) {
114 | if (response.status === 200) {
115 | return { status: "success", message: "Data source is working", title: "Success" };
116 | } else {
117 | return { status: "failed", message: "Data source is not working", title: "Error" };
118 | }
119 | }).catch(function () {
120 | return { status: "failed", message: "Data source is not working", title: "Error" };
121 | });
122 | }
123 | }, {
124 | key: 'annotationQuery',
125 | value: function annotationQuery(options) {
126 | var query = this.templateSrv.replace(options.annotation.query, {}, 'glob');
127 | var annotationQuery = {
128 | range: options.range,
129 | annotation: {
130 | name: options.annotation.name,
131 | datasource: options.annotation.datasource,
132 | enable: options.annotation.enable,
133 | iconColor: options.annotation.iconColor,
134 | query: query
135 | },
136 | rangeRaw: options.rangeRaw
137 | };
138 | return this.doRequest({
139 | url: this.url + '/annotations',
140 | method: 'POST',
141 | data: annotationQuery
142 | }).then(function (result) {
143 | return result.data;
144 | });
145 | }
146 | }, {
147 | key: 'metricFindQuery',
148 | value: function metricFindQuery(q) {
149 | q = this.templateSrv.replace(q, {}, 'glob');
150 | var to = this.templateSrv.timeRange.to.unix() * 1000;
151 | var from = this.templateSrv.timeRange.from.unix() * 1000;
152 | var str = '{"requestId":"Q100","timezone":"","range":{"from":"' + from + '","to":"' + to + '"},' + '"targets":[{"queryType":"query","target":"query","refId":"A","type":"timeserie","datasourceId":' + this.id + ',' + '"query":"' + q + '"}]}';
153 | var query = JSON.parse(str);
154 | return this.doTsdbRequest(query).then(function (response) {
155 | var res = handleTsdbResponse(response);
156 | if (res && res.data && res.data.length) {
157 | var rows = res.data[0].rows;
158 | rows = rows.map(function (item) {
159 | return item[0];
160 | });
161 | return rows;
162 | } else {
163 | return [];
164 | }
165 | }).then(mapToTextValue);
166 | }
167 | }, {
168 | key: 'doRequest',
169 | value: function doRequest(options) {
170 | options.withCredentials = this.withCredentials;
171 | options.headers = this.headers;
172 | return this.backendSrv.datasourceRequest(options);
173 | }
174 | }, {
175 | key: 'doTsdbRequest',
176 | value: function doTsdbRequest(options) {
177 | var tsdbRequestData = {
178 | queries: options.targets
179 | };
180 | if (options.range) {
181 | tsdbRequestData.from = options.range.from.valueOf().toString();
182 | tsdbRequestData.to = options.range.to.valueOf().toString();
183 | }
184 | return this.backendSrv.datasourceRequest({
185 | url: '/api/tsdb/query',
186 | method: 'POST',
187 | data: tsdbRequestData
188 | });
189 | }
190 | }, {
191 | key: 'buildQueryParameters',
192 | value: function buildQueryParameters(options) {
193 | var _this = this;
194 |
195 | //remove placeholder targets
196 | options.targets = _.filter(options.targets, function (target) {
197 | return target.target !== 'select metric';
198 | });
199 | options.targets = _.map(options.targets, function (target) {
200 | return {
201 | queryType: 'query',
202 | target: _this.templateSrv.replace(target.target, options.scopedVars, 'regex'),
203 | refId: target.refId,
204 | hide: target.hide,
205 | type: target.type || 'timeserie',
206 | datasourceId: _this.id,
207 | query: _this.replaceQueryParameters(target, options),
208 | xcol: target.xcol,
209 | ycol: target.ycol
210 | };
211 | });
212 | return options;
213 | }
214 | }, {
215 | key: 'replaceQueryParameters',
216 | value: function replaceQueryParameters(target, options) {
217 | var query = this.templateSrv.replace(target.query, options.scopedVars, function (value, variable) {
218 | if ((typeof value === 'undefined' ? 'undefined' : _typeof(value)) == "object" && (variable.multi || variable.includeAll)) {
219 | var a = [];
220 | value.forEach(function (v) {
221 | if (variable.name == variable.label) a.push('"' + variable.name + '":"' + v + '"');else a.push('"' + v + '"');
222 | });
223 | return a.join(" OR ");
224 | }
225 | if (_.isArray(value)) {
226 | return value.join(' OR ');
227 | }
228 | return value;
229 | });
230 | var re = /\$([0-9]+)([dmhs])/g;
231 | var reArray = query.match(re);
232 | _(reArray).forEach(function (col) {
233 | var old = col;
234 | col = col.replace("$", '');
235 | var sec = 1;
236 | if (col.indexOf("s") != -1) sec = 1;else if (col.indexOf("m") != -1) sec = 60;else if (col.indexOf("h") != -1) sec = 3600;else if (col.indexOf("d") != -1) sec = 3600 * 24;
237 | col = col.replace(/[smhd]/g, '');
238 | var v = parseInt(col);
239 | v = v * sec;
240 | console.log(old, v, col, sec, query);
241 | query = query.replace(old, v);
242 | });
243 | if (query.indexOf("#time_end") != -1) {
244 | query = query.replace("#time_end", parseInt(String(options.range.to._d.getTime() / 1000)));
245 | }
246 | if (query.indexOf("#time_begin") != -1) {
247 | query = query.replace("#time_begin", parseInt(String(options.range.from._d.getTime() / 1000)));
248 | }
249 | return query;
250 | }
251 | }, {
252 | key: 'getTagKeys',
253 | value: function getTagKeys(options) {
254 | var _this2 = this;
255 |
256 | return new Promise(function (resolve) {
257 | _this2.doRequest({
258 | url: _this2.url + '/tag-keys',
259 | method: 'POST',
260 | data: options
261 | }).then(function (result) {
262 | return resolve(result.data);
263 | });
264 | });
265 | }
266 | }, {
267 | key: 'getTagValues',
268 | value: function getTagValues(options) {
269 | var _this3 = this;
270 |
271 | return new Promise(function (resolve) {
272 | _this3.doRequest({
273 | url: _this3.url + '/tag-values',
274 | method: 'POST',
275 | data: options
276 | }).then(function (result) {
277 | return resolve(result.data);
278 | });
279 | });
280 | }
281 | }]);
282 |
283 | return GenericDatasource;
284 | }());
285 |
286 | _export('GenericDatasource', GenericDatasource);
287 | }
288 | };
289 | });
290 | //# sourceMappingURL=datasource.js.map
291 |
--------------------------------------------------------------------------------
/dist/datasource.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../src/datasource.js"],"names":["handleTsdbResponse","response","res","_","forEach","data","results","r","series","push","target","s","name","datapoints","points","tables","t","type","refId","console","log","mapToTextValue","result","map","d","i","text","value","isObject","GenericDatasource","instanceSettings","backendSrv","templateSrv","url","id","withCredentials","headers","basicAuth","length","options","query","buildQueryParameters","targets","filter","hide","Promise","resolve","doTsdbRequest","then","to","Date","getTime","from","str","JSON","parse","status","message","title","catch","replace","annotation","annotationQuery","range","datasource","enable","iconColor","rangeRaw","doRequest","method","q","timeRange","unix","rows","item","datasourceRequest","tsdbRequestData","queries","valueOf","toString","queryType","scopedVars","datasourceId","replaceQueryParameters","xcol","ycol","variable","multi","includeAll","a","v","label","join","isArray","re","reArray","match","col","old","sec","indexOf","parseInt","String","_d"],"mappings":"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","file":"datasource.js","sourcesContent":["import _ from \"lodash\";\nexport class GenericDatasource {\n /** @ngInject */\n constructor(instanceSettings, backendSrv, templateSrv) {\n this.backendSrv = backendSrv;\n this.templateSrv = templateSrv;\n this.type = instanceSettings.type;\n this.url = instanceSettings.url;\n this.name = instanceSettings.name;\n this.id = instanceSettings.id;\n this.withCredentials = instanceSettings.withCredentials;\n this.headers = { 'Content-Type': 'application/json' };\n if (typeof instanceSettings.basicAuth === 'string' && instanceSettings.basicAuth.length > 0) {\n this.headers['Authorization'] = instanceSettings.basicAuth;\n }\n }\n query(options) {\n const query = this.buildQueryParameters(options);\n query.targets = query.targets.filter(t => !t.hide);\n if (query.targets.length <= 0) {\n return Promise.resolve({ data: [] });\n }\n return this.doTsdbRequest(query).then(handleTsdbResponse);\n }\n testDatasource() {\n const to = new Date().getTime();\n const from = to - 5000;\n const str = '{\"requestId\":\"Q100\",\"timezone\":\"\",\"range\":{\"from\":\"' + from + '\",\"to\":\"' + to + '\"},' +\n '\"targets\":[{\"queryType\":\"query\",\"target\":\"count\",\"refId\":\"A\",\"type\":\"timeserie\",\"datasourceId\":' + this.id + ',' +\n '\"query\":\"* | select count(*) as count\",\"ycol\":\"count\"}]}';\n const query = JSON.parse(str);\n return this.doTsdbRequest(query).then(response => {\n if (response.status === 200) {\n return { status: \"success\", message: \"Data source is working\", title: \"Success\" };\n }\n else {\n return { status: \"failed\", message: \"Data source is not working\", title: \"Error\" };\n }\n }).catch(() => {\n return { status: \"failed\", message: \"Data source is not working\", title: \"Error\" };\n });\n }\n annotationQuery(options) {\n const query = this.templateSrv.replace(options.annotation.query, {}, 'glob');\n const annotationQuery = {\n range: options.range,\n annotation: {\n name: options.annotation.name,\n datasource: options.annotation.datasource,\n enable: options.annotation.enable,\n iconColor: options.annotation.iconColor,\n query: query\n },\n rangeRaw: options.rangeRaw\n };\n return this.doRequest({\n url: this.url + '/annotations',\n method: 'POST',\n data: annotationQuery\n }).then(result => {\n return result.data;\n });\n }\n metricFindQuery(q) {\n q = this.templateSrv.replace(q, {}, 'glob');\n const to = this.templateSrv.timeRange.to.unix() * 1000;\n const from = this.templateSrv.timeRange.from.unix() * 1000;\n const str = '{\"requestId\":\"Q100\",\"timezone\":\"\",\"range\":{\"from\":\"' + from + '\",\"to\":\"' + to + '\"},' +\n '\"targets\":[{\"queryType\":\"query\",\"target\":\"query\",\"refId\":\"A\",\"type\":\"timeserie\",\"datasourceId\":' + this.id + ',' +\n '\"query\":\"' + q + '\"}]}';\n const query = JSON.parse(str);\n return this.doTsdbRequest(query).then(response => {\n const res = handleTsdbResponse(response);\n if (res && res.data && res.data.length) {\n let rows = res.data[0].rows;\n rows = rows.map(item => item[0]);\n return rows;\n }\n else {\n return [];\n }\n }).then(mapToTextValue);\n }\n doRequest(options) {\n options.withCredentials = this.withCredentials;\n options.headers = this.headers;\n return this.backendSrv.datasourceRequest(options);\n }\n doTsdbRequest(options) {\n const tsdbRequestData = {\n queries: options.targets,\n };\n if (options.range) {\n tsdbRequestData.from = options.range.from.valueOf().toString();\n tsdbRequestData.to = options.range.to.valueOf().toString();\n }\n return this.backendSrv.datasourceRequest({\n url: '/api/tsdb/query',\n method: 'POST',\n data: tsdbRequestData\n });\n }\n buildQueryParameters(options) {\n //remove placeholder targets\n options.targets = _.filter(options.targets, target => {\n return target.target !== 'select metric';\n });\n options.targets = _.map(options.targets, target => {\n return {\n queryType: 'query',\n target: this.templateSrv.replace(target.target, options.scopedVars, 'regex'),\n refId: target.refId,\n hide: target.hide,\n type: target.type || 'timeserie',\n datasourceId: this.id,\n query: this.replaceQueryParameters(target, options),\n xcol: target.xcol,\n ycol: target.ycol\n };\n });\n return options;\n }\n replaceQueryParameters(target, options) {\n let query = this.templateSrv.replace(target.query, options.scopedVars, function (value, variable) {\n if (typeof value == \"object\" && (variable.multi || variable.includeAll)) {\n const a = [];\n value.forEach(function (v) {\n if (variable.name == variable.label)\n a.push('\"' + variable.name + '\":\"' + v + '\"');\n else\n a.push('\"' + v + '\"');\n });\n return a.join(\" OR \");\n }\n if (_.isArray(value)) {\n return value.join(' OR ');\n }\n return value;\n });\n const re = /\\$([0-9]+)([dmhs])/g;\n const reArray = query.match(re);\n _(reArray).forEach(function (col) {\n const old = col;\n col = col.replace(\"$\", '');\n let sec = 1;\n if (col.indexOf(\"s\") != -1)\n sec = 1;\n else if (col.indexOf(\"m\") != -1)\n sec = 60;\n else if (col.indexOf(\"h\") != -1)\n sec = 3600;\n else if (col.indexOf(\"d\") != -1)\n sec = 3600 * 24;\n col = col.replace(/[smhd]/g, '');\n let v = parseInt(col);\n v = v * sec;\n console.log(old, v, col, sec, query);\n query = query.replace(old, v);\n });\n if (query.indexOf(\"#time_end\") != -1) {\n query = query.replace(\"#time_end\", parseInt(String(options.range.to._d.getTime() / 1000)));\n }\n if (query.indexOf(\"#time_begin\") != -1) {\n query = query.replace(\"#time_begin\", parseInt(String(options.range.from._d.getTime() / 1000)));\n }\n return query;\n }\n getTagKeys(options) {\n return new Promise((resolve) => {\n this.doRequest({\n url: this.url + '/tag-keys',\n method: 'POST',\n data: options\n }).then(result => {\n return resolve(result.data);\n });\n });\n }\n getTagValues(options) {\n return new Promise((resolve) => {\n this.doRequest({\n url: this.url + '/tag-values',\n method: 'POST',\n data: options\n }).then(result => {\n return resolve(result.data);\n });\n });\n }\n}\nexport function handleTsdbResponse(response) {\n const res = [];\n _.forEach(response.data.results, r => {\n _.forEach(r.series, s => {\n res.push({ target: s.name, datapoints: s.points });\n });\n _.forEach(r.tables, t => {\n t.type = 'table';\n t.refId = r.refId;\n res.push(t);\n });\n });\n response.data = res;\n console.log(res);\n return response;\n}\nexport function mapToTextValue(result) {\n return _.map(result, (d, i) => {\n if (d && d.text && d.value) {\n return { text: d.text, value: d.value };\n }\n else if (_.isObject(d)) {\n return { text: d, value: i };\n }\n return { text: d, value: d };\n });\n}\n//# sourceMappingURL=datasource.js.map"]}
--------------------------------------------------------------------------------
/dist/datasource.ts:
--------------------------------------------------------------------------------
1 | import _ from "lodash";
2 |
3 | interface TSDBRequest {
4 | queries: any[];
5 | from?: string;
6 | to?: string;
7 | }
8 |
9 | interface TSDBQuery {
10 | datasourceId: string;
11 | target: any;
12 | queryType?: TSDBQueryType;
13 | refId?: string;
14 | hide?: boolean;
15 | type?: 'timeserie' | 'table';
16 | }
17 |
18 | type TSDBQueryType = 'query' | 'search';
19 |
20 | interface TSDBRequestOptions {
21 | range?: {
22 | from: any;
23 | to: any;
24 | };
25 | targets: TSDBQuery[];
26 | }
27 |
28 | export class GenericDatasource {
29 | name: string;
30 | type: string;
31 | id: string;
32 | url: string;
33 | withCredentials: boolean;
34 | instanceSettings: any;
35 | headers: any;
36 |
37 | /** @ngInject */
38 | constructor(instanceSettings, private backendSrv, private templateSrv) {
39 | this.type = instanceSettings.type;
40 | this.url = instanceSettings.url;
41 | this.name = instanceSettings.name;
42 | this.id = instanceSettings.id;
43 | this.withCredentials = instanceSettings.withCredentials;
44 | this.headers = {'Content-Type': 'application/json'};
45 | if (typeof instanceSettings.basicAuth === 'string' && instanceSettings.basicAuth.length > 0) {
46 | this.headers['Authorization'] = instanceSettings.basicAuth;
47 | }
48 | }
49 |
50 | query(options) {
51 | const query = this.buildQueryParameters(options);
52 | query.targets = query.targets.filter(t => !t.hide);
53 |
54 | if (query.targets.length <= 0) {
55 | return Promise.resolve({data: []});
56 | }
57 |
58 | return this.doTsdbRequest(query).then(handleTsdbResponse);
59 | }
60 |
61 | testDatasource() {
62 | const to = new Date().getTime();
63 | const from = to - 5000;
64 | const str = '{"requestId":"Q100","timezone":"","range":{"from":"' + from + '","to":"' + to + '"},' +
65 | '"targets":[{"queryType":"query","target":"count","refId":"A","type":"timeserie","datasourceId":' + this.id + ',' +
66 | '"query":"* | select count(*) as count","ycol":"count"}]}';
67 | const query = JSON.parse(str);
68 | return this.doTsdbRequest(query).then(response => {
69 | if (response.status === 200) {
70 | return { status: "success", message: "Data source is working", title: "Success" };
71 | } else {
72 | return { status: "failed", message: "Data source is not working", title: "Error" };
73 | }
74 | }).catch(() => {
75 | return { status: "failed", message: "Data source is not working", title: "Error" };
76 | });
77 | }
78 |
79 | annotationQuery(options) {
80 | const query = this.templateSrv.replace(options.annotation.query, {}, 'glob');
81 | const annotationQuery = {
82 | range: options.range,
83 | annotation: {
84 | name: options.annotation.name,
85 | datasource: options.annotation.datasource,
86 | enable: options.annotation.enable,
87 | iconColor: options.annotation.iconColor,
88 | query: query
89 | },
90 | rangeRaw: options.rangeRaw
91 | };
92 |
93 | return this.doRequest({
94 | url: this.url + '/annotations',
95 | method: 'POST',
96 | data: annotationQuery
97 | }).then(result => {
98 | return result.data;
99 | });
100 | }
101 |
102 | metricFindQuery(q) {
103 | q = this.templateSrv.replace(q, {}, 'glob');
104 | const to = this.templateSrv.timeRange.to.unix()*1000;
105 | const from = this.templateSrv.timeRange.from.unix()*1000;
106 | const str = '{"requestId":"Q100","timezone":"","range":{"from":"' + from + '","to":"' + to + '"},' +
107 | '"targets":[{"queryType":"query","target":"query","refId":"A","type":"timeserie","datasourceId":' + this.id + ',' +
108 | '"query":"'+ q +'"}]}';
109 | const query = JSON.parse(str);
110 | return this.doTsdbRequest(query).then(response => {
111 | const res = handleTsdbResponse(response);
112 | if (res && res.data && res.data.length) {
113 | let rows = res.data[0].rows;
114 | rows = rows.map(item => item[0]);
115 | return rows;
116 | } else {
117 | return [];
118 | }
119 | }).then(mapToTextValue);
120 | }
121 |
122 | doRequest(options) {
123 | options.withCredentials = this.withCredentials;
124 | options.headers = this.headers;
125 |
126 | return this.backendSrv.datasourceRequest(options);
127 | }
128 |
129 | doTsdbRequest(options: TSDBRequestOptions) {
130 | const tsdbRequestData: TSDBRequest = {
131 | queries: options.targets,
132 | };
133 |
134 | if (options.range) {
135 | tsdbRequestData.from = options.range.from.valueOf().toString();
136 | tsdbRequestData.to = options.range.to.valueOf().toString();
137 | }
138 |
139 | return this.backendSrv.datasourceRequest({
140 | url: '/api/tsdb/query',
141 | method: 'POST',
142 | data: tsdbRequestData
143 | });
144 | }
145 |
146 | buildQueryParameters(options: any): TSDBRequestOptions {
147 | //remove placeholder targets
148 | options.targets = _.filter(options.targets, target => {
149 | return target.target !== 'select metric';
150 | });
151 |
152 | options.targets = _.map(options.targets, target => {
153 | return {
154 | queryType: 'query',
155 | target: this.templateSrv.replace(target.target, options.scopedVars, 'regex'),
156 | refId: target.refId,
157 | hide: target.hide,
158 | type: target.type || 'timeserie',
159 | datasourceId: this.id,
160 | query: this.replaceQueryParameters(target, options),
161 | xcol: target.xcol,
162 | ycol: target.ycol
163 | };
164 | });
165 |
166 | return options;
167 | }
168 |
169 | replaceQueryParameters(target,options): TSDBRequestOptions {
170 | let query = this.templateSrv.replace(target.query, options.scopedVars, function (value, variable) {
171 | if (typeof value == "object" && (variable.multi || variable.includeAll)) {
172 | const a = [];
173 | value.forEach(function (v) {
174 | if (variable.name == variable.label) a.push('"' + variable.name + '":"' + v + '"');else a.push('"' + v + '"');
175 | });
176 | return a.join(" OR ");
177 | }
178 | if (_.isArray(value)) {
179 | return value.join(' OR ');
180 | }
181 | return value;
182 | });
183 | const re = /\$([0-9]+)([dmhs])/g;
184 | const reArray = query.match(re);
185 | _(reArray).forEach(function (col) {
186 | const old = col;
187 | col = col.replace("$", '');
188 | let sec = 1;
189 | if (col.indexOf("s") != -1) sec = 1;else if (col.indexOf("m") != -1) sec = 60;else if (col.indexOf("h") != -1) sec = 3600;else if (col.indexOf("d") != -1) sec = 3600 * 24;
190 | col = col.replace(/[smhd]/g, '');
191 | let v = parseInt(col);
192 | v = v * sec;
193 | console.log(old, v, col, sec, query);
194 | query = query.replace(old, v);
195 | });
196 | if (query.indexOf("#time_end") != -1) {
197 | query = query.replace("#time_end", parseInt(String(options.range.to._d.getTime() / 1000)));
198 | }
199 | if (query.indexOf("#time_begin") != -1) {
200 | query = query.replace("#time_begin", parseInt(String(options.range.from._d.getTime() / 1000)));
201 | }
202 | return query;
203 | }
204 |
205 | getTagKeys(options) {
206 | return new Promise((resolve) => {
207 | this.doRequest({
208 | url: this.url + '/tag-keys',
209 | method: 'POST',
210 | data: options
211 | }).then(result => {
212 | return resolve(result.data);
213 | });
214 | });
215 | }
216 |
217 | getTagValues(options) {
218 | return new Promise((resolve) => {
219 | this.doRequest({
220 | url: this.url + '/tag-values',
221 | method: 'POST',
222 | data: options
223 | }).then(result => {
224 | return resolve(result.data);
225 | });
226 | });
227 | }
228 | }
229 |
230 | export function handleTsdbResponse(response) {
231 | const res= [];
232 | _.forEach(response.data.results, r => {
233 | _.forEach(r.series, s => {
234 | res.push({target: s.name, datapoints: s.points});
235 | });
236 | _.forEach(r.tables, t => {
237 | t.type = 'table';
238 | t.refId = r.refId;
239 | res.push(t);
240 | });
241 | });
242 |
243 | response.data = res;
244 | console.log(res);
245 | return response;
246 | }
247 |
248 | export function mapToTextValue(result) {
249 | return _.map(result, (d, i) => {
250 | if (d && d.text && d.value) {
251 | return { text: d.text, value: d.value };
252 | } else if (_.isObject(d)) {
253 | return { text: d, value: i};
254 | }
255 | return { text: d, value: d };
256 | });
257 | }
258 |
--------------------------------------------------------------------------------
/dist/img/sls_logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mayunlei/aliyun-log-grafana-datasource-plugin/1907f18d10e9e52a47169cf0e0bafb5f8a11f89d/dist/img/sls_logo.jpg
--------------------------------------------------------------------------------
/dist/module.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | System.register(['./datasource', './query_ctrl'], function (_export, _context) {
4 | "use strict";
5 |
6 | var GenericDatasource, GenericDatasourceQueryCtrl, GenericConfigCtrl, GenericQueryOptionsCtrl, GenericAnnotationsQueryCtrl;
7 |
8 | function _classCallCheck(instance, Constructor) {
9 | if (!(instance instanceof Constructor)) {
10 | throw new TypeError("Cannot call a class as a function");
11 | }
12 | }
13 |
14 | return {
15 | setters: [function (_datasource) {
16 | GenericDatasource = _datasource.GenericDatasource;
17 | }, function (_query_ctrl) {
18 | GenericDatasourceQueryCtrl = _query_ctrl.GenericDatasourceQueryCtrl;
19 | }],
20 | execute: function () {
21 | _export('ConfigCtrl', GenericConfigCtrl = function GenericConfigCtrl() {
22 | _classCallCheck(this, GenericConfigCtrl);
23 | });
24 |
25 | GenericConfigCtrl.templateUrl = 'partials/config.html';
26 |
27 | _export('QueryOptionsCtrl', GenericQueryOptionsCtrl = function GenericQueryOptionsCtrl() {
28 | _classCallCheck(this, GenericQueryOptionsCtrl);
29 | });
30 |
31 | GenericQueryOptionsCtrl.templateUrl = 'partials/query.options.html';
32 |
33 | _export('AnnotationsQueryCtrl', GenericAnnotationsQueryCtrl = function GenericAnnotationsQueryCtrl() {
34 | _classCallCheck(this, GenericAnnotationsQueryCtrl);
35 | });
36 |
37 | GenericAnnotationsQueryCtrl.templateUrl = 'partials/annotations.editor.html';
38 |
39 | _export('Datasource', GenericDatasource);
40 |
41 | _export('QueryCtrl', GenericDatasourceQueryCtrl);
42 |
43 | _export('ConfigCtrl', GenericConfigCtrl);
44 |
45 | _export('QueryOptionsCtrl', GenericQueryOptionsCtrl);
46 |
47 | _export('AnnotationsQueryCtrl', GenericAnnotationsQueryCtrl);
48 | }
49 | };
50 | });
51 | //# sourceMappingURL=module.js.map
52 |
--------------------------------------------------------------------------------
/dist/module.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../src/module.js"],"names":["GenericDatasource","GenericDatasourceQueryCtrl","GenericConfigCtrl","templateUrl","GenericQueryOptionsCtrl","GenericAnnotationsQueryCtrl"],"mappings":";;;;;;;;;;;;;;;AAASA,uB,eAAAA,iB;;AACAC,gC,eAAAA,0B;;;4BACHC,iB;;;;AAENA,wBAAkBC,WAAlB,GAAgC,sBAAhC;;kCACMC,uB;;;;AAENA,8BAAwBD,WAAxB,GAAsC,6BAAtC;;sCACME,2B;;;;AAENA,kCAA4BF,WAA5B,GAA0C,kCAA1C;;4BACSH,iB;;2BAAiCC,0B;;4BAAyCC,iB;;kCAAiCE,uB;;sCAA6CC,2B","file":"module.js","sourcesContent":["import { GenericDatasource } from './datasource';\nimport { GenericDatasourceQueryCtrl } from './query_ctrl';\nclass GenericConfigCtrl {\n}\nGenericConfigCtrl.templateUrl = 'partials/config.html';\nclass GenericQueryOptionsCtrl {\n}\nGenericQueryOptionsCtrl.templateUrl = 'partials/query.options.html';\nclass GenericAnnotationsQueryCtrl {\n}\nGenericAnnotationsQueryCtrl.templateUrl = 'partials/annotations.editor.html';\nexport { GenericDatasource as Datasource, GenericDatasourceQueryCtrl as QueryCtrl, GenericConfigCtrl as ConfigCtrl, GenericQueryOptionsCtrl as QueryOptionsCtrl, GenericAnnotationsQueryCtrl as AnnotationsQueryCtrl };\n//# sourceMappingURL=module.js.map"]}
--------------------------------------------------------------------------------
/dist/module.ts:
--------------------------------------------------------------------------------
1 | import {GenericDatasource} from './datasource';
2 | import {GenericDatasourceQueryCtrl} from './query_ctrl';
3 |
4 | class GenericConfigCtrl {
5 | static templateUrl = 'partials/config.html';
6 | }
7 |
8 | class GenericQueryOptionsCtrl {
9 | static templateUrl = 'partials/query.options.html';
10 | }
11 |
12 | class GenericAnnotationsQueryCtrl {
13 | static templateUrl = 'partials/annotations.editor.html'
14 | }
15 |
16 | export {
17 | GenericDatasource as Datasource,
18 | GenericDatasourceQueryCtrl as QueryCtrl,
19 | GenericConfigCtrl as ConfigCtrl,
20 | GenericQueryOptionsCtrl as QueryOptionsCtrl,
21 | GenericAnnotationsQueryCtrl as AnnotationsQueryCtrl
22 | };
23 |
--------------------------------------------------------------------------------
/dist/partials/annotations.editor.html:
--------------------------------------------------------------------------------
1 |
2 |
Query
3 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/dist/partials/config.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | log service details
4 |
12 |
20 | Default query settings
21 |
25 |
--------------------------------------------------------------------------------
/dist/partials/query.editor.html:
--------------------------------------------------------------------------------
1 |
2 |
8 |
17 |
--------------------------------------------------------------------------------
/dist/partials/query.options.html:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/dist/plugin.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "LogService",
3 | "id": "grafana-log-service-datasource",
4 | "type": "datasource",
5 |
6 | "partials": {
7 | "config": "public/app/plugins/datasource/simplejson/partials/config.html"
8 | },
9 |
10 | "metrics": true,
11 | "annotations": true,
12 | "backend": true,
13 | "alerting": true,
14 | "executable": "aliyun-log-plugin",
15 |
16 | "info": {
17 | "description": "aliyun log datasource",
18 | "author": {
19 | "name": "aliyun log service dev",
20 | "url": "https://aliyun.com/product/sls"
21 | },
22 | "logos": {
23 | "small": "img/sls_logo.jpg",
24 | "large": "img/sls_logo.jpg"
25 | },
26 | "links": [
27 | {
28 | "name": "GitHub",
29 | "url": "https://github.com/grafana/simple-json-backend-datasource"
30 | },
31 | {
32 | "name": "MIT License",
33 | "url": "https://github.com/grafana/simple-json-backend-datasource/blob/master/LICENSE"
34 | }
35 | ],
36 | "version": "1.0.0",
37 | "updated": "2019-11-09"
38 | },
39 |
40 | "dependencies": {
41 | "grafanaVersion": "6.x.x",
42 | "plugins": []
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/dist/query_ctrl.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | System.register(['app/plugins/sdk', './css/query-editor.css!'], function (_export, _context) {
4 | "use strict";
5 |
6 | var QueryCtrl, _createClass, GenericDatasourceQueryCtrl;
7 |
8 | function _classCallCheck(instance, Constructor) {
9 | if (!(instance instanceof Constructor)) {
10 | throw new TypeError("Cannot call a class as a function");
11 | }
12 | }
13 |
14 | function _possibleConstructorReturn(self, call) {
15 | if (!self) {
16 | throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
17 | }
18 |
19 | return call && (typeof call === "object" || typeof call === "function") ? call : self;
20 | }
21 |
22 | function _inherits(subClass, superClass) {
23 | if (typeof superClass !== "function" && superClass !== null) {
24 | throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
25 | }
26 |
27 | subClass.prototype = Object.create(superClass && superClass.prototype, {
28 | constructor: {
29 | value: subClass,
30 | enumerable: false,
31 | writable: true,
32 | configurable: true
33 | }
34 | });
35 | if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
36 | }
37 |
38 | return {
39 | setters: [function (_appPluginsSdk) {
40 | QueryCtrl = _appPluginsSdk.QueryCtrl;
41 | }, function (_cssQueryEditorCss) {}],
42 | execute: function () {
43 | _createClass = function () {
44 | function defineProperties(target, props) {
45 | for (var i = 0; i < props.length; i++) {
46 | var descriptor = props[i];
47 | descriptor.enumerable = descriptor.enumerable || false;
48 | descriptor.configurable = true;
49 | if ("value" in descriptor) descriptor.writable = true;
50 | Object.defineProperty(target, descriptor.key, descriptor);
51 | }
52 | }
53 |
54 | return function (Constructor, protoProps, staticProps) {
55 | if (protoProps) defineProperties(Constructor.prototype, protoProps);
56 | if (staticProps) defineProperties(Constructor, staticProps);
57 | return Constructor;
58 | };
59 | }();
60 |
61 | _export('GenericDatasourceQueryCtrl', GenericDatasourceQueryCtrl = function (_QueryCtrl) {
62 | _inherits(GenericDatasourceQueryCtrl, _QueryCtrl);
63 |
64 | /** @ngInject */
65 | function GenericDatasourceQueryCtrl($scope, $injector) {
66 | _classCallCheck(this, GenericDatasourceQueryCtrl);
67 |
68 | var _this = _possibleConstructorReturn(this, (GenericDatasourceQueryCtrl.__proto__ || Object.getPrototypeOf(GenericDatasourceQueryCtrl)).call(this, $scope, $injector));
69 |
70 | _this.target.target = _this.target.ycol;
71 | _this.target.type = _this.target.type || 'timeserie';
72 | return _this;
73 | }
74 |
75 | _createClass(GenericDatasourceQueryCtrl, [{
76 | key: 'getOptions',
77 | value: function getOptions(query) {
78 | return this.datasource.metricFindQuery(query || '');
79 | }
80 | }, {
81 | key: 'toggleEditorMode',
82 | value: function toggleEditorMode() {
83 | this.target.rawQuery = !this.target.rawQuery;
84 | }
85 | }, {
86 | key: 'onChangeInternal',
87 | value: function onChangeInternal() {
88 | this.panelCtrl.refresh(); // Asks the panel to refresh data.
89 | }
90 | }]);
91 |
92 | return GenericDatasourceQueryCtrl;
93 | }(QueryCtrl));
94 |
95 | _export('GenericDatasourceQueryCtrl', GenericDatasourceQueryCtrl);
96 |
97 | GenericDatasourceQueryCtrl.templateUrl = 'partials/query.editor.html';
98 | //# sourceMappingURL=query_ctrl.js.map
99 | }
100 | };
101 | });
102 | //# sourceMappingURL=query_ctrl.js.map
103 |
--------------------------------------------------------------------------------
/dist/query_ctrl.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../src/query_ctrl.js"],"names":["QueryCtrl","GenericDatasourceQueryCtrl","$scope","$injector","target","ycol","type","query","datasource","metricFindQuery","rawQuery","panelCtrl","refresh","templateUrl"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAASA,qB,kBAAAA,S;;;;;;;;;;;;;;;;;;;;;kDAEIC,0B;;;AACT;AACA,oDAAYC,MAAZ,EAAoBC,SAApB,EAA+B;AAAA;;AAAA,wKACrBD,MADqB,EACbC,SADa;;AAE3B,0BAAKC,MAAL,CAAYA,MAAZ,GAAqB,MAAKA,MAAL,CAAYC,IAAjC;AACA,0BAAKD,MAAL,CAAYE,IAAZ,GAAmB,MAAKF,MAAL,CAAYE,IAAZ,IAAoB,WAAvC;AAH2B;AAI9B;;;;+CACUC,K,EAAO;AACd,+BAAO,KAAKC,UAAL,CAAgBC,eAAhB,CAAgCF,SAAS,EAAzC,CAAP;AACH;;;uDACkB;AACf,6BAAKH,MAAL,CAAYM,QAAZ,GAAuB,CAAC,KAAKN,MAAL,CAAYM,QAApC;AACH;;;uDACkB;AACf,6BAAKC,SAAL,CAAeC,OAAf,GADe,CACW;AAC7B;;;;cAf2CZ,S;;;;AAiBhDC,uCAA2BY,WAA3B,GAAyC,4BAAzC;AACA","file":"query_ctrl.js","sourcesContent":["import { QueryCtrl } from 'app/plugins/sdk';\nimport './css/query-editor.css!';\nexport class GenericDatasourceQueryCtrl extends QueryCtrl {\n /** @ngInject */\n constructor($scope, $injector) {\n super($scope, $injector);\n this.target.target = this.target.ycol;\n this.target.type = this.target.type || 'timeserie';\n }\n getOptions(query) {\n return this.datasource.metricFindQuery(query || '');\n }\n toggleEditorMode() {\n this.target.rawQuery = !this.target.rawQuery;\n }\n onChangeInternal() {\n this.panelCtrl.refresh(); // Asks the panel to refresh data.\n }\n}\nGenericDatasourceQueryCtrl.templateUrl = 'partials/query.editor.html';\n//# sourceMappingURL=query_ctrl.js.map"]}
--------------------------------------------------------------------------------
/dist/query_ctrl.ts:
--------------------------------------------------------------------------------
1 | import {QueryCtrl} from 'app/plugins/sdk';
2 | import './css/query-editor.css!';
3 |
4 | export class GenericDatasourceQueryCtrl extends QueryCtrl {
5 | static templateUrl = 'partials/query.editor.html';
6 |
7 | scope: any;
8 |
9 | /** @ngInject */
10 | constructor($scope, $injector) {
11 | super($scope, $injector);
12 | this.target.target = this.target.ycol;
13 | this.target.type = this.target.type || 'timeserie';
14 | }
15 |
16 | getOptions(query) {
17 | return this.datasource.metricFindQuery(query || '');
18 | }
19 |
20 | toggleEditorMode() {
21 | this.target.rawQuery = !this.target.rawQuery;
22 | }
23 |
24 | onChangeInternal() {
25 | this.panelCtrl.refresh(); // Asks the panel to refresh data.
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/img/demo1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mayunlei/aliyun-log-grafana-datasource-plugin/1907f18d10e9e52a47169cf0e0bafb5f8a11f89d/img/demo1.png
--------------------------------------------------------------------------------
/img/demo2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mayunlei/aliyun-log-grafana-datasource-plugin/1907f18d10e9e52a47169cf0e0bafb5f8a11f89d/img/demo2.png
--------------------------------------------------------------------------------
/img/demo3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mayunlei/aliyun-log-grafana-datasource-plugin/1907f18d10e9e52a47169cf0e0bafb5f8a11f89d/img/demo3.png
--------------------------------------------------------------------------------
/img/demo4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mayunlei/aliyun-log-grafana-datasource-plugin/1907f18d10e9e52a47169cf0e0bafb5f8a11f89d/img/demo4.png
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | verbose: true,
3 | "transform": {
4 | "^.+\\.(ts|tsx)$": "ts-jest"
5 | },
6 | "testRegex": "(\\.|/)([jt]est)\\.[jt]s$",
7 | "moduleFileExtensions": [
8 | "ts",
9 | "tsx",
10 | "js",
11 | "jsx",
12 | "json"
13 | ],
14 | "coverageDirectory": "./tmp/coverage/",
15 | };
16 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "grafana-sls",
3 | "version": "1.0.0",
4 | "description": "Aliyun Log Datasource (backend version)",
5 | "private": true,
6 | "main": "index.js",
7 | "scripts": {
8 | "build": "webpack --config webpack/webpack.prod.conf.js",
9 | "dev": "webpack --config webpack/webpack.dev.conf.js",
10 | "watch": "webpack --config webpack/webpack.dev.conf.js --watch",
11 | "test": "./node_modules/grunt-cli/bin/grunt mochaTest",
12 | "jest": "jest --config jest.config.js"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "git+https://github.com/grafana/simple-json-backend-datasource.git"
17 | },
18 | "author": "Grafana Labs",
19 | "license": "ISC",
20 | "bugs": {
21 | "url": "https://github.com/grafana/simple-json-backend-datasource/issues"
22 | },
23 | "homepage": "https://github.com/grafana/simple-json-backend-datasource",
24 | "engineStrict": true,
25 | "devDependencies": {
26 | "@types/angular": "^1.6.43",
27 | "@types/grafana": "github:CorpGlory/types-grafana",
28 | "@types/jest": "^24.0.11",
29 | "@types/lodash": "^4.14.116",
30 | "babel-plugin-transform-es2015-for-of": "^6.6.0",
31 | "babel-plugin-transform-es2015-modules-systemjs": "^6.24.1",
32 | "babel-preset-es2015": "^6.24.1",
33 | "chai": "~3.5.0",
34 | "clean-webpack-plugin": "^2.0.1",
35 | "copy-webpack-plugin": "^5.0.2",
36 | "css-loader": "^2.1.1",
37 | "grunt": "^1.0.4",
38 | "grunt-babel": "~6.0.0",
39 | "grunt-cli": "^1.2.0",
40 | "grunt-contrib-clean": "^1.1.0",
41 | "grunt-contrib-copy": "^1.0.0",
42 | "grunt-contrib-uglify": "^2.3.0",
43 | "grunt-contrib-watch": "^1.0.0",
44 | "grunt-execute": "~0.2.2",
45 | "grunt-mocha-test": "^0.13.2",
46 | "grunt-systemjs-builder": "^1.0.0",
47 | "jest": "^24.7.1",
48 | "jsdom": "~9.12.0",
49 | "load-grunt-tasks": "^3.5.2",
50 | "lodash": "^4.17.4",
51 | "mocha": "^3.2.0",
52 | "ng-annotate-webpack-plugin": "^0.3.0",
53 | "prunk": "^1.3.0",
54 | "q": "^1.5.0",
55 | "style-loader": "^0.23.1",
56 | "ts-jest": "^24.0.2",
57 | "ts-loader": "^5.3.3",
58 | "tslint": "^5.15.0",
59 | "typescript": "^3.4.3",
60 | "webpack": "^4.29.6",
61 | "webpack-cli": "^3.3.0"
62 | },
63 | "dependencies": {}
64 | }
65 |
--------------------------------------------------------------------------------
/pkg/datasource.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "errors"
6 | sls "github.com/aliyun/aliyun-log-go-sdk"
7 | "github.com/grafana/grafana_plugin_model/go/datasource"
8 | "github.com/hashicorp/go-hclog"
9 | "github.com/hashicorp/go-plugin"
10 | "golang.org/x/net/context"
11 | "sort"
12 | "strconv"
13 | "strings"
14 | )
15 |
16 | type SlsDatasource struct {
17 | plugin.NetRPCUnsupportedPlugin
18 | logger hclog.Logger
19 | }
20 |
21 | func (ds *SlsDatasource) Query(ctx context.Context, tsdbReq *datasource.DatasourceRequest) (*datasource.DatasourceResponse, error) {
22 |
23 | ds.logger.Debug("Query", "datasource", tsdbReq.Datasource.Name, "TimeRange", tsdbReq.TimeRange)
24 |
25 | logSource := &LogSource{}
26 |
27 | err := json.Unmarshal([]byte(tsdbReq.Datasource.JsonData), &logSource)
28 | if err != nil {
29 | ds.logger.Error("Unmarshal logSource", "error ", err)
30 | return nil, err
31 | }
32 |
33 | accessKeyId := tsdbReq.Datasource.DecryptedSecureJsonData["accessKeyId"]
34 | if len(accessKeyId) == 0 {
35 | ds.logger.Error("AccessKeyID cannot be null")
36 | return nil, errors.New("AccessKeyID cannot be null")
37 | }
38 |
39 | accessKeySecret := tsdbReq.Datasource.DecryptedSecureJsonData["accessKeySecret"]
40 | if len(accessKeySecret) == 0 {
41 | ds.logger.Error("AccessKeySecret cannot be null")
42 | return nil, errors.New("AccessKeySecret cannot be null")
43 | }
44 |
45 | client := &sls.Client{
46 | Endpoint: tsdbReq.Datasource.Url,
47 | AccessKeyID: accessKeyId,
48 | AccessKeySecret: accessKeySecret,
49 | }
50 |
51 | queries := tsdbReq.Queries
52 |
53 | from := tsdbReq.TimeRange.FromEpochMs / 1000
54 | to := tsdbReq.TimeRange.ToEpochMs / 1000
55 |
56 | var results []*datasource.QueryResult
57 |
58 | ch := make(chan *datasource.QueryResult, len(queries))
59 |
60 | for _, query := range queries {
61 | go ds.QueryLogs(ch, query, client, logSource, from, to)
62 | }
63 | c := 0
64 | for result := range ch {
65 | c = c + 1
66 | if c == len(queries) {
67 | close(ch)
68 | }
69 | results = append(results, result)
70 | }
71 | rt := &datasource.DatasourceResponse{
72 | Results: results,
73 | }
74 | return rt, nil
75 | }
76 |
77 | func (ds *SlsDatasource) GetValue(v string) *datasource.RowValue {
78 | value := &datasource.RowValue{}
79 | intValue, err := strconv.ParseInt(v, 10, 10)
80 | if err == nil {
81 | value.Int64Value = intValue
82 | value.Kind = datasource.RowValue_TYPE_INT64
83 | } else {
84 | floatValue, err := strconv.ParseFloat(v, 10)
85 | if err == nil {
86 | value.DoubleValue = floatValue
87 | value.Kind = datasource.RowValue_TYPE_DOUBLE
88 | } else {
89 | value.StringValue = v
90 | value.Kind = datasource.RowValue_TYPE_STRING
91 | }
92 | }
93 | return value
94 | }
95 |
96 | func (ds *SlsDatasource) ParseTimestamp(v string) (int64, error) {
97 | floatV, err := strconv.ParseFloat(v, 10)
98 | if err != nil {
99 | return 0, err
100 | }
101 | int64V := int64(floatV)
102 | return int64V * 1000, nil
103 | }
104 |
105 | func (ds *SlsDatasource) SortLogs(logs []map[string]string, xcol string) {
106 | sort.Slice(logs, func(i, j int) bool {
107 | timestamp1, err := ds.ParseTimestamp(logs[i][xcol])
108 | if err != nil {
109 | ds.logger.Error("SortLogs1", "error ", err)
110 | }
111 | timestamp2, err := ds.ParseTimestamp(logs[j][xcol])
112 | if err != nil {
113 | ds.logger.Error("SortLogs2", "error ", err)
114 | }
115 | if timestamp1 < timestamp2 {
116 | return true
117 | }
118 | return false
119 | })
120 | }
121 |
122 | func (ds *SlsDatasource) QueryLogs(ch chan *datasource.QueryResult, query *datasource.Query, client *sls.Client, logSource *LogSource, from int64, to int64) {
123 | modelJson := query.ModelJson
124 |
125 | queryInfo := &QueryInfo{}
126 | err := json.Unmarshal([]byte(modelJson), &queryInfo)
127 | if err != nil {
128 | ds.logger.Error("Unmarshal queryInfo", "error ", err)
129 | ch <- &datasource.QueryResult{
130 | Error: err.Error(),
131 | }
132 | return
133 | }
134 | getLogsResp, err := client.GetLogs(logSource.Project, logSource.LogStore, "",
135 | from, to, queryInfo.Query, 0, 0, true)
136 | if err != nil {
137 | ds.logger.Error("GetLogs ", "query : ", queryInfo.Query, "error ", err)
138 | ch <- &datasource.QueryResult{
139 | Error: err.Error(),
140 | }
141 | return
142 | }
143 | logs := getLogsResp.Logs
144 |
145 | var series []*datasource.TimeSeries
146 | var tables []*datasource.Table
147 | xcol := queryInfo.Xcol
148 | var ycols []string
149 | isFlowGraph := strings.Contains(queryInfo.Ycol, "#:#")
150 | if isFlowGraph {
151 | ycols = strings.Split(queryInfo.Ycol, "#:#")
152 | } else {
153 | ycols = strings.Split(queryInfo.Ycol, ",")
154 | }
155 | if isFlowGraph {
156 | ds.BuildFlowGraph(ch, logs, xcol, ycols, query.RefId)
157 | return
158 | } else if xcol == "pie" {
159 | ds.BuildPieGraph(ch, logs, xcol, ycols, query.RefId)
160 | return
161 | } else if xcol != "" && xcol != "map" && xcol != "pie" && xcol != "bar" && xcol != "table" {
162 | ds.BuildTimingGraph(ch, logs, xcol, ycols, &series)
163 | } else {
164 | ds.BuildTable(ch, logs, xcol, ycols, &tables)
165 | }
166 | ch <- &datasource.QueryResult{
167 | RefId: query.RefId,
168 | Series: series,
169 | Tables: tables,
170 | }
171 | }
172 |
173 | func (ds *SlsDatasource) BuildFlowGraph(ch chan *datasource.QueryResult, logs []map[string]string, xcol string, ycols []string, refId string) {
174 | ds.SortLogs(logs, xcol)
175 | if len(ycols) < 2 {
176 | ch <- &datasource.QueryResult{
177 | Error: "The len of ycols must greater than 2 ",
178 | }
179 | }
180 | var set map[string]bool
181 | set = make(map[string]bool)
182 | for _, alog := range logs {
183 | set[alog[ycols[0]]] = true
184 | }
185 | var series []*datasource.TimeSeries
186 | for flowId := range set {
187 | var points []*datasource.Point
188 | for _, alog := range logs {
189 | if flowId == alog[ycols[0]] {
190 | if alog[ycols[1]] == "null" {
191 | continue
192 | }
193 | floatV, err := strconv.ParseFloat(alog[ycols[1]], 10)
194 | if err != nil {
195 | ds.logger.Error("ParseFloat ycols[1]", "error ", err)
196 | ch <- &datasource.QueryResult{
197 | Error: err.Error(),
198 | }
199 | return
200 | }
201 | floatV1, err := strconv.ParseFloat(alog[xcol], 10)
202 | if err != nil {
203 | ds.logger.Error("ParseFloat xcol", "error ", err)
204 | ch <- &datasource.QueryResult{
205 | Error: err.Error(),
206 | }
207 | return
208 | }
209 | int64V := int64(floatV1)
210 | point := &datasource.Point{
211 | Timestamp: int64V * 1000,
212 | Value: floatV,
213 | }
214 | points = append(points, point)
215 | }
216 | }
217 | timeSeries := &datasource.TimeSeries{
218 | Name: flowId,
219 | Points: points,
220 | }
221 | series = append(series, timeSeries)
222 | }
223 | ch <- &datasource.QueryResult{
224 | RefId: refId,
225 | Series: series,
226 | }
227 | }
228 |
229 | func (ds *SlsDatasource) BuildPieGraph(ch chan *datasource.QueryResult, logs []map[string]string, xcol string, ycols []string, refId string) {
230 | if len(ycols) < 2 {
231 | ch <- &datasource.QueryResult{
232 | Error: "The len of ycols must greater than 2 ",
233 | }
234 | }
235 | var series []*datasource.TimeSeries
236 | for _, alog := range logs {
237 | if alog[ycols[1]] == "null" {
238 | continue
239 | }
240 | floatV, err := strconv.ParseFloat(alog[ycols[1]], 10)
241 | if err != nil {
242 | ds.logger.Error("ParseFloat ycols[1]", "error ", err)
243 | ch <- &datasource.QueryResult{
244 | Error: err.Error(),
245 | }
246 | return
247 | }
248 | point := &datasource.Point{
249 | Timestamp: 0,
250 | Value: floatV,
251 | }
252 | var points []*datasource.Point
253 | points = append(points, point)
254 | timeSeries := &datasource.TimeSeries{
255 | Name: alog[ycols[0]],
256 | Points: points,
257 | }
258 | series = append(series, timeSeries)
259 | }
260 |
261 | ch <- &datasource.QueryResult{
262 | RefId: refId,
263 | Series: series,
264 | }
265 | }
266 |
267 | func (ds *SlsDatasource) BuildTimingGraph(ch chan *datasource.QueryResult, logs []map[string]string, xcol string, ycols []string, series *[]*datasource.TimeSeries) {
268 | ds.SortLogs(logs, xcol)
269 | for _, ycol := range ycols {
270 | var points []*datasource.Point
271 | for _, alog := range logs {
272 | var timestamp int64
273 | var value float64
274 | for k, v := range alog {
275 | if k == xcol {
276 | var err error
277 | timestamp, err = ds.ParseTimestamp(v)
278 | if err != nil {
279 | ds.logger.Error("ParseTimestamp v", "error ", err)
280 | ch <- &datasource.QueryResult{
281 | Error: err.Error(),
282 | }
283 | return
284 | }
285 | }
286 | if k == ycol {
287 | if v == "null" {
288 | continue
289 | }
290 | floatV, err := strconv.ParseFloat(v, 10)
291 | if err != nil {
292 | ds.logger.Error("ParseFloat v", "error ", err)
293 | ch <- &datasource.QueryResult{
294 | Error: err.Error(),
295 | }
296 | return
297 | }
298 | value = floatV
299 | }
300 | }
301 | point := &datasource.Point{
302 | Timestamp: timestamp,
303 | Value: value,
304 | }
305 | points = append(points, point)
306 | }
307 | timeSeries := &datasource.TimeSeries{
308 | Name: ycol,
309 | Points: points,
310 | }
311 | *series = append(*series, timeSeries)
312 | }
313 | }
314 |
315 | func (ds *SlsDatasource) BuildTable(ch chan *datasource.QueryResult, logs []map[string]string, xcol string, ycols []string, tables *[]*datasource.Table) {
316 | var columns []*datasource.TableColumn
317 |
318 | for _, ycol := range ycols {
319 | columns = append(columns, &datasource.TableColumn{
320 | Name: ycol,
321 | })
322 | }
323 | var rows []*datasource.TableRow
324 | for _, alog := range logs {
325 | var values []*datasource.RowValue
326 | for _, ycol := range ycols {
327 | for k, v := range alog {
328 | if k == ycol {
329 | values = append(values, ds.GetValue(v))
330 | }
331 | }
332 | }
333 | if len(ycols) == 1 && ycols[0] == "" {
334 | for k, v := range alog {
335 | if k != "__source__" && k != "__time__" {
336 | values = append(values, ds.GetValue(v))
337 | }
338 | }
339 | }
340 | rows = append(rows, &datasource.TableRow{Values: values})
341 | }
342 | table := &datasource.Table{
343 | Columns: columns,
344 | Rows: rows,
345 | }
346 | *tables = append(*tables, table)
347 | }
348 |
--------------------------------------------------------------------------------
/pkg/models.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type LogSource struct {
4 | Project string `json:"project"`
5 | LogStore string `json:"logstore"`
6 | User string `json:"user"`
7 | Password string `json:"password"`
8 | }
9 |
10 | type QueryInfo struct {
11 | Query string `json:"query"`
12 | Xcol string `json:"xcol"`
13 | Ycol string `json:"ycol"`
14 | }
15 |
--------------------------------------------------------------------------------
/pkg/plugin.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/grafana/grafana_plugin_model/go/datasource"
5 | hclog "github.com/hashicorp/go-hclog"
6 | plugin "github.com/hashicorp/go-plugin"
7 | )
8 |
9 | var pluginLogger = hclog.New(&hclog.LoggerOptions{
10 | Name: "aliyun-log-backend-datasource",
11 | Level: hclog.LevelFromString("WARN"),
12 | })
13 |
14 | func main() {
15 | pluginLogger.Debug("Running Aliyun log servcie backend datasource")
16 |
17 | plugin.Serve(&plugin.ServeConfig{
18 |
19 | HandshakeConfig: plugin.HandshakeConfig{
20 | ProtocolVersion: 1,
21 | MagicCookieKey: "grafana_plugin_type",
22 | MagicCookieValue: "datasource",
23 | },
24 | Plugins: map[string]plugin.Plugin{
25 | "aliyun-log-backend-datasource": &datasource.DatasourcePluginImpl{Plugin: &SlsDatasource{
26 | logger: pluginLogger,
27 | }},
28 | },
29 |
30 | // A non-nil value here enables gRPC serving for this plugin...
31 | GRPCServer: plugin.DefaultGRPCServer,
32 | })
33 | }
34 |
--------------------------------------------------------------------------------
/spec/datasource.test.ts:
--------------------------------------------------------------------------------
1 | import { GenericDatasource, mapToTextValue } from "../src/datasource";
2 |
3 | describe('GenericDatasource', function() {
4 | let ctx: any = {};
5 |
6 | beforeEach(function() {
7 | ctx.backendSrv = {};
8 | ctx.templateSrv = {
9 | replace: jest.fn().mockImplementation(value => value)
10 | };
11 | ctx.ds = new GenericDatasource({}, ctx.backendSrv, ctx.templateSrv);
12 | });
13 |
14 | describe('When invoking a query', () => {
15 |
16 | it('should return an empty array when no targets are set', done => {
17 | ctx.ds.query({targets: []}).then(result => {
18 | expect(result.data).toHaveLength(0);
19 | done();
20 | });
21 | });
22 |
23 | it('should return the server results when a target is set', done => {
24 | ctx.backendSrv.datasourceRequest = jest.fn().mockResolvedValue({
25 | data: { results: [{
26 | refId: 'A',
27 | series: [{
28 | name: 'X',
29 | points: [1, 2, 3]
30 | }]
31 | }]}
32 | });
33 |
34 | ctx.ds.query({ targets: ['hits'], range: { from: 'now-1h', to: 'now' }}).then(result => {
35 | var series = result.data[0];
36 | expect(series.target).toBe('X');
37 | expect(series.datapoints).toHaveLength(3);
38 | done();
39 | });
40 | });
41 | });
42 |
43 | describe('When invoking a metricFindQuery', () => {
44 |
45 | it ('should return the metric results when a target is null', done => {
46 | ctx.backendSrv.datasourceRequest = jest.fn().mockResolvedValue({
47 | data: [
48 | "metric_0",
49 | "metric_1",
50 | "metric_2",
51 | ]
52 | });
53 |
54 | ctx.ds.metricFindQuery({target: null}).then(function(result) {
55 | expect(result).toHaveLength(3);
56 | expect(result[0].text).toBe('metric_0');
57 | expect(result[0].value).toBe('metric_0');
58 | expect(result[1].text).toBe('metric_1');
59 | expect(result[1].value).toBe('metric_1');
60 | expect(result[2].text).toBe('metric_2');
61 | expect(result[2].value).toBe('metric_2');
62 | done();
63 | });
64 | });
65 |
66 | it ('should return the metric target results when a target is set', done => {
67 | ctx.backendSrv.datasourceRequest = jest.fn().mockImplementation(request => {
68 | var target = request.data.target;
69 | var result = [target + "_0", target + "_1", target + "_2"];
70 |
71 | return Promise.resolve({
72 | _request: request,
73 | data: result
74 | });
75 | });
76 |
77 | ctx.ds.metricFindQuery('search').then(function(result) {
78 | expect(result).toHaveLength(3);
79 | expect(result[0].text).toBe('search_0');
80 | expect(result[0].value).toBe('search_0');
81 | expect(result[1].text).toBe('search_1');
82 | expect(result[1].value).toBe('search_1');
83 | expect(result[2].text).toBe('search_2');
84 | expect(result[2].value).toBe('search_2');
85 | done();
86 | });
87 | });
88 |
89 | it ('should return the metric results when the target is an empty string', done => {
90 | ctx.backendSrv.datasourceRequest = jest.fn().mockResolvedValue({
91 | data: [
92 | "metric_0",
93 | "metric_1",
94 | "metric_2",
95 | ]
96 | });
97 |
98 | ctx.ds.metricFindQuery('').then(function(result) {
99 | expect(result).toHaveLength(3);
100 | expect(result[0].text).toBe('metric_0');
101 | expect(result[0].value).toBe('metric_0');
102 | expect(result[1].text).toBe('metric_1');
103 | expect(result[1].value).toBe('metric_1');
104 | expect(result[2].text).toBe('metric_2');
105 | expect(result[2].value).toBe('metric_2');
106 | done();
107 | });
108 | });
109 |
110 | it ('should return the metric results when the args are an empty object', done => {
111 | ctx.backendSrv.datasourceRequest = jest.fn().mockResolvedValue({
112 | data: [
113 | "metric_0",
114 | "metric_1",
115 | "metric_2",
116 | ]
117 | });
118 |
119 | ctx.ds.metricFindQuery().then(function(result) {
120 | expect(result).toHaveLength(3);
121 | expect(result[0].text).toBe('metric_0');
122 | expect(result[0].value).toBe('metric_0');
123 | expect(result[1].text).toBe('metric_1');
124 | expect(result[1].value).toBe('metric_1');
125 | expect(result[2].text).toBe('metric_2');
126 | expect(result[2].value).toBe('metric_2');
127 | done();
128 | });
129 | });
130 |
131 | it ('should return the metric target results when the args are a string', done => {
132 | ctx.backendSrv.datasourceRequest = jest.fn().mockImplementation(request => {
133 | var target = request.data.target;
134 | var result = [target + "_0", target + "_1", target + "_2"];
135 |
136 | return Promise.resolve({
137 | _request: request,
138 | data: result
139 | });
140 | });
141 |
142 | ctx.ds.metricFindQuery('search').then(function(result) {
143 | expect(result).toHaveLength(3);
144 | expect(result[0].text).toBe('search_0');
145 | expect(result[0].value).toBe('search_0');
146 | expect(result[1].text).toBe('search_1');
147 | expect(result[1].value).toBe('search_1');
148 | expect(result[2].text).toBe('search_2');
149 | expect(result[2].value).toBe('search_2');
150 | done();
151 | });
152 | });
153 | });
154 |
155 | describe('When mapping result to text value', () => {
156 |
157 | it ('should return data as text and as value', done => {
158 | var result = mapToTextValue({data: ["zero", "one", "two"]});
159 |
160 | expect(result).toHaveLength(3);
161 | expect(result[0].text).toBe('zero');
162 | expect(result[0].value).toBe('zero');
163 | expect(result[1].text).toBe('one');
164 | expect(result[1].value).toBe('one');
165 | expect(result[2].text).toBe('two');
166 | expect(result[2].value).toBe('two');
167 | done();
168 | });
169 |
170 | it ('should return text as text and value as value', done => {
171 | var data = [
172 | {text: "zero", value: "value_0"},
173 | {text: "one", value: "value_1"},
174 | {text: "two", value: "value_2"},
175 | ];
176 |
177 | var result = mapToTextValue({data: data});
178 |
179 | expect(result).toHaveLength(3);
180 | expect(result[0].text).toBe('zero');
181 | expect(result[0].value).toBe('value_0');
182 | expect(result[1].text).toBe('one');
183 | expect(result[1].value).toBe('value_1');
184 | expect(result[2].text).toBe('two');
185 | expect(result[2].value).toBe('value_2');
186 | done();
187 | });
188 |
189 | it ('should return data as text and index as value', done => {
190 | var data = [
191 | {a: "zero", b: "value_0"},
192 | {a: "one", b: "value_1"},
193 | {a: "two", b: "value_2"},
194 | ];
195 |
196 | var result = mapToTextValue({data: data});
197 |
198 | expect(result).toHaveLength(3);
199 | expect(result[0].text).toBe(data[0]);
200 | expect(result[0].value).toBe(0);
201 | expect(result[1].text).toBe(data[1]);
202 | expect(result[1].value).toBe(1);
203 | expect(result[2].text).toBe(data[2]);
204 | expect(result[2].value).toBe(2);
205 | done();
206 | });
207 | });
208 | });
209 |
--------------------------------------------------------------------------------
/src/css/query-editor.css:
--------------------------------------------------------------------------------
1 | .generic-datasource-query-row .query-keyword {
2 | width: 75px;
3 | }
--------------------------------------------------------------------------------
/src/datasource.js:
--------------------------------------------------------------------------------
1 | import _ from "lodash";
2 | export class GenericDatasource {
3 | /** @ngInject */
4 | constructor(instanceSettings, backendSrv, templateSrv) {
5 | this.backendSrv = backendSrv;
6 | this.templateSrv = templateSrv;
7 | this.type = instanceSettings.type;
8 | this.url = instanceSettings.url;
9 | this.name = instanceSettings.name;
10 | this.id = instanceSettings.id;
11 | this.withCredentials = instanceSettings.withCredentials;
12 | this.headers = { 'Content-Type': 'application/json' };
13 | if (typeof instanceSettings.basicAuth === 'string' && instanceSettings.basicAuth.length > 0) {
14 | this.headers['Authorization'] = instanceSettings.basicAuth;
15 | }
16 | }
17 | query(options) {
18 | const query = this.buildQueryParameters(options);
19 | query.targets = query.targets.filter(t => !t.hide);
20 | if (query.targets.length <= 0) {
21 | return Promise.resolve({ data: [] });
22 | }
23 | return this.doTsdbRequest(query).then(handleTsdbResponse);
24 | }
25 | testDatasource() {
26 | const to = new Date().getTime();
27 | const from = to - 5000;
28 | const str = '{"requestId":"Q100","timezone":"","range":{"from":"' + from + '","to":"' + to + '"},' +
29 | '"targets":[{"queryType":"query","target":"count","refId":"A","type":"timeserie","datasourceId":' + this.id + ',' +
30 | '"query":"* | select count(*) as count","ycol":"count"}]}';
31 | const query = JSON.parse(str);
32 | return this.doTsdbRequest(query).then(response => {
33 | if (response.status === 200) {
34 | return { status: "success", message: "Data source is working", title: "Success" };
35 | }
36 | else {
37 | return { status: "failed", message: "Data source is not working", title: "Error" };
38 | }
39 | }).catch(() => {
40 | return { status: "failed", message: "Data source is not working", title: "Error" };
41 | });
42 | }
43 | annotationQuery(options) {
44 | const query = this.templateSrv.replace(options.annotation.query, {}, 'glob');
45 | const annotationQuery = {
46 | range: options.range,
47 | annotation: {
48 | name: options.annotation.name,
49 | datasource: options.annotation.datasource,
50 | enable: options.annotation.enable,
51 | iconColor: options.annotation.iconColor,
52 | query: query
53 | },
54 | rangeRaw: options.rangeRaw
55 | };
56 | return this.doRequest({
57 | url: this.url + '/annotations',
58 | method: 'POST',
59 | data: annotationQuery
60 | }).then(result => {
61 | return result.data;
62 | });
63 | }
64 | metricFindQuery(q) {
65 | q = this.templateSrv.replace(q, {}, 'glob');
66 | const to = this.templateSrv.timeRange.to.unix() * 1000;
67 | const from = this.templateSrv.timeRange.from.unix() * 1000;
68 | const str = '{"requestId":"Q100","timezone":"","range":{"from":"' + from + '","to":"' + to + '"},' +
69 | '"targets":[{"queryType":"query","target":"query","refId":"A","type":"timeserie","datasourceId":' + this.id + ',' +
70 | '"query":"' + q + '"}]}';
71 | const query = JSON.parse(str);
72 | return this.doTsdbRequest(query).then(response => {
73 | const res = handleTsdbResponse(response);
74 | if (res && res.data && res.data.length) {
75 | let rows = res.data[0].rows;
76 | rows = rows.map(item => item[0]);
77 | return rows;
78 | }
79 | else {
80 | return [];
81 | }
82 | }).then(mapToTextValue);
83 | }
84 | doRequest(options) {
85 | options.withCredentials = this.withCredentials;
86 | options.headers = this.headers;
87 | return this.backendSrv.datasourceRequest(options);
88 | }
89 | doTsdbRequest(options) {
90 | const tsdbRequestData = {
91 | queries: options.targets,
92 | };
93 | if (options.range) {
94 | tsdbRequestData.from = options.range.from.valueOf().toString();
95 | tsdbRequestData.to = options.range.to.valueOf().toString();
96 | }
97 | return this.backendSrv.datasourceRequest({
98 | url: '/api/tsdb/query',
99 | method: 'POST',
100 | data: tsdbRequestData
101 | });
102 | }
103 | buildQueryParameters(options) {
104 | //remove placeholder targets
105 | options.targets = _.filter(options.targets, target => {
106 | return target.target !== 'select metric';
107 | });
108 | options.targets = _.map(options.targets, target => {
109 | return {
110 | queryType: 'query',
111 | target: this.templateSrv.replace(target.target, options.scopedVars, 'regex'),
112 | refId: target.refId,
113 | hide: target.hide,
114 | type: target.type || 'timeserie',
115 | datasourceId: this.id,
116 | query: this.replaceQueryParameters(target, options),
117 | xcol: target.xcol,
118 | ycol: target.ycol
119 | };
120 | });
121 | return options;
122 | }
123 | replaceQueryParameters(target, options) {
124 | let query = this.templateSrv.replace(target.query, options.scopedVars, function (value, variable) {
125 | if (typeof value == "object" && (variable.multi || variable.includeAll)) {
126 | const a = [];
127 | value.forEach(function (v) {
128 | if (variable.name == variable.label)
129 | a.push('"' + variable.name + '":"' + v + '"');
130 | else
131 | a.push('"' + v + '"');
132 | });
133 | return a.join(" OR ");
134 | }
135 | if (_.isArray(value)) {
136 | return value.join(' OR ');
137 | }
138 | return value;
139 | });
140 | const re = /\$([0-9]+)([dmhs])/g;
141 | const reArray = query.match(re);
142 | _(reArray).forEach(function (col) {
143 | const old = col;
144 | col = col.replace("$", '');
145 | let sec = 1;
146 | if (col.indexOf("s") != -1)
147 | sec = 1;
148 | else if (col.indexOf("m") != -1)
149 | sec = 60;
150 | else if (col.indexOf("h") != -1)
151 | sec = 3600;
152 | else if (col.indexOf("d") != -1)
153 | sec = 3600 * 24;
154 | col = col.replace(/[smhd]/g, '');
155 | let v = parseInt(col);
156 | v = v * sec;
157 | console.log(old, v, col, sec, query);
158 | query = query.replace(old, v);
159 | });
160 | if (query.indexOf("#time_end") != -1) {
161 | query = query.replace("#time_end", parseInt(String(options.range.to._d.getTime() / 1000)));
162 | }
163 | if (query.indexOf("#time_begin") != -1) {
164 | query = query.replace("#time_begin", parseInt(String(options.range.from._d.getTime() / 1000)));
165 | }
166 | return query;
167 | }
168 | getTagKeys(options) {
169 | return new Promise((resolve) => {
170 | this.doRequest({
171 | url: this.url + '/tag-keys',
172 | method: 'POST',
173 | data: options
174 | }).then(result => {
175 | return resolve(result.data);
176 | });
177 | });
178 | }
179 | getTagValues(options) {
180 | return new Promise((resolve) => {
181 | this.doRequest({
182 | url: this.url + '/tag-values',
183 | method: 'POST',
184 | data: options
185 | }).then(result => {
186 | return resolve(result.data);
187 | });
188 | });
189 | }
190 | }
191 | export function handleTsdbResponse(response) {
192 | const res = [];
193 | _.forEach(response.data.results, r => {
194 | _.forEach(r.series, s => {
195 | res.push({ target: s.name, datapoints: s.points });
196 | });
197 | _.forEach(r.tables, t => {
198 | t.type = 'table';
199 | t.refId = r.refId;
200 | res.push(t);
201 | });
202 | });
203 | response.data = res;
204 | console.log(res);
205 | return response;
206 | }
207 | export function mapToTextValue(result) {
208 | return _.map(result, (d, i) => {
209 | if (d && d.text && d.value) {
210 | return { text: d.text, value: d.value };
211 | }
212 | else if (_.isObject(d)) {
213 | return { text: d, value: i };
214 | }
215 | return { text: d, value: d };
216 | });
217 | }
218 | //# sourceMappingURL=datasource.js.map
--------------------------------------------------------------------------------
/src/datasource.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"datasource.js","sourceRoot":"","sources":["datasource.ts"],"names":[],"mappings":"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"}
--------------------------------------------------------------------------------
/src/datasource.ts:
--------------------------------------------------------------------------------
1 | import _ from "lodash";
2 |
3 | interface TSDBRequest {
4 | queries: any[];
5 | from?: string;
6 | to?: string;
7 | }
8 |
9 | interface TSDBQuery {
10 | datasourceId: string;
11 | target: any;
12 | queryType?: TSDBQueryType;
13 | refId?: string;
14 | hide?: boolean;
15 | type?: 'timeserie' | 'table';
16 | }
17 |
18 | type TSDBQueryType = 'query' | 'search';
19 |
20 | interface TSDBRequestOptions {
21 | range?: {
22 | from: any;
23 | to: any;
24 | };
25 | targets: TSDBQuery[];
26 | }
27 |
28 | export class GenericDatasource {
29 | name: string;
30 | type: string;
31 | id: string;
32 | url: string;
33 | withCredentials: boolean;
34 | instanceSettings: any;
35 | headers: any;
36 |
37 | /** @ngInject */
38 | constructor(instanceSettings, private backendSrv, private templateSrv) {
39 | this.type = instanceSettings.type;
40 | this.url = instanceSettings.url;
41 | this.name = instanceSettings.name;
42 | this.id = instanceSettings.id;
43 | this.withCredentials = instanceSettings.withCredentials;
44 | this.headers = {'Content-Type': 'application/json'};
45 | if (typeof instanceSettings.basicAuth === 'string' && instanceSettings.basicAuth.length > 0) {
46 | this.headers['Authorization'] = instanceSettings.basicAuth;
47 | }
48 | }
49 |
50 | query(options) {
51 | const query = this.buildQueryParameters(options);
52 | query.targets = query.targets.filter(t => !t.hide);
53 |
54 | if (query.targets.length <= 0) {
55 | return Promise.resolve({data: []});
56 | }
57 |
58 | return this.doTsdbRequest(query).then(handleTsdbResponse);
59 | }
60 |
61 | testDatasource() {
62 | const to = new Date().getTime();
63 | const from = to - 5000;
64 | const str = '{"requestId":"Q100","timezone":"","range":{"from":"' + from + '","to":"' + to + '"},' +
65 | '"targets":[{"queryType":"query","target":"count","refId":"A","type":"timeserie","datasourceId":' + this.id + ',' +
66 | '"query":"* | select count(*) as count","ycol":"count"}]}';
67 | const query = JSON.parse(str);
68 | return this.doTsdbRequest(query).then(response => {
69 | if (response.status === 200) {
70 | return { status: "success", message: "Data source is working", title: "Success" };
71 | } else {
72 | return { status: "failed", message: "Data source is not working", title: "Error" };
73 | }
74 | }).catch(() => {
75 | return { status: "failed", message: "Data source is not working", title: "Error" };
76 | });
77 | }
78 |
79 | annotationQuery(options) {
80 | const query = this.templateSrv.replace(options.annotation.query, {}, 'glob');
81 | const annotationQuery = {
82 | range: options.range,
83 | annotation: {
84 | name: options.annotation.name,
85 | datasource: options.annotation.datasource,
86 | enable: options.annotation.enable,
87 | iconColor: options.annotation.iconColor,
88 | query: query
89 | },
90 | rangeRaw: options.rangeRaw
91 | };
92 |
93 | return this.doRequest({
94 | url: this.url + '/annotations',
95 | method: 'POST',
96 | data: annotationQuery
97 | }).then(result => {
98 | return result.data;
99 | });
100 | }
101 |
102 | metricFindQuery(q) {
103 | q = this.templateSrv.replace(q, {}, 'glob');
104 | const to = this.templateSrv.timeRange.to.unix()*1000;
105 | const from = this.templateSrv.timeRange.from.unix()*1000;
106 | const str = '{"requestId":"Q100","timezone":"","range":{"from":"' + from + '","to":"' + to + '"},' +
107 | '"targets":[{"queryType":"query","target":"query","refId":"A","type":"timeserie","datasourceId":' + this.id + ',' +
108 | '"query":"'+ q +'"}]}';
109 | const query = JSON.parse(str);
110 | return this.doTsdbRequest(query).then(response => {
111 | const res = handleTsdbResponse(response);
112 | if (res && res.data && res.data.length) {
113 | let rows = res.data[0].rows;
114 | rows = rows.map(item => item[0]);
115 | return rows;
116 | } else {
117 | return [];
118 | }
119 | }).then(mapToTextValue);
120 | }
121 |
122 | doRequest(options) {
123 | options.withCredentials = this.withCredentials;
124 | options.headers = this.headers;
125 |
126 | return this.backendSrv.datasourceRequest(options);
127 | }
128 |
129 | doTsdbRequest(options: TSDBRequestOptions) {
130 | const tsdbRequestData: TSDBRequest = {
131 | queries: options.targets,
132 | };
133 |
134 | if (options.range) {
135 | tsdbRequestData.from = options.range.from.valueOf().toString();
136 | tsdbRequestData.to = options.range.to.valueOf().toString();
137 | }
138 |
139 | return this.backendSrv.datasourceRequest({
140 | url: '/api/tsdb/query',
141 | method: 'POST',
142 | data: tsdbRequestData
143 | });
144 | }
145 |
146 | buildQueryParameters(options: any): TSDBRequestOptions {
147 | //remove placeholder targets
148 | options.targets = _.filter(options.targets, target => {
149 | return target.target !== 'select metric';
150 | });
151 |
152 | options.targets = _.map(options.targets, target => {
153 | return {
154 | queryType: 'query',
155 | target: this.templateSrv.replace(target.target, options.scopedVars, 'regex'),
156 | refId: target.refId,
157 | hide: target.hide,
158 | type: target.type || 'timeserie',
159 | datasourceId: this.id,
160 | query: this.replaceQueryParameters(target, options),
161 | xcol: target.xcol,
162 | ycol: target.ycol
163 | };
164 | });
165 |
166 | return options;
167 | }
168 |
169 | replaceQueryParameters(target,options): TSDBRequestOptions {
170 | let query = this.templateSrv.replace(target.query, options.scopedVars, function (value, variable) {
171 | if (typeof value == "object" && (variable.multi || variable.includeAll)) {
172 | const a = [];
173 | value.forEach(function (v) {
174 | if (variable.name == variable.label) a.push('"' + variable.name + '":"' + v + '"');else a.push('"' + v + '"');
175 | });
176 | return a.join(" OR ");
177 | }
178 | if (_.isArray(value)) {
179 | return value.join(' OR ');
180 | }
181 | return value;
182 | });
183 | const re = /\$([0-9]+)([dmhs])/g;
184 | const reArray = query.match(re);
185 | _(reArray).forEach(function (col) {
186 | const old = col;
187 | col = col.replace("$", '');
188 | let sec = 1;
189 | if (col.indexOf("s") != -1) sec = 1;else if (col.indexOf("m") != -1) sec = 60;else if (col.indexOf("h") != -1) sec = 3600;else if (col.indexOf("d") != -1) sec = 3600 * 24;
190 | col = col.replace(/[smhd]/g, '');
191 | let v = parseInt(col);
192 | v = v * sec;
193 | console.log(old, v, col, sec, query);
194 | query = query.replace(old, v);
195 | });
196 | if (query.indexOf("#time_end") != -1) {
197 | query = query.replace("#time_end", parseInt(String(options.range.to._d.getTime() / 1000)));
198 | }
199 | if (query.indexOf("#time_begin") != -1) {
200 | query = query.replace("#time_begin", parseInt(String(options.range.from._d.getTime() / 1000)));
201 | }
202 | return query;
203 | }
204 |
205 | getTagKeys(options) {
206 | return new Promise((resolve) => {
207 | this.doRequest({
208 | url: this.url + '/tag-keys',
209 | method: 'POST',
210 | data: options
211 | }).then(result => {
212 | return resolve(result.data);
213 | });
214 | });
215 | }
216 |
217 | getTagValues(options) {
218 | return new Promise((resolve) => {
219 | this.doRequest({
220 | url: this.url + '/tag-values',
221 | method: 'POST',
222 | data: options
223 | }).then(result => {
224 | return resolve(result.data);
225 | });
226 | });
227 | }
228 | }
229 |
230 | export function handleTsdbResponse(response) {
231 | const res= [];
232 | _.forEach(response.data.results, r => {
233 | _.forEach(r.series, s => {
234 | res.push({target: s.name, datapoints: s.points});
235 | });
236 | _.forEach(r.tables, t => {
237 | t.type = 'table';
238 | t.refId = r.refId;
239 | res.push(t);
240 | });
241 | });
242 |
243 | response.data = res;
244 | console.log(res);
245 | return response;
246 | }
247 |
248 | export function mapToTextValue(result) {
249 | return _.map(result, (d, i) => {
250 | if (d && d.text && d.value) {
251 | return { text: d.text, value: d.value };
252 | } else if (_.isObject(d)) {
253 | return { text: d, value: i};
254 | }
255 | return { text: d, value: d };
256 | });
257 | }
258 |
--------------------------------------------------------------------------------
/src/img/sls_logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mayunlei/aliyun-log-grafana-datasource-plugin/1907f18d10e9e52a47169cf0e0bafb5f8a11f89d/src/img/sls_logo.jpg
--------------------------------------------------------------------------------
/src/module.js:
--------------------------------------------------------------------------------
1 | import { GenericDatasource } from './datasource';
2 | import { GenericDatasourceQueryCtrl } from './query_ctrl';
3 | class GenericConfigCtrl {
4 | }
5 | GenericConfigCtrl.templateUrl = 'partials/config.html';
6 | class GenericQueryOptionsCtrl {
7 | }
8 | GenericQueryOptionsCtrl.templateUrl = 'partials/query.options.html';
9 | class GenericAnnotationsQueryCtrl {
10 | }
11 | GenericAnnotationsQueryCtrl.templateUrl = 'partials/annotations.editor.html';
12 | export { GenericDatasource as Datasource, GenericDatasourceQueryCtrl as QueryCtrl, GenericConfigCtrl as ConfigCtrl, GenericQueryOptionsCtrl as QueryOptionsCtrl, GenericAnnotationsQueryCtrl as AnnotationsQueryCtrl };
13 | //# sourceMappingURL=module.js.map
--------------------------------------------------------------------------------
/src/module.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"module.js","sourceRoot":"","sources":["module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,iBAAiB,EAAC,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAC,0BAA0B,EAAC,MAAM,cAAc,CAAC;AAExD,MAAM,iBAAiB;;AACd,6BAAW,GAAG,sBAAsB,CAAC;AAG9C,MAAM,uBAAuB;;AACpB,mCAAW,GAAG,6BAA6B,CAAC;AAGrD,MAAM,2BAA2B;;AACxB,uCAAW,GAAG,kCAAkC,CAAA;AAGzD,OAAO,EACL,iBAAiB,IAAI,UAAU,EAC/B,0BAA0B,IAAI,SAAS,EACvC,iBAAiB,IAAI,UAAU,EAC/B,uBAAuB,IAAI,gBAAgB,EAC3C,2BAA2B,IAAI,oBAAoB,EACpD,CAAC"}
--------------------------------------------------------------------------------
/src/module.ts:
--------------------------------------------------------------------------------
1 | import {GenericDatasource} from './datasource';
2 | import {GenericDatasourceQueryCtrl} from './query_ctrl';
3 |
4 | class GenericConfigCtrl {
5 | static templateUrl = 'partials/config.html';
6 | }
7 |
8 | class GenericQueryOptionsCtrl {
9 | static templateUrl = 'partials/query.options.html';
10 | }
11 |
12 | class GenericAnnotationsQueryCtrl {
13 | static templateUrl = 'partials/annotations.editor.html'
14 | }
15 |
16 | export {
17 | GenericDatasource as Datasource,
18 | GenericDatasourceQueryCtrl as QueryCtrl,
19 | GenericConfigCtrl as ConfigCtrl,
20 | GenericQueryOptionsCtrl as QueryOptionsCtrl,
21 | GenericAnnotationsQueryCtrl as AnnotationsQueryCtrl
22 | };
23 |
--------------------------------------------------------------------------------
/src/partials/annotations.editor.html:
--------------------------------------------------------------------------------
1 |
2 | Query
3 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/partials/config.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | log service details
4 |
12 |
20 | Default query settings
21 |
25 |
--------------------------------------------------------------------------------
/src/partials/query.editor.html:
--------------------------------------------------------------------------------
1 |
2 |
8 |
17 |
--------------------------------------------------------------------------------
/src/partials/query.options.html:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/plugin.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "LogService",
3 | "id": "grafana-log-service-datasource",
4 | "type": "datasource",
5 |
6 | "partials": {
7 | "config": "public/app/plugins/datasource/simplejson/partials/config.html"
8 | },
9 |
10 | "metrics": true,
11 | "annotations": true,
12 | "backend": true,
13 | "alerting": true,
14 | "executable": "aliyun-log-plugin",
15 |
16 | "info": {
17 | "description": "aliyun log datasource",
18 | "author": {
19 | "name": "aliyun log service dev",
20 | "url": "https://aliyun.com/product/sls"
21 | },
22 | "logos": {
23 | "small": "img/sls_logo.jpg",
24 | "large": "img/sls_logo.jpg"
25 | },
26 | "links": [
27 | {
28 | "name": "GitHub",
29 | "url": "https://github.com/grafana/simple-json-backend-datasource"
30 | },
31 | {
32 | "name": "MIT License",
33 | "url": "https://github.com/grafana/simple-json-backend-datasource/blob/master/LICENSE"
34 | }
35 | ],
36 | "version": "1.0.0",
37 | "updated": "2019-11-09"
38 | },
39 |
40 | "dependencies": {
41 | "grafanaVersion": "6.x.x",
42 | "plugins": []
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/query_ctrl.js:
--------------------------------------------------------------------------------
1 | import { QueryCtrl } from 'app/plugins/sdk';
2 | import './css/query-editor.css!';
3 | export class GenericDatasourceQueryCtrl extends QueryCtrl {
4 | /** @ngInject */
5 | constructor($scope, $injector) {
6 | super($scope, $injector);
7 | this.target.target = this.target.ycol;
8 | this.target.type = this.target.type || 'timeserie';
9 | }
10 | getOptions(query) {
11 | return this.datasource.metricFindQuery(query || '');
12 | }
13 | toggleEditorMode() {
14 | this.target.rawQuery = !this.target.rawQuery;
15 | }
16 | onChangeInternal() {
17 | this.panelCtrl.refresh(); // Asks the panel to refresh data.
18 | }
19 | }
20 | GenericDatasourceQueryCtrl.templateUrl = 'partials/query.editor.html';
21 | //# sourceMappingURL=query_ctrl.js.map
--------------------------------------------------------------------------------
/src/query_ctrl.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"query_ctrl.js","sourceRoot":"","sources":["query_ctrl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAC1C,OAAO,yBAAyB,CAAC;AAEjC,MAAM,OAAO,0BAA2B,SAAQ,SAAS;IAKvD,gBAAgB;IAChB,YAAY,MAAM,EAAE,SAAS;QAC3B,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QACzB,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;QACtC,IAAI,CAAC,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,WAAW,CAAC;IACrD,CAAC;IAED,UAAU,CAAC,KAAK;QACd,OAAO,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,gBAAgB;QACd,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;IAC/C,CAAC;IAED,gBAAgB;QACd,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC,kCAAkC;IAC9D,CAAC;;AArBM,sCAAW,GAAG,4BAA4B,CAAC"}
--------------------------------------------------------------------------------
/src/query_ctrl.ts:
--------------------------------------------------------------------------------
1 | import {QueryCtrl} from 'app/plugins/sdk';
2 | import './css/query-editor.css!';
3 |
4 | export class GenericDatasourceQueryCtrl extends QueryCtrl {
5 | static templateUrl = 'partials/query.editor.html';
6 |
7 | scope: any;
8 |
9 | /** @ngInject */
10 | constructor($scope, $injector) {
11 | super($scope, $injector);
12 | this.target.target = this.target.ycol;
13 | this.target.type = this.target.type || 'timeserie';
14 | }
15 |
16 | getOptions(query) {
17 | return this.datasource.metricFindQuery(query || '');
18 | }
19 |
20 | toggleEditorMode() {
21 | this.target.rawQuery = !this.target.rawQuery;
22 | }
23 |
24 | onChangeInternal() {
25 | this.panelCtrl.refresh(); // Asks the panel to refresh data.
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "moduleResolution": "node",
4 | "target": "es6",
5 | "lib": ["es6", "dom"],
6 | "rootDir": "./src",
7 | "jsx": "react",
8 | "module": "es6",
9 | "baseUrl": "./src",
10 | "declaration": false,
11 | "allowSyntheticDefaultImports": true,
12 | "esModuleInterop": true,
13 | "forceConsistentCasingInFileNames": true,
14 | "importHelpers": false,
15 | "noEmitHelpers": true,
16 | "removeComments": false,
17 | "inlineSourceMap": false,
18 | "sourceMap": true,
19 | "noEmitOnError": false,
20 | "emitDecoratorMetadata": false,
21 | "experimentalDecorators": true,
22 | "noImplicitReturns": true,
23 | "noImplicitThis": true,
24 | "noImplicitUseStrict": false,
25 | "noImplicitAny": false,
26 | "downlevelIteration": true,
27 | // "noUnusedLocals": true,
28 | "pretty": true,
29 | "typeRoots": ["node_modules/@types"],
30 | "skipLibCheck": true
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/webpack/webpack.base.conf.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const CopyWebpackPlugin = require('copy-webpack-plugin');
4 | const CleanWebpackPlugin = require('clean-webpack-plugin');
5 |
6 | function resolve(dir) {
7 | return path.join(__dirname, '..', dir);
8 | }
9 |
10 | module.exports = {
11 | target: 'node',
12 | context: resolve('src'),
13 | entry: './module.ts',
14 | output: {
15 | filename: "module.js",
16 | path: resolve('dist'),
17 | libraryTarget: "amd"
18 | },
19 | externals: [
20 | 'jquery', 'lodash', 'moment', 'angular',
21 | function(context, request, callback) {
22 | var prefix = 'grafana/';
23 | if (request.indexOf(prefix) === 0) {
24 | return callback(null, request.substr(prefix.length));
25 | }
26 | callback();
27 | }
28 | ],
29 | plugins: [
30 | new webpack.optimize.OccurrenceOrderPlugin(),
31 | new CopyWebpackPlugin([
32 | { from: '../LICENSE' },
33 | { from: '../README.md' },
34 | { from: 'plugin.json' },
35 | { from: 'img/*' },
36 | { from: 'partials/*' }
37 | ]),
38 | new CleanWebpackPlugin({
39 | cleanStaleWebpackAssets: false,
40 | cleanAfterEveryBuildPatterns: [
41 | '!README.md', '!LICENSE', '!plugin.json', '!img/*', '!partials/*',
42 | '!simple-json-plugin_linux_amd64', '!simple-json-plugin_windows_amd64.exe',
43 | ],
44 | })
45 | ],
46 | resolve: {
47 | extensions: ['.ts', '.tsx', '.js'],
48 | },
49 | module: {
50 | rules: [
51 | {
52 | test: /\.tsx?$/,
53 | loaders: ['ts-loader'],
54 | exclude: /node_modules/,
55 | },
56 | {
57 | test: /\.css$/,
58 | use: ['style-loader', 'css-loader'],
59 | },
60 | ]
61 | }
62 | };
63 |
--------------------------------------------------------------------------------
/webpack/webpack.dev.conf.js:
--------------------------------------------------------------------------------
1 | const baseWebpackConfig = require('./webpack.base.conf');
2 |
3 | var conf = baseWebpackConfig;
4 | conf.watch = false;
5 | conf.mode = 'development';
6 | conf.devtool = 'source-map';
7 |
8 | module.exports = baseWebpackConfig;
9 |
--------------------------------------------------------------------------------
/webpack/webpack.prod.conf.js:
--------------------------------------------------------------------------------
1 | const baseWebpackConfig = require('./webpack.base.conf');
2 | const ngAnnotatePlugin = require('ng-annotate-webpack-plugin');
3 |
4 |
5 | var conf = baseWebpackConfig;
6 | conf.mode = 'production';
7 |
8 | conf.plugins.push(new ngAnnotatePlugin());
9 |
10 | module.exports = baseWebpackConfig;
11 |
--------------------------------------------------------------------------------