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 }