class EL_LOG_MANAGER

(source code)

description

Log manager

descendants

EL_LOG_MANAGER
   EL_CRC_32_LOG_MANAGER
note
	description: "Log manager"
	descendants: "[
			EL_LOG_MANAGER
				${EL_CRC_32_LOG_MANAGER}
	]"

	author: "Finnian Reilly"
	copyright: "Copyright (c) 2001-2022 Finnian Reilly"
	contact: "finnian at eiffel hyphen loop dot com"

	license: "MIT license (See: en.wikipedia.org/wiki/MIT_License)"
	date: "2024-03-05 11:26:30 GMT (Tuesday 5th March 2024)"
	revision: "21"

class
	EL_LOG_MANAGER

inherit
	EL_STRING_GENERAL_ROUTINES

	EL_MODULE_LOGGING

	EL_SINGLE_THREAD_ACCESS

	EL_MODULE_ARGS; EL_MODULE_CONSOLE; EL_MODULE_DIRECTORY; EL_MODULE_FILE_SYSTEM

	EL_SHARED_DIRECTORY
		rename
			directory as shared_directory
		end

	EL_SHARED_LOG_OPTION

	EL_SOLITARY
		rename
			make as make_solitary
		end

create
	make

feature {NONE} -- Initialization

	 make (logging_active: BOOLEAN; a_output_directory: DIR_PATH)
			--
		do
			make_solitary; make_default

			is_logging_active := logging_active
			output_directory := a_output_directory
			create log_file_by_thread_id_table.make (11)
			create log_file_by_object_id_table.make (11)
			create thread_id_list.make (11)
			console_manager_active := Log_option.thread_toolbar
		end

feature -- Initialization

	initialize
			--
		do
			create thread_registration_consumer.make

			create thread_registration_queue.make (10)
			thread_registration_queue.attach_consumer (thread_registration_consumer)
		end

feature -- Access

	current_thread_log_path: FILE_PATH
			--
		require
			logging_is_active: is_logging_active
		do
			create Result.make_from_path (current_thread_log_file.path)
		end

	output_directory_path: DIR_PATH
		do
			restrict_access
			Result := output_directory.twin
			end_restriction
		end

	thread_index (name: STRING): INTEGER
			--
		do
			restrict_access
			from
				thread_id_list.start
			until
				thread_name (thread_id_list.item) ~ name or thread_id_list.after
			loop
				thread_id_list.forth
			end
			if not thread_id_list.after then
				Result := thread_id_list.index
			end
			end_restriction
		end

feature -- Element change

	activate_console_manager
			--
		do
			restrict_access
			console_manager_active := true

			end_restriction
		end

	add_thread (thread: EL_IDENTIFIED_THREAD_I)
			--	make thread output visible in console
		local
			log_file: like new_log_file
		do
			if logging.is_active then
				restrict_access
					if log_file_by_object_id_table.has_key (thread.object_id) then
						log_file := log_file_by_object_id_table.found_item
						thread_id_list.put_i_th (thread.thread_id, log_file.index)

						log_file_by_thread_id_table.force (log_file, thread.thread_id)
					else
						thread_id_list.extend (thread.thread_id)
						if thread_id_list.count = 1 then
							thread_id_list.start
						end
						log_file := new_log_file (thread)
						log_file_by_thread_id_table [thread.thread_id] := log_file
						log_file_by_object_id_table [thread.object_id] := log_file

						thread_registration_queue.put ([thread])
					end
				end_restriction
			end
		end

feature -- Status query

	is_console_manager_active: BOOLEAN
			--
		do
			restrict_access
			Result := console_manager_active

			end_restriction
		end

	is_valid_console_index (index: INTEGER): BOOLEAN
			--
		do
			restrict_access
			Result := index >=1 and index <= thread_id_list.count

			end_restriction
		end

	is_logging_active: BOOLEAN

	no_thread_logs_created: BOOLEAN
			--
		do
			restrict_access
			Result := log_file_by_thread_id_table.is_empty

			end_restriction
		end

feature -- Basic operations

	redirect_main_thread_to_console
			-- set output of main thread to console
		do
			redirect_thread_to_console (1)
		end

	redirect_thread_to_console (index: INTEGER)
		--	Activate a thread's logging output to console
		-- (Only one thread can be active at a time)
		require
			valid_index: is_logging_active implies is_valid_console_index (index)
		local
			log_file: EL_FILE_AND_CONSOLE_LOG_OUTPUT
		do
			restrict_access
			if logging.is_active and then thread_id_list.index /= index then
				log_file := log_file_by_thread_id_table [thread_id_list.item]
				log_file.stop_console

				thread_id_list.go_i_th (index)
				log_file := log_file_by_thread_id_table [thread_id_list.item]
				log_file.redirect_to_console
			end

			end_restriction
		end

	redirect_output_to_console (thread: EL_IDENTIFIED_THREAD_I)
		local
			thread_id: POINTER
		do
			thread_id := thread.thread_id
			restrict_access
			if logging.is_active and then thread_id_list.item /= thread_id
				and then log_file_by_thread_id_table.has (thread_id)
			then
				log_file_by_thread_id_table.item (thread_id_list.item).stop_console
				thread_id_list.start; thread_id_list.search (thread_id)
				check
					found_thread: not thread_id_list.exhausted
				end
				log_file_by_thread_id_table.item (thread_id_list.item).redirect_to_console
			end
			end_restriction
		end

feature -- Status setting

	clear_current_thread_log
			--
		local
			log_file: EL_FILE_AND_CONSOLE_LOG_OUTPUT
		do
			log_file := current_thread_log_file
			log_file.close
			log_file.wipe_out
			log_file.open_write
		end

	close_logs
			-- Call only when all threads are joined
		do
			restrict_access
			across log_file_by_thread_id_table as log_file loop
				log_file.item.close
			end
			end_restriction
		end

	flush_current_thread_log
			--
		local
			log_file: EL_FILE_AND_CONSOLE_LOG_OUTPUT
		do
			log_file := current_thread_log_file
			log_file.flush_file
		end

feature -- Removal

	delete_logs
			--
		do
			if output_directory.exists then
				Shared_directory.named (output_directory).delete_content
			end
		end

feature {EL_CONSOLE_MANAGER, EL_LOGGABLE, EL_MODULE_LOG_MANAGER} -- Access

	console_thread_index: INTEGER
		--	 Index number of thread currently directed to console
		do
			restrict_access
			Result := thread_id_list.index

			end_restriction
		end

	console_thread_log_file: EL_FILE_AND_CONSOLE_LOG_OUTPUT
		--	 Log file of thread currently directed to console
		do
			restrict_access
			Result := thread_log_file (thread_id_list.item)

			end_restriction
		end

	current_thread_log_file: EL_FILE_AND_CONSOLE_LOG_OUTPUT
		--	 Log file for calling thread
		do
			restrict_access
			Result := thread_log_file ({THREAD_ENVIRONMENT}.current_thread_id)

			end_restriction
		end

	thread_registration_consumer: EL_ACTION_ARGUMENTS_CONSUMER_MAIN_THREAD [TUPLE [EL_IDENTIFIED_THREAD_I]]

feature {NONE} -- Factory

	new_log_file (thread: EL_IDENTIFIED_THREAD_I): EL_FILE_AND_CONSOLE_LOG_OUTPUT
		do
			if Console.is_highlighting_enabled then
				Result := new_highlighted_output (log_file_path (thread.name), thread.name, thread_id_list.count)
			else
				Result := new_output (log_file_path (thread.name), thread.name, thread_id_list.count)
			end
		end

	new_highlighted_output (log_path: FILE_PATH; a_thread_name: READABLE_STRING_GENERAL; a_index: INTEGER): like new_log_file
		do
			create {EL_FILE_AND_HIGHLIGHTED_CONSOLE_LOG_OUTPUT} Result.make (log_path, a_thread_name, a_index)
		end

	new_output (log_path: FILE_PATH; a_thread_name: READABLE_STRING_GENERAL; a_index: INTEGER): like new_log_file
		do
			create Result.make (log_path, a_thread_name, a_index)
		end

feature {NONE} -- Implementation

	log_file_path (name: READABLE_STRING_GENERAL): FILE_PATH
			--
		local
			version_path: FILE_PATH
		do
			if not output_directory.exists then
				File_system.make_directory (output_directory)
			end
			version_path := output_directory + (as_zstring (name) + ".001." + Default_log_file_extension)
			Result := version_path.next_version_path
		end

	thread_log_file (thread_id: POINTER): EL_FILE_AND_CONSOLE_LOG_OUTPUT
		--	
		do
			Result := log_file_by_thread_id_table [thread_id]
		end

	thread_name (thread_id: POINTER): ZSTRING
		--	
		do
			Result := thread_log_file (thread_id).thread_name
		end

feature {NONE} -- Internal attributes

	console_manager_active: BOOLEAN

	log_file_by_object_id_table: HASH_TABLE [EL_FILE_AND_CONSOLE_LOG_OUTPUT, INTEGER]

	log_file_by_thread_id_table: HASH_TABLE [EL_FILE_AND_CONSOLE_LOG_OUTPUT, POINTER]

	output_directory: DIR_PATH

	thread_id_list: ARRAYED_LIST [POINTER]

	thread_registration_queue: EL_THREAD_PRODUCT_QUEUE [TUPLE [EL_IDENTIFIED_THREAD_I]]

feature {NONE} -- Constants

	Default_log_file_extension: STRING = "log"

end