;************************************** ;* ;* lhs_log_ext.pbi ;* ;* Multi Logfile thread logger. ;* ;* For single logthread use lhs_log.pbi ;* ;* (c) by René Linder ;* (c) by Linder Hard- und Software ;* ;* Lizenz LGPL V2.1 ;{ Short Documentation: ;* ;* lhs_log_ext::App_Name = "Test App" ;* LogUUID.s = lhs_log_ext::Create("TestLog") ;* lhs_log_ext::SetLogFile(LogUUID,"test_app.log")) ;* lhs_log_ext::SetMaxSize(LogUUID,32) ;* lhs_log_ext::SetLogLevel(LogUUID,lhs_log_ext::#Error) ;* lhs_log_ext::SetLogDateFormat(LogUUID,"%yyyy.%mm.%dd %hh:%ii:%ss") ;* lhs_log_ext::SetLogFileDateFormat(LogUUID,"%yyyy_%mm_%dd_%hh_%ii_%ss") ;* lhs_log_ext::Init(LogUUID,) ;* lhs_log_ext::Out(LogUUID,"Log started.", #lhs_log_ext::#Debug) ;* lhs_log_ext::OutL(LogUUID,lhs_log_ext::#Info, "Info Level") ;* lhs_log_ext::Close(LogUUID) ;* lhs_log_ext::StopAllLogger() ;} CompilerIf #PB_Compiler_Thread <> 1 CompilerError "Muss Threadsafe Kompiliert werden" ;Need to be compiled threadsafe CompilerEndIf DeclareModule lhs_log_ext Global.s App_Name = "lhs_ext_Logger" #Debug = #True #NoDebug = #False #Console = #True #NoConsole = #False Structure LogThreads Logger_UUID.s Logger_Name.s Thread.i FileID.i LogFile.s Mutex.i WorkerSemaphore.i ExitSemaphore.i Exit.i MaxSize.q LastSize.q LogLevel.i LogFileDateFormat.s LogDateFormat.s debugger.i console.i List Messages.s() EndStructure Global NewMap Logger.LogThreads() Global NewList LoggerUUID.s() Enumeration LogLevel #NoLog #Error #Warning #Info #Debugging EndEnumeration Declare IsCreated(UUID.s) Declare SetLogFile(UUID.s, LogFileName.s) Declare SetMaxSize(UUID.s, Megabyte.i) Declare SetLogLevel(UUID.s, Level.i) Declare SetLogFileDateFormat(UUID.s, DateFormat.s) Declare SetLogDateFormat(UUID.s, DateFormat.s) Declare.s Create(Name.s) Declare Init(UUID.s) Declare Out(UUID.s, ToLog.s, debugger.i = #NoDebug) Declare OutL(UUID.s, LogType.i, ToLog.s, debugger.i = #NoDebug) Declare Close(UUID.s) Declare StopAllLogger() EndDeclareModule Module lhs_log_ext XIncludeFile "lhs_uuid.pbi" Global CreateLoggerMutex.i = CreateMutex() Global ConsoleMutex.i = CreateMutex() Procedure.s Create(Name.s) Define.s InternalUUID If Len(Name) > 0 And TryLockMutex(CreateLoggerMutex) InternalUUID = CreateUUID() AddElement(LoggerUUID()) LoggerUUID() = InternalUUID Logger(InternalUUID)\Logger_UUID = InternalUUID Logger(InternalUUID)\Logger_Name = Name Logger(InternalUUID)\LastSize = 0 Logger(InternalUUID)\Exit.i = 0 Logger(InternalUUID)\Mutex = CreateMutex() Logger(InternalUUID)\WorkerSemaphore = CreateSemaphore() Logger(InternalUUID)\ExitSemaphore = CreateSemaphore() Logger(InternalUUID)\MaxSize.q = 32 * 1024 * 1024 Logger(InternalUUID)\LastSize.q = 0 Logger(InternalUUID)\LogLevel.i = #Info Logger(InternalUUID)\LogFileDateFormat.s = "%yyyy_%mm_%dd_%hh_%ii_%ss" Logger(InternalUUID)\LogDateFormat.s = "%yyyy.%mm.%dd %hh:%ii:%ss" Logger(InternalUUID)\console = #NoConsole UnlockMutex(CreateLoggerMutex) ProcedureReturn InternalUUID EndIf EndProcedure Procedure IsCreated(UUID.s) If FindMapElement(Logger(), UUID) ProcedureReturn #True EndIf ProcedureReturn #False EndProcedure Procedure StopAllLogger() LockMutex(CreateLoggerMutex) ResetList(LoggerUUID()) While NextElement(LoggerUUID()) Close(LoggerUUID()) Wend ClearList(LoggerUUID()) If MapSize(Logger()) = 0 UnlockMutex(CreateLoggerMutex) Else ResetMap(Logger()) While NextMapElement(Logger()) If IsThread(Logger()\Thread) Close(Logger()\Logger_UUID) Else DeleteMapElement(Logger()) EndIf Wend EndIf EndProcedure Procedure SetMaxSize(UUID.s, Megabyte.i) If IsCreated(UUID) If Megabyte > 0 And Megabyte < 1025 Logger(UUID)\MaxSize = Megabyte * 1024 * 1024 Else Logger(UUID)\MaxSize = 32 * 1024 * 1024 EndIf EndIf EndProcedure Procedure SetLogLevel(UUID.s, Level.i) If IsCreated(UUID) Select Level Case #Info Logger(UUID)\LogLevel = #Info Case #Warning Logger(UUID)\LogLevel = #Warning Case #Error Logger(UUID)\LogLevel = #Error Default Logger(UUID)\LogLevel = #Error EndSelect EndIf EndProcedure Procedure SetLogFile(UUID.s, LogFileName.s) Protected.i TestFile_ID If IsCreated(UUID) TestFile_ID = OpenFile(#PB_Any, LogFileName, #PB_File_Append | #PB_File_NoBuffering) If IsFile(TestFile_ID) Logger(UUID)\LogFile = LogFileName CloseFile(TestFile_ID) ProcedureReturn #True Else ProcedureReturn #False EndIf EndIf EndProcedure Procedure SetLogFileDateFormat(UUID.s, DateFormat.s) If IsCreated(UUID) If Len(DateFormat)>4 Logger(UUID)\LogFileDateFormat = DateFormat EndIf EndIf EndProcedure Procedure SetLogDateFormat(UUID.s, DateFormat.s) If IsCreated(UUID) If Len(DateFormat)>4 Logger(UUID)\LogDateFormat = DateFormat EndIf EndIf EndProcedure Procedure CheckMaxSize(UUID.s) If IsCreated(UUID) If Logger(UUID)\LastSize = 0 Logger(UUID)\LastSize = FileSize(Logger(UUID)\LogFile) EndIf ProcedureReturn Bool(Logger(UUID)\LastSize < Logger(UUID)\MaxSize) EndIf EndProcedure Procedure UpdateLastSizeString(UUID.s, String.s) If IsCreated(UUID) Logger(UUID)\LastSize + StringByteLength(String.s) EndIf EndProcedure Procedure LogThread(UUID_Address) Protected MyUUID.s = PeekS(UUID_Address) Protected File_ID Define ToWrite.s SignalSemaphore(Logger(MyUUID)\ExitSemaphore) WaitSemaphore(Logger(MyUUID)\WorkerSemaphore) File_ID = OpenFile(#PB_Any, Logger(MyUUID)\LogFile, #PB_File_Append | #PB_File_NoBuffering) If IsFile(File_ID) Repeat WaitSemaphore(Logger(MyUUID)\WorkerSemaphore) If Exit = 1 LockMutex(Logger(MyUUID)\Mutex) If ListSize(Logger(MyUUID)\Messages()) > 0 Repeat FirstElement(Logger(MyUUID)\Messages()) ToWrite = FormatDate(Logger(MyUUID)\LogDateFormat, Date()) + " - " + Logger(MyUUID)\Messages() UpdateLastSizeString(MyUUID, ToWrite):WriteStringN(File_ID, ToWrite) DeleteElement(Logger(MyUUID)\Messages()) Until ListSize(Logger(MyUUID)\Messages()) = 0 EndIf CloseFile(File_ID) UnlockMutex(Logger(MyUUID)\Mutex) Break Else LockMutex(Logger(MyUUID)\Mutex) FirstElement(Logger(MyUUID)\Messages()) ToWrite = FormatDate(Logger(MyUUID)\LogDateFormat, Date()) + " - " + Logger(MyUUID)\Messages() UpdateLastSizeString(MyUUID, ToWrite) If Not CheckMaxSize(MyUUID) CloseFile(File_ID) If CopyFile(Logger(MyUUID)\LogFile, Logger(MyUUID)\LogFile+FormatDate(Logger(MyUUID)\LogFileDateFormat, Date())) DeleteFile(Logger(MyUUID)\LogFile) EndIf File_ID = OpenFile(#PB_Any, Logger(MyUUID)\LogFile, #PB_File_Append | #PB_File_NoBuffering) EndIf WriteStringN(File_ID, ToWrite) DeleteElement(Logger(MyUUID)\Messages()) UnlockMutex(Logger(MyUUID)\Mutex) EndIf ForEver SignalSemaphore(Logger(MyUUID)\ExitSemaphore) EndIf EndProcedure Procedure Init(UUID.s) If IsCreated(UUID) File_ID = OpenFile(#PB_Any, Logger(UUID)\LogFile, #PB_File_Append | #PB_File_NoBuffering) If IsFile(File_ID) CloseFile(File_ID) Else ProcedureReturn #False EndIf CreateThread(@LogThread(), @UUID) WaitSemaphore(Logger(UUID)\ExitSemaphore) SignalSemaphore(Logger(UUID)\WorkerSemaphore) ProcedureReturn #True Else ProcedureReturn #False EndIf EndProcedure Procedure OutPutConsole(ToLog.s) ;Hardlock ConsoleOutput If LockMutex(ConsoleMutex) PrintN(ToLog) UnlockMutex(ConsoleMutex) EndIf EndProcedure Procedure Out(UUID.s, ToLog.s, debugger.i = #NoDebug) If IsCreated(UUID) LockMutex(Logger(UUID)\Mutex) LastElement(Logger(UUID)\Messages()) AddElement(Logger(UUID)\Messages()) Logger(UUID)\Messages() = ToLog If Logger(UUID)\debugger = #Debug Debug ToLog EndIf If Logger(UUID)\console = #Console OutPutConsole(ToLog) EndIf UnlockMutex(Logger(UUID)\Mutex) SignalSemaphore(Logger(UUID)\WorkerSemaphore) EndIf EndProcedure Procedure OutL(UUID.s, LogType.i, ToLog.s, debugger.i = #NoDebug) If IsCreated(UUID) If debugger = #Debug LogType = #Debugging EndIf If LogType <= Logger(UUID)\LogLevel Or debugger = #Debug Select LogType Case #Info ToLog = "Info: " + ToLog Case #Warning ToLog = "Warning: " + ToLog Case #Error ToLog = "Error: " + ToLog Case #Debugging ToLog = "Debug: " + ToLog Default ToLog = "Debug: " + ToLog EndSelect Out(UUID, ToLog, debugger) ProcedureReturn #True EndIf EndIf EndProcedure Procedure Close(UUID.s) If IsCreated(UUID) Logger(UUID)\Exit=1 SignalSemaphore(Logger(UUID)\WorkerSemaphore) WaitSemaphore(Logger(UUID)\ExitSemaphore) DeleteMapElement(Logger(), UUID) EndIf EndProcedure EndModule