Skip to content

Commit 50fdefa

Browse files
feat: Optionally don't parse attributes for bindplaneauditlogsreceiver (PIPE-719) (#3079)
* raw option for audit logs * feedback
1 parent b9430c7 commit 50fdefa

6 files changed

Lines changed: 92 additions & 25 deletions

File tree

receiver/bindplaneauditlogs/README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@ This receiver is capable of collecting audit logs from a Bindplane instance.
2525

2626
## Configuration
2727

28-
| Field | Type | Default | Required | Description |
29-
| ------------- | ------ | ------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
30-
| api_key | string | | `true` | The Bindplane API key with read access to audit logs. This API key has access to the audit logs of a single project. |
31-
| endpoint | string | | `true` | The endpoint to collect logs from. (e.g. `https://app.bindplane.com`) |
32-
| poll_interval | string | 10s | `false` | The rate at which this receiver will poll Bindplane for logs. This value must be in the range [10 seconds - 24 hours] and must be a string readable by Golang's [time.ParseDuration](https://pkg.go.dev/time#ParseDuration). |
28+
| Field | Type | Default | Required | Description |
29+
| ---------------- | ------ | ------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
30+
| api_key | string | | `true` | The Bindplane API key with read access to audit logs. This API key has access to the audit logs of a single project. |
31+
| endpoint | string | | `true` | The endpoint to collect logs from. (e.g. `https://app.bindplane.com`) |
32+
| poll_interval | string | 10s | `false` | The rate at which this receiver will poll Bindplane for logs. This value must be in the range [10 seconds - 24 hours] and must be a string readable by Golang's [time.ParseDuration](https://pkg.go.dev/time#ParseDuration). |
33+
| parse_attributes | bool | true | `false` | When true, parses audit log fields into log record attributes and sets the body to the description. When false, sets the body to the raw JSON event without attributes. |
3334

3435
### Example Configuration
3536

receiver/bindplaneauditlogs/config.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ type Config struct {
3636
// PollInterval is the interval at which the receiver polls for new audit logs
3737
PollInterval time.Duration `mapstructure:"poll_interval"`
3838

39+
// ParseAttributes when true parses the audit log fields into log record attributes
40+
// and sets the body to the description. When false, the body is set to the raw JSON event.
41+
ParseAttributes bool `mapstructure:"parse_attributes"`
42+
3943
// bindplaneURL is the URL to the BindPlane audit logs API. Taken from the client config endpoint.
4044
bindplaneURL *url.URL
4145
}

receiver/bindplaneauditlogs/config_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,9 @@ func TestValidate(t *testing.T) {
9898
})
9999
}
100100
}
101+
102+
func TestCreateDefaultConfig(t *testing.T) {
103+
cfg := createDefaultConfig().(*Config)
104+
require.Equal(t, true, cfg.ParseAttributes)
105+
require.Equal(t, 10*time.Second, cfg.PollInterval)
106+
}

receiver/bindplaneauditlogs/factory.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ func createLogsReceiver(
4545

4646
func createDefaultConfig() component.Config {
4747
return &Config{
48-
PollInterval: 10 * time.Second,
48+
PollInterval: 10 * time.Second,
49+
ParseAttributes: true,
4950
}
5051
}

receiver/bindplaneauditlogs/logs.go

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -197,27 +197,35 @@ func (r *bindplaneAuditLogsReceiver) processLogEvents(observedTime pcommon.Times
197197
logRecord.SetTimestamp(pcommon.NewTimestampFromTime(*logEvent.Timestamp))
198198
}
199199

200-
// Set body as description
201-
if logEvent.Description != "" {
202-
logRecord.Body().SetStr(logEvent.Description)
203-
}
200+
// Only set attributes if parsing is enabled
201+
if r.cfg.ParseAttributes {
202+
//
203+
if logEvent.Description != "" {
204+
logRecord.Body().SetStr(logEvent.Description)
205+
}
206+
// Set attributes based on the Bindplane audit log format
207+
attrs := logRecord.Attributes()
208+
attrs.PutStr("id", logEvent.ID)
209+
if logEvent.Timestamp != nil {
210+
attrs.PutStr("timestamp", logEvent.Timestamp.Format(bindplaneTimeFormat))
211+
}
212+
attrs.PutStr("resource_name", logEvent.ResourceName)
213+
attrs.PutStr("resource_kind", string(logEvent.ResourceKind))
214+
if logEvent.Configuration != "" {
215+
attrs.PutStr("configuration", logEvent.Configuration)
216+
}
217+
attrs.PutStr("action", string(logEvent.Action))
218+
attrs.PutStr("user", logEvent.User)
204219

205-
// Set attributes based on the Bindplane audit log format
206-
attrs := logRecord.Attributes()
207-
attrs.PutStr("id", logEvent.ID)
208-
if logEvent.Timestamp != nil {
209-
attrs.PutStr("timestamp", logEvent.Timestamp.Format(bindplaneTimeFormat))
210-
}
211-
attrs.PutStr("resource_name", logEvent.ResourceName)
212-
attrs.PutStr("resource_kind", string(logEvent.ResourceKind))
213-
if logEvent.Configuration != "" {
214-
attrs.PutStr("configuration", logEvent.Configuration)
220+
resourceAttrs.PutStr("account", logEvent.Account)
221+
} else {
222+
eventBytes, err := json.Marshal(logEvent)
223+
if err != nil {
224+
r.logger.Error("unable to marshal logEvent", zap.Error(err))
225+
} else {
226+
logRecord.Body().SetStr(string(eventBytes))
227+
}
215228
}
216-
attrs.PutStr("action", string(logEvent.Action))
217-
attrs.PutStr("user", logEvent.User)
218-
219-
resourceAttrs.PutStr("account", logEvent.Account)
220-
221229
}
222230

223231
return logs

receiver/bindplaneauditlogs/logs_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,9 @@ func TestProcessLogEvents(t *testing.T) {
224224
require.Equal(t, 1, logs.LogRecordCount())
225225
logRecord := logs.ResourceLogs().At(0).ScopeLogs().At(0).LogRecords().At(0)
226226

227+
// Verify body is description by default
228+
require.Equal(t, "test description", logRecord.Body().Str())
229+
227230
// Verify attributes
228231
attrs := logRecord.Attributes()
229232
id, _ := attrs.Get("id")
@@ -232,6 +235,50 @@ func TestProcessLogEvents(t *testing.T) {
232235
require.Equal(t, "test-resource", resourceName.Str())
233236
}
234237

238+
func TestProcessLogEventsNoParseAttributes(t *testing.T) {
239+
cfg := createDefaultConfig().(*Config)
240+
cfg.ParseAttributes = false
241+
recv := newReceiver(t, cfg, consumertest.NewNop())
242+
243+
now := time.Now().UTC()
244+
testEvents := []AuditLogEvent{
245+
{
246+
ID: "1",
247+
Timestamp: &now,
248+
ResourceName: "test-resource",
249+
Description: "test description",
250+
ResourceKind: "Source",
251+
Configuration: "test-config",
252+
Action: "Created",
253+
User: "test-user",
254+
Account: "test-account",
255+
},
256+
}
257+
258+
logs := recv.processLogEvents(pcommon.NewTimestampFromTime(now), testEvents)
259+
260+
require.Equal(t, 1, logs.LogRecordCount())
261+
logRecord := logs.ResourceLogs().At(0).ScopeLogs().At(0).LogRecords().At(0)
262+
263+
// Verify body is raw JSON when ParseAttributes is disabled
264+
body := logRecord.Body().Str()
265+
require.Contains(t, body, `"id":"1"`)
266+
require.Contains(t, body, `"resourceName":"test-resource"`)
267+
require.Contains(t, body, `"description":"test description"`)
268+
require.Contains(t, body, `"resourceKind":"Source"`)
269+
require.Contains(t, body, `"configuration":"test-config"`)
270+
require.Contains(t, body, `"action":"Created"`)
271+
require.Contains(t, body, `"user":"test-user"`)
272+
require.Contains(t, body, `"account":"test-account"`)
273+
274+
// Verify the body can be unmarshaled back to an event
275+
var parsedEvent AuditLogEvent
276+
err := json.Unmarshal([]byte(body), &parsedEvent)
277+
require.NoError(t, err)
278+
require.Equal(t, "1", parsedEvent.ID)
279+
require.Equal(t, "test-resource", parsedEvent.ResourceName)
280+
}
281+
235282
func TestLastTimestampUpdate(t *testing.T) {
236283
cfg := createDefaultConfig().(*Config)
237284
cfg.bindplaneURL = &url.URL{

0 commit comments

Comments
 (0)