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:
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:
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
// 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:
// 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:
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
logger.setContext({ userId: '123', sessionId: 'sess_abc' });
// Clear everything
logger.clearContext();
logger.info({ message: 'Action' });
// { data:{message: 'Action'} } - no contextClear Specific Keys
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
logger.clearContext(['sessionId', 'tempFlag']);
// Removes both sessionId and tempFlag, keeps everything elseCommon Context Patterns
User Context
Track user information across all logs:
// 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:
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:
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:
// 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 lifecycleFeature Flag Context
Track which features are enabled:
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
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
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
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
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
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
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
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
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:
// ✅ 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:
// ✅ 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:
// ✅ Good: Cleanup on unmount
useEffect(() => {
logger.setContext({ component: 'ProductList' });
return () => logger.clearContext(['component']);
}, []);
// ❌ Avoid: No cleanup
useEffect(() => {
logger.setContext({ component: 'ProductList' });
}, []);4. Namespace Related Context
Group related context fields:
// ✅ 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:
// ❌ 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:
// ✅ 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:
// ❌ 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)
// ✅ 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
// ✅ 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:
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
// ❌ 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
// ❌ 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
// ❌ 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() });