@@ -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
150190func 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
180227func 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
0 commit comments