Skip to main content

GKE-friendly JSON logging and optional Prometheus metrics for Grafana log-based metrics and monitoring

Project description

gke_log_metrics

Minimal, production-ready library for emitting structured JSON metrics/logs (designed for GKE, compatible with Grafana log-based metrics) and optional Prometheus-format metric export.

Key features:

  • JSON-based structured logging with fixed and extensible fields
  • JSON metrics that accept name and value (no internal auto-increment)
  • Optional Prometheus-format metrics export
  • Configuration precedence: env vars > .configs JSON file > defaults
  • Minimal dependencies (stdlib only)
  • Thread-safe metric aggregation

Installation

pip install gke_log_metrics

Or for development:

cd /path/to/gke_log_metrics
pip install -e .

Quick Start

from gke_log_metrics import Config, get_logger

# Load config (from .configs file and env var overrides)
cfg = Config()

# Get logger instance
logger = get_logger(cfg)

# Normal application log
logger.log("Application started", info={"phase": "startup"})

# Emit a log-based metric (JSON to stdout if METRICS_ENABLED)
logger.json_metric("backup_completed", 1, info={"job": "j1", "status": "success"})

# Unified metric API (updates Prometheus + emits JSON if enabled)
logger.metric("backup_checks_total", 1, labels={"job": "j1"})

# Export Prometheus metrics
print(logger.metrics_to_prometheus())

Configuration

Configuration is loaded in this order (later overrides earlier):

  1. Library defaults
  2. .configs file (environment variables format, or custom path via CONFIG_FILE env var)
  3. OS environment variables

Environment Variables

Variable Type Default Purpose
APP_NAME string "default_app" Application name in log entries
APP_TYPE string "default_type" Application type (e.g., gke_job, gke_api, shinyproxy_app)
OWNER string "default_owner" Owner or team name in log entries
METRICS_ENABLED bool true Enable JSON metrics to stdout (ignores LOG_LEVEL)
PROMETHEUS_ENABLED bool false Enable Prometheus-format metrics collection
LOG_LEVEL string "INFO" Python logging level (only affects logger.log(), not metrics)
CONFIG_FILE string ".configs" Path to environment variables format config file

Example .configs file

APP_NAME=my_backup_app
APP_TYPE=gke_job
OWNER=data_team
METRICS_ENABLED=true
PROMETHEUS_ENABLED=true
LOG_LEVEL=DEBUG

API Reference

Config(config_file=None)

Loads configuration from file and environment. Validates on instantiation.

cfg = Config(config_file='.configs')
# or
cfg = Config()  # uses default '.configs' or CONFIG_FILE env

get_logger(cfg)

Factory function; creates and returns a validated Logger instance.

from gke_log_metrics import get_logger, ValidationError

try:
    logger = get_logger(cfg)
except ValidationError as e:
    print(f"Config error: {e}")
    sys.exit(1)

Logger.log(...)

Normal application logging (respects LOG_LEVEL).

logger.log(
    message: str,
    info: Optional[Dict[str, Any]] = None,
    level: str = "info",  # "debug", "info", "warning", "error"
    app_name: Optional[str] = None,
    app_type: Optional[str] = None,
    extra: Optional[Dict[str, Any]] = None
)

Example:

logger.log("Backup started", info={"job_id": "12345"}, level="info")

Logger.json_metric(...)

Emit a JSON metric to stdout (always prints if METRICS_ENABLED=true, regardless of LOG_LEVEL).

logger.json_metric(
        name: str,
        value: float = 1.0,
        info: Optional[Dict[str, Any]] = None,
        app_name: Optional[str] = None,
        app_type: Optional[str] = None,
        extra: Optional[Dict[str, Any]] = None,
        message: Optional[str] = None,
)

Emitted JSON schema:

{
    "info": { "custom": "object" },
    "app_name": "my_app",
    "owner": "data_team",
    "app_type": "gke_job",
    "metric_name": "backup_completed",
    "metric_value": 1.0,
    "event_type": "metric",
    "timestamp": "2026-02-24T19:12:52.123456+00:00",
    "custom_field": "value"
}

Example:

logger.json_metric(
        "backup_verified",
        1.0,
        info={"job": "daily", "status": "success", "size_bytes": 1048576},
        extra={"duration_seconds": 45.3}
)

Logger.prometheus_metric(...)

Update internal Prometheus-style metrics (counters, gauges, histograms).

logger.prometheus_metric(
    name: str,
    value: float = 1.0,
    labels: Optional[Dict[str, str]] = None
)

Example:

logger.prometheus_metric("backup_checks_total", 1, labels={"job": "daily", "status": "success"})

Logger.metric(...)

Unified API: updates Prometheus metrics AND emits JSON metric (both if enabled).

logger.metric(
    name: str,
    value: float = 1.0,
    labels: Optional[Dict[str, str]] = None,
    message: Optional[str] = None,
    info: Optional[Dict[str, Any]] = None,
    extra: Optional[Dict[str, Any]] = None,
    app_name: Optional[str] = None,
    app_type: Optional[str] = None
)

Example:

logger.metric(
    "backup_checks_total",
    1,
    labels={"job": "daily", "status": "success"},
    message="Backup check succeeded",
    info={"duration": 45.3}
)

Logger.metrics_to_prometheus()

Export accumulated Prometheus metrics in text format.

prom_text = logger.metrics_to_prometheus()
print(prom_text)

Examples

See examples/basic_usage.py for a complete working example:

cd /path/to/gke_log_metrics
PYTHONPATH=. python3 examples/basic_usage.py

Testing

Run the test suite:

pytest tests/ -v

Publishing to PyPI

Option 1: Automatic Publishing via GitHub Actions (Recommended)

When you create a GitHub Release, the CI/CD workflow automatically builds and publishes to PyPI:

  1. Update version in pyproject.toml:

    version = "0.2.0"  # Increment as needed
    
  2. Commit and push the version bump:

    git add pyproject.toml
    git commit -m "Bump version to 0.2.0"
    git push origin master
    
  3. Create a GitHub Release:

  4. GitHub Actions automatically:

Result: Users can install with pip install gke_log_metrics

Option 2: Manual Publishing

For users who need to publish without creating a GitHub Release:

  1. Install build tools:

    pip install build twine
    
  2. Build the package:

    python -m build
    
  3. Publish to PyPI:

    twine upload dist/* -u __token__ -p $PYPI_TOKEN
    

    Or with your PyPI token inline (replace <your-pypi-token>):

    twine upload dist/* -u __token__ -p pypi-<your-pypi-token>
    

Note: Requires PyPI account and API token from https://pypi.org/account/tokens/

Use Cases

GKE Log-Based Metrics

Use json_metric() or metric() to emit structured logs that GKE/Stackdriver can parse as log-based metrics, then visualize in Grafana.

Example workflow:

  1. Your app calls logger.json_metric("backup_status", info={"job":"daily", "status":"success"})
  2. JSON is printed to stdout
  3. GKE captures stdout as logs
  4. Stackdriver creates a log-based metric from the JSON
  5. Grafana queries and visualizes the metric

Prometheus Scraping

Set PROMETHEUS_ENABLED=true and expose metrics via your app's HTTP endpoint (caller responsible).

Example (Flask):

from flask import Flask
from gke_log_metrics import Config, get_logger

app = Flask(__name__)
cfg = Config()
logger = get_logger(cfg)

@app.route('/metrics')
def metrics():
    return logger.metrics_to_prometheus(), 200, {'Content-Type': 'text/plain'}

Behavior Details

Metrics-Enabled Behavior

When METRICS_ENABLED=true:

  • json_metric() prints JSON to stdout always, ignoring LOG_LEVEL
  • metric() emits JSON to stdout (if METRICS_ENABLED) and updates Prometheus (if enabled)
  • json_metric() accepts name and value; there is no internal auto-incrementing counter field

Disabled Metrics

When METRICS_ENABLED=false:

  • json_metric() does nothing (silent)
  • metric() only updates Prometheus (if enabled), no JSON output

App Name / Type Defaulting

  • app_name defaults to config.APP_NAME when not provided
  • app_type defaults to config.APP_TYPE when not provided
  • Both can be overridden per call

Notes:

  • When you call json_metric(...) or metric(...) and do not provide app_name/app_type, the library will use Config.APP_NAME and Config.APP_TYPE respectively.
  • metric(...) will pass these effective values to the JSON metric output so the emitted log always contains app_name and app_type.

Development

Clone and install in editable mode:

git clone https://github.com/CityofEdmonton/gke_log_metrics.git
cd gke_log_metrics
pip install -e .
pytest tests/

License

This project is licensed under the MIT License. See the LICENSE file for details.

Support

For issues, questions, or contributions, contact the maintainers.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gke_log_metrics-0.1.0.tar.gz (9.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gke_log_metrics-0.1.0-py3-none-any.whl (9.3 kB view details)

Uploaded Python 3

File details

Details for the file gke_log_metrics-0.1.0.tar.gz.

File metadata

  • Download URL: gke_log_metrics-0.1.0.tar.gz
  • Upload date:
  • Size: 9.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.6

File hashes

Hashes for gke_log_metrics-0.1.0.tar.gz
Algorithm Hash digest
SHA256 49679ea2cd16035095dec5fd5e9ec92d95bf930adb1541a12f00642f12c409fc
MD5 ca05a596effece8b2a5887f69cea3e04
BLAKE2b-256 50a53f6e73488d0bf15d11e24f4560db1eddecc793902a84d314390dc642aa4f

See more details on using hashes here.

File details

Details for the file gke_log_metrics-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for gke_log_metrics-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0a0f20bfeb7a567b2fe736227b68858ada2afa7edc342bc5f6a7c9b1c98b51c0
MD5 05130e5e3b56d79b7a92651d945eb7e7
BLAKE2b-256 3e825b3cfc0f6b00c2b21f052320ab7edf44cfbc29e6fed57d4adb46dda9a584

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page