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

     1  package syslog
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/go-kit/log"
    11  	"github.com/go-kit/log/level"
    12  	"github.com/influxdata/go-syslog/v3"
    13  	"github.com/influxdata/go-syslog/v3/rfc5424"
    14  	"github.com/prometheus/common/model"
    15  	"github.com/prometheus/prometheus/model/labels"
    16  	"github.com/prometheus/prometheus/model/relabel"
    17  
    18  	"github.com/grafana/loki/clients/pkg/promtail/api"
    19  	"github.com/grafana/loki/clients/pkg/promtail/scrapeconfig"
    20  	"github.com/grafana/loki/clients/pkg/promtail/targets/target"
    21  
    22  	"github.com/grafana/loki/pkg/logproto"
    23  )
    24  
    25  var (
    26  	defaultIdleTimeout      = 120 * time.Second
    27  	defaultMaxMessageLength = 8192
    28  	defaultProtocol         = protocolTCP
    29  )
    30  
    31  // SyslogTarget listens to syslog messages.
    32  // nolint:revive
    33  type SyslogTarget struct {
    34  	metrics       *Metrics
    35  	logger        log.Logger
    36  	handler       api.EntryHandler
    37  	config        *scrapeconfig.SyslogTargetConfig
    38  	relabelConfig []*relabel.Config
    39  
    40  	transport Transport
    41  
    42  	messages     chan message
    43  	messagesDone chan struct{}
    44  }
    45  
    46  type message struct {
    47  	labels    model.LabelSet
    48  	message   string
    49  	timestamp time.Time
    50  }
    51  
    52  // NewSyslogTarget configures a new SyslogTarget.
    53  func NewSyslogTarget(
    54  	metrics *Metrics,
    55  	logger log.Logger,
    56  	handler api.EntryHandler,
    57  	relabel []*relabel.Config,
    58  	config *scrapeconfig.SyslogTargetConfig,
    59  ) (*SyslogTarget, error) {
    60  
    61  	t := &SyslogTarget{
    62  		metrics:       metrics,
    63  		logger:        logger,
    64  		handler:       handler,
    65  		config:        config,
    66  		relabelConfig: relabel,
    67  		messagesDone:  make(chan struct{}),
    68  	}
    69  
    70  	switch t.transportProtocol() {
    71  	case protocolTCP:
    72  		t.transport = NewSyslogTCPTransport(
    73  			config,
    74  			t.handleMessage,
    75  			t.handleMessageError,
    76  			logger,
    77  		)
    78  	case protocolUDP:
    79  		t.transport = NewSyslogUDPTransport(
    80  			config,
    81  			t.handleMessage,
    82  			t.handleMessageError,
    83  			logger,
    84  		)
    85  	default:
    86  		return nil, fmt.Errorf("invalid transport protocol. expected 'tcp' or 'udp', got '%s'", t.transportProtocol())
    87  	}
    88  
    89  	t.messages = make(chan message)
    90  	go t.messageSender(handler.Chan())
    91  
    92  	err := t.transport.Run()
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  	return t, nil
    97  }
    98  
    99  func (t *SyslogTarget) handleMessageError(err error) {
   100  	var ne net.Error
   101  	if errors.As(err, &ne) && ne.Timeout() {
   102  		level.Debug(t.logger).Log("msg", "connection timed out", "err", ne)
   103  		return
   104  	}
   105  	level.Warn(t.logger).Log("msg", "error parsing syslog stream", "err", err)
   106  	t.metrics.syslogParsingErrors.Inc()
   107  }
   108  
   109  func (t *SyslogTarget) handleMessage(connLabels labels.Labels, msg syslog.Message) {
   110  	rfc5424Msg := msg.(*rfc5424.SyslogMessage)
   111  
   112  	if rfc5424Msg.Message == nil {
   113  		t.metrics.syslogEmptyMessages.Inc()
   114  		return
   115  	}
   116  
   117  	lb := labels.NewBuilder(connLabels)
   118  	if v := rfc5424Msg.SeverityLevel(); v != nil {
   119  		lb.Set("__syslog_message_severity", *v)
   120  	}
   121  	if v := rfc5424Msg.FacilityLevel(); v != nil {
   122  		lb.Set("__syslog_message_facility", *v)
   123  	}
   124  	if v := rfc5424Msg.Hostname; v != nil {
   125  		lb.Set("__syslog_message_hostname", *v)
   126  	}
   127  	if v := rfc5424Msg.Appname; v != nil {
   128  		lb.Set("__syslog_message_app_name", *v)
   129  	}
   130  	if v := rfc5424Msg.ProcID; v != nil {
   131  		lb.Set("__syslog_message_proc_id", *v)
   132  	}
   133  	if v := rfc5424Msg.MsgID; v != nil {
   134  		lb.Set("__syslog_message_msg_id", *v)
   135  	}
   136  
   137  	if t.config.LabelStructuredData && rfc5424Msg.StructuredData != nil {
   138  		for id, params := range *rfc5424Msg.StructuredData {
   139  			id = strings.ReplaceAll(id, "@", "_")
   140  			for name, value := range params {
   141  				key := "__syslog_message_sd_" + id + "_" + name
   142  				lb.Set(key, value)
   143  			}
   144  		}
   145  	}
   146  
   147  	processed := relabel.Process(lb.Labels(), t.relabelConfig...)
   148  
   149  	filtered := make(model.LabelSet)
   150  	for _, lbl := range processed {
   151  		if strings.HasPrefix(lbl.Name, "__") {
   152  			continue
   153  		}
   154  		filtered[model.LabelName(lbl.Name)] = model.LabelValue(lbl.Value)
   155  	}
   156  
   157  	var timestamp time.Time
   158  	if t.config.UseIncomingTimestamp && rfc5424Msg.Timestamp != nil {
   159  		timestamp = *rfc5424Msg.Timestamp
   160  	} else {
   161  		timestamp = time.Now()
   162  	}
   163  
   164  	m := *rfc5424Msg.Message
   165  	if t.config.UseRFC5424Message {
   166  		fullMsg, err := rfc5424Msg.String()
   167  		if err != nil {
   168  			level.Debug(t.logger).Log("msg", "failed to convert rfc5424 message to string; using message field instead", "err", err)
   169  		} else {
   170  			m = fullMsg
   171  		}
   172  	}
   173  	t.messages <- message{filtered, m, timestamp}
   174  }
   175  
   176  func (t *SyslogTarget) messageSender(entries chan<- api.Entry) {
   177  	for msg := range t.messages {
   178  		entries <- api.Entry{
   179  			Labels: msg.labels,
   180  			Entry: logproto.Entry{
   181  				Timestamp: msg.timestamp,
   182  				Line:      msg.message,
   183  			},
   184  		}
   185  		t.metrics.syslogEntries.Inc()
   186  	}
   187  	t.messagesDone <- struct{}{}
   188  }
   189  
   190  // Type returns SyslogTargetType.
   191  func (t *SyslogTarget) Type() target.TargetType {
   192  	return target.SyslogTargetType
   193  }
   194  
   195  // Ready indicates whether or not the syslog target is ready to be read from.
   196  func (t *SyslogTarget) Ready() bool {
   197  	return t.transport.Ready()
   198  }
   199  
   200  // DiscoveredLabels returns the set of labels discovered by the syslog target, which
   201  // is always nil. Implements Target.
   202  func (t *SyslogTarget) DiscoveredLabels() model.LabelSet {
   203  	return nil
   204  }
   205  
   206  // Labels returns the set of labels that statically apply to all log entries
   207  // produced by the SyslogTarget.
   208  func (t *SyslogTarget) Labels() model.LabelSet {
   209  	return t.config.Labels
   210  }
   211  
   212  // Details returns target-specific details.
   213  func (t *SyslogTarget) Details() interface{} {
   214  	return map[string]string{}
   215  }
   216  
   217  // Stop shuts down the SyslogTarget.
   218  func (t *SyslogTarget) Stop() error {
   219  	err := t.transport.Close()
   220  	t.transport.Wait()
   221  	close(t.messages)
   222  	// wait for all pending messages to be processed and sent to handler
   223  	<-t.messagesDone
   224  	t.handler.Stop()
   225  	return err
   226  }
   227  
   228  // ListenAddress returns the address SyslogTarget is listening on.
   229  func (t *SyslogTarget) ListenAddress() net.Addr {
   230  	return t.transport.Addr()
   231  }
   232  
   233  func (t *SyslogTarget) transportProtocol() string {
   234  	if t.config.ListenProtocol != "" {
   235  		return t.config.ListenProtocol
   236  	}
   237  	return defaultProtocol
   238  }