Raccolta di logs da Loki

Raccolta di logs da Loki

Noi di Badoo monitoremu in permanenza e tecnulugia novi è evaluemu s'ellu l'utilizanu o micca in u nostru sistema. Vulemu sparte unu di sti studii cù a cumunità. Hè dedicatu à Loki, un sistema di aggregazione di log.

Loki hè una suluzione per almacenà è vede i logs, è sta pila furnisce ancu un sistema flexible per analizà è mandà dati à Prometheus. In maghju, hè stata liberata una altra aghjurnazione, chì hè attivamente promossa da i creatori. Eramu interessati à ciò chì Loki pò fà, quali caratteristiche furnisce, è in quantu pò agisce cum'è una alternativa à ELK, a pila chì usemu avà.

Cosa hè Loki

Grafana Loki hè un inseme di cumpunenti per un sistema di logu cumpletu. A cuntrariu di l'altri sistemi simili, Loki hè basatu annantu à l'idea di indexà solu metadati di log - etichette (cum'è in Prometheus), è cumpressione i logs stessi fiancu à fiancu in pezzi separati.

Home page, GitHub

Prima di andà in una descrizzione di ciò chì pudete fà cù Loki, vogliu chjarificà ciò chì significa "l'idea di indexà solu metadata". Comparamu l'approcciu di Loki è l'approcciu d'indexazione in soluzioni tradiziunali, cum'è Elasticsearch, usendu l'esempiu di una linea da u log nginx:

172.19.0.4 - - [01/Jun/2020:12:05:03 +0000] "GET /purchase?user_id=75146478&item_id=34234 HTTP/1.1" 500 8102 "-" "Stub_Bot/3.0" "0.001"

I sistemi tradiziunali analizanu tutta a fila, cumprese i campi cù assai valori unichi di user_id è item_id, è almacenanu tuttu in indici grandi. U vantaghju di questu approcciu hè chì pudete eseguisce dumande cumplessu rapidamente, postu chì quasi tutte e dati sò in l'indici. Ma avete da pagà per questu in chì l'indici diventa grande, chì si traduce in esigenze di memoria. In u risultatu, l'indici di testu sanu di logs hè paragunabile in grandezza à i logs stessi. Per fà una ricerca rapida, l'indexu deve esse caricatu in memoria. E più logs, più veloce l'indice aumenta è più memoria consuma.

L'approcciu di Loki esige chì solu i dati necessarii sò estratti da a stringa, u numeru di valori di quale hè chjucu. Questu modu avemu un picculu indice è pudemu cercà i dati filtrà per u tempu è i campi indexati, è poi scansendu u restu cù espressioni regulari o ricerca di substring. U prucessu ùn pare micca u più veloce, ma Loki divide a dumanda in parechje parte è eseguisce in parallelu, processendu una grande quantità di dati in pocu tempu. U numaru di shards è dumande parallele in elli hè configurabile; cusì, a quantità di dati chì ponu esse trattati per unità di tempu dipende linearmente da a quantità di risorse furnite.

Stu scambiu trà un grande indice veloce è un picculu indice di forza bruta parallela permette à Loki di cuntrullà u costu di u sistema. Pò esse cunfiguratu flexiblemente è espansu secondu i vostri bisogni.

A pila Loki hè custituita da trè cumpunenti: Promtail, Loki, Grafana. Promtail raccoglie logs, li processa è li manda à Loki. Loki li tene. È Grafana pò dumandà dati da Loki è mostrà. In generale, Loki pò esse usatu micca solu per almacenà logs è cercà per elli. L'intera pila furnisce grandi opportunità per processà è analizà e dati entranti cù u modu Prometheus.
Una descrizzione di u prucessu di stallazione pò esse truvata ccà.

Ricerca di log

Pudete cercà i logs in una interfaccia speciale Grafana - Explorer. E dumande utilizanu a lingua LogQL, chì hè assai simili à u PromQL utilizatu da Prometheus. In principiu, pò esse pensatu cum'è un grep distribuitu.

L'interfaccia di ricerca hè cusì:

Raccolta di logs da Loki

A dumanda stessu hè custituita da duie parti: selettore è filtru. Selector hè una ricerca per metadati indexati (etichette) chì sò assignati à i logs, è u filtru hè una stringa di ricerca o regexp chì filtra i registri definiti da u selettore. In l'esempiu datu: In parentesi curly - u selettore, tuttu dopu - u filtru.

{image_name="nginx.promtail.test"} |= "index"

A causa di a manera chì Loki funziona, ùn pudete micca fà richieste senza un selettore, ma l'etichette ponu esse fatte arbitrariamente generiche.

U selettore hè a chjave-valore di u valore in parentesi curly. Pudete combine selettori è specificà diverse cundizioni di ricerca cù l'operatori =, != o espressioni regulari:

{instance=~"kafka-[23]",name!="kafka-dev"} 
// Найдёт логи с лейблом instance, имеющие значение kafka-2, kafka-3, и исключит dev 

Un filtru hè un testu o regexp chì filtrarà tutte e dati ricevuti da u selettore.

Hè pussibule ottene gràfiche ad-hoc basatu nantu à e dati ricevuti in u modu metrica. Per esempiu, pudete truvà a frequenza di l'occurrence in i logs nginx di una entrata chì cuntene a stringa d'indici:

Raccolta di logs da Loki

Una descrizzione completa di e funziunalità pò esse truvata in a documentazione LogQL.

Analisi di log

Ci hè parechje manere di cullà i logs:

  • Cù l'aiutu di Promtail, un cumpunente standard di a pila per a cullizzioni di logs.
  • Direttamente da u cuntainer docker usendu Loki Docker Logging Driver.
  • Aduprate Fluentd o Fluent Bit chì ponu mandà dati à Loki. A cuntrariu di Promtail, anu parsers pronti per quasi ogni tipu di log è ponu ancu gestisce logs multiline.

Di solitu Promtail hè utilizatu per l'analisi. Face trè cose:

  • Trova fonti di dati.
  • Attaccà etichette à elli.
  • Mandate dati à Loki.

Attualmente Promtail pò leghje logs da i fugliali lucali è da u journal systemd. Deve esse stallatu nantu à ogni macchina da quale i logs sò cullati.

Ci hè integrazione cù Kubernetes: Promtail scopre automaticamente u statu di u cluster per mezu di l'API REST di Kubernetes è raccoglie i logs da un node, serviziu o pod, immediatamente postendu etichette basate nantu à metadata da Kubernetes (nome di pod, nome di file, etc.).

Pudete ancu appiccà etichette basate nantu à e dati da u log usendu Pipeline. Pipeline Promtail pò esse cumpostu di quattru tippi di tappe. Più dettagli - in documentazione ufficiale, Vogliu subitu nutà alcune di e sfumature.

  1. Analisi di fasi. Questu hè u stadiu di RegEx è JSON. À questu stadiu, avemu estratti dati da i logs in a chjamata mappa estratta. Pudete estrattà da JSON per simplificà i campi chì avemu bisognu in a mappa estratta, o attraversu espressioni regulari (RegEx), induve i gruppi chjamati sò "mappati" in a mappa estratta. A mappa estratta hè un almacenamentu chjave-valore, induve a chjave hè u nome di u campu, è u valore hè u so valore da i logs.
  2. Trasforma i tappe. Questa tappa hà duie opzioni: trasfurmà, induve avemu stabilitu e regule di trasfurmazioni, è fonte - a fonte di dati per a trasfurmazioni da a mappa estratta. Se ùn ci hè micca un tali campu in a mappa estratta, allora serà creatu. Cusì, hè pussibule di creà etichette chì ùn sò micca basate nantu à a mappa estratta. À questu stadiu, pudemu manipulà e dati in a mappa estratta cù un abbastanza putente mudellu di golang. Inoltre, avemu da ricurdà chì a mappa estratta hè cumplettamente caricata durante l'analisi, chì permette, per esempiu, di verificà u valore in questu: "{{if .tag}tag value exists{end}}". U Template supporta e cundizioni, i loops, è alcune funzioni di stringa cum'è Replace and Trim.
  3. Fasi d'azzione. In questu stadiu, pudete fà qualcosa cù l'estratti:
    • Crea una etichetta da i dati estratti, chì serà indiziatu da Loki.
    • Cambia o stabilisce l'ora di l'avvenimentu da u logu.
    • Cambia i dati (testu di log) chì andarà à Loki.
    • Crea metrica.
  4. Tappe di filtrazione. A tappa di partita, induve pudemu sia mandà registri chì ùn avemu micca bisognu à /dev/null, o mandalli per un ulteriore prucessu.

Utilizendu l'esempiu di processà i logs nginx ordinali, vi mustrarà cumu pudete analizà i logs cù Promtail.

Per a prova, pigliemu una immagine nginx jwilder/nginx-proxy:alpine mudificata è un picculu demoniu chì pò dumandà sè stessu via HTTP cum'è nginx-proxy. U daemon hà parechje endpoints, à quale pò dà risposti di diverse dimensioni, cù diversi stati HTTP è cù diversi ritardi.

Raccoglieremu i logs da i cuntenituri docker, chì ponu esse truvati longu u percorsu /var/lib/docker/containers/ / -json.log

In docker-compose.yml avemu stallatu Promtail è specifichi u percorsu à a cunfigurazione:

promtail:
  image: grafana/promtail:1.4.1
 // ...
 volumes:
   - /var/lib/docker/containers:/var/lib/docker/containers:ro
   - promtail-data:/var/lib/promtail/positions
   - ${PWD}/promtail/docker.yml:/etc/promtail/promtail.yml
 command:
   - '-config.file=/etc/promtail/promtail.yml'
 // ...

Aghjunghjite u percorsu à i logs à promtail.yml (ci hè una opzione "docker" in a cunfigurazione chì face u listessu in una linea, ma ùn saria micca cusì evidente):

scrape_configs:
 - job_name: containers

   static_configs:
       labels:
         job: containerlogs
         __path__: /var/lib/docker/containers/*/*log  # for linux only

Quandu sta cunfigurazione hè attivata, Loki riceverà logs da tutti i cuntenituri. Per evitari, cambiemu i paràmetri di a prova nginx in docker-compose.yml - aghjunghje logging à u campu di tag:

proxy:
 image: nginx.test.v3
//…
 logging:
   driver: "json-file"
   options:
     tag: "{{.ImageName}}|{{.Name}}"

Edite promtail.yml è cunfigurà Pipeline. I logs sò i seguenti:

{"log":"u001b[0;33;1mnginx.1    | u001b[0mnginx.test 172.28.0.3 - - [13/Jun/2020:23:25:50 +0000] "GET /api/index HTTP/1.1" 200 0 "-" "Stub_Bot/0.1" "0.096"n","stream":"stdout","attrs":{"tag":"nginx.promtail.test|proxy.prober"},"time":"2020-06-13T23:25:50.66740443Z"}
{"log":"u001b[0;33;1mnginx.1    | u001b[0mnginx.test 172.28.0.3 - - [13/Jun/2020:23:25:50 +0000] "GET /200 HTTP/1.1" 200 0 "-" "Stub_Bot/0.1" "0.000"n","stream":"stdout","attrs":{"tag":"nginx.promtail.test|proxy.prober"},"time":"2020-06-13T23:25:50.702925272Z"}

fasi di pipeline:

 - json:
     expressions:
       stream: stream
       attrs: attrs
       tag: attrs.tag

Estrattemu i campi di flussu, attrs, attrs.tag (se ci hè) da u JSON entrante è i mette in a mappa estratta.

 - regex:
     expression: ^(?P<image_name>([^|]+))|(?P<container_name>([^|]+))$
     source: "tag"

S'ellu era pussibile mette u campu di tag in a mappa estratta, allora usendu a regexp extraemu i nomi di l'imaghjini è u containeru.

 - labels:
     image_name:
     container_name:

Assignemu etichette. Se i chjavi image_name è container_name si trovanu in i dati estratti, i so valori saranu attribuiti à l'etichette appropritate.

 - match:
     selector: '{job="docker",container_name="",image_name=""}'
     action: drop

Scartemu tutti i logs chì ùn anu micca l'etichette image_name è container_name set.

  - match:
     selector: '{image_name="nginx.promtail.test"}'
     stages:
       - json:
           expressions:
             row: log

Per tutti i logs chì l'image_name hè uguali à nginx.promtail.test, avemu estrattu u campu di log da u logu di l'origine è u mette in a mappa estratta cù a chjave di fila.

  - regex:
         # suppress forego colors
         expression: .+nginx.+|.+[0m(?P<virtual_host>[a-z_.-]+) +(?P<nginxlog>.+)
         source: logrow

Emu sguassate a stringa di input cù espressioni regulari è tirate fora l'ospite virtuale nginx è a linea di log nginx.

     - regex:
         source: nginxlog
         expression: ^(?P<ip>[w.]+) - (?P<user>[^ ]*) [(?P<timestamp>[^ ]+).*] "(?P<method>[^ ]*) (?P<request_url>[^ ]*) (?P<request_http_protocol>[^ ]*)" (?P<status>[d]+) (?P<bytes_out>[d]+) "(?P<http_referer>[^"]*)" "(?P<user_agent>[^"]*)"( "(?P<response_time>[d.]+)")?

Analizà u log nginx cù espressioni regulari.

    - regex:
           source: request_url
           expression: ^.+.(?P<static_type>jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|js|flv|swf|html|htm)$
     - regex:
           source: request_url
           expression: ^/photo/(?P<photo>[^/?.]+).*$
       - regex:
           source: request_url
           expression: ^/api/(?P<api_request>[^/?.]+).*$

Parse request_url. Cù l'aiutu di regexp, determinemu u scopu di a dumanda: à statics, to photos, to API è stabilisce a chjave currispundente in a mappa estratta.

       - template:
           source: request_type
           template: "{{if .photo}}photo{{else if .static_type}}static{{else if .api_request}}api{{else}}other{{end}}"

Utilizendu operatori cundiziunali in Template, cuntrollemu i campi installati in a mappa estratta è stabilisce i valori richiesti per u campu request_type: photo, static, API. Assignà un altru se falliu. Avà request_type cuntene u tipu di dumanda.

       - labels:
           api_request:
           virtual_host:
           request_type:
           status:

Avemu stabilitu l'etichette api_request, virtual_host, request_type è status (statu HTTP) basatu annantu à ciò chì avemu sappiutu mette in a mappa estratta.

       - output:
           source: nginx_log_row

Cambia l'output. Avà u log nginx pulito da a mappa estratta va à Loki.

Raccolta di logs da Loki

Dopu avè eseguitu a cunfigurazione sopra, pudete vede chì ogni entrata hè tichjata in basa di dati da u log.

Tenite in mente chì l'estrazione di etichette cù un gran numaru di valori (cardinalità) pò rallentà significativamente Loki. Questu hè, ùn deve micca mette in l'indici, per esempiu, user_id. Leghjite più nantu à questu in l'articuluCumu l'etichette in Loki ponu fà e dumande di log più veloce è faciule". Ma questu ùn significa micca chì ùn pudete micca cercà per user_id senza indici. Hè necessariu d'utilizà filtri durante a ricerca ("grab" secondu a dati), è l'indici quì agisce cum'è un identificatore di flussu.

Visualizazione di log

Raccolta di logs da Loki

Loki pò agisce cum'è una fonte di dati per i charts Grafana cù LogQL. E seguenti caratteristiche sò supportate:

  • rate - numeru di records per seconda;
  • cuntà cù u tempu - u numeru di records in u intervallu datu.

Ci sò ancu funzioni aggregating Sum, Avg è altri. Pudete custruisce grafici abbastanza cumplessi, per esempiu, un graficu di u numeru di errori HTTP:

Raccolta di logs da Loki

A fonte di dati predeterminata di Loki hè un pocu menu funziunale cà a fonte di dati Prometheus (per esempiu, ùn pudete micca cambià a legenda), ma Loki pò esse cunnessu cum'è fonte di tipu Prometheus. Ùn sò micca sicuru se questu hè un cumpurtamentu documentatu, ma à ghjudicà da a risposta di i sviluppatori "Cumu cunfigurà Loki cum'è fonte di dati Prometheus? · Issue #1222 · grafana/loki", per esempiu, hè perfettamente legale è Loki hè cumplettamente cumpatibile cù PromQL.

Aghjunghjite Loki cum'è fonte di dati cù u tipu Prometheus è aghjunghje l'URL /loki:

Raccolta di logs da Loki

È pudete fà grafici, cum'è s'ellu travagliassi cù metriche di Prometheus:

Raccolta di logs da Loki

Pensu chì a discrepanza in a funziunalità hè tempuranee è i sviluppatori l'arraggianu in u futuru.

Raccolta di logs da Loki

Metriche

Loki furnisce l'abilità di estrae metriche numeriche da i logs è di mandà à Prometheus. Per esempiu, u logu nginx cuntene u numeru di bytes per risposta, è ancu, cù una certa mudificazione di u formatu di log standard, u tempu in sicondi chì hà pigliatu per risponde. Queste dati ponu esse estratti è mandati à Prometheus.

Aghjunghjite una altra sezione à promtail.yml:

- match:
   selector: '{request_type="api"}'
   stages:
     - metrics:
         http_nginx_response_time:
           type: Histogram
           description: "response time ms"
           source: response_time
           config:
             buckets: [0.010,0.050,0.100,0.200,0.500,1.0]
- match:
   selector: '{request_type=~"static|photo"}'
   stages:
     - metrics:
         http_nginx_response_bytes_sum:
           type: Counter
           description: "response bytes sum"
           source: bytes_out
           config:
             action: add
         http_nginx_response_bytes_count:
           type: Counter
           description: "response bytes count"
           source: bytes_out
           config:
             action: inc

L'opzione permette di definisce è aghjurnà e metriche basate nantu à e dati da a mappa estratta. Queste metriche ùn sò micca mandate à Loki - appariscenu in l'endpoint Promtail /metrics. Prometheus deve esse cunfiguratu per riceve dati da questa tappa. In l'esempiu di sopra, per request_type = "api" cullemu una metrica d'istogramma. Cù stu tipu di metrica hè cunvenutu per ottene percentili. Per statics and photos, cullighjemu a summa di bytes è u numeru di fila in quale avemu ricevutu bytes per calculà a media.

Leghjite più nantu à e metriche ccà.

Apertura un portu nantu à Promtail:

promtail:
     image: grafana/promtail:1.4.1
     container_name: monitoring.promtail
     expose:
       - 9080
     ports:
       - "9080:9080"

Avemu assicuratu chì e metriche cù u prefissu promtail_custom sò apparsu:

Raccolta di logs da Loki

A creazione di Prometheus. Aggiungi promtail di travagliu:

- job_name: 'promtail'
 scrape_interval: 10s
 static_configs:
   - targets: ['promtail:9080']

È fate un graficu:

Raccolta di logs da Loki

Questu modu pudete scopre, per esempiu, e quattru richieste più lente. Pudete ancu cunfigurà u monitoraghju per queste metriche.

Scaling

Loki pò esse in modu binariu unicu è sharded (modu scalabile horizontalmente). In u sicondu casu, pò salvà dati à u nuvulu, è i pezzi è l'indici sò guardati separatamente. In a versione 1.5, a capacità di almacenà in un locu hè implementata, ma ùn hè ancu cunsigliatu per aduprà in a produzzione.

Raccolta di logs da Loki

Chunks ponu esse guardati in un almacenamentu S3-compatibile, è e basa di dati scalabile horizontale ponu esse aduprate per almacenà indici: Cassandra, BigTable o DynamoDB. L'altri parti di Loki - Distributori (per scrive) è Querier (per dumande) - sò senza statu è ancu scala horizontale.

À a cunferenza DevOpsDays Vancouver 2019, unu di i participanti Callum Styan hà annunziatu chì cù Loki u so prughjettu hà petabytes di logs cù un indice di menu di 1% di a dimensione tutale: "Cume Loki Correlates Metrics è Logs - è vi risparmia soldi".

Comparazione di Loki è ELK

Taglia di l'indice

Per pruvà a dimensione di l'indici resultanti, aghju pigliatu logs da u containeru nginx per quale u Pipeline sopra hè stata cunfigurata. U schedariu di logu cuntene 406 linii cù un voluminu tutale di 624 MB. I logs sò stati generati in una ora, circa 109 record per seconda.

Un esempiu di duie linee da u logu:

Raccolta di logs da Loki

Quandu indexatu da ELK, questu hà datu una dimensione d'indice di 30,3 MB:

Raccolta di logs da Loki

In u casu di Loki, questu hà datu circa 128 KB d'indici è circa 3,8 MB di dati in pezzi. Hè da nutà chì u logu hè statu artificialmente generatu è ùn hà micca una larga varietà di dati. Un gzip simplice nantu à u logu originale di Docker JSON cù dati hà datu una cumpressione di 95,4%, è datu chì solu u logu nginx pulitu hè statu mandatu à Loki stessu, a cumpressione à 4 MB hè capisci. U numeru tutale di valori unichi per l'etichette Loki era 35, chì spiega a piccula dimensione di l'indici. Per ELK, u logu hè statu ancu sbulicatu. Cusì, Loki cumpressa i dati originali da 96%, è ELK da 70%.

Cunsumu di memoria

Raccolta di logs da Loki

Se paragunemu tutta a pila di Prometheus è ELK, allora Loki "manghja" parechje volte menu. Hè chjaru chì u serviziu Go cunsuma menu di u serviziu Java, è paragunendu a dimensione di a JVM Heap Elasticsearch è a memoria attribuita per Loki hè sbagliata, ma vale a pena nutà chì Loki usa assai menu memoria. U so vantaghju CPU ùn hè micca cusì evidente, ma hè ancu presente.

Speed

Loki "divora" logs più veloce. A vitezza dipende assai fatturi - chì tipu di logs, quantu sufisticatu noi parse, rete, discu, etc. - ma hè definitu più altu ch'è quellu di ELK (in u mo test - circa duie volte). Questu hè spiegatu da u fattu chì Loki mette assai menu dati in l'indici è, per quessa, passa menu tempu in l'indexazione. In questu casu, a situazione hè invertita cù a veloce di ricerca: Loki rallenta notevolmente nantu à e dati più grande di uni pochi gigabyte, mentri per ELK, a velocità di ricerca ùn dipende micca da a dimensione di dati.

Ricerca di log

Loki hè significativamente inferior à ELK in quantu à e capacità di ricerca di log. Grep cù espressioni regulari hè una cosa forte, ma hè inferjuri à una basa di dati per adulti. A mancanza di dumande di gamma, l'agregazione solu per etichette, l'incapacità di circà senza etichette - tuttu questu ci limita in a ricerca di l'infurmazioni d'interessu in Loki. Questu ùn implica micca chì nunda ùn pò esse truvatu cù Loki, ma definisce u flussu di travaglià cù logs, quandu truvate prima un prublema nantu à i charts Prometheus, è dopu cercate ciò chì hè accadutu in i logs usendu sti etichette.

interfaccia

Prima di tuttu, hè bella (scusate, ùn pudia resiste). Grafana hà una bella interfaccia, ma Kibana hè assai più funziunale.

Loki pro è contro

Di i pluses, pò esse nutatu chì Loki si integra cù Prometheus, rispettivamente, uttene metrica è alerting fora di a scatula. Hè cunvene per cullà logs è almacenà cù Kubernetes Pods, postu chì hà una scuperta di serviziu ereditata da Prometheus è attache automaticamente etichette.

Di i minus - documentazione povira. Certi cose, cum'è e funziunalità è e capacità di Promtail, aghju scupertu solu in u prucessu di studià u codice, u benefiziu di open-source. Un altru svantaghju hè a debule capacità di analisi. Per esempiu, Loki ùn pò micca analizà i logs multiline. Inoltre, i svantaghji includenu u fattu chì Loki hè una tecnulugia relativamente ghjovana (la versione 1.0 era in nuvembre 2019).

cunchiusioni

Loki hè una tecnulugia 100% interessante chì hè adattata per i prughjetti chjuchi è mediani, chì vi permette di risolve parechji prublemi di aggregazione di log, ricerca di log, monitoraghju è analisi di log.

Ùn adupremu micca Loki à Badoo, perchè avemu una pila ELK chì ci cunvene è chì hè stata invasata da diverse soluzioni persunalizate annantu à l'anni. Per noi, u stumbling block hè a ricerca in i logs. Cù quasi 100 GB di logs per ghjornu, hè impurtante per noi di pudè truvà tuttu è un pocu di più è fà rapidamente. Per charting è monitoraghju, usemu altre soluzioni chì sò adattate à i nostri bisogni è integrate l'una cù l'altri. A pila di Loki hà benefici tangibili, ma ùn ci darà micca più di ciò chì avemu, è i so benefici ùn superanu esattamente u costu di a migrazione.

E ancu s'è dopu à a ricerca hè diventatu chjaru chì ùn pudemu micca aduprà Loki, speremu chì questu post vi aiuterà à sceglie.

U repositariu cù u codice utilizatu in l'articulu hè situatu ccà.

Source: www.habr.com

Add a comment