platxa-logging
Structured logging and correlation ID patterns for Platxa services. Covers Python logging, Go slog, TypeScript console patterns, and request tracing across microservices.
What this skill does
# Platxa Logging
Guide for structured logging and correlation ID patterns across Platxa services.
## Overview
| Language | Logger | Correlation | Output Format |
|----------|--------|-------------|---------------|
| **Python** | logging module | ContextVar | JSON (audit) |
| **TypeScript** | Console | Session ID | Text/JSON |
| **Go** | log/slog | context.Context | JSON |
## Core Principles
1. **Structured Over Unstructured**: JSON format for machine parsing and log aggregation
2. **Correlation Required**: Every request gets a unique ID, propagated across all services
3. **Context Propagation**: Thread/goroutine-safe context passing
4. **Consistent Field Keys**: Standardized keys across all languages
5. **Log Levels by Impact**: ERROR=failed, WARN=degraded, INFO=normal, DEBUG=diagnostic
## Standard Field Keys
| Key | Type | Description |
|-----|------|-------------|
| `timestamp` | ISO8601 | When event occurred |
| `level` | string | error/warn/info/debug |
| `message` | string | Human-readable description |
| `request_id` | string | Correlation ID (UUID) |
| `namespace` | string | K8s namespace (instance-xxx) |
| `service` | string | Service name |
| `duration_ms` | int | Operation duration in milliseconds |
| `error` | string | Error message (if any) |
| `user_id` | int | User identifier (if authenticated) |
## Correlation ID Patterns
### HTTP Header Propagation
```
Client → Service A → Service B → Service C
│ │ │
└─ X-Request-ID: abc123 ─┘
```
**Header Name**: `X-Request-ID` (primary) or `X-Correlation-ID`
### Generation Rules
1. **At Service Boundary**: Generate if header missing
2. **Format**: UUID v4 (`550e8400-e29b-41d4-a716-446655440000`)
3. **Propagate**: Include in all downstream requests
4. **Log**: Include in every log entry
### Python Implementation
```python
import uuid
from contextvars import ContextVar
# Thread-safe storage
request_id: ContextVar[str] = ContextVar('request_id', default='')
def get_request_id() -> str:
"""Get current request ID or generate new one."""
rid = request_id.get()
return rid if rid else str(uuid.uuid4())
def set_request_id(rid: str) -> None:
"""Set request ID for current context."""
request_id.set(rid)
# Flask/Werkzeug middleware
@app.before_request
def extract_request_id():
rid = request.headers.get('X-Request-ID', str(uuid.uuid4()))
set_request_id(rid)
g.request_id = rid
@app.after_request
def add_request_id_header(response):
response.headers['X-Request-ID'] = get_request_id()
return response
```
### Go Implementation
```go
type contextKey string
const RequestIDKey contextKey = "request_id"
func RequestIDMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
reqID := r.Header.Get("X-Request-ID")
if reqID == "" {
reqID = uuid.New().String()
}
ctx := context.WithValue(r.Context(), RequestIDKey, reqID)
w.Header().Set("X-Request-ID", reqID)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func GetRequestID(ctx context.Context) string {
if reqID, ok := ctx.Value(RequestIDKey).(string); ok {
return reqID
}
return ""
}
```
### TypeScript Implementation
```typescript
import { v4 as uuidv4 } from 'uuid';
interface RequestContext {
requestId: string;
sessionId: string;
userId?: string;
}
// Express middleware
function requestIdMiddleware(req: Request, res: Response, next: NextFunction) {
const requestId = req.headers['x-request-id'] as string || uuidv4();
req.context = { requestId, sessionId: req.sessionID };
res.setHeader('X-Request-ID', requestId);
next();
}
// Fetch wrapper for downstream calls
async function fetchWithCorrelation(url: string, ctx: RequestContext, options = {}) {
return fetch(url, {
...options,
headers: {
...options.headers,
'X-Request-ID': ctx.requestId,
},
});
}
```
## Structured Logging
### JSON Output Format
```json
{
"timestamp": "2024-01-15T10:30:45.123Z",
"level": "info",
"message": "request completed",
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"namespace": "instance-abc123xy",
"service": "waking-service",
"duration_ms": 45,
"method": "GET",
"path": "/api/status",
"status": 200
}
```
### Python Structured Logger
```python
import logging
import json
from datetime import datetime
class StructuredFormatter(logging.Formatter):
"""JSON formatter with correlation ID."""
def format(self, record: logging.LogRecord) -> str:
log_entry = {
'timestamp': datetime.utcnow().isoformat() + 'Z',
'level': record.levelname.lower(),
'message': record.getMessage(),
'logger': record.name,
'request_id': get_request_id(),
}
# Add extra fields
if hasattr(record, 'namespace'):
log_entry['namespace'] = record.namespace
if hasattr(record, 'duration_ms'):
log_entry['duration_ms'] = record.duration_ms
if record.exc_info:
log_entry['exception'] = self.formatException(record.exc_info)
return json.dumps(log_entry)
# Setup
handler = logging.StreamHandler()
handler.setFormatter(StructuredFormatter())
logging.root.addHandler(handler)
```
### Go slog Setup
```go
import (
"log/slog"
"os"
)
// Field key constants
const (
KeyRequestID = "request_id"
KeyNamespace = "namespace"
KeyDurationMS = "duration_ms"
KeyError = "error"
KeyService = "service"
)
func SetupLogger(level string, jsonFormat bool) {
opts := &slog.HandlerOptions{Level: parseLevel(level)}
var handler slog.Handler
if jsonFormat {
handler = slog.NewJSONHandler(os.Stdout, opts)
} else {
handler = slog.NewTextHandler(os.Stdout, opts)
}
slog.SetDefault(slog.New(handler))
}
// Usage with context
func LogWithContext(ctx context.Context, msg string, args ...any) {
allArgs := append([]any{KeyRequestID, GetRequestID(ctx)}, args...)
slog.Info(msg, allArgs...)
}
```
### TypeScript Logger
```typescript
interface LogEntry {
timestamp: string;
level: 'error' | 'warn' | 'info' | 'debug';
message: string;
requestId?: string;
[key: string]: unknown;
}
class StructuredLogger {
constructor(private service: string, private context?: RequestContext) {}
private log(level: LogEntry['level'], message: string, data?: Record<string, unknown>) {
const entry: LogEntry = {
timestamp: new Date().toISOString(),
level,
message,
service: this.service,
requestId: this.context?.requestId,
...data,
};
if (level === 'error') {
console.error(JSON.stringify(entry));
} else {
console.log(JSON.stringify(entry));
}
}
info(message: string, data?: Record<string, unknown>) { this.log('info', message, data); }
warn(message: string, data?: Record<string, unknown>) { this.log('warn', message, data); }
error(message: string, data?: Record<string, unknown>) { this.log('error', message, data); }
debug(message: string, data?: Record<string, unknown>) { this.log('debug', message, data); }
}
```
## Log Level Guidelines
| Level | When to Use | Example |
|-------|-------------|---------|
| ERROR | Operation failed, requires attention | `"database connection failed"` |
| WARN | Degraded but working, potential issue | `"rate limit at 80%"` |
| INFO | Normal operation, significant events | `"instance started"` |
| DEBUG | Diagnostic detail, development | `"parsing config file"` |
### Dynamic Log Levels (Go)
```go
func LoggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
wrapped := &responseWriter{ResponseWriter: w}
next.ServeHTTP(wrapped, r)
// Dynamic level based on status
level := slog.LevelInfo
if wrapRelated in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.