feat: add structured logging to import pipeline
Build and Deploy / build-and-deploy (push) Successful in 1m17s
Build and Deploy / build-and-deploy (push) Successful in 1m17s
Logs each stage of the import with [roux] prefix: request URL, script path, exit codes, stderr output, JSON parse status, conversion status, and file write path. Error messages now include the attempted URL for easier debugging.
This commit is contained in:
+18
-11
@@ -196,6 +196,7 @@ importHandler recipeDir _state request respond =
|
|||||||
if T.null url
|
if T.null url
|
||||||
then respond (htmlResponse (Html.importPage (Just ("URL is required" :: Text))))
|
then respond (htmlResponse (Html.importPage (Just ("URL is required" :: Text))))
|
||||||
else do
|
else do
|
||||||
|
putStrLn $ "[roux] import request for " <> T.unpack url
|
||||||
result <- try $ runImportPipeline recipeDir url
|
result <- try $ runImportPipeline recipeDir url
|
||||||
case result of
|
case result of
|
||||||
Left (e :: SomeException) ->
|
Left (e :: SomeException) ->
|
||||||
@@ -240,50 +241,56 @@ findScrapeScript = do
|
|||||||
-- | Run the full import pipeline: scrape -> parse -> convert -> render -> save.
|
-- | Run the full import pipeline: scrape -> parse -> convert -> render -> save.
|
||||||
runImportPipeline :: FilePath -> Text -> IO (Either Html.ImportError FilePath)
|
runImportPipeline :: FilePath -> Text -> IO (Either Html.ImportError FilePath)
|
||||||
runImportPipeline recipeDir url = do
|
runImportPipeline recipeDir url = do
|
||||||
-- Step 1: scrape the URL
|
-- Step 1: locate and run the scraper
|
||||||
script <- findScrapeScript
|
script <- findScrapeScript
|
||||||
|
putStrLn $ "[roux] importing from " <> T.unpack url <> " using script " <> script
|
||||||
(exitCode, stdout, stderr) <-
|
(exitCode, stdout, stderr) <-
|
||||||
readProcessWithExitCode
|
readProcessWithExitCode
|
||||||
script
|
script
|
||||||
["--pretty", T.unpack url]
|
["--pretty", T.unpack url]
|
||||||
""
|
""
|
||||||
case exitCode of
|
case exitCode of
|
||||||
ExitFailure _ ->
|
ExitFailure _ -> do
|
||||||
|
putStrLn $ "[roux] scrape-recipe exited with code " <> show exitCode <> " for " <> T.unpack url
|
||||||
|
putStrLn $ "[roux] scrape-recipe stderr: " <> stderr
|
||||||
return $
|
return $
|
||||||
Left $
|
Left $
|
||||||
Html.ImportError $
|
Html.ImportError $
|
||||||
"Could not scrape URL. The scraper exited with: " <> T.pack stderr
|
"Could not scrape URL (" <> url <> "). The scraper exited with: " <> T.pack stderr
|
||||||
ExitSuccess -> do
|
ExitSuccess -> do
|
||||||
-- Step 2: decode stdout (String) as JSON Value
|
-- Step 2: decode stdout (String) as JSON Value
|
||||||
-- stdout from readProcessWithExitCode is a locale-decoded String;
|
|
||||||
-- re-encode to UTF-8 for JSON decoding.
|
|
||||||
let jsonBytes = LB.fromStrict (TE.encodeUtf8 (T.pack stdout))
|
let jsonBytes = LB.fromStrict (TE.encodeUtf8 (T.pack stdout))
|
||||||
jsonVal = A.decode jsonBytes :: Maybe A.Value
|
jsonVal = A.decode jsonBytes :: Maybe A.Value
|
||||||
case jsonVal of
|
case jsonVal of
|
||||||
Nothing ->
|
Nothing -> do
|
||||||
|
putStrLn $ "[roux] failed to decode JSON from scrape-recipe stdout for " <> T.unpack url
|
||||||
return $
|
return $
|
||||||
Left $
|
Left $
|
||||||
Html.ImportError $
|
Html.ImportError $
|
||||||
"Could not parse JSON from scraper output."
|
"Could not parse JSON from scraper output (" <> url <> ")."
|
||||||
Just val -> case parseSchemaOrgRecipe val of
|
Just val -> case parseSchemaOrgRecipe val of
|
||||||
Nothing ->
|
Nothing -> do
|
||||||
|
putStrLn $ "[roux] no schema.org Recipe found in JSON for " <> T.unpack url
|
||||||
return $
|
return $
|
||||||
Left $
|
Left $
|
||||||
Html.ImportError $
|
Html.ImportError $
|
||||||
"Could not parse recipe data. The page may not have schema.org recipe data."
|
"Could not parse recipe data from " <> url <> ". The page may not have schema.org recipe data."
|
||||||
Just schema -> case schemaOrgToCooklang schema of
|
Just schema -> case schemaOrgToCooklang schema of
|
||||||
Left err ->
|
Left err -> do
|
||||||
|
putStrLn $ "[roux] failed to convert schema.org recipe for " <> T.unpack url <> ": " <> T.unpack (T.pack err)
|
||||||
return $
|
return $
|
||||||
Left $
|
Left $
|
||||||
Html.ImportError $
|
Html.ImportError $
|
||||||
"Could not convert recipe: " <> T.pack err
|
"Could not convert recipe from " <> url <> ": " <> T.pack err
|
||||||
Right recipe -> do
|
Right recipe -> do
|
||||||
-- Step 3: render to Cooklang text
|
-- Step 3: render to Cooklang text
|
||||||
let cookText = CooklangPrint.renderRecipe recipe
|
let cookText = CooklangPrint.renderRecipe recipe
|
||||||
filename = CooklangPrint.filenameFromTitle (soName schema)
|
filename = CooklangPrint.filenameFromTitle (soName schema)
|
||||||
filepath = recipeDir </> filename
|
filepath = recipeDir </> filename
|
||||||
-- Step 4: write the .cook file
|
-- Step 4: write the .cook file
|
||||||
|
putStrLn $ "[roux] writing imported recipe to " <> filepath
|
||||||
BS.writeFile filepath (TE.encodeUtf8 cookText)
|
BS.writeFile filepath (TE.encodeUtf8 cookText)
|
||||||
|
putStrLn $ "[roux] successfully imported " <> T.unpack url <> " as " <> filename
|
||||||
return $ Right filename
|
return $ Right filename
|
||||||
|
|
||||||
{- | Watch a directory for .cook file changes and update the shared state.
|
{- | Watch a directory for .cook file changes and update the shared state.
|
||||||
|
|||||||
Reference in New Issue
Block a user