wai-logger FileLogSpec as Scotty Middleware сообщает об openFile: ресурс занят (файл заблокирован)

Я использую Scotty для внутреннего приложения на Haskell, и мне интересно регистрировать все запросы в файл. Существующее промежуточное ПО wai запрос-логгер недостаточно, так как мне бы хотелось, чтобы свойства FileLogSpec, так как мне нравится иметь несколько коротких файлов журналов, что позволяет FileLogSpec.

Я также открыт для альтернативных решений по вопросу регистрации, но я не заинтересован в переходе от Скотти.

Моя проблема в том, что после первого запроса я получаю эту ошибку:

openFile: resource busy (file is locked)
openFile: resource busy (file is locked)

и приложение полностью прекращает ведение журнала.

Моя текущая реализация превращения wai-logger в промежуточное программное обеспечение:

-- type Middleware = Application -> Application
-- Type Application = Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
mw :: Wai.Middleware
mw (app::Wai.Application) (req::Request) (fRes::(Response -> IO ResponseReceived)) =
    app req myResponseFunction
    where
        myResponseFunction :: Response -> IO ResponseReceived
        myResponseFunction response = do
            logger' <- logger
            logger' req (Wai.responseStatus response) Nothing
            fRes response
        logger :: IO Logger.ApacheLogger
        logger = createLoggerActions >>= return . Logger.apacheLogger
        createLoggerActions :: IO Logger.ApacheLoggerActions
        createLoggerActions = let
            -- 5 MB in bytes = 5242880
            mb5InBytes = 5242880
            -- From fast-logger default
            defaultBufSize = 4096 in
                Logger.initLogger
                Logger.FromFallback
                (Logger.LogFile Logger.FileLogSpec {Logger.log_file = "reqLogger", Logger.log_file_size = mb5InBytes, Logger.log_backup_number = 5} defaultBufSize)
                ( return $ B.pack "%d/%b/%Y:%T %z")

Я попытался использовать скобку для очистки регистратора следующим образом:

mw :: Wai.Middleware    
mw (app::Wai.Application) (req::Request) (fRes::(Response -> IO ResponseReceived)) =
    let loggerActions = createLoggerActions in
        bracket_ setup (teardown loggerActions) $ 
        app req (myResponseFunction loggerActions)
    where
        myResponseFunction :: IO Logger.ApacheLoggerActions -> Response -> IO ResponseReceived
        myResponseFunction loggerActions response = do
            logger' <- logger loggerActions
            logger' req (Wai.responseStatus response) Nothing
            fRes response
        logger :: IO Logger.ApacheLoggerActions -> IO Logger.ApacheLogger
        logger loggerActions = loggerActions >>= return . Logger.apacheLogger
        createLoggerActions :: IO Logger.ApacheLoggerActions
        createLoggerActions = let
            -- 5 MB in bytes = 5242880
            mb5InBytes = 5242880
            -- From fast-logger default
            defaultBufSize = 4096 in
                Logger.initLogger
                Logger.FromFallback
                (Logger.LogFile Logger.FileLogSpec {Logger.log_file = "reqLogger", Logger.log_file_size = mb5InBytes, Logger.log_backup_number = 5} defaultBufSize)
                ( return $ B.pack "%d/%b/%Y:%T %z")
        setup :: IO ()
        setup = return ()
        teardown :: IO (Logger.ApacheLoggerActions) -> IO ()
        teardown loggerActions = loggerActions >>= Logger.logRemover

И вот как я его использую:

someFunc :: IO ()
someFunc = do
    appConfig <- loadAppConfig
    workingDir <- Dir.getCurrentDirectory
    putStrLn $ "Working directory: " <> workingDir
    case appConfig of
        Nothing -> putStrLn "Failed to load appconfig.json"
        Just appConfig' ->
            let checkSizeRequirement' = checkSizeRequirement (sizeLimitGB appConfig') in
                scotty 3000 $ do
                    --middleware logStdoutDev
                    middleware $ mw
                    middleware $ staticPolicy (addBase "./ElmFeFMI/")
                    post "/api/fmichecker" ...

К сожалению, это привело к тому же результату.

Я также создал задачу на wai logger: https://github.com/kazu-yamamoto/logger/issues/183

Полный исходный код доступен здесь: https://github.com/into-cps-association/utilities_backend


person Casper Thule Hansen    schedule 17.10.2019    source источник


Ответы (1)


Sicklorkin из irc freenode #haskell ответил довольно очевидным решением -

Убедитесь, что вы случайно не открыли регистратор дважды.

и попросил меня попробовать пройти в регистраторе.

Итак, новое и рабочее решение:

someFunc :: IO ()
someFunc = do
    appConfig <- loadAppConfig
    workingDir <- Dir.getCurrentDirectory
    putStrLn $ "Working directory: " <> workingDir
    fileLogger <- createLogger 
    case appConfig of
        Nothing -> putStrLn "Failed to load appconfig.json"
        Just appConfig' ->
            let checkSizeRequirement' = checkSizeRequirement (sizeLimitGB appConfig') in
                scotty 3000 $ do
                    --middleware logStdoutDev
                    middleware $ (mw fileLogger)

Далее mw был отделен от createLogger:

mw :: Logger.ApacheLogger -> Wai.Middleware
mw logger (app::Wai.Application) (req::Request) (fRes::(Response -> IO ResponseReceived)) =
    app req myResponseFunction
    where
        myResponseFunction :: Response -> IO ResponseReceived
        myResponseFunction response = do
            logger req (Wai.responseStatus response) Nothing
            fRes response

createLogger :: IO Logger.ApacheLogger
createLogger = createLoggerActions >>= return . Logger.apacheLogger
person Casper Thule Hansen    schedule 17.10.2019