diff --git a/docs/exporters.md b/docs/exporters.md index 17eeb372a..c7a9fe46a 100644 --- a/docs/exporters.md +++ b/docs/exporters.md @@ -14,6 +14,7 @@ Below is a list of supported exporters with links to their documentation pages. | Azure Monitor Exporter | [azuremonitorexporter](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/v0.91.0/exporter/azuremonitorexporter/README.md) | | Carbon Exporter | [carbonexporter](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/v0.91.0/exporter/carbonexporter/README.md) | | Chronicle Exporter | [chronicleexporter](../exporter/chronicleexporter/README.md) | +| Chronicle Forwarder Exporter | [chronicleexporter](../exporter/chronicleforwarderexporter/README.md) | | ClickHouse Exporter | [clickhouseexporter](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/v0.91.0/exporter/clickhouseexporter/README.md) | | Coralogix Exporter | [coralogixexporter](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/v0.91.0/exporter/coralogixexporter/README.md) | | Datadog Exporter | [datadogexporter](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/v0.91.0/exporter/datadogexporter/README.md) | diff --git a/exporter/chronicleforwarderexporter/README.md b/exporter/chronicleforwarderexporter/README.md index 80eb3557c..3f8f82e02 100644 --- a/exporter/chronicleforwarderexporter/README.md +++ b/exporter/chronicleforwarderexporter/README.md @@ -1,6 +1,6 @@ # Chronicle Forwarder Exporter -The Chronicle Forwarder Exporter is designed for forwarding logs to a Chronicle endpoint using either Syslog or File-based methods. This exporter supports customization of data export types and various configuration options to tailor the connection and data handling to specific needs. +The Chronicle Forwarder Exporter is designed for forwarding logs to a Chronicle Forwarder endpoint using either Syslog or File-based methods. This exporter supports customization of data export types and various configuration options to tailor the connection and data handling to specific needs. ## Minimum Agent Versions diff --git a/exporter/chronicleforwarderexporter/config.go b/exporter/chronicleforwarderexporter/config.go index dbef5efb5..447646140 100644 --- a/exporter/chronicleforwarderexporter/config.go +++ b/exporter/chronicleforwarderexporter/config.go @@ -15,6 +15,7 @@ package chronicleforwarderexporter import ( + "errors" "fmt" "github.com/observiq/bindplane-agent/expr" @@ -26,11 +27,11 @@ import ( ) const ( - // ExportTypeSyslog is the syslog export type. - ExportTypeSyslog = "syslog" + // exportTypeSyslog is the syslog export type. + exportTypeSyslog = "syslog" - // ExportTypeFile is the file export type. - ExportTypeFile = "file" + // exportTypeFile is the file export type. + exportTypeFile = "file" ) // Config defines configuration for the Chronicle exporter. @@ -66,21 +67,37 @@ type File struct { Path string `mapstructure:"path"` } +// Validate validates the Syslog configuration. +func (s *SyslogConfig) Validate() error { + if s.NetAddr.Endpoint == "" { + return errors.New("incomplete syslog configuration: endpoint is required") + } + return nil +} + +// Validate validates the File configuration. +func (f *File) Validate() error { + if f.Path == "" { + return errors.New("file path is required for file export type") + } + return nil +} + // Validate validates the Chronicle exporter configuration. func (cfg *Config) Validate() error { - if cfg.ExportType != ExportTypeSyslog && cfg.ExportType != ExportTypeFile { - return fmt.Errorf("export_type must be either 'syslog' or 'file'") + if cfg.ExportType != exportTypeSyslog && cfg.ExportType != exportTypeFile { + return errors.New("export_type must be either 'syslog' or 'file'") } - if cfg.ExportType == ExportTypeSyslog { - if cfg.Syslog.NetAddr.Endpoint == "" { - return fmt.Errorf("incomplete syslog configuration: endpoint is required") + if cfg.ExportType == exportTypeSyslog { + if err := cfg.Syslog.Validate(); err != nil { + return err } } - if cfg.ExportType == ExportTypeFile { - if cfg.File.Path == "" { - return fmt.Errorf("file path is required for file export type") + if cfg.ExportType == exportTypeFile { + if err := cfg.File.Validate(); err != nil { + return err } } diff --git a/exporter/chronicleforwarderexporter/config_test.go b/exporter/chronicleforwarderexporter/config_test.go index f1cff2890..6fb5f809f 100644 --- a/exporter/chronicleforwarderexporter/config_test.go +++ b/exporter/chronicleforwarderexporter/config_test.go @@ -30,7 +30,7 @@ func TestConfig_Validate(t *testing.T) { { name: "Valid syslog config", cfg: Config{ - ExportType: ExportTypeSyslog, + ExportType: exportTypeSyslog, Syslog: SyslogConfig{ NetAddr: confignet.NetAddr{ Endpoint: "localhost:514", @@ -43,7 +43,7 @@ func TestConfig_Validate(t *testing.T) { { name: "Invalid syslog config - missing host", cfg: Config{ - ExportType: ExportTypeSyslog, + ExportType: exportTypeSyslog, Syslog: SyslogConfig{ NetAddr: confignet.NetAddr{ Endpoint: "", @@ -56,7 +56,7 @@ func TestConfig_Validate(t *testing.T) { { name: "Valid file config", cfg: Config{ - ExportType: ExportTypeFile, + ExportType: exportTypeFile, File: File{ Path: "/path/to/file", }, @@ -66,7 +66,7 @@ func TestConfig_Validate(t *testing.T) { { name: "Invalid file config - missing path", cfg: Config{ - ExportType: ExportTypeFile, + ExportType: exportTypeFile, File: File{}, }, wantErr: true, diff --git a/exporter/chronicleforwarderexporter/exporter.go b/exporter/chronicleforwarderexporter/exporter.go index e4c528114..486b8bf46 100644 --- a/exporter/chronicleforwarderexporter/exporter.go +++ b/exporter/chronicleforwarderexporter/exporter.go @@ -17,6 +17,7 @@ package chronicleforwarderexporter import ( "context" "crypto/tls" + "errors" "fmt" "io" "net" @@ -71,34 +72,39 @@ func (ce *chronicleForwarderExporter) logsDataPusher(ctx context.Context, ld plo func (ce *chronicleForwarderExporter) openWriter() error { switch ce.cfg.ExportType { - case ExportTypeSyslog: - var conn net.Conn - var err error - if ce.cfg.Syslog.TLSSetting != nil { - tlsConfig, err := ce.cfg.Syslog.TLSSetting.LoadTLSConfig() - if err != nil { - return fmt.Errorf("load TLS config: %w", err) - } - conn, err = tls.Dial(ce.cfg.Syslog.NetAddr.Transport, ce.cfg.Syslog.NetAddr.Endpoint, tlsConfig) - } else { - conn, err = net.Dial(ce.cfg.Syslog.NetAddr.Transport, ce.cfg.Syslog.NetAddr.Endpoint) - } + case exportTypeSyslog: + return ce.openSyslogWriter() + case exportTypeFile: + return ce.openFileWriter() + default: + return errors.New("unsupported export type") + } +} - if err != nil { - return fmt.Errorf("dial: %w", err) - } - ce.writer = conn +func (ce *chronicleForwarderExporter) openFileWriter() error { + var err error + ce.writer, err = os.OpenFile(ce.cfg.File.Path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600) + return err +} - case ExportTypeFile: - var err error - ce.writer, err = os.OpenFile(ce.cfg.File.Path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600) +func (ce *chronicleForwarderExporter) openSyslogWriter() error { + var conn net.Conn + var err error + if ce.cfg.Syslog.TLSSetting != nil { + tlsConfig, err := ce.cfg.Syslog.TLSSetting.LoadTLSConfig() if err != nil { - return fmt.Errorf("open file: %w", err) + return fmt.Errorf("load TLS config: %w", err) } - default: - return fmt.Errorf("unsupported export type") + conn, err = tls.Dial(ce.cfg.Syslog.NetAddr.Transport, ce.cfg.Syslog.NetAddr.Endpoint, tlsConfig) + } else { + conn, err = net.Dial(ce.cfg.Syslog.NetAddr.Transport, ce.cfg.Syslog.NetAddr.Endpoint) } - return nil + + if err != nil { + return fmt.Errorf("dial: %w", err) + } + ce.writer = conn + return err } func (ce *chronicleForwarderExporter) send(msg string) error { diff --git a/exporter/chronicleforwarderexporter/exporter_test.go b/exporter/chronicleforwarderexporter/exporter_test.go index 8b52151f0..5cc3372da 100644 --- a/exporter/chronicleforwarderexporter/exporter_test.go +++ b/exporter/chronicleforwarderexporter/exporter_test.go @@ -41,7 +41,7 @@ func TestLogDataPushingFile(t *testing.T) { defer os.Remove(f.Name()) // Clean up the file afterwards cfg := &Config{ - ExportType: ExportTypeFile, + ExportType: exportTypeFile, File: File{ Path: f.Name(), }, @@ -73,20 +73,28 @@ func TestLogDataPushingNetwork(t *testing.T) { require.NoError(t, err) defer ln.Close() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + go func() { for { - conn, err := ln.Accept() - if err != nil { - log.Println("Error accepting connection:", err) + select { + case <-ctx.Done(): return + default: + conn, err := ln.Accept() + if err != nil { + log.Println("Error accepting connection:", err) + return + } + go handleSyslogConnection(t, conn, logReceived) } - go handleSyslogConnection(t, conn, logReceived) } }() // Configure the exporter to use the mock Syslog server cfg := &Config{ - ExportType: ExportTypeSyslog, + ExportType: exportTypeSyslog, Syslog: SyslogConfig{ NetAddr: confignet.NetAddr{ Endpoint: ln.Addr().String(), diff --git a/exporter/chronicleforwarderexporter/factory.go b/exporter/chronicleforwarderexporter/factory.go index 2911666f0..bfba49a68 100644 --- a/exporter/chronicleforwarderexporter/factory.go +++ b/exporter/chronicleforwarderexporter/factory.go @@ -39,7 +39,7 @@ func createDefaultConfig() component.Config { TimeoutSettings: exporterhelper.NewDefaultTimeoutSettings(), QueueSettings: exporterhelper.NewDefaultQueueSettings(), RetrySettings: exporterhelper.NewDefaultRetrySettings(), - ExportType: ExportTypeSyslog, + ExportType: exportTypeSyslog, Syslog: SyslogConfig{ NetAddr: confignet.NetAddr{ Endpoint: "127.0.0.1:10514", diff --git a/exporter/chronicleforwarderexporter/factory_test.go b/exporter/chronicleforwarderexporter/factory_test.go index b3257ee3f..7a9fa4c76 100644 --- a/exporter/chronicleforwarderexporter/factory_test.go +++ b/exporter/chronicleforwarderexporter/factory_test.go @@ -27,7 +27,7 @@ func Test_createDefaultConfig(t *testing.T) { TimeoutSettings: exporterhelper.NewDefaultTimeoutSettings(), QueueSettings: exporterhelper.NewDefaultQueueSettings(), RetrySettings: exporterhelper.NewDefaultRetrySettings(), - ExportType: ExportTypeSyslog, + ExportType: exportTypeSyslog, Syslog: SyslogConfig{ NetAddr: confignet.NetAddr{ Endpoint: "127.0.0.1:10514", diff --git a/exporter/chronicleforwarderexporter/go.mod b/exporter/chronicleforwarderexporter/go.mod index 76273fd2c..39ad24a6f 100644 --- a/exporter/chronicleforwarderexporter/go.mod +++ b/exporter/chronicleforwarderexporter/go.mod @@ -7,7 +7,7 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl v0.91.0 github.com/stretchr/testify v1.8.4 go.opentelemetry.io/collector/component v0.91.0 - go.opentelemetry.io/collector/config/configtls v0.90.0 + go.opentelemetry.io/collector/config/configtls v0.91.0 go.opentelemetry.io/collector/consumer v0.91.0 go.opentelemetry.io/collector/exporter v0.91.0 go.opentelemetry.io/collector/pdata v1.0.0 @@ -16,7 +16,7 @@ require ( require ( github.com/fsnotify/fsnotify v1.7.0 // indirect - go.opentelemetry.io/collector/config/configopaque v0.90.0 // indirect + go.opentelemetry.io/collector/config/configopaque v0.91.0 // indirect ) require ( diff --git a/exporter/chronicleforwarderexporter/go.sum b/exporter/chronicleforwarderexporter/go.sum index ad65498bc..f37dab312 100644 --- a/exporter/chronicleforwarderexporter/go.sum +++ b/exporter/chronicleforwarderexporter/go.sum @@ -120,12 +120,12 @@ go.opentelemetry.io/collector/component v0.91.0 h1:aBT1i2zGyfh9PalYJLfXVvQp+osHy go.opentelemetry.io/collector/component v0.91.0/go.mod h1:2KBHvjNFdU7oOjsObQeC4Ta2Ef607OISU5obznW00fw= go.opentelemetry.io/collector/config/confignet v0.91.0 h1:3huNXh04O3wXaN4qPhmmiefyz4dYbOlNcR/OKMByqig= go.opentelemetry.io/collector/config/confignet v0.91.0/go.mod h1:cpO8JYWGONaViOygKVw+Hd2UoBcn2cUiyi0WWeFTwJY= -go.opentelemetry.io/collector/config/configopaque v0.90.0 h1:tnuwVWaKbPIhgLawcU4xnex53tJbQsecNq86eZRz1rE= -go.opentelemetry.io/collector/config/configopaque v0.90.0/go.mod h1:TPCHaU+QXiEV+JXbgyr6mSErTI9chwQyasDVMdJr3eY= +go.opentelemetry.io/collector/config/configopaque v0.91.0 h1:bQgJPyARbuXAsU2p6h2YbEm1kHb1stS6hg42ekyMZmI= +go.opentelemetry.io/collector/config/configopaque v0.91.0/go.mod h1:TPCHaU+QXiEV+JXbgyr6mSErTI9chwQyasDVMdJr3eY= go.opentelemetry.io/collector/config/configtelemetry v0.91.0 h1:mEwvqrYfwUJ7LwYfpcF9M8z7LHFoYaKhEPhnERD/88E= go.opentelemetry.io/collector/config/configtelemetry v0.91.0/go.mod h1:+LAXM5WFMW/UbTlAuSs6L/W72WC+q8TBJt/6z39FPOU= -go.opentelemetry.io/collector/config/configtls v0.90.0 h1:bsPZkh5ejlIk/XwLdzz91empM3STU8xr6yArqMVYxJ4= -go.opentelemetry.io/collector/config/configtls v0.90.0/go.mod h1:eLLgpNPxHAtAynKCJN7p9O7GIDEIRKfjsFJs3BQazyg= +go.opentelemetry.io/collector/config/configtls v0.91.0 h1:lZromNeOslPwyVlTPMOzF2q++SY+VONvfH3cDqA0kKk= +go.opentelemetry.io/collector/config/configtls v0.91.0/go.mod h1:E+CW5gZoH8V3z5aSlZxwiof7GAcayzn1HRM+uRILLEI= go.opentelemetry.io/collector/confmap v0.91.0 h1:7U2MT+u74oEzq/WWrpXSLKB7nX5jPNC4drwtQdYfwKk= go.opentelemetry.io/collector/confmap v0.91.0/go.mod h1:uxV+fZ85kG31oovL6Cl3fAMQ3RRPwUvfAbbA9WT1Yhk= go.opentelemetry.io/collector/consumer v0.91.0 h1:0nU1lUe2S0b8iOmF3w3R/9Dt24n413thRTbXz/nJgrM=