Skip to content

Commit

Permalink
remade logging - docs
Browse files Browse the repository at this point in the history
  • Loading branch information
DefinetlyNotAI committed Sep 29, 2024
1 parent 822b6de commit 7f2d771
Show file tree
Hide file tree
Showing 3 changed files with 241 additions and 171 deletions.
65 changes: 52 additions & 13 deletions ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,39 +50,78 @@ To install: `pip install -r requirements.txt`
## Log Class Documentation

### Overview
The `Log` class provides a flexible logging system that supports both console logging via `colorlog` and file logging. It is designed to be easily configurable through parameters such as log level, color scheme, and log file name. This class is particularly useful for applications requiring detailed logging for debugging and monitoring purposes.
The `Log` class provides a flexible logging system that supports both console logging
via `colorlog` and file logging.
It is designed to be easily configurable through parameters such as log level,
color scheme, and log file name.
This class is particularly useful for applications requiring detailed logging
for debugging and monitoring purposes.

### Features
- Supports both console and file logging.
- Configurable log levels (`DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`).
- Configurable log levels (`DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`, `EXCEPTION`).
- Customizable color schemes for console output.
- Automatic creation of log file if it does not exist.
- Timestamped entries for easy tracking of events.

### Usage Example

```python
from algopy import Log # Assuming the class definition is saved in log_class_definition.py

# You Don't need to set all of these, these are the defaults
# You don't have to set config as the whole dictionary you see now,
# Even `config = {} works`, as it autofill missing values with defaults
config = {
"filename": "YOUR_LOG_NAME.log",
"use_colorlog": True, # Recommend to keep it True, enables console logging
"log_level": "YOUR_LEVEL", # Your log level, can be "DEBUG", "INFO (+Internal)", "WARNING", "ERROR (+Exceptions)", or "CRITICAL" - Default is "INFO"
# For possible colors, see below
"debug_color": "cyan",
"info_color": "green",
"warning_color": "yellow",
"error_color": "red",
"critical_color": "red",
"exception_color": "red",
"colorlog_fmt_parameters": "%(log_color)s%(levelname)-8s%(reset)s %(blue)s%(message)s", # Format of the log message.
}

# Initialize the Log class with custom settings
logger = Log(filename="custom_log.log", use_colorlog=True, DEBUG=True, debug_color="cyan", info_color="green")
log = Log()

# Example usage
logger.info("This is an informational message.")
logger.warning("This is a warning message.")
logger.error("This is an error message.")
logger.critical("This is a critical message.")
log.debug("This is a debug message")
log.info("This is an info message")
log.warning("This is a warning message")
log.error("This is an error message")
log.critical("This is a critical message")
log.raw("This is a raw message") # Not recommended, Raw write the message without any formatting, doesn't log on console
log.string("This is a log message from a string", "warn") # Log a message from a string with a custom log level*
log.exception("This is an exception message")
```

### Configuration Options
- `filename`: The name of the log file. Default is `"Server.log"`.
- `use_colorlog`: Whether to enable colored logging in the console. Default is `True`.
- `DEBUG`: Enable debug-level logging. Default is `False`.
- `debug_color`, `info_color`, `warning_color`, `error_color`, `critical_color`: Colors for different log levels in the console. Defaults are `"cyan"` for debug, `"green"` for info, `"yellow"` for warning, and `"red"` for error and critical.
- `colorlog_fmt_parameters`: Format of the log message. Default includes timestamp, log level, and message.
_*_ Supports the following (Case-insensitive) log levels:
`debug`, `info`, `warning`, `error`, `critical`, `exception`.
and log levels shortcuts: `warn`, `err`, `crit`.

### Subclasses and Methods
The `Log` class does not have direct subclasses but can be extended for specialized logging needs.

### Color Settings
| Color Names | Bright Color Names |
|-------------|--------------------|
| black | light\_black |
| red | light\_red |
| green | light\_green |
| yellow | light\_yellow |
| blue | light\_blue |
| purple | light\_purple |
| cyan | light\_cyan |
| white | light\_white |

The `Bright Color Names` aren't standard ANSI codes,
and support for these varies wildly across different terminals.

## Find Class Documentation

### Overview
Expand Down
191 changes: 189 additions & 2 deletions algopy.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,13 @@
# Fun Fact: Interstellar + Undertale + Deltarune + Stardew + Terraria + Minecraft = Life

import heapq
import os
import random
import re
import colorlog
import inspect
import os
from datetime import datetime
import colorlog
import logging


# TODO add custom log class support! So allow log.crash for example if .crash had its params set
Expand Down Expand Up @@ -287,6 +289,191 @@
# Flashsort
# Smoothsort

class Log:
"""
A logging class that supports colored output using the colorlog library.
"""

def __init__(self, config: dict = None):
"""
Initializes the Log class with the given configuration.
:param config: A dictionary containing configuration options.
"""
config = config or {
"filename": "AlgoPy.log",
"use_colorlog": True,
"log_level": "INFO",
"debug_color": "cyan",
"info_color": "green",
"warning_color": "yellow",
"error_color": "red",
"critical_color": "red",
"exception_color": "red",
"colorlog_fmt_parameters": "%(log_color)s%(levelname)-8s%(reset)s %(blue)s%(message)s",
}
self.EXCEPTION_LOG_LEVEL = 45
self.INTERNAL_LOG_LEVEL = 15
logging.addLevelName(self.EXCEPTION_LOG_LEVEL, "EXCEPTION")
logging.addLevelName(self.INTERNAL_LOG_LEVEL, "INTERNAL")
self.color = config.get("use_colorlog", True)
self.filename = config.get("filename", "AlgoPy.log")
if self.color:
logger = colorlog.getLogger()
logger.setLevel(
getattr(logging, config["log_level"].upper(), logging.INFO)
)
handler = colorlog.StreamHandler()
log_colors = {
"INTERNAL": "cyan",
"DEBUG": config.get("debug_color", "cyan"),
"INFO": config.get("info_color", "green"),
"WARNING": config.get("warning_color", "yellow"),
"ERROR": config.get("error_color", "red"),
"CRITICAL": config.get("critical_color", "red"),
"EXCEPTION": config.get("exception_color", "red"),
}

formatter = colorlog.ColoredFormatter(
config.get("colorlog_fmt_parameters", "%(log_color)s%(levelname)-8s%(reset)s %(blue)s%(message)s"),
log_colors=log_colors,
)

handler.setFormatter(formatter)
logger.addHandler(handler)
try:
getattr(logging, config["log_level"].upper())
except AttributeError as AE:
self.__internal(f"Log Level {config['log_level']} not found, setting default level to INFO -> {AE}")

if not os.path.exists(self.filename):
self.newline()
self.raw("| Timestamp | LOG Level |" + " " * 71 + "LOG Messages" + " " * 71 + "|")
self.newline()

@staticmethod
def __timestamp() -> str:
"""
Returns the current timestamp as a string.
:return: Current timestamp in 'YYYY-MM-DD HH:MM:SS' format.
"""
return datetime.now().strftime('%Y-%m-%d %H:%M:%S')

@staticmethod
def __pad_message(message: str) -> str:
"""
Pads or truncates the message to fit the log format.
:param message: The log message to be padded or truncated.
:return: The padded or truncated message.
"""
return (message + " " * (153 - len(message)) if len(message) < 153 else message[:150] + "...") + "|"

def raw(self, message: str):
"""
Logs a raw message directly to the log file.
:param message: The raw message to be logged.
"""
frame = inspect.stack()[1]
if frame.function == "<module>":
self.__internal(f"Raw message called from a non-function - This is not recommended")
with open(self.filename, "a") as f:
f.write(f"{message}\n")

def newline(self):
"""
Logs a newline separator in the log file.
"""
with open(self.filename, "a") as f:
f.write("|" + "-" * 19 + "|" + "-" * 13 + "|" + "-" * 154 + "|" + "\n")

def info(self, message: str):
"""
Logs an info message.
:param message: The info message to be logged.
"""
if self.color:
colorlog.info(message)
self.raw(f"[{self.__timestamp()}] > INFO: | {self.__pad_message(message)}")

def warning(self, message: str):
"""
Logs a warning message.
:param message: The warning message to be logged.
"""
if self.color:
colorlog.warning(message)
self.raw(f"[{self.__timestamp()}] > WARNING: | {self.__pad_message(message)}")

def error(self, message: str):
"""
Logs an error message.
:param message: The error message to be logged.
"""
if self.color:
colorlog.error(message)
self.raw(f"[{self.__timestamp()}] > ERROR: | {self.__pad_message(message)}")

def critical(self, message: str):
"""
Logs a critical message.
:param message: The critical message to be logged.
"""
if self.color:
colorlog.critical(message)
self.raw(f"[{self.__timestamp()}] > CRITICAL: | {self.__pad_message(message)}")

def debug(self, message: str):
"""
Logs a debug message. If the message is "*-*", it logs a separator line.
:param message: The debug message to be logged.
"""
if message == "*-*":
self.raw("|" + "-" * 19 + "|" + "-" * 13 + "|" + "-" * 152 + "|")
else:
colorlog.debug(message)

def string(self, message: str, Type: str):
"""
Logs a message with a specified type. Supported types are 'err', 'warn', and 'crit'.
:param message: The message to be logged.
:param Type: The type of the log message.
"""
type_map = {"err": "error", "warn": "warning", "crit": "critical"}
Type = type_map.get(Type.lower(), Type)
try:
getattr(self, Type.lower())(message)
except AttributeError as AE:
self.__internal(f"A wrong Log Type was called: {Type} not found. -> {AE}")
getattr(self, "Debug".lower())(message)

def exception(self, message: str):
"""
Logs an exception message.
:param message: The exception message to be logged.
"""
if self.color:
colorlog.log(self.EXCEPTION_LOG_LEVEL, message)
self.raw(f"[{self.__timestamp()}] > EXCEPTION:| {self.__pad_message(message)}")

def __internal(self, message: str):
"""
Logs an internal message.
:param message: The internal message to be logged.
"""
if self.color:
colorlog.log(self.INTERNAL_LOG_LEVEL, message)


class Find:
def __init__(self):
Expand Down
Loading

0 comments on commit 7f2d771

Please sign in to comment.