From c111b239f7d102ac24a79fbef420af46dfec66f9 Mon Sep 17 00:00:00 2001
From: tobi <31960611+tsmethurst@users.noreply.github.com>
Date: Sun, 12 Dec 2021 18:00:20 +0100
Subject: [PATCH] Add optional syslog logrus hook (#343)
* add optional syslog logrus hook
* document syslog
---
README.md | 1 +
cmd/gotosocial/common.go | 9 +-
cmd/gotosocial/flag/server.go | 8 +
cmd/gotosocial/flag/usage.go | 3 +
docs/configuration/syslog.md | 41 ++
example/config.yaml | 25 +
go.mod | 1 +
go.sum | 2 +
internal/config/defaults.go | 4 +
internal/config/keys.go | 9 +
internal/config/values.go | 4 +
internal/log/log.go | 53 +-
internal/log/syslog_test.go | 70 ++
internal/media/util_test.go | 10 +-
mkdocs.yml | 1 +
test/cliparsing.sh | 22 +-
test/test.yaml | 25 +
testrig/config.go | 4 +
testrig/log.go | 29 +-
.../sirupsen/logrus/hooks/syslog/README.md | 39 ++
.../sirupsen/logrus/hooks/syslog/syslog.go | 55 ++
.../mcuadros/go-syslog.v2/.travis.yml | 21 +
vendor/gopkg.in/mcuadros/go-syslog.v2/LICENSE | 19 +
.../gopkg.in/mcuadros/go-syslog.v2/README.md | 48 ++
vendor/gopkg.in/mcuadros/go-syslog.v2/doc.go | 5 +
.../mcuadros/go-syslog.v2/format/automatic.go | 104 +++
.../mcuadros/go-syslog.v2/format/format.go | 29 +
.../mcuadros/go-syslog.v2/format/rfc3164.go | 17 +
.../mcuadros/go-syslog.v2/format/rfc5424.go | 17 +
.../mcuadros/go-syslog.v2/format/rfc6587.go | 45 ++
.../gopkg.in/mcuadros/go-syslog.v2/handler.go | 35 +
.../internal/syslogparser/LICENSE | 23 +
.../internal/syslogparser/README.md | 4 +
.../internal/syslogparser/rfc3164/rfc3164.go | 292 +++++++++
.../internal/syslogparser/rfc5424/rfc5424.go | 606 ++++++++++++++++++
.../internal/syslogparser/syslogparser.go | 213 ++++++
.../gopkg.in/mcuadros/go-syslog.v2/server.go | 378 +++++++++++
vendor/modules.txt | 8 +
38 files changed, 2242 insertions(+), 37 deletions(-)
create mode 100644 docs/configuration/syslog.md
create mode 100644 internal/log/syslog_test.go
create mode 100644 vendor/github.com/sirupsen/logrus/hooks/syslog/README.md
create mode 100644 vendor/github.com/sirupsen/logrus/hooks/syslog/syslog.go
create mode 100644 vendor/gopkg.in/mcuadros/go-syslog.v2/.travis.yml
create mode 100644 vendor/gopkg.in/mcuadros/go-syslog.v2/LICENSE
create mode 100644 vendor/gopkg.in/mcuadros/go-syslog.v2/README.md
create mode 100644 vendor/gopkg.in/mcuadros/go-syslog.v2/doc.go
create mode 100644 vendor/gopkg.in/mcuadros/go-syslog.v2/format/automatic.go
create mode 100644 vendor/gopkg.in/mcuadros/go-syslog.v2/format/format.go
create mode 100644 vendor/gopkg.in/mcuadros/go-syslog.v2/format/rfc3164.go
create mode 100644 vendor/gopkg.in/mcuadros/go-syslog.v2/format/rfc5424.go
create mode 100644 vendor/gopkg.in/mcuadros/go-syslog.v2/format/rfc6587.go
create mode 100644 vendor/gopkg.in/mcuadros/go-syslog.v2/handler.go
create mode 100644 vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/LICENSE
create mode 100644 vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/README.md
create mode 100644 vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc3164/rfc3164.go
create mode 100644 vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc5424/rfc5424.go
create mode 100644 vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/syslogparser.go
create mode 100644 vendor/gopkg.in/mcuadros/go-syslog.v2/server.go
diff --git a/README.md b/README.md
index a430edb11..fd31108a6 100644
--- a/README.md
+++ b/README.md
@@ -185,6 +185,7 @@ The following libraries and frameworks are used by GoToSocial, with gratitude
- [gruf/go-store](https://codeberg.org/gruf/go-store); cacheing library. [MIT License](https://spdx.org/licenses/MIT.html).
- [h2non/filetype](https://github.com/h2non/filetype); filetype checking. [MIT License](https://spdx.org/licenses/MIT.html).
- [jackc/pgx](https://github.com/jackc/pgx); Postgres driver. [MIT License](https://spdx.org/licenses/MIT.html).
+- [mcuadros/go-syslog](https://github.com/mcuadros/go-syslog); Syslog server library. [MIT License](https://spdx.org/licenses/MIT.html).
- [microcosm-cc/bluemonday](https://github.com/microcosm-cc/bluemonday); HTML user-input sanitization. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html).
- [mitchellh/mapstructure](https://github.com/mitchellh/mapstructure); Go interface => struct parsing. [MIT License](https://spdx.org/licenses/MIT.html).
- [modernc.org/sqlite](https://gitlab.com/cznic/sqlite); cgo-free port of SQLite. [Other License](https://gitlab.com/cznic/sqlite/-/blob/master/LICENSE).
diff --git a/cmd/gotosocial/common.go b/cmd/gotosocial/common.go
index 7b5a42652..3f4b12613 100644
--- a/cmd/gotosocial/common.go
+++ b/cmd/gotosocial/common.go
@@ -23,7 +23,6 @@
"fmt"
"github.com/spf13/cobra"
- "github.com/spf13/viper"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/log"
@@ -52,12 +51,8 @@ func preRun(cmd *cobra.Command) error {
// The idea here is to take a GTSAction and run it with the given
// context, after initializing any last-minute things like loggers etc.
func run(ctx context.Context, action action.GTSAction) error {
- // if log level has been set...
- if logLevel := viper.GetString(config.Keys.LogLevel); logLevel != "" {
- // then try to initialize the logger to that level
- if err := log.Initialize(logLevel); err != nil {
- return fmt.Errorf("error initializing log: %s", err)
- }
+ if err := log.Initialize(); err != nil {
+ return fmt.Errorf("error initializing log: %s", err)
}
return action(ctx)
diff --git a/cmd/gotosocial/flag/server.go b/cmd/gotosocial/flag/server.go
index 3d3e946f3..aa68573a4 100644
--- a/cmd/gotosocial/flag/server.go
+++ b/cmd/gotosocial/flag/server.go
@@ -34,6 +34,7 @@ func Server(cmd *cobra.Command, values config.Values) {
OIDC(cmd, values)
SMTP(cmd, values)
Router(cmd, values)
+ Syslog(cmd, values)
}
// Router attaches flags pertaining to the gin router.
@@ -109,3 +110,10 @@ func SMTP(cmd *cobra.Command, values config.Values) {
cmd.Flags().String(config.Keys.SMTPPassword, values.SMTPPassword, usage.SMTPPassword)
cmd.Flags().String(config.Keys.SMTPFrom, values.SMTPFrom, usage.SMTPFrom)
}
+
+// Syslog attaches flags pertaining to syslog config.
+func Syslog(cmd *cobra.Command, values config.Values) {
+ cmd.Flags().Bool(config.Keys.SyslogEnabled, values.SyslogEnabled, usage.SyslogEnabled)
+ cmd.Flags().String(config.Keys.SyslogProtocol, values.SyslogProtocol, usage.SyslogProtocol)
+ cmd.Flags().String(config.Keys.SyslogAddress, values.SyslogAddress, usage.SyslogAddress)
+}
diff --git a/cmd/gotosocial/flag/usage.go b/cmd/gotosocial/flag/usage.go
index aa57048c1..ada5ab271 100644
--- a/cmd/gotosocial/flag/usage.go
+++ b/cmd/gotosocial/flag/usage.go
@@ -73,6 +73,9 @@
SMTPUsername: "Username to authenticate with the smtp server as. Eg., 'postmaster@mail.example.org'",
SMTPPassword: "Password to pass to the smtp server.",
SMTPFrom: "Address to use as the 'from' field of the email. Eg., 'gotosocial@example.org'",
+ SyslogEnabled: "Enable the syslog logging hook. Logs will be mirrored to the configured destination.",
+ SyslogProtocol: "Protocol to use when directing logs to syslog. Leave empty to connect to local syslog.",
+ SyslogAddress: "Address:port to send syslog logs to. Leave empty to connect to local syslog.",
AdminAccountUsername: "the username to create/delete/etc",
AdminAccountEmail: "the email address of this account",
AdminAccountPassword: "the password to set for this account",
diff --git a/docs/configuration/syslog.md b/docs/configuration/syslog.md
new file mode 100644
index 000000000..cf983e0b2
--- /dev/null
+++ b/docs/configuration/syslog.md
@@ -0,0 +1,41 @@
+# Syslog
+
+GoToSocial can be configured to mirror logs to [syslog](https://en.wikipedia.org/wiki/Syslog), either via udp/tcp, or a local syslog (eg., `/var/log/syslog`).
+
+This is useful if you want to daemonize GtS and not handle log rotations etc yourself but rely on a proven implementation.
+
+Logs in syslog will look something like this:
+
+```text
+Dec 12 17:44:03 dilettante ./gotosocial[246860]: time=2021-12-12T17:44:03+01:00 level=info msg=connected to SQLITE database
+Dec 12 17:44:03 dilettante ./gotosocial[246860]: time=2021-12-12T17:44:03+01:00 level=info msg=there are no new migrations to run func=doMigration
+```
+
+## Settings
+
+```yaml
+#########################
+##### SYSLOG CONFIG #####
+#########################
+
+# Config for additional syslog log hooks. See https://en.wikipedia.org/wiki/Syslog,
+# and https://github.com/sirupsen/logrus/tree/master/hooks/syslog.
+#
+# These settings are useful when one wants to daemonize GoToSocial and send logs
+# to a specific place, either a local location or a syslog server. Most users will
+# not need to touch these settings.
+
+# Bool. Enable the syslog logging hook. Logs will be mirrored to the configured destination.
+# Options: [true, false]
+# Default: false
+syslog-enabled: false
+
+# String. Protocol to use when directing logs to syslog. Leave empty to connect to local syslog.
+# Options: ["udp", "tcp", ""]
+# Default: "tcp"
+syslog-protocol: "udp"
+
+# String. Address:port to send syslog logs to. Leave empty to connect to local syslog.
+# Default: "localhost:514"
+syslog-address: "localhost:514"
+```
diff --git a/example/config.yaml b/example/config.yaml
index 4999b10f4..83f68ef20 100644
--- a/example/config.yaml
+++ b/example/config.yaml
@@ -396,3 +396,28 @@ smtp-password: ""
# Examples: ["mail@example.org"]
# Default: ""
smtp-from: ""
+
+#########################
+##### SYSLOG CONFIG #####
+#########################
+
+# Config for additional syslog log hooks. See https://en.wikipedia.org/wiki/Syslog,
+# and https://github.com/sirupsen/logrus/tree/master/hooks/syslog.
+#
+# These settings are useful when one wants to daemonize GoToSocial and send logs
+# to a specific place, either a local location or a syslog server. Most users will
+# not need to touch these settings.
+
+# Bool. Enable the syslog logging hook. Logs will be mirrored to the configured destination.
+# Options: [true, false]
+# Default: false
+syslog-enabled: false
+
+# String. Protocol to use when directing logs to syslog. Leave empty to connect to local syslog.
+# Options: ["udp", "tcp", ""]
+# Default: "tcp"
+syslog-protocol: "udp"
+
+# String. Address:port to send syslog logs to. Leave empty to connect to local syslog.
+# Default: "localhost:514"
+syslog-address: "localhost:514"
diff --git a/go.mod b/go.mod
index 7be478c6a..3f6dfbb2c 100644
--- a/go.mod
+++ b/go.mod
@@ -38,6 +38,7 @@ require (
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
golang.org/x/text v0.3.7
+ gopkg.in/mcuadros/go-syslog.v2 v2.3.0
modernc.org/sqlite v1.14.2
mvdan.cc/xurls/v2 v2.3.0
)
diff --git a/go.sum b/go.sum
index e49f21fc4..f49ca195c 100644
--- a/go.sum
+++ b/go.sum
@@ -1224,6 +1224,8 @@ gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:a
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI=
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/mcuadros/go-syslog.v2 v2.3.0 h1:kcsiS+WsTKyIEPABJBJtoG0KkOS6yzvJ+/eZlhD79kk=
+gopkg.in/mcuadros/go-syslog.v2 v2.3.0/go.mod h1:l5LPIyOOyIdQquNg+oU6Z3524YwrcqEm0aKH+5zpt2U=
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
diff --git a/internal/config/defaults.go b/internal/config/defaults.go
index 01eef8c8c..f1666e1a5 100644
--- a/internal/config/defaults.go
+++ b/internal/config/defaults.go
@@ -84,4 +84,8 @@
SMTPUsername: "",
SMTPPassword: "",
SMTPFrom: "GoToSocial",
+
+ SyslogEnabled: false,
+ SyslogProtocol: "udp",
+ SyslogAddress: "localhost:514",
}
diff --git a/internal/config/keys.go b/internal/config/keys.go
index 80ca36381..a451a8dc6 100644
--- a/internal/config/keys.go
+++ b/internal/config/keys.go
@@ -95,6 +95,11 @@ type KeyNames struct {
SMTPPassword string
SMTPFrom string
+ // syslog
+ SyslogEnabled string
+ SyslogProtocol string
+ SyslogAddress string
+
// admin
AdminAccountUsername string
AdminAccountEmail string
@@ -168,6 +173,10 @@ type KeyNames struct {
SMTPPassword: "smtp-password",
SMTPFrom: "smtp-from",
+ SyslogEnabled: "syslog-enabled",
+ SyslogProtocol: "syslog-protocol",
+ SyslogAddress: "syslog-address",
+
AdminAccountUsername: "username",
AdminAccountEmail: "email",
AdminAccountPassword: "password",
diff --git a/internal/config/values.go b/internal/config/values.go
index 387f934d8..a8ffed1af 100644
--- a/internal/config/values.go
+++ b/internal/config/values.go
@@ -83,6 +83,10 @@ type Values struct {
SMTPPassword string
SMTPFrom string
+ SyslogEnabled bool
+ SyslogProtocol string
+ SyslogAddress string
+
AdminAccountUsername string
AdminAccountEmail string
AdminAccountPassword string
diff --git a/internal/log/log.go b/internal/log/log.go
index b06146368..59c994035 100644
--- a/internal/log/log.go
+++ b/internal/log/log.go
@@ -22,31 +22,60 @@
"bytes"
"os"
+ "log/syslog"
+
"github.com/sirupsen/logrus"
+ lSyslog "github.com/sirupsen/logrus/hooks/syslog"
+ "github.com/spf13/viper"
+ "github.com/superseriousbusiness/gotosocial/internal/config"
)
-// Initialize initializes the global Logrus logger to the specified level
+// Initialize initializes the global Logrus logger, reading the desired
+// log level from the viper store, or using a default if the level
+// has not been set in viper.
+//
// It also sets the output to log.outputSplitter,
// so you get error logs on stderr and normal logs on stdout.
-func Initialize(level string) error {
+//
+// If syslog settings are also in viper, then Syslog will be initialized as well.
+func Initialize() error {
logrus.SetOutput(&outputSplitter{})
- logLevel, err := logrus.ParseLevel(level)
- if err != nil {
- return err
- }
- logrus.SetLevel(logLevel)
-
- if logLevel == logrus.TraceLevel {
- logrus.SetReportCaller(true)
- }
-
logrus.SetFormatter(&logrus.TextFormatter{
DisableColors: true,
DisableQuote: true,
FullTimestamp: true,
})
+ keys := config.Keys
+
+ // check if a desired log level has been set
+ logLevel := viper.GetString(keys.LogLevel)
+ if logLevel != "" {
+ level, err := logrus.ParseLevel(logLevel)
+ if err != nil {
+ return err
+ }
+ logrus.SetLevel(level)
+
+ if level == logrus.TraceLevel {
+ logrus.SetReportCaller(true)
+ }
+ }
+
+ // check if syslog has been enabled, and configure it if so
+ if syslogEnabled := viper.GetBool(keys.SyslogEnabled); syslogEnabled {
+ protocol := viper.GetString(keys.SyslogProtocol)
+ address := viper.GetString(keys.SyslogAddress)
+
+ hook, err := lSyslog.NewSyslogHook(protocol, address, syslog.LOG_INFO, "")
+ if err != nil {
+ return err
+ }
+
+ logrus.AddHook(hook)
+ }
+
return nil
}
diff --git a/internal/log/syslog_test.go b/internal/log/syslog_test.go
new file mode 100644
index 000000000..4b6ee14ab
--- /dev/null
+++ b/internal/log/syslog_test.go
@@ -0,0 +1,70 @@
+/*
+ GoToSocial
+ Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+
+package log_test
+
+import (
+ "testing"
+
+ "github.com/sirupsen/logrus"
+ "github.com/spf13/viper"
+ "github.com/stretchr/testify/suite"
+ "github.com/superseriousbusiness/gotosocial/internal/config"
+ "github.com/superseriousbusiness/gotosocial/testrig"
+ "gopkg.in/mcuadros/go-syslog.v2"
+ "gopkg.in/mcuadros/go-syslog.v2/format"
+)
+
+type SyslogTestSuite struct {
+ suite.Suite
+ syslogServer *syslog.Server
+ syslogChannel chan format.LogParts
+}
+
+func (suite *SyslogTestSuite) SetupTest() {
+ testrig.InitTestConfig()
+
+ viper.Set(config.Keys.SyslogEnabled, true)
+ viper.Set(config.Keys.SyslogProtocol, "udp")
+ viper.Set(config.Keys.SyslogAddress, "localhost:42069")
+ server, channel, err := testrig.InitTestSyslog()
+ if err != nil {
+ panic(err)
+ }
+ suite.syslogServer = server
+ suite.syslogChannel = channel
+
+ testrig.InitTestLog()
+}
+
+func (suite *SyslogTestSuite) TearDownTest() {
+ if err := suite.syslogServer.Kill(); err != nil {
+ panic(err)
+ }
+}
+
+func (suite *SyslogTestSuite) TestSyslog() {
+ logrus.Warn("this is a test of the emergency broadcast system!")
+
+ message := <-suite.syslogChannel
+ suite.Contains(message["content"], "this is a test of the emergency broadcast system!")
+}
+
+func TestSyslogTestSuite(t *testing.T) {
+ suite.Run(t, &SyslogTestSuite{})
+}
diff --git a/internal/media/util_test.go b/internal/media/util_test.go
index c54fb6f2c..fb36e1476 100644
--- a/internal/media/util_test.go
+++ b/internal/media/util_test.go
@@ -19,11 +19,13 @@
package media
import (
- "github.com/sirupsen/logrus"
- "github.com/superseriousbusiness/gotosocial/internal/log"
"io/ioutil"
"testing"
+ "github.com/spf13/viper"
+ "github.com/superseriousbusiness/gotosocial/internal/config"
+ "github.com/superseriousbusiness/gotosocial/internal/log"
+
"github.com/stretchr/testify/suite"
)
@@ -38,11 +40,11 @@ type MediaUtilTestSuite struct {
// SetupSuite sets some variables on the suite that we can use as consts (more or less) throughout
func (suite *MediaUtilTestSuite) SetupSuite() {
// doesn't use testrig.InitTestLog() helper to prevent import cycle
- err := log.Initialize(logrus.TraceLevel.String())
+ viper.Set(config.Keys.LogLevel, "trace")
+ err := log.Initialize()
if err != nil {
panic(err)
}
-
}
func (suite *MediaUtilTestSuite) TearDownSuite() {
diff --git a/mkdocs.yml b/mkdocs.yml
index 59965ce22..4986a488b 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -29,6 +29,7 @@ nav:
- "configuration/letsencrypt.md"
- "configuration/oidc.md"
- "configuration/smtp.md"
+ - "configuration/syslog.md"
- "Admin":
- "admin/admin_panel.md"
- "admin/cli.md"
diff --git a/test/cliparsing.sh b/test/cliparsing.sh
index 13e61ede3..364ae8ab0 100755
--- a/test/cliparsing.sh
+++ b/test/cliparsing.sh
@@ -5,7 +5,7 @@ set -e
echo "STARTING CLI TESTS"
echo "TEST_1 Make sure defaults are set correctly."
-TEST_1_EXPECTED='{"account-domain":"","accounts-approval-required":true,"accounts-reason-required":true,"accounts-registration-open":true,"application-name":"gotosocial","bind-address":"0.0.0.0","config-path":"","db-address":"localhost","db-database":"postgres","db-password":"postgres","db-port":5432,"db-tls-ca-cert":"","db-tls-mode":"disable","db-type":"postgres","db-user":"postgres","help":false,"host":"","letsencrypt-cert-dir":"/gotosocial/storage/certs","letsencrypt-email-address":"","letsencrypt-enabled":true,"letsencrypt-port":80,"log-level":"info","media-description-max-chars":500,"media-description-min-chars":0,"media-image-max-size":2097152,"media-video-max-size":10485760,"oidc-client-id":"","oidc-client-secret":"","oidc-enabled":false,"oidc-idp-name":"","oidc-issuer":"","oidc-scopes":["openid","profile","email","groups"],"oidc-skip-verification":false,"port":8080,"protocol":"https","smtp-from":"GoToSocial","smtp-host":"","smtp-password":"","smtp-port":0,"smtp-username":"","software-version":"","statuses-cw-max-chars":100,"statuses-max-chars":5000,"statuses-media-max-files":6,"statuses-poll-max-options":6,"statuses-poll-option-max-chars":50,"storage-backend":"local","storage-base-path":"/gotosocial/storage","storage-serve-base-path":"/fileserver","storage-serve-host":"localhost","storage-serve-protocol":"https","trusted-proxies":["127.0.0.1/32"],"web-asset-base-dir":"./web/assets/","web-template-base-dir":"./web/template/"}'
+TEST_1_EXPECTED='{"account-domain":"","accounts-approval-required":true,"accounts-reason-required":true,"accounts-registration-open":true,"application-name":"gotosocial","bind-address":"0.0.0.0","config-path":"","db-address":"localhost","db-database":"postgres","db-password":"postgres","db-port":5432,"db-tls-ca-cert":"","db-tls-mode":"disable","db-type":"postgres","db-user":"postgres","help":false,"host":"","letsencrypt-cert-dir":"/gotosocial/storage/certs","letsencrypt-email-address":"","letsencrypt-enabled":true,"letsencrypt-port":80,"log-level":"info","media-description-max-chars":500,"media-description-min-chars":0,"media-image-max-size":2097152,"media-video-max-size":10485760,"oidc-client-id":"","oidc-client-secret":"","oidc-enabled":false,"oidc-idp-name":"","oidc-issuer":"","oidc-scopes":["openid","profile","email","groups"],"oidc-skip-verification":false,"port":8080,"protocol":"https","smtp-from":"GoToSocial","smtp-host":"","smtp-password":"","smtp-port":0,"smtp-username":"","software-version":"","statuses-cw-max-chars":100,"statuses-max-chars":5000,"statuses-media-max-files":6,"statuses-poll-max-options":6,"statuses-poll-option-max-chars":50,"storage-backend":"local","storage-base-path":"/gotosocial/storage","storage-serve-base-path":"/fileserver","storage-serve-host":"localhost","storage-serve-protocol":"https","syslog-address":"localhost:514","syslog-enabled":false,"syslog-protocol":"udp","trusted-proxies":["127.0.0.1/32"],"web-asset-base-dir":"./web/assets/","web-template-base-dir":"./web/template/"}'
TEST_1="$(go run ./cmd/gotosocial/... debug config)"
if [ "${TEST_1}" != "${TEST_1_EXPECTED}" ]; then
echo "TEST_1 not equal TEST_1_EXPECTED"
@@ -15,7 +15,7 @@ else
fi
echo "TEST_2 Override db-address from default using cli flag."
-TEST_2_EXPECTED='{"account-domain":"","accounts-approval-required":true,"accounts-reason-required":true,"accounts-registration-open":true,"application-name":"gotosocial","bind-address":"0.0.0.0","config-path":"","db-address":"some.db.address","db-database":"postgres","db-password":"postgres","db-port":5432,"db-tls-ca-cert":"","db-tls-mode":"disable","db-type":"postgres","db-user":"postgres","help":false,"host":"","letsencrypt-cert-dir":"/gotosocial/storage/certs","letsencrypt-email-address":"","letsencrypt-enabled":true,"letsencrypt-port":80,"log-level":"info","media-description-max-chars":500,"media-description-min-chars":0,"media-image-max-size":2097152,"media-video-max-size":10485760,"oidc-client-id":"","oidc-client-secret":"","oidc-enabled":false,"oidc-idp-name":"","oidc-issuer":"","oidc-scopes":["openid","profile","email","groups"],"oidc-skip-verification":false,"port":8080,"protocol":"https","smtp-from":"GoToSocial","smtp-host":"","smtp-password":"","smtp-port":0,"smtp-username":"","software-version":"","statuses-cw-max-chars":100,"statuses-max-chars":5000,"statuses-media-max-files":6,"statuses-poll-max-options":6,"statuses-poll-option-max-chars":50,"storage-backend":"local","storage-base-path":"/gotosocial/storage","storage-serve-base-path":"/fileserver","storage-serve-host":"localhost","storage-serve-protocol":"https","trusted-proxies":["127.0.0.1/32"],"web-asset-base-dir":"./web/assets/","web-template-base-dir":"./web/template/"}'
+TEST_2_EXPECTED='{"account-domain":"","accounts-approval-required":true,"accounts-reason-required":true,"accounts-registration-open":true,"application-name":"gotosocial","bind-address":"0.0.0.0","config-path":"","db-address":"some.db.address","db-database":"postgres","db-password":"postgres","db-port":5432,"db-tls-ca-cert":"","db-tls-mode":"disable","db-type":"postgres","db-user":"postgres","help":false,"host":"","letsencrypt-cert-dir":"/gotosocial/storage/certs","letsencrypt-email-address":"","letsencrypt-enabled":true,"letsencrypt-port":80,"log-level":"info","media-description-max-chars":500,"media-description-min-chars":0,"media-image-max-size":2097152,"media-video-max-size":10485760,"oidc-client-id":"","oidc-client-secret":"","oidc-enabled":false,"oidc-idp-name":"","oidc-issuer":"","oidc-scopes":["openid","profile","email","groups"],"oidc-skip-verification":false,"port":8080,"protocol":"https","smtp-from":"GoToSocial","smtp-host":"","smtp-password":"","smtp-port":0,"smtp-username":"","software-version":"","statuses-cw-max-chars":100,"statuses-max-chars":5000,"statuses-media-max-files":6,"statuses-poll-max-options":6,"statuses-poll-option-max-chars":50,"storage-backend":"local","storage-base-path":"/gotosocial/storage","storage-serve-base-path":"/fileserver","storage-serve-host":"localhost","storage-serve-protocol":"https","syslog-address":"localhost:514","syslog-enabled":false,"syslog-protocol":"udp","trusted-proxies":["127.0.0.1/32"],"web-asset-base-dir":"./web/assets/","web-template-base-dir":"./web/template/"}'
TEST_2="$(go run ./cmd/gotosocial/... --db-address some.db.address debug config)"
if [ "${TEST_2}" != "${TEST_2_EXPECTED}" ]; then
echo "TEST_2 not equal TEST_2_EXPECTED"
@@ -25,7 +25,7 @@ else
fi
echo "TEST_3 Override db-address from default using env var."
-TEST_3_EXPECTED='{"account-domain":"","accounts-approval-required":true,"accounts-reason-required":true,"accounts-registration-open":true,"application-name":"gotosocial","bind-address":"0.0.0.0","config-path":"","db-address":"some.db.address","db-database":"postgres","db-password":"postgres","db-port":5432,"db-tls-ca-cert":"","db-tls-mode":"disable","db-type":"postgres","db-user":"postgres","help":false,"host":"","letsencrypt-cert-dir":"/gotosocial/storage/certs","letsencrypt-email-address":"","letsencrypt-enabled":true,"letsencrypt-port":80,"log-level":"info","media-description-max-chars":500,"media-description-min-chars":0,"media-image-max-size":2097152,"media-video-max-size":10485760,"oidc-client-id":"","oidc-client-secret":"","oidc-enabled":false,"oidc-idp-name":"","oidc-issuer":"","oidc-scopes":["openid","profile","email","groups"],"oidc-skip-verification":false,"port":8080,"protocol":"https","smtp-from":"GoToSocial","smtp-host":"","smtp-password":"","smtp-port":0,"smtp-username":"","software-version":"","statuses-cw-max-chars":100,"statuses-max-chars":5000,"statuses-media-max-files":6,"statuses-poll-max-options":6,"statuses-poll-option-max-chars":50,"storage-backend":"local","storage-base-path":"/gotosocial/storage","storage-serve-base-path":"/fileserver","storage-serve-host":"localhost","storage-serve-protocol":"https","trusted-proxies":["127.0.0.1/32"],"web-asset-base-dir":"./web/assets/","web-template-base-dir":"./web/template/"}'
+TEST_3_EXPECTED='{"account-domain":"","accounts-approval-required":true,"accounts-reason-required":true,"accounts-registration-open":true,"application-name":"gotosocial","bind-address":"0.0.0.0","config-path":"","db-address":"some.db.address","db-database":"postgres","db-password":"postgres","db-port":5432,"db-tls-ca-cert":"","db-tls-mode":"disable","db-type":"postgres","db-user":"postgres","help":false,"host":"","letsencrypt-cert-dir":"/gotosocial/storage/certs","letsencrypt-email-address":"","letsencrypt-enabled":true,"letsencrypt-port":80,"log-level":"info","media-description-max-chars":500,"media-description-min-chars":0,"media-image-max-size":2097152,"media-video-max-size":10485760,"oidc-client-id":"","oidc-client-secret":"","oidc-enabled":false,"oidc-idp-name":"","oidc-issuer":"","oidc-scopes":["openid","profile","email","groups"],"oidc-skip-verification":false,"port":8080,"protocol":"https","smtp-from":"GoToSocial","smtp-host":"","smtp-password":"","smtp-port":0,"smtp-username":"","software-version":"","statuses-cw-max-chars":100,"statuses-max-chars":5000,"statuses-media-max-files":6,"statuses-poll-max-options":6,"statuses-poll-option-max-chars":50,"storage-backend":"local","storage-base-path":"/gotosocial/storage","storage-serve-base-path":"/fileserver","storage-serve-host":"localhost","storage-serve-protocol":"https","syslog-address":"localhost:514","syslog-enabled":false,"syslog-protocol":"udp","trusted-proxies":["127.0.0.1/32"],"web-asset-base-dir":"./web/assets/","web-template-base-dir":"./web/template/"}'
TEST_3="$(GTS_DB_ADDRESS=some.db.address go run ./cmd/gotosocial/... debug config)"
if [ "${TEST_3}" != "${TEST_3_EXPECTED}" ]; then
echo "TEST_3 not equal TEST_3_EXPECTED"
@@ -35,7 +35,7 @@ else
fi
echo "TEST_4 Override db-address from default using both env var and cli flag. The cli flag should take priority."
-TEST_4_EXPECTED='{"account-domain":"","accounts-approval-required":true,"accounts-reason-required":true,"accounts-registration-open":true,"application-name":"gotosocial","bind-address":"0.0.0.0","config-path":"","db-address":"some.other.db.address","db-database":"postgres","db-password":"postgres","db-port":5432,"db-tls-ca-cert":"","db-tls-mode":"disable","db-type":"postgres","db-user":"postgres","help":false,"host":"","letsencrypt-cert-dir":"/gotosocial/storage/certs","letsencrypt-email-address":"","letsencrypt-enabled":true,"letsencrypt-port":80,"log-level":"info","media-description-max-chars":500,"media-description-min-chars":0,"media-image-max-size":2097152,"media-video-max-size":10485760,"oidc-client-id":"","oidc-client-secret":"","oidc-enabled":false,"oidc-idp-name":"","oidc-issuer":"","oidc-scopes":["openid","profile","email","groups"],"oidc-skip-verification":false,"port":8080,"protocol":"https","smtp-from":"GoToSocial","smtp-host":"","smtp-password":"","smtp-port":0,"smtp-username":"","software-version":"","statuses-cw-max-chars":100,"statuses-max-chars":5000,"statuses-media-max-files":6,"statuses-poll-max-options":6,"statuses-poll-option-max-chars":50,"storage-backend":"local","storage-base-path":"/gotosocial/storage","storage-serve-base-path":"/fileserver","storage-serve-host":"localhost","storage-serve-protocol":"https","trusted-proxies":["127.0.0.1/32"],"web-asset-base-dir":"./web/assets/","web-template-base-dir":"./web/template/"}'
+TEST_4_EXPECTED='{"account-domain":"","accounts-approval-required":true,"accounts-reason-required":true,"accounts-registration-open":true,"application-name":"gotosocial","bind-address":"0.0.0.0","config-path":"","db-address":"some.other.db.address","db-database":"postgres","db-password":"postgres","db-port":5432,"db-tls-ca-cert":"","db-tls-mode":"disable","db-type":"postgres","db-user":"postgres","help":false,"host":"","letsencrypt-cert-dir":"/gotosocial/storage/certs","letsencrypt-email-address":"","letsencrypt-enabled":true,"letsencrypt-port":80,"log-level":"info","media-description-max-chars":500,"media-description-min-chars":0,"media-image-max-size":2097152,"media-video-max-size":10485760,"oidc-client-id":"","oidc-client-secret":"","oidc-enabled":false,"oidc-idp-name":"","oidc-issuer":"","oidc-scopes":["openid","profile","email","groups"],"oidc-skip-verification":false,"port":8080,"protocol":"https","smtp-from":"GoToSocial","smtp-host":"","smtp-password":"","smtp-port":0,"smtp-username":"","software-version":"","statuses-cw-max-chars":100,"statuses-max-chars":5000,"statuses-media-max-files":6,"statuses-poll-max-options":6,"statuses-poll-option-max-chars":50,"storage-backend":"local","storage-base-path":"/gotosocial/storage","storage-serve-base-path":"/fileserver","storage-serve-host":"localhost","storage-serve-protocol":"https","syslog-address":"localhost:514","syslog-enabled":false,"syslog-protocol":"udp","trusted-proxies":["127.0.0.1/32"],"web-asset-base-dir":"./web/assets/","web-template-base-dir":"./web/template/"}'
TEST_4="$(GTS_DB_ADDRESS=some.db.address go run ./cmd/gotosocial/... --db-address some.other.db.address debug config)"
if [ "${TEST_4}" != "${TEST_4_EXPECTED}" ]; then
echo "TEST_4 not equal TEST_4_EXPECTED"
@@ -45,7 +45,7 @@ else
fi
echo "TEST_5 Test loading a config file by passing an env var."
-TEST_5_EXPECTED='{"account-domain":"example.org","accounts-approval-required":true,"accounts-reason-required":true,"accounts-registration-open":true,"application-name":"gotosocial","bind-address":"0.0.0.0","config-path":"./test/test.yaml","db-address":"127.0.0.1","db-database":"postgres","db-password":"postgres","db-port":5432,"db-tls-ca-cert":"","db-tls-mode":"disable","db-type":"postgres","db-user":"postgres","help":false,"host":"gts.example.org","letsencrypt-cert-dir":"/gotosocial/storage/certs","letsencrypt-email-address":"","letsencrypt-enabled":true,"letsencrypt-port":80,"log-level":"info","media-description-max-chars":500,"media-description-min-chars":0,"media-image-max-size":2097152,"media-video-max-size":10485760,"oidc-client-id":"","oidc-client-secret":"","oidc-enabled":false,"oidc-idp-name":"","oidc-issuer":"","oidc-scopes":["openid","email","profile","groups"],"oidc-skip-verification":false,"port":8080,"protocol":"https","smtp-from":"someone@example.org","smtp-host":"verycoolemailhost.mail","smtp-password":"smtp-password","smtp-port":8888,"smtp-username":"smtp-username","software-version":"","statuses-cw-max-chars":100,"statuses-max-chars":5000,"statuses-media-max-files":6,"statuses-poll-max-options":6,"statuses-poll-option-max-chars":50,"storage-backend":"local","storage-base-path":"/gotosocial/storage","storage-serve-base-path":"/fileserver","storage-serve-host":"localhost","storage-serve-protocol":"https","trusted-proxies":["127.0.0.1/32","0.0.0.0/0"],"web-asset-base-dir":"./web/assets/","web-template-base-dir":"./web/template/"}'
+TEST_5_EXPECTED='{"account-domain":"example.org","accounts-approval-required":true,"accounts-reason-required":true,"accounts-registration-open":true,"application-name":"gotosocial","bind-address":"0.0.0.0","config-path":"./test/test.yaml","db-address":"127.0.0.1","db-database":"postgres","db-password":"postgres","db-port":5432,"db-tls-ca-cert":"","db-tls-mode":"disable","db-type":"postgres","db-user":"postgres","help":false,"host":"gts.example.org","letsencrypt-cert-dir":"/gotosocial/storage/certs","letsencrypt-email-address":"","letsencrypt-enabled":true,"letsencrypt-port":80,"log-level":"info","media-description-max-chars":500,"media-description-min-chars":0,"media-image-max-size":2097152,"media-video-max-size":10485760,"oidc-client-id":"","oidc-client-secret":"","oidc-enabled":false,"oidc-idp-name":"","oidc-issuer":"","oidc-scopes":["openid","email","profile","groups"],"oidc-skip-verification":false,"port":8080,"protocol":"https","smtp-from":"someone@example.org","smtp-host":"verycoolemailhost.mail","smtp-password":"smtp-password","smtp-port":8888,"smtp-username":"smtp-username","software-version":"","statuses-cw-max-chars":100,"statuses-max-chars":5000,"statuses-media-max-files":6,"statuses-poll-max-options":6,"statuses-poll-option-max-chars":50,"storage-backend":"local","storage-base-path":"/gotosocial/storage","storage-serve-base-path":"/fileserver","storage-serve-host":"localhost","storage-serve-protocol":"https","syslog-address":"localhost:514","syslog-enabled":false,"syslog-protocol":"udp","trusted-proxies":["127.0.0.1/32","0.0.0.0/0"],"web-asset-base-dir":"./web/assets/","web-template-base-dir":"./web/template/"}'
TEST_5="$(GTS_CONFIG_PATH=./test/test.yaml go run ./cmd/gotosocial/... debug config)"
if [ "${TEST_5}" != "${TEST_5_EXPECTED}" ]; then
echo "TEST_5 not equal TEST_5_EXPECTED"
@@ -55,7 +55,7 @@ else
fi
echo "TEST_6 Test loading a config file by passing cli flag."
-TEST_6_EXPECTED='{"account-domain":"example.org","accounts-approval-required":true,"accounts-reason-required":true,"accounts-registration-open":true,"application-name":"gotosocial","bind-address":"0.0.0.0","config-path":"./test/test.yaml","db-address":"127.0.0.1","db-database":"postgres","db-password":"postgres","db-port":5432,"db-tls-ca-cert":"","db-tls-mode":"disable","db-type":"postgres","db-user":"postgres","help":false,"host":"gts.example.org","letsencrypt-cert-dir":"/gotosocial/storage/certs","letsencrypt-email-address":"","letsencrypt-enabled":true,"letsencrypt-port":80,"log-level":"info","media-description-max-chars":500,"media-description-min-chars":0,"media-image-max-size":2097152,"media-video-max-size":10485760,"oidc-client-id":"","oidc-client-secret":"","oidc-enabled":false,"oidc-idp-name":"","oidc-issuer":"","oidc-scopes":["openid","email","profile","groups"],"oidc-skip-verification":false,"port":8080,"protocol":"https","smtp-from":"someone@example.org","smtp-host":"verycoolemailhost.mail","smtp-password":"smtp-password","smtp-port":8888,"smtp-username":"smtp-username","software-version":"","statuses-cw-max-chars":100,"statuses-max-chars":5000,"statuses-media-max-files":6,"statuses-poll-max-options":6,"statuses-poll-option-max-chars":50,"storage-backend":"local","storage-base-path":"/gotosocial/storage","storage-serve-base-path":"/fileserver","storage-serve-host":"localhost","storage-serve-protocol":"https","trusted-proxies":["127.0.0.1/32","0.0.0.0/0"],"web-asset-base-dir":"./web/assets/","web-template-base-dir":"./web/template/"}'
+TEST_6_EXPECTED='{"account-domain":"example.org","accounts-approval-required":true,"accounts-reason-required":true,"accounts-registration-open":true,"application-name":"gotosocial","bind-address":"0.0.0.0","config-path":"./test/test.yaml","db-address":"127.0.0.1","db-database":"postgres","db-password":"postgres","db-port":5432,"db-tls-ca-cert":"","db-tls-mode":"disable","db-type":"postgres","db-user":"postgres","help":false,"host":"gts.example.org","letsencrypt-cert-dir":"/gotosocial/storage/certs","letsencrypt-email-address":"","letsencrypt-enabled":true,"letsencrypt-port":80,"log-level":"info","media-description-max-chars":500,"media-description-min-chars":0,"media-image-max-size":2097152,"media-video-max-size":10485760,"oidc-client-id":"","oidc-client-secret":"","oidc-enabled":false,"oidc-idp-name":"","oidc-issuer":"","oidc-scopes":["openid","email","profile","groups"],"oidc-skip-verification":false,"port":8080,"protocol":"https","smtp-from":"someone@example.org","smtp-host":"verycoolemailhost.mail","smtp-password":"smtp-password","smtp-port":8888,"smtp-username":"smtp-username","software-version":"","statuses-cw-max-chars":100,"statuses-max-chars":5000,"statuses-media-max-files":6,"statuses-poll-max-options":6,"statuses-poll-option-max-chars":50,"storage-backend":"local","storage-base-path":"/gotosocial/storage","storage-serve-base-path":"/fileserver","storage-serve-host":"localhost","storage-serve-protocol":"https","syslog-address":"localhost:514","syslog-enabled":false,"syslog-protocol":"udp","trusted-proxies":["127.0.0.1/32","0.0.0.0/0"],"web-asset-base-dir":"./web/assets/","web-template-base-dir":"./web/template/"}'
TEST_6="$(go run ./cmd/gotosocial/... --config-path ./test/test.yaml debug config)"
if [ "${TEST_6}" != "${TEST_6_EXPECTED}" ]; then
echo "TEST_6 not equal TEST_6_EXPECTED"
@@ -65,7 +65,7 @@ else
fi
echo "TEST_7 Test loading a config file and overriding one of the variables with a cli flag."
-TEST_7_EXPECTED='{"account-domain":"","accounts-approval-required":true,"accounts-reason-required":true,"accounts-registration-open":true,"application-name":"gotosocial","bind-address":"0.0.0.0","config-path":"./test/test.yaml","db-address":"127.0.0.1","db-database":"postgres","db-password":"postgres","db-port":5432,"db-tls-ca-cert":"","db-tls-mode":"disable","db-type":"postgres","db-user":"postgres","help":false,"host":"gts.example.org","letsencrypt-cert-dir":"/gotosocial/storage/certs","letsencrypt-email-address":"","letsencrypt-enabled":true,"letsencrypt-port":80,"log-level":"info","media-description-max-chars":500,"media-description-min-chars":0,"media-image-max-size":2097152,"media-video-max-size":10485760,"oidc-client-id":"","oidc-client-secret":"","oidc-enabled":false,"oidc-idp-name":"","oidc-issuer":"","oidc-scopes":["openid","email","profile","groups"],"oidc-skip-verification":false,"port":8080,"protocol":"https","smtp-from":"someone@example.org","smtp-host":"verycoolemailhost.mail","smtp-password":"smtp-password","smtp-port":8888,"smtp-username":"smtp-username","software-version":"","statuses-cw-max-chars":100,"statuses-max-chars":5000,"statuses-media-max-files":6,"statuses-poll-max-options":6,"statuses-poll-option-max-chars":50,"storage-backend":"local","storage-base-path":"/gotosocial/storage","storage-serve-base-path":"/fileserver","storage-serve-host":"localhost","storage-serve-protocol":"https","trusted-proxies":["127.0.0.1/32","0.0.0.0/0"],"web-asset-base-dir":"./web/assets/","web-template-base-dir":"./web/template/"}'
+TEST_7_EXPECTED='{"account-domain":"","accounts-approval-required":true,"accounts-reason-required":true,"accounts-registration-open":true,"application-name":"gotosocial","bind-address":"0.0.0.0","config-path":"./test/test.yaml","db-address":"127.0.0.1","db-database":"postgres","db-password":"postgres","db-port":5432,"db-tls-ca-cert":"","db-tls-mode":"disable","db-type":"postgres","db-user":"postgres","help":false,"host":"gts.example.org","letsencrypt-cert-dir":"/gotosocial/storage/certs","letsencrypt-email-address":"","letsencrypt-enabled":true,"letsencrypt-port":80,"log-level":"info","media-description-max-chars":500,"media-description-min-chars":0,"media-image-max-size":2097152,"media-video-max-size":10485760,"oidc-client-id":"","oidc-client-secret":"","oidc-enabled":false,"oidc-idp-name":"","oidc-issuer":"","oidc-scopes":["openid","email","profile","groups"],"oidc-skip-verification":false,"port":8080,"protocol":"https","smtp-from":"someone@example.org","smtp-host":"verycoolemailhost.mail","smtp-password":"smtp-password","smtp-port":8888,"smtp-username":"smtp-username","software-version":"","statuses-cw-max-chars":100,"statuses-max-chars":5000,"statuses-media-max-files":6,"statuses-poll-max-options":6,"statuses-poll-option-max-chars":50,"storage-backend":"local","storage-base-path":"/gotosocial/storage","storage-serve-base-path":"/fileserver","storage-serve-host":"localhost","storage-serve-protocol":"https","syslog-address":"localhost:514","syslog-enabled":false,"syslog-protocol":"udp","trusted-proxies":["127.0.0.1/32","0.0.0.0/0"],"web-asset-base-dir":"./web/assets/","web-template-base-dir":"./web/template/"}'
TEST_7="$(go run ./cmd/gotosocial/... --config-path ./test/test.yaml --account-domain '' debug config)"
if [ "${TEST_7}" != "${TEST_7_EXPECTED}" ]; then
echo "TEST_7 not equal TEST_7_EXPECTED"
@@ -75,7 +75,7 @@ else
fi
echo "TEST_8 Test loading a config file and overriding one of the variables with an env var."
-TEST_8_EXPECTED='{"account-domain":"peepee","accounts-approval-required":true,"accounts-reason-required":true,"accounts-registration-open":true,"application-name":"gotosocial","bind-address":"0.0.0.0","config-path":"./test/test.yaml","db-address":"127.0.0.1","db-database":"postgres","db-password":"postgres","db-port":5432,"db-tls-ca-cert":"","db-tls-mode":"disable","db-type":"postgres","db-user":"postgres","help":false,"host":"gts.example.org","letsencrypt-cert-dir":"/gotosocial/storage/certs","letsencrypt-email-address":"","letsencrypt-enabled":true,"letsencrypt-port":80,"log-level":"info","media-description-max-chars":500,"media-description-min-chars":0,"media-image-max-size":2097152,"media-video-max-size":10485760,"oidc-client-id":"","oidc-client-secret":"","oidc-enabled":false,"oidc-idp-name":"","oidc-issuer":"","oidc-scopes":["openid","email","profile","groups"],"oidc-skip-verification":false,"port":8080,"protocol":"https","smtp-from":"someone@example.org","smtp-host":"verycoolemailhost.mail","smtp-password":"smtp-password","smtp-port":8888,"smtp-username":"smtp-username","software-version":"","statuses-cw-max-chars":100,"statuses-max-chars":5000,"statuses-media-max-files":6,"statuses-poll-max-options":6,"statuses-poll-option-max-chars":50,"storage-backend":"local","storage-base-path":"/gotosocial/storage","storage-serve-base-path":"/fileserver","storage-serve-host":"localhost","storage-serve-protocol":"https","trusted-proxies":["127.0.0.1/32","0.0.0.0/0"],"web-asset-base-dir":"./web/assets/","web-template-base-dir":"./web/template/"}'
+TEST_8_EXPECTED='{"account-domain":"peepee","accounts-approval-required":true,"accounts-reason-required":true,"accounts-registration-open":true,"application-name":"gotosocial","bind-address":"0.0.0.0","config-path":"./test/test.yaml","db-address":"127.0.0.1","db-database":"postgres","db-password":"postgres","db-port":5432,"db-tls-ca-cert":"","db-tls-mode":"disable","db-type":"postgres","db-user":"postgres","help":false,"host":"gts.example.org","letsencrypt-cert-dir":"/gotosocial/storage/certs","letsencrypt-email-address":"","letsencrypt-enabled":true,"letsencrypt-port":80,"log-level":"info","media-description-max-chars":500,"media-description-min-chars":0,"media-image-max-size":2097152,"media-video-max-size":10485760,"oidc-client-id":"","oidc-client-secret":"","oidc-enabled":false,"oidc-idp-name":"","oidc-issuer":"","oidc-scopes":["openid","email","profile","groups"],"oidc-skip-verification":false,"port":8080,"protocol":"https","smtp-from":"someone@example.org","smtp-host":"verycoolemailhost.mail","smtp-password":"smtp-password","smtp-port":8888,"smtp-username":"smtp-username","software-version":"","statuses-cw-max-chars":100,"statuses-max-chars":5000,"statuses-media-max-files":6,"statuses-poll-max-options":6,"statuses-poll-option-max-chars":50,"storage-backend":"local","storage-base-path":"/gotosocial/storage","storage-serve-base-path":"/fileserver","storage-serve-host":"localhost","storage-serve-protocol":"https","syslog-address":"localhost:514","syslog-enabled":false,"syslog-protocol":"udp","trusted-proxies":["127.0.0.1/32","0.0.0.0/0"],"web-asset-base-dir":"./web/assets/","web-template-base-dir":"./web/template/"}'
TEST_8="$(GTS_ACCOUNT_DOMAIN='peepee' go run ./cmd/gotosocial/... --config-path ./test/test.yaml debug config)"
if [ "${TEST_8}" != "${TEST_8_EXPECTED}" ]; then
echo "TEST_8 not equal TEST_8_EXPECTED"
@@ -85,7 +85,7 @@ else
fi
echo "TEST_9 Test loading a config file and overriding one of the variables with both an env var and a cli flag. The cli flag should have priority."
-TEST_9_EXPECTED='{"account-domain":"","accounts-approval-required":true,"accounts-reason-required":true,"accounts-registration-open":true,"application-name":"gotosocial","bind-address":"0.0.0.0","config-path":"./test/test.yaml","db-address":"127.0.0.1","db-database":"postgres","db-password":"postgres","db-port":5432,"db-tls-ca-cert":"","db-tls-mode":"disable","db-type":"postgres","db-user":"postgres","help":false,"host":"gts.example.org","letsencrypt-cert-dir":"/gotosocial/storage/certs","letsencrypt-email-address":"","letsencrypt-enabled":true,"letsencrypt-port":80,"log-level":"info","media-description-max-chars":500,"media-description-min-chars":0,"media-image-max-size":2097152,"media-video-max-size":10485760,"oidc-client-id":"","oidc-client-secret":"","oidc-enabled":false,"oidc-idp-name":"","oidc-issuer":"","oidc-scopes":["openid","email","profile","groups"],"oidc-skip-verification":false,"port":8080,"protocol":"https","smtp-from":"someone@example.org","smtp-host":"verycoolemailhost.mail","smtp-password":"smtp-password","smtp-port":8888,"smtp-username":"smtp-username","software-version":"","statuses-cw-max-chars":100,"statuses-max-chars":5000,"statuses-media-max-files":6,"statuses-poll-max-options":6,"statuses-poll-option-max-chars":50,"storage-backend":"local","storage-base-path":"/gotosocial/storage","storage-serve-base-path":"/fileserver","storage-serve-host":"localhost","storage-serve-protocol":"https","trusted-proxies":["127.0.0.1/32","0.0.0.0/0"],"web-asset-base-dir":"./web/assets/","web-template-base-dir":"./web/template/"}'
+TEST_9_EXPECTED='{"account-domain":"","accounts-approval-required":true,"accounts-reason-required":true,"accounts-registration-open":true,"application-name":"gotosocial","bind-address":"0.0.0.0","config-path":"./test/test.yaml","db-address":"127.0.0.1","db-database":"postgres","db-password":"postgres","db-port":5432,"db-tls-ca-cert":"","db-tls-mode":"disable","db-type":"postgres","db-user":"postgres","help":false,"host":"gts.example.org","letsencrypt-cert-dir":"/gotosocial/storage/certs","letsencrypt-email-address":"","letsencrypt-enabled":true,"letsencrypt-port":80,"log-level":"info","media-description-max-chars":500,"media-description-min-chars":0,"media-image-max-size":2097152,"media-video-max-size":10485760,"oidc-client-id":"","oidc-client-secret":"","oidc-enabled":false,"oidc-idp-name":"","oidc-issuer":"","oidc-scopes":["openid","email","profile","groups"],"oidc-skip-verification":false,"port":8080,"protocol":"https","smtp-from":"someone@example.org","smtp-host":"verycoolemailhost.mail","smtp-password":"smtp-password","smtp-port":8888,"smtp-username":"smtp-username","software-version":"","statuses-cw-max-chars":100,"statuses-max-chars":5000,"statuses-media-max-files":6,"statuses-poll-max-options":6,"statuses-poll-option-max-chars":50,"storage-backend":"local","storage-base-path":"/gotosocial/storage","storage-serve-base-path":"/fileserver","storage-serve-host":"localhost","storage-serve-protocol":"https","syslog-address":"localhost:514","syslog-enabled":false,"syslog-protocol":"udp","trusted-proxies":["127.0.0.1/32","0.0.0.0/0"],"web-asset-base-dir":"./web/assets/","web-template-base-dir":"./web/template/"}'
TEST_9="$(GTS_ACCOUNT_DOMAIN='peepee' go run ./cmd/gotosocial/... --config-path ./test/test.yaml --account-domain '' debug config)"
if [ "${TEST_9}" != "${TEST_9_EXPECTED}" ]; then
echo "TEST_9 not equal TEST_9_EXPECTED"
@@ -95,7 +95,7 @@ else
fi
echo "TEST_10 Test loading a config file from json."
-TEST_10_EXPECTED='{"account-domain":"example.org","accounts-approval-required":true,"accounts-reason-required":true,"accounts-registration-open":true,"application-name":"gotosocial","bind-address":"0.0.0.0","config-path":"./test/test.json","db-address":"127.0.0.1","db-database":"postgres","db-password":"postgres","db-port":5432,"db-tls-ca-cert":"","db-tls-mode":"disable","db-type":"postgres","db-user":"postgres","help":false,"host":"gts.example.org","letsencrypt-cert-dir":"/gotosocial/storage/certs","letsencrypt-email-address":"","letsencrypt-enabled":true,"letsencrypt-port":80,"log-level":"info","media-description-max-chars":500,"media-description-min-chars":0,"media-image-max-size":2097152,"media-video-max-size":10485760,"oidc-client-id":"","oidc-client-secret":"","oidc-enabled":false,"oidc-idp-name":"","oidc-issuer":"","oidc-scopes":["openid","email","profile","groups"],"oidc-skip-verification":false,"port":8080,"protocol":"https","smtp-from":"someone@example.org","smtp-host":"verycoolemailhost.mail","smtp-password":"smtp-password","smtp-port":8888,"smtp-username":"smtp-username","software-version":"","statuses-cw-max-chars":100,"statuses-max-chars":5000,"statuses-media-max-files":6,"statuses-poll-max-options":6,"statuses-poll-option-max-chars":50,"storage-backend":"local","storage-base-path":"/gotosocial/storage","storage-serve-base-path":"/fileserver","storage-serve-host":"localhost","storage-serve-protocol":"https","trusted-proxies":["127.0.0.1/32","0.0.0.0/0"],"web-asset-base-dir":"./web/assets/","web-template-base-dir":"./web/template/"}'
+TEST_10_EXPECTED='{"account-domain":"example.org","accounts-approval-required":true,"accounts-reason-required":true,"accounts-registration-open":true,"application-name":"gotosocial","bind-address":"0.0.0.0","config-path":"./test/test.json","db-address":"127.0.0.1","db-database":"postgres","db-password":"postgres","db-port":5432,"db-tls-ca-cert":"","db-tls-mode":"disable","db-type":"postgres","db-user":"postgres","help":false,"host":"gts.example.org","letsencrypt-cert-dir":"/gotosocial/storage/certs","letsencrypt-email-address":"","letsencrypt-enabled":true,"letsencrypt-port":80,"log-level":"info","media-description-max-chars":500,"media-description-min-chars":0,"media-image-max-size":2097152,"media-video-max-size":10485760,"oidc-client-id":"","oidc-client-secret":"","oidc-enabled":false,"oidc-idp-name":"","oidc-issuer":"","oidc-scopes":["openid","email","profile","groups"],"oidc-skip-verification":false,"port":8080,"protocol":"https","smtp-from":"someone@example.org","smtp-host":"verycoolemailhost.mail","smtp-password":"smtp-password","smtp-port":8888,"smtp-username":"smtp-username","software-version":"","statuses-cw-max-chars":100,"statuses-max-chars":5000,"statuses-media-max-files":6,"statuses-poll-max-options":6,"statuses-poll-option-max-chars":50,"storage-backend":"local","storage-base-path":"/gotosocial/storage","storage-serve-base-path":"/fileserver","storage-serve-host":"localhost","storage-serve-protocol":"https","syslog-address":"localhost:514","syslog-enabled":false,"syslog-protocol":"udp","trusted-proxies":["127.0.0.1/32","0.0.0.0/0"],"web-asset-base-dir":"./web/assets/","web-template-base-dir":"./web/template/"}'
TEST_10="$(go run ./cmd/gotosocial/... --config-path ./test/test.json debug config)"
if [ "${TEST_10}" != "${TEST_10_EXPECTED}" ]; then
echo "TEST_10 not equal TEST_10_EXPECTED"
@@ -105,7 +105,7 @@ else
fi
echo "TEST_11 Test loading a partial config file. Default values should be used apart from those set in the config file."
-TEST_11_EXPECTED='{"account-domain":"peepee.poopoo","accounts-approval-required":true,"accounts-reason-required":true,"accounts-registration-open":true,"application-name":"gotosocial","bind-address":"0.0.0.0","config-path":"./test/test2.yaml","db-address":"localhost","db-database":"postgres","db-password":"postgres","db-port":5432,"db-tls-ca-cert":"","db-tls-mode":"disable","db-type":"postgres","db-user":"postgres","help":false,"host":"","letsencrypt-cert-dir":"/gotosocial/storage/certs","letsencrypt-email-address":"","letsencrypt-enabled":true,"letsencrypt-port":80,"log-level":"trace","media-description-max-chars":500,"media-description-min-chars":0,"media-image-max-size":2097152,"media-video-max-size":10485760,"oidc-client-id":"","oidc-client-secret":"","oidc-enabled":false,"oidc-idp-name":"","oidc-issuer":"","oidc-scopes":["openid","profile","email","groups"],"oidc-skip-verification":false,"port":8080,"protocol":"https","smtp-from":"GoToSocial","smtp-host":"","smtp-password":"","smtp-port":0,"smtp-username":"","software-version":"","statuses-cw-max-chars":100,"statuses-max-chars":5000,"statuses-media-max-files":6,"statuses-poll-max-options":6,"statuses-poll-option-max-chars":50,"storage-backend":"local","storage-base-path":"/gotosocial/storage","storage-serve-base-path":"/fileserver","storage-serve-host":"localhost","storage-serve-protocol":"https","trusted-proxies":["127.0.0.1/32"],"web-asset-base-dir":"./web/assets/","web-template-base-dir":"./web/template/"}'
+TEST_11_EXPECTED='{"account-domain":"peepee.poopoo","accounts-approval-required":true,"accounts-reason-required":true,"accounts-registration-open":true,"application-name":"gotosocial","bind-address":"0.0.0.0","config-path":"./test/test2.yaml","db-address":"localhost","db-database":"postgres","db-password":"postgres","db-port":5432,"db-tls-ca-cert":"","db-tls-mode":"disable","db-type":"postgres","db-user":"postgres","help":false,"host":"","letsencrypt-cert-dir":"/gotosocial/storage/certs","letsencrypt-email-address":"","letsencrypt-enabled":true,"letsencrypt-port":80,"log-level":"trace","media-description-max-chars":500,"media-description-min-chars":0,"media-image-max-size":2097152,"media-video-max-size":10485760,"oidc-client-id":"","oidc-client-secret":"","oidc-enabled":false,"oidc-idp-name":"","oidc-issuer":"","oidc-scopes":["openid","profile","email","groups"],"oidc-skip-verification":false,"port":8080,"protocol":"https","smtp-from":"GoToSocial","smtp-host":"","smtp-password":"","smtp-port":0,"smtp-username":"","software-version":"","statuses-cw-max-chars":100,"statuses-max-chars":5000,"statuses-media-max-files":6,"statuses-poll-max-options":6,"statuses-poll-option-max-chars":50,"storage-backend":"local","storage-base-path":"/gotosocial/storage","storage-serve-base-path":"/fileserver","storage-serve-host":"localhost","storage-serve-protocol":"https","syslog-address":"localhost:514","syslog-enabled":false,"syslog-protocol":"udp","trusted-proxies":["127.0.0.1/32"],"web-asset-base-dir":"./web/assets/","web-template-base-dir":"./web/template/"}'
TEST_11="$(go run ./cmd/gotosocial/... --config-path ./test/test2.yaml debug config)"
if [ "${TEST_11}" != "${TEST_11_EXPECTED}" ]; then
echo "TEST_11 not equal TEST_11_EXPECTED"
diff --git a/test/test.yaml b/test/test.yaml
index 467ab920f..96e53a747 100644
--- a/test/test.yaml
+++ b/test/test.yaml
@@ -397,3 +397,28 @@ smtp-password: "smtp-password"
# Examples: ["mail@example.org"]
# Default: ""
smtp-from: "someone@example.org"
+
+#########################
+##### SYSLOG CONFIG #####
+#########################
+
+# Config for additional syslog log hooks. See https://en.wikipedia.org/wiki/Syslog,
+# and https://github.com/sirupsen/logrus/tree/master/hooks/syslog.
+#
+# These settings are useful when one wants to daemonize GoToSocial and send logs
+# to a specific place, either a local location or a syslog server. Most users will
+# not need to touch these settings.
+
+# Bool. Enable the syslog logging hook. Logs will be mirrored to the configured destination.
+# Options: [true, false]
+# Default: false
+syslog-enabled: false
+
+# String. Protocol to use when directing logs to syslog. Leave empty to connect to local syslog.
+# Options: ["udp", "tcp", ""]
+# Default: "tcp"
+syslog-protocol: "udp"
+
+# String. Address:port to send syslog logs to. Leave empty to connect to local syslog.
+# Default: "localhost:514"
+syslog-address: "localhost:514"
diff --git a/testrig/config.go b/testrig/config.go
index be5efab61..3857ddb34 100644
--- a/testrig/config.go
+++ b/testrig/config.go
@@ -117,4 +117,8 @@ func InitTestConfig() {
SMTPUsername: "",
SMTPPassword: "",
SMTPFrom: "GoToSocial",
+
+ SyslogEnabled: false,
+ SyslogProtocol: "udp",
+ SyslogAddress: "localhost:514",
}
diff --git a/testrig/log.go b/testrig/log.go
index 5db12e6e9..b0b3b9da1 100644
--- a/testrig/log.go
+++ b/testrig/log.go
@@ -19,14 +19,37 @@
package testrig
import (
- "github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/log"
+ "gopkg.in/mcuadros/go-syslog.v2"
+ "gopkg.in/mcuadros/go-syslog.v2/format"
)
// InitTestLog sets the global logger to trace level for logging
func InitTestLog() {
- err := log.Initialize(logrus.TraceLevel.String())
- if err != nil {
+ if err := log.Initialize(); err != nil {
panic(err)
}
}
+
+// InitTestSyslog returns a test syslog running on port 42069 and a channel for reading
+// messages sent to the server, or an error if something goes wrong.
+//
+// Callers of this function should call Kill() on the server when they're finished with it!
+func InitTestSyslog() (*syslog.Server, chan format.LogParts, error) {
+ channel := make(syslog.LogPartsChannel)
+ handler := syslog.NewChannelHandler(channel)
+
+ server := syslog.NewServer()
+ server.SetFormat(syslog.Automatic)
+ server.SetHandler(handler)
+
+ if err := server.ListenUDP("localhost:42069"); err != nil {
+ return nil, nil, err
+ }
+
+ if err := server.Boot(); err != nil {
+ return nil, nil, err
+ }
+
+ return server, channel, nil
+}
diff --git a/vendor/github.com/sirupsen/logrus/hooks/syslog/README.md b/vendor/github.com/sirupsen/logrus/hooks/syslog/README.md
new file mode 100644
index 000000000..1bbc0f72d
--- /dev/null
+++ b/vendor/github.com/sirupsen/logrus/hooks/syslog/README.md
@@ -0,0 +1,39 @@
+# Syslog Hooks for Logrus
+
+## Usage
+
+```go
+import (
+ "log/syslog"
+ "github.com/sirupsen/logrus"
+ lSyslog "github.com/sirupsen/logrus/hooks/syslog"
+)
+
+func main() {
+ log := logrus.New()
+ hook, err := lSyslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
+
+ if err == nil {
+ log.Hooks.Add(hook)
+ }
+}
+```
+
+If you want to connect to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). Just assign empty string to the first two parameters of `NewSyslogHook`. It should look like the following.
+
+```go
+import (
+ "log/syslog"
+ "github.com/sirupsen/logrus"
+ lSyslog "github.com/sirupsen/logrus/hooks/syslog"
+)
+
+func main() {
+ log := logrus.New()
+ hook, err := lSyslog.NewSyslogHook("", "", syslog.LOG_INFO, "")
+
+ if err == nil {
+ log.Hooks.Add(hook)
+ }
+}
+```
diff --git a/vendor/github.com/sirupsen/logrus/hooks/syslog/syslog.go b/vendor/github.com/sirupsen/logrus/hooks/syslog/syslog.go
new file mode 100644
index 000000000..02b8df380
--- /dev/null
+++ b/vendor/github.com/sirupsen/logrus/hooks/syslog/syslog.go
@@ -0,0 +1,55 @@
+// +build !windows,!nacl,!plan9
+
+package syslog
+
+import (
+ "fmt"
+ "log/syslog"
+ "os"
+
+ "github.com/sirupsen/logrus"
+)
+
+// SyslogHook to send logs via syslog.
+type SyslogHook struct {
+ Writer *syslog.Writer
+ SyslogNetwork string
+ SyslogRaddr string
+}
+
+// Creates a hook to be added to an instance of logger. This is called with
+// `hook, err := NewSyslogHook("udp", "localhost:514", syslog.LOG_DEBUG, "")`
+// `if err == nil { log.Hooks.Add(hook) }`
+func NewSyslogHook(network, raddr string, priority syslog.Priority, tag string) (*SyslogHook, error) {
+ w, err := syslog.Dial(network, raddr, priority, tag)
+ return &SyslogHook{w, network, raddr}, err
+}
+
+func (hook *SyslogHook) Fire(entry *logrus.Entry) error {
+ line, err := entry.String()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Unable to read entry, %v", err)
+ return err
+ }
+
+ switch entry.Level {
+ case logrus.PanicLevel:
+ return hook.Writer.Crit(line)
+ case logrus.FatalLevel:
+ return hook.Writer.Crit(line)
+ case logrus.ErrorLevel:
+ return hook.Writer.Err(line)
+ case logrus.WarnLevel:
+ return hook.Writer.Warning(line)
+ case logrus.InfoLevel:
+ return hook.Writer.Info(line)
+ case logrus.DebugLevel, logrus.TraceLevel:
+ return hook.Writer.Debug(line)
+ default:
+ return nil
+ }
+}
+
+func (hook *SyslogHook) Levels() []logrus.Level {
+ return logrus.AllLevels
+}
diff --git a/vendor/gopkg.in/mcuadros/go-syslog.v2/.travis.yml b/vendor/gopkg.in/mcuadros/go-syslog.v2/.travis.yml
new file mode 100644
index 000000000..4340e7079
--- /dev/null
+++ b/vendor/gopkg.in/mcuadros/go-syslog.v2/.travis.yml
@@ -0,0 +1,21 @@
+language: go
+go:
+ - 1.4
+ - 1.5
+ - 1.6
+ - 1.7
+ - 1.8
+ - 1.9
+ - "1.10"
+ - "1.11"
+ - "1.12"
+ - tip
+
+matrix:
+ allow_failures:
+ - go: tip
+
+go_import_path: gopkg.in/mcuadros/go-syslog.v2
+
+install:
+ - go get -v -t ./...
diff --git a/vendor/gopkg.in/mcuadros/go-syslog.v2/LICENSE b/vendor/gopkg.in/mcuadros/go-syslog.v2/LICENSE
new file mode 100644
index 000000000..b31548967
--- /dev/null
+++ b/vendor/gopkg.in/mcuadros/go-syslog.v2/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2013 Máximo Cuadros
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/gopkg.in/mcuadros/go-syslog.v2/README.md b/vendor/gopkg.in/mcuadros/go-syslog.v2/README.md
new file mode 100644
index 000000000..e17a0fae2
--- /dev/null
+++ b/vendor/gopkg.in/mcuadros/go-syslog.v2/README.md
@@ -0,0 +1,48 @@
+go-syslog [![Build Status](https://travis-ci.org/mcuadros/go-syslog.svg?branch=master)](https://travis-ci.org/mcuadros/go-syslog) [![GoDoc](http://godoc.org/github.com/mcuadros/go-syslog?status.svg)](hhttps://godoc.org/gopkg.in/mcuadros/go-syslog.v2) [![GitHub release](https://img.shields.io/github/release/mcuadros/go-syslog.svg)](https://github.com/mcuadros/go-syslog/releases)
+==============================
+
+Syslog server library for go, build easy your custom syslog server over UDP, TCP or Unix sockets using RFC3164, RFC6587 or RFC5424
+
+Installation
+------------
+
+The recommended way to install go-syslog
+
+```
+go get gopkg.in/mcuadros/go-syslog.v2
+```
+
+Examples
+--------
+
+How import the package
+
+```go
+import "gopkg.in/mcuadros/go-syslog.v2"
+```
+
+Example of a basic syslog [UDP server](example/basic_udp.go):
+
+```go
+channel := make(syslog.LogPartsChannel)
+handler := syslog.NewChannelHandler(channel)
+
+server := syslog.NewServer()
+server.SetFormat(syslog.RFC5424)
+server.SetHandler(handler)
+server.ListenUDP("0.0.0.0:514")
+server.Boot()
+
+go func(channel syslog.LogPartsChannel) {
+ for logParts := range channel {
+ fmt.Println(logParts)
+ }
+}(channel)
+
+server.Wait()
+```
+
+License
+-------
+
+MIT, see [LICENSE](LICENSE)
diff --git a/vendor/gopkg.in/mcuadros/go-syslog.v2/doc.go b/vendor/gopkg.in/mcuadros/go-syslog.v2/doc.go
new file mode 100644
index 000000000..9ab6466a6
--- /dev/null
+++ b/vendor/gopkg.in/mcuadros/go-syslog.v2/doc.go
@@ -0,0 +1,5 @@
+/*
+Syslog server library for go, build easy your custom syslog server
+over UDP, TCP or Unix sockets using RFC3164, RFC5424 and RFC6587
+*/
+package syslog // import "gopkg.in/mcuadros/go-syslog.v2"
diff --git a/vendor/gopkg.in/mcuadros/go-syslog.v2/format/automatic.go b/vendor/gopkg.in/mcuadros/go-syslog.v2/format/automatic.go
new file mode 100644
index 000000000..2400db866
--- /dev/null
+++ b/vendor/gopkg.in/mcuadros/go-syslog.v2/format/automatic.go
@@ -0,0 +1,104 @@
+package format
+
+import (
+ "bufio"
+ "bytes"
+ "strconv"
+
+ "gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc3164"
+ "gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc5424"
+)
+
+/* Selecting an 'Automatic' format detects incoming format (i.e. RFC3164 vs RFC5424) and Framing
+ * (i.e. RFC6587 s3.4.1 octet counting as described here as RFC6587, and either no framing or
+ * RFC6587 s3.4.2 octet stuffing / non-transparent framing, described here as either RFC3164
+ * or RFC6587).
+ *
+ * In essence if you don't know which format to select, or have multiple incoming formats, this
+ * is the one to go for. There is a theoretical performance penalty (it has to look at a few bytes
+ * at the start of the frame), and a risk that you may parse things you don't want to parse
+ * (rogue syslog clients using other formats), so if you can be absolutely sure of your syslog
+ * format, it would be best to select it explicitly.
+ */
+
+type Automatic struct{}
+
+const (
+ detectedUnknown = iota
+ detectedRFC3164 = iota
+ detectedRFC5424 = iota
+ detectedRFC6587 = iota
+)
+
+/*
+ * Will always fallback to rfc3164 (see section 4.3.3)
+ */
+func detect(data []byte) int {
+ // all formats have a sapce somewhere
+ if i := bytes.IndexByte(data, ' '); i > 0 {
+ pLength := data[0:i]
+ if _, err := strconv.Atoi(string(pLength)); err == nil {
+ return detectedRFC6587
+ }
+ // are we starting with <
+ if data[0] != '<' {
+ return detectedRFC3164
+ }
+ // is there a close angle bracket before the ' '? there should be
+ angle := bytes.IndexByte(data, '>')
+ if (angle < 0) || (angle >= i) {
+ return detectedRFC3164
+ }
+
+ // if a single digit immediately follows the angle bracket, then a space
+ // it is RFC5424, as RFC3164 must begin with a letter (month name)
+ if (angle+2 == i) && (data[angle+1] >= '0') && (data[angle+1] <= '9') {
+ return detectedRFC5424
+ } else {
+ return detectedRFC3164
+ }
+ }
+ // fallback to rfc 3164 section 4.3.3
+ return detectedRFC3164
+}
+
+func (f *Automatic) GetParser(line []byte) LogParser {
+ switch format := detect(line); format {
+ case detectedRFC3164:
+ return &parserWrapper{rfc3164.NewParser(line)}
+ case detectedRFC5424:
+ return &parserWrapper{rfc5424.NewParser(line)}
+ default:
+ // If the line was an RFC6587 line, the splitter should already have removed the length,
+ // so one of the above two will be chosen if the line is correctly formed. However, it
+ // may have a second length illegally placed at the start, in which case the detector
+ // will return detectedRFC6587. The line may also simply be malformed after the length in
+ // which case we will have detectedUnknown. In this case we return the simplest parser so
+ // the illegally formatted line is properly handled
+ return &parserWrapper{rfc3164.NewParser(line)}
+ }
+}
+
+func (f *Automatic) GetSplitFunc() bufio.SplitFunc {
+ return f.automaticScannerSplit
+}
+
+func (f *Automatic) automaticScannerSplit(data []byte, atEOF bool) (advance int, token []byte, err error) {
+ if atEOF && len(data) == 0 {
+ return 0, nil, nil
+ }
+
+ switch format := detect(data); format {
+ case detectedRFC6587:
+ return rfc6587ScannerSplit(data, atEOF)
+ case detectedRFC3164, detectedRFC5424:
+ // the default
+ return bufio.ScanLines(data, atEOF)
+ default:
+ if err != nil {
+ return 0, nil, err
+ }
+ // Request more data
+ return 0, nil, nil
+ }
+}
diff --git a/vendor/gopkg.in/mcuadros/go-syslog.v2/format/format.go b/vendor/gopkg.in/mcuadros/go-syslog.v2/format/format.go
new file mode 100644
index 000000000..f77eb107c
--- /dev/null
+++ b/vendor/gopkg.in/mcuadros/go-syslog.v2/format/format.go
@@ -0,0 +1,29 @@
+package format
+
+import (
+ "bufio"
+ "time"
+
+ "gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser"
+)
+
+type LogParts map[string]interface{}
+
+type LogParser interface {
+ Parse() error
+ Dump() LogParts
+ Location(*time.Location)
+}
+
+type Format interface {
+ GetParser([]byte) LogParser
+ GetSplitFunc() bufio.SplitFunc
+}
+
+type parserWrapper struct {
+ syslogparser.LogParser
+}
+
+func (w *parserWrapper) Dump() LogParts {
+ return LogParts(w.LogParser.Dump())
+}
diff --git a/vendor/gopkg.in/mcuadros/go-syslog.v2/format/rfc3164.go b/vendor/gopkg.in/mcuadros/go-syslog.v2/format/rfc3164.go
new file mode 100644
index 000000000..9ca80f7a7
--- /dev/null
+++ b/vendor/gopkg.in/mcuadros/go-syslog.v2/format/rfc3164.go
@@ -0,0 +1,17 @@
+package format
+
+import (
+ "bufio"
+
+ "gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc3164"
+)
+
+type RFC3164 struct{}
+
+func (f *RFC3164) GetParser(line []byte) LogParser {
+ return &parserWrapper{rfc3164.NewParser(line)}
+}
+
+func (f *RFC3164) GetSplitFunc() bufio.SplitFunc {
+ return nil
+}
diff --git a/vendor/gopkg.in/mcuadros/go-syslog.v2/format/rfc5424.go b/vendor/gopkg.in/mcuadros/go-syslog.v2/format/rfc5424.go
new file mode 100644
index 000000000..bb37c7ced
--- /dev/null
+++ b/vendor/gopkg.in/mcuadros/go-syslog.v2/format/rfc5424.go
@@ -0,0 +1,17 @@
+package format
+
+import (
+ "bufio"
+
+ "gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc5424"
+)
+
+type RFC5424 struct{}
+
+func (f *RFC5424) GetParser(line []byte) LogParser {
+ return &parserWrapper{rfc5424.NewParser(line)}
+}
+
+func (f *RFC5424) GetSplitFunc() bufio.SplitFunc {
+ return nil
+}
diff --git a/vendor/gopkg.in/mcuadros/go-syslog.v2/format/rfc6587.go b/vendor/gopkg.in/mcuadros/go-syslog.v2/format/rfc6587.go
new file mode 100644
index 000000000..e0eef9174
--- /dev/null
+++ b/vendor/gopkg.in/mcuadros/go-syslog.v2/format/rfc6587.go
@@ -0,0 +1,45 @@
+package format
+
+import (
+ "bufio"
+ "bytes"
+ "strconv"
+
+ "gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc5424"
+)
+
+type RFC6587 struct{}
+
+func (f *RFC6587) GetParser(line []byte) LogParser {
+ return &parserWrapper{rfc5424.NewParser(line)}
+}
+
+func (f *RFC6587) GetSplitFunc() bufio.SplitFunc {
+ return rfc6587ScannerSplit
+}
+
+func rfc6587ScannerSplit(data []byte, atEOF bool) (advance int, token []byte, err error) {
+ if atEOF && len(data) == 0 {
+ return 0, nil, nil
+ }
+
+ if i := bytes.IndexByte(data, ' '); i > 0 {
+ pLength := data[0:i]
+ length, err := strconv.Atoi(string(pLength))
+ if err != nil {
+ if string(data[0:1]) == "<" {
+ // Assume this frame uses non-transparent-framing
+ return len(data), data, nil
+ }
+ return 0, nil, err
+ }
+ end := length + i + 1
+ if len(data) >= end {
+ // Return the frame with the length removed
+ return end, data[i+1 : end], nil
+ }
+ }
+
+ // Request more data
+ return 0, nil, nil
+}
diff --git a/vendor/gopkg.in/mcuadros/go-syslog.v2/handler.go b/vendor/gopkg.in/mcuadros/go-syslog.v2/handler.go
new file mode 100644
index 000000000..9914b3ea1
--- /dev/null
+++ b/vendor/gopkg.in/mcuadros/go-syslog.v2/handler.go
@@ -0,0 +1,35 @@
+package syslog
+
+import (
+ "gopkg.in/mcuadros/go-syslog.v2/format"
+)
+
+//The handler receive every syslog entry at Handle method
+type Handler interface {
+ Handle(format.LogParts, int64, error)
+}
+
+type LogPartsChannel chan format.LogParts
+
+//The ChannelHandler will send all the syslog entries into the given channel
+type ChannelHandler struct {
+ channel LogPartsChannel
+}
+
+//NewChannelHandler returns a new ChannelHandler
+func NewChannelHandler(channel LogPartsChannel) *ChannelHandler {
+ handler := new(ChannelHandler)
+ handler.SetChannel(channel)
+
+ return handler
+}
+
+//The channel to be used
+func (h *ChannelHandler) SetChannel(channel LogPartsChannel) {
+ h.channel = channel
+}
+
+//Syslog entry receiver
+func (h *ChannelHandler) Handle(logParts format.LogParts, messageLength int64, err error) {
+ h.channel <- logParts
+}
diff --git a/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/LICENSE b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/LICENSE
new file mode 100644
index 000000000..3e2dbb6f8
--- /dev/null
+++ b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/LICENSE
@@ -0,0 +1,23 @@
+Copyright (c) 2013, Jérôme Renard
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice, this
+ list of conditions and the following disclaimer in the documentation and/or
+ other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/README.md b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/README.md
new file mode 100644
index 000000000..ce59d376f
--- /dev/null
+++ b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/README.md
@@ -0,0 +1,4 @@
+Syslogparser
+============
+
+This is a fork for [github.com/jeromer/syslogparser](https://github.com/jeromer/syslogparser), since this library is intensively used by `go-syslog`, now is integrated as a `internal` package.
diff --git a/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc3164/rfc3164.go b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc3164/rfc3164.go
new file mode 100644
index 000000000..486a0cb54
--- /dev/null
+++ b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc3164/rfc3164.go
@@ -0,0 +1,292 @@
+package rfc3164
+
+import (
+ "bytes"
+ "os"
+ "time"
+
+ "gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser"
+)
+
+type Parser struct {
+ buff []byte
+ cursor int
+ l int
+ priority syslogparser.Priority
+ version int
+ header header
+ message rfc3164message
+ location *time.Location
+ skipTag bool
+}
+
+type header struct {
+ timestamp time.Time
+ hostname string
+}
+
+type rfc3164message struct {
+ tag string
+ content string
+}
+
+func NewParser(buff []byte) *Parser {
+ return &Parser{
+ buff: buff,
+ cursor: 0,
+ l: len(buff),
+ location: time.UTC,
+ }
+}
+
+func (p *Parser) Location(location *time.Location) {
+ p.location = location
+}
+
+func (p *Parser) Parse() error {
+ tcursor := p.cursor
+ pri, err := p.parsePriority()
+ if err != nil {
+ // RFC3164 sec 4.3.3
+ p.priority = syslogparser.Priority{13, syslogparser.Facility{Value: 1}, syslogparser.Severity{Value: 5}}
+ p.cursor = tcursor
+ content, err := p.parseContent()
+ p.header.timestamp = time.Now().Round(time.Second)
+ if err != syslogparser.ErrEOL {
+ return err
+ }
+ p.message = rfc3164message{content: content}
+ return nil
+ }
+
+ tcursor = p.cursor
+ hdr, err := p.parseHeader()
+ if err == syslogparser.ErrTimestampUnknownFormat {
+ // RFC3164 sec 4.3.2.
+ hdr.timestamp = time.Now().Round(time.Second)
+ // No tag processing should be done
+ p.skipTag = true
+ // Reset cursor for content read
+ p.cursor = tcursor
+ } else if err != nil {
+ return err
+ } else {
+ p.cursor++
+ }
+
+ msg, err := p.parsemessage()
+ if err != syslogparser.ErrEOL {
+ return err
+ }
+
+ p.priority = pri
+ p.version = syslogparser.NO_VERSION
+ p.header = hdr
+ p.message = msg
+
+ return nil
+}
+
+func (p *Parser) Dump() syslogparser.LogParts {
+ return syslogparser.LogParts{
+ "timestamp": p.header.timestamp,
+ "hostname": p.header.hostname,
+ "tag": p.message.tag,
+ "content": p.message.content,
+ "priority": p.priority.P,
+ "facility": p.priority.F.Value,
+ "severity": p.priority.S.Value,
+ }
+}
+
+func (p *Parser) parsePriority() (syslogparser.Priority, error) {
+ return syslogparser.ParsePriority(p.buff, &p.cursor, p.l)
+}
+
+func (p *Parser) parseHeader() (header, error) {
+ hdr := header{}
+ var err error
+
+ ts, err := p.parseTimestamp()
+ if err != nil {
+ return hdr, err
+ }
+
+ hostname, err := p.parseHostname()
+ if err != nil {
+ return hdr, err
+ }
+
+ hdr.timestamp = ts
+ hdr.hostname = hostname
+
+ return hdr, nil
+}
+
+func (p *Parser) parsemessage() (rfc3164message, error) {
+ msg := rfc3164message{}
+ var err error
+
+ if !p.skipTag {
+ tag, err := p.parseTag()
+ if err != nil {
+ return msg, err
+ }
+ msg.tag = tag
+ }
+
+ content, err := p.parseContent()
+ if err != syslogparser.ErrEOL {
+ return msg, err
+ }
+
+ msg.content = content
+
+ return msg, err
+}
+
+// https://tools.ietf.org/html/rfc3164#section-4.1.2
+func (p *Parser) parseTimestamp() (time.Time, error) {
+ var ts time.Time
+ var err error
+ var tsFmtLen int
+ var sub []byte
+
+ tsFmts := []string{
+ time.Stamp,
+ time.RFC3339,
+ }
+ // if timestamps starts with numeric try formats with different order
+ // it is more likely that timestamp is in RFC3339 format then
+ if c := p.buff[p.cursor]; c > '0' && c < '9' {
+ tsFmts = []string{
+ time.RFC3339,
+ time.Stamp,
+ }
+ }
+
+ found := false
+ for _, tsFmt := range tsFmts {
+ tsFmtLen = len(tsFmt)
+
+ if p.cursor+tsFmtLen > p.l {
+ continue
+ }
+
+ sub = p.buff[p.cursor : tsFmtLen+p.cursor]
+ ts, err = time.ParseInLocation(tsFmt, string(sub), p.location)
+ if err == nil {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ p.cursor = len(time.Stamp)
+
+ // XXX : If the timestamp is invalid we try to push the cursor one byte
+ // XXX : further, in case it is a space
+ if (p.cursor < p.l) && (p.buff[p.cursor] == ' ') {
+ p.cursor++
+ }
+
+ return ts, syslogparser.ErrTimestampUnknownFormat
+ }
+
+ fixTimestampIfNeeded(&ts)
+
+ p.cursor += tsFmtLen
+
+ if (p.cursor < p.l) && (p.buff[p.cursor] == ' ') {
+ p.cursor++
+ }
+
+ return ts, nil
+}
+
+func (p *Parser) parseHostname() (string, error) {
+ oldcursor := p.cursor
+ hostname, err := syslogparser.ParseHostname(p.buff, &p.cursor, p.l)
+ if err == nil && len(hostname) > 0 && string(hostname[len(hostname)-1]) == ":" { // not an hostname! we found a GNU implementation of syslog()
+ p.cursor = oldcursor - 1
+ myhostname, err := os.Hostname()
+ if err == nil {
+ return myhostname, nil
+ }
+ return "", nil
+ }
+ return hostname, err
+}
+
+// http://tools.ietf.org/html/rfc3164#section-4.1.3
+func (p *Parser) parseTag() (string, error) {
+ var b byte
+ var endOfTag bool
+ var bracketOpen bool
+ var tag []byte
+ var err error
+ var found bool
+
+ from := p.cursor
+
+ for {
+ if p.cursor == p.l {
+ // no tag found, reset cursor for content
+ p.cursor = from
+ return "", nil
+ }
+
+ b = p.buff[p.cursor]
+ bracketOpen = (b == '[')
+ endOfTag = (b == ':' || b == ' ')
+
+ // XXX : parse PID ?
+ if bracketOpen {
+ tag = p.buff[from:p.cursor]
+ found = true
+ }
+
+ if endOfTag {
+ if !found {
+ tag = p.buff[from:p.cursor]
+ found = true
+ }
+
+ p.cursor++
+ break
+ }
+
+ p.cursor++
+ }
+
+ if (p.cursor < p.l) && (p.buff[p.cursor] == ' ') {
+ p.cursor++
+ }
+
+ return string(tag), err
+}
+
+func (p *Parser) parseContent() (string, error) {
+ if p.cursor > p.l {
+ return "", syslogparser.ErrEOL
+ }
+
+ content := bytes.Trim(p.buff[p.cursor:p.l], " ")
+ p.cursor += len(content)
+
+ return string(content), syslogparser.ErrEOL
+}
+
+func fixTimestampIfNeeded(ts *time.Time) {
+ now := time.Now()
+ y := ts.Year()
+
+ if ts.Year() == 0 {
+ y = now.Year()
+ }
+
+ newTs := time.Date(y, ts.Month(), ts.Day(), ts.Hour(), ts.Minute(),
+ ts.Second(), ts.Nanosecond(), ts.Location())
+
+ *ts = newTs
+}
diff --git a/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc5424/rfc5424.go b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc5424/rfc5424.go
new file mode 100644
index 000000000..bea8e5879
--- /dev/null
+++ b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc5424/rfc5424.go
@@ -0,0 +1,606 @@
+// Note to self : never try to code while looking after your kids
+// The result might look like this : https://pbs.twimg.com/media/BXqSuYXIEAAscVA.png
+
+package rfc5424
+
+import (
+ "fmt"
+ "math"
+ "strconv"
+ "time"
+
+ "gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser"
+)
+
+const (
+ NILVALUE = '-'
+)
+
+var (
+ ErrYearInvalid = &syslogparser.ParserError{"Invalid year in timestamp"}
+ ErrMonthInvalid = &syslogparser.ParserError{"Invalid month in timestamp"}
+ ErrDayInvalid = &syslogparser.ParserError{"Invalid day in timestamp"}
+ ErrHourInvalid = &syslogparser.ParserError{"Invalid hour in timestamp"}
+ ErrMinuteInvalid = &syslogparser.ParserError{"Invalid minute in timestamp"}
+ ErrSecondInvalid = &syslogparser.ParserError{"Invalid second in timestamp"}
+ ErrSecFracInvalid = &syslogparser.ParserError{"Invalid fraction of second in timestamp"}
+ ErrTimeZoneInvalid = &syslogparser.ParserError{"Invalid time zone in timestamp"}
+ ErrInvalidTimeFormat = &syslogparser.ParserError{"Invalid time format"}
+ ErrInvalidAppName = &syslogparser.ParserError{"Invalid app name"}
+ ErrInvalidProcId = &syslogparser.ParserError{"Invalid proc ID"}
+ ErrInvalidMsgId = &syslogparser.ParserError{"Invalid msg ID"}
+ ErrNoStructuredData = &syslogparser.ParserError{"No structured data"}
+)
+
+type Parser struct {
+ buff []byte
+ cursor int
+ l int
+ header header
+ structuredData string
+ message string
+}
+
+type header struct {
+ priority syslogparser.Priority
+ version int
+ timestamp time.Time
+ hostname string
+ appName string
+ procId string
+ msgId string
+}
+
+type partialTime struct {
+ hour int
+ minute int
+ seconds int
+ secFrac float64
+}
+
+type fullTime struct {
+ pt partialTime
+ loc *time.Location
+}
+
+type fullDate struct {
+ year int
+ month int
+ day int
+}
+
+func NewParser(buff []byte) *Parser {
+ return &Parser{
+ buff: buff,
+ cursor: 0,
+ l: len(buff),
+ }
+}
+
+func (p *Parser) Location(location *time.Location) {
+ // Ignore as RFC5424 syslog always has a timezone
+}
+
+func (p *Parser) Parse() error {
+ hdr, err := p.parseHeader()
+ if err != nil {
+ return err
+ }
+
+ p.header = hdr
+
+ sd, err := p.parseStructuredData()
+ if err != nil {
+ return err
+ }
+
+ p.structuredData = sd
+ p.cursor++
+
+ if p.cursor < p.l {
+ p.message = string(p.buff[p.cursor:])
+ }
+
+ return nil
+}
+
+func (p *Parser) Dump() syslogparser.LogParts {
+ return syslogparser.LogParts{
+ "priority": p.header.priority.P,
+ "facility": p.header.priority.F.Value,
+ "severity": p.header.priority.S.Value,
+ "version": p.header.version,
+ "timestamp": p.header.timestamp,
+ "hostname": p.header.hostname,
+ "app_name": p.header.appName,
+ "proc_id": p.header.procId,
+ "msg_id": p.header.msgId,
+ "structured_data": p.structuredData,
+ "message": p.message,
+ }
+}
+
+// HEADER = PRI VERSION SP TIMESTAMP SP HOSTNAME SP APP-NAME SP PROCID SP MSGID
+func (p *Parser) parseHeader() (header, error) {
+ hdr := header{}
+
+ pri, err := p.parsePriority()
+ if err != nil {
+ return hdr, err
+ }
+
+ hdr.priority = pri
+
+ ver, err := p.parseVersion()
+ if err != nil {
+ return hdr, err
+ }
+ hdr.version = ver
+ p.cursor++
+
+ ts, err := p.parseTimestamp()
+ if err != nil {
+ return hdr, err
+ }
+
+ hdr.timestamp = ts
+ p.cursor++
+
+ host, err := p.parseHostname()
+ if err != nil {
+ return hdr, err
+ }
+
+ hdr.hostname = host
+ p.cursor++
+
+ appName, err := p.parseAppName()
+ if err != nil {
+ return hdr, err
+ }
+
+ hdr.appName = appName
+ p.cursor++
+
+ procId, err := p.parseProcId()
+ if err != nil {
+ return hdr, nil
+ }
+
+ hdr.procId = procId
+ p.cursor++
+
+ msgId, err := p.parseMsgId()
+ if err != nil {
+ return hdr, nil
+ }
+
+ hdr.msgId = msgId
+ p.cursor++
+
+ return hdr, nil
+}
+
+func (p *Parser) parsePriority() (syslogparser.Priority, error) {
+ return syslogparser.ParsePriority(p.buff, &p.cursor, p.l)
+}
+
+func (p *Parser) parseVersion() (int, error) {
+ return syslogparser.ParseVersion(p.buff, &p.cursor, p.l)
+}
+
+// https://tools.ietf.org/html/rfc5424#section-6.2.3
+func (p *Parser) parseTimestamp() (time.Time, error) {
+ var ts time.Time
+
+ if p.cursor >= p.l {
+ return ts, ErrInvalidTimeFormat
+ }
+
+ if p.buff[p.cursor] == NILVALUE {
+ p.cursor++
+ return ts, nil
+ }
+
+ fd, err := parseFullDate(p.buff, &p.cursor, p.l)
+ if err != nil {
+ return ts, err
+ }
+
+ if p.cursor >= p.l || p.buff[p.cursor] != 'T' {
+ return ts, ErrInvalidTimeFormat
+ }
+
+ p.cursor++
+
+ ft, err := parseFullTime(p.buff, &p.cursor, p.l)
+ if err != nil {
+ return ts, syslogparser.ErrTimestampUnknownFormat
+ }
+
+ nSec, err := toNSec(ft.pt.secFrac)
+ if err != nil {
+ return ts, err
+ }
+
+ ts = time.Date(
+ fd.year,
+ time.Month(fd.month),
+ fd.day,
+ ft.pt.hour,
+ ft.pt.minute,
+ ft.pt.seconds,
+ nSec,
+ ft.loc,
+ )
+
+ return ts, nil
+}
+
+// HOSTNAME = NILVALUE / 1*255PRINTUSASCII
+func (p *Parser) parseHostname() (string, error) {
+ return syslogparser.ParseHostname(p.buff, &p.cursor, p.l)
+}
+
+// APP-NAME = NILVALUE / 1*48PRINTUSASCII
+func (p *Parser) parseAppName() (string, error) {
+ return parseUpToLen(p.buff, &p.cursor, p.l, 48, ErrInvalidAppName)
+}
+
+// PROCID = NILVALUE / 1*128PRINTUSASCII
+func (p *Parser) parseProcId() (string, error) {
+ return parseUpToLen(p.buff, &p.cursor, p.l, 128, ErrInvalidProcId)
+}
+
+// MSGID = NILVALUE / 1*32PRINTUSASCII
+func (p *Parser) parseMsgId() (string, error) {
+ return parseUpToLen(p.buff, &p.cursor, p.l, 32, ErrInvalidMsgId)
+}
+
+func (p *Parser) parseStructuredData() (string, error) {
+ return parseStructuredData(p.buff, &p.cursor, p.l)
+}
+
+// ----------------------------------------------
+// https://tools.ietf.org/html/rfc5424#section-6
+// ----------------------------------------------
+
+// XXX : bind them to Parser ?
+
+// FULL-DATE : DATE-FULLYEAR "-" DATE-MONTH "-" DATE-MDAY
+func parseFullDate(buff []byte, cursor *int, l int) (fullDate, error) {
+ var fd fullDate
+
+ year, err := parseYear(buff, cursor, l)
+ if err != nil {
+ return fd, err
+ }
+
+ if *cursor >= l || buff[*cursor] != '-' {
+ return fd, syslogparser.ErrTimestampUnknownFormat
+ }
+
+ *cursor++
+
+ month, err := parseMonth(buff, cursor, l)
+ if err != nil {
+ return fd, err
+ }
+
+ if *cursor >= l || buff[*cursor] != '-' {
+ return fd, syslogparser.ErrTimestampUnknownFormat
+ }
+
+ *cursor++
+
+ day, err := parseDay(buff, cursor, l)
+ if err != nil {
+ return fd, err
+ }
+
+ fd = fullDate{
+ year: year,
+ month: month,
+ day: day,
+ }
+
+ return fd, nil
+}
+
+// DATE-FULLYEAR = 4DIGIT
+func parseYear(buff []byte, cursor *int, l int) (int, error) {
+ yearLen := 4
+
+ if *cursor+yearLen > l {
+ return 0, syslogparser.ErrEOL
+ }
+
+ // XXX : we do not check for a valid year (ie. 1999, 2013 etc)
+ // XXX : we only checks the format is correct
+ sub := string(buff[*cursor : *cursor+yearLen])
+
+ *cursor += yearLen
+
+ year, err := strconv.Atoi(sub)
+ if err != nil {
+ return 0, ErrYearInvalid
+ }
+
+ return year, nil
+}
+
+// DATE-MONTH = 2DIGIT ; 01-12
+func parseMonth(buff []byte, cursor *int, l int) (int, error) {
+ return syslogparser.Parse2Digits(buff, cursor, l, 1, 12, ErrMonthInvalid)
+}
+
+// DATE-MDAY = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on month/year
+func parseDay(buff []byte, cursor *int, l int) (int, error) {
+ // XXX : this is a relaxed constraint
+ // XXX : we do not check if valid regarding February or leap years
+ // XXX : we only checks that day is in range [01 -> 31]
+ // XXX : in other words this function will not rant if you provide Feb 31th
+ return syslogparser.Parse2Digits(buff, cursor, l, 1, 31, ErrDayInvalid)
+}
+
+// FULL-TIME = PARTIAL-TIME TIME-OFFSET
+func parseFullTime(buff []byte, cursor *int, l int) (fullTime, error) {
+ var loc = new(time.Location)
+ var ft fullTime
+
+ pt, err := parsePartialTime(buff, cursor, l)
+ if err != nil {
+ return ft, err
+ }
+
+ loc, err = parseTimeOffset(buff, cursor, l)
+ if err != nil {
+ return ft, err
+ }
+
+ ft = fullTime{
+ pt: pt,
+ loc: loc,
+ }
+
+ return ft, nil
+}
+
+// PARTIAL-TIME = TIME-HOUR ":" TIME-MINUTE ":" TIME-SECOND[TIME-SECFRAC]
+func parsePartialTime(buff []byte, cursor *int, l int) (partialTime, error) {
+ var pt partialTime
+
+ hour, minute, err := getHourMinute(buff, cursor, l)
+ if err != nil {
+ return pt, err
+ }
+
+ if *cursor >= l || buff[*cursor] != ':' {
+ return pt, ErrInvalidTimeFormat
+ }
+
+ *cursor++
+
+ // ----
+
+ seconds, err := parseSecond(buff, cursor, l)
+ if err != nil {
+ return pt, err
+ }
+
+ pt = partialTime{
+ hour: hour,
+ minute: minute,
+ seconds: seconds,
+ }
+
+ // ----
+
+ if *cursor >= l || buff[*cursor] != '.' {
+ return pt, nil
+ }
+
+ *cursor++
+
+ secFrac, err := parseSecFrac(buff, cursor, l)
+ if err != nil {
+ return pt, nil
+ }
+ pt.secFrac = secFrac
+
+ return pt, nil
+}
+
+// TIME-HOUR = 2DIGIT ; 00-23
+func parseHour(buff []byte, cursor *int, l int) (int, error) {
+ return syslogparser.Parse2Digits(buff, cursor, l, 0, 23, ErrHourInvalid)
+}
+
+// TIME-MINUTE = 2DIGIT ; 00-59
+func parseMinute(buff []byte, cursor *int, l int) (int, error) {
+ return syslogparser.Parse2Digits(buff, cursor, l, 0, 59, ErrMinuteInvalid)
+}
+
+// TIME-SECOND = 2DIGIT ; 00-59
+func parseSecond(buff []byte, cursor *int, l int) (int, error) {
+ return syslogparser.Parse2Digits(buff, cursor, l, 0, 59, ErrSecondInvalid)
+}
+
+// TIME-SECFRAC = "." 1*6DIGIT
+func parseSecFrac(buff []byte, cursor *int, l int) (float64, error) {
+ maxDigitLen := 6
+
+ max := *cursor + maxDigitLen
+ from := *cursor
+ to := from
+
+ for to = from; to < max; to++ {
+ if to >= l {
+ break
+ }
+
+ c := buff[to]
+ if !syslogparser.IsDigit(c) {
+ break
+ }
+ }
+
+ sub := string(buff[from:to])
+ if len(sub) == 0 {
+ return 0, ErrSecFracInvalid
+ }
+
+ secFrac, err := strconv.ParseFloat("0."+sub, 64)
+ *cursor = to
+ if err != nil {
+ return 0, ErrSecFracInvalid
+ }
+
+ return secFrac, nil
+}
+
+// TIME-OFFSET = "Z" / TIME-NUMOFFSET
+func parseTimeOffset(buff []byte, cursor *int, l int) (*time.Location, error) {
+
+ if *cursor >= l || buff[*cursor] == 'Z' {
+ *cursor++
+ return time.UTC, nil
+ }
+
+ return parseNumericalTimeOffset(buff, cursor, l)
+}
+
+// TIME-NUMOFFSET = ("+" / "-") TIME-HOUR ":" TIME-MINUTE
+func parseNumericalTimeOffset(buff []byte, cursor *int, l int) (*time.Location, error) {
+ var loc = new(time.Location)
+
+ sign := buff[*cursor]
+
+ if (sign != '+') && (sign != '-') {
+ return loc, ErrTimeZoneInvalid
+ }
+
+ *cursor++
+
+ hour, minute, err := getHourMinute(buff, cursor, l)
+ if err != nil {
+ return loc, err
+ }
+
+ tzStr := fmt.Sprintf("%s%02d:%02d", string(sign), hour, minute)
+ tmpTs, err := time.Parse("-07:00", tzStr)
+ if err != nil {
+ return loc, err
+ }
+
+ return tmpTs.Location(), nil
+}
+
+func getHourMinute(buff []byte, cursor *int, l int) (int, int, error) {
+ hour, err := parseHour(buff, cursor, l)
+ if err != nil {
+ return 0, 0, err
+ }
+
+ if *cursor >= l || buff[*cursor] != ':' {
+ return 0, 0, ErrInvalidTimeFormat
+ }
+
+ *cursor++
+
+ minute, err := parseMinute(buff, cursor, l)
+ if err != nil {
+ return 0, 0, err
+ }
+
+ return hour, minute, nil
+}
+
+func toNSec(sec float64) (int, error) {
+ _, frac := math.Modf(sec)
+ fracStr := strconv.FormatFloat(frac, 'f', 9, 64)
+ fracInt, err := strconv.Atoi(fracStr[2:])
+ if err != nil {
+ return 0, err
+ }
+
+ return fracInt, nil
+}
+
+// ------------------------------------------------
+// https://tools.ietf.org/html/rfc5424#section-6.3
+// ------------------------------------------------
+
+func parseStructuredData(buff []byte, cursor *int, l int) (string, error) {
+ var sdData string
+ var found bool
+
+ if *cursor >= l {
+ return "-", nil
+ }
+
+ if buff[*cursor] == NILVALUE {
+ *cursor++
+ return "-", nil
+ }
+
+ if buff[*cursor] != '[' {
+ return sdData, ErrNoStructuredData
+ }
+
+ from := *cursor
+ to := from
+
+ for to = from; to < l; to++ {
+ if found {
+ break
+ }
+
+ b := buff[to]
+
+ if b == ']' {
+ switch t := to + 1; {
+ case t == l:
+ found = true
+ case t <= l && buff[t] == ' ':
+ found = true
+ }
+ }
+ }
+
+ if found {
+ *cursor = to
+ return string(buff[from:to]), nil
+ }
+
+ return sdData, ErrNoStructuredData
+}
+
+func parseUpToLen(buff []byte, cursor *int, l int, maxLen int, e error) (string, error) {
+ var to int
+ var found bool
+ var result string
+
+ max := *cursor + maxLen
+
+ for to = *cursor; (to <= max) && (to < l); to++ {
+ if buff[to] == ' ' {
+ found = true
+ break
+ }
+ }
+
+ if found {
+ result = string(buff[*cursor:to])
+ } else if to > max {
+ to = max // don't go past max
+ }
+
+ *cursor = to
+
+ if found {
+ return result, nil
+ }
+
+ return "", e
+}
diff --git a/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/syslogparser.go b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/syslogparser.go
new file mode 100644
index 000000000..7a10820c7
--- /dev/null
+++ b/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/syslogparser.go
@@ -0,0 +1,213 @@
+package syslogparser
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+ "time"
+)
+
+const (
+ PRI_PART_START = '<'
+ PRI_PART_END = '>'
+
+ NO_VERSION = -1
+)
+
+var (
+ ErrEOL = &ParserError{"End of log line"}
+ ErrNoSpace = &ParserError{"No space found"}
+
+ ErrPriorityNoStart = &ParserError{"No start char found for priority"}
+ ErrPriorityEmpty = &ParserError{"Priority field empty"}
+ ErrPriorityNoEnd = &ParserError{"No end char found for priority"}
+ ErrPriorityTooShort = &ParserError{"Priority field too short"}
+ ErrPriorityTooLong = &ParserError{"Priority field too long"}
+ ErrPriorityNonDigit = &ParserError{"Non digit found in priority"}
+
+ ErrVersionNotFound = &ParserError{"Can not find version"}
+
+ ErrTimestampUnknownFormat = &ParserError{"Timestamp format unknown"}
+
+ ErrHostnameTooShort = &ParserError{"Hostname field too short"}
+)
+
+type LogParser interface {
+ Parse() error
+ Dump() LogParts
+ Location(*time.Location)
+}
+
+type ParserError struct {
+ ErrorString string
+}
+
+type Priority struct {
+ P int
+ F Facility
+ S Severity
+}
+
+type Facility struct {
+ Value int
+}
+
+type Severity struct {
+ Value int
+}
+
+type LogParts map[string]interface{}
+
+// https://tools.ietf.org/html/rfc3164#section-4.1
+func ParsePriority(buff []byte, cursor *int, l int) (Priority, error) {
+ pri := newPriority(0)
+
+ if l <= 0 {
+ return pri, ErrPriorityEmpty
+ }
+
+ if buff[*cursor] != PRI_PART_START {
+ return pri, ErrPriorityNoStart
+ }
+
+ i := 1
+ priDigit := 0
+
+ for i < l {
+ if i >= 5 {
+ return pri, ErrPriorityTooLong
+ }
+
+ c := buff[i]
+
+ if c == PRI_PART_END {
+ if i == 1 {
+ return pri, ErrPriorityTooShort
+ }
+
+ *cursor = i + 1
+ return newPriority(priDigit), nil
+ }
+
+ if IsDigit(c) {
+ v, e := strconv.Atoi(string(c))
+ if e != nil {
+ return pri, e
+ }
+
+ priDigit = (priDigit * 10) + v
+ } else {
+ return pri, ErrPriorityNonDigit
+ }
+
+ i++
+ }
+
+ return pri, ErrPriorityNoEnd
+}
+
+// https://tools.ietf.org/html/rfc5424#section-6.2.2
+func ParseVersion(buff []byte, cursor *int, l int) (int, error) {
+ if *cursor >= l {
+ return NO_VERSION, ErrVersionNotFound
+ }
+
+ c := buff[*cursor]
+ *cursor++
+
+ // XXX : not a version, not an error though as RFC 3164 does not support it
+ if !IsDigit(c) {
+ return NO_VERSION, nil
+ }
+
+ v, e := strconv.Atoi(string(c))
+ if e != nil {
+ *cursor--
+ return NO_VERSION, e
+ }
+
+ return v, nil
+}
+
+func IsDigit(c byte) bool {
+ return c >= '0' && c <= '9'
+}
+
+func newPriority(p int) Priority {
+ // The Priority value is calculated by first multiplying the Facility
+ // number by 8 and then adding the numerical value of the Severity.
+
+ return Priority{
+ P: p,
+ F: Facility{Value: p / 8},
+ S: Severity{Value: p % 8},
+ }
+}
+
+func FindNextSpace(buff []byte, from int, l int) (int, error) {
+ var to int
+
+ for to = from; to < l; to++ {
+ if buff[to] == ' ' {
+ to++
+ return to, nil
+ }
+ }
+
+ return 0, ErrNoSpace
+}
+
+func Parse2Digits(buff []byte, cursor *int, l int, min int, max int, e error) (int, error) {
+ digitLen := 2
+
+ if *cursor+digitLen > l {
+ return 0, ErrEOL
+ }
+
+ sub := string(buff[*cursor : *cursor+digitLen])
+
+ *cursor += digitLen
+
+ i, err := strconv.Atoi(sub)
+ if err != nil {
+ return 0, e
+ }
+
+ if i >= min && i <= max {
+ return i, nil
+ }
+
+ return 0, e
+}
+
+func ParseHostname(buff []byte, cursor *int, l int) (string, error) {
+ from := *cursor
+
+ if from >= l {
+ return "", ErrHostnameTooShort
+ }
+
+ var to int
+
+ for to = from; to < l; to++ {
+ if buff[to] == ' ' {
+ break
+ }
+ }
+
+ hostname := buff[from:to]
+
+ *cursor = to
+
+ return string(hostname), nil
+}
+
+func ShowCursorPos(buff []byte, cursor int) {
+ fmt.Println(string(buff))
+ padding := strings.Repeat("-", cursor)
+ fmt.Println(padding + "↑\n")
+}
+
+func (err *ParserError) Error() string {
+ return err.ErrorString
+}
diff --git a/vendor/gopkg.in/mcuadros/go-syslog.v2/server.go b/vendor/gopkg.in/mcuadros/go-syslog.v2/server.go
new file mode 100644
index 000000000..352597b89
--- /dev/null
+++ b/vendor/gopkg.in/mcuadros/go-syslog.v2/server.go
@@ -0,0 +1,378 @@
+package syslog
+
+import (
+ "bufio"
+ "crypto/tls"
+ "errors"
+ "net"
+ "strings"
+ "sync"
+ "time"
+
+ "gopkg.in/mcuadros/go-syslog.v2/format"
+)
+
+var (
+ RFC3164 = &format.RFC3164{} // RFC3164: http://www.ietf.org/rfc/rfc3164.txt
+ RFC5424 = &format.RFC5424{} // RFC5424: http://www.ietf.org/rfc/rfc5424.txt
+ RFC6587 = &format.RFC6587{} // RFC6587: http://www.ietf.org/rfc/rfc6587.txt - octet counting variant
+ Automatic = &format.Automatic{} // Automatically identify the format
+)
+
+const (
+ datagramChannelBufferSize = 10
+ datagramReadBufferSize = 64 * 1024
+)
+
+// A function type which gets the TLS peer name from the connection. Can return
+// ok=false to terminate the connection
+type TlsPeerNameFunc func(tlsConn *tls.Conn) (tlsPeer string, ok bool)
+
+type Server struct {
+ listeners []net.Listener
+ connections []net.PacketConn
+ wait sync.WaitGroup
+ doneTcp chan bool
+ datagramChannel chan DatagramMessage
+ format format.Format
+ handler Handler
+ lastError error
+ readTimeoutMilliseconds int64
+ tlsPeerNameFunc TlsPeerNameFunc
+ datagramPool sync.Pool
+}
+
+//NewServer returns a new Server
+func NewServer() *Server {
+ return &Server{tlsPeerNameFunc: defaultTlsPeerName, datagramPool: sync.Pool{
+ New: func() interface{} {
+ return make([]byte, 65536)
+ },
+ }}
+}
+
+//Sets the syslog format (RFC3164 or RFC5424 or RFC6587)
+func (s *Server) SetFormat(f format.Format) {
+ s.format = f
+}
+
+//Sets the handler, this handler with receive every syslog entry
+func (s *Server) SetHandler(handler Handler) {
+ s.handler = handler
+}
+
+//Sets the connection timeout for TCP connections, in milliseconds
+func (s *Server) SetTimeout(millseconds int64) {
+ s.readTimeoutMilliseconds = millseconds
+}
+
+// Set the function that extracts a TLS peer name from the TLS connection
+func (s *Server) SetTlsPeerNameFunc(tlsPeerNameFunc TlsPeerNameFunc) {
+ s.tlsPeerNameFunc = tlsPeerNameFunc
+}
+
+// Default TLS peer name function - returns the CN of the certificate
+func defaultTlsPeerName(tlsConn *tls.Conn) (tlsPeer string, ok bool) {
+ state := tlsConn.ConnectionState()
+ if len(state.PeerCertificates) <= 0 {
+ return "", false
+ }
+ cn := state.PeerCertificates[0].Subject.CommonName
+ return cn, true
+}
+
+//Configure the server for listen on an UDP addr
+func (s *Server) ListenUDP(addr string) error {
+ udpAddr, err := net.ResolveUDPAddr("udp", addr)
+ if err != nil {
+ return err
+ }
+
+ connection, err := net.ListenUDP("udp", udpAddr)
+ if err != nil {
+ return err
+ }
+ connection.SetReadBuffer(datagramReadBufferSize)
+
+ s.connections = append(s.connections, connection)
+ return nil
+}
+
+//Configure the server for listen on an unix socket
+func (s *Server) ListenUnixgram(addr string) error {
+ unixAddr, err := net.ResolveUnixAddr("unixgram", addr)
+ if err != nil {
+ return err
+ }
+
+ connection, err := net.ListenUnixgram("unixgram", unixAddr)
+ if err != nil {
+ return err
+ }
+ connection.SetReadBuffer(datagramReadBufferSize)
+
+ s.connections = append(s.connections, connection)
+ return nil
+}
+
+//Configure the server for listen on a TCP addr
+func (s *Server) ListenTCP(addr string) error {
+ tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
+ if err != nil {
+ return err
+ }
+
+ listener, err := net.ListenTCP("tcp", tcpAddr)
+ if err != nil {
+ return err
+ }
+
+ s.doneTcp = make(chan bool)
+ s.listeners = append(s.listeners, listener)
+ return nil
+}
+
+//Configure the server for listen on a TCP addr for TLS
+func (s *Server) ListenTCPTLS(addr string, config *tls.Config) error {
+ listener, err := tls.Listen("tcp", addr, config)
+ if err != nil {
+ return err
+ }
+
+ s.doneTcp = make(chan bool)
+ s.listeners = append(s.listeners, listener)
+ return nil
+}
+
+//Starts the server, all the go routines goes to live
+func (s *Server) Boot() error {
+ if s.format == nil {
+ return errors.New("please set a valid format")
+ }
+
+ if s.handler == nil {
+ return errors.New("please set a valid handler")
+ }
+
+ for _, listener := range s.listeners {
+ s.goAcceptConnection(listener)
+ }
+
+ if len(s.connections) > 0 {
+ s.goParseDatagrams()
+ }
+
+ for _, connection := range s.connections {
+ s.goReceiveDatagrams(connection)
+ }
+
+ return nil
+}
+
+func (s *Server) goAcceptConnection(listener net.Listener) {
+ s.wait.Add(1)
+ go func(listener net.Listener) {
+ loop:
+ for {
+ select {
+ case <-s.doneTcp:
+ break loop
+ default:
+ }
+ connection, err := listener.Accept()
+ if err != nil {
+ continue
+ }
+
+ s.goScanConnection(connection)
+ }
+
+ s.wait.Done()
+ }(listener)
+}
+
+func (s *Server) goScanConnection(connection net.Conn) {
+ scanner := bufio.NewScanner(connection)
+ if sf := s.format.GetSplitFunc(); sf != nil {
+ scanner.Split(sf)
+ }
+
+ remoteAddr := connection.RemoteAddr()
+ var client string
+ if remoteAddr != nil {
+ client = remoteAddr.String()
+ }
+
+ tlsPeer := ""
+ if tlsConn, ok := connection.(*tls.Conn); ok {
+ // Handshake now so we get the TLS peer information
+ if err := tlsConn.Handshake(); err != nil {
+ connection.Close()
+ return
+ }
+ if s.tlsPeerNameFunc != nil {
+ var ok bool
+ tlsPeer, ok = s.tlsPeerNameFunc(tlsConn)
+ if !ok {
+ connection.Close()
+ return
+ }
+ }
+ }
+
+ var scanCloser *ScanCloser
+ scanCloser = &ScanCloser{scanner, connection}
+
+ s.wait.Add(1)
+ go s.scan(scanCloser, client, tlsPeer)
+}
+
+func (s *Server) scan(scanCloser *ScanCloser, client string, tlsPeer string) {
+loop:
+ for {
+ select {
+ case <-s.doneTcp:
+ break loop
+ default:
+ }
+ if s.readTimeoutMilliseconds > 0 {
+ scanCloser.closer.SetReadDeadline(time.Now().Add(time.Duration(s.readTimeoutMilliseconds) * time.Millisecond))
+ }
+ if scanCloser.Scan() {
+ s.parser([]byte(scanCloser.Text()), client, tlsPeer)
+ } else {
+ break loop
+ }
+ }
+ scanCloser.closer.Close()
+
+ s.wait.Done()
+}
+
+func (s *Server) parser(line []byte, client string, tlsPeer string) {
+ parser := s.format.GetParser(line)
+ err := parser.Parse()
+ if err != nil {
+ s.lastError = err
+ }
+
+ logParts := parser.Dump()
+ logParts["client"] = client
+ if logParts["hostname"] == "" && (s.format == RFC3164 || s.format == Automatic) {
+ if i := strings.Index(client, ":"); i > 1 {
+ logParts["hostname"] = client[:i]
+ } else {
+ logParts["hostname"] = client
+ }
+ }
+ logParts["tls_peer"] = tlsPeer
+
+ s.handler.Handle(logParts, int64(len(line)), err)
+}
+
+//Returns the last error
+func (s *Server) GetLastError() error {
+ return s.lastError
+}
+
+//Kill the server
+func (s *Server) Kill() error {
+ for _, connection := range s.connections {
+ err := connection.Close()
+ if err != nil {
+ return err
+ }
+ }
+
+ for _, listener := range s.listeners {
+ err := listener.Close()
+ if err != nil {
+ return err
+ }
+ }
+ // Only need to close channel once to broadcast to all waiting
+ if s.doneTcp != nil {
+ close(s.doneTcp)
+ }
+ if s.datagramChannel != nil {
+ close(s.datagramChannel)
+ }
+ return nil
+}
+
+//Waits until the server stops
+func (s *Server) Wait() {
+ s.wait.Wait()
+}
+
+type TimeoutCloser interface {
+ Close() error
+ SetReadDeadline(t time.Time) error
+}
+
+type ScanCloser struct {
+ *bufio.Scanner
+ closer TimeoutCloser
+}
+
+type DatagramMessage struct {
+ message []byte
+ client string
+}
+
+func (s *Server) goReceiveDatagrams(packetconn net.PacketConn) {
+ s.wait.Add(1)
+ go func() {
+ defer s.wait.Done()
+ for {
+ buf := s.datagramPool.Get().([]byte)
+ n, addr, err := packetconn.ReadFrom(buf)
+ if err == nil {
+ // Ignore trailing control characters and NULs
+ for ; (n > 0) && (buf[n-1] < 32); n-- {
+ }
+ if n > 0 {
+ var address string
+ if addr != nil {
+ address = addr.String()
+ }
+ s.datagramChannel <- DatagramMessage{buf[:n], address}
+ }
+ } else {
+ // there has been an error. Either the server has been killed
+ // or may be getting a transitory error due to (e.g.) the
+ // interface being shutdown in which case sleep() to avoid busy wait.
+ opError, ok := err.(*net.OpError)
+ if (ok) && !opError.Temporary() && !opError.Timeout() {
+ return
+ }
+ time.Sleep(10 * time.Millisecond)
+ }
+ }
+ }()
+}
+
+func (s *Server) goParseDatagrams() {
+ s.datagramChannel = make(chan DatagramMessage, datagramChannelBufferSize)
+
+ s.wait.Add(1)
+ go func() {
+ defer s.wait.Done()
+ for {
+ select {
+ case msg, ok := (<-s.datagramChannel):
+ if !ok {
+ return
+ }
+ if sf := s.format.GetSplitFunc(); sf != nil {
+ if _, token, err := sf(msg.message, true); err == nil {
+ s.parser(token, msg.client, "")
+ }
+ } else {
+ s.parser(msg.message, msg.client, "")
+ }
+ s.datagramPool.Put(msg.message[:cap(msg.message)])
+ }
+ }
+ }()
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 586053879..9c1ce34a9 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -249,6 +249,7 @@ github.com/russross/blackfriday/v2
# github.com/sirupsen/logrus v1.8.1
## explicit; go 1.13
github.com/sirupsen/logrus
+github.com/sirupsen/logrus/hooks/syslog
# github.com/spf13/afero v1.6.0
## explicit; go 1.13
github.com/spf13/afero
@@ -635,6 +636,13 @@ google.golang.org/protobuf/types/descriptorpb
# gopkg.in/ini.v1 v1.66.2
## explicit
gopkg.in/ini.v1
+# gopkg.in/mcuadros/go-syslog.v2 v2.3.0
+## explicit
+gopkg.in/mcuadros/go-syslog.v2
+gopkg.in/mcuadros/go-syslog.v2/format
+gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser
+gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc3164
+gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc5424
# gopkg.in/square/go-jose.v2 v2.6.0
## explicit
gopkg.in/square/go-jose.v2