Ma77Ball commented on code in PR #5375:
URL: https://github.com/apache/texera/pull/5375#discussion_r3769965361


##########
common/observability/src/main/scala/org/apache/texera/observability/TexeraOtelLogAppender.scala:
##########
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.texera.observability
+
+import ch.qos.logback.classic.Level
+import ch.qos.logback.classic.spi.{ILoggingEvent, IThrowableProxy, 
ThrowableProxyUtil}
+import ch.qos.logback.core.UnsynchronizedAppenderBase
+import io.opentelemetry.api.OpenTelemetry
+import io.opentelemetry.api.common.AttributeKey
+import io.opentelemetry.api.logs.{Logger, Severity}
+import io.opentelemetry.api.trace.Span
+import io.opentelemetry.context.Context
+
+import java.util.concurrent.TimeUnit
+
+/**
+  * Logback appender that sanitizes each event via [[LogSanitizer]] and
+  * emits it as an OTel LogRecord. [[append]] is a no-op until [[bind]]
+  * is called and after [[stop]].
+  *
+  * This is internal plumbing, not the developer logging API. Code logs
+  * through the normal SLF4J / scala-logging interface and adds correlation
+  * ids via MDC; [[OtelInit.init]] attaches this appender to the ROOT logger
+  * so those records also reach OTel:
+  *
+  * {{{
+  *   class Foo extends LazyLogging {
+  *     MDC.put("workflowId", id)      // forwarded as an OTel log attribute
+  *     try logger.info("started")     // body + severity + trace context
+  *     finally MDC.remove("workflowId")
+  *   }
+  * }}}
+  */
+class TexeraOtelLogAppender extends UnsynchronizedAppenderBase[ILoggingEvent] {
+
+  // @volatile so a late bind() is visible to appender threads.
+  @volatile private var otelLogger: Option[Logger] = None
+
+  def bind(otel: OpenTelemetry): Unit = {
+    otelLogger = Some(otel.getLogsBridge.get("texera.logback"))
+  }
+
+  override def stop(): Unit = {
+    otelLogger = None
+    super.stop()
+  }
+
+  override def append(event: ILoggingEvent): Unit = {
+    otelLogger match {
+      case None => () // not bound
+      case Some(logger) =>
+        try {
+          emit(logger, event)
+        } catch {
+          // An appender must not throw into the calling thread.
+          case t: Throwable =>
+            addError("OTel log emission failed", t)
+        }
+    }
+  }
+
+  private def emit(logger: Logger, event: ILoggingEvent): Unit = {
+    // Append the stack trace to the body when a throwable is attached.
+    val baseBody = LogSanitizer.sanitize(event.getFormattedMessage)
+    val body = Option(event.getThrowableProxy) match {
+      case Some(proxy) =>
+        // Skip the C0 strip so trace newlines survive, but still cap.
+        LogSanitizer.truncate(baseBody + "\n" + formatThrowable(proxy))

Review Comment:
   Fixed. Secrets are now redacted across the whole record including the stack 
trace; control-char stripping still only touches the message so trace line 
breaks survive. Added a test.



##########
common/observability/src/main/scala/org/apache/texera/observability/OtelInit.scala:
##########
@@ -0,0 +1,382 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.texera.observability
+
+import com.typesafe.scalalogging.LazyLogging
+import io.opentelemetry.api.{GlobalOpenTelemetry, OpenTelemetry}
+import io.opentelemetry.api.common.{AttributeKey, Attributes}
+import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporter
+import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter
+import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter
+import io.opentelemetry.sdk.OpenTelemetrySdk
+import io.opentelemetry.sdk.logs.SdkLoggerProvider
+import io.opentelemetry.sdk.logs.`export`.{BatchLogRecordProcessor, 
LogRecordExporter}
+import io.opentelemetry.sdk.metrics.SdkMeterProvider
+import io.opentelemetry.sdk.metrics.`export`.{MetricExporter, 
PeriodicMetricReader}
+import io.opentelemetry.sdk.resources.Resource
+import io.opentelemetry.sdk.trace.SdkTracerProvider
+import io.opentelemetry.sdk.trace.`export`.{BatchSpanProcessor, SpanExporter}
+
+import java.net.URI
+import java.time.Duration
+import scala.util.{Failure, Success, Try}
+
+/**
+  * Bootstraps the OpenTelemetry SDK for a Texera service.
+  *
+  * Enabled by default; set OTEL_SDK_DISABLED=true to turn it off. Reads
+  * OTEL_* env vars, validates the endpoint against an allowlist, builds
+  * tracer/log/metric providers, and attaches a Logback appender.
+  * Returns None when disabled or misconfigured; never throws.
+  */
+object OtelInit extends LazyLogging {
+
+  /** Endpoint schemes we accept. */
+  private[observability] val AllowedSchemes: Set[String] = Set("http", 
"https", "grpc")

Review Comment:
   Good catch. Allowlist is now `{http, https}` only, matching what the 
exporter accepts. Spec updated.



##########
common/observability/src/test/scala/org/apache/texera/observability/TexeraOtelLogAppenderSpec.scala:
##########
@@ -0,0 +1,173 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.texera.observability
+
+import ch.qos.logback.classic.{Level, Logger, LoggerContext}
+import ch.qos.logback.classic.spi.LoggingEvent
+import io.opentelemetry.api.OpenTelemetry
+import io.opentelemetry.api.logs.Severity
+import io.opentelemetry.sdk.OpenTelemetrySdk
+import io.opentelemetry.sdk.logs.SdkLoggerProvider
+import io.opentelemetry.sdk.logs.`export`.SimpleLogRecordProcessor
+import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter
+import org.scalatest.flatspec.AnyFlatSpec
+import org.scalatest.matchers.should.Matchers
+import org.slf4j.LoggerFactory
+
+import scala.jdk.CollectionConverters._
+
+class TexeraOtelLogAppenderSpec extends AnyFlatSpec with Matchers {
+
+  /** Build an OpenTelemetry SDK whose LoggerProvider drains to the
+    *  given in-memory exporter via the synchronous SimpleLogRecordProcessor,
+    *  so tests don't depend on batch timing.
+    */
+  private def newFixture(): (OpenTelemetry, InMemoryLogRecordExporter, 
TexeraOtelLogAppender) = {
+    val exporter = InMemoryLogRecordExporter.create()
+    val lp = SdkLoggerProvider
+      .builder()
+      .addLogRecordProcessor(SimpleLogRecordProcessor.create(exporter))
+      .build()
+    val sdk = OpenTelemetrySdk.builder().setLoggerProvider(lp).build()
+    val appender = new TexeraOtelLogAppender()
+    
appender.setContext(LoggerFactory.getILoggerFactory.asInstanceOf[LoggerContext])
+    appender.bind(sdk)
+    appender.start()
+    (sdk, exporter, appender)
+  }
+
+  private def makeEvent(
+      message: String,
+      level: Level = Level.INFO,
+      mdc: Map[String, String] = Map.empty
+  ): LoggingEvent = {
+    val ctx = LoggerFactory.getILoggerFactory.asInstanceOf[LoggerContext]
+    val logger = ctx.getLogger("test.logger").asInstanceOf[Logger]
+    val ev = new LoggingEvent("fqcn", logger, level, message, null, null)
+    if (mdc.nonEmpty) ev.setMDCPropertyMap(mdc.asJava)
+    ev
+  }
+
+  // ----- positive paths -------------------------------------------------
+
+  "TexeraOtelLogAppender" should "emit an INFO record with body + severity" in 
{
+    val (_, exporter, appender) = newFixture()
+    appender.doAppend(makeEvent("hello world"))
+
+    val records = exporter.getFinishedLogRecordItems.asScala
+    records should have size 1
+    records.head.getBodyValue.asString shouldBe "hello world"
+    records.head.getSeverity shouldBe Severity.INFO
+    records.head.getSeverityText shouldBe "INFO"
+  }
+
+  it should "map every log level to a distinct OTel severity" in {
+    val (_, exporter, appender) = newFixture()
+    Seq(Level.TRACE, Level.DEBUG, Level.INFO, Level.WARN, Level.ERROR).foreach 
{ lvl =>
+      appender.doAppend(makeEvent(s"msg-$lvl", lvl))
+    }
+    val severities = 
exporter.getFinishedLogRecordItems.asScala.map(_.getSeverity).toSet
+    severities shouldBe Set(
+      Severity.TRACE,
+      Severity.DEBUG,
+      Severity.INFO,
+      Severity.WARN,
+      Severity.ERROR
+    )
+  }
+
+  // ----- security: sanitisation happens at the boundary -----------------
+
+  it should "strip CRLF from a forged log-injection payload before emission" 
in {
+    val (_, exporter, appender) = newFixture()
+    appender.doAppend(makeEvent("hello\r\nFAKE LOG LINE\r\nworld"))
+
+    val body = 
exporter.getFinishedLogRecordItems.asScala.head.getBodyValue.asString
+    body shouldBe "helloFAKE LOG LINEworld"
+    body should not include "\n"
+    body should not include "\r"
+  }
+
+  it should "redact Bearer tokens at emission time" in {
+    val (_, exporter, appender) = newFixture()
+    appender.doAppend(makeEvent("Authorization: Bearer abc123XYZ.foo"))
+
+    val body = 
exporter.getFinishedLogRecordItems.asScala.head.getBodyValue.asString
+    body should include("[REDACTED]")
+    body should not include "abc123XYZ"
+  }
+
+  it should "truncate a 1 MiB body to MaxBodyBytes with the marker" in {
+    val (_, exporter, appender) = newFixture()
+    val oversize = "x" * (1024 * 1024)
+    appender.doAppend(makeEvent(oversize))
+
+    val body = 
exporter.getFinishedLogRecordItems.asScala.head.getBodyValue.asString
+    body.length shouldBe LogSanitizer.MaxBodyBytes
+    body should endWith(LogSanitizer.TruncatedMarker)
+  }
+
+  // ----- security: MDC allowlist ----------------------------------------
+
+  it should "forward only allowlisted MDC keys as log attributes" in {
+    val (_, exporter, appender) = newFixture()
+    appender.doAppend(
+      makeEvent(
+        "msg",
+        mdc = Map(
+          "trace_id" -> "abc",
+          "texera.workflow.id" -> "42",
+          "secret" -> "should-not-leak",
+          "password" -> "p4ssw0rd"
+        )
+      )
+    )
+
+    val record = exporter.getFinishedLogRecordItems.asScala.head
+    val attrs = record.getAttributes.asMap.asScala.iterator.map {
+      case (k, v) => k.getKey -> v.toString
+    }.toMap
+
+    attrs.keySet should contain allOf ("trace_id", "texera.workflow.id")
+    attrs.keySet should not contain ("secret")

Review Comment:
   Fixed at the source, not by relaxing the test. `filterMdc` now redacts the 
values of credential-named keys (password, secret, token, ...).



##########
common/observability/src/main/scala/org/apache/texera/observability/OtelInit.scala:
##########
@@ -0,0 +1,382 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.texera.observability
+
+import com.typesafe.scalalogging.LazyLogging
+import io.opentelemetry.api.{GlobalOpenTelemetry, OpenTelemetry}
+import io.opentelemetry.api.common.{AttributeKey, Attributes}
+import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporter
+import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter
+import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter
+import io.opentelemetry.sdk.OpenTelemetrySdk
+import io.opentelemetry.sdk.logs.SdkLoggerProvider
+import io.opentelemetry.sdk.logs.`export`.{BatchLogRecordProcessor, 
LogRecordExporter}
+import io.opentelemetry.sdk.metrics.SdkMeterProvider
+import io.opentelemetry.sdk.metrics.`export`.{MetricExporter, 
PeriodicMetricReader}
+import io.opentelemetry.sdk.resources.Resource
+import io.opentelemetry.sdk.trace.SdkTracerProvider
+import io.opentelemetry.sdk.trace.`export`.{BatchSpanProcessor, SpanExporter}
+
+import java.net.URI
+import java.time.Duration
+import scala.util.{Failure, Success, Try}
+
+/**
+  * Bootstraps the OpenTelemetry SDK for a Texera service.
+  *
+  * Enabled by default; set OTEL_SDK_DISABLED=true to turn it off. Reads
+  * OTEL_* env vars, validates the endpoint against an allowlist, builds
+  * tracer/log/metric providers, and attaches a Logback appender.
+  * Returns None when disabled or misconfigured; never throws.
+  */
+object OtelInit extends LazyLogging {
+
+  /** Endpoint schemes we accept. */
+  private[observability] val AllowedSchemes: Set[String] = Set("http", 
"https", "grpc")
+
+  /** Hosts we accept for the OTLP endpoint by default. */
+  private[observability] val DefaultAllowedHosts: Set[String] = Set(
+    "localhost",
+    "127.0.0.1",
+    "::1",
+    "[::1]"
+  )
+
+  /** Default endpoint. 127.0.0.1 (not "localhost") to force IPv4 so a
+    *  natively-run service reaches the collector on dual-stack hosts.
+    */
+  private val DefaultEndpoint = "http://127.0.0.1:4317";
+
+  /** Metric export interval bounds; out-of-range values clamp to the
+    *  default (see clampIntervalMs).
+    */
+  private[observability] val MinMetricIntervalMs: Long = 1000L
+  private[observability] val MaxMetricIntervalMs: Long = 10L * 60L * 1000L
+  private[observability] val DefaultMetricIntervalMs: Long = 30L * 1000L
+
+  // Idempotency guard: init() is a no-op after the first call.
+  @volatile private var initialized: Option[OpenTelemetry] = None
+
+  /**
+    * Initialize the SDK for the given service name. Returns Some on
+    * success, None when disabled or misconfigured. When enabled, also
+    * attaches a [[TexeraOtelLogAppender]] to the Logback ROOT logger.
+    */
+  def init(serviceName: String): Option[OpenTelemetry] =
+    synchronized {
+      if (initialized.isDefined) return initialized
+
+      val env = (key: String) => Option(System.getenv(key))
+      val result = initInternal(
+        serviceName = serviceName,
+        envProvider = env,
+        spanExporterFactory = buildOtlpSpanExporter,
+        logExporterFactory = endpoint => Some(buildOtlpLogExporter(endpoint)),
+        metricExporterFactory = endpoint => 
Some(buildOtlpMetricExporter(endpoint)),
+        logbackAttacher = LogbackBinder.attach
+      )
+      // Register globally so OTel-aware code can use GlobalOpenTelemetry
+      // without threading the SDK through callsites. set() throws on a
+      // second call; wrap defensively.
+      result.foreach { sdk =>
+        Try(GlobalOpenTelemetry.set(sdk)).failed.foreach { t =>
+          logger.warn(
+            s"GlobalOpenTelemetry already set; using the existing instance: 
${t.getMessage}"
+          )
+        }
+      }
+      result
+    }
+
+  /**
+    * Test-only entry point: injects an env-var map and exporters so the
+    * SDK makes no network connection. Does not attach the Logback appender.
+    */
+  private[observability] def initForTest(
+      serviceName: String,
+      envOverride: Map[String, String],
+      exporter: SpanExporter,
+      metricExporter: Option[MetricExporter] = None
+  ): Option[OpenTelemetry] =
+    synchronized {
+      initInternal(
+        serviceName = serviceName,
+        envProvider = envOverride.get,
+        spanExporterFactory = _ => exporter,
+        logExporterFactory = _ => None,
+        metricExporterFactory = _ => metricExporter,
+        logbackAttacher = (_, _) => () // no-op in tests
+      )
+    }
+
+  /** Test-only: forget any previously-installed SDK. Does not unregister
+    * shutdown hooks (the previous SDK is closed instead).
+    */
+  private[observability] def resetForTest(): Unit =
+    synchronized {
+      initialized.foreach {
+        case sdk: OpenTelemetrySdk =>
+          Try(sdk.getSdkTracerProvider.close())
+          Try(sdk.getSdkLoggerProvider.close())
+          Try(sdk.getSdkMeterProvider.close())
+        case _ => ()
+      }
+      initialized = None
+    }
+
+  private def initInternal(
+      serviceName: String,
+      envProvider: String => Option[String],
+      spanExporterFactory: String => SpanExporter,
+      logExporterFactory: String => Option[LogRecordExporter],
+      metricExporterFactory: String => Option[MetricExporter],
+      logbackAttacher: (String, OpenTelemetry) => Unit
+  ): Option[OpenTelemetry] = {
+    if (initialized.isDefined) return initialized
+
+    // Enabled by default; OTEL_SDK_DISABLED=true opts out. An
+    // unreachable endpoint drops records without crashing the service.
+    val disabled = envProvider("OTEL_SDK_DISABLED").getOrElse("false")

Review Comment:
   Done both. Flipped to default-off per #5367, and routed all five OTEL_* 
knobs through `observability.conf` (plus `EnvironmentalVariable`, `.env`, and 
`values.yaml`).



##########
common/observability/src/main/scala/org/apache/texera/observability/OtelInit.scala:
##########
@@ -0,0 +1,382 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.texera.observability
+
+import com.typesafe.scalalogging.LazyLogging
+import io.opentelemetry.api.{GlobalOpenTelemetry, OpenTelemetry}
+import io.opentelemetry.api.common.{AttributeKey, Attributes}
+import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporter
+import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter
+import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter
+import io.opentelemetry.sdk.OpenTelemetrySdk
+import io.opentelemetry.sdk.logs.SdkLoggerProvider
+import io.opentelemetry.sdk.logs.`export`.{BatchLogRecordProcessor, 
LogRecordExporter}
+import io.opentelemetry.sdk.metrics.SdkMeterProvider
+import io.opentelemetry.sdk.metrics.`export`.{MetricExporter, 
PeriodicMetricReader}
+import io.opentelemetry.sdk.resources.Resource
+import io.opentelemetry.sdk.trace.SdkTracerProvider
+import io.opentelemetry.sdk.trace.`export`.{BatchSpanProcessor, SpanExporter}
+
+import java.net.URI
+import java.time.Duration
+import scala.util.{Failure, Success, Try}
+
+/**
+  * Bootstraps the OpenTelemetry SDK for a Texera service.
+  *
+  * Enabled by default; set OTEL_SDK_DISABLED=true to turn it off. Reads
+  * OTEL_* env vars, validates the endpoint against an allowlist, builds
+  * tracer/log/metric providers, and attaches a Logback appender.
+  * Returns None when disabled or misconfigured; never throws.
+  */
+object OtelInit extends LazyLogging {
+
+  /** Endpoint schemes we accept. */
+  private[observability] val AllowedSchemes: Set[String] = Set("http", 
"https", "grpc")
+
+  /** Hosts we accept for the OTLP endpoint by default. */
+  private[observability] val DefaultAllowedHosts: Set[String] = Set(
+    "localhost",
+    "127.0.0.1",
+    "::1",

Review Comment:
   Deleted; `[::1]` already covers IPv6 loopback.



##########
common/observability/src/main/scala/org/apache/texera/observability/LogSanitizer.scala:
##########
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.texera.observability
+
+import scala.jdk.CollectionConverters._
+
+/**
+  * Pure functions that sanitize log bodies and MDC before export:
+  * strip control characters, redact secrets, cap body size, and
+  * filter MDC down by dropping denied keys.
+  */
+object LogSanitizer {
+
+  /** Per-record body byte cap. */

Review Comment:
   Fixed the comment to "length cap, in chars".



##########
common/observability/src/main/scala/org/apache/texera/observability/OtelInit.scala:
##########
@@ -0,0 +1,382 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.texera.observability
+
+import com.typesafe.scalalogging.LazyLogging
+import io.opentelemetry.api.{GlobalOpenTelemetry, OpenTelemetry}
+import io.opentelemetry.api.common.{AttributeKey, Attributes}
+import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporter
+import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter
+import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter
+import io.opentelemetry.sdk.OpenTelemetrySdk
+import io.opentelemetry.sdk.logs.SdkLoggerProvider
+import io.opentelemetry.sdk.logs.`export`.{BatchLogRecordProcessor, 
LogRecordExporter}
+import io.opentelemetry.sdk.metrics.SdkMeterProvider
+import io.opentelemetry.sdk.metrics.`export`.{MetricExporter, 
PeriodicMetricReader}
+import io.opentelemetry.sdk.resources.Resource
+import io.opentelemetry.sdk.trace.SdkTracerProvider
+import io.opentelemetry.sdk.trace.`export`.{BatchSpanProcessor, SpanExporter}
+
+import java.net.URI
+import java.time.Duration
+import scala.util.{Failure, Success, Try}
+
+/**
+  * Bootstraps the OpenTelemetry SDK for a Texera service.
+  *
+  * Enabled by default; set OTEL_SDK_DISABLED=true to turn it off. Reads
+  * OTEL_* env vars, validates the endpoint against an allowlist, builds
+  * tracer/log/metric providers, and attaches a Logback appender.
+  * Returns None when disabled or misconfigured; never throws.
+  */
+object OtelInit extends LazyLogging {
+
+  /** Endpoint schemes we accept. */
+  private[observability] val AllowedSchemes: Set[String] = Set("http", 
"https", "grpc")
+
+  /** Hosts we accept for the OTLP endpoint by default. */
+  private[observability] val DefaultAllowedHosts: Set[String] = Set(
+    "localhost",
+    "127.0.0.1",
+    "::1",
+    "[::1]"
+  )
+
+  /** Default endpoint. 127.0.0.1 (not "localhost") to force IPv4 so a
+    *  natively-run service reaches the collector on dual-stack hosts.
+    */
+  private val DefaultEndpoint = "http://127.0.0.1:4317";
+
+  /** Metric export interval bounds; out-of-range values clamp to the

Review Comment:
   Both comments now say out-of-range values fall back to the default.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to