CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-opencensus

A comprehensive observability framework providing distributed tracing, metrics collection, and statistics gathering capabilities for Python applications.

Pending
Overview
Eval results
Files

logging.mddocs/

Logging Integration

Integration with Python's logging system to automatically include trace context in log records, enabling correlation between logs and traces for better observability and debugging.

Capabilities

Log Context Extraction

Extract current OpenCensus trace context for inclusion in log records and correlation with distributed traces.

def get_log_attrs():
    """
    Get logging attributes from OpenCensus context.
    
    Returns:
    LogAttrs: Named tuple with trace_id, span_id, and sampling_decision
            or ATTR_DEFAULTS if no context available
    """

LogAttrs = namedtuple('LogAttrs', ['trace_id', 'span_id', 'sampling_decision'])
"""
Named tuple containing trace context for logging.

Fields:
- trace_id: str, 32-character hex trace ID or default
- span_id: str, 16-character hex span ID or default  
- sampling_decision: bool, whether trace is sampled
"""

ATTR_DEFAULTS = LogAttrs("00000000000000000000000000000000", "0000000000000000", False)
"""Default log attributes when no trace context is available."""

Enhanced Logger Adapter

Logging adapter that automatically adds OpenCensus trace context to all log records for correlation and filtering.

class TraceLoggingAdapter:
    """
    LoggerAdapter that adds OpenCensus context to log records.
    
    Inherits from logging.LoggerAdapter and automatically includes
    trace context in all log messages.
    
    Parameters:
    - logger: logging.Logger, underlying logger instance
    - extra: dict, additional context to include in logs
    """
    def __init__(self, logger, extra=None): ...

    def process(self, msg, kwargs):
        """
        Process log message and add trace context.
        
        Automatically adds current trace context to the log record's
        extra data for correlation with distributed traces.
        
        Parameters:
        - msg: str, log message
        - kwargs: dict, keyword arguments for logging call
        
        Returns:
        tuple: (processed_message, updated_kwargs)
        """

Context-Aware Logger

Custom logger class that automatically includes OpenCensus trace context in log record creation.

class TraceLogger:
    """
    Logger subclass that includes OpenCensus context in records.
    
    Inherits from logging.getLoggerClass() and automatically adds
    trace context to all log records at creation time.
    """
    def makeRecord(self, *args, **kwargs):
        """
        Create log record with OpenCensus trace context.
        
        Extends standard makeRecord to automatically include current
        trace context in the log record's attributes.
        
        Parameters:
        - *args: positional arguments for standard makeRecord
        - **kwargs: keyword arguments for standard makeRecord
        
        Returns:
        LogRecord: Enhanced log record with trace context
        """

Log Context Constants

Standard field names and default values for trace context in log records.

TRACE_ID_KEY = 'traceId'
"""str: Log record field name for trace ID"""

SPAN_ID_KEY = 'spanId'  
"""str: Log record field name for span ID"""

SAMPLING_DECISION_KEY = 'traceSampled'
"""str: Log record field name for sampling decision"""

Usage Examples

Basic Trace Logging Adapter

import logging
from opencensus.log import TraceLoggingAdapter, get_log_attrs
from opencensus.trace.tracer import Tracer

# Set up standard logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

# Create handler with formatter that includes trace fields
handler = logging.StreamHandler()
formatter = logging.Formatter(
    '%(asctime)s - %(name)s - %(levelname)s - '
    'TraceID=%(traceId)s SpanID=%(spanId)s - %(message)s'
)
handler.setFormatter(formatter)
logger.addHandler(handler)

# Wrap with trace logging adapter
trace_logger = TraceLoggingAdapter(logger)

# Use within traced operations
tracer = Tracer()

with tracer.span('process_order') as span:
    span.add_attribute('order_id', '12345')
    
    # Log messages automatically include trace context
    trace_logger.info('Starting order processing')
    
    try:
        # Process order logic
        process_order_data()
        trace_logger.info('Order processed successfully')
        
    except Exception as e:
        trace_logger.error('Order processing failed', exc_info=True)
        raise

# Log output will include trace ID and span ID:
# 2024-01-15 10:30:15 - __main__ - INFO - TraceID=abc123... SpanID=def456... - Starting order processing

Custom Logger Integration

import logging
from opencensus.log import TraceLogger, get_log_attrs

# Set TraceLogger as default logger class
logging.setLoggerClass(TraceLogger)

# Create logger - will automatically include trace context
logger = logging.getLogger(__name__)

# Configure with trace-aware formatting
handler = logging.StreamHandler()
formatter = logging.Formatter(
    '%(levelname)s [%(traceId)s:%(spanId)s] %(name)s: %(message)s'
)
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)

from opencensus.trace.tracer import Tracer

def process_user_request(user_id):
    tracer = Tracer()
    
    with tracer.span('process_user_request') as span:
        span.add_attribute('user_id', user_id)
        
        # All log calls automatically include trace context
        logger.info(f'Processing request for user {user_id}')
        
        with tracer.span('validate_user') as child_span:
            logger.debug('Validating user credentials')
            validate_user(user_id)
            logger.debug('User validation completed')
        
        with tracer.span('load_user_data') as child_span:
            logger.debug('Loading user data from database')
            user_data = load_user_data(user_id)
            logger.info(f'Loaded {len(user_data)} records for user')
        
        logger.info('Request processing completed')

# Usage
process_user_request('user123')

Manual Context Extraction

import logging
from opencensus.log import get_log_attrs, TRACE_ID_KEY, SPAN_ID_KEY, SAMPLING_DECISION_KEY

# Standard logger setup
logger = logging.getLogger(__name__)

def custom_log_with_trace(level, message, **kwargs):
    """Custom logging function that manually adds trace context."""
    
    # Get current trace context
    trace_attrs = get_log_attrs()
    
    # Add trace context to extra data
    extra = kwargs.get('extra', {})
    extra.update({
        TRACE_ID_KEY: trace_attrs.trace_id,
        SPAN_ID_KEY: trace_attrs.span_id,
        SAMPLING_DECISION_KEY: trace_attrs.sampling_decision
    })
    
    # Log with enhanced context
    logger.log(level, message, extra=extra, **kwargs)

# Usage within traced context
from opencensus.trace.tracer import Tracer

tracer = Tracer()
with tracer.span('database_operation') as span:
    custom_log_with_trace(logging.INFO, 'Executing database query')
    
    # Check if we have active trace context
    attrs = get_log_attrs()
    if attrs.trace_id != "00000000000000000000000000000000":
        logger.info('Active trace detected', extra={
            TRACE_ID_KEY: attrs.trace_id,
            SPAN_ID_KEY: attrs.span_id
        })
    else:
        logger.info('No active trace context')

Structured Logging with Trace Context

import logging
import json
from opencensus.log import TraceLoggingAdapter, get_log_attrs

class StructuredTraceFormatter(logging.Formatter):
    """JSON formatter that includes OpenCensus trace context."""
    
    def format(self, record):
        # Get trace context
        trace_attrs = get_log_attrs()
        
        # Build structured log entry
        log_entry = {
            'timestamp': self.formatTime(record),
            'level': record.levelname,
            'logger': record.name,
            'message': record.getMessage(),
            'trace': {
                'trace_id': trace_attrs.trace_id,
                'span_id': trace_attrs.span_id,
                'sampled': trace_attrs.sampling_decision
            }
        }
        
        # Add exception info if present
        if record.exc_info:
            log_entry['exception'] = self.formatException(record.exc_info)
        
        # Add any extra fields
        for key, value in record.__dict__.items():
            if key not in ['name', 'levelname', 'levelno', 'pathname', 'filename',
                          'module', 'lineno', 'funcName', 'created', 'msecs',
                          'relativeCreated', 'thread', 'threadName', 'processName',
                          'process', 'getMessage', 'exc_info', 'exc_text', 'stack_info',
                          'args', 'msg']:
                log_entry[key] = value
        
        return json.dumps(log_entry)

# Setup structured logging with trace context
logger = logging.getLogger(__name__)
handler = logging.StreamHandler()
handler.setFormatter(StructuredTraceFormatter())
logger.addHandler(handler)
logger.setLevel(logging.INFO)

# Use with trace adapter
trace_logger = TraceLoggingAdapter(logger)

from opencensus.trace.tracer import Tracer

tracer = Tracer()
with tracer.span('api_request') as span:
    span.add_attribute('endpoint', '/api/users')
    span.add_attribute('method', 'GET')
    
    # Structured log with automatic trace context
    trace_logger.info('API request received', extra={
        'endpoint': '/api/users',
        'method': 'GET',
        'user_agent': 'OpenCensus/1.0'
    })

# Output will be JSON with embedded trace context:
# {"timestamp": "2024-01-15 10:30:15,123", "level": "INFO", 
#  "logger": "__main__", "message": "API request received",
#  "trace": {"trace_id": "abc123...", "span_id": "def456...", "sampled": true},
#  "endpoint": "/api/users", "method": "GET", "user_agent": "OpenCensus/1.0"}

Integration with Application Frameworks

import logging
from opencensus.log import TraceLoggingAdapter
from opencensus.trace.tracer import Tracer
from opencensus.trace.propagation import trace_context_http_header_format

# Flask example
from flask import Flask, request, g

app = Flask(__name__)

# Setup trace-aware logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
trace_logger = TraceLoggingAdapter(logger)

@app.before_request
def before_request():
    # Extract trace context from incoming request
    propagator = trace_context_http_header_format.TraceContextPropagator()
    span_context = propagator.from_headers(request.headers)
    
    # Create tracer with incoming context
    g.tracer = Tracer(span_context=span_context)
    g.span = g.tracer.span(f'{request.method} {request.path}')
    g.span.start()
    
    # Log request with trace context
    trace_logger.info(f'Incoming request: {request.method} {request.path}',
                     extra={'method': request.method, 'path': request.path})

@app.after_request
def after_request(response):
    # Log response with trace context
    trace_logger.info(f'Response status: {response.status_code}',
                     extra={'status_code': response.status_code})
    
    if hasattr(g, 'span'):
        g.span.add_attribute('http.status_code', response.status_code)
        g.span.finish()
    
    return response

@app.route('/api/users/<user_id>')
def get_user(user_id):
    # All log messages within request automatically have trace context
    trace_logger.info(f'Fetching user data for {user_id}')
    
    with g.tracer.span('database_query') as span:
        span.add_attribute('user_id', user_id)
        trace_logger.debug('Executing database query')
        
        # Simulate database operation
        user_data = {'id': user_id, 'name': 'John Doe'}
        
        trace_logger.info('User data retrieved successfully')
    
    return user_data

if __name__ == '__main__':
    app.run(debug=True)

Install with Tessl CLI

npx tessl i tessl/pypi-opencensus

docs

common.md

exporters.md

index.md

logging.md

metrics-stats.md

tags-context.md

tracing.md

tile.json