Explorar o código

testdata: logs scenario (#17182)

Adds logs scenario which is quite basic and not that smart 
to begin with. This will hopefully ease development of 
Explore and support for logs in Grafana.
Marcus Efraimsson %!s(int64=6) %!d(string=hai) anos
pai
achega
3b008d06b4

+ 79 - 0
pkg/tsdb/testdata/scenarios.go

@@ -2,6 +2,7 @@ package testdata
 
 import (
 	"encoding/json"
+	"fmt"
 	"math"
 	"math/rand"
 	"strconv"
@@ -261,6 +262,84 @@ func init() {
 			return queryRes
 		},
 	})
+
+	registerScenario(&Scenario{
+		Id:   "logs",
+		Name: "Logs",
+
+		Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
+			from := context.TimeRange.GetFromAsMsEpoch()
+			to := context.TimeRange.GetToAsMsEpoch()
+			lines := query.Model.Get("lines").MustInt64(10)
+			includeLevelColumn := query.Model.Get("levelColumn").MustBool(false)
+
+			logLevelGenerator := newRandomStringProvider([]string{
+				"emerg",
+				"alert",
+				"crit",
+				"critical",
+				"warn",
+				"warning",
+				"err",
+				"eror",
+				"error",
+				"info",
+				"notice",
+				"dbug",
+				"debug",
+				"trace",
+				"",
+			})
+			containerIDGenerator := newRandomStringProvider([]string{
+				"f36a9eaa6d34310686f2b851655212023a216de955cbcc764210cefa71179b1a",
+				"5a354a630364f3742c602f315132e16def594fe68b1e4a195b2fce628e24c97a",
+			})
+			hostnameGenerator := newRandomStringProvider([]string{
+				"srv-001",
+				"srv-002",
+			})
+
+			table := tsdb.Table{
+				Columns: []tsdb.TableColumn{
+					{Text: "time"},
+					{Text: "message"},
+					{Text: "container_id"},
+					{Text: "hostname"},
+				},
+				Rows: []tsdb.RowValues{},
+			}
+
+			if includeLevelColumn {
+				table.Columns = append(table.Columns, tsdb.TableColumn{Text: "level"})
+			}
+
+			for i := int64(0); i < lines && to > from; i++ {
+				row := tsdb.RowValues{float64(to)}
+
+				logLevel := logLevelGenerator.Next()
+				timeFormatted := time.Unix(to/1000, 0).Format(time.RFC3339)
+				lvlString := ""
+				if !includeLevelColumn {
+					lvlString = fmt.Sprintf("lvl=%s ", logLevel)
+				}
+
+				row = append(row, fmt.Sprintf("t=%s %smsg=\"Request Completed\" logger=context userId=1 orgId=1 uname=admin method=GET path=/api/datasources/proxy/152/api/prom/label status=502 remote_addr=[::1] time_ms=1 size=0 referer=\"http://localhost:3000/explore?left=%%5B%%22now-6h%%22,%%22now%%22,%%22Prometheus%%202.x%%22,%%7B%%7D,%%7B%%22ui%%22:%%5Btrue,true,true,%%22none%%22%%5D%%7D%%5D\"", timeFormatted, lvlString))
+				row = append(row, containerIDGenerator.Next())
+				row = append(row, hostnameGenerator.Next())
+
+				if includeLevelColumn {
+					row = append(row, logLevel)
+				}
+
+				table.Rows = append(table.Rows, row)
+				to -= query.IntervalMs
+			}
+
+			queryRes := tsdb.NewQueryResult()
+			queryRes.Tables = append(queryRes.Tables, &table)
+			return queryRes
+		},
+	})
 }
 
 func getRandomWalk(query *tsdb.Query, tsdbQuery *tsdb.TsdbQuery) *tsdb.QueryResult {

+ 22 - 0
pkg/tsdb/testdata/utils.go

@@ -0,0 +1,22 @@
+package testdata
+
+import (
+	"math/rand"
+	"time"
+)
+
+type randomStringProvider struct {
+	r    *rand.Rand
+	data []string
+}
+
+func newRandomStringProvider(data []string) *randomStringProvider {
+	return &randomStringProvider{
+		r:    rand.New(rand.NewSource(time.Now().UnixNano())),
+		data: data,
+	}
+}
+
+func (p *randomStringProvider) Next() string {
+	return p.data[p.r.Int31n(int32(len(p.data)))]
+}

+ 2 - 1
public/app/plugins/datasource/testdata/datasource.ts

@@ -32,10 +32,11 @@ export class TestDataDatasource extends DataSourceApi<TestDataQuery> {
         scenarioId: item.scenarioId,
         intervalMs: options.intervalMs,
         maxDataPoints: options.maxDataPoints,
+        datasourceId: this.id,
         stringInput: item.stringInput,
         points: item.points,
         alias: item.alias,
-        datasourceId: this.id,
+        ...item,
       };
     });
 

+ 34 - 19
public/app/plugins/datasource/testdata/partials/query.editor.html

@@ -40,8 +40,8 @@
 		<div class="gf-form gf-form">
 			<label class="gf-form-label query-keyword width-7">Type</label>
 			<div class="gf-form-select-wrapper">
-				<select 
-				  ng-model="ctrl.target.stream.type" 
+				<select
+				  ng-model="ctrl.target.stream.type"
 				  class="gf-form-input"
 					ng-options="type for type in ['signal','logs', 'fetch']"
 				  ng-change="ctrl.streamChanged()" />
@@ -50,45 +50,60 @@
 		</div>
 		<div class="gf-form">
 			<label class="gf-form-label query-keyword">Speed (ms)</label>
-			<input type="number" 
-				class="gf-form-input width-5" 
-				placeholder="value" 
-				ng-model="ctrl.target.stream.speed" 
+			<input type="number"
+				class="gf-form-input width-5"
+				placeholder="value"
+				ng-model="ctrl.target.stream.speed"
 				min="10"
 				step="10"
 				ng-change="ctrl.streamChanged()" />
 		</div>
 		<div class="gf-form" ng-if="ctrl.target.stream.type === 'signal'">
 			<label class="gf-form-label query-keyword">Spread</label>
-			<input type="number" 
-				class="gf-form-input width-5" 
-				placeholder="value" 
-				ng-model="ctrl.target.stream.spread" 
+			<input type="number"
+				class="gf-form-input width-5"
+				placeholder="value"
+				ng-model="ctrl.target.stream.spread"
 				min="0.5"
 				step="0.1"
 				ng-change="ctrl.streamChanged()" />
 		</div>
 		<div class="gf-form" ng-if="ctrl.target.stream.type === 'signal'">
 			<label class="gf-form-label query-keyword">Noise</label>
-			<input type="number" 
-				class="gf-form-input width-5" 
-				placeholder="value" 
-				ng-model="ctrl.target.stream.noise" 
+			<input type="number"
+				class="gf-form-input width-5"
+				placeholder="value"
+				ng-model="ctrl.target.stream.noise"
 				min="0"
 				step="0.1"
 				ng-change="ctrl.streamChanged()" />
 		</div>
 		<div class="gf-form gf-form--grow" ng-if="ctrl.target.stream.type === 'fetch'">
 			<label class="gf-form-label query-keyword">URL</label>
-			<input type="string" 
-				class="gf-form-input gf-form-label--grow" 
-				placeholder="Fetch URL" 
-				ng-model="ctrl.target.stream.url" 
-				ng-change="ctrl.streamChanged()" 
+			<input type="string"
+				class="gf-form-input gf-form-label--grow"
+				placeholder="Fetch URL"
+				ng-model="ctrl.target.stream.url"
+				ng-change="ctrl.streamChanged()"
 				ng-model-onblur />
 		</div>
 		<div class="gf-form gf-form--grow" ng-if="ctrl.target.stream.type !== 'fetch'">
 			<div class="gf-form-label gf-form-label--grow"></div>
 		</div>
 	</div>
+
+	<div class="gf-form-inline" ng-if="ctrl.scenario.id === 'logs'">
+		<div class="gf-form">
+			<label class="gf-form-label query-keyword">Lines</label>
+			<input type="number"
+				class="gf-form-input width-5"
+				placeholder="10"
+				ng-model="ctrl.target.lines"
+				ng-change="ctrl.refresh()"
+				ng-model-onblur />
+		</div>
+		<div class="gf-form">
+			<gf-form-switch class="gf-form" label="Level" label-class="query-keyword width-5" checked="ctrl.target.levelColumn" switch-class="max-width-6" on-change="ctrl.refresh()"></gf-form-switch>
+		</div>
+	</div>
 </query-editor-row>