Skip to content

Context & Metadata ​

Context is persistent metadata that's automatically included in all subsequent logs. It's one of the most powerful features in Logstyx for creating meaningful, searchable logs without repetition.

What is Context? ​

Context is metadata that applies to multiple log entries. Instead of repeating the same information in every log, you set it once and it's automatically included until you clear it.

Without Context:

javascript
logger.info({ message: 'User viewed product', userId: '123', sessionId: 'sess_abc' });
logger.info({ message: 'User added to cart', userId: '123', sessionId: 'sess_abc' });
logger.info({ message: 'User checked out', userId: '123', sessionId: 'sess_abc' });

With Context:

javascript
logger.setContext({ userId: '123', sessionId: 'sess_abc' });

logger.info({ message: 'User viewed product' });
logger.info({ message: 'User added to cart' });
logger.info({ message: 'User checked out' });

logger.clearContext();

Both produce the same logs, but the context approach is cleaner and less error-prone.

Setting Context ​

Basic Usage ​

javascript
// Set context
logger.setContext({
  userId: '12345',
  sessionId: 'sess_abc',
  environment: 'production'
});

// This log includes all context fields
logger.info({ message: 'User action performed' });

// Sent as:
// {
//  context:{
//   userId: '12345',
//   sessionId: 'sess_abc',
//   environment: 'production'
//  },
// data:{
//   message: 'User action performed',
// }
//}

Adding to Existing Context ​

Context is additive - new context is merged with existing context:

javascript
// Set initial context
logger.setContext({ userId: '123' });

// Add more context
logger.setContext({ sessionId: 'sess_abc' });

// Now both are included
logger.info({ message: 'Action' });
// {context : {userId: '123', sessionId: 'sess_abc' }, data : { message: 'Action'}}

Overwriting Context Values ​

Setting a key that already exists will overwrite it:

javascript
logger.setContext({ status: 'guest' });
logger.info({ message: 'Action 1' });  // status: 'guest'

logger.setContext({ status: 'authenticated' });
logger.info({ message: 'Action 2' });  // status: 'authenticated'

Clearing Context ​

Clear All Context ​

javascript
logger.setContext({ userId: '123', sessionId: 'sess_abc' });

// Clear everything
logger.clearContext();

logger.info({ message: 'Action' });
// { data:{message: 'Action'} } - no context

Clear Specific Keys ​

javascript
logger.setContext({ 
  userId: '123', 
  sessionId: 'sess_abc',
  tempFlag: true 
});

// Clear only specific keys
logger.clearContext(['tempFlag']);

logger.info({ message: 'Action' });
// { context : {userId: '123', sessionId: 'sess_abc'}, data :{message: 'Action'} }

Clear Multiple Keys ​

javascript
logger.clearContext(['sessionId', 'tempFlag']);
// Removes both sessionId and tempFlag, keeps everything else

Common Context Patterns ​

User Context ​

Track user information across all logs:

javascript
// On user login
function handleLogin(user) {
  logger.setContext({
    userId: user.id,
    userEmail: user.email,
    userRole: user.role,
    accountType: user.subscription
  });
  
  logger.info({ message: 'User logged in' });
}

// On logout
function handleLogout() {
  logger.info({ message: 'User logged out' });
  
  // Clear user context
  logger.clearContext(['userId', 'userEmail', 'userRole', 'accountType']);
}

Request Context (Express) ​

Track request information throughout the request lifecycle:

javascript
app.use((req, res, next) => {
  logger.setContext({
    requestId: req.id,
    method: req.method,
    path: req.path,
    ip: req.ip
  });
  
  next();
  
  // Clear after response
  res.on('finish', () => {
    logger.clearContext(['requestId', 'method', 'path', 'ip']);
  });
});

// Now all logs in this request include request context
app.get('/api/users', async (req, res) => {
  logger.info({ message: 'Fetching users' });
  // Includes requestId, method, path, ip automatically
  
  const users = await User.find();
  res.json(users);
});

Session Context (React) ​

Track session information across components:

jsx
function App() {
  const logger = useLogstyx({ projectId: 'proj_xyz' });
  
  useEffect(() => {
    logger.setContext({
      sessionId: generateSessionId(),
      page: window.location.pathname,
      referrer: document.referrer,
      timestamp: Date.now()
    });
    
    return () => logger.clearContext();
  }, []);
  
  return <YourApp />;
}

Environment Context ​

Set once at application start:

javascript
// On app initialization
logger.setContext({
  environment: process.env.NODE_ENV,
  version: process.env.APP_VERSION,
  region: process.env.AWS_REGION,
  hostname: require('os').hostname()
});

// These are included in all logs throughout the app lifecycle

Feature Flag Context ​

Track which features are enabled:

javascript
logger.setContext({
  features: {
    newCheckout: true,
    betaUI: false,
    aiRecommendations: true
  }
});

// Logs will show which features were active when events occurred
logger.info({ message: 'Purchase completed' });

React Examples ​

Component-Level Context ​

jsx
import { useEffect } from 'react';
import useLogstyx from 'logstyx-js-react';

function ProductPage({ productId }) {
  const logger = useLogstyx({ projectId: 'proj_xyz' });
  
  useEffect(() => {
    // Set context when component mounts
    logger.setContext({
      page: 'product',
      productId: productId
    });
    
    logger.info({ message: 'Product page viewed' });
    
    // Clear when component unmounts
    return () => {
      logger.clearContext(['page', 'productId']);
    };
  }, [productId]);
  
  const handleAddToCart = () => {
    logger.info({ message: 'Added to cart' });
    // Automatically includes page and productId
  };
  
  return (
    <button onClick={handleAddToCart}>
      Add to Cart
    </button>
  );
}

User Authentication Context ​

jsx
import { useEffect } from 'react';

function AuthProvider({ children }) {
  const logger = useLogstyx({ projectId: 'proj_xyz' });
  const { user } = useAuth();
  
  useEffect(() => {
    if (user) {
      logger.setContext({
        userId: user.id,
        userRole: user.role,
        authenticated: true
      });
    } else {
      logger.clearContext(['userId', 'userRole', 'authenticated']);
    }
  }, [user]);
  
  return children;
}

Router Context ​

jsx
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';

function App() {
  const logger = useLogstyx({ projectId: 'proj_xyz' });
  const location = useLocation();
  
  useEffect(() => {
    logger.setContext({
      route: location.pathname,
      search: location.search
    });
    
    logger.info({ message: 'Page view' });
  }, [location]);
  
  return <YourApp />;
}

React Native Examples ​

Screen Context ​

jsx
import { useEffect } from 'react';

function ProfileScreen({ route }) {
  const logger = logstyx({ 
    projectId: 'proj_xyz', 
    appid: 'com.example.app' 
  });
  
  useEffect(() => {
    logger.setContext({
      screen: 'profile',
      userId: route.params.userId,
      platform: Platform.OS
    });
    
    logger.info({ message: 'Screen mounted' });
    
    return () => {
      logger.info({ message: 'Screen unmounted' });
      logger.clearContext(['screen', 'userId']);
    };
  }, [route.params.userId]);
  
  return <View>...</View>;
}

App State Context ​

jsx
import { useEffect } from 'react';
import { AppState } from 'react-native';

function App() {
  const logger = logstyx({ 
    projectId: 'proj_xyz', 
    appid: 'com.example.app' 
  });
  
  useEffect(() => {
    const subscription = AppState.addEventListener('change', (state) => {
      logger.setContext({ appState: state });
      logger.info({ message: 'App state changed', state });
    });
    
    return () => subscription.remove();
  }, []);
  
  return <YourApp />;
}

Node.js Examples ​

Request Context with Express ​

javascript
const express = require('express');
const useLogstyx = require('logstyx-js-node');

const app = express();
const logger = useLogstyx({ 
  projectId: 'proj_xyz',
  apiKey: 'key_abc'
});

// Middleware to set request context
app.use((req, res, next) => {
  const requestId = req.headers['x-request-id'] || generateId();
  
  logger.setContext({
    requestId,
    method: req.method,
    path: req.path,
    userAgent: req.headers['user-agent']
  });
  
  // Clear after response
  res.on('finish', () => {
    logger.info({ 
      message: 'Request completed',
      statusCode: res.statusCode 
    });
    
    logger.clearContext(['requestId', 'method', 'path', 'userAgent']);
  });
  
  next();
});

app.get('/api/users', async (req, res) => {
  logger.info({ message: 'Fetching users' });
  // Automatically includes requestId, method, path, userAgent
  
  const users = await User.find();
  res.json(users);
});

Transaction Context ​

javascript
async function processPayment(orderId, amount) {
  const transactionId = generateId();
  
  logger.setContext({
    transactionId,
    orderId,
    amount
  });
  
  try {
    logger.info({ message: 'Payment processing started' });
    
    const result = await stripe.charges.create({ amount });
    
    logger.info({ 
      message: 'Payment processed successfully',
      chargeId: result.id 
    });
    
    return result;
  } catch (error) {
    logger.error({ 
      message: 'Payment processing failed',
      error: error.message 
    });
    throw error;
  } finally {
    logger.clearContext(['transactionId', 'orderId', 'amount']);
  }
}

Background Job Context ​

javascript
async function processJob(job) {
  logger.setContext({
    jobId: job.id,
    jobType: job.type,
    attempt: job.attemptsMade
  });
  
  try {
    logger.info({ message: 'Job started' });
    
    const result = await job.execute();
    
    logger.info({ 
      message: 'Job completed',
      duration: result.duration 
    });
  } catch (error) {
    logger.error({ 
      message: 'Job failed',
      error: error.message 
    });
    throw error;
  } finally {
    logger.clearContext(['jobId', 'jobType', 'attempt']);
  }
}

Best Practices ​

1. Set Context Early ​

Set context as early as possible in the lifecycle:

javascript
// ✅ Good: Set at the start
function handleRequest(req, res) {
  logger.setContext({ requestId: req.id });
  
  logger.info({ message: 'Request started' });
  processRequest(req);
}

// ❌ Avoid: Setting late
function handleRequest(req, res) {
  processRequest(req);
  logger.setContext({ requestId: req.id });  // Too late!
}

2. Always Clear Context ​

Prevent context leakage by clearing when done:

javascript
// ✅ Good: Clear after use
async function processOrder(order) {
  logger.setContext({ orderId: order.id });
  
  try {
    await process(order);
  } finally {
    logger.clearContext(['orderId']);  // Always clear
  }
}

// ❌ Avoid: Forgetting to clear
async function processOrder(order) {
  logger.setContext({ orderId: order.id });
  await process(order);
  // Context leaks to next operation!
}

3. Use Cleanup Functions ​

In React, use cleanup functions:

jsx
// ✅ Good: Cleanup on unmount
useEffect(() => {
  logger.setContext({ component: 'ProductList' });
  
  return () => logger.clearContext(['component']);
}, []);

// ❌ Avoid: No cleanup
useEffect(() => {
  logger.setContext({ component: 'ProductList' });
}, []);

Group related context fields:

javascript
// ✅ Good: Namespaced
logger.setContext({
  user: {
    id: '123',
    role: 'admin'
  },
  request: {
    id: 'req_xyz',
    method: 'POST'
  }
});

// ✅ Also good: Prefixed
logger.setContext({
  userId: '123',
  userRole: 'admin',
  requestId: 'req_xyz',
  requestMethod: 'POST'
});

5. Don't Put Logs in Context ​

Context is for metadata, not log messages:

javascript
// ❌ Wrong: Log message in context
logger.setContext({ message: 'Processing payment' });
logger.info({ amount: 99.99 });

// ✅ Correct: Message in log, metadata in context
logger.setContext({ orderId: 'ord_123' });
logger.info({ message: 'Processing payment', amount: 99.99 });

6. Use Context for Repeated Data ​

Only put data in context if it repeats across multiple logs:

javascript
// ✅ Good: Repeated data in context
logger.setContext({ orderId: 'ord_123', userId: 'user_456' });
logger.info({ message: 'Order validation started' });
logger.info({ message: 'Inventory checked' });
logger.info({ message: 'Payment processed' });
logger.clearContext(['orderId', 'userId']);

// ❌ Avoid: Single-use data in context
logger.setContext({ timestamp: Date.now() });
logger.info({ message: 'One-time event' });  // Just put it in the log
logger.clearContext(['timestamp']);

7. Keep Context Lightweight ​

Don't store large objects in context:

javascript
// ❌ Avoid: Large objects
logger.setContext({ 
  entireOrder: { /* huge object */ } 
});

// ✅ Better: Just identifiers
logger.setContext({ 
  orderId: order.id,
  orderTotal: order.total 
});

Context vs. Direct Logging ​

When to Use Context ​

Use context when:

  • Data repeats across multiple logs
  • Tracking a flow or session
  • Information applies to a scope (request, component, job)
javascript
// ✅ Good use of context
logger.setContext({ orderId: 'ord_123' });
logger.info({ message: 'Order created' });
logger.info({ message: 'Inventory reserved' });
logger.info({ message: 'Payment processed' });
logger.clearContext(['orderId']);

When to Use Direct Logging ​

Use direct logging when:

  • Data is unique to that specific log
  • One-time events
  • Data changes with each log
javascript
// ✅ Good use of direct logging
logger.info({ 
  message: 'Payment processed',
  amount: 99.99,
  transactionId: 'txn_abc'
});

Context and Data are Separate ​

Context and data are stored in separate fields in the payload:

javascript
logger.setContext({ 
  userId: '123',
  sessionId: 'sess_abc' 
});

logger.info({ 
  message: 'Purchase completed',
  amount: 99.99,
  items: 3
});

// Sent as:
// {
//   level: 'info',
//   projectId: 'proj_xyz',
//   context: {              // Context in its own field
//     userId: '123',
//     sessionId: 'sess_abc'
//   },
//   data: {                 // Data in its own field
//     message: 'Purchase completed',
//     amount: 99.99,
//     items: 3
//   }
// }

This separation means:

  • Context and data don't conflict or override each other
  • You can search by context fields separately from data fields
  • Context persists across logs, data is per-log
  • Both are stored and indexed independently in the backend

Common Mistakes ​

1. Not Clearing Context ​

javascript
// ❌ Wrong: Context leaks
function handleUser1() {
  logger.setContext({ userId: '123' });
  logger.info({ message: 'User 1 action' });
}

function handleUser2() {
  logger.info({ message: 'User 2 action' });
  // Still includes userId: '123' from previous function!
}

// ✅ Correct: Always clear
function handleUser1() {
  logger.setContext({ userId: '123' });
  logger.info({ message: 'User 1 action' });
  logger.clearContext(['userId']);
}

2. Setting Context Too Late ​

javascript
// ❌ Wrong: Set after logs
async function processOrder(order) {
  logger.info({ message: 'Processing order' });  // No context!
  logger.setContext({ orderId: order.id });
}

// ✅ Correct: Set before logs
async function processOrder(order) {
  logger.setContext({ orderId: order.id });
  logger.info({ message: 'Processing order' });
}

3. Overusing Context ​

javascript
// ❌ Wrong: One-time data in context
logger.setContext({ timestamp: Date.now() });
logger.info({ message: 'Single event' });
logger.clearContext(['timestamp']);

// ✅ Correct: Just include it directly
logger.info({ message: 'Single event', timestamp: Date.now() });

Released under the MIT License.