Skip to content
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

Add InMemoryLogExporter #3757

Merged
merged 5 commits into from
Oct 21, 2021
Merged

Conversation

jack-berg
Copy link
Member

@jack-berg jack-berg commented Oct 15, 2021

Continuation of #3749. A InMemoryLogExporter makes it easier to write tests for log appenders.

@jkwatson
Copy link
Contributor

Probably not actually a continuation of the one you reference, since that seems to be completely unrelated. ;)

*/
@Override
public CompletableResultCode export(Collection<LogData> logs) {
if (isStopped) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need this check for some reason? Would it hurt to just not worry about whether shutdown has been called or not?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a strong preference, but this is consistent with InMemorySpanExporter and InMemoryMetricExporter.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hrm. I wonder why that was done and why it matters.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps its easier to test the flush / shutdown methods from the SdkMeterProvider and SdkTracerProvider with a well behaved in memory exporter. 🤷

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I turned off the functionality in the Span version and it didn't seem to break anything. Agreed: 🤷🏽

@jack-berg
Copy link
Member Author

jack-berg commented Oct 15, 2021

Probably not actually a continuation of the one you reference, since that seems to be completely unrelated. ;)

Ha! #3749, not #3479. Oops, updated description.

/** A {@link LogExporter} implementation that can be used to test OpenTelemetry integration. */
public final class InMemoryLogExporter implements LogExporter {

// using LinkedBlockingQueue to avoid manual locks for thread-safe operations
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think we need this comment

public final class InMemoryLogExporter implements LogExporter {

// using LinkedBlockingQueue to avoid manual locks for thread-safe operations
private final Queue<LogData> finishedLogItems = new LinkedBlockingQueue<>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, I'd usually use ConcurrentLinkedQueue since we don't want any blocking behavior (in practice, the usage means it doesn't matter too much though)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I lifted this from InMemoryMetricExporter. Looking closer, InMemorySpanExporter is inconsistent as it uses ArrayList<>().

I'd like to make all three consistent, and change them all to use Collections.synchronizedList(new ArrayList<>()), which should thread safe, non-blocking (except for synchronization), and plenty fast for the use case. Thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no strong opinion. These classes are for testing, so any thread-safe, insertion-order preserving collection should do. And, yes, consistency would be good to have.

@codecov
Copy link

codecov bot commented Oct 19, 2021

Codecov Report

Merging #3757 (dfbb1ac) into main (7f6ac89) will increase coverage by 0.04%.
The diff coverage is 100.00%.

Impacted file tree graph

@@             Coverage Diff              @@
##               main    #3757      +/-   ##
============================================
+ Coverage     89.08%   89.13%   +0.04%     
- Complexity     3917     3951      +34     
============================================
  Files           470      472       +2     
  Lines         12344    12442      +98     
  Branches       1208     1225      +17     
============================================
+ Hits          10997    11090      +93     
+ Misses          945      938       -7     
- Partials        402      414      +12     
Impacted Files Coverage Δ
...telemetry/sdk/logs/export/InMemoryLogExporter.java 100.00% <100.00%> (ø)
...ry/sdk/metrics/testing/InMemoryMetricExporter.java 100.00% <100.00%> (ø)
...try/sdk/testing/exporter/InMemorySpanExporter.java 93.33% <100.00%> (-2.13%) ⬇️
...nsion/incubator/trace/data/DelegatingSpanData.java 82.41% <0.00%> (ø)
...entelemetry/sdk/trace/data/DelegatingSpanData.java 82.41% <0.00%> (ø)
...ntelemetry/sdk/extension/resources/OsResource.java 90.69% <0.00%> (+4.65%) ⬆️
...ntelemetry/sdk/logs/export/SimpleLogProcessor.java 88.57% <0.00%> (+5.71%) ⬆️
...ava/io/opentelemetry/sdk/internal/RateLimiter.java 100.00% <0.00%> (+5.88%) ⬆️
...metry/sdk/extension/resources/ProcessResource.java 87.50% <0.00%> (+6.25%) ⬆️
... and 2 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 7f6ac89...dfbb1ac. Read the comment docs.

* @return a {@code List} of the finished {@code Log}s.
*/
public List<LogData> getFinishedLogItems() {
synchronized (finishedLogItems) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Who'd have expected such nits on this PR :P

I don't know if it's an implementation detail or not that new ArrayList(list) should be threadsafe because it delegates to toArray, not using an iterator. If others agree that it should be safe, then we can remove the synchronized.

But I do know that it's unfortunate to have to take a lock anyways despite using a sychronized structure... I would suggest sticking to the ConcurrentLinkedQueue, or unsynchronized with lock in each method, vs this hybrid.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new ArrayList(list) does delegate to toArray(), but there shouldn't be any performance impact of this synchronized keyword because toArray() has to obtain a lock on the list instance already. So synchronized just obtains the lock earlier, and then its a reentrant lock so it doesn't matter.

So in this case, synchronized is just extra defense against the new ArrayList() implementation.

With that said, looking at the internals, ConcurrentLinkedQueue's toArray() implementation is free of synchronization. Since we don't care about size() of ConcurrentLinkedQueue not being constant time, I think its a good option 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Just want to clarify performance was never a concern for these testing classes :) Just didn't want inconsistency in the patterns for accessing the variable (some methods used synchronized, others don't). Now that's fixed :)

* @return a {@code List} of the finished {@code Log}s.
*/
public List<LogData> getFinishedLogItems() {
synchronized (finishedLogItems) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Who'd have expected such nits on this PR :P

I don't know if it's an implementation detail or not that new ArrayList(list) should be threadsafe because it delegates to toArray, not using an iterator.

But I do know that it's unfortunate to have to take a lock anyways despite using a sychronized structure... I would suggest sticking to the ConcurrentLinkedQueue, or unsynchronized with lock in each method, vs this hybrid.

@anuraaga anuraaga merged commit cde99eb into open-telemetry:main Oct 21, 2021
This was referenced Dec 19, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants