Skip to content

Commit 4c3bbc2

Browse files
authored
lwc-events: guard against exception from extractValue (#1745)
Ensure that if the extractValue implementation for an event throws an exception, it will not impact processing other events. If the extract fails, then it will treat the value as `null`.
1 parent b108ee9 commit 4c3bbc2

File tree

6 files changed

+41
-15
lines changed

6 files changed

+41
-15
lines changed

atlas-lwc-events/src/main/scala/com/netflix/atlas/lwc/events/AbstractLwcEventClient.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ abstract class AbstractLwcEventClient(clock: Clock) extends LwcEventClient {
6767
expr.dataExpr,
6868
clock,
6969
sub.step,
70-
Some(event => expr.projectionKeys.map(event.extractValue)),
70+
Some(event => expr.projectionKeys.map(event.extractValueSafe)),
7171
submit
7272
)
7373
EventHandler(

atlas-lwc-events/src/main/scala/com/netflix/atlas/lwc/events/DatapointConverter.scala

+3-3
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,8 @@ private[events] object DatapointConverter {
9191
case Some(k) =>
9292
tags.get("statistic") match {
9393
case Some("count") => _ => 1.0
94-
case Some("totalOfSquares") => event => squared(event.extractValue(k), event.value)
95-
case _ => event => toDouble(event.extractValue(k), event.value)
94+
case Some("totalOfSquares") => event => squared(event.extractValueSafe(k), event.value)
95+
case _ => event => toDouble(event.extractValueSafe(k), event.value)
9696
}
9797
case None =>
9898
event => toDouble(event.value, 1.0)
@@ -339,7 +339,7 @@ private[events] object DatapointConverter {
339339

340340
private def getRawValue(event: LwcEvent): Any = {
341341
params.tags.get("value") match {
342-
case Some(k) => event.extractValue(k)
342+
case Some(k) => event.extractValueSafe(k)
343343
case None => event.value
344344
}
345345
}

atlas-lwc-events/src/main/scala/com/netflix/atlas/lwc/events/DatapointEvent.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ case class DatapointEvent(
5959
@scala.annotation.tailrec
6060
private def encodeColumns(columns: List[String], gen: JsonGenerator): Unit = {
6161
if (columns.nonEmpty) {
62-
Json.encode(gen, extractValue(columns.head))
62+
Json.encode(gen, extractValueSafe(columns.head))
6363
encodeColumns(columns.tail, gen)
6464
}
6565
}

atlas-lwc-events/src/main/scala/com/netflix/atlas/lwc/events/LwcEvent.scala

+23-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ package com.netflix.atlas.lwc.events
1717

1818
import com.fasterxml.jackson.core.JsonGenerator
1919
import com.netflix.atlas.json.Json
20+
import com.netflix.spectator.api.Spectator
21+
import org.slf4j.LoggerFactory
2022

2123
import java.io.StringWriter
2224
import scala.util.Using
@@ -48,7 +50,7 @@ trait LwcEvent {
4850
* to ensure the two are consistent.
4951
*/
5052
def tagValue(key: String): String = {
51-
extractValue(key) match {
53+
extractValueSafe(key) match {
5254
case v: String => v
5355
case e: Enum[_] => e.name()
5456
case _ => null
@@ -61,6 +63,25 @@ trait LwcEvent {
6163
*/
6264
def extractValue(key: String): Any
6365

66+
/**
67+
* Internal method for extracting the value that handles exceptions if any. If an exception
68+
* is thrown, then the value will be treated as `null`. The underlying exception will be
69+
* logged at trace level as it can be quite noisy if it is a common pattern across events.
70+
*/
71+
private[events] final def extractValueSafe(key: String): Any = {
72+
try {
73+
extractValue(key)
74+
} catch {
75+
case e: Exception =>
76+
LoggerFactory.getLogger(getClass).trace(s"failed to extract value for key: $key", e)
77+
Spectator
78+
.globalRegistry()
79+
.counter("lwc.extractValueFailures", "error", e.getClass.getSimpleName)
80+
.increment()
81+
null
82+
}
83+
}
84+
6485
/** Encode the raw event as JSON. */
6586
def encode(gen: JsonGenerator): Unit = {
6687
Json.encode(gen, rawEvent)
@@ -78,7 +99,7 @@ trait LwcEvent {
7899
def encodeAsRow(columns: List[String], gen: JsonGenerator): Unit = {
79100
gen.writeStartArray()
80101
columns.foreach { key =>
81-
Json.encode(gen, extractValue(key))
102+
Json.encode(gen, extractValueSafe(key))
82103
}
83104
gen.writeEndArray()
84105
}

atlas-lwc-events/src/test/scala/com/netflix/atlas/lwc/events/LwcEventDurationSuite.scala

+4-4
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,13 @@ class LwcEventDurationSuite extends FunSuite {
4949
}
5050

5151
test("extractValue: exists") {
52-
assertEquals(sampleLwcEvent.extractValue("app"), "www")
53-
assertEquals(sampleLwcEvent.extractValue("node"), "i-123")
54-
assertEquals(sampleLwcEvent.extractValue("duration"), Duration.ofMillis(42))
52+
assertEquals(sampleLwcEvent.extractValueSafe("app"), "www")
53+
assertEquals(sampleLwcEvent.extractValueSafe("node"), "i-123")
54+
assertEquals(sampleLwcEvent.extractValueSafe("duration"), Duration.ofMillis(42))
5555
}
5656

5757
test("extractValue: missing") {
58-
assertEquals(sampleLwcEvent.extractValue("foo"), null)
58+
assertEquals(sampleLwcEvent.extractValueSafe("foo"), null)
5959
}
6060

6161
test("defautl value") {

atlas-lwc-events/src/test/scala/com/netflix/atlas/lwc/events/LwcEventSuite.scala

+9-4
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,17 @@ class LwcEventSuite extends FunSuite {
4848
}
4949

5050
test("extractValue: exists") {
51-
assertEquals(sampleLwcEvent.extractValue("app"), "www")
52-
assertEquals(sampleLwcEvent.extractValue("node"), "i-123")
53-
assertEquals(sampleLwcEvent.extractValue("duration"), 42L)
51+
assertEquals(sampleLwcEvent.extractValueSafe("app"), "www")
52+
assertEquals(sampleLwcEvent.extractValueSafe("node"), "i-123")
53+
assertEquals(sampleLwcEvent.extractValueSafe("duration"), 42L)
5454
}
5555

5656
test("extractValue: missing") {
57-
assertEquals(sampleLwcEvent.extractValue("foo"), null)
57+
assertEquals(sampleLwcEvent.extractValueSafe("foo"), null)
58+
}
59+
60+
test("extractValue: exception thrown") {
61+
assertEquals(sampleLwcEvent.extractValueSafe("npe"), null)
5862
}
5963

6064
test("toJson: raw event") {
@@ -87,6 +91,7 @@ object LwcEventSuite {
8791
case "tags" => span.tags
8892
case "duration" => span.duration
8993
case "level" => Logger.Level.TRACE
94+
case "npe" => throw new NullPointerException()
9095
case k => span.tags.getOrElse(k, null)
9196
}
9297
}

0 commit comments

Comments
 (0)