Skip to content

Usage Tracking

The dcc_backend_common.usage_tracking module provides a service for tracking and logging usage events compatible with OpenSearch functionality, with built-in user pseudonymization for privacy compliance.

Overview

The module provides:

  • UsageTrackingService: Main service class for tracking application usage events
  • User pseudonymization: One-way HMAC-based hashing of user IDs for privacy
  • Structured logging: Events logged in a format compatible with OpenSearch
  • Flexible event data: Support for custom key-value pairs in event logs
  • Always emitted: Events go through the pinned usage logger, so LOG_LEVEL never silences them

Where the Events Go

UsageTrackingService writes to the usage logger (see Logger — Usage Events), not to your module logger. That logger is pinned to INFO in init_logger(), so business events survive even LOG_LEVEL=WARNING.

Fields are flat, snake_case, top-level keys, so OpenSearch can aggregate on them without nested paths. Filter on logger: "usage" and event: "app_event" to isolate them.

Example production output:

json
{
  "timestamp": "2025-07-14T09:12:03.481920Z",
  "logger": "usage",
  "level": "info",
  "event": "app_event",
  "action": "document_processor.extract_text",
  "pseudonym_id": "9f2c1b...",
  "document_type": "pdf",
  "page_count": 10
}

Installation

The usage tracking module is part of the dcc-backend-common package:

bash
uv add dcc-backend-common

Quick Start

python
from dcc_backend_common.usage_tracking import UsageTrackingService

# Initialize the service with an HMAC secret
usage_tracking = UsageTrackingService(hmac_secret="your-secret-key")

# Log a usage event
usage_tracking.log_event(
    module="my_module",
    func="process_data",
    user_id="user123",
    data_size=1024,
    processing_time=0.5
)

API Reference

UsageTrackingService

The main service class for tracking usage events.

Initialization

python
UsageTrackingService(hmac_secret: str)
ParameterTypeDescription
hmac_secretstrSecret key used for HMAC-based pseudonymization of user IDs

Raises ValueError if hmac_secret is empty or the literal string "none" — an unset secret would make every pseudonym trivially reproducible, so the service refuses to start instead.

Example:

python
from dcc_backend_common.usage_tracking import UsageTrackingService

usage_tracking = UsageTrackingService(hmac_secret="your-hmac-secret-key")

get_pseudonymized_user_id

Generates a consistent, one-way pseudonym for a given user ID using HMAC-SHA256.

python
get_pseudonymized_user_id(user_id: str | None) -> str
ParameterTypeDescription
user_idstr | NoneThe user ID to pseudonymize. If None, returns pseudonym for "unknown"

Returns: str - A hexadecimal HMAC-SHA256 hash of the user ID

Example:

python
pseudonym = usage_tracking.get_pseudonymized_user_id("user123")
# Returns: "a1b2c3d4e5f6..." (consistent hash)

# Unknown users
pseudonym = usage_tracking.get_pseudonymized_user_id(None)
# Returns: pseudonym for "unknown"

Features:

  • One-way: Cannot be reversed to obtain the original user ID
  • Consistent: Same user ID always produces the same pseudonym
  • Privacy-compliant: Suitable for GDPR and privacy regulations

log_event

Logs a usage event with structured data compatible with OpenSearch.

python
log_event(
    module: str,
    func: str,
    user_id: str | None,
    **kwargs: str | int | float | bool | None
) -> None
ParameterTypeDescription
modulestrThe module name where the event occurred
funcstrThe function name where the event occurred
user_idstr | NoneThe user ID (will be pseudonymized automatically)
**kwargsstr | int | float | bool | NoneAdditional event data as flat top-level key-value pairs

Emits an app_event record with:

FieldValue
event"app_event"
action"{module}.{func}"
pseudonym_idHMAC-SHA256 of the user ID
(your kwargs)Merged in as top-level keys

Scalars only

Keep **kwargs to str, int, float, bool or None. Nested dicts and lists defeat OpenSearch aggregation, which is the point of the flat shape. Never pass raw user content or unhashed user identifiers.

Example:

python
usage_tracking.log_event(
    module="document_processor",
    func="extract_text",
    user_id="user123",
    document_type="pdf",
    page_count=10,
    processing_time_ms=250.5,
    success=True
)

Configuration

HMAC Secret

The HMAC secret should be:

  • Secure: Use a strong, randomly generated secret
  • Consistent: Same secret across all application instances for consistent pseudonymization
  • Stored securely: Use environment variables or secret management systems

The built-in AppConfig already exposes it as hmac_secret (from HMAC_SECRET), so wire the service from config rather than reading the environment again:

python
config = AppConfig.from_env()
usage_tracking = UsageTrackingService(hmac_secret=config.hmac_secret)

Rotating the secret

Changing the secret changes every pseudonym, breaking continuity of per-user analysis across the rotation point.

  • Logger — the pinned usage logger these events use
  • Configuration — where hmac_secret comes from
  • LLM Agent — the other producer on the usage logger

Source Code

The full implementation is available on GitHub: dcc_backend_common/usage_tracking/usage_tracking.py

Developed with ❤️ by the DCC. Documentation released under the MIT License.