Wpis z mikrobloga

Mirasy, pytanie o tracing w appce spring bootowej. Jest to nowa apka, którą robimy od zera i potrzebuję dodać tracing do logów.

Mamy eventy w Azure Event Hubs, które na razie (bo nie mamy jeszcze proda - trochę taka zabaw póki co) - generujemy ręcznie poprzez feature Azurowy "Data Generator", gdzie możemy podać jsona i dodać do "kolejki", a w naszej appce spring bootowej consumer sobie je pobiera i wywołuje odpowiednią akcję. Gdzieś w środku logiki uderzamy webclientem do zewnętrznego serwisu.
Jeszcze nie wiadomo w jaki sposób docelowo będą generowane te eventy.

Co potrzebuję:
1. Dodać trace id (minimum z wymagań) i jakiś correlation id, te dane muszą być propagowane do każdego z serwisów.

I teraz pytanie, z czego najlepiej skorzystać?
Czytam o Spring Cloud Sleuth oraz Micrometer. Lead coś wspominał, żeby obczaić czy Micrometer nie wystarczy lub coś innego niż Sleuth, ale z tego co ja czytam to Micrometer chyba nie jest do tracingu.

2. Potrzebuję zastanowić się jak to zrobić w dwóch przypadkach:
- dla obecnej sytuacji, gdzie generujemy event ręcznie z jsonem - raczej w tym przypadku to nie my powinniśmy generować trace id itd, tylko już po stronie consumera w logice?
- Jeśli jest jakiś producent w appce, który generuje eventy - wtedy już producent będzie tworzył trace id/ correlation id itp.

Powiedzmy, że mam pomysł na otrzymywanie eventu w formie obiektu:

class Request<T> {
String traceId;
String correlationId;
T request; //tutaj json z danymi
}

czy odpalając całe flow w spring appce dla tego eventu, zaczynając od consumera, który wywołuje jakąś logikę (.handle(event)) powinienem do każdej metody przekazywać ten traceId, correlationId, żeby przy uderzeniu do zewnętrznego serwisu posłać te dane?
Nie za bardzo wiem jak to ma działać :)

#programista15k #programista25k #programowanie #java #spring #azure
  • 18
@mirek_dev zależy którą wersję springa masz i czy reaktywnie. Zakładam, że skoro nowa apka to 3.0. Używałem micrometer tracing bridge i zamiast przekazywać to z requestem to span/trsceid/correlation idzie przez MDC. Jjeśli stworzysz poprawnie resttemplate to będzie się automatycznie propagowało. Dla kolejki pewnie najlepiej dodać to do nagłówków zamiast w body. Implementacja tego dla listener/publisher pewnie będzie mocno zależeć od tego co używasz.
@mirek_dev:

dla obecnej sytuacji, gdzie generujemy event ręcznie z jsonem - raczej w tym przypadku to nie my powinniśmy generować trace id itd, tylko już po stronie consumera w logice?


To zaleźy.

Jeśli jest jakiś producent w appce, który generuje eventy - wtedy już producent będzie tworzył trace id/ correlation id itp.


To zależy. ( ͡° ͜ʖ ͡°)

A teraz od czego? Od tego co i jak
markaron - @mirek_dev: 
 dla obecnej sytuacji, gdzie generujemy event ręcznie z jsone...

źródło: spans-traces

Pobierz
@markaron: No i najważniejsze, instrumentacja aplikacji pozwala na bardziej dokładne i szczegółowe przysyłanie trace'ów. Z racji tego, że sidecar container nie ma dostępu do procesu aplikacji, ma mniejsze możliwości w zakresie zbierania i przesyłania informacji.
@exori_vis: Ale jak napisałem wyżej, taki sidecar container działa poza procesem aplikacji więc ilość informacji jakie może zgromadzić na jej temat jest mocno ograniczona. Do podstawowego observability i monitoringu wystarczy. Przy bardziej zaawansowanych use case'ach trzeba robić instrumentację.
  • 0
@Myrcin-: @markaron @exori_vis Problem w tym, że na przyklad ja dostaję event do azure event hub. Consumer pobierając ten event, pobiera tak naprawdę task, który ma się wykonać albo od razu czyli leci przez całe flow, serwisy, uderza do zewnętrznych api itd - albo za jakiś czas i wtedy odkładamy go do jakiegoś cache-kolejki i odpala się np. za godzinę, w zależności co mu tam podamy. I tutaj mam problem z
@mirek_dev: Dalej nie rozumiem w czym masz problem.

Serwis A dostaje request, generowany jest kontekst (trace id) który jest dołączany do eventu/wiadomości i ląduje sobie na kolejce (Kafka, Azure Event Hub, RabbitMQ, cokolwiek) i tam sobie czeka. Konsument wiadomości pobiera tą wiadomość razem z kontekstem (wygenerowanym trace id) i przekazuje dalej do serwisu B za pomocą requestu HTTP w którym kontekst dołączany jest do żądania. Po obsłudze serwis B wysyła request
  • 0
Konsument wiadomości pobiera tą wiadomość razem z kontekstem (wygenerowanym trace id) i przekazuje dalej do serwisu B za pomocą requestu HTTP w którym kontekst dołączany jest do żądania.


@markaron: W tym mam problem. Ale może tak ma być? Bo Przekazanie wiadomości z serwisu A do B to nie są jakieś chain http calle. Zanim ten serwis A przekaże do serwisu B to jest wywoływany łańcuch metod, gdzie przekazuje różne parametry. I
@mirek_dev:

Zanim ten serwis A przekaże do serwisu B to jest wywoływany łańcuch metod


Rozumiem, że chodzi ci o coś w rodzaju?

public class MyController
{
@jakastamjavovaadnotacja
public MyResponse Foo(...) {

var event = getEvent();

method1(event);
method2(event);
method3(event);

sendToNextService(event);
}
}
  • 0
@markaron Prawie.
Coś takiego, pisałem z palca i wymyślałem:

@PostMapping("run-task")
public ResponseEntity<TaskResult> runTask(@RequestBody RequestObj obj) {
taskService.handleTask(obj).isStatus ? ResponseEntity.ok(.)
}

inna klasa:
public TaskResult handleTask(RequestObj obj) {
WykopObjTask wykopobj = wykopService.findInRepoBasedOnFields(obj.getField1, obj.getField2);

RedditObjTask redditObj = Wykop.builder.fields(obj.getfield);

return anotherService.run(redditObj).isStatus ? TaskResult200 : TaskResult400;
}

innKlasa:
public TaskResult run(RedditObj redditObj) {
SubReddit subReddit = redditobj.getSth();

//another builder with result of a new object eg.
AllTasksFromSubreddit allTasksFromSubreddit = builder...

TaskResult taskResult = webclient.post().uri().header().bodValue....
}
Dobra, już kumam, mogę ci pokazać jak ja to robię w .NET przy pomocy biblioteki Jaeger, pewnie w Javovych bibliotekach będzie podobnie. Generalnie na początku obsługi requestu tworzę sobie tak jakby startową Activity (rozpoczyna Trace) do której w każdej metodzie którą wykonuję dodaję nową Activity tworzoną w ramach metody reprezentującą Span. Wygląda to mniej więcej tak:

class Program
{
static ActivitySource s_source = new ActivitySource("Sample.DistributedTracing");

static async Task Main(string[] args)
{
await