Lua Academy · leksjon

Strukturerte feilobjekter

Send tabeller som feilobjekter for å formidle type og kontekst.

Leksjon 4 av 412 trinn

Strukturerte feilobjekter er en gratis leksjon i Lua Academy på CoddyKit. Dette er leksjon 4 av 4. Du kan lese valgfritt 3 leksjoner fra denne læringsstien gratis i sin helhet – deretter låser CoddyKit PRO opp alle leksjoner, samt praktisk øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i Lua Academy, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i Lua Academy inneholder totalt 4 leksjoner.

Hvorfor strukturerte feil?

Vanlige strengfeil er vanskelige å håndtere programmatisk. Strukturerte feilobjekter (tabeller) inneholder typeinformasjon og kontekstdata, og kallere kan undersøke og reagere på dem. Dette muliggjør feilbasert ruting uten strengparsing.

-- Plain string: hard to handle programmatically
error("database error: connection refused")

-- Structured: type + data
error({
  type = "DatabaseError",
  code = "CONN_REFUSED",
  host = "localhost",
  port = 5432,
  message = "connection refused"
})

Mønster for feilkonstuktører

Opprett en feilfabrikkfunksjon for hver feiltype. Fabrikken bygger en tabell med konsistente felt: type, melding og relevant kontekst. En __tostring-metamethod gjør at feilen skrives ut på en lesbar måte.

local ErrorMT = {__tostring = function(e)
  return string.format("[%s] %s", e.type, e.message)
end}

local function makeError(errType, msg, data)
  local e = {type=errType, message=msg}
  if data then for k,v in pairs(data) do e[k]=v end end
  return setmetatable(e, ErrorMT)
end

local E = {
  notFound = function(name) return makeError("NOT_FOUND","not found: "..name,{name=name}) end,
  badInput = function(msg,field) return makeError("BAD_INPUT",msg,{field=field}) end,
}

local ok, err = pcall(error, E.notFound("user:42"))
print(tostring(err))   -- [NOT_FOUND] not found: user:42

Typesjekking av feilobjekter

Etter at De har fanget en feil, bør De kontrollere om den er en tabell med et kjent typefelt. Da kan De velge ulike strategier for gjenoppretting basert på feiltypen, uten å være avhengig av mønstergjenkjenning for strenger.

local function handleRequest(fn)
  local ok, err = pcall(fn)
  if ok then return true end
  if type(err) == "table" then
    if err.type == "NOT_FOUND" then
      print("404: " .. err.message)
    elseif err.type == "BAD_INPUT" then
      print("400: " .. err.message .. " (field: " .. (err.field or "?") .. ")")
    else
      print("500: unhandled error: " .. tostring(err))
    end
  else
    print("500: " .. tostring(err))
  end
  return false
end

Feilhierarki

Simuler et feilhierarki ved å kontrollere is_a-felter eller bruke metatables. Underordnede feiltyper arver feltene til den overordnede feiltypen og kan behandles som den overordnede typen av kode som ikke trenger detaljene.

local function isError(e, errType)
  if type(e) ~= "table" then return false end
  return e.type == errType or e.parentType == errType
end

local function makeDbError(code, msg)
  return {type="DbError:"..code, parentType="DbError", code=code, message=msg}
end

local err = makeDbError("TIMEOUT","query timed out")
print(isError(err, "DbError"))          -- true
print(isError(err, "DbError:TIMEOUT"))  -- true
print(isError(err, "NetworkError"))     -- false

Pakke inn feil

Når De fanger og kaster en feil på nytt, bør De pakke inn den opprinnelige feilen for å legge til kontekst uten å miste den. Omslaget har sin egen type og inneholder den opprinnelige feilen som årsak.

local function wrapError(msg, cause)
  return {
    type = "WrappedError",
    message = msg,
    cause = cause,
  }
end

local function loadConfig(path)
  local ok, err = pcall(function()
    local f = assert(io.open(path,"r"))
    local content = f:read("a")
    f:close()
    return content
  end)
  if not ok then
    error(wrapError("failed to load config: "..path, err))
  end
end

local ok2, e = pcall(loadConfig, "missing.cfg")
if not ok2 then
  print(e.message)
  print("Caused by:", tostring(e.cause))
end

Feilkoder kontra feiltyper

To vanlige konvensjoner er feilkoder (numeriske, som HTTP-statuskoder) og feiltypestrenger (semantiske navn). Feilkoder er enkle å sammenligne numerisk, mens typestrenger forklarer seg selv. Mange systemer bruker begge deler.

local STATUS = {OK=200, NOT_FOUND=404, SERVER_ERROR=500, BAD_REQUEST=400}

local function makeStatusError(status, msg)
  return {status=status, message=msg, type="HTTPError"}
end

local function handleError(e)
  if e.status == STATUS.NOT_FOUND then
    print("Resource not found:", e.message)
  elseif e.status >= 500 then
    print("Server error:", e.message)
  else
    print("Error", e.status, e.message)
  end
end

handleError(makeStatusError(404, "user not found"))

Feilstakk (årsakskjede)

Når en feil skyldes en annen feil, bør De lenke dem sammen. Dette gir et fullstendig bilde av hva som gikk galt på hvert lag i applikasjonen. Pakk ut kjeden for å logge eller vise hele feilhistorien.

local function unwindCause(e, depth)
  depth = depth or 0
  local pad = string.rep("  ", depth)
  if type(e) == "table" then
    print(pad .. (e.type or "Error") .. ": " .. (e.message or "?"))
    if e.cause then unwindCause(e.cause, depth+1) end
  else
    print(pad .. tostring(e))
  end
end

local inner = {type="IoError", message="permission denied"}
local outer = {type="ConfigError", message="cannot load config", cause=inner}
unwindCause(outer)
-- ConfigError: cannot load config
--   IoError: permission denied

Feil i callback-kontekst

Når feil oppstår inne i callbacks (hendelsesbehandlere, iteratorer), propageres de til den som kalte callbacken. Bruk pcall til å fange dem og rapportere hvilken callback som mislyktes, sammen med relevant kontekst.

local function runCallbacks(callbacks, data)
  local errors = {}
  for name, fn in pairs(callbacks) do
    local ok, err = pcall(fn, data)
    if not ok then
      errors[#errors+1] = {callback=name, error=err}
    end
  end
  return errors
end

local cbs = {
  validate = function(d) assert(d.name, "name required") end,
  transform = function(d) d.name = d.name:upper() end,
}

local errs = runCallbacks(cbs, {})
for _, e in ipairs(errs) do
  print(e.callback, "->", e.error)
end

Skrive ut feildetaljer

En hjelpefunksjon som skriver ut et feilobjekt på en strukturert og lesbar måte, og som håndterer både streng- og tabellfeil. Dette er nyttig ved applikasjonsgrenser der feil logges eller vises til brukere.

local function printError(err, prefix)
  prefix = prefix or "Error"
  if type(err) ~= "table" then
    print(prefix .. ": " .. tostring(err))
    return
  end
  print(prefix .. " [" .. (err.type or "unknown") .. "]")
  print("  Message: " .. (err.message or "?"))
  for k, v in pairs(err) do
    if k ~= "type" and k ~= "message" and k ~= "cause" then
      print("  " .. k .. ": " .. tostring(v))
    end
  end
  if err.cause then printError(err.cause, "  Caused by") end
end

Assert med strukturerte feil

Opprett en assertT (assert med typede feil) som utløser en strukturert feil i stedet for en vanlig streng. Da blir det enkelt for kallere å teste etter bestemte feiltyper.

local function assertT(cond, errType, msg, data)
  if not cond then
    local e = {type=errType, message=msg}
    if data then for k,v in pairs(data) do e[k]=v end end
    error(e, 2)
  end
  return cond
end

local function createUser(name, age)
  assertT(type(name)=="string", "BAD_INPUT", "name must be string", {field="name"})
  assertT(age >= 0 and age <= 150, "BAD_INPUT", "invalid age", {field="age", value=age})
  return {name=name, age=age}
end

local ok, err = pcall(createUser, "Alice", -5)
if not ok then print(err.type, err.field, err.value) end

Hurtigsjekk

Hva er den viktigste fordelen ved å sende en tabell til error() i stedet for en streng?

Oppsummering: strukturerte feil

Oppsummering:

  • Send tabeller til error() for strukturerte feil som kan undersøkes
  • Ta med type, melding og relevante kontekstfelter
  • Legg til __tostring for lesbar utskrift
  • Pakk inn feil for å legge til kontekst uten å miste årsaken
  • Velg håndtering etter type: kontroller err.type, ikke strengmønstre
Gratis å komme i gang

Lær deg Lua med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
40
Leksjoner
159

Ofte stilte spørsmål

Er leksjonen «Strukturerte feilobjekter» gratis?

Ja – du kan lese valgfritt 3 av leksjonene i læringsstien Lua Academy, inkludert «Strukturerte feilobjekter», gratis i sin helhet her på nettet. Deretter låser CoddyKit PRO opp alle leksjoner, samt interaktiv øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Kurset i Lua Academy inneholder totalt 4 leksjoner.

Hva lærer jeg i «Strukturerte feilobjekter»?

Send tabeller som feilobjekter for å formidle type og kontekst. Du øver på Lua Academy med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med Lua Academy?

Ingen tidligere erfaring er nødvendig. Lua Academy på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 4 av 4.

Hvor lang tid tar leksjonen «Strukturerte feilobjekter»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne Lua Academy-leksjonen?

Ja. Alle Lua Academy-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. Funksjonen error()
  2. Beskyttede kall med pcall
  3. xpcall og meldingsbehandlere
  4. Strukturerte feilobjekter
← Tilbake til Lua Academy