openreplay/backend/pkg/integrations/clients/elasticsearch.go
Alexander f8a8dfa459
[Integrations] small refactoring (#1752)
* feat(backend): small refactoring in integrations

* feat(backend): ignore context timeout error
2023-12-14 11:28:05 +01:00

219 lines
5.6 KiB
Go

package clients
import (
"bytes"
"context"
b64 "encoding/base64"
"encoding/json"
"fmt"
elasticlib "github.com/elastic/go-elasticsearch/v7"
"log"
"strconv"
"time"
"openreplay/backend/pkg/messages"
)
type elasticsearch struct {
Host string
Port json.Number
ApiKeyId string //`json:"api_key_id"`
ApiKey string //`json:"api_key"`
Indexes string
}
type elasticsearchLog struct {
Message string
Time time.Time `json:"utc_time"` // Should be parsed automatically from RFC3339
}
func (es *elasticsearch) Request(c *client) error {
address := es.Host + ":" + es.Port.String()
apiKey := b64.StdEncoding.EncodeToString([]byte(es.ApiKeyId + ":" + es.ApiKey))
cfg := elasticlib.Config{
Addresses: []string{
address,
},
APIKey: apiKey,
}
esC, err := elasticlib.NewClient(cfg)
if err != nil {
log.Println("Error while creating new ES client")
log.Println(err)
return err
}
gteTs := c.requestData.GetLastMessageTimestamp() + 1000
log.Printf("gteTs: %v ", gteTs)
var buf bytes.Buffer
query := map[string]interface{}{
"query": map[string]interface{}{
"bool": map[string]interface{}{
"filter": []map[string]interface{}{
{
"match": map[string]interface{}{
"message": map[string]interface{}{
"query": "openReplaySessionToken=", // asayer_session_id=
},
},
},
{
"range": map[string]interface{}{
"utc_time": map[string]interface{}{
"gte": strconv.FormatUint(gteTs, 10),
"lte": "now",
},
},
},
{
"term": map[string]interface{}{
"tags": "error",
},
},
},
},
},
}
if err := json.NewEncoder(&buf).Encode(query); err != nil {
return fmt.Errorf("Error encoding the query: %s", err)
}
res, err := esC.Search(
esC.Search.WithContext(context.Background()),
esC.Search.WithIndex(es.Indexes),
esC.Search.WithSize(1000),
esC.Search.WithScroll(time.Minute*2),
esC.Search.WithBody(&buf),
esC.Search.WithSort("utc_time:asc"),
)
if err != nil {
return fmt.Errorf("Error getting response: %s", err)
}
defer res.Body.Close()
if res.IsError() {
var e map[string]interface{}
if err := json.NewDecoder(res.Body).Decode(&e); err != nil {
log.Printf("Error parsing the Error response body: %v\n", err)
return fmt.Errorf("Error parsing the Error response body: %v", err)
} else {
log.Printf("Elasticsearch Error [%s] %s: %s\n",
res.Status(),
e["error"],
e["error"],
)
return fmt.Errorf("Elasticsearch Error [%s] %s: %s",
res.Status(),
e["error"],
e["error"],
)
}
}
for {
var esResp map[string]interface{}
if err := json.NewDecoder(res.Body).Decode(&esResp); err != nil {
return fmt.Errorf("Error parsing the response body: %s", err)
// If no error, then convert response to a map[string]interface
}
if _, ok := esResp["hits"]; !ok {
log.Printf("Hits not found in \n%v\n", esResp)
break
}
hits := esResp["hits"].(map[string]interface{})["hits"].([]interface{})
if len(hits) == 0 {
log.Println("No hits found")
break
}
log.Printf("received %d hits", len(hits))
for _, hit := range hits {
// Parse the attributes/fields of the document
doc := hit.(map[string]interface{})
source := doc["_source"].(map[string]interface{})
if _, ok := source["message"]; !ok {
log.Printf("message not found in doc \n%v\n", doc)
c.errChan <- fmt.Errorf("message not found in doc '%v' ", doc)
continue
}
if _, ok := source["utc_time"]; !ok {
log.Printf("utc_time not found in doc \n%v\n", doc)
c.errChan <- fmt.Errorf("utc_time not found in doc '%v' ", doc)
continue
}
parsedTime, err := time.Parse(time.RFC3339, source["utc_time"].(string))
if err != nil {
log.Println("cannot parse time")
c.errChan <- fmt.Errorf("cannot parse RFC3339 time of doc '%v' ", doc)
continue
}
esLog := elasticsearchLog{Message: source["message"].(string), Time: parsedTime}
docID := doc["_id"]
token, err := GetToken(esLog.Message)
if err != nil {
log.Printf("Error generating token: %s\n", err)
c.errChan <- err
continue
}
timestamp := uint64(esLog.Time.UnixMilli())
c.requestData.SetLastMessageTimestamp(timestamp)
var sessionID uint64
sessionID, err = strconv.ParseUint(token, 10, 64)
if err != nil {
log.Printf("Error converting token to uint46: %s\n", err)
sessionID = 0
}
payload, err := json.Marshal(source)
if err != nil {
log.Printf("Error converting source to json: %v\n", source)
continue
}
c.evChan <- &SessionErrorEvent{
//SessionID: sessionID,
SessionID: sessionID,
Token: token,
IntegrationEvent: &messages.IntegrationEvent{
Source: "elasticsearch",
Timestamp: timestamp,
Name: fmt.Sprintf("%v", docID),
Payload: string(payload),
},
}
}
if _, ok := esResp["_scroll_id"]; !ok {
log.Println("_scroll_id not found")
break
}
log.Println("Scrolling...")
scrollId := esResp["_scroll_id"]
res, err = esC.Scroll(
esC.Scroll.WithContext(context.Background()),
esC.Scroll.WithScrollID(fmt.Sprintf("%v", scrollId)),
esC.Scroll.WithScroll(time.Minute*2),
)
if err != nil {
return fmt.Errorf("Error getting scroll response: %s", err)
}
defer res.Body.Close()
if res.IsError() {
var e map[string]interface{}
if err := json.NewDecoder(res.Body).Decode(&e); err != nil {
return fmt.Errorf("Error parsing the response body: %v", err)
} else {
return fmt.Errorf("Elasticsearch [%s] %s: %s",
res.Status(),
e["error"], //.(map[string]interface{})["type"],
e["error"], //.(map[string]interface{})["reason"],
)
}
}
}
return nil
}