diff --git a/comp/core/agenttelemetry/impl/sender.go b/comp/core/agenttelemetry/impl/sender.go index 585807eb6a539b..ddb83a3b19a893 100644 --- a/comp/core/agenttelemetry/impl/sender.go +++ b/comp/core/agenttelemetry/impl/sender.go @@ -57,7 +57,9 @@ type senderImpl struct { cfgComp config.Reader logComp log.Component - client client + compress bool + compressionLevel int + client client endpoints *logconfig.Endpoints @@ -207,9 +209,12 @@ func newSenderImpl( cfgComp: cfgComp, logComp: logComp, - client: client, - endpoints: endpoints, - agentVersion: agentVersion.GetNumberAndPre(), + compress: cfgComp.GetBool("agent_telemetry.use_compression"), + compressionLevel: cfgComp.GetInt("agent_telemetry.compression_level"), + client: client, + endpoints: endpoints, + agentVersion: agentVersion.GetNumberAndPre(), + // pre-fill parts of payload which are not changing during run-time payloadTemplate: Payload{ APIVersion: "v2", @@ -321,11 +326,23 @@ func (s *senderImpl) flushSession(ss *senderSession) error { return fmt.Errorf("failed to marshal agent telemetry payload: %w", err) } - reqBody, err := scrubber.ScrubJSON(payloadJSON) + reqBodyRaw, err := scrubber.ScrubJSON(payloadJSON) if err != nil { return fmt.Errorf("failed to scrubl agent telemetry payload: %w", err) } + // Try to compress the payload if needed + reqBody := reqBodyRaw + gzipCompressed := s.compress + if s.compress { + reqBody, err = gzipCompress(reqBodyRaw, s.compressionLevel) + if err != nil { + gzipCompressed = false + reqBody = reqBodyRaw + s.logComp.Errorf("Failed to compress agent telemetry payload: %v", err) + } + } + // Send the payload to all endpoints var errs error reqType := payloads.RequestType @@ -337,7 +354,7 @@ func (s *senderImpl) flushSession(ss *senderSession) error { errs = errors.Join(errs, err) continue } - s.addHeaders(req, reqType, ep.GetAPIKey(), bodyLen) + s.addHeaders(req, reqType, ep.GetAPIKey(), bodyLen, gzipCompressed) resp, err := s.client.Do(req.WithContext(ss.cancelCtx)) if err != nil { errs = errors.Join(errs, err) @@ -387,7 +404,7 @@ func (s *senderImpl) sendAgentMetricPayloads(ss *senderSession, metrics []*agent } } -func (s *senderImpl) addHeaders(req *http.Request, requesttype, apikey, bodylen string) { +func (s *senderImpl) addHeaders(req *http.Request, requesttype, apikey, bodylen string, gzipCompressed bool) { req.Header.Add("DD-Api-Key", apikey) req.Header.Add("Content-Type", "application/json") req.Header.Add("Content-Length", bodylen) @@ -397,4 +414,8 @@ func (s *senderImpl) addHeaders(req *http.Request, requesttype, apikey, bodylen req.Header.Add("DD-Telemetry-Product-Version", s.agentVersion) // Not clear how to acquire that. Appears that EVP adds it automatically req.Header.Add("datadog-container-id", "") + + if gzipCompressed { + req.Header.Set("Content-Encoding", "gzip") + } } diff --git a/comp/core/agenttelemetry/impl/utils.go b/comp/core/agenttelemetry/impl/utils.go index 07f6b86aebf402..a8f6c273e71bbe 100644 --- a/comp/core/agenttelemetry/impl/utils.go +++ b/comp/core/agenttelemetry/impl/utils.go @@ -6,6 +6,8 @@ package agenttelemetryimpl import ( + "bytes" + "compress/gzip" "fmt" "sort" @@ -123,3 +125,25 @@ func cloneLabelsSorted(labels []*dto.LabelPair) []*dto.LabelPair { func makeLabelPairKey(l *dto.LabelPair) string { return fmt.Sprintf("%s:%s:", l.GetName(), l.GetValue()) } + +// Compresses payload via gzip +func gzipCompress(payload []byte, compressionLevel int) ([]byte, error) { + var payloadCompressed bytes.Buffer + gzipWriter, err := gzip.NewWriterLevel(&payloadCompressed, compressionLevel) + if err != nil { + return nil, err + } + _, err = gzipWriter.Write(payload) + if err != nil { + return nil, err + } + err = gzipWriter.Flush() + if err != nil { + return nil, err + } + err = gzipWriter.Close() + if err != nil { + return nil, err + } + return payloadCompressed.Bytes(), nil +} diff --git a/releasenotes/notes/agent-tel-gzip-bba8a51c1aa3ba2f.yaml b/releasenotes/notes/agent-tel-gzip-bba8a51c1aa3ba2f.yaml new file mode 100644 index 00000000000000..28813332bc7f1e --- /dev/null +++ b/releasenotes/notes/agent-tel-gzip-bba8a51c1aa3ba2f.yaml @@ -0,0 +1,11 @@ +# Each section from every release note are combined when the +# CHANGELOG.rst is rendered. So the text needs to be worded so that +# it does not depend on any information only available in another +# section. This may mean repeating some details, but each section +# must be readable independently of the other. +# +# Each section note must be formatted as reStructuredText. +--- +enhancements: + - | + Use HTTP gzip compression for the Agent telemetry payloads.