Skip to main content

CONA Logging Guide

All logs are centralized in Axiom for monitoring, debugging, and security analysis.

Quick Reference

ContextHow to LogDataset
Webapp (Server Actions)log({ message, level, metadata })cona_webapp
Temporal Activitieslog.info(message, fields)cona_temporal-workers
Client ComponentsuseLogger()cona_webapp
MiddlewareAuto-logged via logMiddlewareRequest()cona_webapp

Webapp Logging

Server Actions & API Routes

import { log } from "@/app/lib/logger";

export async function serverAction() {
  try {
    await log({
      message: "Operation started",
      level: "info",
      metadata: { userId, action: "create_invoice" }
    });
    
    // ... your code
    
  } catch (error) {
    await log({
      message: "Operation failed",
      level: "error",
      error: error instanceof Error ? error : new Error("Unknown error"),
      metadata: { userId, action: "create_invoice" }
    });
  }
}

Client Components

"use client";
import { useLogger } from "@/lib/axiom/client";

export function MyComponent() {
  const log = useLogger();
  
  const handleClick = () => {
    log.info("Button clicked", { buttonId: "submit" });
  };
}

Temporal Worker Logging

Already configured ✅ - Temporal workers send logs to Axiom automatically

Activities

import { log } from "@temporalio/activity";

export async function myActivity(params) {
  log.info("Activity started", {
    organizationId: params.organizationId,
    integrationId: params.integrationId
  });
  
  try {
    // ... your code
    log.info("Activity completed");
  } catch (error) {
    log.error("Activity failed", {
      error: error instanceof Error ? error.message : String(error)
    });
    throw error;
  }
}
Dataset: cona_temporal-workers
Configuration: apps/temporal-workers/src/logger.ts (uses Pino + @axiomhq/pino)

Database Access Logging (Optional)

For sensitive database operations, add context:
log.info("Database query", {
  action: "fetch_customers",
  table: "cona_customers",
  organizationId: params.organizationId,
});

const customers = await prisma.cona_customers.findMany();

Middleware Logging

Automatic - All requests are logged via logMiddlewareRequest(request) Custom security events:
import { logSecurityEvent } from "@/lib/middleware-logger";

const response = NextResponse.redirect(url);
logSecurityEvent(request, response, {
  action: "blocked",
  reason: "No valid session",
  blocked: true
});

Log Levels

  • info - Normal operations, successful actions
  • warn - Expected issues (auth failures, validation errors)
  • error - Unexpected failures, exceptions

What to Log

Always Include:

✅ Action/operation name
✅ Organization ID (if applicable)
✅ User ID (if applicable)
✅ Success/failure status

Include for Errors:

✅ Error message
✅ Error stack trace
✅ Context (what was being attempted)

Never Log:

❌ Passwords
❌ API keys
❌ Session tokens
❌ Credit card numbers

Viewing Logs

Axiom Dashboard: https://axiom.co/ Datasets:
  • cona_webapp - Web application (middleware, server actions, client)
  • cona_temporal-workers - Temporal workers (workflows, activities)
Query examples:
// Recent webapp errors
['cona_webapp']
| where ['_time'] >= ago(1h)
| where ['level'] == "error"
| sort by ['_time'] desc

// Auth failures (brute force detection)
['cona_webapp']
| where ['_time'] >= ago(5m)
| where ['action'] == "auth_required" or ['action'] == "api_auth_required"
| where ['blocked'] == true
| summarize failures = count() by ['ip']
| where failures > 5

// Temporal activity failures
['cona_temporal-workers']
| where ['_time'] >= ago(1h)
| where ['level'] == "error"
| where ['label'] == "activity"

// Combined errors (all systems)
union ['cona_webapp'], ['cona_temporal-workers']
| where ['_time'] >= ago(1h)
| where ['level'] == "error"
| sort by ['_time'] desc

Security Monitoring

Auth failures are automatically logged:
  • Web page access without session → action: "auth_required"
  • API access without session → action: "api_auth_required"
  • Both include blocked: true flag
Query to detect brute force:
['cona_webapp']
| where ['blocked'] == true
| summarize count() by ['ip'], bin(['_time'], 1m)
| where count > 5

Retention

  • Security events: 12 months
  • Error logs: 6 months
  • Info logs: 3 months

More Info

  • Security Monitoring: docs/axiom-security-monitoring.md
  • Axiom Setup & MCP: Axiom Guide
  • Query Examples: docs/axiom-corrected-queries.md