diff --git a/etc/kuiper.yaml b/etc/kuiper.yaml index 566e6ec3e6..5a59bbaa0e 100644 --- a/etc/kuiper.yaml +++ b/etc/kuiper.yaml @@ -5,6 +5,18 @@ basic: consoleLog: false # true|false, if it's set to true, then the log will be print to log file fileLog: true + # syslog settings + syslog: + # true|false, if it's set to true, then the log will be print to syslog + enable: false + # The syslog protocol, tcp or udp; Leave empty if no remote syslog server is used + network: udp + # The syslog server address; Leave empty if no remote syslog server is used + address: localhost:514 + # The syslog level, supports debug, info, warn, error + level: info + # The syslog tag; Leave empty if no tag is used + tag: kuiper # How many hours to split the file rotateTime: 24 # Maximum file storage hours diff --git a/internal/conf/conf.go b/internal/conf/conf.go index 9a36172208..94f838e71f 100644 --- a/internal/conf/conf.go +++ b/internal/conf/conf.go @@ -1,4 +1,4 @@ -// Copyright 2021-2023 EMQ Technologies Co., Ltd. +// Copyright 2023 EMQ Technologies Co., Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -130,27 +130,52 @@ type SQLConf struct { MaxConnections int `yaml:"maxConnections"` } +type syslogConf struct { + Enable bool `yaml:"enable"` + Network string `yaml:"network"` + Address string `yaml:"address"` + Tag string `yaml:"tag"` + Level string `yaml:"level"` +} + +func (s *syslogConf) Validate() error { + if s.Network == "" { + s.Network = "udp" + } + if s.Level == "" { + s.Level = "info" + } + switch s.Level { + case "debug", "info", "warn", "error": + // valid, do nothing + default: + return fmt.Errorf("invalid syslog level: %s", s.Level) + } + return nil +} + type KuiperConf struct { Basic struct { - Debug bool `yaml:"debug"` - ConsoleLog bool `yaml:"consoleLog"` - FileLog bool `yaml:"fileLog"` - RotateTime int `yaml:"rotateTime"` - MaxAge int `yaml:"maxAge"` - TimeZone string `yaml:"timezone"` - Ip string `yaml:"ip"` - Port int `yaml:"port"` - RestIp string `yaml:"restIp"` - RestPort int `yaml:"restPort"` - RestTls *tlsConf `yaml:"restTls"` - Prometheus bool `yaml:"prometheus"` - PrometheusPort int `yaml:"prometheusPort"` - PluginHosts string `yaml:"pluginHosts"` - Authentication bool `yaml:"authentication"` - IgnoreCase bool `yaml:"ignoreCase"` - SQLConf *SQLConf `yaml:"sql"` - RulePatrolInterval string `yaml:"rulePatrolInterval"` - CfgStorageType string `yaml:"cfgStorageType"` + Debug bool `yaml:"debug"` + ConsoleLog bool `yaml:"consoleLog"` + FileLog bool `yaml:"fileLog"` + Syslog *syslogConf `yaml:"syslog"` + RotateTime int `yaml:"rotateTime"` + MaxAge int `yaml:"maxAge"` + TimeZone string `yaml:"timezone"` + Ip string `yaml:"ip"` + Port int `yaml:"port"` + RestIp string `yaml:"restIp"` + RestPort int `yaml:"restPort"` + RestTls *tlsConf `yaml:"restTls"` + Prometheus bool `yaml:"prometheus"` + PrometheusPort int `yaml:"prometheusPort"` + PluginHosts string `yaml:"pluginHosts"` + Authentication bool `yaml:"authentication"` + IgnoreCase bool `yaml:"ignoreCase"` + SQLConf *SQLConf `yaml:"sql"` + RulePatrolInterval string `yaml:"rulePatrolInterval"` + CfgStorageType string `yaml:"cfgStorageType"` } Rule api.RuleOption Sink *SinkConf @@ -260,6 +285,17 @@ func InitConf() { SetDebugLevel(true) } + if os.Getenv(logger.KuiperSyslogKey) == "true" || Config.Basic.Syslog != nil { + c := Config.Basic.Syslog + if c == nil { + c = &syslogConf{} + } + err := logger.InitSyslog(c.Network, c.Address, c.Level, c.Tag) + if err != nil { + log.Fatal(err) + } + } + if err := SetConsoleAndFileLog(Config.Basic.ConsoleLog, Config.Basic.FileLog); err != nil { log.Fatal(err) } @@ -302,6 +338,10 @@ func InitConf() { } _ = Config.Sink.Validate() + if Config.Basic.Syslog != nil { + _ = Config.Basic.Syslog.Validate() + } + _ = ValidateRuleOption(&Config.Rule) } diff --git a/internal/conf/conf_test.go b/internal/conf/conf_test.go index 99eb616a7c..9bb76e34ee 100644 --- a/internal/conf/conf_test.go +++ b/internal/conf/conf_test.go @@ -1,4 +1,4 @@ -// Copyright 2022-2023 EMQ Technologies Co., Ltd. +// Copyright 2023 EMQ Technologies Co., Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -384,3 +384,41 @@ func TestSinkConf_Validate(t *testing.T) { }) } } + +func TestSyslogConf_Validate(t *testing.T) { + tests := []struct { + name string + sc *syslogConf + wantErr error + }{ + { + name: "valid config", + sc: &syslogConf{ + Enable: false, + Network: "udp", + Address: "localhost:514", + Tag: "kuiper", + Level: "info", + }, + wantErr: nil, + }, + { + name: "empty config", + sc: &syslogConf{}, + }, + { + name: "invalid level", + sc: &syslogConf{ + Enable: false, + Level: "warning", + }, + wantErr: errors.New("invalid syslog level: warning"), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.sc.Validate() + assert.Equal(t, tt.wantErr, err) + }) + } +} diff --git a/internal/conf/logger/logger.go b/internal/conf/logger/logger.go index 855b3f801b..f1be3ac4bf 100644 --- a/internal/conf/logger/logger.go +++ b/internal/conf/logger/logger.go @@ -38,7 +38,6 @@ func InitLogger() { return } Log = logrus.New() - initSyslog() filenameHook := filename.NewHook() filenameHook.Field = "file" Log.AddHook(filenameHook) diff --git a/internal/conf/logger/syslog.go b/internal/conf/logger/syslog.go index 6c5ffab7fb..4d34573271 100644 --- a/internal/conf/logger/syslog.go +++ b/internal/conf/logger/syslog.go @@ -1,4 +1,4 @@ -// Copyright 2021-2023 EMQ Technologies Co., Ltd. +// Copyright 2023 EMQ Technologies Co., Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -13,25 +13,37 @@ // limitations under the License. //go:build !windows -// +build !windows package logger import ( "log/syslog" - "os" logrus_syslog "github.com/sirupsen/logrus/hooks/syslog" ) const KuiperSyslogKey = "KuiperSyslogKey" -func initSyslog() { - if "true" == os.Getenv(KuiperSyslogKey) { - if hook, err := logrus_syslog.NewSyslogHook("", "", syslog.LOG_INFO, ""); err != nil { - Log.Error("Unable to connect to local syslog daemon") - } else { - Log.AddHook(hook) - } +func InitSyslog(network, address, level, tag string) error { + p := syslog.LOG_INFO + switch level { + case "debug": + p = syslog.LOG_DEBUG + case "info": + p = syslog.LOG_INFO + case "warn": + p = syslog.LOG_WARNING + case "error": + p = syslog.LOG_ERR + default: + p = syslog.LOG_INFO } + if hook, err := logrus_syslog.NewSyslogHook(network, address, p, tag); err != nil { + Log.Error("Unable to connect to local syslog daemon") + return err + } else { + Log.Infof("Setting up syslog network %s, address %s, level %s, tag %s", network, address, level, tag) + Log.AddHook(hook) + } + return nil } diff --git a/internal/conf/logger/syslog_win.go b/internal/conf/logger/syslog_win.go index 372d53bf05..83e5aaca8a 100644 --- a/internal/conf/logger/syslog_win.go +++ b/internal/conf/logger/syslog_win.go @@ -1,4 +1,4 @@ -// Copyright 2021-2023 EMQ Technologies Co., Ltd. +// Copyright 2023 EMQ Technologies Co., Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -13,10 +13,9 @@ // limitations under the License. //go:build windows -// +build windows package logger -func initSyslog() { - // Not supported in windows, do nothing. +func InitSyslog(network, address, level, tag string) error { + Log.Warnf("Syslog is not supported on windows") }