Skip to content

Commit fc16a1d

Browse files
committed
Add oxide host as a resource attribute.
Useful when collecting metrics/logs from multiple oxide racks. Metrics and logs should also include a rack id, but we should also include a human-readable label identifying the rack.
1 parent 33b5e52 commit fc16a1d

File tree

5 files changed

+72
-20
lines changed

5 files changed

+72
-20
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ tool (
99

1010
require (
1111
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.139.0
12-
github.com/oxidecomputer/oxide.go v0.8.1-0.20260304174018-b0f8ba0c764c
12+
github.com/oxidecomputer/oxide.go v0.8.1-0.20260319134325-8a08d5f59e23
1313
github.com/stretchr/testify v1.11.1
1414
go.opentelemetry.io/collector/component v1.45.0
1515
go.opentelemetry.io/collector/component/componenttest v0.139.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,8 @@ github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT9
493493
github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
494494
github.com/oxidecomputer/oxide.go v0.8.1-0.20260304174018-b0f8ba0c764c h1:DfIxEF0z8prXMmYYDuRa4gTUXPHZ+4EYb3pc4iKqg+A=
495495
github.com/oxidecomputer/oxide.go v0.8.1-0.20260304174018-b0f8ba0c764c/go.mod h1:pL9BcSmHMyhR8Utxr2AcV6wG59OIrcSV3dNduIzGGdo=
496+
github.com/oxidecomputer/oxide.go v0.8.1-0.20260319134325-8a08d5f59e23 h1:2CoVXnrabOEEJMYs7a4I6/Sagqj1hrrOwZWj0M1c6j8=
497+
github.com/oxidecomputer/oxide.go v0.8.1-0.20260319134325-8a08d5f59e23/go.mod h1:I36KqKtLBuKdi9HeXRwP2FHc7s98+HvYgFozvfyKnpE=
496498
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
497499
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
498500
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=

receiver/oxideauditlogsreceiver/scraper.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"encoding/json"
66
"errors"
77
"fmt"
8+
"net/url"
89
"os"
910
"path/filepath"
1011
"time"
@@ -36,6 +37,7 @@ type auditLogClient interface {
3637
AuditLogList(ctx context.Context, params oxide.AuditLogListParams) (
3738
*oxide.AuditLogEntryResultsPage, error,
3839
)
40+
Host() string
3941
}
4042

4143
// scraperMetrics holds metrics about the log scraper.
@@ -122,6 +124,15 @@ func (s *auditLogScraper) Start(_ context.Context, _ component.Host) error {
122124

123125
func (s *auditLogScraper) Scrape(ctx context.Context) (plog.Logs, error) {
124126
logs := plog.NewLogs()
127+
resource := logs.ResourceLogs().AppendEmpty()
128+
attrs := resource.Resource().Attributes()
129+
attrs.PutStr("service.name", "oxide")
130+
host := s.client.Host()
131+
if u, err := url.Parse(host); err == nil && u.Host != "" {
132+
host = u.Host
133+
}
134+
attrs.PutStr("oxide.host", host)
135+
scope := resource.ScopeLogs().AppendEmpty()
125136
startTime := time.Now()
126137

127138
params := oxide.AuditLogListParams{
@@ -157,7 +168,7 @@ func (s *auditLogScraper) Scrape(ctx context.Context) (plog.Logs, error) {
157168
continue
158169
}
159170

160-
if err := addLogRecord(logs, entry, startTime); err != nil {
171+
if err := addLogRecord(scope, entry, startTime); err != nil {
161172
return logs, fmt.Errorf("adding log record: %w", err)
162173
}
163174

@@ -260,7 +271,7 @@ func (s *auditLogScraper) saveCursor() {
260271
}
261272
}
262273

263-
func addLogRecord(logs plog.Logs, entry oxide.AuditLogEntry, observedTime time.Time) error {
274+
func addLogRecord(scope plog.ScopeLogs, entry oxide.AuditLogEntry, observedTime time.Time) error {
264275
raw, err := json.Marshal(entry)
265276
if err != nil {
266277
return fmt.Errorf("marshaling audit log entry %s: %w", entry.Id, err)
@@ -270,10 +281,6 @@ func addLogRecord(logs plog.Logs, entry oxide.AuditLogEntry, observedTime time.T
270281
return fmt.Errorf("unmarshaling audit log entry %s: %w", entry.Id, err)
271282
}
272283

273-
resource := logs.ResourceLogs().AppendEmpty()
274-
resource.Resource().Attributes().PutStr("service.name", "oxide")
275-
276-
scope := resource.ScopeLogs().AppendEmpty()
277284
log := scope.LogRecords().AppendEmpty()
278285

279286
if entry.TimeStarted != nil {

receiver/oxideauditlogsreceiver/scraper_test.go

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -67,21 +67,13 @@ func TestAddLogRecord(t *testing.T) {
6767
}
6868

6969
logs := plog.NewLogs()
70+
scope := logs.ResourceLogs().AppendEmpty().ScopeLogs().AppendEmpty()
7071
observedTime := now.Add(time.Minute)
71-
require.NoError(t, addLogRecord(logs, entry, observedTime))
72-
73-
require.Equal(t, 1, logs.ResourceLogs().Len())
74-
resource := logs.ResourceLogs().At(0)
75-
76-
// Check resource attributes.
77-
serviceName, ok := resource.Resource().Attributes().Get("service.name")
78-
require.True(t, ok)
79-
require.Equal(t, "oxide", serviceName.Str())
72+
require.NoError(t, addLogRecord(scope, entry, observedTime))
8073

8174
// Check log record.
82-
require.Equal(t, 1, resource.ScopeLogs().Len())
83-
require.Equal(t, 1, resource.ScopeLogs().At(0).LogRecords().Len())
84-
log := resource.ScopeLogs().At(0).LogRecords().At(0)
75+
require.Equal(t, 1, scope.LogRecords().Len())
76+
log := scope.LogRecords().At(0)
8577

8678
require.Equal(t,
8779
pcommon.NewTimestampFromTime(startTime),
@@ -149,12 +141,13 @@ func TestCursor(t *testing.T) {
149141
}
150142

151143
logs := plog.NewLogs()
144+
scope := logs.ResourceLogs().AppendEmpty().ScopeLogs().AppendEmpty()
152145
now := time.Now()
153146
for _, entry := range entries {
154147
if entry.Id == s.cursor.ID {
155148
continue
156149
}
157-
require.NoError(t, addLogRecord(logs, entry, now))
150+
require.NoError(t, addLogRecord(scope, entry, now))
158151
if entry.TimeCompleted != nil {
159152
s.cursor = cursor{
160153
TimeCompleted: entry.TimeCompleted,
@@ -187,6 +180,7 @@ func makeEntry(id string, tc *time.Time) oxide.AuditLogEntry {
187180

188181
// mockAuditLogClient returns pages in sequence, allowing injection of errors.
189182
type mockAuditLogClient struct {
183+
host string
190184
pages []mockPage
191185
calls int
192186
}
@@ -208,11 +202,52 @@ func (m *mockAuditLogClient) AuditLogList(
208202
return page.result, page.err
209203
}
210204

205+
func (m *mockAuditLogClient) Host() string {
206+
return m.host
207+
}
208+
209+
func TestScrape(t *testing.T) {
210+
t1 := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC)
211+
212+
client := &mockAuditLogClient{
213+
host: "https://example.oxide.computer",
214+
pages: []mockPage{
215+
{
216+
result: &oxide.AuditLogEntryResultsPage{
217+
Items: []oxide.AuditLogEntry{makeEntry("id-1", &t1)},
218+
},
219+
},
220+
},
221+
}
222+
223+
s := newAuditLogScraper(
224+
&Config{InitialLookback: time.Hour},
225+
componenttest.NewNopTelemetrySettings(),
226+
client,
227+
)
228+
require.NoError(t, s.Start(context.Background(), nil))
229+
230+
logs, err := s.Scrape(context.Background())
231+
require.NoError(t, err)
232+
require.Equal(t, 1, logs.ResourceLogs().Len())
233+
234+
attrs := logs.ResourceLogs().At(0).Resource().Attributes()
235+
236+
serviceName, ok := attrs.Get("service.name")
237+
require.True(t, ok)
238+
require.Equal(t, "oxide", serviceName.Str())
239+
240+
host, ok := attrs.Get("oxide.host")
241+
require.True(t, ok)
242+
require.Equal(t, "example.oxide.computer", host.Str())
243+
}
244+
211245
func TestScrapePartialResults(t *testing.T) {
212246
t1 := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC)
213247
t2 := time.Date(2025, 1, 1, 0, 0, 1, 0, time.UTC)
214248

215249
client := &mockAuditLogClient{
250+
host: "https://example.oxide.computer",
216251
pages: []mockPage{
217252
{
218253
result: &oxide.AuditLogEntryResultsPage{
@@ -255,6 +290,7 @@ func TestCursorPersistence(t *testing.T) {
255290
cursorPath := filepath.Join(t.TempDir(), "cursor.json")
256291

257292
client := &mockAuditLogClient{
293+
host: "https://example.oxide.computer",
258294
pages: []mockPage{
259295
{
260296
result: &oxide.AuditLogEntryResultsPage{

receiver/oxidemetricsreceiver/scraper.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package oxidemetricsreceiver
33
import (
44
"context"
55
"fmt"
6+
"net/url"
67
"regexp"
78
"slices"
89
"time"
@@ -224,6 +225,12 @@ func (s *oxideScraper) Scrape(ctx context.Context) (pmetric.Metrics, error) {
224225
for _, series := range table.Timeseries {
225226
rm := metrics.ResourceMetrics().AppendEmpty()
226227
resource := rm.Resource()
228+
resource.Attributes().PutStr("service.name", "oxide")
229+
host := s.client.Host()
230+
if u, err := url.Parse(host); err == nil && u.Host != "" {
231+
host = u.Host
232+
}
233+
resource.Attributes().PutStr("oxide.host", host)
227234

228235
addLabels(series, resource)
229236

0 commit comments

Comments
 (0)