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 }