Skip to content

Commit e2216b0

Browse files
Merge pull request #4896 from linuxfoundation/unicron-datadog-logging
Unicron datadog logging
2 parents ecfd360 + 625c572 commit e2216b0

24 files changed

Lines changed: 1926 additions & 56 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,3 +266,4 @@ cla-backend/python-api.log
266266
cla-backend/python-api.err
267267
cla-backend-go/golang-api.err
268268
cla-backend-go/golang-api.log
269+
utils/otel_dd_go/otel_dd

cla-backend-go/cmd/server.go

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ import (
8181

8282
"github.com/linuxfoundation/easycla/cla-backend-go/api_logs"
8383
"github.com/linuxfoundation/easycla/cla-backend-go/signatures"
84+
"github.com/linuxfoundation/easycla/cla-backend-go/telemetry"
8485
v2Signatures "github.com/linuxfoundation/easycla/cla-backend-go/v2/signatures"
8586

8687
ini "github.com/linuxfoundation/easycla/cla-backend-go/init"
@@ -146,8 +147,52 @@ type combinedRepo struct {
146147
projects_cla_groups.Repository
147148
}
148149

150+
const (
151+
envDDBAPILogging = "DDB_API_LOGGING"
152+
envOtelDatadogAPILogging = "OTEL_DATADOG_API_LOGGING"
153+
)
154+
155+
// parseBoolish parses common "boolean-ish" env var values.
156+
// Returns (value, ok). ok=false means "unknown/invalid".
157+
func parseBoolish(v string) (bool, bool) {
158+
s := strings.TrimSpace(strings.ToLower(v))
159+
switch s {
160+
case "1", "true", "yes", "y", "on":
161+
return true, true
162+
case "0", "false", "no", "n", "off":
163+
return false, true
164+
default:
165+
return false, false
166+
}
167+
}
168+
169+
// enabledByEnvOrStage implements:
170+
// - if env var set to true/1/yes -> enabled
171+
// - if env var set to false/0/no -> disabled
172+
// - if env var unset/empty -> defaultByStage[idx]
173+
// - idx 0 = dev/default stage, index 1 = prod stage
174+
func enabledByEnvOrStage(envVar, stage string, defaultByStage [2]bool) bool {
175+
if raw, ok := os.LookupEnv(envVar); ok && strings.TrimSpace(raw) != "" {
176+
if b, ok2 := parseBoolish(raw); ok2 {
177+
return b
178+
}
179+
log.Warnf("LG:api-log-flag-invalid:%s value=%q (falling back to STAGE default)", envVar, raw)
180+
}
181+
st := strings.TrimSpace(strings.ToLower(stage))
182+
if st == "prod" || st == "production" {
183+
return defaultByStage[1]
184+
}
185+
// dev and all non-prod stages default to enabled
186+
return defaultByStage[0]
187+
}
188+
149189
// apiPathLoggerWithDB creates a middleware that logs API requests to DynamoDB
150190
func apiPathLoggerWithDB(apiLogsRepo api_logs.Repository) func(http.Handler) http.Handler {
191+
// No-op when API logging is disabled. This prevents nil deref panics if the middleware
192+
// remains in the handler chain but repo creation is skipped.
193+
if apiLogsRepo == nil {
194+
return func(next http.Handler) http.Handler { return next }
195+
}
151196
return func(next http.Handler) http.Handler {
152197
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
153198
log.Infof("LG:api-request-path:%s", r.URL.Path)
@@ -177,6 +222,8 @@ func apiPathLoggerWithDB(apiLogsRepo api_logs.Repository) func(http.Handler) htt
177222
}
178223

179224
// server function called by environment specific server functions
225+
//
226+
//nolint:gocyclo
180227
func server(localMode bool) http.Handler {
181228
f := logrus.Fields{
182229
"functionName": "cmd.server",
@@ -208,6 +255,28 @@ func server(localMode bool) http.Handler {
208255

209256
stage := viper.GetString("STAGE")
210257
dynamodbRegion := ini.GetProperty("DYNAMODB_AWS_REGION")
258+
ddbAPILoggingEnabled := enabledByEnvOrStage(envDDBAPILogging, stage, [2]bool{true, false})
259+
otelDatadogEnabled := enabledByEnvOrStage(envOtelDatadogAPILogging, stage, [2]bool{true, true})
260+
261+
// Initialize OTel SDK -> Datadog Lambda Extension (OTLP) once at cold start.
262+
// If init fails, disable OTel logging but never fail startup.
263+
if otelDatadogEnabled {
264+
version := strings.TrimSpace(Commit)
265+
if strings.TrimSpace(version) == "" {
266+
version = Version
267+
}
268+
if strings.TrimSpace(version) == "" {
269+
version = "unknown"
270+
}
271+
if er := telemetry.InitDatadogOTel(telemetry.DatadogOTelConfig{
272+
Stage: stage,
273+
Service: "easycla-backend",
274+
Version: version,
275+
}); er != nil {
276+
log.Infof("LG:otel-datadog-disabled err=%v", er)
277+
otelDatadogEnabled = false
278+
}
279+
}
211280

212281
log.WithFields(f).Infof("Service %s starting...", ini.ServiceName)
213282

@@ -221,6 +290,8 @@ func server(localMode bool) http.Handler {
221290
log.Infof("Golang OS : %s", runtime.GOOS)
222291
log.Infof("Golang Arch : %s", runtime.GOARCH)
223292
log.Infof("DYANAMODB_AWS_REGION : %s", dynamodbRegion)
293+
log.Infof("DDB_API_LOGGING : %t", ddbAPILoggingEnabled)
294+
log.Infof("OTEL_DATADOG_API_LOGGING: %t", otelDatadogEnabled)
224295
log.Infof("GH_ORG_VALIDATION : %t", githubOrgValidation)
225296
log.Infof("COMPANY_USER_VALIDATION : %t", companyUserValidation)
226297
log.Infof("STAGE : %s", stage)
@@ -239,6 +310,8 @@ func server(localMode bool) http.Handler {
239310
f["companyUserValidation"] = companyUserValidation
240311
f["stage"] = stage
241312
f["serviceHost"] = host
313+
f["ddbAPILogging"] = ddbAPILoggingEnabled
314+
f["otelDatadog"] = otelDatadogEnabled
242315
log.WithFields(f).Info("config")
243316
}
244317

@@ -305,7 +378,14 @@ func server(localMode bool) http.Handler {
305378
approvalListRepo := approval_list.NewRepository(awsSession, stage)
306379
v1CompanyRepo := v1Company.NewRepository(awsSession, stage)
307380
eventsRepo := events.NewRepository(awsSession, stage)
308-
apiLogsRepo := api_logs.NewRepository(stage, dynamodb.New(awsSession))
381+
382+
var apiLogsRepo api_logs.Repository
383+
if ddbAPILoggingEnabled {
384+
apiLogsRepo = api_logs.NewRepository(stage, dynamodb.New(awsSession))
385+
} else {
386+
apiLogsRepo = nil
387+
}
388+
309389
v1ProjectClaGroupRepo := projects_cla_groups.NewRepository(awsSession, stage)
310390
v1CLAGroupRepo := repository.NewRepository(awsSession, stage, gitV1Repository, gerritRepo, v1ProjectClaGroupRepo)
311391
metricsRepo := metrics.NewRepository(awsSession, stage, configFile.APIGatewayURL, v1ProjectClaGroupRepo)
@@ -497,6 +577,12 @@ func server(localMode bool) http.Handler {
497577
v2API.Serve(middlewareSetupfunc), v2SwaggerSpec.BasePath()),
498578
configFile.AllowedOrigins)
499579
}
580+
581+
// OTel/Datadog (OTLP -> Datadog Lambda Extension) - enabled by flag
582+
if otelDatadogEnabled {
583+
apiHandler = telemetry.WrapHTTPHandler(apiHandler)
584+
}
585+
500586
return apiHandler
501587
}
502588

cla-backend-go/go.mod

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,15 @@ require (
2929
github.com/gofrs/uuid v4.0.0+incompatible
3030
github.com/golang/mock v1.6.0
3131
github.com/google/go-github/v37 v37.0.0
32-
github.com/google/uuid v1.1.4
32+
github.com/google/uuid v1.6.0
3333
github.com/gorilla/sessions v1.2.1 // indirect
3434
github.com/imroc/req v0.3.0
3535
github.com/jessevdk/go-flags v1.4.0
3636
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
3737
github.com/jmoiron/sqlx v1.2.0
3838
github.com/juju/mempool v0.0.0-20160205104927-24974d6c264f // indirect
3939
github.com/juju/zip v0.0.0-20160205105221-f6b1e93fa2e2
40-
github.com/kr/pretty v0.3.0 // indirect
40+
github.com/kr/pretty v0.3.1 // indirect
4141
github.com/leodido/go-urn v1.2.1 // indirect
4242
github.com/mattn/go-isatty v0.0.17 // indirect
4343
github.com/mitchellh/mapstructure v1.5.0
@@ -51,45 +51,55 @@ require (
5151
github.com/sirupsen/logrus v1.9.3
5252
github.com/spf13/cobra v1.7.0
5353
github.com/spf13/viper v1.12.0
54-
github.com/stretchr/testify v1.8.4
54+
github.com/stretchr/testify v1.11.1
5555
github.com/verdverm/frisby v0.0.0-20170604211311-b16556248a9a
5656
github.com/xanzy/go-gitlab v0.50.1
5757
go.uber.org/ratelimit v0.1.0
58-
golang.org/x/crypto v0.7.0 // indirect
58+
golang.org/x/crypto v0.47.0 // indirect
5959
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d // indirect
60-
golang.org/x/net v0.8.0
61-
golang.org/x/oauth2 v0.6.0
62-
golang.org/x/sync v0.2.0
63-
golang.org/x/sys v0.33.0 // indirect
60+
golang.org/x/net v0.49.0
61+
golang.org/x/oauth2 v0.34.0
62+
golang.org/x/sync v0.19.0
63+
golang.org/x/sys v0.40.0 // indirect
6464
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e
65-
google.golang.org/protobuf v1.30.0 // indirect
65+
google.golang.org/protobuf v1.36.11 // indirect
6666
)
6767

6868
require (
6969
github.com/aws/aws-sdk-go-v2/service/s3 v1.53.1
7070
github.com/bradleyfalzon/ghinstallation/v2 v2.2.0
7171
github.com/golang-jwt/jwt v3.2.2+incompatible
7272
github.com/golang-jwt/jwt/v4 v4.5.0
73+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0
74+
go.opentelemetry.io/otel v1.40.0
75+
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0
76+
go.opentelemetry.io/otel/sdk v1.40.0
7377
)
7478

7579
require (
7680
github.com/ProtonMail/go-crypto v0.0.0-20230321155629-9a39f2531310 // indirect
7781
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
7882
github.com/aws/smithy-go v1.20.2 // indirect
83+
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
84+
github.com/cespare/xxhash/v2 v2.3.0 // indirect
7985
github.com/cloudflare/circl v1.3.2 // indirect
8086
github.com/docker/go-units v0.4.0 // indirect
8187
github.com/fatih/color v1.15.0 // indirect
88+
github.com/felixge/httpsnoop v1.0.4 // indirect
8289
github.com/fsnotify/fsnotify v1.5.4 // indirect
8390
github.com/gin-contrib/sse v0.1.0 // indirect
91+
github.com/go-logr/logr v1.4.3 // indirect
92+
github.com/go-logr/stdr v1.2.2 // indirect
8493
github.com/go-openapi/analysis v0.21.4 // indirect
8594
github.com/go-openapi/jsonpointer v0.19.5 // indirect
8695
github.com/go-openapi/jsonreference v0.20.0 // indirect
8796
github.com/go-playground/locales v0.13.0 // indirect
8897
github.com/go-playground/universal-translator v0.17.0 // indirect
89-
github.com/golang/protobuf v1.5.3 // indirect
98+
github.com/golang/protobuf v1.5.4 // indirect
9099
github.com/google/go-github/v50 v50.2.0 // indirect
91100
github.com/google/go-querystring v1.1.0 // indirect
92101
github.com/gorilla/securecookie v1.1.1 // indirect
102+
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect
93103
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
94104
github.com/hashicorp/go-retryablehttp v0.6.8 // indirect
95105
github.com/hashicorp/hcl v1.0.0 // indirect
@@ -110,16 +120,23 @@ require (
110120
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
111121
github.com/pkg/errors v0.9.1 // indirect
112122
github.com/pmezard/go-difflib v1.0.0 // indirect
113-
github.com/rogpeppe/go-internal v1.6.1 // indirect
123+
github.com/rogpeppe/go-internal v1.14.1 // indirect
114124
github.com/spf13/afero v1.9.5 // indirect
115125
github.com/spf13/cast v1.5.0 // indirect
116126
github.com/spf13/jwalterweatherman v1.1.0 // indirect
117127
github.com/spf13/pflag v1.0.5 // indirect
118128
github.com/subosito/gotenv v1.4.1 // indirect
119129
github.com/ugorji/go/codec v1.2.6 // indirect
120130
go.mongodb.org/mongo-driver v1.10.1 // indirect
121-
golang.org/x/text v0.9.0 // indirect
122-
google.golang.org/appengine v1.6.7 // indirect
131+
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
132+
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 // indirect
133+
go.opentelemetry.io/otel/metric v1.40.0 // indirect
134+
go.opentelemetry.io/otel/trace v1.40.0 // indirect
135+
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
136+
golang.org/x/text v0.33.0 // indirect
137+
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect
138+
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect
139+
google.golang.org/grpc v1.78.0 // indirect
123140
gopkg.in/ini.v1 v1.67.0 // indirect
124141
gopkg.in/yaml.v2 v2.4.0 // indirect
125142
gopkg.in/yaml.v3 v3.0.1 // indirect

0 commit comments

Comments
 (0)