Configuration
Logging

Logging

The Intapp REST Client includes structured logging for debugging and monitoring.

Default Behavior

By default, the client logs at INFO level:

from intapp_rest_client import RestClient
 
client = RestClient(
    base_url="https://api.example.com",
    api_key="your-key"
)
 
# INFO level logs
data = client.get("/api/resources")
# Output: Request: GET https://api.example.com/api/resources
# Output: Response: 200 (123.4ms)

Log Levels

from intapp_rest_client import RestClient, LogLevel
 
# Debug level - see all details
client = RestClient(
    base_url="https://api.example.com",
    api_key="your-key",
    log_level=LogLevel.DEBUG
)
 
# Warning level - only warnings and errors
client = RestClient(
    base_url="https://api.example.com",
    api_key="your-key",
    log_level=LogLevel.WARNING
)
LevelValueShows
DEBUG10Everything including headers and bodies
INFO20Request/response summary (default)
WARNING30Warnings and errors only
ERROR40Errors only
CRITICAL50Critical errors only

Log Output

INFO Level

2024-01-15 10:30:45 - intapp_rest_client.RestClient - INFO - Request: GET https://api.example.com/api/resources
2024-01-15 10:30:45 - intapp_rest_client.RestClient - INFO - Response: 200 (123.4ms)

DEBUG Level

2024-01-15 10:30:45 - intapp_rest_client.RestClient - INFO - Request: GET https://api.example.com/api/resources
2024-01-15 10:30:45 - intapp_rest_client.RestClient - DEBUG - Request details: RequestLog(timestamp='2024-01-15T10:30:45', method='GET', url='https://api.example.com/api/resources', headers={'Content-Type': 'application/json', 'Authorization': '***MASKED***'}, params=None, body=None, request_id='abc-123')
2024-01-15 10:30:45 - intapp_rest_client.RestClient - INFO - Response: 200 (123.4ms)
2024-01-15 10:30:45 - intapp_rest_client.RestClient - DEBUG - Response details: ResponseLog(timestamp='2024-01-15T10:30:45', status_code=200, headers={...}, body='{"data": [...]}', duration_ms=123.4, request_id='abc-123')

Sensitive Data Masking

The client automatically masks sensitive headers:

# These headers are automatically masked in logs:
# - Authorization
# - X-API-Key
# - Api-Key
# - Secret
# - Password
 
# Log output shows:
# headers={'Authorization': '***MASKED***', 'X-API-Key': '***MASKED***', ...}

Request ID Tracking

Each request is assigned a unique ID for tracing:

client = RestClient(
    base_url="https://api.example.com",
    api_key="your-key",
    enable_request_id=True,  # Default
    request_id_header="X-Request-ID"  # Default header name
)

Request IDs appear in:

  • Request headers sent to the server
  • Log entries
  • Error messages

Custom Logging Configuration

Using Python's logging module

import logging
 
# Configure the client's logger
logger = logging.getLogger("intapp_rest_client.RestClient")
logger.setLevel(logging.DEBUG)
 
# Add a custom handler
handler = logging.FileHandler("api_requests.log")
handler.setFormatter(logging.Formatter(
    '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
))
logger.addHandler(handler)

Disable Logging

import logging
 
# Disable client logging entirely
logging.getLogger("intapp_rest_client").setLevel(logging.CRITICAL)

JSON Logging

For structured logging in production:

import logging
import json
 
class JSONFormatter(logging.Formatter):
    def format(self, record):
        return json.dumps({
            "timestamp": self.formatTime(record),
            "level": record.levelname,
            "logger": record.name,
            "message": record.getMessage()
        })
 
logger = logging.getLogger("intapp_rest_client.RestClient")
handler = logging.StreamHandler()
handler.setFormatter(JSONFormatter())
logger.addHandler(handler)

Request/Response Hooks

For custom logging or metrics:

from intapp_rest_client import RestClient
 
def log_request(method, url, headers, body):
    print(f"→ {method} {url}")
    if body:
        print(f"  Body: {body}")
 
def log_response(response, duration_ms):
    print(f"← {response.status_code} ({duration_ms:.1f}ms)")
 
client = RestClient(
    base_url="https://api.example.com",
    api_key="your-key",
    pre_request_hook=log_request,
    post_response_hook=log_response
)
 
data = client.get("/api/resources")
# Output:
# → GET https://api.example.com/api/resources
# ← 200 (123.4ms)

Metrics Collection

from prometheus_client import Counter, Histogram
 
REQUEST_COUNT = Counter('api_requests_total', 'Total API requests', ['method', 'status'])
REQUEST_LATENCY = Histogram('api_request_latency_seconds', 'Request latency')
 
def collect_metrics(response, duration_ms):
    REQUEST_COUNT.labels(
        method=response.request.method,
        status=response.status_code
    ).inc()
    REQUEST_LATENCY.observe(duration_ms / 1000)
 
client = RestClient(
    base_url="https://api.example.com",
    api_key="your-key",
    post_response_hook=collect_metrics
)
💡

Hooks are called even when requests fail, making them reliable for metrics collection.

Best Practices

  1. Production: Use INFO level for normal operation
  2. Debugging: Switch to DEBUG temporarily to diagnose issues
  3. CI/CD: Use WARNING or higher to reduce log noise
  4. Sensitive Data: Never log request/response bodies containing PII

Next Steps