@@ -57,9 +57,6 @@ func NewQuickwitDatasource(settings backend.DataSourceInstanceSettings) (instanc
5757 return nil , err
5858 }
5959
60- timeField , toOk := jsonData ["timeField" ].(string )
61- timeOutputFormat , tofOk := jsonData ["timeOutputFormat" ].(string )
62-
6360 logLevelField , ok := jsonData ["logLevelField" ].(string )
6461 if ! ok {
6562 logLevelField = ""
@@ -74,6 +71,7 @@ func NewQuickwitDatasource(settings backend.DataSourceInstanceSettings) (instanc
7471 if ! ok {
7572 index = ""
7673 }
74+ // XXX : Legacy check, should not happen ?
7775 if index == "" {
7876 index = settings .Database
7977 }
@@ -92,18 +90,11 @@ func NewQuickwitDatasource(settings backend.DataSourceInstanceSettings) (instanc
9290 maxConcurrentShardRequests = 256
9391 }
9492
95- if ! toOk || ! tofOk {
96- timeField , timeOutputFormat , err = GetTimestampFieldInfos (index , settings .URL , httpCli )
97- if nil != err {
98- return nil , err
99- }
100- }
101-
10293 configuredFields := es.ConfiguredFields {
103- TimeField : timeField ,
104- TimeOutputFormat : timeOutputFormat ,
10594 LogLevelField : logLevelField ,
10695 LogMessageField : logMessageField ,
96+ TimeField : "" ,
97+ TimeOutputFormat : "" ,
10798 }
10899
109100 model := es.DatasourceInfo {
@@ -113,16 +104,62 @@ func NewQuickwitDatasource(settings backend.DataSourceInstanceSettings) (instanc
113104 Database : index ,
114105 MaxConcurrentShardRequests : int64 (maxConcurrentShardRequests ),
115106 ConfiguredFields : configuredFields ,
107+ ReadyStatus : make (chan es.ReadyStatus , 1 ),
108+ ShouldInit : true ,
116109 }
117- return & QuickwitDatasource {dsInfo : model }, nil
110+
111+ ds := & QuickwitDatasource {dsInfo : model }
112+
113+ // Create an initialization goroutine
114+ go func (ds * QuickwitDatasource , readyStatus chan <- es.ReadyStatus ) {
115+ var status es.ReadyStatus = es.ReadyStatus {
116+ IsReady : false ,
117+ Err : nil ,
118+ }
119+ for {
120+ // Will retry init everytime the channel is consumed until ready
121+ if ! status .IsReady || ds .dsInfo .ShouldInit {
122+ qwlog .Debug ("Initializing Datasource" )
123+ status .IsReady = true
124+ status .Err = nil
125+
126+ indexMetadataList , err := GetIndexesMetadata (ds .dsInfo .Database , ds .dsInfo .URL , ds .dsInfo .HTTPClient )
127+ if err != nil {
128+ status .IsReady = false
129+ status .Err = fmt .Errorf ("failed to get index metadata : %w" , err )
130+ } else if len (indexMetadataList ) == 0 {
131+ status .IsReady = false
132+ status .Err = fmt .Errorf ("no index found for %s" , ds .dsInfo .Database )
133+ } else {
134+ timeField , timeOutputFormat , err := GetTimestampFieldInfos (indexMetadataList )
135+ if nil != err {
136+ status .IsReady = false
137+ status .Err = err
138+ } else if "" == timeField {
139+ status .IsReady = false
140+ status .Err = fmt .Errorf ("timefield is empty for %s" , ds .dsInfo .Database )
141+ } else if "" == timeOutputFormat {
142+ status .Err = fmt .Errorf ("timefield's output_format is empty, logs timestamps will not be parsed correctly for %s" , ds .dsInfo .Database )
143+ }
144+
145+ ds .dsInfo .ConfiguredFields .TimeField = timeField
146+ ds .dsInfo .ConfiguredFields .TimeOutputFormat = timeOutputFormat
147+ ds .dsInfo .ShouldInit = false
148+ }
149+ }
150+ readyStatus <- status
151+ }
152+ }(ds , model .ReadyStatus )
153+ return ds , nil
118154}
119155
120156// Dispose here tells plugin SDK that plugin wants to clean up resources when a new instance
121157// created. As soon as datasource settings change detected by SDK old datasource instance will
122158// be disposed and a new one will be created using NewSampleDatasource factory function.
123159func (ds * QuickwitDatasource ) Dispose () {
124- // Clean up datasource instance resources.
125- // TODO
160+ // FIXME: The ReadyStatus channel should probably be closed here, but doing it
161+ // causes odd calls to healthcheck to fail. Needs investigation
162+ // close(ds.dsInfo.ReadyStatus)
126163}
127164
128165// CheckHealth handles health checks sent from Grafana to the plugin.
@@ -131,13 +168,39 @@ func (ds *QuickwitDatasource) Dispose() {
131168// a datasource is working as expected.
132169func (ds * QuickwitDatasource ) CheckHealth (ctx context.Context , req * backend.CheckHealthRequest ) (* backend.CheckHealthResult , error ) {
133170 res := & backend.CheckHealthResult {}
134-
135171 res .Status = backend .HealthStatusOk
136172 res .Message = "plugin is running"
173+
174+ ds .dsInfo .ShouldInit = true
175+ status := <- ds .dsInfo .ReadyStatus
176+
177+ if nil != status .Err {
178+ res .Status = backend .HealthStatusError
179+ res .Message = fmt .Errorf ("Failed to initialize datasource: %w" , status .Err ).Error ()
180+ } else if "" == ds .dsInfo .ConfiguredFields .TimeField {
181+ res .Status = backend .HealthStatusError
182+ res .Message = fmt .Sprintf ("timefield is missing from index config \" %s\" " , ds .dsInfo .Database )
183+ } else if "" == ds .dsInfo .ConfiguredFields .TimeOutputFormat {
184+ res .Status = backend .HealthStatusError
185+ res .Message = fmt .Sprintf ("timefield's output_format is missing from index config \" %s\" " , ds .dsInfo .Database )
186+ }
187+ qwlog .Debug (res .Message )
188+
137189 return res , nil
138190}
139191
140192func (ds * QuickwitDatasource ) QueryData (ctx context.Context , req * backend.QueryDataRequest ) (* backend.QueryDataResponse , error ) {
193+ // Ensure ds is initialized, we need timestamp infos
194+ status := <- ds .dsInfo .ReadyStatus
195+ if ! status .IsReady {
196+ qwlog .Debug (fmt .Errorf ("Datasource initialization failed: %w" , status .Err ).Error ())
197+ response := & backend.QueryDataResponse {
198+ Responses : backend.Responses {},
199+ }
200+ response .Responses ["__qwQueryDataError" ] = backend .ErrDataResponse (backend .StatusInternal , "Datasource initialization failed" )
201+ return response , nil
202+ }
203+
141204 return queryData (ctx , req .Queries , & ds .dsInfo )
142205}
143206
0 commit comments