Add optional syslog logrus hook (#343)
* add optional syslog logrus hook * document syslog
This commit is contained in:
parent
909f801742
commit
c111b239f7
|
@ -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).
|
- [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).
|
- [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).
|
- [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).
|
- [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).
|
- [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).
|
- [modernc.org/sqlite](https://gitlab.com/cznic/sqlite); cgo-free port of SQLite. [Other License](https://gitlab.com/cznic/sqlite/-/blob/master/LICENSE).
|
||||||
|
|
|
@ -23,7 +23,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"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
|
// The idea here is to take a GTSAction and run it with the given
|
||||||
// context, after initializing any last-minute things like loggers etc.
|
// context, after initializing any last-minute things like loggers etc.
|
||||||
func run(ctx context.Context, action action.GTSAction) error {
|
func run(ctx context.Context, action action.GTSAction) error {
|
||||||
// if log level has been set...
|
if err := log.Initialize(); err != nil {
|
||||||
if logLevel := viper.GetString(config.Keys.LogLevel); logLevel != "" {
|
return fmt.Errorf("error initializing log: %s", err)
|
||||||
// then try to initialize the logger to that level
|
|
||||||
if err := log.Initialize(logLevel); err != nil {
|
|
||||||
return fmt.Errorf("error initializing log: %s", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return action(ctx)
|
return action(ctx)
|
||||||
|
|
|
@ -34,6 +34,7 @@ func Server(cmd *cobra.Command, values config.Values) {
|
||||||
OIDC(cmd, values)
|
OIDC(cmd, values)
|
||||||
SMTP(cmd, values)
|
SMTP(cmd, values)
|
||||||
Router(cmd, values)
|
Router(cmd, values)
|
||||||
|
Syslog(cmd, values)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Router attaches flags pertaining to the gin router.
|
// 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.SMTPPassword, values.SMTPPassword, usage.SMTPPassword)
|
||||||
cmd.Flags().String(config.Keys.SMTPFrom, values.SMTPFrom, usage.SMTPFrom)
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -73,6 +73,9 @@ var usage = config.KeyNames{
|
||||||
SMTPUsername: "Username to authenticate with the smtp server as. Eg., 'postmaster@mail.example.org'",
|
SMTPUsername: "Username to authenticate with the smtp server as. Eg., 'postmaster@mail.example.org'",
|
||||||
SMTPPassword: "Password to pass to the smtp server.",
|
SMTPPassword: "Password to pass to the smtp server.",
|
||||||
SMTPFrom: "Address to use as the 'from' field of the email. Eg., 'gotosocial@example.org'",
|
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",
|
AdminAccountUsername: "the username to create/delete/etc",
|
||||||
AdminAccountEmail: "the email address of this account",
|
AdminAccountEmail: "the email address of this account",
|
||||||
AdminAccountPassword: "the password to set for this account",
|
AdminAccountPassword: "the password to set for this account",
|
||||||
|
|
|
@ -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"
|
||||||
|
```
|
|
@ -396,3 +396,28 @@ smtp-password: ""
|
||||||
# Examples: ["mail@example.org"]
|
# Examples: ["mail@example.org"]
|
||||||
# Default: ""
|
# Default: ""
|
||||||
smtp-from: ""
|
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"
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -38,6 +38,7 @@ require (
|
||||||
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b
|
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b
|
||||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
|
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
|
||||||
golang.org/x/text v0.3.7
|
golang.org/x/text v0.3.7
|
||||||
|
gopkg.in/mcuadros/go-syslog.v2 v2.3.0
|
||||||
modernc.org/sqlite v1.14.2
|
modernc.org/sqlite v1.14.2
|
||||||
mvdan.cc/xurls/v2 v2.3.0
|
mvdan.cc/xurls/v2 v2.3.0
|
||||||
)
|
)
|
||||||
|
|
2
go.sum
2
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.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI=
|
gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI=
|
||||||
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
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.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 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||||
|
|
|
@ -84,4 +84,8 @@ var Defaults = Values{
|
||||||
SMTPUsername: "",
|
SMTPUsername: "",
|
||||||
SMTPPassword: "",
|
SMTPPassword: "",
|
||||||
SMTPFrom: "GoToSocial",
|
SMTPFrom: "GoToSocial",
|
||||||
|
|
||||||
|
SyslogEnabled: false,
|
||||||
|
SyslogProtocol: "udp",
|
||||||
|
SyslogAddress: "localhost:514",
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,6 +95,11 @@ type KeyNames struct {
|
||||||
SMTPPassword string
|
SMTPPassword string
|
||||||
SMTPFrom string
|
SMTPFrom string
|
||||||
|
|
||||||
|
// syslog
|
||||||
|
SyslogEnabled string
|
||||||
|
SyslogProtocol string
|
||||||
|
SyslogAddress string
|
||||||
|
|
||||||
// admin
|
// admin
|
||||||
AdminAccountUsername string
|
AdminAccountUsername string
|
||||||
AdminAccountEmail string
|
AdminAccountEmail string
|
||||||
|
@ -168,6 +173,10 @@ var Keys = KeyNames{
|
||||||
SMTPPassword: "smtp-password",
|
SMTPPassword: "smtp-password",
|
||||||
SMTPFrom: "smtp-from",
|
SMTPFrom: "smtp-from",
|
||||||
|
|
||||||
|
SyslogEnabled: "syslog-enabled",
|
||||||
|
SyslogProtocol: "syslog-protocol",
|
||||||
|
SyslogAddress: "syslog-address",
|
||||||
|
|
||||||
AdminAccountUsername: "username",
|
AdminAccountUsername: "username",
|
||||||
AdminAccountEmail: "email",
|
AdminAccountEmail: "email",
|
||||||
AdminAccountPassword: "password",
|
AdminAccountPassword: "password",
|
||||||
|
|
|
@ -83,6 +83,10 @@ type Values struct {
|
||||||
SMTPPassword string
|
SMTPPassword string
|
||||||
SMTPFrom string
|
SMTPFrom string
|
||||||
|
|
||||||
|
SyslogEnabled bool
|
||||||
|
SyslogProtocol string
|
||||||
|
SyslogAddress string
|
||||||
|
|
||||||
AdminAccountUsername string
|
AdminAccountUsername string
|
||||||
AdminAccountEmail string
|
AdminAccountEmail string
|
||||||
AdminAccountPassword string
|
AdminAccountPassword string
|
||||||
|
|
|
@ -22,31 +22,60 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"log/syslog"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"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,
|
// It also sets the output to log.outputSplitter,
|
||||||
// so you get error logs on stderr and normal logs on stdout.
|
// 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{})
|
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{
|
logrus.SetFormatter(&logrus.TextFormatter{
|
||||||
DisableColors: true,
|
DisableColors: true,
|
||||||
DisableQuote: true,
|
DisableQuote: true,
|
||||||
FullTimestamp: 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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{})
|
||||||
|
}
|
|
@ -19,11 +19,13 @@
|
||||||
package media
|
package media
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
|
|
||||||
"github.com/stretchr/testify/suite"
|
"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
|
// SetupSuite sets some variables on the suite that we can use as consts (more or less) throughout
|
||||||
func (suite *MediaUtilTestSuite) SetupSuite() {
|
func (suite *MediaUtilTestSuite) SetupSuite() {
|
||||||
// doesn't use testrig.InitTestLog() helper to prevent import cycle
|
// 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 {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *MediaUtilTestSuite) TearDownSuite() {
|
func (suite *MediaUtilTestSuite) TearDownSuite() {
|
||||||
|
|
|
@ -29,6 +29,7 @@ nav:
|
||||||
- "configuration/letsencrypt.md"
|
- "configuration/letsencrypt.md"
|
||||||
- "configuration/oidc.md"
|
- "configuration/oidc.md"
|
||||||
- "configuration/smtp.md"
|
- "configuration/smtp.md"
|
||||||
|
- "configuration/syslog.md"
|
||||||
- "Admin":
|
- "Admin":
|
||||||
- "admin/admin_panel.md"
|
- "admin/admin_panel.md"
|
||||||
- "admin/cli.md"
|
- "admin/cli.md"
|
||||||
|
|
|
@ -5,7 +5,7 @@ set -e
|
||||||
echo "STARTING CLI TESTS"
|
echo "STARTING CLI TESTS"
|
||||||
|
|
||||||
echo "TEST_1 Make sure defaults are set correctly."
|
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)"
|
TEST_1="$(go run ./cmd/gotosocial/... debug config)"
|
||||||
if [ "${TEST_1}" != "${TEST_1_EXPECTED}" ]; then
|
if [ "${TEST_1}" != "${TEST_1_EXPECTED}" ]; then
|
||||||
echo "TEST_1 not equal TEST_1_EXPECTED"
|
echo "TEST_1 not equal TEST_1_EXPECTED"
|
||||||
|
@ -15,7 +15,7 @@ else
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "TEST_2 Override db-address from default using cli flag."
|
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)"
|
TEST_2="$(go run ./cmd/gotosocial/... --db-address some.db.address debug config)"
|
||||||
if [ "${TEST_2}" != "${TEST_2_EXPECTED}" ]; then
|
if [ "${TEST_2}" != "${TEST_2_EXPECTED}" ]; then
|
||||||
echo "TEST_2 not equal TEST_2_EXPECTED"
|
echo "TEST_2 not equal TEST_2_EXPECTED"
|
||||||
|
@ -25,7 +25,7 @@ else
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "TEST_3 Override db-address from default using env var."
|
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)"
|
TEST_3="$(GTS_DB_ADDRESS=some.db.address go run ./cmd/gotosocial/... debug config)"
|
||||||
if [ "${TEST_3}" != "${TEST_3_EXPECTED}" ]; then
|
if [ "${TEST_3}" != "${TEST_3_EXPECTED}" ]; then
|
||||||
echo "TEST_3 not equal TEST_3_EXPECTED"
|
echo "TEST_3 not equal TEST_3_EXPECTED"
|
||||||
|
@ -35,7 +35,7 @@ else
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "TEST_4 Override db-address from default using both env var and cli flag. The cli flag should take priority."
|
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)"
|
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
|
if [ "${TEST_4}" != "${TEST_4_EXPECTED}" ]; then
|
||||||
echo "TEST_4 not equal TEST_4_EXPECTED"
|
echo "TEST_4 not equal TEST_4_EXPECTED"
|
||||||
|
@ -45,7 +45,7 @@ else
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "TEST_5 Test loading a config file by passing an env var."
|
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)"
|
TEST_5="$(GTS_CONFIG_PATH=./test/test.yaml go run ./cmd/gotosocial/... debug config)"
|
||||||
if [ "${TEST_5}" != "${TEST_5_EXPECTED}" ]; then
|
if [ "${TEST_5}" != "${TEST_5_EXPECTED}" ]; then
|
||||||
echo "TEST_5 not equal TEST_5_EXPECTED"
|
echo "TEST_5 not equal TEST_5_EXPECTED"
|
||||||
|
@ -55,7 +55,7 @@ else
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "TEST_6 Test loading a config file by passing cli flag."
|
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)"
|
TEST_6="$(go run ./cmd/gotosocial/... --config-path ./test/test.yaml debug config)"
|
||||||
if [ "${TEST_6}" != "${TEST_6_EXPECTED}" ]; then
|
if [ "${TEST_6}" != "${TEST_6_EXPECTED}" ]; then
|
||||||
echo "TEST_6 not equal TEST_6_EXPECTED"
|
echo "TEST_6 not equal TEST_6_EXPECTED"
|
||||||
|
@ -65,7 +65,7 @@ else
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "TEST_7 Test loading a config file and overriding one of the variables with a cli flag."
|
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)"
|
TEST_7="$(go run ./cmd/gotosocial/... --config-path ./test/test.yaml --account-domain '' debug config)"
|
||||||
if [ "${TEST_7}" != "${TEST_7_EXPECTED}" ]; then
|
if [ "${TEST_7}" != "${TEST_7_EXPECTED}" ]; then
|
||||||
echo "TEST_7 not equal TEST_7_EXPECTED"
|
echo "TEST_7 not equal TEST_7_EXPECTED"
|
||||||
|
@ -75,7 +75,7 @@ else
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "TEST_8 Test loading a config file and overriding one of the variables with an env var."
|
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)"
|
TEST_8="$(GTS_ACCOUNT_DOMAIN='peepee' go run ./cmd/gotosocial/... --config-path ./test/test.yaml debug config)"
|
||||||
if [ "${TEST_8}" != "${TEST_8_EXPECTED}" ]; then
|
if [ "${TEST_8}" != "${TEST_8_EXPECTED}" ]; then
|
||||||
echo "TEST_8 not equal TEST_8_EXPECTED"
|
echo "TEST_8 not equal TEST_8_EXPECTED"
|
||||||
|
@ -85,7 +85,7 @@ else
|
||||||
fi
|
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."
|
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)"
|
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
|
if [ "${TEST_9}" != "${TEST_9_EXPECTED}" ]; then
|
||||||
echo "TEST_9 not equal TEST_9_EXPECTED"
|
echo "TEST_9 not equal TEST_9_EXPECTED"
|
||||||
|
@ -95,7 +95,7 @@ else
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "TEST_10 Test loading a config file from json."
|
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)"
|
TEST_10="$(go run ./cmd/gotosocial/... --config-path ./test/test.json debug config)"
|
||||||
if [ "${TEST_10}" != "${TEST_10_EXPECTED}" ]; then
|
if [ "${TEST_10}" != "${TEST_10_EXPECTED}" ]; then
|
||||||
echo "TEST_10 not equal TEST_10_EXPECTED"
|
echo "TEST_10 not equal TEST_10_EXPECTED"
|
||||||
|
@ -105,7 +105,7 @@ else
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "TEST_11 Test loading a partial config file. Default values should be used apart from those set in the config file."
|
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)"
|
TEST_11="$(go run ./cmd/gotosocial/... --config-path ./test/test2.yaml debug config)"
|
||||||
if [ "${TEST_11}" != "${TEST_11_EXPECTED}" ]; then
|
if [ "${TEST_11}" != "${TEST_11_EXPECTED}" ]; then
|
||||||
echo "TEST_11 not equal TEST_11_EXPECTED"
|
echo "TEST_11 not equal TEST_11_EXPECTED"
|
||||||
|
|
|
@ -397,3 +397,28 @@ smtp-password: "smtp-password"
|
||||||
# Examples: ["mail@example.org"]
|
# Examples: ["mail@example.org"]
|
||||||
# Default: ""
|
# Default: ""
|
||||||
smtp-from: "someone@example.org"
|
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"
|
||||||
|
|
|
@ -117,4 +117,8 @@ var TestDefaults = config.Values{
|
||||||
SMTPUsername: "",
|
SMTPUsername: "",
|
||||||
SMTPPassword: "",
|
SMTPPassword: "",
|
||||||
SMTPFrom: "GoToSocial",
|
SMTPFrom: "GoToSocial",
|
||||||
|
|
||||||
|
SyslogEnabled: false,
|
||||||
|
SyslogProtocol: "udp",
|
||||||
|
SyslogAddress: "localhost:514",
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,14 +19,37 @@
|
||||||
package testrig
|
package testrig
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"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
|
// InitTestLog sets the global logger to trace level for logging
|
||||||
func InitTestLog() {
|
func InitTestLog() {
|
||||||
err := log.Initialize(logrus.TraceLevel.String())
|
if err := log.Initialize(); err != nil {
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
# Syslog Hooks for Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/>
|
||||||
|
|
||||||
|
## 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
|
@ -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
|
||||||
|
}
|
|
@ -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 ./...
|
|
@ -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.
|
|
@ -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)
|
|
@ -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"
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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())
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
23
vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/LICENSE
generated
vendored
Normal file
23
vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/LICENSE
generated
vendored
Normal file
|
@ -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.
|
4
vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/README.md
generated
vendored
Normal file
4
vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/README.md
generated
vendored
Normal file
|
@ -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.
|
292
vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc3164/rfc3164.go
generated
vendored
Normal file
292
vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc3164/rfc3164.go
generated
vendored
Normal file
|
@ -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
|
||||||
|
}
|
606
vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc5424/rfc5424.go
generated
vendored
Normal file
606
vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/rfc5424/rfc5424.go
generated
vendored
Normal file
|
@ -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
|
||||||
|
}
|
213
vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/syslogparser.go
generated
vendored
Normal file
213
vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/syslogparser.go
generated
vendored
Normal file
|
@ -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
|
||||||
|
}
|
|
@ -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)])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
|
@ -249,6 +249,7 @@ github.com/russross/blackfriday/v2
|
||||||
# github.com/sirupsen/logrus v1.8.1
|
# github.com/sirupsen/logrus v1.8.1
|
||||||
## explicit; go 1.13
|
## explicit; go 1.13
|
||||||
github.com/sirupsen/logrus
|
github.com/sirupsen/logrus
|
||||||
|
github.com/sirupsen/logrus/hooks/syslog
|
||||||
# github.com/spf13/afero v1.6.0
|
# github.com/spf13/afero v1.6.0
|
||||||
## explicit; go 1.13
|
## explicit; go 1.13
|
||||||
github.com/spf13/afero
|
github.com/spf13/afero
|
||||||
|
@ -635,6 +636,13 @@ google.golang.org/protobuf/types/descriptorpb
|
||||||
# gopkg.in/ini.v1 v1.66.2
|
# gopkg.in/ini.v1 v1.66.2
|
||||||
## explicit
|
## explicit
|
||||||
gopkg.in/ini.v1
|
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
|
# gopkg.in/square/go-jose.v2 v2.6.0
|
||||||
## explicit
|
## explicit
|
||||||
gopkg.in/square/go-jose.v2
|
gopkg.in/square/go-jose.v2
|
||||||
|
|
Loading…
Reference in New Issue