ftech
/Logger
/amendments
/6
v1.0 - Implemented console + refactored code to feature camel case syntax.
Francois Techene made amendment 6 5 days ago
--- logger-test.c Tue Apr 8 12:16:18 2025
+++ logger-test.c Sun Mar 29 16:42:19 2026
@@ -20,13 +20,64 @@
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include "logger.h"
+#define APP_NAME "AppTemplate"
+
+#define VERS_RES_ID 1
+
+#define ABOUT_DIALOG_ID 130
+#define ABOUT_OK_BTN_ID 1
+
+#define MBAR_ID 128
+
+#define APPLE_MENU_ID 128
+#define APPLE_MENU_ABOUT_ID 1
+
+#define FILE_MENU_ID 129
+#define FILE_MENU_NEW_ID 1
+#define FILE_MENU_QUIT_ID 2
+
+void drawHeader(void);
+void testLogLevels(void);
+void runTests(void);
+
+void loadMenuBar(void);
+void setMenuDefaults(void);
+short onMenuClick(long menu_id);
+void doEvent(EventRecord event);
+
+void centerWindow(WindowPtr win);
+
+Rect drawBtn(int h, int v, Str255 str);
+void drawWindow(void);
+void showAbout(void);
+
+WindowPtr mainWindow;
+DialogPtr dialog;
+
+MenuHandle appleMenu;
+MenuHandle fileMenu;
+
+Rect testBtn;
+Rect printBtn;
+Rect logBtn;
+Rect errBtn;
+Rect warnBtn;
+Rect infoBtn;
+Rect debugBtn;
+
+Boolean isReady = false;
+Boolean quitting = false;
+Boolean isFirstUpdate = true;
+
+
void
-draw_header(void)
+drawHeader(void)
{
- logger.log(" \n\n");
+ logger.log(" \r\r");
logger.log(" ____________________ ");
logger.log(" | __________________ | ");
logger.log(" | | | | ");
@@ -48,96 +99,447 @@ draw_header(void)
logger.log("/ /__| (_) | (_| | (_| | __/ | ");
logger.log("\\____/\\___/ \\__, |\\__, |\\___|_| ");
logger.log(" |___/ |___/ ");
- logger.log("\n");
+ logger.log("\r");
}
void
-test_log_levels(void)
+testLogLevels(void)
{
- logger.log_level = NONE_LEVEL;
+ logger.logLevel = NONE_LEVEL;
- logger.log("");
- logger.log("#### Testing NONE_LEVEL: Must not show any log:");
+ logger.print("\r#### Testing NONE_LEVEL: Must not show any log:\r");
logger.debug("This debug log should not be visible");
logger.info("This info log should not be visible");
logger.warn("This warning log should not be visible");
logger.error("This error log should not be visible");
- logger.log_level = ERROR_LEVEL;
+ logger.logLevel = ERROR_LEVEL;
- logger.log("");
- logger.log("#### Testing ERROR_LEVEL:");
+ logger.print("\r#### Testing ERROR_LEVEL:\r");
logger.debug("This debug log should not be visible !!!");
logger.info("This info log should not be visible !!!");
logger.warn("This warning log should not be visible !!!");
logger.error("This error log should be visible");
- logger.log_level = WARNING_LEVEL;
+ logger.logLevel = WARNING_LEVEL;
- logger.log("");
- logger.log("#### Testing WARNING_LEVEL:");
+ logger.print("\r#### Testing WARNING_LEVEL:\r");
logger.debug("This debug log should not be visible !!!");
logger.info("This info log should not be visible !!!");
logger.warn("This warning log should be visible");
logger.error("This error log should be visible");
- logger.log_level = INFO_LEVEL;
+ logger.logLevel = INFO_LEVEL;
- logger.log("");
- logger.log("#### Testing INFO_LEVEL:");
+ logger.print("\r#### Testing INFO_LEVEL:\r");
logger.debug("This debug log should not be visible !!!");
logger.info("This info log should be visible");
logger.warn("This warning log should be visible");
logger.error("This error log should be visible");
- logger.log_level = DEBUG_LEVEL;
- logger.log("");
- logger.log("#### Testing DEBUG_LEVEL:");
+ logger.logLevel = DEBUG_LEVEL;
+
+ logger.print("\r#### Testing DEBUG_LEVEL:\r");
logger.debug("This is a debug message with number %d ", 1);
logger.info("This is an info message with number %d ", 2);
logger.warn("This is a warning with number %d ", 3);
logger.error("This is an error with number %d ", 4);
}
-int
-main(void)
+
+void
+runTests(void)
{
- init_logger(DEBUG_LEVEL, CONSOLE_OUT, NULL);
- draw_header();
+#ifdef USE_LOGGER // Fail safe
+ LogConsoleScrollToBottom();
+#endif
+
+ drawHeader();
+ testLogLevels();
+ logger.log("\rDone!");
+}
+
+
+int main(void)
+{
+ EventRecord event;
- init_logger(DEBUG_LEVEL, NONE_OUT, NULL);
+ short dlgItem;
- logger.debug("This debug log should not be visible !!!");
- logger.info("This info log should not be visible !!!");
- logger.warn("This warning log should not be visible !!!");
- logger.error("This error log should not be visible !!!");
+ short eventMask = mDownMask +
+ mUpMask +
+ keyDownMask +
+ keyUpMask +
+ autoKeyMask +
+ updateMask;
+ SetEventMask(everyEvent); // So we can get keyUp event.
+ FlushEvents(eventMask, 0);
+ InitGraf(&thePort);
+ InitFonts();
+ InitWindows();
+ InitMenus();
+ TEInit();
+ InitDialogs(0);
+ InitCursor();
+ MaxApplZone();
+ // Loa menus
+ //
+ loadMenuBar();
- // You can use a file in a shared folder on the network
- // and use "tail -f" on the remote machine the see the logs
- // Comment the next init_log if you want to use this one.
- //
- init_logger(DEBUG_LEVEL, FILE_OUT, "Shared:Mac Dev:Logger:test.log");
-
- // This path is relative to the project's folder
- //
- init_logger(DEBUG_LEVEL, FILE_OUT, "test.log");
-
-
- printf("Logging to file: %s ...\n", logger.file_path);
-
- draw_header();
- test_log_levels();
+ // Init Window
+ //
+ mainWindow = GetNewWindow(128, 0L, (WindowPtr)-1L);
+ drawWindow();
+ dialog = NULL;
- printf("Done! Check the logs in the log file.");
+ isReady = true;
- return 1;
+ InitLogger(DEBUG_LEVEL, CONSOLE_OUT, NULL);
+
+ // Handle events
+ //
+ while (!quitting) {
+ if (GetNextEvent(eventMask, &event)) {
+
+ if (dialog && IsDialogEvent(&event)) {
+ DialogSelect(&event, &dialog, &dlgItem);
+ }
+ else {
+ doEvent(event);
+ }
+ }
+ }
+
+
+ return EXIT_SUCCESS;
+}
+
+/////////////////////////////////////////////////////
+// Windows
+//
+
+void
+centerWindow(WindowPtr win)
+{
+ Rect screenRect;
+ Rect winRect;
+ int width, height, scrWidth, scrHeight;
+ int left, top;
+
+ screenRect = screenBits.bounds;
+ winRect = win->portRect;
+
+ scrWidth = screenRect.right - screenRect.left;
+ scrHeight = screenRect.bottom - screenRect.top;
+
+ width = winRect.right - winRect.left;
+ height = winRect.bottom - winRect.top;
+
+ left = (scrWidth / 2) - (width / 2);
+ top = (scrHeight / 2) - (height / 2);
+
+ MoveWindow(win, left, top, FALSE);
+}
+
+Rect
+drawBtn(int h, int v, Str255 str)
+{
+ Rect r = { 0, 0, 15, 100 }; // top, left, bottom, right
+
+ TextFont(monaco);
+ TextFace(0);
+ TextSize(9);
+
+ OffsetRect(&r, h - 1, v - 1);
+ FrameRect(&r);
+
+ OffsetRect(&r, -1, -1);
+ FillRect(&r, white);
+ FrameRect(&r);
+
+ MoveTo(r.left + 4, r.bottom - 4);
+
+ DrawString(str);
+
+ return r;
+}
+
+void
+drawWindow(void)
+{
+ SetPort(mainWindow);
+ SetPortBits(&mainWindow->portBits);
+
+ TextFont(helvetica);
+ TextFace(bold);
+ TextSize(18);
+
+ MoveTo(20, 25);
+ DrawString("\pLogger test app");
+
+ TextFont(geneva);
+ TextFace(0);
+ TextSize(9);
+
+ MoveTo(20, 50);
+ DrawString("\pThis is a simple app to test the Logger tool \
+to help debug your Macintosh application.");
+
+ MoveTo(20, 65);
+ DrawString("\pThe Logger console should be visible at the bottom of the screen.");
+
+ MoveTo(20, 90);
+ DrawString("\pThe following buttons highlight the different logging \
+functions of the logger.");
+
+ printBtn = drawBtn(20, 110, "\plogger.print()");
+ logBtn = drawBtn(20, printBtn.bottom + 10, "\plogger.log()");
+ errBtn = drawBtn(20, logBtn.bottom + 10, "\plogger.error()");
+ warnBtn = drawBtn(20, errBtn.bottom + 10, "\plogger.warn()");
+ infoBtn = drawBtn(20, warnBtn.bottom + 10, "\plogger.info()");
+ debugBtn = drawBtn(20, infoBtn.bottom + 10, "\plogger.debug()");
+
+ testBtn = drawBtn(20, debugBtn.bottom + 25, "\pRun tests");
+}
+
+void
+showAbout(void)
+{
+ Handle versionRes;
+ Str255 versionStr = "\p";
+ char appName[32];
+ short item;
+ unsigned char* p;
+
+ // Get version details from the version resource
+ //
+ versionRes = GetResource('vers', VERS_RES_ID);
+ if (versionRes) {
+ HLock(versionRes);
+ p = (unsigned char*)*versionRes;
+
+ p += 6; // skip fixed header (6 bytes)
+ p += 1 + p[0]; // skip short version string
+
+ // Get the long verstion string
+ //
+ BlockMove(p, versionStr, p[0] + 1);
+
+ HUnlock(versionRes);
+ }
+
+ strcpy(appName, APP_NAME);
+ appName[strlen(appName)] = '\0';
+
+
+ ParamText(CtoPstr(appName), versionStr, "\p", "\p");
+
+ // Load the About dialog
+ //
+ dialog = GetNewDialog(ABOUT_DIALOG_ID, NULL, (WindowPtr)-1);
+
+ if (!dialog) {
+ return;
+ }
+
+ centerWindow(dialog);
+
+ ShowWindow(dialog);
+
+ do {
+ ModalDialog(NULL, &item);
+ } while (item != ABOUT_OK_BTN_ID);
+
+ DisposeDialog(dialog);
+ dialog = NULL;
+}
+
+/////////////////////////////////////////////////////
+// Menu Bar
+//
+
+void
+loadMenuBar()
+{
+ Handle mbar = GetNewMBar(MBAR_ID);
+ SetMenuBar(mbar);
+
+ appleMenu = GetMHandle(APPLE_MENU_ID);
+ AddResMenu(appleMenu, 'DRVR');
+
+ fileMenu = GetMHandle(FILE_MENU_ID);
+
+ setMenuDefaults();
+ DrawMenuBar();
+}
+
+void
+setMenuDefaults()
+{
+ DisableItem(fileMenu, FILE_MENU_NEW_ID);
+}
+
+short
+onMenuClick(long menu_id)
+{
+ short menu, item;
+ short ret = true;
+
+ menu = HiWord(menu_id);
+ item = LoWord(menu_id);
+
+ if (isReady == 0) {
+ return false;
+ }
+
+ switch (menu) {
+ case APPLE_MENU_ID:
+ switch (item) {
+ case APPLE_MENU_ABOUT_ID: {
+ showAbout();
+ break;
+ }
+ default: {
+ Str255 da;
+ GrafPtr savePort;
+
+ GetItem(appleMenu, item, da);
+ GetPort(&savePort);
+ OpenDeskAcc(da);
+ SetPort(savePort);
+ break;
+ }
+ }
+ break;
+ case FILE_MENU_ID:
+ switch(item) {
+ case FILE_MENU_NEW_ID:
+ break;
+
+ case FILE_MENU_QUIT_ID:
+ quitting = true;
+ break;
+ }
+ break;
+ default:
+ ret = false;
+ }
+
+ HiliteMenu(0);
+ return ret;
+}
+
+
+/////////////////////////////////////////////////////
+// Events
+//
+
+void
+doEvent(EventRecord event)
+{
+ WindowPtr eventWin;
+ short eventIn;
+ char key;
+ Point localPt;
+
+ if(logger.onEvent(event)) {
+ return;
+ }
+
+ localPt = event.where;
+ GlobalToLocal(&localPt);
+
+ switch (event.what) {
+
+ case nullEvent:
+ break;
+
+ case keyDown:
+ //case autoKey:
+ key = (char)(event.message & charCodeMask);
+ if ((event.modifiers & cmdKey) != 0) {
+ if (onMenuClick(MenuKey(key)))
+ break;
+ }
+ break;
+
+ case keyUp:
+ key = (char)(event.message & charCodeMask);
+ break;
+
+ case mouseDown:
+ eventIn = FindWindow(event.where, &eventWin);
+
+ switch (eventIn) {
+ case inMenuBar:
+ onMenuClick(MenuSelect(event.where));
+ break;
+ case inSysWindow:
+ SystemClick(&event, eventWin);
+ break;
+ case inContent:
+ if (PtInRect(localPt, &testBtn)) {
+ runTests();
+ }
+ else if (PtInRect(localPt, &printBtn)) {
+ logger.print("Printing some text. It doesn't end with a new line. ");
+ }
+ else if (PtInRect(localPt, &logBtn)) {
+ logger.log("This is a simple log");
+ }
+ else if (PtInRect(localPt, &errBtn)) {
+ logger.error("This is an error log");
+ }
+ else if (PtInRect(localPt, &warnBtn)) {
+ logger.warn("This is a warning log");
+ }
+ else if (PtInRect(localPt, &infoBtn)) {
+ logger.info("This is an info log");
+ }
+ else if (PtInRect(localPt, &debugBtn)) {
+ logger.debug("This is a debug log");
+ }
+ break;
+ case inDrag:
+ DragWindow(eventWin, event.where, &screenBits.bounds);
+ break;
+ case inGoAway:
+ if (TrackGoAway(eventWin, event.where)) {
+ quitting = true;
+ }
+ break;
+ }
+ break;
+
+ case updateEvt:
+
+ eventWin = (WindowPtr)event.message;
+ if (eventWin) {
+ BeginUpdate(eventWin);
+
+ // We don't redraw on the first update
+ //
+ if(!isFirstUpdate) {
+ drawWindow();
+ }
+ else {
+ isFirstUpdate = false;
+ }
+
+ EndUpdate(eventWin);
+ }
+ break;
+
+ case activateEvt:
+ break;
+ }
}
--- logger.c Tue Apr 8 14:38:34 2025
+++ logger.c Sun Mar 29 17:15:15 2026
@@ -16,74 +16,659 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ * ----------------------------------------------------------------
+ * Utility library to log to a file or to the console.
+ *
+ * Before using the logger, you must initialize it by calling:
+ *
+ * initLogger(DEBUG_LEVEL, LOG_METHOD, LOG_FILE);
+ *
+ * Where those variables are defined in logger.h.
+ *
+ * You may put all logging code inbetween the USE_LOGGER condition
+ * so that loggin logic can be easily fully disabled.
+ *
+ * #ifdef USE_LOGGER
+ * initLogger(DEBUG_LEVEL, LOG_METHOD, LOG_FILE);
+ * #endif
+ *
+ * #ifdef USE_LOGGER
+ * logger.debug("message");
+ * #endif
+ *
+ * If you are logging to the console, you need to forward the
+ * application's events to the logger and ignore the event if it
+ * has been handled by the console:
+ *
+ * if(logger.onEvent(event)) {
+ * return;
+ * }
+ * ----------------------------------------------------------------
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
+#include <string.h>
#include "logger.h"
+
+void LogToNone(char *format, ...);
+Boolean DoNotIntercept(EventRecord event);
+
+#ifdef USE_LOGGER
+
+typedef struct _LogTextState {
+ int font;
+ int size;
+ int mode;
+ int face;
+
+} LogTextState;
+
+#define CONSOLE_BAR_SIZE 12
+#define CONSOLE_TEXT_FONT monaco
+#define CONSOLE_TEXT_SIZE 9
+#define CONSOLE_MIN_HEIGHT 67
+#define CONSOLE_MIN_WIDTH 150
+
+LogTextState logSavedTextState;
+PenState logSavedPentState;
+
+void UseLogger(LogLevel level, LogOutputMethod out, char *filePath);
+void LogConsoleInit(void);
+void LogConsoleDraw(void);
+void LogConsoleUpdate(void);
+void LogConsoleInitScrollbar(void);
+void LogConsoleResetTE(void);
+void LogConsoleResetScroll(void);
+void LogConsoleScrollToBottom(void);
+void LogConsoleSetLayout();
+void LogConsoleResize(Rect* newBounds);
+void LogConsoleZoom(void);
+void LogConsoleClear(void);
+void LogToConsole(char *format, ...);
+void LogToFile(char *format, ...);
+void LogDefault(char *format, ...);
+void LogDebug(char *format, ...);
+void LogInfo(char *format, ...);
+void LogWarn(char *format, ...);
+void LogError(char *format, ...);
+int DoLogGrow(Point p);
+pascal void DoLogScroll(ControlHandle ctl, short ctlEventIn);
+Boolean DoLogEvent(EventRecord event);
+
+void LogSaveTextState(void);
+void LogRestoreTextState(void);
+
+#endif
+
Logger logger;
void
-init_logger(LogLevel level,
- LogOutputMethod out,
- char* file_path)
+LogToNone(char* format, ...)
{
- logger.debug = log_debug;
- logger.info = log_info;
- logger.warn = log_warn;
- logger.error = log_error;
+ (void) format; // unused.
+ return;
+}
- logger.log_level = level;
- logger.output_method = out;
- logger.file_path = file_path;
+Boolean
+DoNotIntercept(EventRecord event)
+{
+ return false;
+}
+
+void
+InitLogger(LogLevel level,
+ LogOutputMethod out,
+ char* filePath)
+{
+ logger.print = LogToNone;
+ logger.debug = LogToNone;
+ logger.info = LogToNone;
+ logger.warn = LogToNone;
+ logger.error = LogToNone;
+ logger.onEvent = DoNotIntercept;
+
+
+#ifdef USE_LOGGER
+ UseLogger(level,out, filePath);
+#endif
+}
+
+#ifdef USE_LOGGER // Only load if required.
+
+void
+UseLogger(LogLevel level,
+ LogOutputMethod out,
+ char* filePath)
+{
+ logger.log = LogDefault;
+ logger.debug = LogDebug;
+ logger.info = LogInfo;
+ logger.warn = LogWarn;
+ logger.error = LogError;
+
+ logger.logLevel = level;
+ logger.outputMethod = out;
+ logger.filePath = filePath;
+
+ logger.console.textArea = NULL;
+
if (out == CONSOLE_OUT) {
- logger.log = log_to_console;
+ logger.onEvent = DoLogEvent;
+ LogConsoleInit();
}
else if (out == FILE_OUT) {
FILE* file;
- logger.log = log_to_file;
+ // Using Unix New Line character for tail to
+ // read correctly on Linux.
+ //
+ logger.newLine = "\x0A\r";
+
+ logger.print = LogToFile;
// Empty log file. If the file cannot be created, the log output
// is set to NONE_OUT;
//
- file = fopen(logger.file_path, "w");
+ file = fopen(logger.filePath, "w");
if(!file) {
- logger.output_method = NONE_OUT;
- logger.log = log_to_none;
+ logger.outputMethod = NONE_OUT;
+ logger.print = LogToNone;
return;
}
fprintf(file, "");
fclose(file);
}
else {
- logger.log = log_to_none;
+ logger.print = LogToNone;
}
}
+
+void
+LogConsoleInit(void)
+{
+ GrafPtr oldPort;
+ Rect winBounds;
+ Rect viewRect;
+ Rect textRect;
+
+ logger.console.lineHeight = CONSOLE_TEXT_SIZE + 2;
+ logger.console.width = screenBits.bounds.right - 2;
+ logger.console.scrollPos = 0;
+ logger.console.maxScrollPos = 0;
+ logger.console.nbLines = 0;
+ logger.console.contentHeight = 0;
+ logger.console.nbOutputLines = CONSOLE_LINES;
+ logger.console.height = (logger.console.lineHeight * CONSOLE_LINES)
+ + (CONSOLE_BAR_SIZE * 2) - 1;
+
+
+ // Make prevBounds to the zoomed in position for zoom toggle
+ //
+ logger.console.growBounds.left = screenBits.bounds.left + 5;
+ logger.console.growBounds.right = screenBits.bounds.right - 5;
+ logger.console.growBounds.top = screenBits.bounds.top + 25;
+ logger.console.growBounds.bottom = screenBits.bounds.bottom - 5;
+
+ logger.console.prevBounds = logger.console.growBounds;
+
+ logger.newLine = "\r";
+ logger.print = LogToConsole;
+
+ winBounds.left = screenBits.bounds.left + 1;
+ winBounds.right = screenBits.bounds.right - 1;
+ winBounds.bottom = screenBits.bounds.bottom - 1;
+ winBounds.top = winBounds.bottom - logger.console.height;
+
+
+ logger.console.window = NewWindow(0L, &winBounds, "\pConsole", TRUE,
+ plainDBox, (WindowPtr)-1L, TRUE, 0);
+
+ if (!logger.console.window) {
+ logger.print = LogToNone;
+ return;
+ }
+
+ logger.console.origBounds = winBounds;
+
+ GetPort(&oldPort);
+ SetPort(logger.console.window);
+
+ SendBehind(logger.console.window, NULL);
+
+ LogConsoleSetLayout();
+
+
+ // Tex Edit
+ //
+ viewRect = logger.console.view;
+ //InsetRect(&viewRect, 2, 2);
+
+ textRect = viewRect;
+ InsetRect(&textRect, 2, 0);
+
+ logger.console.textArea = TENew(&textRect, &viewRect);
+
+ (*(logger.console.textArea))->txFont = CONSOLE_TEXT_FONT;
+ (*(logger.console.textArea))->txSize = CONSOLE_TEXT_SIZE;
+ (*(logger.console.textArea))->lineHeight = logger.console.lineHeight;
+ (*(logger.console.textArea))->fontAscent = logger.console.lineHeight - 2;
+ (*(logger.console.textArea))->txFace = 0;
+
+ //TEActivate(logger.console.textArea);
+
+ LogConsoleInitScrollbar();
+
+ LogConsoleDraw();
+
+ SetPort(oldPort);
+}
+
+void LogConsoleDraw(void)
+{
+ Rect r;
+ Pattern tbarPattern;
+ GrafPtr oldPort;
+
+ GetPort(&oldPort);
+ SetPort(logger.console.window);
+
+ LogSaveTextState();
+ GetPenState(&logSavedPentState);
+ PenNormal();
+
+ EraseRect(&logger.console.view);
+
+ GetIndPattern(&tbarPattern, sysPatListID, 24);
+ FillRect(&logger.console.titleBar, tbarPattern);
+ FrameRect(&logger.console.titleBar);
+
+ FillRect(&logger.console.bottomBar, white);
+ FrameRect(&logger.console.bottomBar);
+
+ FillRect(&logger.console.growBtn, white);
+ FrameRect(&logger.console.growBtn);
+ r = logger.console.growBtn;
+ InsetRect(&r, 5, 3);
+ OffsetRect(&r, 1, 1);
+ FrameRect(&r);
+
+ r.right -= 1;
+ r.bottom -= 1;
+ OffsetRect(&r, -2, -2);
+ FillRect(&r, white);
+ FrameRect(&r);
+
+ // Lower button
+ //
+ FillRect(&logger.console.lowerBtn, white);
+ FrameRect(&logger.console.lowerBtn);
+ MoveTo(logger.console.lowerBtn.left + 4,
+ logger.console.lowerBtn.bottom - 2);
+
+ TextFont(geneva);
+ TextFace(0);
+ TextSize(12);
+
+ DrawString("\p_");
+
+
+ // Clear button
+ //
+ FillRect(&logger.console.clearBtn, white);
+ FrameRect(&logger.console.clearBtn);
+ MoveTo(logger.console.clearBtn.left + 4,
+ logger.console.clearBtn.bottom - 2);
+
+ TextFont(geneva);
+ TextFace(0);
+ TextSize(8);
+
+ DrawString("\pclear");
+
+ // Close button
+ //
+ FillRect(&logger.console.closeBtn, white);
+ FrameRect(&logger.console.closeBtn);
+
+ // Expand button
+ //
+ FillRect(&logger.console.zoomBtn, white);
+ FrameRect(&logger.console.zoomBtn);
+
+
+ LogConsoleUpdate();
+
+
+ LogRestoreTextState();
+ SetPenState(&logSavedPentState);
+
+ SetPort(oldPort);
+}
+
+void LogConsoleUpdate(void)
+{
+ TEUpdate(&logger.console.view, logger.console.textArea);
+ DrawControls(logger.console.window);
+}
+
void
-log_to_none(char* format, ...)
+LogConsoleInitScrollbar(void)
{
- return;
+ GrafPtr oldPort;
+ Rect scrollbarRect;
+ int value;
+
+ GetPort(&oldPort);
+ SetPort(logger.console.window);
+
+ scrollbarRect.left = logger.console.window->portRect.right - 15;
+ scrollbarRect.right = logger.console.window->portRect.right + 1;
+ scrollbarRect.top = logger.console.titleBar.bottom - 1;
+ scrollbarRect.bottom = logger.console.bottomBar.top + 1;
+
+ value = logger.console.contentHeight - logger.console.height;
+
+ logger.console.vScroll = NewControl(logger.console.window,
+ &scrollbarRect,
+ "\p",
+ TRUE,
+ 0, // intinial value;
+ 0, // min
+ value, // max
+ scrollBarProc,
+ 0);
+
+ SetCtlMax(logger.console.vScroll,
+ logger.console.contentHeight - logger.console.height);
+
+ SetCtlValue(logger.console.vScroll, logger.console.scrollPos);
+
+ HiliteControl(logger.console.vScroll, 255);
+
+ SetPort(oldPort);
}
void
-log_to_console(char* format, ...)
+LogConsoleResetTE(void)
{
+ Rect viewRect = logger.console.view;
+
+ //InsetRect(&viewRect, 2, 2);
+
+ // Resize the size of the text area
+ //
+ (*(logger.console.textArea))->viewRect = viewRect;
+ (*(logger.console.textArea))->destRect.right = viewRect.right - 1;
+}
+
+void
+LogConsoleResetScroll(void)
+{
+ int oldMaxScrollPos = logger.console.maxScrollPos;
+
+ logger.console.maxScrollPos = (logger.console.nbLines
+ - logger.console.nbOutputLines)
+ * logger.console.lineHeight;
+
+ if (logger.console.maxScrollPos <= 0) {
+
+ // If there is nothing to scroll,
+ // put everything back at the top
+ //
+ TEScroll(0, logger.console.scrollPos, logger.console.textArea);
+
+ logger.console.scrollPos = 0;
+ logger.console.maxScrollPos = 0;
+ }
+ else if (logger.console.scrollPos > logger.console.maxScrollPos) {
+
+ // Window is growing
+ //
+ if (logger.console.maxScrollPos < oldMaxScrollPos) {
+
+ int delta = logger.console.scrollPos - logger.console.maxScrollPos;
+ TEScroll(0, delta, logger.console.textArea);
+ }
+
+ logger.console.scrollPos = logger.console.maxScrollPos;
+ }
+ else {
+ HiliteControl(logger.console.vScroll, 0);
+ }
+
+ MoveControl(logger.console.vScroll,
+ logger.console.bounds.right - 15,
+ logger.console.titleBar.bottom - 1);
+
+ SizeControl(logger.console.vScroll,
+ 16,
+ logger.console.bottomBar.top
+ - logger.console.titleBar.bottom + 2);
+
+ SetCtlMax(logger.console.vScroll, logger.console.maxScrollPos);
+ SetCtlValue(logger.console.vScroll, logger.console.scrollPos);
+}
+
+void
+LogConsoleScrollToBottom(void)
+{
+ int delta = logger.console.maxScrollPos - logger.console.scrollPos;
+
+ logger.console.scrollPos = logger.console.maxScrollPos;
+ SetCtlValue(logger.console.vScroll, logger.console.scrollPos);
+
+ TEScroll(0, -delta, logger.console.textArea);
+
+ LogConsoleUpdate();
+}
+
+void
+LogConsoleSetLayout(void)
+{
+ logger.console.bounds = logger.console.window->portRect;
+
+ logger.console.view = logger.console.bounds;
+ logger.console.view.right -= 15;
+ logger.console.view.top += CONSOLE_BAR_SIZE;
+ logger.console.view.bottom -= CONSOLE_BAR_SIZE - 1;
+
+ // Title bar & buttons
+ //
+ logger.console.titleBar.left = logger.console.bounds.left - 1;
+ logger.console.titleBar.right = logger.console.bounds.right + 1;
+ logger.console.titleBar.top = logger.console.bounds.top - 1;
+ logger.console.titleBar.bottom = logger.console.titleBar.top
+ + CONSOLE_BAR_SIZE + 1;
+
+ logger.console.bottomBar.left = logger.console.bounds.left - 1;
+ logger.console.bottomBar.right = logger.console.bounds.right + 1;
+ logger.console.bottomBar.top = logger.console.bounds.bottom
+ - CONSOLE_BAR_SIZE +1;
+ logger.console.bottomBar.bottom = logger.console.bounds.bottom + 1;
+
+
+ logger.console.zoomBtn.right = logger.console.bounds.right - 5;
+ logger.console.zoomBtn.left = logger.console.zoomBtn.right - 9;
+ logger.console.zoomBtn.top = logger.console.bounds.top + 1;
+ logger.console.zoomBtn.bottom = logger.console.zoomBtn.top + 9;
+
+ logger.console.growBtn.right = logger.console.bounds.right + 1;
+ logger.console.growBtn.left = logger.console.growBtn.right - 16;
+ logger.console.growBtn.bottom = logger.console.bounds.bottom + 1;
+ logger.console.growBtn.top = logger.console.growBtn.bottom
+ - CONSOLE_BAR_SIZE;
+
+
+ logger.console.lowerBtn.right = logger.console.zoomBtn.left - 10;
+ logger.console.lowerBtn.left = logger.console.lowerBtn.right - 16;
+ logger.console.lowerBtn.top = logger.console.bounds.top + 1;
+ logger.console.lowerBtn.bottom = logger.console.lowerBtn.top + 9;
+
+ logger.console.clearBtn.right = logger.console.lowerBtn.left -4;
+ logger.console.clearBtn.left = logger.console.clearBtn.right - 26;
+ logger.console.clearBtn.top = logger.console.bounds.top + 1;
+ logger.console.clearBtn.bottom = logger.console.clearBtn.top + 9;
+
+ logger.console.closeBtn.left = logger.console.bounds.left + 5;
+ logger.console.closeBtn.right = logger.console.closeBtn.left + 9;
+ logger.console.closeBtn.top = logger.console.bounds.top + 1;
+ logger.console.closeBtn.bottom = logger.console.closeBtn.top + 9;
+}
+
+void
+LogConsoleResize(Rect* newBounds)
+{
+ int width = newBounds->right - newBounds->left;
+ int height = newBounds->bottom - newBounds->top;
+
+ int nbLines = (int)((height - (CONSOLE_BAR_SIZE * 2) + 1)
+ / logger.console.lineHeight);
+
+ // Adjust the hieight of the window to match the number of
+ // lines and line height of the TE
+ //
+ logger.console.nbOutputLines = nbLines;
+ logger.console.width = width;
+ logger.console.height = (logger.console.lineHeight * nbLines)
+ + (CONSOLE_BAR_SIZE * 2) - 1;
+
+ newBounds->bottom = newBounds->top + logger.console.height;
+
+ MoveWindow(logger.console.window,
+ newBounds->left,
+ newBounds->top,
+ TRUE);
+
+ SizeWindow(logger.console.window,
+ logger.console.width,
+ logger.console.height,
+ FALSE);
+
+ EraseRect(newBounds);
+ LogConsoleSetLayout();
+ LogConsoleResetTE();
+ LogConsoleResetScroll();
+ LogConsoleDraw();
+
+ //LogConsoleScrollToBottom();
+}
+
+void
+LogConsoleZoom(void)
+{
+ Point topLeft;
+ Point bottomRight;
+
+ topLeft.h = logger.console.window->portRect.left;
+ topLeft.v = logger.console.window->portRect.top;
+
+ bottomRight.h = logger.console.window->portRect.right;
+ bottomRight.v = logger.console.window->portRect.bottom;
+
+ LocalToGlobal(&topLeft);
+ LocalToGlobal(&bottomRight);
+
+ LogConsoleResize(&logger.console.prevBounds);
+
+ logger.console.prevBounds.left = topLeft.h;
+ logger.console.prevBounds.top = topLeft.v;
+ logger.console.prevBounds.right = bottomRight.h;
+ logger.console.prevBounds.bottom = bottomRight.v;
+}
+
+void
+LogConsoleClear(void)
+{
+ GrafPtr oldPort;
+ Rect scrollbarRect;
+ int value;
+
+ GetPort(&oldPort);
+ SetPort(logger.console.window);
+
+ // Move back to the top of the text area
+ //
+ TEScroll(0, logger.console.scrollPos, logger.console.textArea);
+
+ // Select all and erase
+ //
+ TESetSelect(0,32767,logger.console.textArea);
+ TEDelete(logger.console.textArea);
+
+ logger.console.scrollPos = 0;
+ logger.console.maxScrollPos = 0;
+ logger.console.nbLines = 0;
+ logger.console.contentHeight = 0;
+
+ SetCtlMax(logger.console.vScroll, 0);
+ SetCtlValue(logger.console.vScroll, 0);
+
+ HiliteControl(logger.console.vScroll, 255);
+
+ LogConsoleUpdate();
+
+ SetPort(oldPort);
+}
+
+void
+LogToConsole(char* format, ...)
+{
va_list ap;
+ char log[256] = "";
+ int oldMaxScroll = logger.console.maxScrollPos;
+ GrafPtr oldPort;
+
+ GetPort(&oldPort);
+ SetPort(logger.console.window);
+
va_start(ap, format);
- vprintf(format, ap);
+ vsprintf(log, format, ap);
va_end(ap);
- printf("\n");
+
+ TEInsert(log, strlen(log), logger.console.textArea);
+
+
+ // Update scroll data
+ //
+ logger.console.nbLines = (*(logger.console.textArea))->nLines;
+ logger.console.contentHeight = logger.console.nbLines * logger.console.lineHeight;
+
+ logger.console.maxScrollPos = (logger.console.nbLines
+ - logger.console.nbOutputLines)
+ * logger.console.lineHeight;
+
+ SetCtlMax(logger.console.vScroll, logger.console.maxScrollPos);
+
+
+ // Auto-scroll to bottom (do this only once)
+ //
+ if (logger.console.nbLines == logger.console.nbOutputLines + 1) {
+ // Enable the scrollbar once it is needed
+ //
+ HiliteControl(logger.console.vScroll, 0);
+ }
+
+ if (logger.console.nbLines > logger.console.nbOutputLines &&
+ logger.console.scrollPos == oldMaxScroll) {
+
+ LogConsoleScrollToBottom();
+ }
+
+ SetPort(oldPort);
+
+ ShowHide(logger.console.window, TRUE);
}
void
-log_to_file(char* format, ...)
+LogToFile(char* format, ...)
{
FILE* file;
va_list ap;
@@ -91,7 +676,7 @@ log_to_file(char* format, ...)
// Open file in Append Binary mode so the
// new line character is not converted.
//
- file = fopen(logger.file_path, "ab");
+ file = fopen(logger.filePath, "ab");
if(!file) {
return;
}
@@ -100,21 +685,29 @@ log_to_file(char* format, ...)
vfprintf(file, format, ap);
va_end(ap);
- // Using Unix New Line character for tail to
- // read correctly on Linux.
- //
- fprintf(file, " \x0A\r");
-
fclose(file);
}
void
-log_debug(char* format, ...)
+LogDefault(char* format, ...)
{
va_list ap;
char log[256] = "";
+
+ va_start(ap, format);
+ vsprintf(log, format, ap);
+ va_end(ap);
- if (logger.log_level < DEBUG_LEVEL) {
+ logger.print("%s %s", log, logger.newLine);
+}
+
+void
+LogDebug(char* format, ...)
+{
+ va_list ap;
+ char log[256] = "";
+
+ if (logger.logLevel < DEBUG_LEVEL) {
return;
}
@@ -122,16 +715,17 @@ log_debug(char* format, ...)
vsprintf(log, format, ap);
va_end(ap);
- logger.log("DEBUG: %s", log);
+
+ logger.print("DEBUG: %s %s", log, logger.newLine);
}
void
-log_info(char* format, ...)
+LogInfo(char* format, ...)
{
va_list ap;
char log[256] = "";
- if (logger.log_level < INFO_LEVEL) {
+ if (logger.logLevel < INFO_LEVEL) {
return;
}
@@ -139,16 +733,16 @@ log_info(char* format, ...)
vsprintf(log, format, ap);
va_end(ap);
- logger.log("INFO: %s", log);
+ logger.print("INFO: %s %s", log, logger.newLine);
}
void
-log_warn(char* format, ...)
+LogWarn(char* format, ...)
{
va_list ap;
char log[256] = "";
- if (logger.log_level < WARNING_LEVEL) {
+ if (logger.logLevel < WARNING_LEVEL) {
return;
}
@@ -156,16 +750,16 @@ log_warn(char* format, ...)
vsprintf(log, format, ap);
va_end(ap);
- logger.log("WARNING: %s", log);
+ logger.print("WARNING: %s %s", log, logger.newLine);
}
void
-log_error(char* format, ...)
+LogError(char* format, ...)
{
va_list ap;
char log[256] = "";
- if (logger.log_level < ERROR_LEVEL) {
+ if (logger.logLevel < ERROR_LEVEL) {
return;
}
@@ -173,5 +767,236 @@ log_error(char* format, ...)
vsprintf(log, format, ap);
va_end(ap);
- logger.log("ERROR: %s", log);
+ logger.print("ERROR: %s %s", log, logger.newLine);
}
+
+
+int
+DoLogGrow(Point p)
+{
+ Rect newBounds;
+ GrafPtr savePort;
+ Rect limits;
+ long theResult;
+ Point orig = {0, 0};
+
+ GetPort(&savePort);
+ SetPort(logger.console.window);
+
+ SetRect(&limits, CONSOLE_MIN_WIDTH, // Min horizontal size
+ CONSOLE_MIN_HEIGHT, // Min vertical size
+ screenBits.bounds.right, // Max horizontal size
+ screenBits.bounds.bottom); // Max vertical size
+
+ theResult = GrowWindow(logger.console.window, p, &limits);
+ if (theResult == 0) {
+ return 0;
+ }
+
+ LocalToGlobal(&orig);
+ newBounds.left = orig.h;
+ newBounds.right = newBounds.left + LoWord(theResult);
+ newBounds.top = orig.v;
+ newBounds.bottom = newBounds.top + HiWord(theResult);
+
+ LogConsoleResize(&newBounds);
+
+ logger.console.prevBounds = logger.console.growBounds;
+
+ return 1;
+}
+
+pascal void DoLogScroll(ControlHandle ctl, short ctlEventIn)
+{
+ int value = logger.console.scrollPos;
+ int orig = value;
+
+ (void) ctl; // unused
+
+ switch (ctlEventIn) {
+
+ case inUpButton:
+ value -= logger.console.lineHeight;
+ break;
+
+ case inDownButton:
+ value += logger.console.lineHeight;
+ break;
+
+ case inPageUp:
+ value -= logger.console.lineHeight * (logger.console.nbOutputLines - 1);
+ break;
+
+ case inPageDown:
+ value += logger.console.lineHeight * (logger.console.nbOutputLines - 1);
+ break;
+ }
+
+ // Clamp
+ //
+ if (value < 0) {
+ value = 0;
+ }
+ else if (value > logger.console.maxScrollPos) {
+ value = logger.console.maxScrollPos;
+ }
+
+ if (value != orig) {
+ logger.console.scrollPos = value;
+ SetCtlValue(logger.console.vScroll, logger.console.scrollPos);
+
+ TEScroll(0, -(value - orig), logger.console.textArea);
+
+ LogConsoleUpdate();
+ }
+}
+
+Boolean DoLogEvent(EventRecord event)
+{
+ Boolean interceped = FALSE;
+
+ GrafPtr oldPort;
+ WindowPtr eventWin;
+ ControlHandle ctl;
+ int eventIn, ctlEventIn;
+ Point localPt;
+
+ if (logger.outputMethod != CONSOLE_OUT) {
+ return FALSE;
+ }
+
+ GetPort(&oldPort);
+ SetPort(logger.console.window);
+
+ localPt = event.where;
+ GlobalToLocal(&localPt);
+
+
+ switch (event.what) {
+
+ case nullEvent:
+ break;
+
+ case mouseDown:
+ eventIn = FindWindow(event.where, &eventWin);
+
+ if (eventWin != logger.console.window) {
+ interceped = FALSE;
+ break;
+ }
+
+ SelectWindow(eventWin);
+
+ switch (eventIn) {
+
+ case inContent:
+
+ ctlEventIn = FindControl(localPt, eventWin, &ctl);
+
+ if (PtInRect(localPt, &logger.console.closeBtn)) {
+ HideWindow(logger.console.window);
+ interceped = TRUE;
+ }
+ else if (PtInRect(localPt, &logger.console.lowerBtn)) {
+ Rect origBounds = logger.console.origBounds;
+
+ logger.console.prevBounds = logger.console.growBounds;
+ LogConsoleResize(&origBounds);
+ LogConsoleScrollToBottom();
+ }
+ else if (PtInRect(localPt, &logger.console.clearBtn)) {
+ LogConsoleClear();
+ }
+ else if (PtInRect(localPt, &logger.console.zoomBtn)) {
+ LogConsoleZoom();
+ LogConsoleScrollToBottom();
+ }
+ else if (PtInRect(localPt, &logger.console.titleBar)) {
+ DragWindow(eventWin, event.where, &screenBits.bounds);
+ InvalRect(&screenBits.bounds);
+ }
+ else if (PtInRect(localPt, &logger.console.growBtn)) {
+ DoLogGrow(event.where);
+ }
+ else if (ctl == logger.console.vScroll) {
+
+ if (ctlEventIn == inThumb) {
+ int ctlValue;
+ int delta;
+
+ // Dragging the thumb
+ //
+ TrackControl(ctl, localPt, NULL);
+
+ ctlValue = GetCtlValue(ctl);
+ delta = ctlValue - logger.console.scrollPos;
+
+ logger.console.scrollPos = ctlValue;
+
+ TEScroll(0, -delta, logger.console.textArea);
+
+ LogConsoleUpdate();
+
+ }
+ else {
+ // Buttons + page scroll
+ //
+ TrackControl(ctl, localPt, DoLogScroll);
+ }
+ }
+
+ interceped = TRUE;
+ break;
+ case inGrow:
+ logger.debug("In grow");
+ interceped = TRUE;
+ break;
+ case inGoAway:
+ if (TrackGoAway(eventWin, event.where)) {
+ HideWindow(logger.console.window);
+ interceped = TRUE;
+ }
+ break;
+ }
+ break;
+
+ case updateEvt:
+ eventWin = (WindowPtr)event.message;
+
+ if (eventWin && eventWin == logger.console.window) {
+ BeginUpdate(eventWin);
+
+ LogConsoleDraw();
+
+ EndUpdate(eventWin);
+ }
+ break;
+
+ case activateEvt:
+ break;
+ }
+
+ SetPort(oldPort);
+
+ return interceped;
+}
+
+void
+LogSaveTextState(void)
+{
+ logSavedTextState.font = thePort->txFont;
+ logSavedTextState.size = thePort->txSize;
+ logSavedTextState.mode = thePort->txMode;
+ logSavedTextState.face = thePort->txFace;
+}
+
+void
+LogRestoreTextState(void)
+{
+ TextFont(logSavedTextState.font);
+ TextSize(logSavedTextState.size);
+ TextMode(logSavedTextState.mode);
+ TextFace(logSavedTextState.face);
+}
+
+#endif
--- logger.h Tue Apr 8 09:30:11 2025
+++ logger.h Sat Mar 28 18:49:11 2026
@@ -1,6 +1,6 @@
/* logger.h
*
- * Copyright 2025 Francois Techene
+ * Copyright 2026 Francois Techene
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,6 +21,19 @@
#ifndef LOGGER_H
#define LOGGER_H
+////////////////////////////////////////////////
+// Logger setup
+//
+#define USE_LOGGER // Comment to disable logs
+
+#define LOG_LEVEL DEBUG_LEVEL // NONE_LEVEL, ERROR_LEVEL, WARNING_LEVEL, INFO_LEVEL, DEBUG_LEVEL
+#define LOG_METHOD CONSOLE_OUT // NONE_OUT, CONSOLE_OUT, FILE_OUT
+#define LOG_FILE "::logs:output.log" // Change to the path to your log file
+ // requires LOG_METHOD set to FILE_OUT
+#define CONSOLE_LINES 4
+
+////////////////////////////////////////////////
+
typedef enum {
NONE_LEVEL = 0,
ERROR_LEVEL,
@@ -37,36 +50,70 @@ typedef enum {
} LogOutputMethod;
+#ifdef USE_LOGGER // Only load if required.
+typedef struct _LogConsole {
+
+ WindowPtr window;
+ TEHandle textArea;
+
+ ControlHandle vScroll;
+
+ Rect bounds;
+ Rect origBounds;
+ Rect growBounds;
+ Rect prevBounds;
+ Rect titleBar;
+ Rect bottomBar;
+ Rect view;
+
+ Rect lowerBtn;
+ Rect clearBtn;
+ Rect closeBtn;
+ Rect zoomBtn;
+ Rect growBtn;
+
+ int width;
+ int height;
+ int lineHeight;
+ int contentHeight;
+ int nbLines;
+ int scrollPos;
+ int maxScrollPos;
+ int nbOutputLines;
+
+} LogConsole;
+
+#endif
+
typedef struct _Logger {
+ void (*print) (char* format, ...);
void (*log) (char* format, ...);
void (*debug) (char* format, ...);
void (*info) (char* format, ...);
void (*warn) (char* format, ...);
void (*error) (char* format, ...);
+ Boolean (*onEvent) (EventRecord event);
- char* file_path;
- int output_method;
- int log_level;
+#ifdef USE_LOGGER // Only load if required.
+ LogConsole console;
+ char* filePath;
+ int outputMethod;
+ int logLevel;
+ char* newLine;
+#endif
+
} Logger;
extern Logger logger;
-void init_logger(LogLevel level,
- LogOutputMethod out,
- char* file_path);
+void InitLogger(LogLevel level,
+ LogOutputMethod out,
+ char* file_path);
-void log_debug(char* format, ...);
-void log_info(char* format, ...);
-void log_warn(char* format, ...);
-void log_error(char* format, ...);
-
-void log_to_none(char* format, ...);
-void log_to_console(char* format, ...);
-void log_to_file(char* format, ...);
-
+Boolean DoLogEvent(EventRecord event);
#endif
--- README Tue Apr 8 14:39:25 2025
+++ README Sun Mar 29 17:18:36 2026
@@ -9,7 +9,8 @@ This library has been built and tested using THINK C v
In order to install it, just import `logger.c` and `logger.h` to your project and include `logger.h` in your source files.
-The Logger library requires ANSI to be added to your project.
+The Logger library requires ANSI to be added to your project. It also requires the MacTraps libs if you plan to use the graphical console.
+You may only use the graphical cosole if you are developing a graphical application and that all the Toolbox inits are made by your application.
---
@@ -17,35 +18,36 @@ The Logger library requires ANSI to be added to your p
## Initializing the logger:
-For the logger to log to the console (through printf()), call the init_logger() function with a log level and `CONSOLE_OUT` as the output method.
+The logger can log to a graphical console that pops up at the bottom of the screen.
+If you want to use the console, call the InitLogger() function with a log level and `CONSOLE_OUT` as the output method.
- init_logger(ERROR_LEVEL,
- CONSOLE_OUT,
- NULL);
+ InitLogger(DEBUG_LEVEL,
+ CONSOLE_OUT,
+ NULL);
-For the logger to log to a file, call the init_logger() function with the FILE_OUT as the output method along with the path to the file to log to.
+For the logger to log to a file, call the InitLogger() function with the FILE_OUT as the output method along with the path to the file to log to.
- init_logger(DEBUG_LEVEL,
- FILE_OUT,
- "file.log");
+ InitLogger(DEBUG_LEVEL,
+ FILE_OUT,
+ ":file.log");
You can define paths as follow:
-* "file.log" - A simple file name relative to the project.
-* ":logs:file.log" - An path relative to the project.
+* ":file.log" - A simple file name relative to the project.
+* "::logs:file.log" - Going back up 1 level and moving to the logs folder.
* "Macintosh HD:logs:file.log" - An absolute path.
Tip: The log file can be stored on a network volume (from Netatalk) and loaded with `tail -f` on a modern machine so that logs are shown in real time.
Note that if the log file doesn't exist, it is created but only if its containg folder exists.
-If the file cannot be created, the output method is set to NONE_OUT.
-The log file is cleared every time init_logger() is called.
+If the file cannot be created, the output method is set to NONE_OUT. Nothing gets logged.
+The log file is cleared every time InitLogger() is called.
Log output methods are as follow:
- NONE_OUT = 0,
- CONSOLE_OUT = 1,
- FILE_OUT = 2,
+ NONE_OUT = 0, // Nothing gets logged. Fail safe internal use.
+ CONSOLE_OUT = 1, // Log to the graphical console.
+ FILE_OUT = 2 // Log to a file.
Log levels are as follow:
NONE_LEVEL = 0,
@@ -55,14 +57,14 @@ Log levels are as follow:
DEBUG_LEVEL = 4,
-init_logger() initializes the global variable `logger`.
+InitLogger() initializes the global variable `logger`.
## Using the logger:
-You can log a new line by calling:
+You can log some text by calling:
- logger.log(char* format, ...);
+ logger.print(char* format, ...);
You can format your log message with variables:
@@ -74,22 +76,34 @@ You can format your log message with variables:
You can also use the following methods to differentiate debug, warning and error messages from your log output.
+ logger.log(char* format, ...); // Same as print() but with a trailing new line.
logger.debug(char* format, ...);
logger.info(char* format, ...);
logger.warn(char* format, ...);
logger.error(char* format, ...);
+
+
+## Using the console:
+
+If you are logging to the console, you need to forward the application's events to the logger and ignore the event if it has been handled by the console.
+
+You may place the following code at the top of your events handler function:
+
+ if(logger.onEvent(event)) {
+ return;
+ }
## Customizing:
-You can override the logger.log() function to handle logs the way you want, like printing the message in a text area on the screen.
+You can override the logger.print() function to handle logs the way you want, like for defining your own output method.
- void my_log(char* format, ...)
+ void myLog(char* format, ...)
{
//Handle my log message.
}
-After initializing with no output, just set logger.log.
+After initializing with no output, just set logger.print.
- init_logger(DEBUG_LEVEL, NONE_OUT, NULL);
- logger.log = my_log;
+ InitLogger(DEBUG_LEVEL, NONE_OUT, NULL);
+ logger.print = myLog;
--- test.log Tue Apr 8 14:40:16 2025
+++ test.log Fri Mar 27 11:40:52 2026
@@ -21,23 +21,3 @@
\____/\___/ \__, |\__, |\___|_|
|___/ |___/
-
-#### Testing NONE_LEVEL: Must not show any log:
-
-#### Testing ERROR_LEVEL:
-ERROR: This error log should be visible
-
-#### Testing WARNING_LEVEL:
-WARNING: This warning log should be visible
-ERROR: This error log should be visible
-
-#### Testing INFO_LEVEL:
-INFO: This info log should be visible
-WARNING: This warning log should be visible
-ERROR: This error log should be visible
-
-#### Testing DEBUG_LEVEL:
-DEBUG: This is a debug message with number 1
-INFO: This is an info message with number 2
-WARNING: This is a warning with number 3
-ERROR: This is an error with number 4