github.com/siglens/siglens@v0.0.0-20240328180423-f7ce9ae441ed/pkg/influx/writer/putmetrics.go (about)

     1  /*
     2  Copyright 2023.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package writer
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/csv"
    22  	"encoding/json"
    23  	"io"
    24  	"strings"
    25  
    26  	. "github.com/siglens/siglens/pkg/segment/utils"
    27  	"github.com/siglens/siglens/pkg/segment/writer"
    28  	"github.com/siglens/siglens/pkg/usageStats"
    29  	"github.com/siglens/siglens/pkg/utils"
    30  	log "github.com/sirupsen/logrus"
    31  	"github.com/valyala/fasthttp"
    32  )
    33  
    34  type InfluxPutResp struct {
    35  	Failed  uint64   `json:"failed"`
    36  	Success uint64   `json:"success"`
    37  	Errors  []string `json:"errors,omitempty"`
    38  }
    39  
    40  func PutMetrics(ctx *fasthttp.RequestCtx, myid uint64) {
    41  
    42  	var processedCount uint64
    43  	var failedCount uint64
    44  	var err error
    45  
    46  	cType := string(ctx.Request.Header.ContentType())
    47  	if cType == "text/plain; charset=utf-8" {
    48  		eType := string(ctx.Request.Header.ContentEncoding())
    49  		if eType == "gzip" {
    50  			var body []byte
    51  			body, err = ctx.Request.BodyGunzip()
    52  			if err != nil {
    53  				log.Errorf("PutMetrics: error unzipping body! %v", err)
    54  			}
    55  			processedCount, failedCount, err = HandlePutMetrics(body, myid)
    56  		} else {
    57  			body := ctx.PostBody()
    58  			processedCount, failedCount, err = HandlePutMetrics(body, myid)
    59  		}
    60  	} else {
    61  		log.Errorf("PutMetrics: unknown content type [%s]! %v", cType, err)
    62  		writeInfluxResponse(ctx, processedCount, failedCount, "unknown content type", fasthttp.StatusBadRequest)
    63  		return
    64  	}
    65  
    66  	if err != nil {
    67  		writeInfluxResponse(ctx, processedCount, failedCount, err.Error(), fasthttp.StatusBadRequest)
    68  	}
    69  	writeInfluxResponse(ctx, processedCount, failedCount, "", fasthttp.StatusOK)
    70  }
    71  
    72  func HandlePutMetrics(fullData []byte, myid uint64) (uint64, uint64, error) {
    73  
    74  	//to have a check if there are any errors in the request
    75  	//to check for status : 200 or 400
    76  	//to check if json is greater than MAX_RECORD_SIZE
    77  
    78  	var successCount uint64 = 0
    79  	var failedCount uint64 = 0
    80  	var err error = nil
    81  
    82  	bytesReceived := uint64(len(fullData))
    83  	reader := csv.NewReader(bytes.NewBuffer(fullData))
    84  	for {
    85  		record, err := reader.Read()
    86  		if err != nil {
    87  			// If there is an error, check if it's EOF
    88  			if err == io.EOF {
    89  				break // End of file
    90  			}
    91  			return 0, 0, err
    92  
    93  		} else {
    94  
    95  			csvRow := strings.Join(record, ",")
    96  			mErr := writer.AddTimeSeriesEntryToInMemBuf([]byte(csvRow), SIGNAL_METRICS_INFLUX, myid)
    97  			if mErr != nil {
    98  				log.Errorf("HandlePutMetrics: failed to add time series entry %+v", mErr)
    99  				failedCount++
   100  			} else {
   101  				successCount++
   102  			}
   103  
   104  		}
   105  
   106  	}
   107  
   108  	if err != nil {
   109  		mErr := writer.AddTimeSeriesEntryToInMemBuf(fullData, SIGNAL_METRICS_INFLUX, myid)
   110  		if mErr != nil {
   111  			log.Errorf("HandlePutMetrics: failed to add time series entry %+v", mErr)
   112  			failedCount++
   113  		} else {
   114  			successCount++
   115  		}
   116  		return failedCount, successCount, err
   117  	}
   118  	usageStats.UpdateMetricsStats(bytesReceived, successCount, myid)
   119  	return successCount, failedCount, nil
   120  
   121  }
   122  
   123  func writeInfluxResponse(ctx *fasthttp.RequestCtx, processedCount uint64, failedCount uint64, err string, code int) {
   124  
   125  	resp := InfluxPutResp{Success: processedCount, Failed: failedCount}
   126  	if err != "" {
   127  		resp.Errors = []string{err}
   128  	}
   129  
   130  	ctx.SetStatusCode(code)
   131  	ctx.SetContentType(utils.ContentJson)
   132  	jval, mErr := json.Marshal(resp)
   133  	if mErr != nil {
   134  		log.Errorf("writeInfluxResponse: failed to marshal resp %+v", mErr)
   135  	}
   136  	_, mErr = ctx.Write(jval)
   137  
   138  	if mErr != nil {
   139  		log.Errorf("writeInfluxResponse: failed to write jval to http request %+v", mErr)
   140  	}
   141  
   142  }