Skip to content

Commit

Permalink
Adds a new front end for viewing logs | fixes the manifest log messag…
Browse files Browse the repository at this point in the history
…e too (tgstation#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
:cl:
admin: new log viewer, try it out. (View Round Logs)
/:cl:

Fixes tgstation#75605

---------

Co-authored-by: san7890 <[email protected]>
  • Loading branch information
ZephyrTFA and san7890 authored May 30, 2023
1 parent 9add5a2 commit c6205dd
Show file tree
Hide file tree
Showing 10 changed files with 422 additions and 34 deletions.
17 changes: 17 additions & 0 deletions code/__DEFINES/logging.dm
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/// The number of entries to store per category, don't make this too large or you'll start to see performance issues
#define CONFIG_MAX_CACHED_LOG_ENTRIES 1000

/// The number of *minimum* ticks between each log re-render, making this small will cause performance issues
/// Admins can still manually request a re-render
#define LOG_UPDATE_TIMEOUT 5 SECONDS

//Investigate logging defines
#define INVESTIGATE_ACCESSCHANGES "id_card_changes"
#define INVESTIGATE_ATMOS "atmos"
Expand Down Expand Up @@ -66,6 +73,16 @@
#define LOG_JSON_ENTRIES "entries"
#define LOG_JSON_LOGGING_START "log-start"

// Log entry keys
#define LOG_ENTRY_KEY_TIMESTAMP "ts"
#define LOG_ENTRY_KEY_CATEGORY "cat"
#define LOG_ENTRY_KEY_MESSAGE "msg"
#define LOG_ENTRY_KEY_DATA "data"
#define LOG_ENTRY_KEY_WORLD_STATE "w-state"
#define LOG_ENTRY_KEY_SEMVER_STORE "s-store"
#define LOG_ENTRY_KEY_ID "id"
#define LOG_ENTRY_KEY_SCHEMA_VERSION "s-ver"

// Category for invalid/missing categories
#define LOG_CATEGORY_NOT_FOUND "invalid-category"

Expand Down
5 changes: 4 additions & 1 deletion code/__HELPERS/logging/manifest.dm
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
/// Logging for player manifest (ckey, name, job, special role, roundstart/latejoin)
/proc/log_manifest(ckey, datum/mind/mind, mob/body, latejoin = FALSE)
logger.Log(LOG_CATEGORY_MANIFEST, "manifest entry added", list(
var/message = {"([body.real_name])\[[ckey]\] added to manifest
as a(n) [latejoin ? "LATEJOIN " : ""][mind.assigned_role.title][mind.special_role ? mind.special_role : ""]
with a location of [loc_name(body.loc)]"}
logger.Log(LOG_CATEGORY_MANIFEST, message, list(
"mind" = mind, "body" = body, "latejoin" = latejoin
))
4 changes: 0 additions & 4 deletions code/__HELPERS/type2type.dm
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,3 @@ GLOBAL_LIST_INIT(modulo_angle_to_dir, list(NORTH,NORTHEAST,EAST,SOUTHEAST,SOUTH,
/// for use inside of browse() calls to html assets that might be loaded on a cdn.
/proc/url2htmlloader(url)
return {"<html><head><meta http-equiv="refresh" content="0;URL='[url]'"/></head><body onLoad="parent.location='[url]'"></body></html>"}

/// Formats a larger number to correct textual representation without losing data
/proc/big_number_to_text(number)
return num2text(number, INFINITY)
4 changes: 4 additions & 0 deletions code/controllers/subsystem/lua.dm
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ SUBSYSTEM_DEF(lua)
var/gc_guard

/datum/controller/subsystem/lua/Initialize()
if(!CONFIG_GET(flag/auxtools_enabled))
warning("SSlua requires auxtools to be enabled to run.")
return SS_INIT_NO_NEED

try
// Initialize the auxtools library
AUXTOOLS_CHECK(AUXLUA)
Expand Down
1 change: 1 addition & 0 deletions code/modules/admin/admin_verbs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ GLOBAL_PROTECT(admin_verbs_admin)
/client/proc/toggle_combo_hud, /* toggle display of the combination pizza antag and taco sci/med/eng hud */
/client/proc/toggle_view_range, /*changes how far we can see*/
/client/proc/cmd_admin_law_panel,
/client/proc/log_viewer_new,
)
GLOBAL_LIST_INIT(admin_verbs_ban, list(/client/proc/unban_panel, /client/proc/ban_panel, /client/proc/stickybanpanel))
GLOBAL_PROTECT(admin_verbs_ban)
Expand Down
9 changes: 9 additions & 0 deletions code/modules/logging/log_category.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down
41 changes: 15 additions & 26 deletions code/modules/logging/log_entry.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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()

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(",")]\]"
// 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(",")]}"

#undef MANUAL_JSON_ENTRY

/// Writes the log entry to a file.
/datum/log_entry/proc/write_entry_to_file(file)
Expand Down
92 changes: 90 additions & 2 deletions code/modules/logging/log_holder.dm
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

GLOBAL_REAL(logger, /datum/log_holder)
/**
* Main datum to manage logging actions
Expand All @@ -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)
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion tgui/packages/tgui/components/Section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Component, InfernoNode, RefObject, createRef } from 'inferno';
import { addScrollableNode, removeScrollableNode } from '../events';
import { canRender, classes } from 'common/react';

type SectionProps = BoxProps & {
export type SectionProps = BoxProps & {
className?: string;
title?: InfernoNode;
buttons?: InfernoNode;
Expand Down
Loading

0 comments on commit c6205dd

Please sign in to comment.