Skip to content

Commit cc9a6cc

Browse files
authored
DSET-3467 feature: add more details to User-Agent header (#37)
* DSET-3467 feature: add more details to User-Agent header - propagate OtelCollector attributes to - library version - OS details * DSET-3467 rename library in user agent
1 parent dab2a50 commit cc9a6cc

File tree

9 files changed

+97
-36
lines changed

9 files changed

+97
-36
lines changed

examples/client/main.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,14 @@ func main() {
5252
panic(err)
5353
}
5454

55+
libraryConsumerUserAgentSuffix := "OtelCollector;1.2.3"
56+
5557
// build client
5658
cl, err := client.NewClient(
5759
cfg,
5860
&http.Client{},
5961
zap.Must(zap.NewDevelopment()),
62+
&libraryConsumerUserAgentSuffix,
6063
)
6164
if err != nil {
6265
panic(err)

examples/readme/main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,9 @@ func main() {
7676
if err != nil {
7777
panic(err)
7878
}
79+
libraryConsumerUserAgentSuffix := "OtelCollector;1.2.3"
7980
// build client
80-
cl, err := client.NewClient(cfg, &http.Client{}, logger)
81+
cl, err := client.NewClient(cfg, &http.Client{}, logger, &libraryConsumerUserAgentSuffix)
8182
if err != nil {
8283
panic(err)
8384
}

pkg/api/request/request.go

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,8 @@ import (
88
"net/http"
99

1010
"github.com/scalyr/dataset-go/pkg/config"
11-
12-
"github.com/scalyr/dataset-go/pkg/version"
1311
)
1412

15-
const UserAgent = "datasetexporter/" + version.Version + " (" + version.ReleasedDate + ")"
16-
1713
func (ap *AuthParams) setToken(token string) {
1814
ap.Token = token
1915
}
@@ -29,7 +25,8 @@ type TokenSetter interface {
2925

3026
// ApiRequest represents a generic DataSet REST API request, with all its properties
3127
type ApiRequest struct {
32-
requestType string
28+
httpMethod string
29+
userAgent string
3330
payload []byte
3431
request interface{}
3532
uri string
@@ -39,7 +36,7 @@ type ApiRequest struct {
3936
}
4037

4138
func NewApiRequest(requestType string, uri string) *ApiRequest {
42-
return &ApiRequest{requestType: requestType, uri: uri}
39+
return &ApiRequest{httpMethod: requestType, uri: uri}
4340
}
4441

4542
func (r *ApiRequest) WithWriteLog(tokens config.DataSetTokens) *ApiRequest {
@@ -94,7 +91,7 @@ func (r *ApiRequest) WithWriteConfig(tokens config.DataSetTokens) *ApiRequest {
9491
return r
9592
}
9693

97-
func (r *ApiRequest) JsonRequest(request TokenSetter) *ApiRequest {
94+
func (r *ApiRequest) jsonRequest(request TokenSetter) *ApiRequest {
9895
payload, err := json.Marshal(request)
9996
r.request = request
10097
if err != nil {
@@ -105,13 +102,18 @@ func (r *ApiRequest) JsonRequest(request TokenSetter) *ApiRequest {
105102
return r
106103
}
107104

108-
func (r *ApiRequest) RawRequest(payload []byte) *ApiRequest {
105+
func (r *ApiRequest) WithPayload(payload []byte) *ApiRequest {
109106
r.payload = payload
110107
return r
111108
}
112109

110+
func (r *ApiRequest) WithUserAgent(userAgent string) *ApiRequest {
111+
r.userAgent = userAgent
112+
return r
113+
}
114+
113115
func (r *ApiRequest) emptyRequest() *ApiRequest {
114-
return r.JsonRequest(TokenSetter(&AuthParams{}))
116+
return r.jsonRequest(TokenSetter(&AuthParams{}))
115117
}
116118

117119
func (r *ApiRequest) HttpRequest() (*http.Request, error) {
@@ -141,14 +143,14 @@ func (r *ApiRequest) HttpRequest() (*http.Request, error) {
141143
return nil, r.err
142144
}
143145

144-
req, err := http.NewRequest(r.requestType, r.uri, &buf)
146+
req, err := http.NewRequest(r.httpMethod, r.uri, &buf)
145147
if err != nil {
146148
r.err = fmt.Errorf("failed to create NewApiRequest: %w", err)
147149
return nil, r.err
148150
}
149151
req.Header.Add("Content-Type", "application/json")
150152
req.Header.Add("Content-Encoding", "gzip")
151-
req.Header.Add("User-Agent", UserAgent)
153+
req.Header.Add("User-Agent", r.userAgent)
152154

153155
return req, nil
154156
}

pkg/api/request/request_test.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package request
1818

1919
import (
20+
"net/http"
2021
"testing"
2122

2223
"github.com/scalyr/dataset-go/pkg/config"
@@ -25,7 +26,7 @@ import (
2526

2627
func TestMissingAuthJSONResponse(t *testing.T) {
2728
tokens := config.DataSetTokens{}
28-
r := NewApiRequest("GET", "/meh").WithWriteConfig(tokens).WithReadConfig(tokens).WithReadLog(tokens).WithWriteLog(tokens)
29+
r := NewApiRequest(http.MethodGet, "/meh").WithWriteConfig(tokens).WithReadConfig(tokens).WithReadLog(tokens).WithWriteLog(tokens)
2930
_, err2 := r.HttpRequest()
3031
assert.NotNil(t, err2, "Should of gotten an error about missing authentication, got %s", r.supportedKeys)
3132

@@ -35,23 +36,34 @@ func TestMissingAuthJSONResponse(t *testing.T) {
3536

3637
func TestAuthOrderJSONResponse(t *testing.T) {
3738
tokens := config.DataSetTokens{WriteLog: "writeLog", ReadLog: "readLog", WriteConfig: "writeConfig", ReadConfig: "readConfig"}
38-
r := NewApiRequest("GET", "/meh").WithWriteConfig(tokens).WithReadConfig(tokens).WithReadLog(tokens).WithWriteLog(tokens)
39+
r := NewApiRequest(http.MethodGet, "/meh").WithWriteConfig(tokens).WithReadConfig(tokens).WithReadLog(tokens).WithWriteLog(tokens)
3940
_, err := r.HttpRequest()
4041
assert.Nil(t, err, "Should not have gotten an error about missing authentication")
4142
assert.Equal(t, "writeConfig", r.apiKey, "WriteConfig API Key should have been used")
4243

43-
r = NewApiRequest("GET", "/meh").WithReadConfig(tokens).WithReadLog(tokens).WithWriteLog(tokens)
44+
r = NewApiRequest(http.MethodGet, "/meh").WithReadConfig(tokens).WithReadLog(tokens).WithWriteLog(tokens)
4445
_, err2 := r.HttpRequest()
4546
assert.Nil(t, err2)
4647
assert.Equal(t, "readConfig", r.apiKey, "ReadConfig API Key should have been used")
4748

48-
r = NewApiRequest("GET", "/meh").WithReadLog(tokens).WithWriteLog(tokens)
49+
r = NewApiRequest(http.MethodGet, "/meh").WithReadLog(tokens).WithWriteLog(tokens)
4950
_, err3 := r.HttpRequest()
5051
assert.Nil(t, err3)
5152
assert.Equal(t, "readLog", r.apiKey, "ReadLog API Key should have been used")
5253

53-
r = NewApiRequest("GET", "/meh").WithWriteLog(tokens)
54+
r = NewApiRequest(http.MethodGet, "/meh").WithWriteLog(tokens)
5455
_, err4 := r.HttpRequest()
5556
assert.Nil(t, err4)
5657
assert.Equal(t, "writeLog", r.apiKey, "WriteLog API Key should have been used")
5758
}
59+
60+
func TestNewRequest(t *testing.T) {
61+
userAgent := "userAgent"
62+
uri := "/meh"
63+
tokens := config.DataSetTokens{WriteLog: "AAAA"}
64+
r, err := NewApiRequest(http.MethodGet, uri).WithWriteLog(tokens).WithUserAgent(userAgent).HttpRequest()
65+
assert.Nil(t, err)
66+
assert.Equal(t, "GET", r.Method)
67+
assert.Equal(t, uri, r.URL.Path)
68+
assert.Equal(t, userAgent, r.Header.Get("User-Agent"))
69+
}

pkg/client/add_events.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -319,8 +319,8 @@ func (client *DataSetClient) sendAddEventsBuffer(buf *buffer.Buffer) (*add_event
319319
resp := &add_events.AddEventsResponse{}
320320

321321
httpRequest, err := request.NewApiRequest(
322-
"POST", client.addEventsEndpointUrl,
323-
).WithWriteLog(client.Config.Tokens).RawRequest(payload).HttpRequest()
322+
http.MethodPost, client.addEventsEndpointUrl,
323+
).WithWriteLog(client.Config.Tokens).WithPayload(payload).WithUserAgent(client.userAgent).HttpRequest()
324324
if err != nil {
325325
return nil, len(payload), fmt.Errorf("cannot create request: %w", err)
326326
}

pkg/client/add_events_long_running_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ func TestAddEventsManyLogsShouldSucceed(t *testing.T) {
9898
RetryMaxElapsedTime: 10 * RetryBase,
9999
},
100100
}
101-
sc, err := NewClient(config, &http.Client{}, zap.Must(zap.NewDevelopment()))
101+
sc, err := NewClient(config, &http.Client{}, zap.Must(zap.NewDevelopment()), nil)
102102
require.Nil(t, err)
103103

104104
sessionInfo := &add_events.SessionInfo{ServerId: "a", ServerType: "b"}

pkg/client/add_events_test.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ func TestAddEventsRetry(t *testing.T) {
9393
buffer_config.WithRetryInitialInterval(RetryBase),
9494
buffer_config.WithRetryMaxInterval(RetryBase),
9595
))
96-
sc, err := NewClient(config, &http.Client{}, zap.Must(zap.NewDevelopment()))
96+
sc, err := NewClient(config, &http.Client{}, zap.Must(zap.NewDevelopment()), nil)
9797
require.Nil(t, err)
9898

9999
sessionInfo := &add_events.SessionInfo{ServerId: "a", ServerType: "b"}
@@ -166,7 +166,7 @@ func TestAddEventsRetryAfterSec(t *testing.T) {
166166
buffer_config.WithRetryInitialInterval(RetryBase),
167167
buffer_config.WithRetryMaxInterval(RetryBase),
168168
))
169-
sc, err := NewClient(config, &http.Client{}, zap.Must(zap.NewDevelopment()))
169+
sc, err := NewClient(config, &http.Client{}, zap.Must(zap.NewDevelopment()), nil)
170170
require.Nil(t, err)
171171

172172
sessionInfo := &add_events.SessionInfo{ServerId: "a", ServerType: "b"}
@@ -252,7 +252,7 @@ func TestAddEventsRetryAfterTime(t *testing.T) {
252252
buffer_config.WithRetryInitialInterval(RetryBase),
253253
buffer_config.WithRetryMaxInterval(RetryBase),
254254
))
255-
sc, err := NewClient(config, &http.Client{}, zap.Must(zap.NewDevelopment()))
255+
sc, err := NewClient(config, &http.Client{}, zap.Must(zap.NewDevelopment()), nil)
256256
require.Nil(t, err)
257257

258258
sessionInfo := &add_events.SessionInfo{ServerId: "a", ServerType: "b"}
@@ -341,7 +341,7 @@ func TestAddEventsLargeEvent(t *testing.T) {
341341
buffer_config.WithRetryInitialInterval(RetryBase),
342342
buffer_config.WithRetryMaxInterval(RetryBase),
343343
))
344-
sc, err := NewClient(config, &http.Client{}, zap.Must(zap.NewDevelopment()))
344+
sc, err := NewClient(config, &http.Client{}, zap.Must(zap.NewDevelopment()), nil)
345345
require.Nil(t, err)
346346

347347
sessionInfo := &add_events.SessionInfo{ServerId: "a", ServerType: "b"}
@@ -423,7 +423,7 @@ func TestAddEventsLargeEventThatNeedEscaping(t *testing.T) {
423423
buffer_config.WithRetryInitialInterval(RetryBase),
424424
buffer_config.WithRetryMaxInterval(RetryBase),
425425
))
426-
sc, err := NewClient(config, &http.Client{}, zap.Must(zap.NewDevelopment()))
426+
sc, err := NewClient(config, &http.Client{}, zap.Must(zap.NewDevelopment()), nil)
427427
require.Nil(t, err)
428428

429429
sessionInfo := &add_events.SessionInfo{ServerId: "a", ServerType: "b"}
@@ -447,7 +447,7 @@ func TestAddEventsRejectAfterFinish(t *testing.T) {
447447
buffer_config.WithRetryInitialInterval(RetryBase),
448448
buffer_config.WithRetryMaxInterval(RetryBase),
449449
))
450-
sc, err := NewClient(config, &http.Client{}, zap.Must(zap.NewDevelopment()))
450+
sc, err := NewClient(config, &http.Client{}, zap.Must(zap.NewDevelopment()), nil)
451451
require.Nil(t, err)
452452
err = sc.Shutdown()
453453
assert.Nil(t, err)
@@ -493,7 +493,7 @@ func TestAddEventsWithBufferSweeper(t *testing.T) {
493493
RetryMaxElapsedTime: 10 * RetryBase,
494494
},
495495
}
496-
sc, err := NewClient(config, &http.Client{}, zap.Must(zap.NewDevelopment()))
496+
sc, err := NewClient(config, &http.Client{}, zap.Must(zap.NewDevelopment()), nil)
497497
require.Nil(t, err)
498498

499499
sessionInfo := &add_events.SessionInfo{ServerId: "a", ServerType: "b"}
@@ -527,7 +527,7 @@ func TestAddEventsDoNotRetryForever(t *testing.T) {
527527
config := newDataSetConfig(server.URL, *newBufferSettings(
528528
buffer_config.WithRetryMaxElapsedTime(time.Duration(5) * time.Second),
529529
))
530-
sc, err := NewClient(config, &http.Client{}, zap.Must(zap.NewDevelopment()))
530+
sc, err := NewClient(config, &http.Client{}, zap.Must(zap.NewDevelopment()), nil)
531531
require.Nil(t, err)
532532

533533
sessionInfo := &add_events.SessionInfo{ServerId: "a", ServerType: "b"}
@@ -550,7 +550,7 @@ func TestAddEventsLogResponseBodyOnInvalidJson(t *testing.T) {
550550
config := newDataSetConfig(server.URL, *newBufferSettings(
551551
buffer_config.WithRetryMaxElapsedTime(time.Duration(3) * time.Second),
552552
))
553-
sc, err := NewClient(config, &http.Client{}, zap.Must(zap.NewDevelopment()))
553+
sc, err := NewClient(config, &http.Client{}, zap.Must(zap.NewDevelopment()), nil)
554554
require.Nil(t, err)
555555

556556
sessionInfo := &add_events.SessionInfo{ServerId: "a", ServerType: "b"}
@@ -583,7 +583,7 @@ func TestAddEventsAreNotRejectedOncePreviousReqRetriesMaxLifetimeExpired(t *test
583583
buffer_config.WithRetryMaxElapsedTime(time.Duration(maxElapsedTime)*time.Second),
584584
buffer_config.WithRetryRandomizationFactor(0.000000001),
585585
))
586-
client, err := NewClient(dataSetConfig, &http.Client{}, zap.Must(zap.NewDevelopment()))
586+
client, err := NewClient(dataSetConfig, &http.Client{}, zap.Must(zap.NewDevelopment()), nil)
587587
require.Nil(t, err)
588588

589589
sessionInfo := &add_events.SessionInfo{ServerId: "a", ServerType: "b"}
@@ -614,7 +614,7 @@ func TestAddEventsAreRejectedOncePreviousReqRetriesMaxLifetimeNotExpired(t *test
614614
buffer_config.WithRetryMaxElapsedTime(time.Duration(maxElapsedTime)*time.Second),
615615
buffer_config.WithRetryRandomizationFactor(0.000000001),
616616
))
617-
client, err := NewClient(dataSetConfig, &http.Client{}, zap.Must(zap.NewDevelopment()))
617+
client, err := NewClient(dataSetConfig, &http.Client{}, zap.Must(zap.NewDevelopment()), nil)
618618
require.Nil(t, err)
619619

620620
sessionInfo := &add_events.SessionInfo{ServerId: "a", ServerType: "b"}

pkg/client/client.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package client
1919
import (
2020
"fmt"
2121
"net/http"
22+
"runtime"
2223
"strconv"
2324
"strings"
2425
"sync"
@@ -95,9 +96,10 @@ type DataSetClient struct {
9596
// Stores sanitized complete URL to the addEvents API endpoint, e.g.
9697
// https://app.scalyr.com/api/addEvents
9798
addEventsEndpointUrl string
99+
userAgent string
98100
}
99101

100-
func NewClient(cfg *config.DataSetConfig, client *http.Client, logger *zap.Logger) (*DataSetClient, error) {
102+
func NewClient(cfg *config.DataSetConfig, client *http.Client, logger *zap.Logger, userAgentSuffix *string) (*DataSetClient, error) {
101103
logger.Info(
102104
"Using config: ",
103105
zap.String("config", cfg.String()),
@@ -122,6 +124,20 @@ func NewClient(cfg *config.DataSetConfig, client *http.Client, logger *zap.Logge
122124
addEventsEndpointUrl += "/api/addEvents"
123125
}
124126

127+
userAgent := fmt.Sprintf(
128+
"%s;%s;%s;%s;%s;%s;%d",
129+
"dataset-go",
130+
version.Version,
131+
version.ReleasedDate,
132+
id,
133+
runtime.GOOS,
134+
runtime.GOARCH,
135+
runtime.NumCPU(),
136+
)
137+
if userAgentSuffix != nil && *userAgentSuffix != "" {
138+
userAgent = userAgent + ";" + *userAgentSuffix
139+
}
140+
125141
dataClient := &DataSetClient{
126142
Id: id,
127143
Config: cfg,
@@ -148,6 +164,7 @@ func NewClient(cfg *config.DataSetConfig, client *http.Client, logger *zap.Logge
148164
firstReceivedAt: atomic.Int64{},
149165
lastAcceptedAt: atomic.Int64{},
150166
addEventsEndpointUrl: addEventsEndpointUrl,
167+
userAgent: userAgent,
151168
}
152169

153170
// run buffer sweeper if requested

pkg/client/client_test.go

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@ import (
2121
"fmt"
2222
"net/http"
2323
"net/http/httptest"
24+
"runtime"
2425
"testing"
2526

27+
"github.com/scalyr/dataset-go/pkg/version"
28+
2629
"github.com/stretchr/testify/assert"
2730
"github.com/stretchr/testify/require"
2831

@@ -42,7 +45,7 @@ func TestNewClient(t *testing.T) {
4245
t.Setenv("SCALYR_READCONFIG_TOKEN", "readconfig")
4346
cfg, err := config.New(config.FromEnv())
4447
assert.Nil(t, err)
45-
sc4, err := NewClient(cfg, nil, zap.Must(zap.NewDevelopment()))
48+
sc4, err := NewClient(cfg, nil, zap.Must(zap.NewDevelopment()), nil)
4649
require.Nil(t, err)
4750
assert.Equal(t, sc4.Config.Tokens.ReadLog, "readlog")
4851
assert.Equal(t, sc4.Config.Tokens.WriteLog, "writelog")
@@ -61,7 +64,7 @@ func TestClientBuffer(t *testing.T) {
6164
Endpoint: ts.URL,
6265
Tokens: config.DataSetTokens{WriteLog: token},
6366
BufferSettings: buffer_config.NewDefaultDataSetBufferSettings(),
64-
}, &http.Client{}, zap.Must(zap.NewDevelopment()))
67+
}, &http.Client{}, zap.Must(zap.NewDevelopment()), nil)
6568
require.Nil(t, err)
6669

6770
sessionInfo := add_events.SessionInfo{
@@ -193,7 +196,7 @@ func TestAddEventsEndpointUrlWithoutTrailingSlash(t *testing.T) {
193196
t.Setenv("SCALYR_SERVER", "https://app.scalyr.com")
194197
cfg, err := config.New(config.FromEnv())
195198
assert.Nil(t, err)
196-
sc, err := NewClient(cfg, nil, zap.Must(zap.NewDevelopment()))
199+
sc, err := NewClient(cfg, nil, zap.Must(zap.NewDevelopment()), nil)
197200
require.Nil(t, err)
198201
assert.Equal(t, sc.addEventsEndpointUrl, "https://app.scalyr.com/api/addEvents")
199202
}
@@ -202,7 +205,30 @@ func TestAddEventsEndpointUrlWithTrailingSlash(t *testing.T) {
202205
t.Setenv("SCALYR_SERVER", "https://app.scalyr.com/")
203206
cfg2, err := config.New(config.FromEnv())
204207
assert.Nil(t, err)
205-
sc2, err := NewClient(cfg2, nil, zap.Must(zap.NewDevelopment()))
208+
sc2, err := NewClient(cfg2, nil, zap.Must(zap.NewDevelopment()), nil)
206209
require.Nil(t, err)
207210
assert.Equal(t, sc2.addEventsEndpointUrl, "https://app.scalyr.com/api/addEvents")
208211
}
212+
213+
func TestUserAgent(t *testing.T) {
214+
t.Setenv("SCALYR_SERVER", "https://app.scalyr.com/")
215+
libraryConsumerUserAgentSuffix := "OtelCollector;0.80.0;traces"
216+
numCpu := fmt.Sprint(runtime.NumCPU())
217+
cfg, err := config.New(config.FromEnv())
218+
assert.Nil(t, err)
219+
client, err := NewClient(cfg, nil, zap.Must(zap.NewDevelopment()), &libraryConsumerUserAgentSuffix)
220+
clientId := client.Id.String()
221+
require.Nil(t, err)
222+
assert.Equal(t, client.userAgent, "dataset-go;"+version.Version+";"+version.ReleasedDate+";"+clientId+";"+runtime.GOOS+";"+runtime.GOARCH+";"+numCpu+";"+libraryConsumerUserAgentSuffix)
223+
}
224+
225+
func TestUserAgentWithoutCollectorAttrs(t *testing.T) {
226+
t.Setenv("SCALYR_SERVER", "https://app.scalyr.com/")
227+
numCpu := fmt.Sprint(runtime.NumCPU())
228+
cfg, err := config.New(config.FromEnv())
229+
assert.Nil(t, err)
230+
client, err := NewClient(cfg, nil, zap.Must(zap.NewDevelopment()), nil)
231+
clientId := client.Id.String()
232+
require.Nil(t, err)
233+
assert.Equal(t, client.userAgent, "dataset-go;"+version.Version+";"+version.ReleasedDate+";"+clientId+";"+runtime.GOOS+";"+runtime.GOARCH+";"+numCpu)
234+
}

0 commit comments

Comments
 (0)