{"id":25036777,"url":"https://github.com/andriystr/sqlog","last_synced_at":"2026-02-11T14:03:34.513Z","repository":{"id":270902359,"uuid":"911806157","full_name":"andriystr/sqlog","owner":"andriystr","description":"Hierarchical logging","archived":false,"fork":false,"pushed_at":"2025-01-03T22:55:27.000Z","size":10,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-09-25T03:10:04.718Z","etag":null,"topics":["emacs","html","library","logging","org-mode","python","python-library","python3","sqlite","sqlite3"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/sqlog/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/andriystr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-01-03T22:36:38.000Z","updated_at":"2025-06-11T14:50:02.000Z","dependencies_parsed_at":null,"dependency_job_id":"b04eccc7-3e2d-444e-bced-9ae9678f3160","html_url":"https://github.com/andriystr/sqlog","commit_stats":null,"previous_names":["andriystr/sqlog"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/andriystr/sqlog","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andriystr%2Fsqlog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andriystr%2Fsqlog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andriystr%2Fsqlog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andriystr%2Fsqlog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andriystr","download_url":"https://codeload.github.com/andriystr/sqlog/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andriystr%2Fsqlog/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29333925,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-11T12:42:24.625Z","status":"ssl_error","status_checked_at":"2026-02-11T12:41:23.344Z","response_time":97,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["emacs","html","library","logging","org-mode","python","python-library","python3","sqlite","sqlite3"],"created_at":"2025-02-06T00:44:02.627Z","updated_at":"2026-02-11T14:03:34.508Z","avatar_url":"https://github.com/andriystr.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Overview\n\n**sqlog** is a library for hierarchical logging. All logs are grouped into sections, which simplifies reading and analysis.\n\n---\n\n## Quick Start\n\nThe library integrates with Python's standard logging module, providing its own log handler. Here's an example:\n\n```python\nimport logging                  # Standard logging library\nfrom datetime import datetime   # Work with date and time\nfrom sqlog import SqFileHandler # Import sqlog log handler\n\n# Initialize the log handler and specify the file to write to\nsq_handler = SqFileHandler('log.sqlog')\n\nlogger = logging.getLogger('My Program')  # Create logger\nlogger.addHandler(sq_handler)             # Assign log handler\n\nlogging.basicConfig(level=logging.INFO)  # Set logging level to INFO\n\n# Create a section and log within its context\nwith sq_handler.section('Program Work'):\n    for i in range(10):\n        # Create a subsection for each task\n        with sq_handler.section(f'Task({i})') as sec:\n            time_start = datetime.now()  # Task start time\n\n            num = i ** 2  # Perform task\n            logger.info(f'{i} ** 2 = {num}')  # Log result\n\n            work_time = datetime.now() - time_start  # Task duration\n            sec.add_header_str(f'work time: {work_time}')  # Add header to subsection\n```\n\nTo convert logs into a readable format (e.g., HTML or Org files), use the following commands:\n\n- Convert to Org file: `sqlog log.sqlog -o log.org`\n- Convert to HTML file: `sqlog log.sqlog -o log.html`\n\n---\n\n## Using the Library\n\n### Library Operation Description\n\nWhen initializing the log handler (`SqFileHandler`), a main section (`top_section`) is created. This section serves as the root and contains all subsequent sections and logs. Each program run creates a new main section, which serves as the root for all logs of the current program execution, logically dividing them into sections.\n\nLogs are divided into sections, making them structured and easier to analyze. Subsections further organize logs depending on the developer's needs. Logs are written to the current section: initially, this is the main section. When entering the context of a subsection, it becomes the current section. After exiting the context, the parent section becomes the current one again.\n\nSections are created using the `section` method of the handler (`SqFileHandler`), which automatically manages their hierarchy. Independent sections can also be created for manual log management. Here’s an example:\n\n```python\n# Create an independent section\ncommon_section = sq_handler.section('Common')\n\n# Log in the current and independent sections\nwith sq_handler.section('Task'):\n    logger.info('Task status ...', common_section)\n```\n\nYou can also create subsections for independent sections:\n\n```python\nwith sq_handler.section('Task'):\n    common_subsection = common_section.section('Common Subsection')\n    logger.info('Task state ...', common_subsection)\n```\n\nThus, logs are written both in the current section and the specified independent section, offering flexibility in organizing entries.\n\nImportant: All sections must belong to the same file and be created by the same handler. Logging in sections of different files will not work.\n\nLog files have the `.sqlog` extension and are implemented in SQLite3 database format. These files collect all logs, which can later be converted into readable formats.\n\n### Section Header Formatting\n\nSection headers support formatting using a subset of HTML.\n\n*Available tags:*\n- `\u003cb\u003e` Bold text\n- `\u003ci\u003e` Italic\n- `\u003cu\u003e` Underlined text\n- `\u003cs\u003e` Strikethrough text\n- `\u003cpre\u003e` or `\u003ccode\u003e` Code block\n- `\u003cd\u003e` or `\u003cdelim\u003e` Delimiter (single tag)\n\nTag attributes (only for HTML output format):\n- `color`: Text color\n- `bg` or `background`: Background color of the text\n\nTo create a section with an unformatted header: `.section('some text...', html=False)`  \nOr to add unformatted text to the header: `.add_header_str('some text...', html=False)`\n\n---\n\n## Conversion to Readable Format\n\n### Utility Arguments\n\nThe `sqlog` utility accepts the following arguments:\n- Positional argument: Path to `.sqlog` file\n- `-o` or `--output`: Path to the output file\n- `-f` or `--format`: Output format (HTML or Org)\n- `-l` or `--log_format`: Log format string, default:\n  - For Org format: `- %(levelname)s :: %(name)s :: %(message)s`\n  - For HTML format: `\u003cb\u003e%(levelname)s\u003c/b\u003e \u003cspan class=\"delimiter\"\u003e|\u003c/span\u003e %(name)s \u003cspan class=\"delimiter\"\u003e|\u003c/span\u003e %(message)s`\n  - More details: [Python Logging Docs](https://docs.python.org/3/library/logging.html#logrecord-attributes)\n- `-s` or `--start_level`: Starting section level for Org format (default: 1)\n\nIf `--output` is not specified, the result is printed in the terminal (requires explicit format specification). The format is automatically determined by the output file extension.\n\n---\n\n## Example\n\n```python\nimport asyncio                   # Import async library\nimport logging                   # Import standard logging module\nfrom random import randint       # Import function to generate random numbers\nfrom datetime import datetime    # Import date and time module\nfrom sqlog import SqFileHandler  # Import log handler from sqlog library\n\n# Create a logger to be used for logging\nlogger = logging.getLogger('My Program')\n# Create SqFileHandler to write logs to file log.sqlog\nsq_handler = SqFileHandler('log.sqlog')\n# Create a section for logs to be written to the 'Common' section\ncommon_section = sq_handler.section('Common')\n# Add sq_handler to our logger\nlogger.addHandler(sq_handler)\n\n# Set logging level to DEBUG to record all messages\nlogger.setLevel(logging.DEBUG)\n# Disable propagation of logs so they don’t get written to other handlers\nlogger.propagate = False\n\n# Async function to perform tasks with delays\nasync def task(i):\n    # Simulate random delay\n    await asyncio.sleep(randint(0, 3))\n    \n    # Create a subsection for each task with a unique name\n    with sq_handler.section(f'\u003cb\u003eTask({i})\u003c/b\u003e') as sec:\n        # Record task start time\n        time_start = datetime.now()\n\n        # Simulate delay as if the task is running\n        await asyncio.sleep(randint(0, 3))\n\n        # Perform computation (square a number)\n        num = i ** 2\n\n        # Log the result in the current section and 'Common' section\n        logger.info(f'{i} ** 2 = {num}', common_section)\n\n        # Another delay before task completion\n        await asyncio.sleep(randint(0, 3))\n\n        # Calculate task duration\n        work_time = datetime.now() - time_start\n\n        # Add task duration info to subsection header\n        sec.add_header_str(f'\u003cdelim\u003ework time: {work_time}')\n\n# Main async function to run all tasks\nasync def main():\n    # Record the start time of all tasks\n    start_dt = datetime.now().strftime('%Y-%m-%d %H:%M:%S')\n\n    # Create the main section with start time\n    with sq_handler.section(f'\u003cb\u003eStart\u003c/b\u003e {start_dt}'):\n        # Run all 10 tasks concurrently\n        await asyncio.gather(*[task(i) for i in range(10)])\n\n# Get the current event loop for async execution\nloop = asyncio.get_event_loop_policy().get_event_loop()\n# Run the main loop until all\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandriystr%2Fsqlog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandriystr%2Fsqlog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandriystr%2Fsqlog/lists"}