diff --git a/exporter/chronicleexporter/exporter.go b/exporter/chronicleexporter/exporter.go index 9ced6219c..2e515cf20 100644 --- a/exporter/chronicleexporter/exporter.go +++ b/exporter/chronicleexporter/exporter.go @@ -257,8 +257,8 @@ func (ce *chronicleExporter) logsHTTPDataPusher(ctx context.Context, ld plog.Log return fmt.Errorf("marshal logs: %w", err) } - for _, payload := range payloads { - if err := ce.uploadToChronicleHTTP(ctx, payload); err != nil { + for logType, payload := range payloads { + if err := ce.uploadToChronicleHTTP(ctx, payload, logType); err != nil { return fmt.Errorf("upload to chronicle: %w", err) } } @@ -268,14 +268,13 @@ func (ce *chronicleExporter) logsHTTPDataPusher(ctx context.Context, ld plog.Log // This uses the DataPlane URL for the request // URL for the request: https://{region}-chronicle.googleapis.com/{version}/projects/{project}/location/{region}/instances/{customerID}/logTypes/{logtype}/logs:import -func buildEndpoint(cfg *Config) string { - // TODO handle override of LogType +func buildEndpoint(cfg *Config, logType string) string { // Location Endpoint Version Project Location Instance LogType formatString := "https://%s-%s/%s/projects/%s/locations/%s/instances/%s/logTypes/%s/logs:import" - return fmt.Sprintf(formatString, cfg.Location, cfg.Endpoint, "v1alpha", cfg.Project, cfg.Location, cfg.CustomerID, cfg.LogType) + return fmt.Sprintf(formatString, cfg.Location, cfg.Endpoint, "v1alpha", cfg.Project, cfg.Location, cfg.CustomerID, logType) } -func (ce *chronicleExporter) uploadToChronicleHTTP(ctx context.Context, logs *api.ImportLogsRequest) error { +func (ce *chronicleExporter) uploadToChronicleHTTP(ctx context.Context, logs *api.ImportLogsRequest, logType string) error { data, err := protojson.Marshal(logs) if err != nil { @@ -298,7 +297,7 @@ func (ce *chronicleExporter) uploadToChronicleHTTP(ctx context.Context, logs *ap body = bytes.NewBuffer(data) } - request, err := http.NewRequestWithContext(ctx, "POST", buildEndpoint(ce.cfg), body) + request, err := http.NewRequestWithContext(ctx, "POST", buildEndpoint(ce.cfg, logType), body) if err != nil { return fmt.Errorf("create request: %w", err) } diff --git a/exporter/chronicleexporter/marshal.go b/exporter/chronicleexporter/marshal.go index d7ed07390..1ca1c0c92 100644 --- a/exporter/chronicleexporter/marshal.go +++ b/exporter/chronicleexporter/marshal.go @@ -48,7 +48,7 @@ var supportedLogTypes = map[string]string{ //go:generate mockery --name logMarshaler --filename mock_log_marshaler.go --structname MockMarshaler --inpackage type logMarshaler interface { MarshalRawLogs(ctx context.Context, ld plog.Logs) ([]*api.BatchCreateLogsRequest, error) - MarshalRawLogsForHTTP(ctx context.Context, ld plog.Logs) ([]*api.ImportLogsRequest, error) + MarshalRawLogsForHTTP(ctx context.Context, ld plog.Logs) (map[string]*api.ImportLogsRequest, error) } type protoMarshaler struct { @@ -226,7 +226,7 @@ func (m *protoMarshaler) constructPayloads(rawLogs map[string][]*api.LogEntry) [ return payloads } -func (m *protoMarshaler) MarshalRawLogsForHTTP(ctx context.Context, ld plog.Logs) ([]*api.ImportLogsRequest, error) { +func (m *protoMarshaler) MarshalRawLogsForHTTP(ctx context.Context, ld plog.Logs) (map[string]*api.ImportLogsRequest, error) { rawLogs, err := m.extractRawHTTPLogs(ctx, ld) if err != nil { return nil, fmt.Errorf("extract raw logs: %w", err) @@ -279,15 +279,15 @@ func buildForwarderString(cfg Config) string { return fmt.Sprintf(format, cfg.Project, cfg.Location, cfg.CustomerID, cfg.Forwarder) } -func (m *protoMarshaler) constructHTTPPayloads(rawLogs map[string][]*api.Log) []*api.ImportLogsRequest { - payloads := make([]*api.ImportLogsRequest, 0, len(rawLogs)) +func (m *protoMarshaler) constructHTTPPayloads(rawLogs map[string][]*api.Log) map[string]*api.ImportLogsRequest { + payloads := make(map[string]*api.ImportLogsRequest, len(rawLogs)) - // TODO logType - for _, entries := range rawLogs { + for logType, entries := range rawLogs { if len(entries) > 0 { - payloads = append(payloads, + payloads[logType] = &api.ImportLogsRequest{ // TODO: Add parent and hint + // We don't yet have solid guidance on what these should be Parent: "", Hint: "", @@ -297,8 +297,7 @@ func (m *protoMarshaler) constructHTTPPayloads(rawLogs map[string][]*api.Log) [] Logs: entries, }, }, - }, - ) + } } } return payloads diff --git a/exporter/chronicleexporter/marshal_test.go b/exporter/chronicleexporter/marshal_test.go index 38e4c7a5e..a3b9afec1 100644 --- a/exporter/chronicleexporter/marshal_test.go +++ b/exporter/chronicleexporter/marshal_test.go @@ -201,7 +201,7 @@ func TestProtoMarshaler_MarshalRawLogsForHTTP(t *testing.T) { cfg Config labels []*api.Label logRecords func() plog.Logs - expectations func(t *testing.T, requests []*api.ImportLogsRequest) + expectations func(t *testing.T, requests map[string]*api.ImportLogsRequest) }{ { name: "Single log record with expected data", @@ -221,9 +221,9 @@ func TestProtoMarshaler_MarshalRawLogsForHTTP(t *testing.T) { logRecords: func() plog.Logs { return mockLogs(mockLogRecord("Test log message", map[string]any{"log_type": "WINEVTLOG"})) }, - expectations: func(t *testing.T, requests []*api.ImportLogsRequest) { + expectations: func(t *testing.T, requests map[string]*api.ImportLogsRequest) { require.Len(t, requests, 1) - logs := requests[0].GetInlineSource().Logs + logs := requests["WINEVTLOG"].GetInlineSource().Logs require.Len(t, logs, 1) // Convert Data (byte slice) to string for comparison @@ -251,9 +251,9 @@ func TestProtoMarshaler_MarshalRawLogsForHTTP(t *testing.T) { record2.Body().SetStr("Second log message") return logs }, - expectations: func(t *testing.T, requests []*api.ImportLogsRequest) { + expectations: func(t *testing.T, requests map[string]*api.ImportLogsRequest) { require.Len(t, requests, 1, "Expected a single batch request") - logs := requests[0].GetInlineSource().Logs + logs := requests["WINEVTLOG"].GetInlineSource().Logs require.Len(t, logs, 2, "Expected two log entries in the batch") // Verifying the first log entry data require.Equal(t, "First log message", string(logs[0].Data)) @@ -273,9 +273,9 @@ func TestProtoMarshaler_MarshalRawLogsForHTTP(t *testing.T) { logRecords: func() plog.Logs { return mockLogs(mockLogRecord("", map[string]any{"key1": "value1", "log_type": "WINEVTLOG"})) }, - expectations: func(t *testing.T, requests []*api.ImportLogsRequest) { + expectations: func(t *testing.T, requests map[string]*api.ImportLogsRequest) { require.Len(t, requests, 1) - logs := requests[0].GetInlineSource().Logs + logs := requests["WINEVTLOG"].GetInlineSource().Logs require.Len(t, logs, 1) // Assuming the attributes are marshaled into the Data field as a JSON string @@ -296,7 +296,7 @@ func TestProtoMarshaler_MarshalRawLogsForHTTP(t *testing.T) { logRecords: func() plog.Logs { return plog.NewLogs() // No log records added }, - expectations: func(t *testing.T, requests []*api.ImportLogsRequest) { + expectations: func(t *testing.T, requests map[string]*api.ImportLogsRequest) { require.Len(t, requests, 0, "Expected no requests due to no log records") }, }, diff --git a/exporter/chronicleexporter/mock_log_marshaler.go b/exporter/chronicleexporter/mock_log_marshaler.go index 8f4294577..0467bd516 100644 --- a/exporter/chronicleexporter/mock_log_marshaler.go +++ b/exporter/chronicleexporter/mock_log_marshaler.go @@ -48,23 +48,23 @@ func (_m *MockMarshaler) MarshalRawLogs(ctx context.Context, ld plog.Logs) ([]*a } // MarshalRawLogsForHTTP provides a mock function with given fields: ctx, ld -func (_m *MockMarshaler) MarshalRawLogsForHTTP(ctx context.Context, ld plog.Logs) ([]*api.ImportLogsRequest, error) { +func (_m *MockMarshaler) MarshalRawLogsForHTTP(ctx context.Context, ld plog.Logs) (map[string]*api.ImportLogsRequest, error) { ret := _m.Called(ctx, ld) if len(ret) == 0 { panic("no return value specified for MarshalRawLogsForHTTP") } - var r0 []*api.ImportLogsRequest + var r0 map[string]*api.ImportLogsRequest var r1 error - if rf, ok := ret.Get(0).(func(context.Context, plog.Logs) ([]*api.ImportLogsRequest, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, plog.Logs) (map[string]*api.ImportLogsRequest, error)); ok { return rf(ctx, ld) } - if rf, ok := ret.Get(0).(func(context.Context, plog.Logs) []*api.ImportLogsRequest); ok { + if rf, ok := ret.Get(0).(func(context.Context, plog.Logs) map[string]*api.ImportLogsRequest); ok { r0 = rf(ctx, ld) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]*api.ImportLogsRequest) + r0 = ret.Get(0).(map[string]*api.ImportLogsRequest) } }