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

     1  package gelf
     2  
     3  import (
     4  	"crypto/rand"
     5  	"fmt"
     6  	"io"
     7  	"net"
     8  	"strings"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/go-kit/log"
    13  	"github.com/grafana/go-gelf/v2/gelf"
    14  	"github.com/prometheus/common/model"
    15  	"github.com/prometheus/prometheus/model/relabel"
    16  	"github.com/stretchr/testify/require"
    17  
    18  	"github.com/grafana/loki/clients/pkg/promtail/client/fake"
    19  	"github.com/grafana/loki/clients/pkg/promtail/scrapeconfig"
    20  )
    21  
    22  func Test_Gelf(t *testing.T) {
    23  	client := fake.New(func() {})
    24  	tm, err := NewTargetManager(NewMetrics(nil), log.NewNopLogger(), client, []scrapeconfig.Config{
    25  		{
    26  			JobName: "gelf",
    27  			GelfConfig: &scrapeconfig.GelfTargetConfig{
    28  				ListenAddress:        ":0",
    29  				UseIncomingTimestamp: true,
    30  				Labels:               model.LabelSet{"cfg": "true"},
    31  			},
    32  			RelabelConfigs: []*relabel.Config{
    33  				{
    34  					SourceLabels: model.LabelNames{"__gelf_message_level"},
    35  					TargetLabel:  "level",
    36  					Replacement:  "$1",
    37  					Action:       relabel.Replace,
    38  					Regex:        relabel.MustNewRegexp("(.*)"),
    39  				},
    40  				{
    41  					SourceLabels: model.LabelNames{"__gelf_message_host"},
    42  					TargetLabel:  "hostname",
    43  					Replacement:  "$1",
    44  					Action:       relabel.Replace,
    45  					Regex:        relabel.MustNewRegexp("(.*)"),
    46  				},
    47  				{
    48  					SourceLabels: model.LabelNames{"__gelf_message_facility"},
    49  					TargetLabel:  "facility",
    50  					Replacement:  "$1",
    51  					Action:       relabel.Replace,
    52  					Regex:        relabel.MustNewRegexp("(.*)"),
    53  				},
    54  			},
    55  		},
    56  	})
    57  	require.NoError(t, err)
    58  	defer tm.Stop()
    59  	target := tm.targets["gelf"]
    60  	require.NotNil(t, target)
    61  	w, err := gelf.NewUDPWriter(target.gelfReader.Addr())
    62  	require.NoError(t, err)
    63  	defer func() {
    64  		require.NoError(t, w.Close())
    65  	}()
    66  	baseTs := float64(time.Unix(10, 0).Unix()) + 0.250
    67  	ts := baseTs
    68  
    69  	for i := 0; i < 10; i++ {
    70  		require.NoError(t, w.WriteMessage(&gelf.Message{
    71  			Short:    "short",
    72  			Full:     "full",
    73  			Version:  "2.2",
    74  			Host:     "thishost",
    75  			TimeUnix: ts,
    76  			Level:    gelf.LOG_ERR,
    77  			Facility: "gelftest",
    78  			Extra: map[string]interface{}{
    79  				"_foo": "bar",
    80  			},
    81  		}))
    82  		ts += 0.250
    83  	}
    84  
    85  	require.Eventually(t, func() bool {
    86  		return len(client.Received()) == 10
    87  	}, 1*time.Second, 20*time.Millisecond)
    88  
    89  	for i, actual := range client.Received() {
    90  		require.Equal(t, "error", string(actual.Labels["level"]))
    91  		require.Equal(t, "true", string(actual.Labels["cfg"]))
    92  		require.Equal(t, "thishost", string(actual.Labels["hostname"]))
    93  		require.Equal(t, "gelftest", string(actual.Labels["facility"]))
    94  
    95  		require.Equal(t, secondsToUnixTimestamp(baseTs+float64(i)*0.250), actual.Timestamp)
    96  
    97  		var gelfMsg gelf.Message
    98  
    99  		require.NoError(t, gelfMsg.UnmarshalJSON([]byte(actual.Line)))
   100  
   101  		require.Equal(t, "short", gelfMsg.Short)
   102  		require.Equal(t, "full", gelfMsg.Full)
   103  		require.Equal(t, "2.2", gelfMsg.Version)
   104  		require.Equal(t, "thishost", gelfMsg.Host)
   105  		require.Equal(t, "bar", gelfMsg.Extra["_foo"])
   106  		require.Equal(t, gelf.LOG_ERR, int(gelfMsg.Level))
   107  		require.Equal(t, "gelftest", gelfMsg.Facility)
   108  
   109  	}
   110  }
   111  
   112  func TestConvertTime(t *testing.T) {
   113  	require.Equal(t, time.Unix(0, int64(time.Second+(time.Duration(250)*time.Millisecond))), secondsToUnixTimestamp(float64(time.Unix(1, 0).Unix())+0.250))
   114  }
   115  
   116  func Test_GelfChunksUnordered(t *testing.T) {
   117  	client := fake.New(func() {})
   118  
   119  	tm, err := NewTargetManager(NewMetrics(nil), log.NewNopLogger(), client, []scrapeconfig.Config{
   120  		{
   121  			JobName: "gelf",
   122  			GelfConfig: &scrapeconfig.GelfTargetConfig{
   123  				ListenAddress: ":0",
   124  			},
   125  		},
   126  	})
   127  	require.NoError(t, err)
   128  	defer tm.Stop()
   129  
   130  	target := tm.targets["gelf"]
   131  	require.NotNil(t, target)
   132  	connection, err := net.Dial("udp", target.gelfReader.Addr())
   133  	require.NoError(t, err)
   134  	defer func() {
   135  		require.NoError(t, connection.Close())
   136  	}()
   137  
   138  	chunksA := createChunks(t, "a")
   139  	chunksB := createChunks(t, "b")
   140  	// send messages(a, b) chunks in order: chunk-0a, chunk-0b, chunk-1a, chunk-1b
   141  	for i := 0; i < len(chunksB); i++ {
   142  		writeA, err := connection.Write(chunksA[i])
   143  		require.NoError(t, err)
   144  		require.Equal(t, len(chunksA[i]), writeA)
   145  
   146  		writeB, err := connection.Write(chunksB[i])
   147  		require.NoError(t, err)
   148  		require.Equal(t, len(chunksB[i]), writeB)
   149  	}
   150  
   151  	require.Eventually(t, func() bool {
   152  		return len(client.Received()) == 2
   153  	}, 2*time.Second, 100*time.Millisecond, "expected 2 messages to be received")
   154  }
   155  
   156  func createChunks(t *testing.T, char string) [][]byte {
   157  	chunksA, err := splitToChunks([]byte(fmt.Sprintf("{\"short_message\":\"%v\"}", strings.Repeat(char, gelf.ChunkSize*2))))
   158  	require.NoError(t, err)
   159  	return chunksA
   160  }
   161  
   162  // static value that indicated that GELF message is chunked
   163  var magicChunked = []byte{0x1e, 0x0f}
   164  
   165  const (
   166  	chunkedHeaderLen = 12
   167  	chunkedDataLen   = gelf.ChunkSize - chunkedHeaderLen
   168  )
   169  
   170  func splitToChunks(messageBytes []byte) ([][]byte, error) {
   171  	chunksCount := uint8(len(messageBytes)/chunkedDataLen + 1)
   172  	messageID := make([]byte, 8)
   173  	n, err := io.ReadFull(rand.Reader, messageID)
   174  	if err != nil || n != 8 {
   175  		return nil, fmt.Errorf("rand.Reader: %d/%s", n, err)
   176  	}
   177  	chunks := make([][]byte, 0, chunksCount)
   178  	bytesLeft := len(messageBytes)
   179  	for i := uint8(0); i < chunksCount; i++ {
   180  		buf := make([]byte, 0, gelf.ChunkSize)
   181  		buf = append(buf, magicChunked...)
   182  		buf = append(buf, messageID...)
   183  		buf = append(buf, i)
   184  		buf = append(buf, chunksCount)
   185  		chunkLen := chunkedDataLen
   186  		if chunkLen > bytesLeft {
   187  			chunkLen = bytesLeft
   188  		}
   189  		off := int(i) * chunkedDataLen
   190  		chunkData := messageBytes[off : off+chunkLen]
   191  		buf = append(buf, chunkData...)
   192  
   193  		chunks = append(chunks, buf)
   194  		bytesLeft -= chunkLen
   195  	}
   196  
   197  	if bytesLeft != 0 {
   198  		return nil, fmt.Errorf("error: %d bytes left after splitting", bytesLeft)
   199  	}
   200  	return chunks, nil
   201  }