-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Basic reporting through toString #220
Conversation
You are correct. Currently, we have only one 'real' implementation here https://github.com/typelevel/otel4s/blob/main/java/all/src/main/scala/org/typelevel/otel4s/java/OtelJava.scala#L67. We can override the The OpenTelemetry classes in Java, like For now, it seems like delegating should be enough. |
So, I sat in a call with @armanbilge and wrote one test case to observe the output of JOpenTelemetry's noop implementation's toString method, and it's...not pretty.
Wanted to get some opinions on how to proceed. |
@@ -68,6 +68,8 @@ object OtelJava { | |||
def propagators: ContextPropagators[F] = contextPropagators | |||
def meterProvider: MeterProvider[F] = metrics.meterProvider | |||
def tracerProvider: TracerProvider[F] = traces.tracerProvider | |||
|
|||
override def toString: String = jOtel.toString() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if the Java OpenTelemetry#toString
is not guaranteed to be nice in general, but we can still use propgators.toString
, meterProvider.toString
, tracerProvider.toString
etc. to put together our own toString
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The uninformative .toString
you're getting is the default from java.lang.Object
.
In general, the Java Otel API is built on interfaces. I wrote 99% of my Java before interfaces could have default implementations, and I don't know whether an interface can override java.lang.Object#toString
. Legal or not, I can confirm some, including OpenTelemetry
, don't. "Not guaranteed to be nice" is correct.
Some implementations have a .toString
, like DefaultContextPropagators
. Some do not, like DefaultTracerProvider
. So you'd make some progress going down that road, but won't be rid of the hexadecimal hashCodes, and ... there can be multiple implementations of these interfaces. See "no guarantees."
The golden rule of software quality tells us to upstream better .toString
s to Java, but that's not what @sherriesyt volunteered for. If that's interesting anyway, I can help. If it's not... I've been writing Java for a quarter century, and another hour probably wouldn't kill me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't mind dabbling in Java (though I'm rusty) -- can you point me to where to start? Is this somewhere in opentelemetry-java?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. The noop
value in your test is implemented here, and a .toString
in that class would make it nicer.
The one I would expect to see in production is here, and already has a thorough toString
. So while the easy one to test is in rough shape, the good news is the common production one should already work pretty well!
If you decide to chase this upstream, the build tool is Gradle. I've used gradle publishToMavenLocal
before. You can see the version number it generates in the output (I don't have it handy to be exact), and you can put that version into build.sbt to see how your work interacts with Otel4s.
There are a lot of checks for formatting and licenses and what not. There's a
|
Ah, you got around to commenting first, I pushed and in the midst of writing up a comment here my stomach went "I ache now."1 I did the lazy2 thing and tried out the other One small question out of this is that I don't have a clean way of asserting that the output equals some expected string, because (e.g.) One bigger question: sorry, you slightly lost me at Gradle. Would I be building the Java SDK (cloning their repo and so forth) using Gradle (presumably with my tiny local change) and then feeding the version of the thing I built into the build.sbt of Otel4s? (and why 1: all fine now, I was happy with my dinner tonight, my stomach less so 2: lazy because I didn't fix the upstream problem first |
Until the upstream 'toString' of noop SDK is addressed, can we detect the noop instance by comparing it with For example: val isNoop = jOtel == JOpenTelemetry.noop()
new Otel4s {
def toString = if (isNoop) "Noop - and something informative there if we want :)" else jOtel.toString()
}
We can dynamically inject value for the And that's how we configure it https://github.com/typelevel/otel4s/blob/main/build.sbt#L163-L167. Currently, we collect only Then, you can inject s"OpenTelemetrySdk{... telemetry.sdk.version=\"${BuildInfo.openTelemetrySdkVersion}\"}} ..." We do a similar trick in a different test - https://github.com/typelevel/otel4s/blob/main/java/metrics/src/test/scala/org/typelevel/otel4s/java/metrics/CounterSuite.scala#L56
This one is more complicated. As Ross suggested, we can contribute a meaningful For example, NoopTextMapPropagator has a custom |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One small question out of this is that I don't have a clean way of asserting that the output equals some expected string, because (e.g.)
NoopLogRecordProcessor@{insert-hex-here}
(that is part of thetoString
output) will always have some different hex code. Any advice on what assertion I can make in this test case?
Instead of checking for exact equality, you can do something like:
val got = (...).toString
assert(clue(got).startsWith("NoopLogRecordProcessor"))
(Anything wrapped in clue(...)
will be printed out if the assert
fails, to give a "clue" as to what went wrong.)
One bigger question: sorry, you slightly lost me at Gradle. Would I be building the Java SDK (cloning their repo and so forth) using Gradle (presumably with my tiny local change) and then feeding the version of the thing I built into the build.sbt of Otel4s?
Yup exactly, this is what Ross was getting at. Personally I don't think it's worth the fuss (sorry Ross ;) since it's hard to go wrong with a toString
, so there's little to gain by further testing it in our own codebase. But in theory it shouldn't be too hard to do.
(and why
gradle publishToMavenLocal
and not use Maven? Just a noob question of mine, I only have some experience with Maven and none with Gradle)
Hmm, "Maven" is a bit over-loaded. It is actually 2 (or 3 ...) things:
- a build tool
- a repository format for publishing artifacts
- Maven Central, a public repository where effectively the entire Java ecosystem publishes/retrieves artifacts
In this case, Gradle is the build tool, and publishToMavenLocal
publishes it to your computer's local, Maven-formatted repository. You can't use the Maven build tool, because the Java OTel SDK repository is using the Gradle build tool (similarly, we are using the sbt build tool instead of the Maven build tool).
test("JOtelSDK toString returns useful info") { | ||
assertEquals( | ||
JOpenTelemetrySdk.builder().build().toString(), | ||
"OpenTelemetrySdk{tracerProvider=SdkTracerProvider{clock=SystemClock{}, idGenerator=RandomIdGenerator{}, resource=Resource{schemaUrl=null, attributes={service.name=\"unknown_service:java\", telemetry.sdk.language=\"java\", telemetry.sdk.name=\"opentelemetry\", telemetry.sdk.version=\"1.26.0\"}}, spanLimitsSupplier=SpanLimitsValue{maxNumberOfAttributes=128, maxNumberOfEvents=128, maxNumberOfLinks=128, maxNumberOfAttributesPerEvent=128, maxNumberOfAttributesPerLink=128, maxAttributeValueLength=2147483647}, sampler=ParentBased{root:AlwaysOnSampler,remoteParentSampled:AlwaysOnSampler,remoteParentNotSampled:AlwaysOffSampler,localParentSampled:AlwaysOnSampler,localParentNotSampled:AlwaysOffSampler}, spanProcessor=NoopSpanProcessor{}}, meterProvider=SdkMeterProvider{clock=SystemClock{}, resource=Resource{schemaUrl=null, attributes={service.name=\"unknown_service:java\", telemetry.sdk.language=\"java\", telemetry.sdk.name=\"opentelemetry\", telemetry.sdk.version=\"1.26.0\"}}, metricReaders=[], views=[]}, loggerProvider=SdkLoggerProvider{clock=SystemClock{}, resource=Resource{schemaUrl=null, attributes={service.name=\"unknown_service:java\", telemetry.sdk.language=\"java\", telemetry.sdk.name=\"opentelemetry\", telemetry.sdk.version=\"1.26.0\"}}, logLimits=LogLimits{maxNumberOfAttributes=128, maxAttributeValueLength=2147483647}, logRecordProcessor=io.opentelemetry.sdk.logs.NoopLogRecordProcessor@59364403}, propagators=DefaultContextPropagators{textMapPropagator=NoopTextMapPropagator}}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, this is something 😂
As a next step, can we adjust the test to build an Otel4s[IO]
with the JOpenTelemetrySdk
and check its toString
? Currently this test is not exercising otel4s code at all 😛
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉 lgtm!
Eventually, we will get a better toString from the upstream: open-telemetry/opentelemetry-java#5493. |
I'm happy with the current approach, thanks @sherriesyt! |
Thanks @iRevive for the feedback & for improving the Java |
Yeah, I don't want to overspecify the upstream library's toString in our own tests. My golden rule comment was wanting to make sure that any improvements we make happen upstream, like the one Maksym linked. I think we're all good here. |
Basic reporting through toString
This is super not ready, just opening an early draft for visibility. Meant for #188.
I think I realized after this first commit that since Otel4s is a trait, the implementation of
toString
shouldn't lie here (instead, it should just specify that a String is returned) -- is this correct? (And we leave the implementation to all the things with typeOtel4s[F[_]]
/ all the things that extendOtel4s[F[_]]
?)