{-# LANGUAGE NoImplicitPrelude, RecordWildCards #-}{-# LANGUAGE FlexibleInstances #-}-- | Fast logging system to copy log data directly to Handle buffer.moduleSystem.Log.FastLogger(-- * LoggerLogger,mkLogger,renewLogger-- * Logging,loggerPutStr,loggerPutBuilder,loggerFlush-- * Strings,LogStr(..),ToLogStr(..)-- * Misc functions,loggerDate-- * File rotation,moduleSystem.Log.FastLogger.File)whereimportBlaze.ByteString.BuilderimportControl.ApplicativeimportControl.MonadimportqualifiedData.ByteStringasBSimportData.ByteString.Internal(ByteString(..),c2w)importData.ListimportData.MaybeimportData.TypeableimportForeignimportGHC.BaseimportGHC.IO.BufferimportqualifiedGHC.IO.BufferedIOasBufferedimportqualifiedGHC.IO.DeviceasRawIOimportGHC.IO.FDimportGHC.IO.Handle.InternalsimportGHC.IO.Handle.TextimportGHC.IO.Handle.TypesimportGHC.IORefimportGHC.NumimportGHC.RealimportSystem.IOimportSystem.Log.FastLogger.DateimportSystem.Log.FastLogger.FileimportqualifiedData.TextasTSimportqualifiedData.Text.EncodingasTEimportqualifiedData.Text.LazyasTLimportqualifiedData.ByteStringasSimportqualifiedData.ByteString.LazyasL-- | Abstract data type for logger.dataLogger=Logger{loggerAutoFlush::Bool,loggerHandle::Handle,loggerDateRef::DateRef}logBufSize::IntlogBufSize=4096initHandle::Handle->IO()initHandlehdl=hSetBufferinghdl(BlockBuffering(JustlogBufSize))-- | Creates a 'Logger' from the given handle.mkLogger::Bool-- ^ Automatically flush on each loggerPut?->Handle-- ^ If 'Handle' is associated with a file, 'AppendMode' must be used.->IOLoggermkLoggerautoFlushhdl=doinitHandlehdlLoggerautoFlushhdl<$>dateInit-- | Creates a new 'Logger' from old one by replacing 'Handle'.-- The new 'Handle' automatically inherits the file mode of-- the old one.-- The old 'Handle' is automatically closed.renewLogger::Logger->Handle->IOLoggerrenewLoggerloggernewhdl=doletoldhdl=loggerHandleloggerhFlusholdhdlhCloseoldhdlinitHandlenewhdlreturn$logger{loggerHandle=newhdl}-- | A date type to contain 'String' and 'ByteString'.-- This data is exported so that format can be defined.-- This would be replaced with 'Builder' someday when-- it can be written directly to 'Handle' buffer.dataLogStr=LS!String|LB!ByteStringclassToLogStrawheretoLogStr::a->LogStrinstanceToLogStr[Char]wheretoLogStr=LSinstanceToLogStrByteStringwheretoLogStr=LBinstanceToLogStrL.ByteStringwheretoLogStr=LB.S.concat.L.toChunksinstanceToLogStrTS.TextwheretoLogStr=LB.TE.encodeUtf8instanceToLogStrTL.TextwheretoLogStr=LB.TE.encodeUtf8.TL.toStricthPutLogStr::Handle->[LogStr]->IO()hPutLogStrhandlebss=wantWritableHandle"hPutLogStr"handle$\h_->bufsWriteh_bss-- based on GHC.IO.Handle.TextbufsWrite::Handle__->[LogStr]->IO()bufsWriteh_@Handle__{..}bss=doold_buf@Buffer{bufRaw=old_raw,bufR=w,bufSize=size}<-readIORefhaByteBufferifsize-w>lenthendowithRawBufferold_raw$\ptr->go(ptr`plusPtr`w)bsswriteIORefhaByteBufferold_buf{bufR=w+len}elsedoold_buf'<-Buffered.flushWriteBufferhaDeviceold_bufwriteIORefhaByteBufferold_buf'ifsize>lenthenbufsWriteh_bsselseallocaBytessize$\ptr->dogoptrbssletJustfd=casthaDevice::MaybeFD_<-RawIO.writeNonBlockingfdptrsizereturn()wherelen=foldl'(\xy->x+getLengthy)0bssgetLength(LBs)=BS.lengthsgetLength(LSs)=lengthsgo::PtrWord8->[LogStr]->IO()go_[]=return()godst(LBb:bs)=dodst'<-copydstbgodst'bsgodst(LSs:ss)=dodst'<-copy'dstsgodst'sscopy::PtrWord8->ByteString->IO(PtrWord8)copydst(PSptrofflen)=withForeignPtrptr$\s->doletsrc=s`plusPtr`off_<-memcpydstsrc(fromIntegrallen)return(dst`plusPtr`len)copy'::PtrWord8->String->IO(PtrWord8)copy'dst[]=returndstcopy'dst(x:xs)=dopokedst(c2wx)copy'(dst`plusPtr`1)xs-- | The 'hPut' function to copy a list of 'LogStr' to the buffer-- of 'Handle' of 'Logger' directly.loggerPutStr::Logger->[LogStr]->IO()loggerPutStrloggerstrs=dohPutLogStrhdlstrswhenautoFlush$hFlushhdlwherehdl=loggerHandleloggerautoFlush=loggerAutoFlushlogger-- | The 'hPut' function directory to copy 'Builder' to the buffer.-- The current implementation is inefficient at this moment.-- This would replace 'loggerPutStr' someday.loggerPutBuilder::Logger->Builder->IO()loggerPutBuilderloggerbuilder=dologgerPutStrlogger.return.LB.toByteString$builderwhenautoFlush$hFlushhdlwherehdl=loggerHandleloggerautoFlush=loggerAutoFlushlogger-- | Flushing the buffer of 'Handle' of 'Logger'.loggerFlush::Logger->IO()loggerFlushlogger=hFlush$loggerHandlelogger-- | Obtaining date string from 'Logger'.loggerDate::Logger->IOZonedDateloggerDatelogger=getDate$loggerDateReflogger