@@ -9,18 +9,21 @@ import (
99 "bytes"
1010 "cmp"
1111 "context"
12- "encoding/json"
1312 "fmt"
1413 "io"
14+ "log/slog"
1515 "net/http"
1616 "net/url"
17+ "os"
1718 "sync"
1819 "time"
1920
2021 "github.com/DataDog/dd-trace-go/v2/internal"
2122 "github.com/DataDog/dd-trace-go/v2/internal/env"
2223 "github.com/DataDog/dd-trace-go/v2/internal/globalconfig"
2324 "github.com/DataDog/dd-trace-go/v2/internal/log"
25+ telemetrylog "github.com/DataDog/dd-trace-go/v2/internal/telemetry/log"
26+ jsoniter "github.com/json-iterator/go"
2427)
2528
2629const (
@@ -75,9 +78,9 @@ type exposureSubject struct {
7578
7679// exposureContext represents service context metadata for the exposure payload
7780type exposureContext struct {
78- ServiceName string `json:"service"`
79- Version string `json:"version,omitempty"`
80- Env string `json:"env,omitempty"`
81+ Service string `json:"service"`
82+ Version string `json:"version,omitempty"`
83+ Env string `json:"env,omitempty"`
8184}
8285
8386// exposurePayload represents the complete payload sent to the exposure endpoint
@@ -97,31 +100,34 @@ type exposureWriter struct {
97100 ticker * time.Ticker
98101 stopChan chan struct {}
99102 stopped bool
103+ jsonConfig jsoniter.API
100104}
101105
102106// newExposureWriter creates a new exposure writer with the given configuration
103107func newExposureWriter (config ProviderConfig ) * exposureWriter {
104- // Build service context from environment variables
105- serviceName := cmp .Or (env .Get ("DD_SERVICE" ), globalconfig .ServiceName ())
106- if serviceName == "" {
107- serviceName = "unknown"
108+ agentURL := internal .AgentURLFromEnv ()
109+ var httpClient * http.Client
110+ if agentURL .Scheme == "unix" {
111+ httpClient = internal .UDSClient (agentURL .Path , defaultHTTPTimeout )
112+ agentURL = internal .UnixDataSocketURL (agentURL .Path )
113+ } else {
114+ httpClient = internal .DefaultHTTPClient (defaultHTTPTimeout , false )
108115 }
109116
110- context := exposureContext {
111- ServiceName : serviceName ,
112- Version : env .Get ("DD_VERSION" ),
113- Env : env .Get ("DD_ENV" ),
114- }
117+ executable , _ := os .Executable ()
115118
116119 return & exposureWriter {
117- buffer : make ([]exposureEvent , 0 ),
120+ buffer : make ([]exposureEvent , 1 << 8 ), // Initial capacity of 256
118121 flushInterval : cmp .Or (config .ExposureFlushInterval , defaultExposureFlushInterval ),
119- httpClient : & http.Client {
120- Timeout : defaultHTTPTimeout ,
122+ httpClient : httpClient ,
123+ agentURL : agentURL ,
124+ stopChan : make (chan struct {}),
125+ jsonConfig : jsoniter.Config {}.Froze (),
126+ context : exposureContext {
127+ Service : cmp .Or (env .Get ("DD_SERVICE" ), globalconfig .ServiceName (), executable ),
128+ Version : env .Get ("DD_VERSION" ),
129+ Env : env .Get ("DD_ENV" ),
121130 },
122- agentURL : internal .AgentURLFromEnv (),
123- context : context ,
124- stopChan : make (chan struct {}),
125131 }
126132}
127133
@@ -132,6 +138,13 @@ func (w *exposureWriter) start() {
132138 defer func () {
133139 if r := recover (); r != nil {
134140 log .Error ("openfeature: exposure writer recovered panic: %v" , r )
141+ var errAttr slog.Attr
142+ if err , ok := r .(error ); ok {
143+ errAttr = slog .Any ("panic" , telemetrylog .NewSafeError (err ))
144+ } else {
145+ errAttr = slog .Any ("panic" , r )
146+ }
147+ telemetrylog .Error ("openfeature: exposure writer recovered panic" , errAttr )
135148 }
136149 w .stop ()
137150 }()
@@ -171,17 +184,14 @@ func (w *exposureWriter) flush() {
171184
172185 // Move buffer to local variable and create new buffer
173186 events := w .buffer
174- w .buffer = make ([]exposureEvent , len (events )/ 2 )
187+ w .buffer = make ([]exposureEvent , 0 , len (events )/ 2 )
175188 w .mu .Unlock ()
176189
177- // Build payload
178- payload := exposurePayload {
190+ // Send to agent
191+ if err := w . sendToAgent ( exposurePayload {
179192 Context : w .context ,
180193 Exposures : events ,
181- }
182-
183- // Send to agent
184- if err := w .sendToAgent (payload ); err != nil {
194+ }); err != nil {
185195 log .Error ("openfeature: failed to send exposure events: %v" , err .Error ())
186196 } else {
187197 log .Debug ("openfeature: successfully sent %d exposure events" , len (events ))
@@ -191,16 +201,19 @@ func (w *exposureWriter) flush() {
191201// sendToAgent sends the exposure payload to the Datadog Agent via EVP proxy
192202func (w * exposureWriter ) sendToAgent (payload exposurePayload ) error {
193203 // Serialize payload
194- jsonData , err := json .Marshal (payload )
195- if err != nil {
196- return fmt .Errorf ("failed to marshal exposure payload: %w" , err )
204+ var bytesBuffer bytes.Buffer
205+ encoder := w .jsonConfig .NewEncoder (& bytesBuffer )
206+ if err := encoder .Encode (payload ); err != nil {
207+ return fmt .Errorf ("failed to encode exposure payload: %w" , err )
197208 }
198209
199210 // Build request URL
200- requestURL := w .buildRequestURL ()
211+ u := * w .agentURL
212+ u .Path = exposureEndpoint
213+ requestURL := u .String ()
201214
202215 // Create HTTP request
203- req , err := http .NewRequestWithContext (context .Background (), "POST" , requestURL , bytes . NewReader ( jsonData ) )
216+ req , err := http .NewRequestWithContext (context .Background (), "POST" , requestURL , & bytesBuffer )
204217 if err != nil {
205218 return fmt .Errorf ("failed to create request: %w" , err )
206219 }
@@ -227,21 +240,6 @@ func (w *exposureWriter) sendToAgent(payload exposurePayload) error {
227240 return nil
228241}
229242
230- // buildRequestURL constructs the full URL for the exposure endpoint
231- func (w * exposureWriter ) buildRequestURL () string {
232- if w .agentURL .Scheme == "unix" {
233- // For Unix domain sockets, use the HTTP adapter
234- u := internal .UnixDataSocketURL (w .agentURL .Path )
235- u .Path = exposureEndpoint
236- return u .String ()
237- }
238-
239- // For HTTP/HTTPS URLs, append the endpoint path
240- u := * w .agentURL
241- u .Path = exposureEndpoint
242- return u .String ()
243- }
244-
245243// stop stops the exposure writer and flushes any remaining events
246244func (w * exposureWriter ) stop () {
247245 w .mu .Lock ()
0 commit comments