Adds a new front end for viewing logs | fixes the manifest log message too (#75617)

## About The Pull Request


![image](https://github.com/tgstation/tgstation/assets/12817816/13d5f3c7-c0cc-4930-8119-e6bde66a1f61)

![image](https://github.com/tgstation/tgstation/assets/12817816/034a17d8-c552-4c3a-8e5f-b210fc4231e5)

## Why It's Good For The Game

I promised I would add it; and while it's not as nice as my previous
iteration it is faster and more streamlined.
## Changelog
🆑
admin: new log viewer, try it out. (View Round Logs)
/🆑

Fixes #75605

---------

Co-authored-by: san7890 <the@san7890.com>
This commit is contained in:
Zephyr
2023-05-29 17:56:15 -07:00
committed by GitHub
co-authored by san7890
parent 9add5a2569
commit c6205dd063
10 changed files with 421 additions and 33 deletions
+9
View File
@@ -23,6 +23,12 @@
/// Whether the readable version of the log message is formatted internally instead of by rustg
var/internal_formatting = TRUE
/// List of log entries for this category
var/list/entries = list()
/// Total number of entries this round so far
var/entry_count = 0
GENERAL_PROTECT_DATUM(/datum/log_category)
/// Backup log category to catch attempts to log to a category that doesn't exist
@@ -42,6 +48,9 @@ GENERAL_PROTECT_DATUM(/datum/log_category)
)
write_entry(entry)
entry_count += 1
if(entry_count <= CONFIG_MAX_CACHED_LOG_ENTRIES)
entries += entry
/// Allows for category specific file splitting. Needs to accept a null entry for the default file.
/// If master_category it will always return the output of master_category.get_output_file(entry)
+14 -25
View File
@@ -81,34 +81,23 @@ GENERAL_PROTECT_DATUM(/datum/log_entry)
#endif
return output
#define MANUAL_JSON_ENTRY(list, key, value) list.Add("\"[key]\":[(!isnull(value)) ? json_encode(value) : "null"]")
/// Converts the log entry to a JSON string.
/datum/log_entry/proc/to_json_text()
// I do not trust byond's json encoder, so we're doing it manually
var/list/json_strings = list()
// I do not trust byond's json encoder, and need to ensure the order doesn't change.
var/list/json_entries = list()
MANUAL_JSON_ENTRY(json_entries, LOG_ENTRY_KEY_TIMESTAMP, timestamp)
MANUAL_JSON_ENTRY(json_entries, LOG_ENTRY_KEY_CATEGORY, category)
MANUAL_JSON_ENTRY(json_entries, LOG_ENTRY_KEY_MESSAGE, message)
MANUAL_JSON_ENTRY(json_entries, LOG_ENTRY_KEY_DATA, data)
MANUAL_JSON_ENTRY(json_entries, LOG_ENTRY_KEY_WORLD_STATE, world.get_world_state_for_logging())
MANUAL_JSON_ENTRY(json_entries, LOG_ENTRY_KEY_SEMVER_STORE, semver_store)
MANUAL_JSON_ENTRY(json_entries, LOG_ENTRY_KEY_ID, id)
MANUAL_JSON_ENTRY(json_entries, LOG_ENTRY_KEY_SCHEMA_VERSION, schema_version)
return "{[json_entries.Join(",")]}"
json_strings += json_encode(timestamp)
json_strings += json_encode(category)
json_strings += json_encode(message)
if(length(data))
json_strings += json_encode(data)
else
json_strings += "null"
json_strings += json_encode(world.get_world_state_for_logging())
if(length(semver_store))
json_strings += json_encode(semver_store)
else
json_strings += "null"
json_strings += "[id]"
json_strings += json_encode(schema_version)
return "\[[json_strings.Join(",")]\]"
#undef MANUAL_JSON_ENTRY
/// Writes the log entry to a file.
/datum/log_entry/proc/write_entry_to_file(file)
+90 -2
View File
@@ -1,4 +1,3 @@
GLOBAL_REAL(logger, /datum/log_holder)
/**
* Main datum to manage logging actions
@@ -22,11 +21,100 @@ GLOBAL_REAL(logger, /datum/log_holder)
/// Whether or not logging as human readable text is enabled
var/human_readable_enabled = FALSE
/// Cached ui_data
var/list/data_cache = list()
/// Last time the ui_data was updated
var/last_data_update = 0
var/initialized = FALSE
var/shutdown = FALSE
GENERAL_PROTECT_DATUM(/datum/log_holder)
/client/proc/log_viewer_new()
set name = "View Round Logs"
set category = "Admin"
logger.ui_interact(mob)
/datum/log_holder/ui_interact(mob/user, datum/tgui/ui)
if(!check_rights_for(user.client, R_ADMIN))
return
ui = SStgui.try_update_ui(user, src, ui)
if(isnull(ui))
ui = new(user, src, "LogViewer")
ui.set_autoupdate(FALSE)
ui.open()
/datum/log_holder/ui_state(mob/user)
return GLOB.admin_state
/datum/log_holder/ui_static_data(mob/user)
var/list/data = list(
"round_id" = GLOB.round_id,
"logging_start_timestamp" = logging_start_timestamp,
)
var/list/tree = list()
data["tree"] = tree
var/list/enabled_categories = list()
for(var/enabled in log_categories)
enabled_categories += enabled
tree["enabled"] = enabled_categories
var/list/disabled_categories = list()
for(var/disabled in src.disabled_categories)
disabled_categories += disabled
tree["disabled"] = disabled_categories
return data
/datum/log_holder/ui_data(mob/user)
if(!last_data_update || (world.time - last_data_update) > LOG_UPDATE_TIMEOUT)
cache_ui_data()
return data_cache
/datum/log_holder/proc/cache_ui_data()
var/list/category_map = list()
for(var/datum/log_category/category as anything in log_categories)
category = log_categories[category]
var/list/category_data = list()
var/list/entries = list()
for(var/datum/log_entry/entry as anything in category.entries)
entries += list(list(
"id" = entry.id,
"message" = entry.message,
"timestamp" = entry.timestamp,
"data" = entry.data,
"semver" = entry.semver_store,
))
category_data["entries"] = entries
category_data["entry_count"] = category.entry_count
category_map[category.category] = category_data
data_cache.Cut()
last_data_update = world.time
data_cache["categories"] = category_map
data_cache["last_data_update"] = last_data_update
/datum/log_holder/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
switch(action)
if("re-render")
cache_ui_data()
SStgui.update_uis(src)
return TRUE
else
stack_trace("unknown ui_act action [action] for [type]")
/// Assembles basic information for logging, creating the log category datums and checking for config flags as required
/datum/log_holder/proc/init_logging()
if(initialized)
@@ -145,7 +233,7 @@ GENERAL_PROTECT_DATUM(/datum/log_holder)
var/list/category_header = list(
LOG_HEADER_INIT_TIMESTAMP = logging_start_timestamp,
LOG_HEADER_ROUND_ID = big_number_to_text(GLOB.round_id),
LOG_HEADER_ROUND_ID = GLOB.round_id,
LOG_HEADER_SECRET = category_instance.secret,
LOG_HEADER_CATEGORY_LIST = contained_categories,
LOG_HEADER_CATEGORY = category_instance.category,