Skip to content

Commit efe86db

Browse files
committed
add tests
Signed-off-by: alexferl <me@alexferl.com>
1 parent add86ae commit efe86db

18 files changed

+332
-534
lines changed

.github/workflows/test.yaml

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: Test and coverage
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
build:
7+
runs-on: ubuntu-latest
8+
steps:
9+
- uses: actions/checkout@v2
10+
with:
11+
fetch-depth: 2
12+
- uses: actions/setup-go@v2
13+
with:
14+
go-version: '1.22'
15+
- name: Run coverage
16+
run: go test -race -coverprofile=coverage.out -covermode=atomic
17+
- name: Upload coverage to Codecov
18+
uses: codecov/codecov-action@v3

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ _testmain.go
2323
*.test
2424
*.prof
2525

26+
*.out
2627
*.log
2728
.idea
2829
tinysyslog-bin

Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM golang:1.20.7-alpine3.18 as builder
1+
FROM golang:1.22.2-alpine3.19 as builder
22
MAINTAINER Alexandre Ferland <me@alexferl.com>
33

44
WORKDIR /build

Makefile

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: dev run test cover fmt pre-commit docker-build docker-run
1+
.PHONY: dev run test cover cover-html fmt pre-commit docker-build docker-run
22

33
.DEFAULT: help
44
help:
@@ -10,6 +10,8 @@ help:
1010
@echo " run go test"
1111
@echo "make cover"
1212
@echo " run go test with -cover"
13+
@echo "make cover-html"
14+
@echo " run go test with -cover and show HTML"
1315
@echo "make tidy"
1416
@echo " run go mod tidy"
1517
@echo "make fmt"
@@ -46,6 +48,10 @@ test:
4648
cover:
4749
go test -cover -v ./...
4850

51+
cover-html:
52+
go test -v -coverprofile=coverage.out ./...
53+
go tool cover -html=coverage.out
54+
4955
tidy:
5056
go mod tidy
5157

README.md

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# tinysyslog [![Go Report Card](https://goreportcard.com/badge/github.com/alexferl/tinysyslog)](https://goreportcard.com/report/github.com/alexferl/tinysyslog)
1+
# tinysyslog [![Go Report Card](https://goreportcard.com/badge/github.com/alexferl/tinysyslog)](https://goreportcard.com/report/github.com/alexferl/tinysyslog) [![codecov](https://codecov.io/gh/alexferl/tinysyslog/branch/master/graph/badge.svg)](https://codecov.io/gh/alexferl/tinysyslog)
22

33
A tiny and simple syslog server with log rotation. tinysyslog was born out of the need for a tiny, easy to set up and
44
use syslog server that simply writes every incoming log (in [RFC 5424](https://datatracker.ietf.org/doc/html/rfc5424) format **only**) to a file that is automatically rotated,
@@ -88,25 +88,25 @@ Usage of ./tinysyslogd:
8888
--app-name string The name of the application. (default "tinysyslog")
8989
--bind-addr string IP and port to listen on. (default "127.0.0.1:5140")
9090
--env-name string The environment of the application. Used to load the right configs file. (default "PROD")
91-
--filter string Filter to filter logs with. Valid filters are: regex.
91+
--filter string Filter to filter logs with. Valid filters: [regex]
9292
--filter-regex string Regex to filter with.
93-
--log-level string The granularity of log outputs. Valid levels: 'PANIC', 'FATAL', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'TRACE', 'DISABLED' (default "INFO")
94-
--log-output string The output to write to. 'stdout' means log to stdout, 'stderr' means log to stderr. (default "stdout")
95-
--log-writer string The log writer. Valid writers are: 'console' and 'json'. (default "console")
96-
--mutator string Mutator type to use. Valid mutators are: text, json. (default "text")
97-
--sink-console-output string Console to output too. Valid outputs are: stdout, stderr. (default "stdout")
93+
--log-level string The granularity of log outputs. Valid levels: [PANIC FATAL ERROR WARN INFO DISABLED TRACE DISABLED] (default "INFO")
94+
--log-output string The output to write to. Valid outputs: [stdout stderr] (default "stdout")
95+
--log-writer string The log writer. Valid writers: [console json] (default "console")
96+
--mutator string Mutator type to use. Valid mutators: [text json] (default "text")
97+
--sink-console-output string Console to output to. Valid outputs: [stdout stderr] (default "stdout")
9898
--sink-elasticsearch-addresses strings Elasticsearch server addresses.
9999
--sink-elasticsearch-api-key string Elasticsearch api key.
100100
--sink-elasticsearch-cloud-id string Elasticsearch cloud id.
101101
--sink-elasticsearch-index-name string Elasticsearch index name. (default "tinysyslog")
102102
--sink-elasticsearch-password string Elasticsearch password.
103103
--sink-elasticsearch-service-token string Elasticsearch service token.
104104
--sink-elasticsearch-username string Elasticsearch username.
105-
--sink-filesystem-filename string File to write incoming logs to. (default "syslog.log")
105+
--sink-filesystem-filename string File path to write incoming logs to. (default "syslog.log")
106106
--sink-filesystem-max-age int Maximum age (in days) before a log is deleted. (default 30)
107107
--sink-filesystem-max-backups int Maximum backups to keep. (default 10)
108108
--sink-filesystem-max-size int Maximum log size (in megabytes) before it's rotated. (default 100)
109-
--sinks strings Sinks to save syslogs to. Valid sinks are: console, elasticsearch and filesystem. (default [console])
109+
--sinks strings Sinks to save syslogs to. Valid sinks: [console elasticsearch filesystem] (default [console])
110110
--socket-type string Type of socket to use, TCP or UDP. If no type is specified, both are used.
111111
```
112112

cmd/tinysyslogd/main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@ func main() {
1414
server := tinysyslog.NewServer()
1515
err := server.Run()
1616
if err != nil {
17-
log.Fatal().Err(err).Msg("error staring server")
17+
log.Fatal().Err(err).Msg("failed staring server")
1818
}
1919
}

config/config.go

+16-10
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
libLog "github.com/alexferl/golib/log"
88
"github.com/spf13/pflag"
99
"github.com/spf13/viper"
10+
11+
"tinysyslog/constants"
1012
)
1113

1214
// Config holds all configuration for our program
@@ -66,7 +68,7 @@ func NewConfig() *Config {
6668
Logging: libLog.DefaultConfig,
6769
BindAddr: "127.0.0.1:5140",
6870
ConsoleSink: ConsoleSink{
69-
Output: "stdout",
71+
Output: constants.ConsoleStdOut,
7072
},
7173
ElasticSearchSink: ElasticSearchSink{
7274
IndexName: "tinysyslog",
@@ -81,11 +83,11 @@ func NewConfig() *Config {
8183
LogFile: "stdout",
8284
LogFormat: "text",
8385
LogLevel: "info",
84-
MutatorType: "text",
86+
MutatorType: constants.MutatorText,
8587
RegexFilter: RegexFilter{
8688
Regex: "",
8789
},
88-
SinkTypes: []string{"console"},
90+
SinkTypes: []string{constants.SinkConsole},
8991
SocketType: "",
9092
}
9193
}
@@ -122,13 +124,17 @@ const (
122124
func (c *Config) addFlags(fs *pflag.FlagSet) {
123125
fs.StringVar(&c.BindAddr, BindAddr, c.BindAddr, "IP and port to listen on.")
124126
fs.StringVar(&c.FilterType, Filter, c.FilterType,
125-
"Filter to filter logs with. Valid filters are: regex.")
127+
fmt.Sprintf("Filter to filter logs with. Valid filters: %s", constants.Filters),
128+
)
126129
fs.StringVar(&c.RegexFilter.Regex, FilterRegex, c.RegexFilter.Regex, "Regex to filter with.")
127-
fs.StringVar(&c.MutatorType, Mutator, c.MutatorType, "Mutator type to use. Valid mutators are: text, json.")
128-
fs.StringSliceVar(&c.SinkTypes, Sinks, c.SinkTypes, "Sinks to save syslogs to. "+
129-
"Valid sinks are: console, elasticsearch and filesystem.")
130-
fs.StringVar(&c.ConsoleSink.Output, SinkConsoleOutput, c.ConsoleSink.Output, "Console to output too. "+
131-
"Valid outputs are: stdout, stderr.")
130+
fs.StringVar(&c.MutatorType, Mutator, c.MutatorType,
131+
fmt.Sprintf("Mutator type to use. Valid mutators: %s", constants.Mutators),
132+
)
133+
fs.StringSliceVar(&c.SinkTypes, Sinks, c.SinkTypes,
134+
fmt.Sprintf("Sinks to save syslogs to. Valid sinks: %s", constants.Sinks),
135+
)
136+
fs.StringVar(&c.ConsoleSink.Output, SinkConsoleOutput, c.ConsoleSink.Output,
137+
fmt.Sprintf("Console to output to. Valid outputs: %s", constants.ConsoleOutputs))
132138
fs.StringSliceVar(&c.ElasticSearchSink.Addresses, SinkElasticsearchAddresses, c.ElasticSearchSink.Addresses,
133139
"Elasticsearch server addresses.")
134140
fs.StringVar(&c.ElasticSearchSink.IndexName, SinkElasticsearchIndexName, c.ElasticSearchSink.IndexName,
@@ -144,7 +150,7 @@ func (c *Config) addFlags(fs *pflag.FlagSet) {
144150
fs.StringVar(&c.ElasticSearchSink.ServiceToken, SinkElasticsearchServiceToken, c.ElasticSearchSink.ServiceToken,
145151
"Elasticsearch service token.")
146152
fs.StringVar(&c.FilesystemSink.Filename, SinkFilesystemFilename, c.FilesystemSink.Filename,
147-
"File to write incoming logs to.")
153+
"File path to write incoming logs to.")
148154
fs.IntVar(&c.FilesystemSink.MaxAge, SinkFilesystemMaxAge, c.FilesystemSink.MaxAge,
149155
"Maximum age (in days) before a log is deleted.")
150156
fs.IntVar(&c.FilesystemSink.MaxBackups, SinkFilesystemMaxBackups, c.FilesystemSink.MaxBackups,

constants/constants.go

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package constants
2+
3+
const (
4+
MutatorText = "text"
5+
MutatorJSON = "json"
6+
)
7+
8+
var Mutators = []string{MutatorText, MutatorJSON}
9+
10+
const (
11+
FilterRegex = "regex"
12+
)
13+
14+
var Filters = []string{FilterRegex}
15+
16+
const (
17+
SinkConsole = "console"
18+
SinkElasticsearch = "elasticsearch"
19+
SinkFilesystem = "filesystem"
20+
)
21+
22+
var Sinks = []string{SinkConsole, SinkElasticsearch, SinkFilesystem}
23+
24+
const (
25+
ConsoleStdOut = "stdout"
26+
ConsoleStdErr = "stderr"
27+
)
28+
29+
var ConsoleOutputs = []string{ConsoleStdOut, ConsoleStdErr}

factories.go

+15-14
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/spf13/viper"
99

1010
"tinysyslog/config"
11+
"tinysyslog/constants"
1112
"tinysyslog/filters"
1213
"tinysyslog/mutators"
1314
"tinysyslog/sinks"
@@ -17,17 +18,17 @@ import (
1718
func MutatorFactory() mutators.Mutator {
1819
mutatorType := viper.GetString(config.Mutator)
1920

20-
if mutatorType == "text" {
21+
if mutatorType == constants.MutatorText {
2122
log.Debug().Msgf("using mutator '%s'", mutatorType)
2223
return mutators.NewText()
2324
}
2425

25-
if mutatorType == "json" {
26+
if mutatorType == constants.MutatorJSON {
2627
log.Debug().Msgf("using mutator '%s'", mutatorType)
2728
return mutators.NewJSON()
2829
}
2930

30-
log.Warn().Msgf("unknown mutator '%s'. Falling back to 'text'", mutatorType)
31+
log.Warn().Msgf("unknown mutator '%s'. Falling back to '%s'", mutatorType, constants.MutatorText)
3132
return mutators.NewText()
3233
}
3334

@@ -40,13 +41,13 @@ func FilterFactory() filters.Filter {
4041
return filters.NewNoOp()
4142
}
4243

43-
if filterType == "regex" {
44+
if filterType == constants.FilterRegex {
4445
filter := viper.GetString(config.FilterRegex)
4546
log.Debug().Msgf("using filter '%s' with regular expression '%s'", filterType, filter)
4647
return filters.NewRegex(filter)
4748
}
4849

49-
log.Warn().Msgf("unknown filter '%s', falling back to no filtering")
50+
log.Warn().Msgf("unknown filter '%s', falling back to no filtering", filterType)
5051
return filters.NewNoOp()
5152
}
5253

@@ -59,23 +60,23 @@ func SinksFactory() []sinks.Sink {
5960

6061
for _, sink := range sinkTypes {
6162
switch sink {
62-
case "console":
63+
case constants.SinkConsole:
6364
cOutput := viper.GetString(config.SinkConsoleOutput)
6465
var stdOutput *os.File
6566

66-
if cOutput == "stdout" {
67+
if cOutput == constants.ConsoleStdOut {
6768
stdOutput = os.Stdout
68-
} else if cOutput == "stderr" {
69+
} else if cOutput == constants.ConsoleStdErr {
6970
stdOutput = os.Stderr
7071
} else {
71-
log.Warn().Msgf("unknown console output '%s', falling back to 'stdout'", cOutput)
72+
log.Warn().Msgf("unknown console output '%s', falling back to '%s'", cOutput, constants.ConsoleStdOut)
7273
}
7374
log.Debug().Msgf("adding sink '%s'", sink)
7475
c := sinks.NewConsole(stdOutput)
7576
sinksList = append(sinksList, c)
76-
case "elasticsearch":
77-
if mutatorType != "json" {
78-
log.Panic().Msg("mutator must be 'json' when using 'elasticsearch' sink")
77+
case constants.SinkElasticsearch:
78+
if mutatorType != constants.MutatorJSON {
79+
log.Panic().Msgf("mutator must be '%s' when using '%s' sink", constants.MutatorJSON, constants.SinkElasticsearch)
7980
}
8081

8182
cfg := sinks.ElasticsearchConfig{
@@ -89,10 +90,10 @@ func SinksFactory() []sinks.Sink {
8990
ServiceToken: viper.GetString(config.SinkElasticsearchServiceToken),
9091
}
9192

92-
log.Debug().Msgf("adding sink type '%s'", sink)
93+
log.Debug().Msgf("adding sink '%s'", sink)
9394
es := sinks.NewElasticsearch(cfg)
9495
sinksList = append(sinksList, es)
95-
case "filesystem":
96+
case constants.SinkFilesystem:
9697
fsFilename := viper.GetString(config.SinkFilesystemFilename)
9798
fsMaxAge := viper.GetInt(config.SinkFilesystemMaxAge)
9899
fsMaxBackups := viper.GetInt(config.SinkFilesystemMaxBackups)

filters/noop_test.go

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package filters
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestNoOp(t *testing.T) {
10+
msg := `<165>1 2016-01-01T12:01:21Z hostname appname 1234 ID47 [exampleSDID@32473 iut="9" eventSource="test" eventID="123"] message"`
11+
12+
f := NewNoOp()
13+
s, err := f.Filter(msg)
14+
assert.NoError(t, err)
15+
assert.Equal(t, msg, s)
16+
}

filters/regex_test.go

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package filters
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestRegex(t *testing.T) {
10+
msg := `<165>1 2016-01-01T12:01:21Z hostname appname 1234 ID47 [exampleSDID@32473 iut="9" eventSource="test" eventID="123"] message"`
11+
12+
testCases := []struct {
13+
name string
14+
re string
15+
res string
16+
err bool
17+
}{
18+
{"match", "appname", "", false},
19+
{"no match", "xyz", msg, false},
20+
{"no regex", "", msg, false},
21+
{"invalid regex", "(?=\"", "", true},
22+
}
23+
24+
for _, tc := range testCases {
25+
t.Run(tc.name, func(t *testing.T) {
26+
f := NewRegex(tc.re)
27+
s, err := f.Filter(msg)
28+
if tc.err {
29+
assert.Error(t, err)
30+
} else {
31+
assert.NoError(t, err)
32+
assert.Equal(t, tc.res, s)
33+
}
34+
})
35+
}
36+
}

0 commit comments

Comments
 (0)