Skip to content

Commit

Permalink
add tests
Browse files Browse the repository at this point in the history
Signed-off-by: alexferl <me@alexferl.com>
  • Loading branch information
alexferl committed Apr 7, 2024
1 parent add86ae commit efe86db
Show file tree
Hide file tree
Showing 18 changed files with 332 additions and 534 deletions.
18 changes: 18 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Test and coverage

on: [push, pull_request]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 2
- uses: actions/setup-go@v2
with:
go-version: '1.22'
- name: Run coverage
run: go test -race -coverprofile=coverage.out -covermode=atomic
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ _testmain.go
*.test
*.prof

*.out
*.log
.idea
tinysyslog-bin
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.20.7-alpine3.18 as builder
FROM golang:1.22.2-alpine3.19 as builder
MAINTAINER Alexandre Ferland <me@alexferl.com>

WORKDIR /build
Expand Down
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: dev run test cover fmt pre-commit docker-build docker-run
.PHONY: dev run test cover cover-html fmt pre-commit docker-build docker-run

.DEFAULT: help
help:
Expand All @@ -10,6 +10,8 @@ help:
@echo " run go test"
@echo "make cover"
@echo " run go test with -cover"
@echo "make cover-html"
@echo " run go test with -cover and show HTML"
@echo "make tidy"
@echo " run go mod tidy"
@echo "make fmt"
Expand Down Expand Up @@ -46,6 +48,10 @@ test:
cover:
go test -cover -v ./...

cover-html:
go test -v -coverprofile=coverage.out ./...
go tool cover -html=coverage.out

tidy:
go mod tidy

Expand Down
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# tinysyslog [![Go Report Card](https://goreportcard.com/badge/github.com/alexferl/tinysyslog)](https://goreportcard.com/report/github.com/alexferl/tinysyslog)
# 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)

A tiny and simple syslog server with log rotation. tinysyslog was born out of the need for a tiny, easy to set up and
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,
Expand Down Expand Up @@ -88,25 +88,25 @@ Usage of ./tinysyslogd:
--app-name string The name of the application. (default "tinysyslog")
--bind-addr string IP and port to listen on. (default "127.0.0.1:5140")
--env-name string The environment of the application. Used to load the right configs file. (default "PROD")
--filter string Filter to filter logs with. Valid filters are: regex.
--filter string Filter to filter logs with. Valid filters: [regex]
--filter-regex string Regex to filter with.
--log-level string The granularity of log outputs. Valid levels: 'PANIC', 'FATAL', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'TRACE', 'DISABLED' (default "INFO")
--log-output string The output to write to. 'stdout' means log to stdout, 'stderr' means log to stderr. (default "stdout")
--log-writer string The log writer. Valid writers are: 'console' and 'json'. (default "console")
--mutator string Mutator type to use. Valid mutators are: text, json. (default "text")
--sink-console-output string Console to output too. Valid outputs are: stdout, stderr. (default "stdout")
--log-level string The granularity of log outputs. Valid levels: [PANIC FATAL ERROR WARN INFO DISABLED TRACE DISABLED] (default "INFO")
--log-output string The output to write to. Valid outputs: [stdout stderr] (default "stdout")
--log-writer string The log writer. Valid writers: [console json] (default "console")
--mutator string Mutator type to use. Valid mutators: [text json] (default "text")
--sink-console-output string Console to output to. Valid outputs: [stdout stderr] (default "stdout")
--sink-elasticsearch-addresses strings Elasticsearch server addresses.
--sink-elasticsearch-api-key string Elasticsearch api key.
--sink-elasticsearch-cloud-id string Elasticsearch cloud id.
--sink-elasticsearch-index-name string Elasticsearch index name. (default "tinysyslog")
--sink-elasticsearch-password string Elasticsearch password.
--sink-elasticsearch-service-token string Elasticsearch service token.
--sink-elasticsearch-username string Elasticsearch username.
--sink-filesystem-filename string File to write incoming logs to. (default "syslog.log")
--sink-filesystem-filename string File path to write incoming logs to. (default "syslog.log")
--sink-filesystem-max-age int Maximum age (in days) before a log is deleted. (default 30)
--sink-filesystem-max-backups int Maximum backups to keep. (default 10)
--sink-filesystem-max-size int Maximum log size (in megabytes) before it's rotated. (default 100)
--sinks strings Sinks to save syslogs to. Valid sinks are: console, elasticsearch and filesystem. (default [console])
--sinks strings Sinks to save syslogs to. Valid sinks: [console elasticsearch filesystem] (default [console])
--socket-type string Type of socket to use, TCP or UDP. If no type is specified, both are used.
```

Expand Down
2 changes: 1 addition & 1 deletion cmd/tinysyslogd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ func main() {
server := tinysyslog.NewServer()
err := server.Run()
if err != nil {
log.Fatal().Err(err).Msg("error staring server")
log.Fatal().Err(err).Msg("failed staring server")
}
}
26 changes: 16 additions & 10 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
libLog "github.com/alexferl/golib/log"
"github.com/spf13/pflag"
"github.com/spf13/viper"

"tinysyslog/constants"
)

// Config holds all configuration for our program
Expand Down Expand Up @@ -66,7 +68,7 @@ func NewConfig() *Config {
Logging: libLog.DefaultConfig,
BindAddr: "127.0.0.1:5140",
ConsoleSink: ConsoleSink{
Output: "stdout",
Output: constants.ConsoleStdOut,
},
ElasticSearchSink: ElasticSearchSink{
IndexName: "tinysyslog",
Expand All @@ -81,11 +83,11 @@ func NewConfig() *Config {
LogFile: "stdout",
LogFormat: "text",
LogLevel: "info",
MutatorType: "text",
MutatorType: constants.MutatorText,
RegexFilter: RegexFilter{
Regex: "",
},
SinkTypes: []string{"console"},
SinkTypes: []string{constants.SinkConsole},
SocketType: "",
}
}
Expand Down Expand Up @@ -122,13 +124,17 @@ const (
func (c *Config) addFlags(fs *pflag.FlagSet) {
fs.StringVar(&c.BindAddr, BindAddr, c.BindAddr, "IP and port to listen on.")
fs.StringVar(&c.FilterType, Filter, c.FilterType,
"Filter to filter logs with. Valid filters are: regex.")
fmt.Sprintf("Filter to filter logs with. Valid filters: %s", constants.Filters),
)
fs.StringVar(&c.RegexFilter.Regex, FilterRegex, c.RegexFilter.Regex, "Regex to filter with.")
fs.StringVar(&c.MutatorType, Mutator, c.MutatorType, "Mutator type to use. Valid mutators are: text, json.")
fs.StringSliceVar(&c.SinkTypes, Sinks, c.SinkTypes, "Sinks to save syslogs to. "+
"Valid sinks are: console, elasticsearch and filesystem.")
fs.StringVar(&c.ConsoleSink.Output, SinkConsoleOutput, c.ConsoleSink.Output, "Console to output too. "+
"Valid outputs are: stdout, stderr.")
fs.StringVar(&c.MutatorType, Mutator, c.MutatorType,
fmt.Sprintf("Mutator type to use. Valid mutators: %s", constants.Mutators),
)
fs.StringSliceVar(&c.SinkTypes, Sinks, c.SinkTypes,
fmt.Sprintf("Sinks to save syslogs to. Valid sinks: %s", constants.Sinks),
)
fs.StringVar(&c.ConsoleSink.Output, SinkConsoleOutput, c.ConsoleSink.Output,
fmt.Sprintf("Console to output to. Valid outputs: %s", constants.ConsoleOutputs))
fs.StringSliceVar(&c.ElasticSearchSink.Addresses, SinkElasticsearchAddresses, c.ElasticSearchSink.Addresses,
"Elasticsearch server addresses.")
fs.StringVar(&c.ElasticSearchSink.IndexName, SinkElasticsearchIndexName, c.ElasticSearchSink.IndexName,
Expand All @@ -144,7 +150,7 @@ func (c *Config) addFlags(fs *pflag.FlagSet) {
fs.StringVar(&c.ElasticSearchSink.ServiceToken, SinkElasticsearchServiceToken, c.ElasticSearchSink.ServiceToken,
"Elasticsearch service token.")
fs.StringVar(&c.FilesystemSink.Filename, SinkFilesystemFilename, c.FilesystemSink.Filename,
"File to write incoming logs to.")
"File path to write incoming logs to.")
fs.IntVar(&c.FilesystemSink.MaxAge, SinkFilesystemMaxAge, c.FilesystemSink.MaxAge,
"Maximum age (in days) before a log is deleted.")
fs.IntVar(&c.FilesystemSink.MaxBackups, SinkFilesystemMaxBackups, c.FilesystemSink.MaxBackups,
Expand Down
29 changes: 29 additions & 0 deletions constants/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package constants

const (
MutatorText = "text"
MutatorJSON = "json"
)

var Mutators = []string{MutatorText, MutatorJSON}

const (
FilterRegex = "regex"
)

var Filters = []string{FilterRegex}

const (
SinkConsole = "console"
SinkElasticsearch = "elasticsearch"
SinkFilesystem = "filesystem"
)

var Sinks = []string{SinkConsole, SinkElasticsearch, SinkFilesystem}

const (
ConsoleStdOut = "stdout"
ConsoleStdErr = "stderr"
)

var ConsoleOutputs = []string{ConsoleStdOut, ConsoleStdErr}
29 changes: 15 additions & 14 deletions factories.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/spf13/viper"

"tinysyslog/config"
"tinysyslog/constants"
"tinysyslog/filters"
"tinysyslog/mutators"
"tinysyslog/sinks"
Expand All @@ -17,17 +18,17 @@ import (
func MutatorFactory() mutators.Mutator {
mutatorType := viper.GetString(config.Mutator)

if mutatorType == "text" {
if mutatorType == constants.MutatorText {
log.Debug().Msgf("using mutator '%s'", mutatorType)
return mutators.NewText()
}

if mutatorType == "json" {
if mutatorType == constants.MutatorJSON {
log.Debug().Msgf("using mutator '%s'", mutatorType)
return mutators.NewJSON()
}

log.Warn().Msgf("unknown mutator '%s'. Falling back to 'text'", mutatorType)
log.Warn().Msgf("unknown mutator '%s'. Falling back to '%s'", mutatorType, constants.MutatorText)
return mutators.NewText()
}

Expand All @@ -40,13 +41,13 @@ func FilterFactory() filters.Filter {
return filters.NewNoOp()
}

if filterType == "regex" {
if filterType == constants.FilterRegex {
filter := viper.GetString(config.FilterRegex)
log.Debug().Msgf("using filter '%s' with regular expression '%s'", filterType, filter)
return filters.NewRegex(filter)
}

log.Warn().Msgf("unknown filter '%s', falling back to no filtering")
log.Warn().Msgf("unknown filter '%s', falling back to no filtering", filterType)
return filters.NewNoOp()
}

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

for _, sink := range sinkTypes {
switch sink {
case "console":
case constants.SinkConsole:
cOutput := viper.GetString(config.SinkConsoleOutput)
var stdOutput *os.File

if cOutput == "stdout" {
if cOutput == constants.ConsoleStdOut {
stdOutput = os.Stdout
} else if cOutput == "stderr" {
} else if cOutput == constants.ConsoleStdErr {
stdOutput = os.Stderr
} else {
log.Warn().Msgf("unknown console output '%s', falling back to 'stdout'", cOutput)
log.Warn().Msgf("unknown console output '%s', falling back to '%s'", cOutput, constants.ConsoleStdOut)
}
log.Debug().Msgf("adding sink '%s'", sink)
c := sinks.NewConsole(stdOutput)
sinksList = append(sinksList, c)
case "elasticsearch":
if mutatorType != "json" {
log.Panic().Msg("mutator must be 'json' when using 'elasticsearch' sink")
case constants.SinkElasticsearch:
if mutatorType != constants.MutatorJSON {
log.Panic().Msgf("mutator must be '%s' when using '%s' sink", constants.MutatorJSON, constants.SinkElasticsearch)
}

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

log.Debug().Msgf("adding sink type '%s'", sink)
log.Debug().Msgf("adding sink '%s'", sink)
es := sinks.NewElasticsearch(cfg)
sinksList = append(sinksList, es)
case "filesystem":
case constants.SinkFilesystem:
fsFilename := viper.GetString(config.SinkFilesystemFilename)
fsMaxAge := viper.GetInt(config.SinkFilesystemMaxAge)
fsMaxBackups := viper.GetInt(config.SinkFilesystemMaxBackups)
Expand Down
16 changes: 16 additions & 0 deletions filters/noop_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package filters

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestNoOp(t *testing.T) {
msg := `<165>1 2016-01-01T12:01:21Z hostname appname 1234 ID47 [exampleSDID@32473 iut="9" eventSource="test" eventID="123"] message"`

f := NewNoOp()
s, err := f.Filter(msg)
assert.NoError(t, err)
assert.Equal(t, msg, s)
}
36 changes: 36 additions & 0 deletions filters/regex_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package filters

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestRegex(t *testing.T) {
msg := `<165>1 2016-01-01T12:01:21Z hostname appname 1234 ID47 [exampleSDID@32473 iut="9" eventSource="test" eventID="123"] message"`

testCases := []struct {
name string
re string
res string
err bool
}{
{"match", "appname", "", false},
{"no match", "xyz", msg, false},
{"no regex", "", msg, false},
{"invalid regex", "(?=\"", "", true},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
f := NewRegex(tc.re)
s, err := f.Filter(msg)
if tc.err {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.Equal(t, tc.res, s)
}
})
}
}
Loading

0 comments on commit efe86db

Please sign in to comment.