niedziela, 8 grudnia 2024

CloudWatch Insights - monitoring async workers traffic distribution

Symfony Framework Messenger allows to run parts of your system's processes asynchronously. To gain a better understanding of how your application really works, you can use the following queries. 

These queries filter all logs that comes from Symfony Messenger Workers and aggregate them to provide broader perspective. These also prepare a summary of all asynchronous execution units in your system, giving you greater awareness of the overall complexity and the building blocks that make up the whole.

So, at the very beginning let me explain the data from the logs in my application. This way you can apply these queries to your system, keeping in mind that my log structures might differ partially or entirely.

  • message - with value MESSENGER_WORKER_MESSAGE_FINISH indicates Symfony Messenger Message that was successfully handled, 
  • extra.messengerWorker.class - event/command class that was handled by Worker
  • extra.messengerWorker.executionId - represents each message that was handled by Messenger Worker 
  • extra.requestDuration - whole Worker duration, 
  • context.messengerWorker.processingDuration - execution of a single Message duration
  • context.messengerWorker.memoryUsageMb - whole Worker memory usage

馃搳 List all unique Workers 

Thanks to that query you can easily check how many different async-workers do you have in your system. dedup function helps a lot with removing duplicated message class names.    

fields @timestamp, @message, @logStream, @log
| filter message = "MESSENGER_WORKER_MESSAGE_FINISH"
| display extra.messengerWorker.class 
| dedup extra.messengerWorker.class 

All unique Messenger events/commands that are handel asynchronously.
This summary gathers all async execution units (that have worked in a given period of time)
of you application in one place. 

馃搳 Reveal traffic distribution per Worker 

That query shows the busiest workers in your system.     

fields @timestamp, @message, @logStream, @log
| filter message = "MESSENGER_WORKER_MESSAGE_FINISH"
| stats count(*) as executionCount by extra.messengerWorker.class  
| sort by executionCount desc

It's a simple list which execution count for each individual worker that was handled in a given period of time.

The wider time period you'll set for the query the more (probably) unique worker type you might get as a results. 

Changing the time period of the query to only one day would reveal less worker types
but returned metrics might be more useful. 

馃搳 More detailed Workers data 

Each row symbolizes a single worker per instances, how many messages they handled, when the first and last messages were handled, and, of course, the message type they handled. Take look at interesting usage of  latest(fieldName) function which allows to present not aggregated data in this summary.      

fields @timestamp, @message, @logStream, @log
| parse @logStream "/php/*" as log_stream_id
| filter message = "MESSENGER_WORKER_MESSAGE_FINISH"
| stats latest(substr(log_stream_id, 0, 6)) as instance,
        count(extra.messengerWorker.executionId) as executions, 
        min(@timestamp) as start,
        max(@timestamp) as end,
        max(extra.requestDuration / 60) as duration_in_minutes,
        latest(extra.messengerWorker.class) as message_class
  by extra.executionId as worker_exectuion_id

You can use that query when you need to check a specific worker performance.

馃搳 The longest Workers executions

To identify the longest workers execution in you application just use the query bellow. These spots could be the first candidates for optimization or potential bottlenecks that slow down your application.       

fields @timestamp, @message, @logStream, @log
| filter message like /(?i)(MESSENGER_WORKER_MESSAGE_FINISH)/
| display datetime, 
          context.messengerWorker.processingDuration / 60,
          context.messengerWorker.memoryUsageMb,
          extra.messengerWorker.class
| sort context.messengerWorker.processingDuration desc

niedziela, 1 grudnia 2024

CloudWatch Insights - revealing endpoint's clients

I'll show you how to list all the clients of your's application HTTP endpoints, how large is the traffic they generate and how it's exactly distributed in a given period of time. That few queries below could easily reveal the knowledge about application dependencies (with a minimal effort on your end) that you might be not aware of.

At the very beginning I'm gonna show you how the log structure looks like in my case. It may differ how it would look like in your application but what's matter is to highlighting a values that you need to achieve our goal to find out who is calling your application endpoints. 


JSON structure

  • User Agent of the client - in my case it's passed in request headers as key user-agent which needs to be parsed first because even that key is auto-discovered by CloudWatch it contains a dash (-) in a key name so cannot be used in a query,
  • value that distinguish Http Request Logmessage,
  • Http Request Method - context.method,
  • Http Request Namecontext.route - we cannot use exact URI that was called by client because the same URIs may differ when endpoint has param inside endpoint path e.g. {{id}} of the resource it refers to - which makes logs aggregation not possible,  
  • Caller IPcontext.clientIp,  

馃搳 List all the Clients per endpoint

fields @timestamp, @message, @logStream, @log
| parse @message '"user-agent":["*"]' as userAgent
| filter message = "HTTP_REQUEST" 
| display context.method, 
          context.route, 
          userAgent, 
          context.clientIp
| sort context.method, 
       context.route, 
       userAgent asc
| dedup context.route, 
        context.clientIp


馃搳 Count all requests done by each Client

fields @timestamp, @message, @logStream, @log
| parse @message '"user-agent":["*"]' as userAgent
| filter message = "HTTP_REQUEST" and
         ispresent(userAgent)
| stats count(userAgent) as endpointCallByExternalService by userAgent
| sort endpointCallByExternalService desc


馃搳 Traffic distribution in a time per demand Client

Based on results above you need to manually parse each Client that you want to see in this chart and add it in the stats section to finally display it. 
fields @timestamp, @message, @logStream, @log
| parse @message '"user-agent":["*"]' as userAgent
| parse userAgent /(?<isGo>Go-http-client?)/
| parse userAgent /(?<isSymfony>Symfony?)/
| parse userAgent /(?<isPostman>PostmanRuntime?)/
| parse userAgent /(?<isJava>Java?)/
| parse userAgent /(?<isPython>python-requests?)/
| parse userAgent /(?<isRuby>Ruby?)/
| filter message = "HTTP_REQUEST" and
         ispresent(userAgent)
| stats count(isGo),
        count(isSymfony),
        count(isPostman),
        count(isJava),
        count(isPython),
        count(isRuby)
  by bin(1h)
        


Conclusion


Since you got the new perspective and knowledge of the system that you're maintain, you can start asking questions about the things that are still unrevealed:

  • does one User Agent is represented by two or more systems (e.g. two application implemented in Java)?
  • which of the User Agents are internal system and which one are external? 
  • why these are needing date from your application?
  • why traffic distribution looks exactly like this?    

sobota, 30 listopada 2024

CloudWatch Insights - most used HTTP endpoints


Having a standard PHP Symfony Framework log structure like one below you can measure whole application HTTP traffic using CloudWatch Insights.  

{
   "message":"Matched route \"some_endpoint_name\",
   "context":{
      "route":"some_endpoint_name",
      "request_uri":"http://some.domain.com/some/endpoint/91b5602d-f098-471a-aa05-92937fea3636/name",
      "method":"POST"
   },
   "level":200,
   "level_name":"INFO",
   "channel":"request",
   "datetime":"2024-11-30T10:26:08.594527+00:00",
}

As you can see context.request_uri  key it's hard to aggregate since each request for the same endpoint will differ due to passed in URI params like UUID of the resource. To avoid mentioned problem we should use rather a context.route value which is declared in Symfony Controller class as one of the value Action Attribute.  

Here is example of the CloudWatch Insights query which show in descending order all of the most used POST/PATCH/PUT/DELETE (these are changing state of the system) endpoints in your application: 

fields @timestamp, @message, @logStream, @log
| filter channel = "request" and context.method != "GET" 
| stats count(*) as endpointsCallsCount by context.route, context.method
| sort endpointsCallsCount desc


The standard output of the query, it's descending order easily shows the most used endpoints in your application

You can also see results as chart so you can compare all endpoints share in total HTTP traffic volume  

For monitor all GET endpoints worth to filter non functional endpoints call that only checks an availability of the system:

fields @timestamp, @message, @logStream, @log
| filter channel = "request" and context.method = "GET" and context.route not in ["health_check","ping"]
| stats count(*) as endpointsCallsCount by context.route, context.method
| sort endpointsCallsCount desc

pi膮tek, 8 listopada 2024

Observability

Tradycyjne metody monitoringu przesta艂y by膰 wystarczaj膮ce w dobie z艂o偶onych system贸w - wymagane jest bardziej wnikliwe zrozumienie system贸w oraz przy艣pieszanie rozwi膮zywania incydent贸w.

Sama implementacja system贸w obserwowalnych i ich utrzymanie rodzi nowe problemy. Zewn臋trzne systemy s艂u偶膮 do obserwowania aplikacji; kwestionowanie w celu poznania wewn臋trznej pracy systemu i stanu systemu.

Je偶eli mamy mo偶liwo艣膰 pozyskania informacji na temat stanu aplikacji w ka偶dym jej aspekcie nawet z kt贸rymi byli艣my nie zaznajomieni jeszcze jaki艣 czas temu, kt贸rych nie przewidzieli艣my - a przysz艂y nam one dopiero teraz i mamy mo偶liwo艣膰 zweryfikowania tych偶e danych. Oznacza to, 偶e wska藕nik Observability systemu jest wysoki.

Stale musimy usprawnia膰 proces zwi臋kszania wska藕nika Observability; dzi臋ki wysokiemu Observability mo偶emy wypatrywa膰 niecodziennych/podejrzanych wzorc贸w i zachowa艅; pozwala na analiz臋 interakcji u偶ytkownika z systemem; dzi臋ki Observability mamy wgl膮d w dynamik臋 komunikacji mi臋dzy mikro serwisami/kontenerami; taka analiza powinna by膰 standardowym elementem pracy programist贸w (development life-cycle). Observability daje mo偶liwo艣膰 wgl膮du w zachowanie systemu, dzi臋ki tym informacj膮 deweloperzy mog膮 poprawia膰 niezawodno艣膰/wydajno艣膰 systemu; analiza log贸w, metryk, trace’贸w pozwala na zidentyfikowanie bottleneck’贸w wydajno艣ciowych;

Kluczowe koncepty:

  • Root cause analysis
  • Highly observable system (has intricate details/critical insights)
  • Realtime monitoring/alerting
  • resource utilization
  • error rates
  • Synthetic Journeys
  • performance metrics
  • deviations from normal patterns
  • APM (Application Performance Monitoring)
  • application dependencies,
  • Distributed tracing - z艂o偶one systemy gdzie pojedynczy request oddzia艂uje na wiele mikro serwis贸w w r贸偶nych data centers - taki trace ma sw贸j ID
  • Telemetry Instrumentations (Open Telemetry Standard) event wysy艂any do central location (tracking user journey; troubleshooting errors)
  • Site Reliability Engineering (SRE)
  • feature flagging
  • incident analysis
  • blue-green deployment
  • chaos engineering; “pytania kt贸re zostan膮 postawione bez wcze艣niejszej wiedzy”
  • alert ➡️ warto艣膰 progowa predefiniowanej metryki zosta艂a przekroczona; remediations (艣rodki zaradcze)
  • podej艣cie reaktywne: zidentyfikowanie i rozwi膮zanie problemu po tym jak wyst膮pi
  • podej艣cie proaktywne
Observability pomaga zrozumie膰 wewn臋trzne zachowanie systemu co mo偶e wy艂oni膰 potencjalne problemy kt贸re b臋d膮 mie膰 miejsce w przysz艂o艣ci.

Alert musi mie膰 dane dot. powodu jego wyst膮pienia (moje do艣wiadczenie: miejsce wyst膮pienia, dane kontekstowe - zasobu kt贸rego dotyczy).

Tradycyjny monitoring i dashboardy polegaj膮 na wiedzy Seniora (dependency on human expertise) - czyli metodologia (tradycyjny monitoring) polegaj膮ca na objawach ani偶eli na actual Root Cause - to nie mo偶e by膰 d艂u偶ej stosowane gdy z艂o偶ono艣膰 i skala jest du偶a; “Information to debug issues in details”, “ask open questions”, “trace the system to find real cause of problems (deeply hidden)”; organizacja nie polega na wiedzy eksperta i na subiektywnym zgadywaniu, a prowadzi do bardziej obiektywnej analizy; Z艂o偶one interakcje mi臋dzy systemami rozproszonymi; metrics, events, logs, traces, telemetry data - unforeseen issues. Szybkie rozwi膮zanie problemu to ograniczenie down-time; identify & solve potential issue before they affect users - w przeciwie艅stwie do reagowania na problem; Problemy z Observability: przechowywanie tych danych, przesy艂anie tych danych po sieci; zmiana sposobu my艣lenia z re na pro; observability kosztuje; security & privacy; 



殴r贸d艂o