package main import ( "context" "encoding/json" "fmt" "log/slog" "strings" "time" "github.com/elastic/go-elasticsearch/v8" ) func NewElasticsearchClient(config ElasticsearchConfig) (*elasticsearch.Client, error) { esConfig := elasticsearch.Config{ Addresses: []string{config.URL}, } if config.Username != "" && config.Password != "" { esConfig.Username = config.Username esConfig.Password = config.Password } if config.APIKey != "" { esConfig.APIKey = config.APIKey } client, err := elasticsearch.NewClient(esConfig) if err != nil { return nil, fmt.Errorf("failed to create elasticsearch client: %w", err) } return client, nil } func TestElasticsearchConnection(es *elasticsearch.Client) error { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() res, err := es.Info(es.Info.WithContext(ctx)) if err != nil { return fmt.Errorf("connection test failed: %w", err) } defer res.Body.Close() if res.IsError() { return fmt.Errorf("connection test failed: %s", res.String()) } return nil } type ElasticsearchSender interface { SendBatch(baseIndex string, entries []LogEntry) error SendSystemMetrics(baseIndex string, metrics SystemResources) error } type ElasticsearchClient struct { client *elasticsearch.Client } func NewElasticsearchSender(client *elasticsearch.Client) ElasticsearchSender { return &ElasticsearchClient{client: client} } func (esc *ElasticsearchClient) SendBatch(baseIndex string, entries []LogEntry) error { if len(entries) == 0 { return nil } var body strings.Builder for _, entry := range entries { // indexName := determineIndexName(baseIndex, entry) indexName := "tixel" indexLine := fmt.Sprintf(`{"index":{"_index":"%s"}}`, indexName) body.WriteString(indexLine) body.WriteString("\n") data, err := json.Marshal(entry) if err != nil { slog.Error("error marshalling JSON", "error", err) continue } body.WriteString(string(data)) body.WriteString("\n") } ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() res, err := esc.client.Bulk( strings.NewReader(body.String()), esc.client.Bulk.WithContext(ctx), ) if err != nil { return fmt.Errorf("bulk request error: %w", err) } defer res.Body.Close() if res.IsError() { return fmt.Errorf("bulk request failed: %s", res.String()) } slog.Debug("Batch successfully sent", "count", len(entries)) return nil } func (esc *ElasticsearchClient) SendSystemMetrics(baseIndex string, metrics SystemResources) error { msg := LogEntry{ Service: "system-metrics", Timestamp: time.Now(), LogLevel: "Info", SystemMetrics: metrics, } data, err := json.Marshal(msg) if err != nil { return fmt.Errorf("JSON marshalling error: %w", err) } systemIndex := "tixel" ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() res, err := esc.client.Index( systemIndex, strings.NewReader(string(data)), esc.client.Index.WithContext(ctx), ) if err != nil { return fmt.Errorf("elasticsearch index error: %w", err) } defer res.Body.Close() if res.IsError() { return fmt.Errorf("elasticsearch error: %s", res.String()) } slog.Debug("System-Metrics sent", "CPU", metrics.CPUPercent, "MEM_used", metrics.MemoryUsed, "MEM_total", metrics.MemoryTotal, "MEM_percentage", metrics.MemoryPercent, ) return nil } // func determineIndexName(baseIndex string, entry LogEntry) string { // switch entry.Type { // case "system_metrics": // return fmt.Sprintf("%s-system", baseIndex) // case "service_log": // return fmt.Sprintf("%s-service-%s", baseIndex, entry.Service) // default: // return fmt.Sprintf("%s-%s", baseIndex, entry.Tool) // } // }