class EROS_SERVER_ACTIVITY_METERS

(source code)

description

Server activity meters

note
	description: "Server activity meters"

	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: "2023-03-10 10:10:55 GMT (Friday 10th March 2023)"
	revision: "11"

class
	EROS_SERVER_ACTIVITY_METERS

inherit
	EL_MAIN_THREAD_REGULAR_INTERVAL_EVENT_CONSUMER
		rename
			on_event as refresh,
			on_events_start as on_server_activity_start,
			on_events_end as  on_server_activity_end,
			stop as stop_consumer,
			prompt as prompt_refresh
		export
			{NONE} all
		redefine
			on_server_activity_start, on_server_activity_end, prompt_refresh
		end

	EL_MODULE_LOG

	EROS_UI_CONSTANTS

create
	make

feature {NONE} -- Initialization

	make (a_max_threads: INTEGER)
			--
		local
			row: EV_HORIZONTAL_BOX
		do
			make_default
			max_threads := a_max_threads

			create service_stats.make (max_threads)
			service_stats.set_display_refresh_timer (
				create {EL_LOGGED_REGULAR_INTERVAL_EVENT_PROCESSOR}.make_event_producer ("Activity meters refresh timer", Current, Refresh_interval)
			)

			create thread_count_suffix.make_from_string (" of ")
			thread_count_suffix.append_integer (max_threads)

			create row
			row.set_border_width (5)
			row.set_padding (10)

			create thread_count_meter
			create queued_connection_count_meter
			row.extend (
				create_meter_column ("Connections", <<
					create_meter_frame ("Request threads", << thread_count_meter >>),
					create_meter_frame ("Queued connections", << queued_connection_count_meter >>)
				>>)
			)
			row.disable_item_expand (row.last)
			row.extend (vertical_separator (5))

			create procedure_count_meter; create procedure_rate_meter
			create function_count_meter; create function_rate_meter
			row.extend (
				create_meter_column ("Routines", <<
					create_meter_frame ("Procedures executed", << procedure_count_meter, procedure_rate_meter >>),
					create_meter_frame ("Functions executed", << function_count_meter, function_rate_meter >>)
				>>)
			)
			row.disable_item_expand (row.last)
			row.extend (vertical_separator (5))

			create failure_meter
			row.extend (
				create_meter_column ("Errors", <<
					create_meter_frame ("Routine failures", << failure_meter >>)
				>>)
			)
			row.disable_item_expand (row.last)
			row.extend (vertical_separator (5))

			create data_received_meter; create data_received_rate_meter
			create data_sent_meter; create data_sent_rate_meter
			row.extend (
				create_meter_column ("Data volume", <<
					create_meter_frame ("Megabytes received", << data_received_meter, data_received_rate_meter >>),
					create_meter_frame ("Megabytes sent", << data_sent_meter, data_sent_rate_meter >>)
				>>)
			)
			row.disable_item_expand (row.last)

			widget := row
		end

feature -- Access

	widget: EV_WIDGET

	service_stats: EROS_ROUTINE_CALL_SERVICE_STATS

feature -- Basic operations

	reset
			--
		do
			refresh_count := 0
			refresh_count := 0

			-- Line 1
			set_thread_count_meter (0)
			set_count_meter (queued_connection_count_meter, 0, Format_queued_connection_count)

			set_count_meter (function_count_meter, 0, Format_count)
			set_routine_rate_meter (function_rate_meter, 0)

			set_count_meter (procedure_count_meter, 0, Format_count)
			set_routine_rate_meter (procedure_rate_meter, 0)

			set_count_meter (failure_meter, 0, Format_failed_connection_count)

			-- Line 2
			set_data_meter (data_received_meter, 0)
			set_data_rate_meter (data_received_rate_meter, 0)
			set_data_meter (data_sent_meter, 0)
			set_data_rate_meter (data_sent_rate_meter, 0)
		end

	refresh
			--
		do
			if refresh_count \\ 5 = 0 then
				log.enter_no_header ("refresh")
				log.put_integer_field ("Activity meters refresh count", refresh_count)
				log.put_new_line
				log.exit_no_trailer
			end

			set_count_meter (function_count_meter, service_stats.function_count.item, Format_count)
			set_routine_rate_meter (function_rate_meter, service_stats.function_rate)

			set_count_meter (procedure_count_meter, service_stats.procedure_count.item, Format_count)
			set_routine_rate_meter (procedure_rate_meter, service_stats.procedure_rate)

			set_count_meter (failure_meter, service_stats.failure_count.item, Format_failed_connection_count)

			set_data_meter (data_received_meter, service_stats.bytes_received_count.item)
			set_data_rate_meter (data_received_rate_meter, service_stats.bytes_received_rate)

			set_data_meter (data_sent_meter, service_stats.bytes_sent_count.item)
			set_data_rate_meter (data_sent_rate_meter, service_stats.bytes_sent_rate)

			set_thread_count_meter (service_stats.thread_count.item)
			set_count_meter (
				queued_connection_count_meter, service_stats.queued_connection_count.item,
				Format_queued_connection_count
			)
		end

feature -- Element change

	set_routine_rate_meter (meter: EV_LABEL; rate: INTEGER)
			--
		do
			meter.set_text (Format_rate.formatted (rate) + once "/sec")
		end

	set_data_rate_meter (meter: EV_LABEL; byte_rate: DOUBLE)
			--
		do
			meter.set_text (Format_data_rate.formatted (byte_rate / Mega_bytes) + once "/sec")
		end

	set_count_meter (meter: EV_LABEL; count: INTEGER; format: FORMAT_INTEGER)
			--
		do
			meter.set_text (format.formatted (count))
		end

	set_thread_count_meter (count: INTEGER)
			--
		do
			thread_count_meter.set_text (Format_thread_count.formatted (count) + thread_count_suffix)
		end

	set_data_meter (meter: EV_LABEL; bytes: INTEGER_64)
			--
		do
			meter.set_text (Format_data.formatted (bytes / Mega_bytes) + once " mb")
		end

feature {NONE} -- UI building

	create_meter_frame (a_name: STRING; meters: ARRAY [EV_LABEL]): EL_FRAME [EL_HORIZONTAL_BOX]
			--
		local
			meter: EV_LABEL; i: INTEGER
		do
			create Result.make_with_text (0.2, 0.2, a_name)
			Result.set_style (Frame_style.Ev_frame_etched_out)

			from i := 1 until i > meters.count loop
				meter := meters [i]
				meter.set_background_color (Stock_colors.Black)
				meter.set_foreground_color (Color_orange)
				meter.set_font (Meter_font)
				meter.set_minimum_height ((Meter_font.height * 1.2).rounded)

				Result.extend (meter)
				Result.disable_last_item_expand
				i := i + 1
			end
		end

	create_meter_column (heading: STRING; meter_frames: ARRAY [like create_meter_frame]): EV_VERTICAL_BOX
			--
			local
				label: EV_LABEL
			i: INTEGER
		do
			create Result
			Result.set_padding (5)
			create label.make_with_text (heading)
			label.set_font (Heading_font)
			label.align_text_center
			Result.extend (label)
			Result.disable_item_expand (Result.last)

			from i := 1 until i > meter_frames.count loop
				Result.extend (meter_frames [i])
				Result.disable_item_expand (Result.last)
				i := i + 1
			end
		end

	vertical_separator (a_width: INTEGER): EV_VERTICAL_SEPARATOR
			--
		do
			create Result
			Result.set_minimum_width (a_width)
		end

feature {NONE} -- Implementation: routines


	on_server_activity_start
			--
		do
			refresh_count := 0
			reset
			refresh
		end

	on_server_activity_end
			--
		do
			refresh
			set_routine_rate_meter (function_rate_meter, 0)
			set_routine_rate_meter (procedure_rate_meter, 0)
			set_data_rate_meter (data_received_rate_meter, 0)
			set_data_rate_meter (data_sent_rate_meter, 0)
		end

	prompt_refresh
			--
		do
			Precursor
			refresh_count := refresh_count + 1
			if refresh_count \\ 5 = 0 then
				log.enter_no_header ("prompt_refresh")
				log.put_integer_field ("Timer event count", refresh_count)
				log.put_new_line
				log.exit_no_trailer
			end
		end

feature {NONE} -- Meter labels

	thread_count_meter: EV_LABEL

	queued_connection_count_meter: EV_LABEL

	procedure_count_meter: EV_LABEL

	function_count_meter: EV_LABEL

	procedure_rate_meter: EV_LABEL

	function_rate_meter: EV_LABEL

	failure_meter: EV_LABEL

	data_received_meter: EV_LABEL

	data_received_rate_meter: EV_LABEL

	data_sent_meter: EV_LABEL

	data_sent_rate_meter: EV_LABEL

feature {NONE} -- Implementation: attributes

	max_threads: INTEGER

	refresh_count: INTEGER

	thread_count_suffix: STRING

feature {NONE} -- Constants

	Format_data: FORMAT_DOUBLE
			--
		once
			create Result.make (6, 1)
			Result.show_zero
		end

	Format_data_rate: FORMAT_DOUBLE
			--
		once
			create Result.make (5, 2)
			Result.show_zero
		end

	Format_count: FORMAT_INTEGER
			--
		once
			create Result.make (7)
		end

	Format_thread_count, Format_queued_connection_count, Format_failed_connection_count: FORMAT_INTEGER
			--
		once
			create Result.make (3)
		end

	Format_rate: FORMAT_INTEGER
			--
		once
			create Result.make (4)
		end

	Mega_bytes: INTEGER
			--
		once
			Result := 1000 * 1000
		end

	Color_orange: EV_COLOR
			--
		once
			create Result.make_with_8_bit_rgb (250, 161, 29)
		end

	Refresh_interval: INTEGER = 250 -- millisecs

end