categraf/logs/diagnostic/message_receiver.go

161 lines
3.7 KiB
Go

// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.
package diagnostic
import (
"fmt"
"os"
"sync"
"time"
logsconfig "flashcat.cloud/categraf/config/logs"
"flashcat.cloud/categraf/logs/message"
)
// MessageReceiver interface to handle messages for diagnostics
type MessageReceiver interface {
HandleMessage(message.Message, []byte)
}
type messagePair struct {
msg *message.Message
redactedMsg []byte
}
// BufferedMessageReceiver handles in coming log messages and makes them available for diagnostics
type BufferedMessageReceiver struct {
inputChan chan messagePair
enabled bool
m sync.RWMutex
}
// Filters for processing log messages
type Filters struct {
Name string `json:"name"`
Type string `json:"type"`
Source string `json:"source"`
}
// NewBufferedMessageReceiver creates a new MessageReceiver
func NewBufferedMessageReceiver() *BufferedMessageReceiver {
return &BufferedMessageReceiver{
inputChan: make(chan messagePair, logsconfig.ChanSize),
}
}
// Start opens new input channel
func (b *BufferedMessageReceiver) Start() {
b.inputChan = make(chan messagePair, logsconfig.ChanSize)
}
// Stop closes the input channel
func (b *BufferedMessageReceiver) Stop() {
close(b.inputChan)
}
// Clear empties buffered messages
func (b *BufferedMessageReceiver) clear() {
l := len(b.inputChan)
for i := 0; i < l; i++ {
<-b.inputChan
}
}
// SetEnabled start collecting log messages for diagnostics. Returns true if state was successfully changed
func (b *BufferedMessageReceiver) SetEnabled(e bool) bool {
b.m.Lock()
defer b.m.Unlock()
if b.enabled == e {
return false
}
b.enabled = e
if !e {
b.clear()
}
return true
}
// IsEnabled returns the enabled state of the message receiver
func (b *BufferedMessageReceiver) IsEnabled() bool {
b.m.RLock()
defer b.m.RUnlock()
return b.enabled
}
// HandleMessage buffers a message for diagnostic processing
func (b *BufferedMessageReceiver) HandleMessage(m message.Message, redactedMsg []byte) {
if !b.IsEnabled() {
return
}
b.inputChan <- messagePair{&m, redactedMsg}
}
// Filter writes the buffered events from the input channel formatted as a string to the output channel
func (b *BufferedMessageReceiver) Filter(filters *Filters, done <-chan struct{}) <-chan string {
out := make(chan string, logsconfig.ChanSize)
go func() {
defer close(out)
for {
select {
case msgPair := <-b.inputChan:
if shouldHandleMessage(msgPair.msg, filters) {
out <- formatMessage(msgPair.msg, msgPair.redactedMsg)
}
case <-done:
return
}
}
}()
return out
}
func shouldHandleMessage(m *message.Message, filters *Filters) bool {
if filters == nil {
return true
}
shouldHandle := true
if filters.Name != "" {
shouldHandle = shouldHandle && m.Origin.LogSource.Name == filters.Name
}
if filters.Type != "" {
shouldHandle = shouldHandle && m.Origin.LogSource.Config.Type == filters.Type
}
if filters.Source != "" {
shouldHandle = shouldHandle && filters.Source == m.Origin.Source()
}
return shouldHandle
}
func formatMessage(m *message.Message, redactedMsg []byte) string {
hostname, err := os.Hostname()
if err != nil {
hostname = "unknown"
}
ts := time.Now().UTC()
if !m.Timestamp.IsZero() {
ts = m.Timestamp
}
return fmt.Sprintf("Integration Name: %s | Type: %s | Status: %s | Timestamp: %s | Hostname: %s | Service: %s | Source: %s | Tags: %s | Message: %s\n",
m.Origin.LogSource.Name,
m.Origin.LogSource.Config.Type,
m.GetStatus(),
ts,
hostname,
m.Origin.Service(),
m.Origin.Source(),
m.Origin.TagsToString(),
string(redactedMsg))
}