github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/clients/pkg/promtail/targets/gcplog/push_target.go (about)

     1  package gcplog
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"net/http"
     8  
     9  	"github.com/go-kit/log"
    10  	"github.com/go-kit/log/level"
    11  	"github.com/prometheus/common/model"
    12  	"github.com/prometheus/prometheus/model/relabel"
    13  	"github.com/weaveworks/common/logging"
    14  	"github.com/weaveworks/common/server"
    15  
    16  	"github.com/grafana/loki/clients/pkg/promtail/api"
    17  	"github.com/grafana/loki/clients/pkg/promtail/scrapeconfig"
    18  	"github.com/grafana/loki/clients/pkg/promtail/targets/serverutils"
    19  	"github.com/grafana/loki/clients/pkg/promtail/targets/target"
    20  
    21  	util_log "github.com/grafana/loki/pkg/util/log"
    22  )
    23  
    24  type pushTarget struct {
    25  	logger         log.Logger
    26  	handler        api.EntryHandler
    27  	config         *scrapeconfig.GcplogTargetConfig
    28  	jobName        string
    29  	server         *server.Server
    30  	metrics        *Metrics
    31  	relabelConfigs []*relabel.Config
    32  }
    33  
    34  // newPushTarget creates a brand new GCP Push target, capable of receiving message from a GCP PubSub push subscription.
    35  func newPushTarget(metrics *Metrics, logger log.Logger, handler api.EntryHandler, jobName string, config *scrapeconfig.GcplogTargetConfig, relabel []*relabel.Config) (*pushTarget, error) {
    36  	wrappedLogger := log.With(logger, "component", "gcp_push")
    37  
    38  	ht := &pushTarget{
    39  		metrics:        metrics,
    40  		logger:         wrappedLogger,
    41  		handler:        handler,
    42  		jobName:        jobName,
    43  		config:         config,
    44  		relabelConfigs: relabel,
    45  	}
    46  
    47  	mergedServerConfigs, err := serverutils.MergeWithDefaults(config.Server)
    48  	if err != nil {
    49  		return nil, fmt.Errorf("failed to parse configs and override defaults when configuring gcp push target: %w", err)
    50  	}
    51  	config.Server = mergedServerConfigs
    52  
    53  	err = ht.run()
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	return ht, nil
    59  }
    60  
    61  func (h *pushTarget) run() error {
    62  	level.Info(h.logger).Log("msg", "starting gcp push target", "job", h.jobName)
    63  
    64  	// To prevent metric collisions because all metrics are going to be registered in the global Prometheus registry.
    65  
    66  	tentativeServerMetricNamespace := "promtail_gcp_push_target_" + h.jobName
    67  	if !model.IsValidMetricName(model.LabelValue(tentativeServerMetricNamespace)) {
    68  		return fmt.Errorf("invalid prometheus-compatible job name: %s", h.jobName)
    69  	}
    70  	h.config.Server.MetricsNamespace = tentativeServerMetricNamespace
    71  
    72  	// We don't want the /debug and /metrics endpoints running, since this is not the main promtail HTTP server.
    73  	// We want this target to expose the least surface area possible, hence disabling WeaveWorks HTTP server metrics
    74  	// and debugging functionality.
    75  	h.config.Server.RegisterInstrumentation = false
    76  
    77  	// Wrapping util logger with component-specific key vals, and the expected GoKit logging interface
    78  	h.config.Server.Log = logging.GoKit(log.With(util_log.Logger, "component", "gcp_push"))
    79  
    80  	srv, err := server.New(h.config.Server)
    81  	if err != nil {
    82  		return err
    83  	}
    84  
    85  	h.server = srv
    86  	h.server.HTTP.Path("/gcp/api/v1/push").Methods("POST").Handler(http.HandlerFunc(h.push))
    87  
    88  	go func() {
    89  		err := srv.Run()
    90  		if err != nil {
    91  			level.Error(h.logger).Log("msg", "gcp push target shutdown with error", "err", err)
    92  		}
    93  	}()
    94  
    95  	return nil
    96  }
    97  
    98  func (h *pushTarget) push(w http.ResponseWriter, r *http.Request) {
    99  	entries := h.handler.Chan()
   100  	defer r.Body.Close()
   101  
   102  	pushMessage := PushMessage{}
   103  	bs, err := io.ReadAll(r.Body)
   104  	if err != nil {
   105  		h.metrics.gcpPushErrors.WithLabelValues().Inc()
   106  		level.Warn(h.logger).Log("msg", "failed to read incoming gcp push request", "err", err.Error())
   107  		http.Error(w, err.Error(), http.StatusBadRequest)
   108  		return
   109  	}
   110  	err = json.Unmarshal(bs, &pushMessage)
   111  	if err != nil {
   112  		h.metrics.gcpPushErrors.WithLabelValues().Inc()
   113  		level.Warn(h.logger).Log("msg", "failed to unmarshall gcp push request", "err", err.Error())
   114  		http.Error(w, err.Error(), http.StatusBadRequest)
   115  		return
   116  	}
   117  
   118  	entry, err := translate(pushMessage, h.config.Labels, h.config.UseIncomingTimestamp, h.relabelConfigs, r.Header.Get("X-Scope-OrgID"))
   119  	if err != nil {
   120  		h.metrics.gcpPushErrors.WithLabelValues().Inc()
   121  		level.Warn(h.logger).Log("msg", "failed to translate gcp push request", "err", err.Error())
   122  		http.Error(w, err.Error(), http.StatusBadRequest)
   123  		return
   124  	}
   125  
   126  	level.Debug(h.logger).Log("msg", fmt.Sprintf("Received line: %s", entry.Line))
   127  
   128  	entries <- entry
   129  	h.metrics.gcpPushEntries.WithLabelValues().Inc()
   130  	w.WriteHeader(http.StatusNoContent)
   131  }
   132  
   133  func (h *pushTarget) Type() target.TargetType {
   134  	return target.GcplogTargetType
   135  }
   136  
   137  func (h *pushTarget) DiscoveredLabels() model.LabelSet {
   138  	return nil
   139  }
   140  
   141  func (h *pushTarget) Labels() model.LabelSet {
   142  	return h.config.Labels
   143  }
   144  
   145  func (h *pushTarget) Ready() bool {
   146  	return true
   147  }
   148  
   149  func (h *pushTarget) Details() interface{} {
   150  	return map[string]string{}
   151  }
   152  
   153  func (h *pushTarget) Stop() error {
   154  	level.Info(h.logger).Log("msg", "stopping gcp push target", "job", h.jobName)
   155  	h.server.Shutdown()
   156  	h.handler.Stop()
   157  	return nil
   158  }