Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create cyhy logging module #1

Merged
merged 9 commits into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# https://coverage.readthedocs.io/en/latest/config.html

[run]
source = src/example
source = src/cyhy_logging
omit =
branch = true

Expand Down
8 changes: 4 additions & 4 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ updates:
- dependency-name: hashicorp/setup-terraform
- dependency-name: mxschmitt/action-tmate
- dependency-name: step-security/harden-runner
# # Managed by cisagov/skeleton-python-library
# - dependency-name: actions/download-artifact
# - dependency-name: actions/upload-artifact
# - dependency-name: github/codeql-action
# Managed by cisagov/skeleton-python-library
- dependency-name: actions/download-artifact
- dependency-name: actions/upload-artifact
- dependency-name: github/codeql-action
package-ecosystem: github-actions
schedule:
interval: weekly
Expand Down
15 changes: 0 additions & 15 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,6 @@ jobs:
fail-fast: false
matrix:
python-version:
- "3.7"
- "3.8"
- "3.9"
- "3.10"
- "3.11"
- "3.12"
steps:
- id: harden-runner
Expand Down Expand Up @@ -286,11 +281,6 @@ jobs:
fail-fast: false
matrix:
python-version:
- "3.7"
- "3.8"
- "3.9"
- "3.10"
- "3.11"
- "3.12"
steps:
- id: harden-runner
Expand Down Expand Up @@ -341,11 +331,6 @@ jobs:
fail-fast: false
matrix:
python-version:
- "3.7"
- "3.8"
- "3.9"
- "3.10"
- "3.11"
- "3.12"
steps:
- id: harden-runner
Expand Down
83 changes: 70 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,81 @@
# cyhy-logging #
# cyhy-logging 🪵 #

[![GitHub Build Status](https://github.com/cisagov/cyhy-logging/workflows/build/badge.svg)](https://github.com/cisagov/cyhy-logging/actions)
[![CodeQL](https://github.com/cisagov/cyhy-logging/workflows/CodeQL/badge.svg)](https://github.com/cisagov/cyhy-logging/actions/workflows/codeql-analysis.yml)
[![Coverage Status](https://coveralls.io/repos/github/cisagov/cyhy-logging/badge.svg?branch=develop)](https://coveralls.io/github/cisagov/cyhy-logging?branch=develop)
[![Known Vulnerabilities](https://snyk.io/test/github/cisagov/cyhy-logging/develop/badge.svg)](https://snyk.io/test/github/cisagov/cyhy-logging)

This is a generic skeleton project that can be used to quickly get a
new [cisagov](https://github.com/cisagov) Python library GitHub
project started. This skeleton project contains [licensing
information](LICENSE), as well as
[pre-commit hooks](https://pre-commit.com) and
[GitHub Actions](https://github.com/features/actions) configurations
appropriate for a Python library project.
This is a Python package that provides a standard logging configuration which is
used by the Cyber Hygiene (CyHy) system.

## New Repositories from a Skeleton ##
The logging setup uses the
[`RichHandler`](https://rich.readthedocs.io/en/stable/logging.html) to provide
visually appealing and informative log outputs, including enhanced tracebacks
when debugging. Additionally, default filters like the
[`RedactPasswordFilter`](src/cyhy_logging/log_filters.py) are applied to all log
handlers to automatically filter and redact sensitive information.

Please see our [Project Setup guide](https://github.com/cisagov/development-guide/tree/develop/project_setup)
for step-by-step instructions on how to start a new repository from
a skeleton. This will save you time and effort when configuring a
new repository!
To ensure that all logs across different parts of the CyHy system are properly
grouped under a single root, the `CYHY_ROOT_LOGGER` constant is used. By using
this constant when configuring loggers (`CYHY_ROOT_LOGGER` as the root logger),
all log messages are consistently organized under the same logging namespace,
which simplifies log management and makes it easier to locate all related logs.
Additionally, using `CYHY_ROOT_LOGGER` allows the verbosity of CyHy logs to be
increased independently without affecting the verbosity of other packages that
use the logging system.

## Pre-requisites ##

- [Python 3.12](https://www.python.org/downloads/) or newer

## Example Usage ##

```python
from cyhy_logging import CYHY_ROOT_LOGGER, setup_logging
import logging

# Set up logging
logger = logging.getLogger(f"{CYHY_ROOT_LOGGER}.{__name__}")
setup_logging("DEBUG")

# Use the logger
logger.debug("This is a debug message")
logger.info("This is an info message")
logger.warning("This is a warning message")
logger.error("This is an error message")
logger.critical("This is a critical message")

# The logger will redact sensitive information by default
logger.info("Logging into database at https://admin:password@example.com/secret")

# Raise an exception to demonstrate the enhanced traceback
raise Exception("This is an exception")

# Shutdown logging
logging.shutdown()
```

Output:

```console
[13:37:28] DEBUG This is a debug message <python-input-0>:9
INFO This is an info message <python-input-0>:10
WARNING This is a warning message <python-input-0>:11
ERROR This is an error message <python-input-0>:12
CRITICAL This is a critical message <python-input-0>:13
INFO Logging into database at <python-input-0>:16
https://admin:****@example.com/secret
╭───────────────────── Traceback (most recent call last) ──────────────────────╮
│ in <module>:18 │
│ ╭───────────────────────────────── locals ─────────────────────────────────╮ │
│ │ CYHY_ROOT_LOGGER = 'cyhy' │ │
│ │ logger = <Logger cyhy.__main__ (DEBUG)> │ │
│ │ logging = <module 'logging' from │ │
│ │ '/Users/lemmy/.pyenv/versions/3.13.0/lib/python3.13/… │ │
│ ╰──────────────────────────────────────────────────────────────────────────╯ │
╰──────────────────────────────────────────────────────────────────────────────╯
Exception: This is an exception
```

## Contributing ##

Expand Down
2 changes: 1 addition & 1 deletion bump_version.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ set -o nounset
set -o errexit
set -o pipefail

VERSION_FILE=src/example/_version.py
VERSION_FILE=src/cyhy_logging/_version.py

HELP_INFORMATION="bump_version.sh (show|major|minor|patch|prerelease|build|finalize)"

Expand Down
2 changes: 1 addition & 1 deletion setup-env
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ for req_file in "requirements-dev.txt" "requirements-test.txt" "requirements.txt
done

# Install all necessary mypy type stubs
mypy --install-types src/
mypy --install-types --non-interactive src/

# Install git pre-commit hooks now or later.
pre-commit install ${INSTALL_HOOKS:+"--install-hooks"}
Expand Down
36 changes: 12 additions & 24 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
This is the setup module for the example project.
This is the setup module for the cyhy_logging project.

Based on:

Expand Down Expand Up @@ -42,10 +42,10 @@ def get_version(version_file):


setup(
name="example",
name="cyhy-logging",
# Versions should comply with PEP440
version=get_version("src/example/_version.py"),
description="Example Python library",
version=get_version("src/cyhy_logging/_version.py"),
description="CyHy Logging Python library",
long_description=readme(),
long_description_content_type="text/markdown",
# Landing page for CISA's cybersecurity mission
Expand All @@ -57,16 +57,16 @@ def get_version(version_file):
"Tracker": "https://github.com/cisagov/cyhy-logging/issues",
},
# Author details
author="Cybersecurity and Infrastructure Security Agency",
author_email="github@cisa.dhs.gov",
author="Mark Feldhousen",
author_email="mark.feldhousen@gwe.cisa.dhs.gov",
license="License :: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication",
# See https://pypi.python.org/pypi?%3Aaction=list_classifiers
classifiers=[
# How mature is this project? Common values are
# 3 - Alpha
# 4 - Beta
# 5 - Production/Stable
"Development Status :: 3 - Alpha",
"Development Status :: 5 - Production/Stable",
# Indicate who your project is intended for
"Intended Audience :: Developers",
# Pick your license as you wish (should match "license" above)
Expand All @@ -75,38 +75,26 @@ def get_version(version_file):
# that you indicate whether you support Python 2, Python 3 or both.
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: Implementation :: CPython",
],
python_requires=">=3.7",
python_requires=">=3.12",
# What does your project relate to?
keywords="skeleton",
packages=find_packages(where="src"),
package_dir={"": "src"},
package_data={"example": ["data/*.txt"]},
package_data={"cyhy_logging": ["py.typed"]},
py_modules=[splitext(basename(path))[0] for path in glob("src/*.py")],
include_package_data=True,
install_requires=["docopt", "schema", "setuptools >= 24.2.0"],
install_requires=["rich", "setuptools"],
extras_require={
"test": [
"coverage",
# coveralls 1.11.0 added a service number for calls from
# GitHub Actions. This caused a regression which resulted in a 422
# response from the coveralls API with the message:
# Unprocessable Entity for url: https://coveralls.io/api/v1/jobs
# 1.11.1 fixed this issue, but to ensure expected behavior we'll pin
# to never grab the regression version.
"coveralls != 1.11.0",
"coveralls",
"pre-commit",
"pytest-cov",
"pytest",
]
},
# Conveniently allows one to run the CLI tool as `example`
entry_points={"console_scripts": ["example = example.example:main"]},
entry_points={},
)
16 changes: 16 additions & 0 deletions src/cyhy_logging/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""The cyhy_logging library."""

# We disable the following Flake8 checks:
# - "Module level import not at top of file (E402)" here because the constants
# need to be defined early to prevent a circular import issue.
# - "Module imported but unused (F401)" here because although this import is not
# directly used, it populates the value package_name.__version__, which is
# used to get version information about this Python package.

CYHY_ROOT_LOGGER = "cyhy"


from ._version import __version__ # noqa: F401, E402
from .cyhy_logging import setup_logging # noqa: E402

__all__ = ["CYHY_ROOT_LOGGER", "setup_logging", "__version__"]
2 changes: 1 addition & 1 deletion src/example/_version.py → src/cyhy_logging/_version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""This file defines the version of this module."""

__version__ = "0.2.1"
__version__ = "1.0.0"
41 changes: 41 additions & 0 deletions src/cyhy_logging/cyhy_logging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""cyhy_logging Python library and tool."""

# Standard Python Libraries
import logging
from typing import Optional

# Third-Party Libraries
from rich.logging import RichHandler
from rich.traceback import install as install_rich_tracebacks

from . import CYHY_ROOT_LOGGER
from .log_filters import RedactPasswordFilter


def setup_logging(log_level: Optional[str] = None) -> None:
"""Set up logging for the CyHy namespace."""
# If a log_level is provided, ensure it is uppercase
if log_level:
log_level = log_level.upper()

logging.basicConfig(
datefmt="[%X]",
force=True,
format="%(message)s",
handlers=[RichHandler()],
)

# Install the Rich traceback handler
if log_level == "DEBUG":
install_rich_tracebacks(show_locals=True)

# Add a filter to redact passwords from URLs to all handlers of the root logger
password_redact_filter = RedactPasswordFilter()
root_logger = logging.getLogger()
for handler in root_logger.handlers:
handler.addFilter(password_redact_filter)

# Set the log level for the CyHy namespace
if log_level:
cyhy_logger = logging.getLogger(CYHY_ROOT_LOGGER)
cyhy_logger.setLevel(log_level)
Loading
Loading