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

Implement logs retriever pagination using new Elasticsearch Java client #315

Conversation

cyrille-leclerc
Copy link
Contributor

  • Make sure you are opening from a topic/feature/bugfix branch (right side) and not your main branch!
  • Ensure that the pull request title represents the desired changelog entry
  • Please describe what you did
  • Link to relevant issues in GitHub or Jira
  • Link to relevant pull requests, esp. upstream and downstream changes
  • Ensure you have provided tests - that demonstrates feature works or fixes the issue

ScrollRequest scrollRequest = ScrollRequest.of(builder -> builder.scrollId(context.scrollId));
ScrollResponse<ObjectNode> scrollResponse = this.elasticsearchClient.scroll(scrollRequest, ObjectNode.class);
hits = scrollResponse.hits().hits();
newScrollId = context.scrollId; // TODO why doesn't the scroll response hold a new scrollId? scrollResponse.scrollId();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@swallez why is scrollResponse.scrollId null? I expected it to hold the scrollIdI should use next time so I don't have to understand if the backend has decided to reuse the sqame id or create a new one, similarly to the point in time API..

Copy link

Choose a reason for hiding this comment

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

Have a look at the documentation. The way scroll works is as follows:

  • send a normal search request with the scroll parameter set that indicates a TTL,
  • for the next "pages", call client.scroll() to fetch the next batch.

hits = scrollResponse.hits().hits();
newScrollId = context.scrollId; // TODO why doesn't the scroll response hold a new scrollId? scrollResponse.scrollId();
}
complete = hits.size() != PAGE_SIZE; // TODO is there smarter?
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@swallez what's the best way to understand if the search request returned all the entries or if I should scroll more? as scrollResponse.scrollId is always null, I end up doing one wasted query if the number of entries of the response is a multiple of PAGE_SIZE

@cyrille-leclerc
Copy link
Contributor Author

java.lang.ClassNotFoundException: org.glassfish.json.JsonProviderImpl
	at jenkins.util.AntClassLoader.findClassInComponents(AntClassLoader.java:1387)
	at jenkins.util.AntClassLoader.findClass(AntClassLoader.java:1342)
	at jenkins.util.AntClassLoader.loadClass(AntClassLoader.java:1089)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
	at java.base/java.lang.Class.forName0(Native Method)
	at java.base/java.lang.Class.forName(Class.java:315)
	at jakarta.json.spi.JsonProvider.provider(JsonProvider.java:72)
Caused: jakarta.json.JsonException: Provider org.glassfish.json.JsonProviderImpl not found
	at jakarta.json.spi.JsonProvider.provider(JsonProvider.java:75)
	at co.elastic.clients.json.jackson.JsonValueParser.<init>(JsonValueParser.java:39)
	at co.elastic.clients.json.jackson.JacksonJsonpParser.getValue(JacksonJsonpParser.java:233)
	at co.elastic.clients.json.JsonData.from(JsonData.java:96)
	at co.elastic.clients.json.JsonpDeserializer$2.deserialize(JsonpDeserializer.java:112)
	at co.elastic.clients.elasticsearch._types.ErrorCause.lambda$setupErrorCauseDeserializer$0(ErrorCause.java:391)
	at co.elastic.clients.json.ObjectDeserializer.parseUnknownField(ObjectDeserializer.java:205)
	at co.elastic.clients.json.ObjectDeserializer.deserialize(ObjectDeserializer.java:174)
	at co.elastic.clients.json.ObjectDeserializer.deserialize(ObjectDeserializer.java:137)
	at co.elastic.clients.json.ObjectBuilderDeserializer.deserialize(ObjectBuilderDeserializer.java:85)
	at co.elastic.clients.json.DelegatingDeserializer$SameType.deserialize(DelegatingDeserializer.java:48)
	at co.elastic.clients.json.JsonpDeserializerBase$ArrayDeserializer.deserialize(JsonpDeserializerBase.java:320)
	at co.elastic.clients.json.JsonpDeserializerBase$ArrayDeserializer.deserialize(JsonpDeserializerBase.java:285)
	at co.elastic.clients.json.JsonpDeserializer.deserialize(JsonpDeserializer.java:75)
	at co.elastic.clients.json.ObjectDeserializer$FieldObjectDeserializer.deserialize(ObjectDeserializer.java:72)
	at co.elastic.clients.json.ObjectDeserializer.deserialize(ObjectDeserializer.java:176)
	at co.elastic.clients.json.ObjectDeserializer.deserialize(ObjectDeserializer.java:137)
	at co.elastic.clients.json.JsonpDeserializer.deserialize(JsonpDeserializer.java:75)
	at co.elastic.clients.json.ObjectBuilderDeserializer.deserialize(ObjectBuilderDeserializer.java:79)
	at co.elastic.clients.json.DelegatingDeserializer$SameType.deserialize(DelegatingDeserializer.java:43)
	at co.elastic.clients.json.ObjectDeserializer$FieldObjectDeserializer.deserialize(ObjectDeserializer.java:72)
	at co.elastic.clients.json.ObjectDeserializer.deserialize(ObjectDeserializer.java:176)
	at co.elastic.clients.json.ObjectDeserializer.deserialize(ObjectDeserializer.java:137)
	at co.elastic.clients.json.JsonpDeserializer.deserialize(JsonpDeserializer.java:75)
	at co.elastic.clients.json.ObjectBuilderDeserializer.deserialize(ObjectBuilderDeserializer.java:79)
	at co.elastic.clients.json.DelegatingDeserializer$SameType.deserialize(DelegatingDeserializer.java:43)
	at co.elastic.clients.transport.rest_client.RestClientTransport.getHighLevelResponse(RestClientTransport.java:276)
	at co.elastic.clients.transport.rest_client.RestClientTransport.performRequest(RestClientTransport.java:144)
	at co.elastic.clients.elasticsearch.ElasticsearchClient.search(ElasticsearchClient.java:1487)
	at io.jenkins.plugins.opentelemetry.backend.elastic.ElasticsearchLogStorageScrollingRetriever.overallLog(ElasticsearchLogStorageScrollingRetriever.java:113)
	at io.jenkins.plugins.opentelemetry.job.log.OtelLogStorage.overallLog(OtelLogStorage.java:98)
	at org.jenkinsci.plugins.workflow.job.WorkflowRun.getLogText(WorkflowRun.java:1057)
	at hudson.model.Run.writeLogTo(Run.java:1554)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)

@swallez
Copy link

swallez commented Feb 11, 2022

I implemented log retrieval using point in time in swallez@e454add (test works AFAICT)

Feel free to pull these changes here.

@cyrille-leclerc
Copy link
Contributor Author

thanks, can you by any chance do a PR?

@swallez
Copy link

swallez commented Feb 11, 2022

@cyrille-leclerc I'll let you take it from there, I'm happy I could help but don't have much time to dedicate to this.

"logPosition=(end:now,start:now-1d,streamLive:!f)&" +
"logFilter=(language:kuery,query:%27trace.id:" + traceId + "%27)&";
if (pageNo == 0) {
w.write("View logs in Kibana: " + logsVisualizationUrl + "\n\n");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@kuisathaverat do you know how to produce an HTML link? I guess it's related to the code of io.jenkins.plugins.opentelemetry.job.log.ConsoleNotes

Copy link
Contributor

Choose a reason for hiding this comment

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

it depends on the render you have configured for Jenkins, my experience on showing an URL in the console log is that will work if the URL is simple, but in the case of the Kibana URL is not simple, it has many parameters with delimiters and so on, in my test I could make the link work in all the cases (probably URL encoded works better, but I did not test it), and the link is ugly because is too long. Because of that, I changed to use an Action in the previous plugin.

@cyrille-leclerc
Copy link
Contributor Author

@kuisathaverat I don't understand how the API works, I don't succeed in having any kind of pagination. We have 3 invocations OtelLogStorage.overallLog() then OverallLog.writeHtmlTo()invoked on the last AnnotatedLargeText so I restricted the retrieval of logs from Elasticsearch to the first 100 lines and then I add a link to Kibana.

image

@cyrille-leclerc cyrille-leclerc marked this pull request as ready for review February 15, 2022 12:41
@kuisathaverat
Copy link
Contributor

kuisathaverat commented Feb 15, 2022

@kuisathaverat I don't understand how the API works, I don't succeed in having any kind of pagination. We have 3 invocations OtelLogStorage.overallLog() then OverallLog.writeHtmlTo()invoked on the last AnnotatedLargeText so I restricted the retrieval of logs from Elasticsearch to the first 100 lines and then I add a link to Kibana.

I think is the best approach, it limits the possibility of issues in Jenkins when you have huge logs, you always can go to Elasticsearch to check the whole log.

import java.util.HashMap;
import java.util.Map;

public class CustomLogStorageRetriever implements LogStorageRetriever<CustomLogStorageRetriever.CustomLogsQueryContext> {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@kuisathaverat I want to introduce a generic LogStorageRetriever that just print a hyperlink to the visualization backend

@cyrille-leclerc cyrille-leclerc merged commit 6af398d into send-build-logs-to-otel Feb 15, 2022
@cyrille-leclerc cyrille-leclerc deleted the implement-pagination-using-new-elasticsearch-client branch February 15, 2022 15:01
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.

3 participants