Troy Hunt představil Have I Been Pwned 2.0, tj. nový vylepšený web služby, kde si uživatelé mohou zkontrolovat, zda se jejich hesla a osobní údaje neobjevili v únicích dat a případně se nechat na další úniky upozorňovat.
Microsoft představil open source textový editor Edit bežící v terminálu. Zdrojové kódy jsou k dispozici na GitHubu pod licencí MIT.
V Seattlu a také online probíhá konference Microsoft Build 2025. Microsoft představuje své novinky. Windows Subsystem for Linux je nově open source. Zdrojové kódy jsou k dispozici na GitHubu pod licencí MIT.
Z příspěvku Turris Sentinel – co přinesl rok 2024 na blogu CZ.NIC: "Za poslední rok (únor 2024 – únor 2025) jsme zachytili 8,3 miliardy incidentů a to z 232 zemí a z jejich závislých území. Tyto útoky přišly od 6,2 milionu útočníků (respektive unikátních adres). SMTP minipot je stále nejlákavější pastí, zhruba 79 % útoků bylo směřováno na tento minipot, 16 % útoků směřovalo na minipot Telnet, 3 % útoků směřovaly na minipot HTTP a 2 % na minipot FTP. Dále jsme zaznamenali 3,2 milionu unikátních hesel a 318 tisíc unikátních loginů, které útočníci zkoušeli."
Byla vydána (Mastodon, 𝕏) nová verze 3.0.4 svobodné aplikace pro úpravu a vytváření rastrové grafiky GIMP (GNU Image Manipulation Program). Přehled novinek v oznámení o vydání a v souboru NEWS na GitLabu. Nový GIMP je již k dispozici také na Flathubu.
Byla vydána nová stabilní verze 7.4 webového prohlížeče Vivaldi (Wikipedie). Postavena je na Chromiu 136. Přehled novinek i s náhledy v příspěvku na blogu.
Spolek vpsFree.cz vydal statistiky týkající se distribucí nasazených na serverech členů. V dlouhodobém pohledu je zřejmé, že většina uživatelů z původního CentOS přechází na Rocky Linux. Pozoruhodný je také nárůst obliby distribuce NixOS, která dnes zaujímá třetí místo po Debianu a Ubuntu.
Google minulý týden představil Material 3 Expressive, tj. novou verzi svého designového jazyka Material Design pro Android 16 a Wear OS 6.
Byl vydán Debian 12.11, tj. jedenáctá opravná verze Debianu 12 s kódovým názvem Bookworm. Řešeny jsou především bezpečnostní problémy, ale také několik vážných chyb. Instalační média Debianu 12 lze samozřejmě nadále k instalaci používat. Po instalaci stačí systém aktualizovat.
Makepad dospěl do verze 1.0 (𝕏). Jedná se o multiplatformní open source UI framework pro Rust napsaný v Rustu.
Pokud jste něco v Go programovali, tak jste se už pravděpodobně setkali s typem os.Error
. Hodnoty typu os.Error
v Go indikují nenormální, chybový stav. Kupříkladu funkce os.Open
vrátí ne nilovou hodnotu os.Error
pokud se nepodaří soubor otevřít.
func Open(name string) (file *File, err Error)
Níže uvedená funkce otevírá soubor voláním os.Open
. Při chybě vypíšeme chybové hlášení a ukončíme program pomocí log.Fatal
.
func main() { f, err := os.Open("filename.ext") if err != nil { log.Fatal(err) } // proměnná f má nyní hodnotu otevřeného souboru typu *File }
Už s tímto množstvím znalostí o os.Error
se dá v Go udělat mnoho věcí, nicméně v dnešním článku se na os.Error
podíváme podrobněji a probereme některé doporučené postupy ošetřování chyb v Go.
os.Error
je rozhraní. Hodnota typu os.Error
reprezentuje jakoukoli hodnotu, která umí sama sebe popsat hodnotou typu string.
package os type Error interface { String() string }
Na rozhraní os.Error
není nic zvláštního. Je to jen široce zaužívaná konvence.
Nejběžnější implementací rozhraní os.Error
je neexportovaný typ errorString v modulu os.
type errorString string func (s errorString) String() string { return string(s) }
Hodnoty typu errorString
je možné vytvářet např. funkcí os.NewError
. Argumentem funkce je řetězec, který je konvertován na typ errorString
, který ovšem implementuje rozhraní os.Error
.
func NewError(s string) Error { return errorString(s) }
os.NewError
se dá použít třeba takto:
func Sqrt(f float64) (float64, os.Error) { if f < 0 { return 0, os.NewError("math: odmocnina ze záporného čísla") } // implementace }
Pokud zavoláme Sqrt
se záporným argumentem, dostaneme ne nilovou hodnotu rozhraní os.Error
(jehož konkrétní implementací je hodnota typu os.errorString
). Textovou hodnotu chyby lze získat buď voláním metody rozhraní os.Error
, tj. String()
, nebo prostým výpisem chyby:
f, err := Sqrt(-1) if err != nil { fmt.Println(err) }
Modul fmt umí vypsat cokoli, co má metodu String()
– a to hodnota typu os.Error
splňuje.
Správný textový souhrn kontextu chyby závisí na implementaci toho kterého chybového typu. Chyba kterou vrací os.Open
má textovou podobu např. „open /etc/passwd: permission denied
“ a nikoli jen „permission denied
“. Naopak chyba vrácená z výše uvedené funkce Sqrt
neříká nic o konkrétní hodnotě nesprávného argumentu.
Tuto informaci můžeme přidat třeba pomocí užitečné funkce fmt.Errorf
. Ta naformátuje řetězec podle stejných pravidel jako fmt.Printf
a výsledek převede na os.Error
použitím os.NewError
.
if f < 0 { return 0, fmt.Errorf("math: odmocnina ze záporného čísla %g", f) }
V mnoha případech je fmt.Errorf
zcela postačující. Avšak díky tomu, že os.Error
je rozhraní, můžeme jako chybu předávat i složitější datové struktury – ty pak poskytnou volajícímu možnost prozkoumat všechny podrobnosti chyby.
Řekněme, že náš hypotetický volající bude opět chtít získat konkrétní neplatnou hodnotu předanou funkci Sqrt
. To se dá zařídit třeba definicí nového chybového typu – místo použití fmt.Errorf
:
type NegativeSqrtError float64 func (f NegativeSqrtError) String() string { return fmt.Sprintf("math: odmocnina ze záporného čísla %g", float64(f)) }
V případě potřeby pak může programátor v některém místě ověřovat typ (type assertion) a nakládat s hodnotu typu NegativeSqrtError
nějakým zvláštním způsobem, zatímco všechna stávající volání, která např. pouze předávají ne nilové chyby funkcím jako fmt.Println
nebo log.Fatal
, nebudou touto změnou nijak dotčeny.
Další příklad – modul json definuje typ SyntaxError
. Funkce json.Decode
jej vrací když zjistí chybu při syntaktické analýze dat ve formátu JSON.
type SyntaxError struct { msg string // popis chyby Offset int64 // chyba zjištěna po přečtení 'Offset' bajtů } func (e *SyntaxError) String() string { return e.msg }
Pole Offset se v textové podobě chybového hlášení vůbec nevyskytuje, ale programátor má nyní možnost tuto hodnotu použít pro přidání údajů o zdrojovém souboru a řádce:
if err := dec.Decode(&val); err != nil { if serr, ok := err.(*json.SyntaxError); ok { line, col := findLine(f, serr.Offset) return fmt.Errorf("%s:%d:%d: %v", f.Name(), line, col, err) } return err }
Uvedený kód je mírně zjednodušenou verzí části skutečného programu – projektu Camlistore.
Rozhraní os.Error
předepisuje pouze metodu String
; konkrétní implementace chyby může mít i další metody. Například modul net vrací v duchu běžné konvence chyby typu os.Error
, ale některé implementace chyb v tomto modulu přidávají ještě další metody, definované rozhraním net.Error
.
package net type Error interface { os.Error Timeout() bool // Byla chyba způsobena vypršením časového limitu? Temporary() bool // Je chyba dočasného charakteru? }
Uživatelský kód může opět ověřením typu detekovat chybu typu net.Error
a následně třeba reagovat různě na dočasnou chybou sítě a trvalou. Takže např. nějaký web crawler by se v prvním případě mohl rozhodnout chvíli počkat a zkusit operaci provést znovu, zatímco při jakékoli jiné nebo neodstranitelné chybě svoji činnost ukončí.
if nerr, ok := err.(net.Error); ok && nerr.Temporary() { time.Sleep(1e9) continue } if err != nil { log.Fatal(err) }
Ošetřování chyb je v Go důležité stejně jako v jiných jazycích. Návrh jazyka a uvedené zvyklosti by měly programátora vybízet k explicitním kontrolám chyb v místě jejich výskytu (v kontrastu ke konvencím některých jiných jazyků, kde se v jednom místě výjimky vyvolávají – aby se pak někdy a někde úplně jinde následně zachytily ). V některých případech to v Go může vést k většímu počtu řádek, které je potřeba napsat. Naštěstí máme po ruce pár triků, jak tento dopad minimalizovat – třeba v případě obsluhy posloupnosti několika možných chyb.
Představme si třeba Go aplikaci v App Engine, která bude obsluhovat HTTP požadavky na záznam z datového úložiště (datastore) a výsledek bude formátovat šablonou.
func init() { http.HandleFunc("/view", viewRecord) } func viewRecord(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) key := datastore.NewKey("Record", r.FormValue("id"), 0, nil) record := new(Record) if err := datastore.Get(c, key, record); err != nil { http.Error(w, err.String(), 500) return } if err := viewTemplate.Execute(w, record); err != nil { http.Error(w, err.String(), 500) } }
Tato funkce ošetřuje chyby vrácené funkcemi datastore.Get
a viewTemplate.Execute
. V obou případech se uživateli zobrazí jednoduché chybové hlášení spolu s HTTP stavovým kódem 500 („Internal Server Error“). Zatím funkce vypadá ještě poměrně dobře, ale pokud přidáme pár dalších obslužných funkcí pro HTTP požadavky, tak rychle skončíme u mnoha kopií stále stejného kódu pro ošetření chyb.
Nadefinováním vlastní obslužné HTTP funkce appHandler
, která vrací chybu, lze takové opakování omezit:
type appHandler func(http.ResponseWriter, *http.Request) os.Error
Stejně tak změňme i funkci viewRecord
, aby i ta vracela chybu:
func viewRecord(w http.ResponseWriter, r *http.Request) os.Error { c := appengine.NewContext(r) key := datastore.NewKey("Record", r.FormValue("id"), 0, nil) record := new(Record) if err := datastore.Get(c, key, record); err != nil { return err } return viewTemplate.Execute(w, record) }
To je sice jednodušší než původní verze, ale modul http neumí zacházet s funkcemi, které vracejí os.Error
. Napravit to můžeme tím, že pro (funkční) typ appHandler
implementujeme metodu ServeHTTP
, definovanou rozhraním http.Handler
:
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if err := fn(w, r); err != nil { http.Error(w, err.String(), 500) } }
Metoda ServeHTTP
zavolá funkci fn a pokud dojde k chybě tak zobrazí uživateli chybové hlášení. Všimněte si, že přijímač metody fn
je funkce – věc, kterou Go umí! Metoda vyvolá tuto funkci tak, že zavolá onen přijímač – příkaz je v tomto případě fn(w, r)
.
Teď už jen postačí při registraci funkce viewRecord
modulem http použít funkci Handle
(místo původní HandleFunc
), protože appHandler
je typu http.Handler
(nikoli http.HandlerFunc
).
func init() { http.Handle("/view", appHandler(viewRecord)) }
Když už máme tuto základní infrastrukturu ošetřování chyb pohromadě, tak bychom ji mohli učinit uživatelsky trochu přívětivější. Místo toho, abychom jen zobrazili textovou podobu chyby, bude možná lepší vrátit uživateli jednoduché chybové hlášení – spolu s příslušným HTTP stavovým kódem – a současně vypsat kompletní popis chyby do vývojářské konzole App Engine a trochu tím tak ulehčit programátorovi ladění.
Toho můžeme dosáhnout deklarací struktury typu appError
, která kromě pole typu os.Error
bude obsahovat ještě pár dalších:
type appError struct { Error os.Error Message string Code int }
Dalším krokem bude úprava typu appHandler – nyní bude vracet hodnoty typu *appError
:
type appHandler func(http.ResponseWriter, *http.Request) *appError
(Obvykle je chyba vracet konkrétní chybový typ místo hodnoty typu rozhraní os.Error
. K důvodům se dostaneme v další části seriálu. V tomto případě je to ale správně, protože ServeHTTP
je jediným místem, které hodnotu „vidí“ a používá její obsah.)
Teď ještě zařídíme, aby metoda ServeHTTP
, definovaná pro typ appHandler
, zobrazila pole Message
struktury appError
, současně se správným HTTP stavovým Code
a ještě vypsala Error
do vývojářské konzole.
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if e := fn(w, r); e != nil { // e je *appError, nikoli os.Error. c := appengine.NewContext(r) c.Errorf("%v", e.Error) http.Error(w, e.Message, e.Code) } }
Jako poslední věc upravíme funkci viewRecord
tak, aby vyhovovala novému „podpisu“ a aby vracela více kontextu při výskytu chyby:
func viewRecord(w http.ResponseWriter, r *http.Request) *appError { c := appengine.NewContext(r) key := datastore.NewKey("Record", r.FormValue("id"), 0, nil) record := new(Record) if err := datastore.Get(c, key, record); err != nil { return &appError{err, "Record not found", 404} } if err := viewTemplate.Execute(w, record); err != nil { return &appError{err, "Can't display record", 500} } return nil }
Tato verze funkce viewRecord
má sice stejnou délku jako ta původní, nyní má ale každý případ ošetření chyby svůj specifický význam (více podrobností – kontextu) a tak poskytujeme uživateli o trochu více pohodlí.
Tím to nekončí. Mohli bychom ošetřování chyb v naší aplikaci ještě dále vylepšovat. Několik námětů:
appError
by šlo napsat konstruktor (v Go je to jen obyčejná funkce), který – opět pro účely ladění – automaticky ukládá při chybě výpis trasy zásobníku někam, kde se to bude později hodit.
recover
) v rámci appHandler. Podrobnostmi se zabývá mj. článek Defer, Panic, and Recover, ale věnovali jsme se této problematice i v předchozích dílech seriálu.
Korektní ošetřování chyb je podstatným požadavkem na každý dobrý program. Využitím technik popsaných v tomto článku byste měli být schopni psát spolehlivější a přitom v zápisu kratší Go programy.
Autor: Andrew Gerrand
(Originální text v angličtině a licence pro šíření: vizte odkazy v záhlaví této české verze.)
Jan Mercl, autor překladu, pracuje v Laboratořích CZ.NIC jako programátor pro výzkum a vývoj.
Nástroje: Tisk bez diskuse
Tiskni
Sdílej: