Usage Guide¶
This guide provides detailed examples and explains the core concepts of ascii_colors
.
Core Concepts: Direct Print vs. Logging¶
ascii_colors
offers two fundamentally different ways to generate styled terminal output:
Important
Understanding this distinction is key to using the library effectively.
Direct Print Methods (ASCIIColors.red, print, bold, bg_red, etc.) * What they do: Print strings directly to a stream (default: sys.stdout) using Python’s built-in print function. * How they work: Apply ANSI escape codes for colors and styles immediately before printing. * Logging System: They completely bypass the logging system. Levels, Handlers, Formatters, and Context are ignored. * Use Case: Best for simple, immediate visual feedback, status messages, user prompts, banners, or decorative output where structured logging isn’t needed.
Logging System (basicConfig, getLogger, logger.info, Handlers, Formatters) * What it does: Provides a structured, leveled logging framework, similar to Python’s standard logging. * How it works: Log messages are created as LogRecord-like objects. They are filtered by level, processed by Formatters to create strings, and then sent by Handlers to destinations (console, files, etc.). * Styling: Console output (via ConsoleHandler/StreamHandler) is typically colored based on the log level (e.g., Warnings Yellow, Errors Red) by default. Formatting is highly customizable. * Interaction: Use the recommended logging-compatible API (import ascii_colors as logging) or the ASCIIColors class methods (ASCIIColors.info, add_handler). Both control the same underlying global logging state. * Use Case: Ideal for application logs, debugging information, tracing events, and any scenario requiring structured, filterable, and configurable output routing.
Using the Logging Compatibility Layer (import ascii_colors as logging)¶
The most convenient and recommended way to utilize the logging features is through the compatibility layer. It allows you to use familiar functions and patterns from Python’s standard logging module.
1import ascii_colors as logging # The crucial import alias
2
3# Configure using basicConfig (similar to standard logging)
4logging.basicConfig(
5 level=logging.INFO, # Set the minimum level to log
6 format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
7 datefmt='%H:%M:%S'
8 # Default handler is StreamHandler to stderr with level-based coloring
9)
10
11# Get logger instances
12logger = logging.getLogger('MyModule')
13another_logger = logging.getLogger('Another.Component')
14
15# Log messages using standard methods
16logger.debug("This won't be shown (global level is INFO)")
17logger.info("Module started.")
18another_logger.warning("Something looks suspicious.")
19another_logger.error("Failed to process request.")
20
21# Benefit: Console output is automatically colored by level!
This approach provides a smooth transition for projects already using standard logging or a familiar interface for new projects.
Available Colors and Styles (for Direct Printing)¶
These constants are attributes of the ASCIIColors
class. Use them with direct print methods like print()
, bold()
, bg_red()
, multicolor()
, etc.
Note
Actual rendering depends on the capabilities of your terminal emulator. Most modern terminals support these styles and basic/bright colors. 256-color support (used for orange) is also common but not universal.
Reset Code¶
ASCIIColors.color_reset
: Resets all styles and colors to the terminal default. Automatically appended by most direct print methods.
Text Styles¶
ASCIIColors.style_bold
ASCIIColors.style_dim
ASCIIColors.style_italic
ASCIIColors.style_underline
ASCIIColors.style_blink
(support varies)ASCIIColors.style_reverse
(swaps foreground/background)ASCIIColors.style_hidden
(conceals text; support varies)ASCIIColors.style_strikethrough
Foreground Colors (Regular)¶
ASCIIColors.color_black
ASCIIColors.color_red
ASCIIColors.color_green
ASCIIColors.color_yellow
ASCIIColors.color_blue
ASCIIColors.color_magenta
ASCIIColors.color_cyan
ASCIIColors.color_white
ASCIIColors.color_orange
(256-color approximation)
Foreground Colors (Bright)¶
ASCIIColors.color_bright_black
(often gray)ASCIIColors.color_bright_red
ASCIIColors.color_bright_green
ASCIIColors.color_bright_yellow
ASCIIColors.color_bright_blue
ASCIIColors.color_bright_magenta
ASCIIColors.color_bright_cyan
ASCIIColors.color_bright_white
Background Colors (Regular)¶
ASCIIColors.bg_black
ASCIIColors.bg_red
ASCIIColors.bg_green
ASCIIColors.bg_yellow
ASCIIColors.bg_blue
ASCIIColors.bg_magenta
ASCIIColors.bg_cyan
ASCIIColors.bg_white
ASCIIColors.bg_orange
(256-color approximation)
Background Colors (Bright)¶
ASCIIColors.bg_bright_black
ASCIIColors.bg_bright_red
ASCIIColors.bg_bright_green
ASCIIColors.bg_bright_yellow
ASCIIColors.bg_bright_blue
ASCIIColors.bg_bright_magenta
ASCIIColors.bg_bright_cyan
ASCIIColors.bg_bright_white
Direct Printing Examples¶
1from ascii_colors import ASCIIColors
2
3# --- Simple Colors ---
4ASCIIColors.red("Error: File not found.")
5ASCIIColors.green("Success: Configuration saved.")
6ASCIIColors.blue("Info: Processing request...")
7
8# --- Styles ---
9ASCIIColors.bold("Important Announcement")
10ASCIIColors.underline("Section Header", color=ASCIIColors.color_yellow)
11ASCIIColors.italic("Note: This feature is experimental.", color=ASCIIColors.color_magenta)
12ASCIIColors.dim("Less important details.")
13ASCIIColors.strikethrough("Deprecated option.")
14
15# --- Backgrounds ---
16ASCIIColors.bg_yellow("WARNING", color=ASCIIColors.color_black) # Black text on yellow BG
17ASCIIColors.print_with_bg(
18 " Critical Failure! ",
19 color=ASCIIColors.color_bright_white,
20 background=ASCIIColors.bg_bright_red
21)
22
23# --- Combining ---
24ASCIIColors.print(
25 " Status: OK ",
26 color=ASCIIColors.color_black, # Text color
27 style=ASCIIColors.style_bold + ASCIIColors.style_reverse, # Bold and Reverse video
28 background=ASCIIColors.bg_bright_green # Base background (reversed)
29)
30
31# --- Multicolor ---
32ASCIIColors.multicolor(
33 ["Task: ", "Upload", " | Progress: ", "100%", " | Status: ", "DONE"],
34 [
35 ASCIIColors.color_white, ASCIIColors.color_cyan, # Task
36 ASCIIColors.color_white, ASCIIColors.color_bright_yellow, # Progress
37 ASCIIColors.color_white, ASCIIColors.color_bright_green # Status
38 ]
39)
40
41# --- Highlight ---
42log_line = "INFO: User 'admin' logged in from 127.0.0.1"
43ASCIIColors.highlight(
44 text=log_line,
45 subtext=["INFO", "admin", "127.0.0.1"], # Words/phrases to highlight
46 color=ASCIIColors.color_white, # Default text color
47 highlight_color=ASCIIColors.bg_blue + ASCIIColors.color_bright_white # Style for highlights
48)
Logging System Examples¶
Setup with `basicConfig`¶
The simplest way to configure logging. Ideal for scripts or basic applications.
1import ascii_colors as logging
2import sys
3
4logging.basicConfig(
5 level=logging.DEBUG, # Log everything from DEBUG upwards
6 stream=sys.stdout, # Send logs to standard output
7 format='%(levelname)s:%(name)s:%(message)s' # Simple format
8)
9
10logger = logging.getLogger("BasicApp")
11logger.debug("Starting process...")
12logger.info("User interaction needed.")
Manual Setup for Advanced Control¶
Provides full control over handlers, formatters, and levels.
1import ascii_colors as logging
2from ascii_colors import handlers # Access RotatingFileHandler
3import sys
4from pathlib import Path
5
6# --- Get the root logger to configure ---
7root_logger = logging.getLogger()
8
9# --- Clear existing handlers (important if re-configuring) ---
10root_logger.handlers.clear()
11# Alternatively, use: logging.basicConfig(force=True, ...) to reset
12
13# --- Set overall level for the logger ---
14root_logger.setLevel(logging.DEBUG) # Allow all messages to pass to handlers
15
16# --- Configure Console Handler ---
17console_formatter = logging.Formatter(
18 # Using {}-style formatting
19 fmt='[{asctime}] <{name}> {levelname:^8s}: {message}',
20 style='{',
21 datefmt='%H:%M:%S'
22)
23console_handler = logging.StreamHandler(stream=sys.stdout)
24console_handler.setLevel(logging.INFO) # Console shows INFO and above
25console_handler.setFormatter(console_formatter)
26root_logger.addHandler(console_handler)
27
28# --- Configure File Handler ---
29log_file = Path("app_detailed.log")
30file_formatter = logging.Formatter(
31 # Using %-style formatting with source info
32 fmt='%(asctime)s|%(levelname)-8s|%(name)s:%(lineno)d|%(message)s',
33 style='%',
34 datefmt='%Y-%m-%d %H:%M:%S',
35 include_source=True # Capture file/line number (adds overhead)
36)
37file_handler = logging.FileHandler(log_file, mode='w', encoding='utf-8')
38file_handler.setLevel(logging.DEBUG) # File logs everything (DEBUG and above)
39file_handler.setFormatter(file_formatter)
40root_logger.addHandler(file_handler)
41
42# --- Configure Rotating JSON File Handler ---
43json_log_file = Path("audit.jsonl")
44json_formatter = logging.JSONFormatter(
45 # Define the structure of the JSON output
46 fmt={
47 "ts": "asctime",
48 "level": "levelname",
49 "logger": "name",
50 "msg": "message",
51 "req_id": "request_id", # Include custom context
52 "user": "user_name", # Include custom context
53 "file": "filename",
54 "line": "lineno",
55 },
56 datefmt="iso", # ISO8601 timestamp format
57 style='%', # Style applies to how fmt values are looked up
58 json_ensure_ascii=False
59)
60# Use the handlers namespace
61rotating_json_handler = handlers.RotatingFileHandler(
62 json_log_file,
63 maxBytes=5 * 1024 * 1024, # 5 MB
64 backupCount=5,
65 encoding='utf-8'
66)
67rotating_json_handler.setLevel(logging.WARNING) # Log WARNING and above as JSON
68rotating_json_handler.setFormatter(json_formatter)
69root_logger.addHandler(rotating_json_handler)
70
71
72# --- Now, log messages using any logger ---
73main_logger = logging.getLogger("MainApp")
74util_logger = logging.getLogger("MainApp.Utils")
75
76main_logger.debug("Low-level detail.") # File only
77util_logger.info("Utility function called.") # Console and File
78main_logger.warning(
79 "Possible issue detected.",
80 extra={"request_id": "xyz789", "user_name": "guest"} # Add context
81) # Console, File, and JSON
82main_logger.error(
83 "Operation failed!",
84 extra={"request_id": "xyz789", "user_name": "admin"}
85) # Console, File, and JSON
Logging Messages¶
Use standard logger methods.
1import ascii_colors as logging
2
3# Assume logging is configured (e.g., via basicConfig or manual setup)
4logger = logging.getLogger("DataProcessor")
5
6# Standard levels
7logger.debug("Starting data validation for batch %d.", 101)
8logger.info("Processing %d records.", 5000)
9logger.warning("Record %d has missing field 'email'. Skipping.", 1234)
10logger.error("Failed to connect to database '%s'.", "prod_db")
11logger.critical("Data corruption detected! Halting process.")
12
13# Logging exceptions
14try:
15 data = {}
16 value = data['required_key']
17except KeyError as e:
18 # Option 1: Log error with traceback automatically
19 logger.exception("Missing required data key!")
20 # Option 2: Log error manually including traceback
21 # logger.error("Missing required data key!", exc_info=True)
22 # Option 3: Use the utility function (logs at ERROR level)
23 # from ascii_colors import trace_exception
24 # trace_exception(e)
Context Management¶
Add thread-local context to enrich log records automatically. Formatters must be configured to include the context keys.
1import ascii_colors as logging
2from ascii_colors import ASCIIColors # Needed for context manager
3import threading
4import time
5import sys
6import random
7
8# Setup a formatter that includes 'request_id' and 'user'
9log_format = "[{asctime}] Req:{request_id}|User:{user} ({name}) {levelname}: {message}"
10formatter = logging.Formatter(log_format, style='{', datefmt='%H:%M:%S')
11handler = logging.StreamHandler(stream=sys.stdout)
12handler.setFormatter(formatter)
13logging.basicConfig(level=logging.INFO, handlers=[handler], force=True) # Reset config
14
15logger = logging.getLogger("WebServer")
16
17def handle_web_request(req_id, user):
18 # Use the context manager: sets context vars for this thread's scope
19 with ASCIIColors.context(request_id=req_id, user=user):
20 logger.info("Request received.")
21 # Simulate work
22 time.sleep(random.uniform(0.1, 0.3))
23 if user == "guest":
24 logger.warning("Guest access has limited permissions.")
25 logger.info("Request processed successfully.")
26 # Context (request_id, user) is automatically cleared upon exiting 'with'
27
28# Simulate multiple concurrent requests
29threads = []
30for i in range(3):
31 user = random.choice(['alice', 'bob', 'guest'])
32 req_id = f"req-{i+1:03d}"
33 thread = threading.Thread(target=handle_web_request, args=(req_id, user))
34 threads.append(thread)
35 thread.start()
36
37for t in threads:
38 t.join()
39
40# Outside the threads/context, the keys are not set
41# logger.info("Finished processing all requests.") # Would cause KeyError if context keys missing
Utilities¶
Animation Spinner (`execute_with_animation`)¶
Displays a spinner while a function executes. Uses direct printing for the spinner itself.
1import time
2from ascii_colors import ASCIIColors
3import ascii_colors as logging # For logging within the task
4
5# Configure logging if the task needs it
6logging.basicConfig(level=logging.INFO, format='%(message)s')
7
8def simulate_database_query(query_id):
9 logging.info(f"[Task {query_id}] Starting query...")
10 duration = random.uniform(1, 3)
11 time.sleep(duration)
12 if random.random() < 0.2: # Simulate occasional failures
13 raise ConnectionError(f"DB connection lost during query {query_id}")
14 logging.info(f"[Task {query_id}] Query finished.")
15 return f"Query {query_id} results (found {random.randint(10,100)} rows)"
16
17# --- Execute with animation ---
18query_to_run = "Q101"
19try:
20 # result = ASCIIColors.execute_with_animation(
21 # pending_text=f"Running database query {query_to_run}...",
22 # func=simulate_database_query,
23 # # *args for func:
24 # query_id=query_to_run,
25 # # Optional color for the pending text:
26 # color=ASCIIColors.color_cyan
27 # )
28 # Use the direct print methods for the overall status of the animated task
29 # ASCIIColors.success(f"Animation completed: {result}")
30 # Dummy call for example build without randomness
31 ASCIIColors.success("Animation completed: Query Q101 results (...)")
32
33except Exception as e:
34 ASCIIColors.fail(f"Animation failed: {e}")
35 # Optionally log the failure using the logging system
36 # logging.exception(f"Database query {query_to_run} failed")
Trace Exception (`trace_exception`)¶
A convenience function to log an exception and its traceback using the configured logging system at the ERROR level.
1import ascii_colors as logging
2from ascii_colors import trace_exception
3
4# Assumes logging is configured
5logging.basicConfig(level=logging.INFO)
6
7try:
8 value = int("not_a_number")
9except ValueError as e:
10 # Instead of logger.error("...", exc_info=True) or logger.exception(...)
11 trace_exception(e) # Logs the error message and full traceback