Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
eceb951
initial commit
Umang01-hash Sep 19, 2025
8153592
Merge remote-tracking branch 'origin' into fix/gofr_telemetry
Umang01-hash Sep 25, 2025
6174e60
update implementation to use JSON
Umang01-hash Sep 25, 2025
e1cb05e
Merge remote-tracking branch 'origin' into fix/gofr_telemetry
Umang01-hash Sep 25, 2025
c8386a7
Merge branch 'development' into fix/gofr_telemetry
Umang01-hash Sep 26, 2025
2f5f6fc
revert unwanted changes
Umang01-hash Sep 26, 2025
b9342cd
fix linters
Umang01-hash Sep 26, 2025
06f3294
Merge remote-tracking branch 'origin' into fix/gofr_telemetry
Umang01-hash Sep 26, 2025
a337e96
Merge remote-tracking branch 'origin/fix/gofr_telemetry' into fix/gof…
Umang01-hash Sep 26, 2025
0e867fe
update telemetry endpoint
Umang01-hash Sep 26, 2025
bd10520
Merge branch 'development' into fix/gofr_telemetry
Umang01-hash Sep 26, 2025
ff7d972
Merge branch 'development' into fix/gofr_telemetry
Umang01-hash Sep 26, 2025
2e2aa70
Merge branch 'development' into fix/gofr_telemetry
Umang01-hash Sep 29, 2025
8fc2cfb
Merge branch 'development' into fix/gofr_telemetry
Umang01-hash Oct 3, 2025
5702fbf
Merge branch 'development' into fix/gofr_telemetry
Umang01-hash Oct 6, 2025
37011b4
Merge branch 'development' into fix/gofr_telemetry
Umang01-hash Oct 6, 2025
3d12a2d
Merge branch 'development' into fix/gofr_telemetry
Umang01-hash Oct 6, 2025
5a52a8f
renamed config to GOFR_TELEMETRY for consistency
Umang01-hash Oct 6, 2025
7e80da4
Merge branch 'development' into fix/gofr_telemetry
Umang01-hash Oct 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pkg/gofr/container/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ func (c *Container) Create(conf config.Config) {

c.metricsManager = metrics.NewMetricsManager(exporters.Prometheus(c.GetAppName(), c.GetAppVersion()), c.Logger)

exporters.SendFrameworkStartupTelemetry(c.GetAppName(), c.GetAppVersion())

// Register framework metrics
c.registerFrameworkMetrics()

Expand Down
99 changes: 99 additions & 0 deletions pkg/gofr/metrics/exporters/telemetry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package exporters

import (
"bytes"
"context"
"encoding/json"
"net/http"
"os"
"runtime"
"time"

"github.com/google/uuid"

"gofr.dev/pkg/gofr/version"
)

const (
defaultTelemetryEndpoint = "https://gofr.dev/telemetry/v1/metrics"
defaultAppName = "gofr-app"
requestTimeout = 10 * time.Second
)

// TelemetryData represents the JSON telemetry payload.
type TelemetryData struct {
Timestamp string `json:"timestamp"`
EventID string `json:"event_id"`
Source string `json:"source"`
ServiceName string `json:"service_name,omitempty"`
ServiceVersion string `json:"service_version,omitempty"`
RawDataSize int `json:"raw_data_size"`
FrameworkVersion string `json:"framework_version,omitempty"`
GoVersion string `json:"go_version,omitempty"`
OS string `json:"os,omitempty"`
Architecture string `json:"architecture,omitempty"`
StartupTime string `json:"startup_time,omitempty"`
}

// SendFrameworkStartupTelemetry sends telemetry data.
func SendFrameworkStartupTelemetry(appName, appVersion string) {
if os.Getenv("GOFR_TELEMETRY") == "false" {
return
}

go sendTelemetryData(appName, appVersion)
}

func sendTelemetryData(appName, appVersion string) {
if appName == "" {
appName = defaultAppName
}

if appVersion == "" {
appVersion = "unknown"
}

now := time.Now().UTC()

data := TelemetryData{
Timestamp: now.Format(time.RFC3339),
EventID: uuid.New().String(),
Source: "gofr-framework",
ServiceName: appName,
ServiceVersion: appVersion,
RawDataSize: 0,
FrameworkVersion: version.Framework,
GoVersion: runtime.Version(),
OS: runtime.GOOS,
Architecture: runtime.GOARCH,
StartupTime: now.Format(time.RFC3339),
}

sendToEndpoint(&data, defaultTelemetryEndpoint)
}

func sendToEndpoint(data *TelemetryData, endpoint string) {
jsonData, err := json.Marshal(data)
if err != nil {
return
}

ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
defer cancel()

req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewBuffer(jsonData))
if err != nil {
return
}

req.Header.Set("Content-Type", "application/json")

client := &http.Client{Timeout: 10 * time.Second}

resp, err := client.Do(req)
if err != nil {
return
}

resp.Body.Close()
}
87 changes: 87 additions & 0 deletions pkg/gofr/metrics/exporters/telemetry_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package exporters

import (
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"runtime"
"testing"
"time"

"github.com/google/uuid"
"github.com/stretchr/testify/assert"

"gofr.dev/pkg/gofr/version"
)

func TestSendFrameworkStartupTelemetry_Disabled(t *testing.T) {
t.Setenv("GOFR_TELEMETRY", "true")

requestMade := false

server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
requestMade = true

w.WriteHeader(http.StatusOK)
}))
defer server.Close()

SendFrameworkStartupTelemetry("test-app", "1.0.0")
time.Sleep(100 * time.Millisecond)

assert.False(t, requestMade, "Expected no telemetry when disabled")
}

func TestSendFrameworkStartupTelemetry_DefaultValues(t *testing.T) {
var receivedData TelemetryData

server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
assert.NoError(t, err)

err = json.Unmarshal(body, &receivedData)
assert.NoError(t, err)

w.WriteHeader(http.StatusOK)
}))
defer server.Close()

// Test with empty values to verify defaults.
testSendTelemetryData("", "", server.URL)
time.Sleep(100 * time.Millisecond)

assert.Equal(t, defaultAppName, receivedData.ServiceName)
assert.Equal(t, "unknown", receivedData.ServiceVersion)
assert.Equal(t, "gofr-framework", receivedData.Source)
assert.Equal(t, 0, receivedData.RawDataSize)
}

// Helper function that replicates sendTelemetryData but with configurable endpoint.
func testSendTelemetryData(appName, appVersion, endpoint string) {
if appName == "" {
appName = defaultAppName
}

if appVersion == "" {
appVersion = "unknown"
}

now := time.Now().UTC()

data := TelemetryData{
Timestamp: now.Format(time.RFC3339),
EventID: uuid.New().String(),
Source: "gofr-framework",
ServiceName: appName,
ServiceVersion: appVersion,
RawDataSize: 0,
FrameworkVersion: version.Framework,
GoVersion: runtime.Version(),
OS: runtime.GOOS,
Architecture: runtime.GOARCH,
StartupTime: now.Format(time.RFC3339),
}

sendToEndpoint(&data, endpoint)
}