unpoller_unpoller/integrations/lokiunifi/report.go

135 lines
3.7 KiB
Go

package lokiunifi
import (
"strconv"
"strings"
"time"
"github.com/unifi-poller/poller"
"github.com/unifi-poller/unifi"
)
// LogStream contains a stream of logs (like a log file).
// This app uses one stream per log entry because each log may have different labels.
type LogStream struct {
Labels map[string]string `json:"stream"` // "the file name"
Entries [][]string `json:"values"` // "the log lines"
}
// Logs is the main logs-holding structure.
type Logs struct {
Streams []LogStream `json:"streams"` // "multiple files"
}
// Report is the temporary data generated and sent to Loki at every interval.
type Report struct {
Eve int // Total count of Events.
IDS int // Total count of IDS/IPS Events.
Start time.Time
Last time.Time
Loki *Client
Events *poller.Events
Logs
poller.Logger
}
// SendEvents loops the event Logs, matches the interface
// type, calls the appropriate method for the data, and completes the report.
// This runs once per interval, if there was no collection error.
func (r *Report) SendEvents() error {
for _, e := range r.Events.Logs {
switch event := e.(type) {
case *unifi.IDS:
r.SaveIDS(event)
case *unifi.Event:
r.SaveEvent(event)
default: // unlikely.
r.LogErrorf("unknown event type: %T", e)
}
}
return r.Loki.Send(r.Logs)
}
// SaveIDS stores a structured IDS Event for batch sending to Loki.
func (r *Report) SaveIDS(ids *unifi.IDS) {
if ids.Datetime.Before(r.Last) {
return
}
r.IDS++ // increase counter and append new log line.
r.Streams = append(r.Streams, LogStream{
Entries: [][]string{{strconv.FormatInt(ids.Datetime.UnixNano(), 10), ids.Msg}},
Labels: cleanLabels(map[string]string{
"application": "unifi_ids",
"source": ids.SourceName,
"site_name": ids.SiteName,
"subsystem": ids.Subsystem,
"category": ids.Catname,
"event_type": ids.EventType,
"key": ids.Key,
"app_protocol": ids.AppProto,
"protocol": ids.Proto,
"interface": ids.InIface,
"src_country": ids.SrcIPCountry,
"usgip": ids.USGIP,
"action": ids.InnerAlertAction,
}),
})
}
// SaveEvent stores a structured UniFi Event for batch sending to Loki.
func (r *Report) SaveEvent(event *unifi.Event) {
if event.Datetime.Before(r.Last) {
return
}
r.Eve++ // increase counter and append new log line.
r.Streams = append(r.Streams, LogStream{
Entries: [][]string{{strconv.FormatInt(event.Datetime.UnixNano(), 10), event.Msg}},
Labels: cleanLabels(map[string]string{
"application": "unifi_event",
"admin": event.Admin, // username
"site_name": event.SiteName,
"source": event.SourceName,
"subsystem": event.Subsystem,
"ap_from": event.ApFrom,
"ap_to": event.ApTo,
"ap": event.Ap,
"ap_name": event.ApName,
"gw": event.Gw,
"gw_name": event.GwName,
"sw": event.Sw,
"sw_name": event.SwName,
"category": event.Catname,
"radio": event.Radio,
"radio_from": event.RadioFrom,
"radio_to": event.RadioTo,
"key": event.Key,
"interface": event.InIface,
"event_type": event.EventType,
"ssid": event.SSID,
"channel": event.Channel.Txt,
"channel_from": event.ChannelFrom.Txt,
"channel_to": event.ChannelTo.Txt,
"usgip": event.USGIP,
"network": event.Network,
"app_protocol": event.AppProto,
"protocol": event.Proto,
"action": event.InnerAlertAction,
"src_country": event.SrcIPCountry,
}),
})
}
// cleanLabels removes any tag that is empty.
func cleanLabels(labels map[string]string) map[string]string {
for i := range labels {
if strings.TrimSpace(labels[i]) == "" {
delete(labels, i)
}
}
return labels
}